mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-16 07:28:52 +00:00
Update IR's helper functions
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -160,6 +160,7 @@ dependencies = [
|
|||||||
"lang-c 0.8.0 (git+https://github.com/kaist-cp/lang-c)",
|
"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)",
|
"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)",
|
"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]]
|
[[package]]
|
||||||
@@ -367,6 +368,14 @@ name = "vec_map"
|
|||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.9.0+wasi-snapshot-preview1"
|
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.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 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 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 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 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"
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|||||||
@@ -32,3 +32,4 @@ failure = "0.1.7"
|
|||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
ordered-float = "1.0"
|
ordered-float = "1.0"
|
||||||
hexf = "0.1.0"
|
hexf = "0.1.0"
|
||||||
|
wait-timeout = "0.2.0"
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ built by the test script. For more information, we refer to the
|
|||||||
### Reduce
|
### Reduce
|
||||||
|
|
||||||
When the fuzzer finds a buggy input program for your compiler, it is highly likely that the input
|
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.
|
much as possible.
|
||||||
|
|
||||||
Suppose `tests/test_polished.c` is the buggy input program. Then the following script reduces the
|
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
|
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
|
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.
|
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
10
examples/array4.c
Normal 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
13
examples/bitwise.c
Normal 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;
|
||||||
|
}
|
||||||
5
examples/integer_literal.c
Normal file
5
examples/integer_literal.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
int main() {
|
||||||
|
short temp = 0;
|
||||||
|
unsigned int temp2 = 4294967163;
|
||||||
|
return (char)(temp ^ temp2) == 123;
|
||||||
|
}
|
||||||
5
examples/integer_literal2.c
Normal file
5
examples/integer_literal2.c
Normal 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
16
examples/logical_op.c
Normal 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;
|
||||||
|
}
|
||||||
8
examples/minus_constant.c
Normal file
8
examples/minus_constant.c
Normal 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
7
examples/shift.c
Normal 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;
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
int main() {
|
int main() {
|
||||||
long int l = 1;
|
long int l = 1;
|
||||||
long l2 = 2;
|
long l2 = 2;
|
||||||
short int s = 3;
|
long long l3 = 3;
|
||||||
short s2 = 4;
|
short int s = 4;
|
||||||
int i = 5;
|
short s2 = 5;
|
||||||
char c = 6;
|
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
5
examples/typecast.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
char temp = 0x00L;
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return (temp = 0xEF36L) >= (2L);
|
||||||
|
}
|
||||||
4
examples/unary.c
Normal file
4
examples/unary.c
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
int main() {
|
||||||
|
unsigned char temp = 0x00L;
|
||||||
|
return 1 > (--temp);
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ pub trait HasDtype {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct BaseDtype {
|
struct BaseDtype {
|
||||||
scalar: Option<ast::TypeSpecifier>,
|
scalar: Option<ast::TypeSpecifier>,
|
||||||
size_modifier: Option<ast::TypeSpecifier>,
|
size_modifiers: Vec<ast::TypeSpecifier>,
|
||||||
signed_option: Option<ast::TypeSpecifier>,
|
signed_option: Option<ast::TypeSpecifier>,
|
||||||
typedef_name: Option<String>,
|
typedef_name: Option<String>,
|
||||||
is_const: bool,
|
is_const: bool,
|
||||||
@@ -129,12 +129,7 @@ impl BaseDtype {
|
|||||||
self.scalar = Some(type_specifier.clone());
|
self.scalar = Some(type_specifier.clone());
|
||||||
}
|
}
|
||||||
ast::TypeSpecifier::Short | ast::TypeSpecifier::Long => {
|
ast::TypeSpecifier::Short | ast::TypeSpecifier::Long => {
|
||||||
if self.size_modifier.is_some() {
|
self.size_modifiers.push(type_specifier.clone())
|
||||||
return Err(DtypeError::Misc {
|
|
||||||
message: "two or more size modifiers in declaration specifiers".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.size_modifier = Some(type_specifier.clone());
|
|
||||||
}
|
}
|
||||||
ast::TypeSpecifier::TypedefName(identifier) => {
|
ast::TypeSpecifier::TypedefName(identifier) => {
|
||||||
if self.typedef_name.is_some() {
|
if self.typedef_name.is_some() {
|
||||||
@@ -277,7 +272,7 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
fn try_from(spec: BaseDtype) -> Result<Self, DtypeError> {
|
fn try_from(spec: BaseDtype) -> Result<Self, DtypeError> {
|
||||||
assert!(
|
assert!(
|
||||||
!(spec.scalar.is_none()
|
!(spec.scalar.is_none()
|
||||||
&& spec.size_modifier.is_none()
|
&& spec.size_modifiers.is_empty()
|
||||||
&& spec.signed_option.is_none()
|
&& spec.signed_option.is_none()
|
||||||
&& spec.typedef_name.is_none()
|
&& spec.typedef_name.is_none()
|
||||||
&& !spec.is_const),
|
&& !spec.is_const),
|
||||||
@@ -303,32 +298,40 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
ast::TypeSpecifier::Int => Self::INT,
|
ast::TypeSpecifier::Int => Self::INT,
|
||||||
ast::TypeSpecifier::Float => Self::FLOAT,
|
ast::TypeSpecifier::Float => Self::FLOAT,
|
||||||
ast::TypeSpecifier::Double => Self::DOUBLE,
|
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),
|
_ => panic!("Dtype::try_from::<BaseDtype>: {:?} is not a scalar type", t),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Self::default()
|
Self::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Applies size modifier
|
let number_of_modifier = spec.size_modifiers.len();
|
||||||
if let Some(size_modifier) = spec.size_modifier {
|
dtype = match number_of_modifier {
|
||||||
if dtype != Self::INT {
|
0 => dtype,
|
||||||
return Err(DtypeError::Misc {
|
1 => match spec.size_modifiers[0] {
|
||||||
message: "size modifier can only be used with `int`".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dtype = match size_modifier {
|
|
||||||
ast::TypeSpecifier::Short => Self::SHORT,
|
ast::TypeSpecifier::Short => Self::SHORT,
|
||||||
ast::TypeSpecifier::Long => Self::LONG,
|
ast::TypeSpecifier::Long => Self::LONG,
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Dtype::try_from::<BaseDtype>: {:?} is not a size modifier",
|
"Dtype::try_from::<BaseDtype>: {:?} is not a size modifiers",
|
||||||
size_modifier
|
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.
|
// Applies signedness.
|
||||||
if let Some(signed_option) = spec.signed_option {
|
if let Some(signed_option) = spec.signed_option {
|
||||||
@@ -693,8 +696,7 @@ impl Dtype {
|
|||||||
|
|
||||||
let declarator_kind = &declarator.kind;
|
let declarator_kind = &declarator.kind;
|
||||||
match &declarator_kind.node {
|
match &declarator_kind.node {
|
||||||
ast::DeclaratorKind::Abstract => panic!("ast::DeclaratorKind::Abstract is unsupported"),
|
ast::DeclaratorKind::Abstract | ast::DeclaratorKind::Identifier(_) => Ok(self),
|
||||||
ast::DeclaratorKind::Identifier(_) => Ok(self),
|
|
||||||
ast::DeclaratorKind::Declarator(declarator) => {
|
ast::DeclaratorKind::Declarator(declarator) => {
|
||||||
self.with_ast_declarator(&declarator.node)
|
self.with_ast_declarator(&declarator.node)
|
||||||
}
|
}
|
||||||
|
|||||||
276
src/ir/interp.rs
276
src/ir/interp.rs
@@ -89,6 +89,14 @@ impl Value {
|
|||||||
Self::Pointer { bid, offset, dtype }
|
Self::Pointer { bid, offset, dtype }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn array(inner_dtype: Dtype, values: Vec<Self>) -> Self {
|
||||||
|
Self::Array {
|
||||||
|
inner_dtype,
|
||||||
|
values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_int(self) -> Option<(u128, usize, bool)> {
|
fn get_int(self) -> Option<(u128, usize, bool)> {
|
||||||
if let Value::Int {
|
if let Value::Int {
|
||||||
@@ -130,7 +138,12 @@ impl Value {
|
|||||||
} => Self::int(u128::default(), *width, *is_signed),
|
} => Self::int(u128::default(), *width, *is_signed),
|
||||||
ir::Dtype::Float { width, .. } => Self::float(f64::default(), *width),
|
ir::Dtype::Float { width, .. } => Self::float(f64::default(), *width),
|
||||||
ir::Dtype::Pointer { inner, .. } => Self::nullptr(inner.deref().clone()),
|
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::Function { .. } => panic!("function type does not have a default value"),
|
||||||
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||||
}
|
}
|
||||||
@@ -251,6 +264,7 @@ impl<'i> StackFrame<'i> {
|
|||||||
|
|
||||||
mod calculator {
|
mod calculator {
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
use crate::ir::*;
|
||||||
use lang_c::ast;
|
use lang_c::ast;
|
||||||
|
|
||||||
// TODO: change to template function in the future
|
// TODO: change to template function in the future
|
||||||
@@ -278,53 +292,126 @@ mod calculator {
|
|||||||
assert_eq!(lhs_w, rhs_w);
|
assert_eq!(lhs_w, rhs_w);
|
||||||
assert_eq!(lhs_s, rhs_s);
|
assert_eq!(lhs_s, rhs_s);
|
||||||
|
|
||||||
match op {
|
let result = match op {
|
||||||
// TODO: consider signed value in the future
|
// TODO: explain why plus & minus do not need to consider `is_signed'
|
||||||
ast::BinaryOperator::Plus => Ok(Value::int(lhs + rhs, lhs_w, lhs_s)),
|
ast::BinaryOperator::Plus => (lhs as i128 + rhs as i128) as u128,
|
||||||
ast::BinaryOperator::Minus => Ok(Value::int(lhs - rhs, lhs_w, lhs_s)),
|
ast::BinaryOperator::Minus => (lhs as i128 - rhs as i128) as u128,
|
||||||
ast::BinaryOperator::Multiply => Ok(Value::int(lhs * rhs, lhs_w, lhs_s)),
|
ast::BinaryOperator::Multiply => {
|
||||||
ast::BinaryOperator::Modulo => Ok(Value::int(lhs % rhs, lhs_w, lhs_s)),
|
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 => {
|
ast::BinaryOperator::Equals => {
|
||||||
let result = if lhs == rhs { 1 } else { 0 };
|
let result = if lhs == rhs { 1 } else { 0 };
|
||||||
Ok(Value::int(result, 1, false))
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::NotEquals => {
|
ast::BinaryOperator::NotEquals => {
|
||||||
let result = if lhs != rhs { 1 } else { 0 };
|
let result = if lhs != rhs { 1 } else { 0 };
|
||||||
Ok(Value::int(result, 1, false))
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::Less => {
|
ast::BinaryOperator::Less => {
|
||||||
// TODO: consider signed option
|
let condition = if lhs_s {
|
||||||
let result = if lhs < rhs { 1 } else { 0 };
|
(lhs as i128) < (rhs as i128)
|
||||||
Ok(Value::int(result, 1, false))
|
} else {
|
||||||
|
lhs < rhs
|
||||||
|
};
|
||||||
|
let result = if condition { 1 } else { 0 };
|
||||||
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::Greater => {
|
ast::BinaryOperator::Greater => {
|
||||||
// TODO: consider signed option
|
let condition = if lhs_s {
|
||||||
let result = if lhs > rhs { 1 } else { 0 };
|
(lhs as i128) > (rhs as i128)
|
||||||
Ok(Value::int(result, 1, false))
|
} else {
|
||||||
|
lhs > rhs
|
||||||
|
};
|
||||||
|
let result = if condition { 1 } else { 0 };
|
||||||
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::LessOrEqual => {
|
ast::BinaryOperator::LessOrEqual => {
|
||||||
// TODO: consider signed option
|
let condition = if lhs_s {
|
||||||
let result = if lhs <= rhs { 1 } else { 0 };
|
(lhs as i128) <= (rhs as i128)
|
||||||
Ok(Value::int(result, 1, false))
|
} else {
|
||||||
|
lhs <= rhs
|
||||||
|
};
|
||||||
|
let result = if condition { 1 } else { 0 };
|
||||||
|
return Ok(Value::int(result, 1, false));
|
||||||
}
|
}
|
||||||
ast::BinaryOperator::GreaterOrEqual => {
|
ast::BinaryOperator::GreaterOrEqual => {
|
||||||
// TODO: consider signed option
|
let condition = if lhs_s {
|
||||||
let result = if lhs >= rhs { 1 } else { 0 };
|
(lhs as i128) >= (rhs as i128)
|
||||||
Ok(Value::int(result, 1, false))
|
} else {
|
||||||
}
|
lhs >= rhs
|
||||||
ast::BinaryOperator::LogicalAnd => {
|
};
|
||||||
assert!(lhs < 2);
|
let result = if condition { 1 } else { 0 };
|
||||||
assert!(rhs < 2);
|
return Ok(Value::int(result, 1, false));
|
||||||
let result = lhs | rhs;
|
|
||||||
Ok(Value::int(result, 1, lhs_s))
|
|
||||||
}
|
}
|
||||||
_ => todo!(
|
_ => todo!(
|
||||||
"calculate_binary_operator_expression: not supported operator {:?}",
|
"calculate_binary_operator_expression: not supported operator {:?}",
|
||||||
op
|
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,
|
is_signed,
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
assert!(is_signed);
|
// TODO: check what is exact behavior of appling minus to unsigned value
|
||||||
let result = -(value as i128);
|
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))
|
Ok(Value::int(result as u128, width, is_signed))
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
@@ -367,27 +460,95 @@ mod calculator {
|
|||||||
let result = if value == 0 { 1 } else { 0 };
|
let result = if value == 0 { 1 } else { 0 };
|
||||||
Ok(Value::int(result, width, is_signed))
|
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) {
|
match (value, dtype) {
|
||||||
(Value::Undef { .. }, _) => Err(()),
|
(Value::Undef { .. }, _) => Err(()),
|
||||||
// TODO: distinguish zero/signed extension in the future
|
// TODO: distinguish zero/signed extension in the future
|
||||||
// TODO: consider truncate in the future
|
// TODO: consider truncate in the future
|
||||||
(
|
(
|
||||||
Value::Int { value, .. },
|
Value::Int { value, width, .. },
|
||||||
crate::ir::Dtype::Int {
|
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, ..
|
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))
|
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
|
// TODO
|
||||||
@@ -397,7 +558,7 @@ enum Byte {
|
|||||||
Undef,
|
Undef,
|
||||||
Concrete(u8),
|
Concrete(u8),
|
||||||
Pointer {
|
Pointer {
|
||||||
bid: usize,
|
bid: Option<usize>,
|
||||||
offset: isize,
|
offset: isize,
|
||||||
index: usize,
|
index: usize,
|
||||||
},
|
},
|
||||||
@@ -415,7 +576,7 @@ impl Byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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 }
|
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 {
|
if let Self::Pointer { bid, offset, index } = self {
|
||||||
Some((*bid, *offset, *index))
|
Some((*bid, *offset, *index))
|
||||||
} else {
|
} else {
|
||||||
@@ -482,6 +643,7 @@ impl Byte {
|
|||||||
Ok(Value::int(value, *width, *is_signed))
|
Ok(Value::int(value, *width, *is_signed))
|
||||||
}
|
}
|
||||||
ir::Dtype::Float { width, .. } => {
|
ir::Dtype::Float { width, .. } => {
|
||||||
|
let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE;
|
||||||
let value = some_or!(
|
let value = some_or!(
|
||||||
bytes
|
bytes
|
||||||
.by_ref()
|
.by_ref()
|
||||||
@@ -491,7 +653,7 @@ impl Byte {
|
|||||||
return Ok(Value::undef(dtype.clone()))
|
return Ok(Value::undef(dtype.clone()))
|
||||||
);
|
);
|
||||||
let value = Self::bytes_to_u128(&value, false);
|
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
|
f32::from_bits(value as u32) as f64
|
||||||
} else {
|
} else {
|
||||||
f64::from_bits(value as u64)
|
f64::from_bits(value as u64)
|
||||||
@@ -519,7 +681,7 @@ impl Byte {
|
|||||||
{
|
{
|
||||||
Value::undef(inner.deref().clone())
|
Value::undef(inner.deref().clone())
|
||||||
} else {
|
} 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 } => {
|
Value::Float { value, width } => {
|
||||||
let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE;
|
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_FLOAT => (*value as f32).to_bits() as u128,
|
||||||
Dtype::SIZE_OF_DOUBLE => (*value as f64).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),
|
_ => 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()
|
.iter()
|
||||||
.map(|b| Self::concrete(*b))
|
.map(|b| Self::concrete(*b))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
Value::Pointer { bid, offset, .. } => (0..Dtype::SIZE_OF_POINTER)
|
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(),
|
.collect(),
|
||||||
Value::Array {
|
Value::Array {
|
||||||
inner_dtype,
|
inner_dtype,
|
||||||
@@ -698,9 +860,29 @@ impl<'i> State<'i> {
|
|||||||
Value::default_from_dtype(&dtype)
|
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);
|
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::Function { .. } => panic!("function variable does not exist"),
|
||||||
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||||
},
|
},
|
||||||
|
|||||||
112
src/ir/mod.rs
112
src/ir/mod.rs
@@ -496,28 +496,30 @@ impl TryFrom<&ast::Constant> for Constant {
|
|||||||
fn try_from(constant: &ast::Constant) -> Result<Self, Self::Error> {
|
fn try_from(constant: &ast::Constant) -> Result<Self, Self::Error> {
|
||||||
match constant {
|
match constant {
|
||||||
ast::Constant::Integer(integer) => {
|
ast::Constant::Integer(integer) => {
|
||||||
let is_signed = !integer.suffix.unsigned;
|
|
||||||
|
|
||||||
let dtype = match integer.suffix.size {
|
let dtype = match integer.suffix.size {
|
||||||
ast::IntegerSize::Int => Dtype::INT,
|
ast::IntegerSize::Int => Dtype::INT,
|
||||||
ast::IntegerSize::Long => Dtype::LONG,
|
ast::IntegerSize::Long => Dtype::LONG,
|
||||||
ast::IntegerSize::LongLong => Dtype::LONGLONG,
|
ast::IntegerSize::LongLong => Dtype::LONGLONG,
|
||||||
}
|
};
|
||||||
.set_signed(is_signed);
|
|
||||||
|
|
||||||
let pat = match integer.base {
|
let pat = match integer.base {
|
||||||
ast::IntegerBase::Decimal => Self::DECIMAL,
|
ast::IntegerBase::Decimal => Self::DECIMAL,
|
||||||
ast::IntegerBase::Octal => Self::OCTAL,
|
ast::IntegerBase::Octal => Self::OCTAL,
|
||||||
ast::IntegerBase::Hexadecimal => Self::HEXADECIMAL,
|
ast::IntegerBase::Hexadecimal => Self::HEXADECIMAL,
|
||||||
};
|
};
|
||||||
|
let value = u128::from_str_radix(integer.number.deref(), pat).unwrap();
|
||||||
|
|
||||||
let value = if is_signed {
|
let is_signed = !integer.suffix.unsigned && {
|
||||||
i128::from_str_radix(integer.number.deref(), pat).unwrap() as u128
|
// Even if `suffix` represents `signed`, integer literal cannot be translated
|
||||||
} else {
|
// to minus value. For this reason, if the sign bit is on, dtype automatically
|
||||||
u128::from_str_radix(integer.number.deref(), pat).unwrap()
|
// 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) => {
|
ast::Constant::Float(float) => {
|
||||||
let pat = match float.base {
|
let pat = match float.base {
|
||||||
@@ -529,18 +531,34 @@ impl TryFrom<&ast::Constant> for Constant {
|
|||||||
ast::FloatFormat::Float => {
|
ast::FloatFormat::Float => {
|
||||||
// Casting from an f32 to an f64 is perfect and lossless (f32 -> f64)
|
// 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
|
// https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#type-cast-expressions
|
||||||
let value = if pat == Self::DECIMAL {
|
let value = match pat {
|
||||||
float.number.parse::<f32>().unwrap() as f64
|
Self::DECIMAL => float.number.parse::<f32>().unwrap() as f64,
|
||||||
} else {
|
Self::HEXADECIMAL => {
|
||||||
parse_hexf32(float.number.deref(), false).unwrap() as f64
|
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)
|
(Dtype::FLOAT, value)
|
||||||
}
|
}
|
||||||
ast::FloatFormat::Double => {
|
ast::FloatFormat::Double => {
|
||||||
let value = if pat == Self::DECIMAL {
|
let value = match pat {
|
||||||
float.number.parse::<f64>().unwrap()
|
Self::DECIMAL => float.number.parse::<f64>().unwrap(),
|
||||||
} else {
|
Self::HEXADECIMAL => {
|
||||||
parse_hexf64(float.number.deref(), false).unwrap()
|
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)
|
(Dtype::DOUBLE, value)
|
||||||
}
|
}
|
||||||
@@ -568,10 +586,23 @@ impl TryFrom<&ast::Expression> for Constant {
|
|||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(expr: &ast::Expression) -> Result<Self, Self::Error> {
|
fn try_from(expr: &ast::Expression) -> Result<Self, Self::Error> {
|
||||||
if let ast::Expression::Constant(constant) = expr {
|
match expr {
|
||||||
Self::try_from(&constant.node)
|
ast::Expression::Constant(constant) => Self::try_from(&constant.node),
|
||||||
} else {
|
ast::Expression::UnaryOperator(unary) => {
|
||||||
Err(())
|
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 {
|
pub fn is_undef(&self) -> bool {
|
||||||
if let Self::Undef { .. } = self {
|
if let Self::Undef { .. } = self {
|
||||||
true
|
true
|
||||||
@@ -669,7 +727,17 @@ impl fmt::Display for Constant {
|
|||||||
match self {
|
match self {
|
||||||
Self::Undef { .. } => write!(f, "undef"),
|
Self::Undef { .. } => write!(f, "undef"),
|
||||||
Self::Unit => write!(f, "unit"),
|
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::Float { value, .. } => write!(f, "{}", value),
|
||||||
Self::GlobalVariable { name, .. } => write!(f, "%{}", name),
|
Self::GlobalVariable { name, .. } => write!(f, "%{}", name),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,18 @@ impl WriteLine for (&String, &Declaration) {
|
|||||||
let decl = self.1;
|
let decl = self.1;
|
||||||
|
|
||||||
match decl {
|
match decl {
|
||||||
Declaration::Variable { dtype, .. } => {
|
Declaration::Variable { dtype, initializer } => {
|
||||||
writeln!(write, "{} = {}", name, dtype)?;
|
writeln!(
|
||||||
|
write,
|
||||||
|
"{} = {} {}",
|
||||||
|
name,
|
||||||
|
if let Some(init) = initializer {
|
||||||
|
init.to_string()
|
||||||
|
} else {
|
||||||
|
"default".to_string()
|
||||||
|
},
|
||||||
|
dtype
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Declaration::Function {
|
Declaration::Function {
|
||||||
signature,
|
signature,
|
||||||
@@ -189,19 +199,28 @@ impl WriteString for Operand {
|
|||||||
|
|
||||||
impl WriteOp for ast::BinaryOperator {
|
impl WriteOp for ast::BinaryOperator {
|
||||||
fn write_operation(&self) -> String {
|
fn write_operation(&self) -> String {
|
||||||
|
// TODO: represent signed & unsigned if necessary
|
||||||
match self {
|
match self {
|
||||||
Self::Multiply => "mul",
|
Self::Multiply => "mul",
|
||||||
Self::Divide => "div",
|
Self::Divide => "div",
|
||||||
Self::Modulo => "mod",
|
Self::Modulo => "mod",
|
||||||
Self::Plus => "add",
|
Self::Plus => "add",
|
||||||
Self::Minus => "sub",
|
Self::Minus => "sub",
|
||||||
|
Self::ShiftLeft => "shl",
|
||||||
|
Self::ShiftRight => "shr",
|
||||||
Self::Equals => "cmp eq",
|
Self::Equals => "cmp eq",
|
||||||
Self::NotEquals => "cmp ne",
|
Self::NotEquals => "cmp ne",
|
||||||
Self::Less => "cmp lt",
|
Self::Less => "cmp lt",
|
||||||
Self::LessOrEqual => "cmp le",
|
Self::LessOrEqual => "cmp le",
|
||||||
Self::Greater => "cmp gt",
|
Self::Greater => "cmp gt",
|
||||||
Self::GreaterOrEqual => "cmp ge",
|
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()
|
.to_string()
|
||||||
}
|
}
|
||||||
@@ -210,8 +229,13 @@ impl WriteOp for ast::BinaryOperator {
|
|||||||
impl WriteOp for ast::UnaryOperator {
|
impl WriteOp for ast::UnaryOperator {
|
||||||
fn write_operation(&self) -> String {
|
fn write_operation(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Plus => "plus",
|
||||||
Self::Minus => "minus",
|
Self::Minus => "minus",
|
||||||
_ => todo!(),
|
Self::Negate => "negate",
|
||||||
|
_ => todo!(
|
||||||
|
"ast::UnaryOperator::WriteOp: write operation for {:?} is needed",
|
||||||
|
self
|
||||||
|
),
|
||||||
}
|
}
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/tests.rs
24
src/tests.rs
@@ -3,6 +3,7 @@ use std::fs::{self, File};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
use wait_timeout::ChildExt;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
@@ -45,18 +46,28 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute compiled executable
|
// Execute compiled executable
|
||||||
let status = Command::new(fs::canonicalize(bin_path.clone()).unwrap())
|
let mut child = Command::new(fs::canonicalize(bin_path.clone()).unwrap())
|
||||||
.status()
|
.spawn()
|
||||||
.expect("failed to execute the compiled executable")
|
.expect("failed to execute the compiled executable");
|
||||||
.code()
|
|
||||||
.expect("failed to return an exit code");
|
|
||||||
|
|
||||||
// Remove compiled executable
|
|
||||||
Command::new("rm")
|
Command::new("rm")
|
||||||
.arg(bin_path)
|
.arg(bin_path)
|
||||||
.status()
|
.status()
|
||||||
.expect("failed to remove compiled executable");
|
.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) {
|
let ir = match Irgen::default().translate(unit) {
|
||||||
Ok(ir) => ir,
|
Ok(ir) => ir,
|
||||||
Err(irgen_error) => panic!("{}", irgen_error),
|
Err(irgen_error) => panic!("{}", irgen_error),
|
||||||
@@ -73,5 +84,6 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
|
|||||||
panic!("non-integer value occurs")
|
panic!("non-integer value occurs")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("kecc: {:?}\ngcc: {:?}", result, status);
|
||||||
assert_eq!(result, ir::Value::int(status as u128, 32, true));
|
assert_eq!(result, ir::Value::int(status as u128, 32, true));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ REPLACE_DICT = {
|
|||||||
"long __undefined;": "",
|
"long __undefined;": "",
|
||||||
"return 0;": "return crc32_context % 128;",
|
"return 0;": "return crc32_context % 128;",
|
||||||
r"__attribute__\s*\(\(.*\)\)": "",
|
r"__attribute__\s*\(\(.*\)\)": "",
|
||||||
"_Float128": "long double",
|
"_Float128": "double",
|
||||||
|
"long double": "double",
|
||||||
|
"(\+[0-9^FL]*)L": r"\1",
|
||||||
"union": "struct",
|
"union": "struct",
|
||||||
r"enum[\w\s]*\{[^\}]*\};": "",
|
r"enum[\w\s]*\{[^\}]*\};": "",
|
||||||
r"typedef enum[\w\s]*\{[^;]*;[\s_A-Z]*;": "",
|
r"typedef enum[\w\s]*\{[^;]*;[\s_A-Z]*;": "",
|
||||||
@@ -38,21 +40,23 @@ REPLACE_DICT = {
|
|||||||
r"[^\n]*_IO_2_1_[^;]*;": "", # extern을 지우면서 생긴 size를 알 수 없는 struct 삭제
|
r"[^\n]*_IO_2_1_[^;]*;": "", # extern을 지우면서 생긴 size를 알 수 없는 struct 삭제
|
||||||
r"__asm\s*\([^\)]*\)": "", # asm extension in mac
|
r"__asm\s*\([^\)]*\)": "", # asm extension in mac
|
||||||
r"__asm__\s*\([^\)]*\)": "", # asm extension in linux
|
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
|
# To check fuzzer before make kecc support struct type
|
||||||
"struct[^}]*};": "",
|
# "struct[^}]*};": "",
|
||||||
" struct[^{]*[^}]*}[^;]*;": "",
|
# " struct[^{]*[^}]*}[^;]*;": "",
|
||||||
"typedef struct _IO_FILE __FILE;": "",
|
# "typedef struct _IO_FILE __FILE;": "",
|
||||||
"struct _IO_FILE;": "",
|
# "struct _IO_FILE;": "",
|
||||||
"typedef struct _IO_FILE FILE;": "typedef int FILE;",
|
# "typedef struct _IO_FILE FILE;": "typedef int FILE;",
|
||||||
"typedef struct _IO_FILE": "typedef int",
|
# "typedef struct _IO_FILE": "typedef int",
|
||||||
"typedef struct __locale_struct": "typedef int",
|
# "typedef struct __locale_struct": "typedef int",
|
||||||
"typedef __locale_t locale_t;": "typedef int locale_t;",
|
# "typedef __locale_t locale_t;": "typedef int locale_t;",
|
||||||
"struct _IO_FILE_plus;": "",
|
# "struct _IO_FILE_plus;": "",
|
||||||
"typedef _G_fpos_t": "typedef int",
|
# "typedef _G_fpos_t": "typedef int",
|
||||||
"typedef struct[^\n]*\n{[^}]*}[^;]*;": "",
|
# "typedef struct[^\n]*\n{[^}]*}[^;]*;": "",
|
||||||
"typedef struct[^{]{[^}]*}": "typedef int",
|
# "typedef struct[^{]{[^}]*}": "typedef int",
|
||||||
"struct _IO_FILE": "int",
|
# "struct _IO_FILE": "int",
|
||||||
}
|
}
|
||||||
CSMITH_DIR = "csmith-2.3.0"
|
CSMITH_DIR = "csmith-2.3.0"
|
||||||
|
|
||||||
@@ -229,7 +233,7 @@ def fuzz(tests_dir, fuzz_arg, num_iter):
|
|||||||
try:
|
try:
|
||||||
args = ["cargo", "run", "--release", "--bin", "fuzz", "--", fuzz_arg, os.path.join(tests_dir, "test_polished.c")]
|
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 = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tests_dir)
|
||||||
proc.communicate(timeout=10)
|
proc.communicate(timeout=60)
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
raise Exception("Test `{}` failed with exit code {}.".format(" ".join(args), proc.returncode))
|
raise Exception("Test `{}` failed with exit code {}.".format(" ".join(args), proc.returncode))
|
||||||
except subprocess.TimeoutExpired as e:
|
except subprocess.TimeoutExpired as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user