Update IR's helper functions

This commit is contained in:
Jeehoon Kang
2020-04-11 12:20:53 +09:00
parent 6edb4665c0
commit 72ec63ea82
19 changed files with 505 additions and 125 deletions

10
Cargo.lock generated
View File

@@ -160,6 +160,7 @@ dependencies = [
"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)",
"wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -367,6 +368,14 @@ name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@@ -443,6 +452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"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 wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View File

@@ -32,3 +32,4 @@ failure = "0.1.7"
tempfile = "3.1.0"
ordered-float = "1.0"
hexf = "0.1.0"
wait-timeout = "0.2.0"

View File

@@ -78,7 +78,7 @@ built by the test script. For more information, we refer to the
### Reduce
When the fuzzer finds a buggy input program for your compiler, it is highly likely that the input
program is too big to manually inspect. We use `creduce` that reduces the buggy input program as
program is too big to manually inspect. We use `creduce` that reduces the buggy input program as
much as possible.
Suppose `tests/test_polished.c` is the buggy input program. Then the following script reduces the
@@ -96,3 +96,6 @@ The script performs unguided test-case reduction using `creduce`: given a buggy
reduces the program; check if the reduced program still fails on the test, and if so, replaces the
given program with the reduced one; repeat until you get a small enough buggy program. For more
information, we refer to the [Creduce](https://embed.cs.utah.edu/creduce/) homepage.
**[NOTICE]** The fuzzer supports Ubuntu 18.04 only. It may work for other platforms, but if it
doesn't, please run the fuzzer in Ubuntu 18.04.

10
examples/array4.c Normal file
View File

@@ -0,0 +1,10 @@
int main() {
int a[10];
int *p = a;
for (int i = 0; i < 10; i++) {
*(p++) = i;
}
return a[5] == 5;
}

13
examples/bitwise.c Normal file
View File

@@ -0,0 +1,13 @@
int main() {
unsigned char a = -1;
unsigned char b = -128;
unsigned char c = 127;
unsigned char d = b | a; // -1 (255)
unsigned char e = b & a; // -128 (128)
unsigned char f = b & c; // 0 (0)
unsigned char g = b | c; // -1 (255)
unsigned char h = -1 ^ -1; // 0 (0)
unsigned char i = -1 ^ 0; // -1 (255)
return d == 255 && e == 128 && f == 0 && g == 255 && h == 0 && i == 255;
}

View File

@@ -0,0 +1,5 @@
int main() {
short temp = 0;
unsigned int temp2 = 4294967163;
return (char)(temp ^ temp2) == 123;
}

View File

@@ -0,0 +1,5 @@
int main() {
int temp = 0;
// `0xFFFFFFFF` is translated as `unsigned int` not `int`
return temp < 0xFFFFFFFF;
}

16
examples/logical_op.c Normal file
View File

@@ -0,0 +1,16 @@
int main() {
int a = 0;
int b = 0;
int c = 0;
int d = 0;
if ((a = 1) || (b = 1)) {
b++;
}
if ((c = 1) && (d = 1)) {
d++;
}
return b == 1 && d == 2;
}

View File

@@ -0,0 +1,8 @@
int a = -1;
long b = -1l;
float c = -1.5f;
double d = -1.5;
int main() {
return (a + b + (int)c + (long)d) == -4;
}

7
examples/shift.c Normal file
View File

@@ -0,0 +1,7 @@
int main() {
char a = -1;
char b = a << 1;
unsigned char c = (unsigned char)b >> 1;
return b == -2 && c == 0x7F;
}

View File

@@ -1,10 +1,11 @@
int main() {
long int l = 1;
long l2 = 2;
short int s = 3;
short s2 = 4;
int i = 5;
char c = 6;
long long l3 = 3;
short int s = 4;
short s2 = 5;
int i = 6;
char c = 7;
return l + l2 + s + s2 + i + c;
return (l + l2 + l3 + s + s2 + i + c) == 28;
}

5
examples/typecast.c Normal file
View File

@@ -0,0 +1,5 @@
char temp = 0x00L;
int main(){
return (temp = 0xEF36L) >= (2L);
}

4
examples/unary.c Normal file
View File

@@ -0,0 +1,4 @@
int main() {
unsigned char temp = 0x00L;
return 1 > (--temp);
}

View File

@@ -23,7 +23,7 @@ pub trait HasDtype {
#[derive(Default)]
struct BaseDtype {
scalar: Option<ast::TypeSpecifier>,
size_modifier: Option<ast::TypeSpecifier>,
size_modifiers: Vec<ast::TypeSpecifier>,
signed_option: Option<ast::TypeSpecifier>,
typedef_name: Option<String>,
is_const: bool,
@@ -129,12 +129,7 @@ impl BaseDtype {
self.scalar = Some(type_specifier.clone());
}
ast::TypeSpecifier::Short | ast::TypeSpecifier::Long => {
if self.size_modifier.is_some() {
return Err(DtypeError::Misc {
message: "two or more size modifiers in declaration specifiers".to_string(),
});
}
self.size_modifier = Some(type_specifier.clone());
self.size_modifiers.push(type_specifier.clone())
}
ast::TypeSpecifier::TypedefName(identifier) => {
if self.typedef_name.is_some() {
@@ -277,7 +272,7 @@ impl TryFrom<BaseDtype> for Dtype {
fn try_from(spec: BaseDtype) -> Result<Self, DtypeError> {
assert!(
!(spec.scalar.is_none()
&& spec.size_modifier.is_none()
&& spec.size_modifiers.is_empty()
&& spec.signed_option.is_none()
&& spec.typedef_name.is_none()
&& !spec.is_const),
@@ -303,32 +298,40 @@ impl TryFrom<BaseDtype> for Dtype {
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::<BaseDtype>: {:?} is not a scalar type", t),
}
} else {
Self::default()
};
// Applies size modifier
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_modifier {
let number_of_modifier = spec.size_modifiers.len();
dtype = match number_of_modifier {
0 => dtype,
1 => match spec.size_modifiers[0] {
ast::TypeSpecifier::Short => Self::SHORT,
ast::TypeSpecifier::Long => Self::LONG,
_ => panic!(
"Dtype::try_from::<BaseDtype>: {:?} is not a size modifier",
size_modifier
"Dtype::try_from::<BaseDtype>: {:?} is not a size modifiers",
spec.size_modifiers
),
},
2 => {
if spec.size_modifiers[0] != ast::TypeSpecifier::Long
|| spec.size_modifiers[1] != ast::TypeSpecifier::Long
{
return Err(DtypeError::Misc {
message: "two or more size modifiers in declaration specifiers"
.to_string(),
});
}
Self::LONGLONG
}
}
_ => {
return Err(DtypeError::Misc {
message: "two or more size modifiers in declaration specifiers".to_string(),
})
}
};
// Applies signedness.
if let Some(signed_option) = spec.signed_option {
@@ -693,8 +696,7 @@ impl Dtype {
let declarator_kind = &declarator.kind;
match &declarator_kind.node {
ast::DeclaratorKind::Abstract => panic!("ast::DeclaratorKind::Abstract is unsupported"),
ast::DeclaratorKind::Identifier(_) => Ok(self),
ast::DeclaratorKind::Abstract | ast::DeclaratorKind::Identifier(_) => Ok(self),
ast::DeclaratorKind::Declarator(declarator) => {
self.with_ast_declarator(&declarator.node)
}

View File

@@ -89,6 +89,14 @@ impl Value {
Self::Pointer { bid, offset, dtype }
}
#[inline]
fn array(inner_dtype: Dtype, values: Vec<Self>) -> Self {
Self::Array {
inner_dtype,
values,
}
}
#[inline]
fn get_int(self) -> Option<(u128, usize, bool)> {
if let Value::Int {
@@ -130,7 +138,12 @@ impl Value {
} => Self::int(u128::default(), *width, *is_signed),
ir::Dtype::Float { width, .. } => Self::float(f64::default(), *width),
ir::Dtype::Pointer { inner, .. } => Self::nullptr(inner.deref().clone()),
ir::Dtype::Array { .. } => panic!("array type does not have a default value"),
ir::Dtype::Array { inner, size } => {
let values = (0..*size)
.map(|_| Self::default_from_dtype(inner))
.collect();
Self::array(inner.deref().clone(), values)
}
ir::Dtype::Function { .. } => panic!("function type does not have a default value"),
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
}
@@ -251,6 +264,7 @@ impl<'i> StackFrame<'i> {
mod calculator {
use super::Value;
use crate::ir::*;
use lang_c::ast;
// TODO: change to template function in the future
@@ -278,53 +292,126 @@ mod calculator {
assert_eq!(lhs_w, rhs_w);
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)),
let result = match op {
// TODO: explain why plus & minus do not need to consider `is_signed'
ast::BinaryOperator::Plus => (lhs as i128 + rhs as i128) as u128,
ast::BinaryOperator::Minus => (lhs as i128 - rhs as i128) as u128,
ast::BinaryOperator::Multiply => {
if lhs_s {
(lhs as i128 * rhs as i128) as u128
} else {
lhs * rhs
}
}
ast::BinaryOperator::Divide => {
if lhs_s {
(lhs as i128 / rhs as i128) as u128
} else {
lhs / rhs
}
}
ast::BinaryOperator::Modulo => {
if lhs_s {
(lhs as i128 % rhs as i128) as u128
} else {
lhs % rhs
}
}
ast::BinaryOperator::ShiftLeft => {
let rhs = if lhs_s {
let rhs = rhs as i128;
assert!(rhs >= 0);
assert!(rhs < (lhs_w as i128));
rhs as u128
} else {
assert!(rhs < (lhs_w as u128));
rhs
};
lhs << rhs
}
ast::BinaryOperator::ShiftRight => {
if lhs_s {
// arithmetic shift right
let rhs = rhs as i128;
assert!(rhs >= 0);
assert!(rhs < (lhs_w as i128));
((lhs as i128) >> rhs) as u128
} else {
// logical shift right
assert!(rhs < (lhs_w as u128));
let bit_mask = (1u128 << lhs_w as u128) - 1;
let lhs = lhs & bit_mask;
lhs >> rhs
}
}
ast::BinaryOperator::BitwiseAnd => lhs & rhs,
ast::BinaryOperator::BitwiseXor => lhs ^ rhs,
ast::BinaryOperator::BitwiseOr => lhs | rhs,
ast::BinaryOperator::Equals => {
let result = if lhs == rhs { 1 } else { 0 };
Ok(Value::int(result, 1, false))
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::NotEquals => {
let result = if lhs != rhs { 1 } else { 0 };
Ok(Value::int(result, 1, false))
return 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, false))
let condition = if lhs_s {
(lhs as i128) < (rhs as i128)
} else {
lhs < rhs
};
let result = if condition { 1 } else { 0 };
return 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, false))
let condition = if lhs_s {
(lhs as i128) > (rhs as i128)
} else {
lhs > rhs
};
let result = if condition { 1 } else { 0 };
return 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, false))
let condition = if lhs_s {
(lhs as i128) <= (rhs as i128)
} else {
lhs <= rhs
};
let result = if condition { 1 } else { 0 };
return 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, false))
}
ast::BinaryOperator::LogicalAnd => {
assert!(lhs < 2);
assert!(rhs < 2);
let result = lhs | rhs;
Ok(Value::int(result, 1, lhs_s))
let condition = if lhs_s {
(lhs as i128) >= (rhs as i128)
} else {
lhs >= rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
_ => todo!(
"calculate_binary_operator_expression: not supported operator {:?}",
op
),
}
};
let result = if lhs_s {
sign_extension(result, lhs_w as u128)
} else {
trim_unnecessary_bits(result, lhs_w as u128)
};
Ok(Value::int(result, lhs_w, lhs_s))
}
_ => todo!(),
(op, lhs, rhs) => todo!(
"calculate_binary_operator_expression: not supported case for {:?} {:?} {:?}",
op,
lhs,
rhs
),
}
}
@@ -350,8 +437,14 @@ mod calculator {
is_signed,
},
) => {
assert!(is_signed);
let result = -(value as i128);
// TODO: check what is exact behavior of appling minus to unsigned value
let result = if is_signed {
(-(value as i128)) as u128
} else {
let result = (-(value as i128)) as u128;
let bit_mask = (1u128 << (width as u128)) - 1;
result & bit_mask
};
Ok(Value::int(result as u128, width, is_signed))
}
(
@@ -367,27 +460,95 @@ mod calculator {
let result = if value == 0 { 1 } else { 0 };
Ok(Value::int(result, width, is_signed))
}
_ => todo!(),
(op, operand) => todo!(
"calculate_unary_operator_expression: not supported case for {:?} {:?}",
op,
operand,
),
}
}
pub fn calculate_typecast(value: Value, dtype: crate::ir::Dtype) -> Result<Value, ()> {
pub fn calculate_typecast(value: Value, dtype: Dtype) -> Result<Value, ()> {
if value.dtype() == dtype {
return Ok(value);
}
match (value, dtype) {
(Value::Undef { .. }, _) => Err(()),
// TODO: distinguish zero/signed extension in the future
// TODO: consider truncate in the future
(
Value::Int { value, .. },
crate::ir::Dtype::Int {
Value::Int { value, width, .. },
Dtype::Int {
width: target_width,
is_signed: target_signed,
..
},
) => {
// Other cases
let result = if target_signed {
if width >= target_width {
// TODO: explain the logic in the future
let value = trim_unnecessary_bits(value, target_width as u128);
sign_extension(value, target_width as u128)
} else {
value
}
} else {
trim_unnecessary_bits(value, target_width as u128)
};
Ok(Value::int(result, target_width, target_signed))
}
(
Value::Int {
value, is_signed, ..
},
Dtype::Float { width, .. },
) => {
let casted_value = if is_signed {
value as i128 as f64
} else {
value as f64
};
Ok(Value::float(casted_value, width))
}
(
Value::Float { value, .. },
Dtype::Int {
width, is_signed, ..
},
) => Ok(Value::int(value, width, is_signed)),
(Value::Float { value, .. }, crate::ir::Dtype::Float { width, .. }) => {
) => {
let casted_value = if is_signed {
value as i128 as u128
} else {
value as u128
};
Ok(Value::int(casted_value, width, is_signed))
}
(Value::Float { value, .. }, Dtype::Float { width, .. }) => {
Ok(Value::float(value, width))
}
(value, dtype) => todo!("calculate_typecast ({:?}) {:?}", dtype, value),
(value, dtype) => todo!("calculate_typecast ({:?}) {:?}", value, dtype),
}
}
#[inline]
fn sign_extension(value: u128, width: u128) -> u128 {
let base = 1u128 << (width - 1);
if value >= base {
let bit_mask = -1i128 << (width as i128);
value | bit_mask as u128
} else {
value
}
}
#[inline]
fn trim_unnecessary_bits(value: u128, width: u128) -> u128 {
let bit_mask = (1u128 << width) - 1;
value & bit_mask
}
}
// TODO
@@ -397,7 +558,7 @@ enum Byte {
Undef,
Concrete(u8),
Pointer {
bid: usize,
bid: Option<usize>,
offset: isize,
index: usize,
},
@@ -415,7 +576,7 @@ impl Byte {
}
#[inline]
fn pointer(bid: usize, offset: isize, index: usize) -> Self {
fn pointer(bid: Option<usize>, offset: isize, index: usize) -> Self {
Self::Pointer { bid, offset, index }
}
@@ -427,7 +588,7 @@ impl Byte {
}
}
fn get_pointer(&self) -> Option<(usize, isize, usize)> {
fn get_pointer(&self) -> Option<(Option<usize>, isize, usize)> {
if let Self::Pointer { bid, offset, index } = self {
Some((*bid, *offset, *index))
} else {
@@ -482,6 +643,7 @@ impl Byte {
Ok(Value::int(value, *width, *is_signed))
}
ir::Dtype::Float { width, .. } => {
let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE;
let value = some_or!(
bytes
.by_ref()
@@ -491,7 +653,7 @@ impl Byte {
return Ok(Value::undef(dtype.clone()))
);
let value = Self::bytes_to_u128(&value, false);
let value = if *width == Dtype::SIZE_OF_FLOAT {
let value = if size == Dtype::SIZE_OF_FLOAT {
f32::from_bits(value as u32) as f64
} else {
f64::from_bits(value as u64)
@@ -519,7 +681,7 @@ impl Byte {
{
Value::undef(inner.deref().clone())
} else {
Value::pointer(Some(*bid), *offset, inner.deref().clone())
Value::pointer(*bid, *offset, inner.deref().clone())
},
)
}
@@ -556,19 +718,19 @@ impl Byte {
}
Value::Float { value, width } => {
let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE;
let value: u128 = match size {
let value_bits: 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)
Self::u128_to_bytes(value_bits, size)
.iter()
.map(|b| Self::concrete(*b))
.collect::<Vec<_>>()
}
Value::Pointer { bid, offset, .. } => (0..Dtype::SIZE_OF_POINTER)
.map(|i| Self::pointer(bid.unwrap(), *offset, i))
.map(|i| Self::pointer(*bid, *offset, i))
.collect(),
Value::Array {
inner_dtype,
@@ -698,9 +860,29 @@ impl<'i> State<'i> {
Value::default_from_dtype(&dtype)
};
// Type cast
let value =
calculator::calculate_typecast(value, dtype.clone()).map_err(|_| {
InterpreterError::Misc {
func_name: self.stack_frame.func_name.clone(),
pc: self.stack_frame.pc,
msg: "calculate_typecast when initialize global variable"
.into(),
}
})?;
self.memory.store(bid, 0, &value);
}
ir::Dtype::Array { .. } => {
let value = if let Some(_list_init) = initializer {
// TODO: is type cast required?
todo!("Initializer::List is needed")
} else {
Value::default_from_dtype(&dtype)
};
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"),
},

View File

@@ -496,28 +496,30 @@ impl TryFrom<&ast::Constant> for Constant {
fn try_from(constant: &ast::Constant) -> Result<Self, Self::Error> {
match constant {
ast::Constant::Integer(integer) => {
let is_signed = !integer.suffix.unsigned;
let dtype = match integer.suffix.size {
ast::IntegerSize::Int => Dtype::INT,
ast::IntegerSize::Long => Dtype::LONG,
ast::IntegerSize::LongLong => Dtype::LONGLONG,
}
.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 = u128::from_str_radix(integer.number.deref(), pat).unwrap();
let value = if is_signed {
i128::from_str_radix(integer.number.deref(), pat).unwrap() as u128
} else {
u128::from_str_radix(integer.number.deref(), pat).unwrap()
let is_signed = !integer.suffix.unsigned && {
// Even if `suffix` represents `signed`, integer literal cannot be translated
// to minus value. For this reason, if the sign bit is on, dtype automatically
// transformed to `unsigned`. Let's say integer literal is `0xFFFFFFFF`,
// it translated to unsigned integer even though it has no `U` suffix.
let width = dtype.get_int_width().unwrap();
let threshold = 1u128 << (width as u128 - 1);
value < threshold
};
Ok(Self::int(value, dtype))
Ok(Self::int(value, dtype.set_signed(is_signed)))
}
ast::Constant::Float(float) => {
let pat = match float.base {
@@ -529,18 +531,34 @@ impl TryFrom<&ast::Constant> for Constant {
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
let value = if pat == Self::DECIMAL {
float.number.parse::<f32>().unwrap() as f64
} else {
parse_hexf32(float.number.deref(), false).unwrap() as f64
let value = match pat {
Self::DECIMAL => float.number.parse::<f32>().unwrap() as f64,
Self::HEXADECIMAL => {
let mut hex_number = "0x".to_string();
hex_number.push_str(float.number.deref());
parse_hexf32(&hex_number, true).unwrap() as f64
}
_ => panic!(
"Constant::try_from::<&ast::Constant>: \
{:?} is not a pattern of `pat`",
pat
),
};
(Dtype::FLOAT, value)
}
ast::FloatFormat::Double => {
let value = if pat == Self::DECIMAL {
float.number.parse::<f64>().unwrap()
} else {
parse_hexf64(float.number.deref(), false).unwrap()
let value = match pat {
Self::DECIMAL => float.number.parse::<f64>().unwrap(),
Self::HEXADECIMAL => {
let mut hex_number = "0x".to_string();
hex_number.push_str(float.number.deref());
parse_hexf64(&hex_number, true).unwrap()
}
_ => panic!(
"Constant::try_from::<&ast::Constant>: \
{:?} is not a pattern of `pat`",
pat
),
};
(Dtype::DOUBLE, value)
}
@@ -568,10 +586,23 @@ impl TryFrom<&ast::Expression> for Constant {
type Error = ();
fn try_from(expr: &ast::Expression) -> Result<Self, Self::Error> {
if let ast::Expression::Constant(constant) = expr {
Self::try_from(&constant.node)
} else {
Err(())
match expr {
ast::Expression::Constant(constant) => Self::try_from(&constant.node),
ast::Expression::UnaryOperator(unary) => {
let constant = Self::try_from(&unary.node.operand.node)?;
// When an IR is generated, there are cases where some expressions must be
// interpreted unconditionally as compile-time constant value. In this case,
// we need to translate also the expression applied `minus` unary operator
// to compile-time constant value directly.
// Let's say expression is `case -1: { .. }`,
// `-1` must be interpreted to compile-time constant value.
match &unary.node.operator.node {
ast::UnaryOperator::Minus => Ok(constant.minus()),
ast::UnaryOperator::Plus => Ok(constant),
_ => Err(()),
}
}
_ => Err(()),
}
}
}
@@ -655,6 +686,33 @@ impl Constant {
}
}
#[inline]
fn minus(self) -> Self {
match self {
Self::Int {
value,
width,
is_signed,
} => {
assert!(is_signed);
let minus_value = -(value as i128);
Self::Int {
value: minus_value as u128,
width,
is_signed,
}
}
Self::Float { mut value, width } => {
*value.as_mut() *= -1.0f64;
Self::Float { value, width }
}
_ => panic!(
"constant value generated by `Constant::from_ast_expression` \
must be `Constant{{Int, Float}}`"
),
}
}
pub fn is_undef(&self) -> bool {
if let Self::Undef { .. } = self {
true
@@ -669,7 +727,17 @@ impl fmt::Display for Constant {
match self {
Self::Undef { .. } => write!(f, "undef"),
Self::Unit => write!(f, "unit"),
Self::Int { value, .. } => write!(f, "{}", value),
Self::Int {
value, is_signed, ..
} => write!(
f,
"{}",
if *is_signed {
(*value as i128).to_string()
} else {
value.to_string()
}
),
Self::Float { value, .. } => write!(f, "{}", value),
Self::GlobalVariable { name, .. } => write!(f, "%{}", name),
}

View File

@@ -37,8 +37,18 @@ impl WriteLine for (&String, &Declaration) {
let decl = self.1;
match decl {
Declaration::Variable { dtype, .. } => {
writeln!(write, "{} = {}", name, dtype)?;
Declaration::Variable { dtype, initializer } => {
writeln!(
write,
"{} = {} {}",
name,
if let Some(init) = initializer {
init.to_string()
} else {
"default".to_string()
},
dtype
)?;
}
Declaration::Function {
signature,
@@ -189,19 +199,28 @@ impl WriteString for Operand {
impl WriteOp for ast::BinaryOperator {
fn write_operation(&self) -> String {
// TODO: represent signed & unsigned if necessary
match self {
Self::Multiply => "mul",
Self::Divide => "div",
Self::Modulo => "mod",
Self::Plus => "add",
Self::Minus => "sub",
Self::ShiftLeft => "shl",
Self::ShiftRight => "shr",
Self::Equals => "cmp eq",
Self::NotEquals => "cmp ne",
Self::Less => "cmp lt",
Self::LessOrEqual => "cmp le",
Self::Greater => "cmp gt",
Self::GreaterOrEqual => "cmp ge",
_ => todo!(),
Self::BitwiseAnd => "and",
Self::BitwiseXor => "xor",
Self::BitwiseOr => "or",
_ => todo!(
"ast::BinaryOperator::WriteOp: write operation for {:?} is needed",
self
),
}
.to_string()
}
@@ -210,8 +229,13 @@ impl WriteOp for ast::BinaryOperator {
impl WriteOp for ast::UnaryOperator {
fn write_operation(&self) -> String {
match self {
Self::Plus => "plus",
Self::Minus => "minus",
_ => todo!(),
Self::Negate => "negate",
_ => todo!(
"ast::UnaryOperator::WriteOp: write operation for {:?} is needed",
self
),
}
.to_string()
}

View File

@@ -3,6 +3,7 @@ use std::fs::{self, File};
use std::path::Path;
use std::process::{Command, Stdio};
use tempfile::tempdir;
use wait_timeout::ChildExt;
use crate::*;
@@ -45,18 +46,28 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
}
// Execute compiled executable
let status = Command::new(fs::canonicalize(bin_path.clone()).unwrap())
.status()
.expect("failed to execute the compiled executable")
.code()
.expect("failed to return an exit code");
let mut child = Command::new(fs::canonicalize(bin_path.clone()).unwrap())
.spawn()
.expect("failed to execute the compiled executable");
// Remove compiled executable
Command::new("rm")
.arg(bin_path)
.status()
.expect("failed to remove compiled executable");
let status = some_or!(
child
.wait_timeout_ms(500)
.expect("failed to obtain exit status from child process"),
{
println!("timeout occurs");
child.kill().unwrap();
child.wait().unwrap();
return;
}
);
let status = some_or!(status.code(), return);
let ir = match Irgen::default().translate(unit) {
Ok(ir) => ir,
Err(irgen_error) => panic!("{}", irgen_error),
@@ -73,5 +84,6 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
panic!("non-integer value occurs")
};
println!("kecc: {:?}\ngcc: {:?}", result, status);
assert_eq!(result, ir::Value::int(status as u128, 32, true));
}

View File

@@ -23,7 +23,9 @@ REPLACE_DICT = {
"long __undefined;": "",
"return 0;": "return crc32_context % 128;",
r"__attribute__\s*\(\(.*\)\)": "",
"_Float128": "long double",
"_Float128": "double",
"long double": "double",
"(\+[0-9^FL]*)L": r"\1",
"union": "struct",
r"enum[\w\s]*\{[^\}]*\};": "",
r"typedef enum[\w\s]*\{[^;]*;[\s_A-Z]*;": "",
@@ -38,21 +40,23 @@ REPLACE_DICT = {
r"[^\n]*_IO_2_1_[^;]*;": "", # extern을 지우면서 생긴 size를 알 수 없는 struct 삭제
r"__asm\s*\([^\)]*\)": "", # asm extension in mac
r"__asm__\s*\([^\)]*\)": "", # asm extension in linux
"typedef __builtin_va_list __gnuc_va_list;": "",
"typedef __gnuc_va_list va_list;": "",
# 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",
# "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"
@@ -229,7 +233,7 @@ def fuzz(tests_dir, fuzz_arg, num_iter):
try:
args = ["cargo", "run", "--release", "--bin", "fuzz", "--", fuzz_arg, os.path.join(tests_dir, "test_polished.c")]
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tests_dir)
proc.communicate(timeout=10)
proc.communicate(timeout=60)
if proc.returncode != 0:
raise Exception("Test `{}` failed with exit code {}.".format(" ".join(args), proc.returncode))
except subprocess.TimeoutExpired as e: