From 073a65ae53c9d2de9ff348175d12ba5bec6bd143 Mon Sep 17 00:00:00 2001 From: Jeehoon Kang Date: Thu, 23 Apr 2020 22:41:48 +0900 Subject: [PATCH] Update skeleton --- README.md | 9 +- examples/mem2reg/mem2reg.input.ir | 50 ++++ examples/mem2reg/mem2reg.output.ir | 50 ++++ examples/struct.c | 26 ++ examples/struct2.c | 18 ++ examples/{hw1 => }/temp2.c | 4 +- src/c/parse.rs | 54 +++- src/ir/dtype.rs | 455 +++++++++++++++++++++++++---- src/ir/interp.rs | 339 +++++++++++++++------ src/ir/mod.rs | 7 +- src/ir/parse.rs | 22 +- src/ir/write_ir.rs | 23 ++ tests/fuzz.py | 20 +- tests/reduce-criteria-template.sh | 1 + tests/test_examples.rs | 10 +- 15 files changed, 889 insertions(+), 199 deletions(-) create mode 100644 examples/mem2reg/mem2reg.input.ir create mode 100644 examples/mem2reg/mem2reg.output.ir create mode 100644 examples/struct.c create mode 100644 examples/struct2.c rename examples/{hw1 => }/temp2.c (80%) diff --git a/README.md b/README.md index dc82db5..84a1203 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,14 @@ cargo run --release -- examples/fibonacci.c # compile with release build ## Test ``` -cargo test # debug build test -cargo test --release # release build test +RUST_MIN_STACK=8388608 cargo test # debug build test +RUST_MIN_STACK=8388608 cargo test --release # release build test -cargo test # run a particular test +RUST_MIN_STACK=8388608 cargo test # run a particular test ``` -`` can be `test_examples_write_c`, `test_examples_irgen`, ... +`RUST_MIN_STACK=8388608` is necessary for deep call stack for irgen tests. `` can be +`test_examples_write_c`, `test_examples_irgen`, ... ## Fuzzing diff --git a/examples/mem2reg/mem2reg.input.ir b/examples/mem2reg/mem2reg.input.ir new file mode 100644 index 0000000..95f9022 --- /dev/null +++ b/examples/mem2reg/mem2reg.input.ir @@ -0,0 +1,50 @@ +fun unit @sink { +init: + bid: b0 + allocations: + +block b0: + %b0:p0:i32 + ret unit:unit +} + +fun i32 @single_block { +init: + bid: b0 + allocations: + %l0:i32:x + +block b0: + %b0:i0:unit = store 42:i32 %l0:*i32 + %b0:i1:i32 = load %l0:*i32 + + %b0:i2:unit = store 37:i32 %l0:*i32 + %b0:i3:i32 = load %l0:*i32 + + %b0:i4:unit = call @sink(%b0:i1:i32) + %b0:i5:unit = call @sink(%b0:i3:i32) + + ret 0:i32 +} + +fun i32 @multi_block { +init: + bid: b0 + allocations: + %l0:i32:x + +block b0: + %b0:i0:unit = store 42:i32 %l0:*i32 + %b0:i1:i32 = load %l0:*i32 + + %b0:i2:unit = store 37:i32 %l0:*i32 + %b0:i3:i32 = load %l0:*i32 + + j b1() + +block b1: + %b1:i0:unit = call @sink(%b0:i1:i32) + %b1:i1:unit = call @sink(%b0:i3:i32) + + j b0() +} diff --git a/examples/mem2reg/mem2reg.output.ir b/examples/mem2reg/mem2reg.output.ir new file mode 100644 index 0000000..b7676e3 --- /dev/null +++ b/examples/mem2reg/mem2reg.output.ir @@ -0,0 +1,50 @@ +fun unit @sink { +init: + bid: b0 + allocations: + +block b0: + %b0:p0:i32 + ret unit:unit +} + +fun i32 @single_block { +init: + bid: b0 + allocations: + %l0:i32:x + +block b0: + %b0:i0:unit = nop + %b0:i1:i32 = load %l0:*i32 + + %b0:i2:unit = nop + %b0:i3:i32 = load %l0:*i32 + + %b0:i4:unit = call @sink(42:i32) + %b0:i5:unit = call @sink(37:i32) + + ret 0:i32 +} + +fun i32 @multi_block { +init: + bid: b0 + allocations: + %l0:i32:x + +block b0: + %b0:i0:unit = nop + %b0:i1:i32 = load %l0:*i32 + + %b0:i2:unit = nop + %b0:i3:i32 = load %l0:*i32 + + j b1() + +block b1: + %b1:i0:unit = call @sink(42:i32) + %b1:i1:unit = call @sink(37:i32) + + j b0() +} diff --git a/examples/struct.c b/examples/struct.c new file mode 100644 index 0000000..ab3d164 --- /dev/null +++ b/examples/struct.c @@ -0,0 +1,26 @@ +typedef struct { + char a; + struct { + int b[4][5]; + }; + double c; +} Temp; + +void init(int row, int col, int arr[4][5]) { + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + arr[i][j] = i * j; + } + } +} + +int main() { + Temp temp; + int row = 4, col = 5; + init(row, col, temp.b); + + Temp temp2; + temp2 = temp; + + return temp2.b[2][3] == 6; +} diff --git a/examples/struct2.c b/examples/struct2.c new file mode 100644 index 0000000..8141865 --- /dev/null +++ b/examples/struct2.c @@ -0,0 +1,18 @@ +typedef struct { + char a; + struct { + int b[4]; + }; + long c; +} Temp; + +int main() { + const Temp temp = {1, {{2, 3, 4, 5}}, 6}; + + Temp temp2; + temp2 = temp; + + int sum = temp2.a + temp2.b[2] + temp2.c; + + return sum == 11; +} diff --git a/examples/hw1/temp2.c b/examples/temp2.c similarity index 80% rename from examples/hw1/temp2.c rename to examples/temp2.c index 290a0a5..ab2b373 100644 --- a/examples/hw1/temp2.c +++ b/examples/temp2.c @@ -1,10 +1,12 @@ +struct color { int number; char name; }; + int f(int i, int const a[i]) { int temp = 0; const float temp2 = 0.f, temp3 = 0.f; temp = sizeof(unsigned char); temp = _Alignof(unsigned char); - struct color { int number; char name; } c; + struct color c; c.name; struct color *cp = &c; cp->name; diff --git a/src/c/parse.rs b/src/c/parse.rs index e61da7d..a984275 100644 --- a/src/c/parse.rs +++ b/src/c/parse.rs @@ -76,20 +76,7 @@ impl AssertSupported for TranslationUnit { impl AssertSupported for ExternalDeclaration { fn assert_supported(&self) { match self { - Self::Declaration(decl) => { - for spec in &decl.node.specifiers { - if let DeclarationSpecifier::StorageClass(storage_class) = &spec.node { - // `typedef` is allowed only when it is used in the external declaration. - if StorageClassSpecifier::Typedef != storage_class.node { - panic!("`StorageClassifier` other than `Typedef`") - } - } else { - spec.assert_supported(); - } - } - - decl.node.declarators.assert_supported(); - } + Self::Declaration(decl) => decl.assert_supported(), Self::StaticAssert(_) => panic!("ExternalDeclaration::StaticAssert"), Self::FunctionDefinition(fdef) => fdef.assert_supported(), } @@ -115,7 +102,7 @@ impl AssertSupported for FunctionDefinition { impl AssertSupported for DeclarationSpecifier { fn assert_supported(&self) { match self { - Self::StorageClass(_) => panic!("DeclarationSpecifier::StorageClass"), + Self::StorageClass(storage_class) => storage_class.assert_supported(), Self::TypeSpecifier(type_specifier) => type_specifier.assert_supported(), Self::TypeQualifier(type_qualifier) => type_qualifier.assert_supported(), Self::Function(_) => panic!("DeclarationSpecifier::Function"), @@ -125,6 +112,12 @@ impl AssertSupported for DeclarationSpecifier { } } +impl AssertSupported for StorageClassSpecifier { + fn assert_supported(&self) { + assert_eq!(*self, Self::Typedef) + } +} + impl AssertSupported for TypeSpecifier { fn assert_supported(&self) { match self { @@ -306,7 +299,36 @@ impl AssertSupported for DeclaratorKind { impl AssertSupported for BlockItem { fn assert_supported(&self) { match self { - Self::Declaration(decl) => decl.assert_supported(), + Self::Declaration(decl) => { + decl.node.declarators.assert_supported(); + + for spec in &decl.node.specifiers { + spec.assert_supported(); + match &spec.node { + DeclarationSpecifier::StorageClass(_) => { + // In C, `typedef` can be declared within the function. + // However, KECC does not allow this feature + // because it complicates IR generating logic. + // For example, KECC does not allow a declaration using `typedef` + // such as `typedef int i32_t;` declaration in a function definition. + panic!("`StorageClassifier` is not allowed at `BlockItem`") + } + DeclarationSpecifier::TypeSpecifier(type_specifier) => { + if let TypeSpecifier::Struct(struct_type) = &type_specifier.node { + struct_type.node.kind.assert_supported(); + // In C, `struct` can be declared within the function. + // However, KECC does not allow this feature + // because it complicates IR generating logic. + // For example, KECC allows `struct A var;` declaration + // using pre-declared `struct A`, but not `struct A { int a; } var;` + // which tries to declare `struct A` newly. + assert!(struct_type.node.declarations.is_none()); + } + } + _ => (), + } + } + } Self::StaticAssert(_) => panic!("BlockItem::StaticAssert"), Self::Statement(stmt) => stmt.assert_supported(), } diff --git a/src/ir/dtype.rs b/src/ir/dtype.rs index f718573..d797185 100644 --- a/src/ir/dtype.rs +++ b/src/ir/dtype.rs @@ -10,6 +10,7 @@ use std::hash::Hash; use itertools::izip; use crate::ir::*; +use crate::some_or; #[derive(Debug, PartialEq, Fail)] pub enum DtypeError { @@ -57,8 +58,9 @@ pub enum Dtype { }, Struct { name: Option, - fields: Vec>, + fields: Option>>, is_const: bool, + size_align_offsets: Option<(usize, usize, Vec)>, }, Function { ret: Box, @@ -323,26 +325,27 @@ impl TryFrom for Dtype { }); } + assert!(struct_type.identifier.is_some() || struct_type.declarations.is_some()); assert_eq!(struct_type.kind.node, ast::StructKind::Struct); let struct_name = struct_type.identifier.map(|i| i.node.name); let fields = if let Some(declarations) = struct_type.declarations { - declarations + let fields = declarations .iter() .map(|d| Self::try_from_ast_struct_declaration(&d.node)) .collect::, _>>()? - .concat() + .concat(); + + Some(fields) } else { - Vec::new() + None }; - let mut field_names = HashSet::new(); - for field in &fields { - if let Some(name) = field.name() { - if !field_names.insert(name.clone()) { - return Err(DtypeError::Misc { - message: format!("`{}` is arleady used in struct", name), - }); - } + if let Some(fields) = &fields { + let mut field_names = HashSet::new(); + if !check_no_duplicate_field(fields, &mut field_names) { + return Err(DtypeError::Misc { + message: "struct has duplicate field name".to_string(), + }); } } @@ -545,11 +548,67 @@ impl Dtype { } #[inline] - pub fn structure(name: Option, fields: Vec>) -> Self { + pub fn structure(name: Option, fields: Option>>) -> Self { Self::Struct { name, fields, is_const: false, + size_align_offsets: None, + } + } + + fn fill_size_align_offsets_of_struct( + self, + structs: &HashMap>, + ) -> Result { + if let Self::Struct { + name, + fields, + is_const, + size_align_offsets, + } = self + { + assert!( + name.is_some() && fields.is_some() && !is_const && size_align_offsets.is_none() + ); + + let fields = fields.unwrap(); + let align_of = fields + .iter() + .map(|f| f.deref().size_align_of(structs)) + .collect::, _>>()? + .iter() + .map(|(_, a)| *a) + .max() + .unwrap_or(0); + + let mut offsets = Vec::new(); + let mut offset = 0; + for field in &fields { + let (size_of_dtype, align_of_dtype) = field.deref().size_align_of(structs)?; + + let pad = if (offset % align_of_dtype) != 0 { + align_of_dtype - (offset % align_of_dtype) + } else { + 0 + }; + + offset += pad; + offsets.push(offset); + + offset += size_of_dtype; + } + + let size_of = ((offset - 1) / align_of + 1) * align_of; + + Ok(Self::Struct { + name, + fields: Some(fields), + is_const, + size_align_offsets: Some((size_of, align_of, offsets)), + }) + } else { + panic!("struct type is needed") } } @@ -606,7 +665,16 @@ impl Dtype { } #[inline] - pub fn get_struct_fields(&self) -> Option<&Vec>> { + pub fn get_struct_name(&self) -> Option<&Option> { + if let Self::Struct { name, .. } = self { + Some(name) + } else { + None + } + } + + #[inline] + pub fn get_struct_fields(&self) -> Option<&Option>>> { if let Self::Struct { fields, .. } = self { Some(fields) } else { @@ -614,6 +682,18 @@ impl Dtype { } } + #[inline] + pub fn get_struct_size_align_offsets(&self) -> Option<&Option<(usize, usize, Vec)>> { + if let Self::Struct { + size_align_offsets, .. + } = self + { + Some(size_align_offsets) + } else { + None + } + } + #[inline] pub fn get_function_inner(&self) -> Option<(&Self, &Vec)> { if let Self::Function { ret, params } = self { @@ -643,25 +723,37 @@ impl Dtype { #[inline] /// Check if `Dtype` is constant. if it is constant, the variable of `Dtype` is not assignable. - pub fn is_const(&self) -> bool { + pub fn is_const(&self, structs: &HashMap>) -> bool { match self { Self::Unit { is_const } => *is_const, Self::Int { is_const, .. } => *is_const, Self::Float { is_const, .. } => *is_const, Self::Pointer { is_const, .. } => *is_const, Self::Array { .. } => true, - Self::Struct { - fields, is_const, .. - } => { + Self::Struct { name, is_const, .. } => { + let name = name.as_ref().expect("`name` must be exist"); + let struct_type = structs + .get(name) + .expect("struct type matched with `name` must exist") + .as_ref() + .expect("`struct_type` must have its definition"); + let fields = struct_type + .get_struct_fields() + .expect("`struct_type` must be struct type") + .as_ref() + .expect("`fields` must be `Some`"); + *is_const // If any of the fields in the structure type is constant, return `true`. - || fields.iter().any(|f| { + || fields + .iter() + .any(|f| { // If an array is wrapped in a struct and the array's inner type is not // constant, it is assignable to another object of the same struct type. if let Self::Array { inner, .. } = f.deref() { - inner.is_const() + inner.is_const_for_array_struct_field_inner(structs) } else { - f.deref().is_const() + f.deref().is_const(structs) } }) } @@ -670,6 +762,17 @@ impl Dtype { } } + fn is_const_for_array_struct_field_inner( + &self, + structs: &HashMap>, + ) -> bool { + if let Self::Array { inner, .. } = self { + inner.is_const_for_array_struct_field_inner(structs) + } else { + self.is_const(structs) + } + } + pub fn set_const(self, is_const: bool) -> Self { match self { Self::Unit { .. } => Self::Unit { is_const }, @@ -683,17 +786,26 @@ impl Dtype { Self::Float { width, .. } => Self::Float { width, is_const }, Self::Pointer { inner, .. } => Self::Pointer { inner, is_const }, Self::Array { .. } => self, - Self::Struct { name, fields, .. } => Self::Struct { + Self::Struct { + name, + fields, + size_align_offsets, + .. + } => Self::Struct { name, fields, is_const, + size_align_offsets, }, Self::Function { .. } => self, Self::Typedef { name, .. } => Self::Typedef { name, is_const }, } } - pub fn size_align_of(&self) -> Result<(usize, usize), DtypeError> { + pub fn size_align_of( + &self, + structs: &HashMap>, + ) -> Result<(usize, usize), DtypeError> { match self { Self::Unit { .. } => Ok((0, 1)), Self::Int { width, .. } | Self::Float { width, .. } => { @@ -704,19 +816,84 @@ impl Dtype { } Self::Pointer { .. } => Ok((Self::SIZE_OF_POINTER, Self::SIZE_OF_POINTER)), Self::Array { inner, size, .. } => { - let (size_of_inner, align_of_inner) = inner.size_align_of()?; + let (size_of_inner, align_of_inner) = inner.size_align_of(structs)?; Ok(( size * std::cmp::max(size_of_inner, align_of_inner), align_of_inner, )) } - Self::Struct { .. } => todo!(), + Self::Struct { name, .. } => { + let name = name.as_ref().expect("`name` must be exist"); + let struct_type = structs + .get(name) + .ok_or_else(|| DtypeError::Misc { + message: format!("unknown struct name `{}`", name), + })? + .as_ref() + .expect("`struct_type` must have its definition"); + let (size_of, align_of, _) = struct_type + .get_struct_size_align_offsets() + .expect("`struct_type` must be stcut type") + .as_ref() + .unwrap(); + + Ok((*size_of, *align_of)) + } Self::Function { .. } => Ok((0, 1)), Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"), } } + pub fn get_offset_struct_field( + &self, + field_name: &str, + structs: &HashMap>, + ) -> Option<(usize, Self)> { + if let Self::Struct { + fields, + size_align_offsets, + .. + } = self + { + let fields = fields.as_ref().expect("struct should have its definition"); + let (_, _, offsets) = size_align_offsets + .as_ref() + .expect("struct should have `offsets` as `Some`"); + + assert_eq!(fields.len(), offsets.len()); + for (field, offset) in izip!(fields, offsets) { + if let Some(name) = field.name() { + if name == field_name { + return Some((*offset, field.deref().clone())); + } + } else { + let field_dtype = field.deref(); + let struct_name = field_dtype + .get_struct_name() + .expect("`field_dtype` must be struct type") + .as_ref() + .expect("structure type must have its name"); + let struct_type = structs + .get(struct_name) + .expect("`structs` must have value matched with `struct_name`") + .as_ref() + .expect("`struct_type` must have its definition"); + + let (offset_inner, dtype) = some_or!( + struct_type.get_offset_struct_field(field_name, structs), + continue + ); + return Some((*offset + offset_inner, dtype)); + } + } + + None + } else { + None + } + } + pub fn set_signed(self, is_signed: bool) -> Self { match self { Self::Int { @@ -767,8 +944,19 @@ impl Dtype { .collect::, _>>()?; if fields.is_empty() { - // Add anonymous field - Ok(vec![Named::new(None, dtype)]) + // If anonymous field is `Dtype::Struct`, structure type of this field + // can use this field's field as its field. + // For exampe, let's `struct A { struct { int f; }} t;`, `t.f` is valid. + if let Self::Struct { name, .. } = &dtype { + if name.is_none() { + // Note that `const` qualifier has no effect in this time. + return Ok(vec![Named::new(None, dtype.set_const(false))]); + } + } + + Err(DtypeError::Misc { + message: "declaration does not declare anything".to_string(), + }) } else { Ok(fields) } @@ -870,15 +1058,19 @@ impl Dtype { Ok(Self::array(self, value as usize)) } - pub fn resolve_typedefs(self, typedefs: &HashMap) -> Result { + pub fn resolve_typedefs( + self, + typedefs: &HashMap, + structs: &HashMap>, + ) -> Result { let dtype = match &self { Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self, Self::Pointer { inner, is_const } => { - let inner = inner.deref().clone().resolve_typedefs(typedefs)?; + let inner = inner.deref().clone().resolve_typedefs(typedefs, structs)?; Self::pointer(inner).set_const(*is_const) } Self::Array { inner, size } => { - let inner = inner.deref().clone().resolve_typedefs(typedefs)?; + let inner = inner.deref().clone().resolve_typedefs(typedefs, structs)?; Self::Array { inner: Box::new(inner), size: *size, @@ -888,24 +1080,30 @@ impl Dtype { name, fields, is_const, + .. } => { - let resolved_dtypes = fields - .iter() - .map(|f| f.deref().clone().resolve_typedefs(typedefs)) - .collect::, _>>()?; + if let Some(fields) = fields { + let resolved_dtypes = fields + .iter() + .map(|f| f.deref().clone().resolve_typedefs(typedefs, structs)) + .collect::, _>>()?; - assert_eq!(fields.len(), resolved_dtypes.len()); - let fields = izip!(fields, resolved_dtypes) - .map(|(f, d)| Named::new(f.name().cloned(), d)) - .collect::>(); + assert_eq!(fields.len(), resolved_dtypes.len()); + let fields = izip!(fields, resolved_dtypes) + .map(|(f, d)| Named::new(f.name().cloned(), d)) + .collect::>(); - Self::structure(name.clone(), fields).set_const(*is_const) + Self::structure(name.clone(), Some(fields)).set_const(*is_const) + } else { + assert!(name.is_some()); + self + } } Self::Function { ret, params } => { - let ret = ret.deref().clone().resolve_typedefs(typedefs)?; + let ret = ret.deref().clone().resolve_typedefs(typedefs, structs)?; let params = params .iter() - .map(|p| p.clone().resolve_typedefs(typedefs)) + .map(|p| p.clone().resolve_typedefs(typedefs, structs)) .collect::, _>>()?; Self::function(ret, params) @@ -917,7 +1115,7 @@ impl Dtype { message: format!("unknown type name `{}`", name), })? .clone(); - let is_const = dtype.is_const() || *is_const; + let is_const = dtype.is_const(structs) || *is_const; dtype.set_const(is_const) } @@ -925,6 +1123,112 @@ impl Dtype { Ok(dtype) } + + /// If the struct type has a definition, it is saved to the struct table + /// and transformed to a struct type with no definition. + pub fn resolve_structs( + self, + structs: &mut HashMap>, + tempid_counter: &mut usize, + ) -> Result { + let dtype = match &self { + Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self, + Self::Pointer { inner, is_const } => { + let inner = inner.deref(); + + // the pointer type can have undeclared struct type as inner. + // For example, let's `struct A { struct B *p }`, even if `struct B` has not been + // declared before, it can be used as inner type of the pointer. + if let Self::Struct { name, fields, .. } = inner { + if fields.is_none() { + let name = name.as_ref().expect("`name` must be `Some`"); + let _ = structs.entry(name.to_string()).or_insert(None); + return Ok(self.clone()); + } + } + + let resolved_inner = inner.clone().resolve_structs(structs, tempid_counter)?; + Self::pointer(resolved_inner).set_const(*is_const) + } + Self::Array { inner, size } => { + let inner = inner + .deref() + .clone() + .resolve_structs(structs, tempid_counter)?; + Self::Array { + inner: Box::new(inner), + size: *size, + } + } + Self::Struct { + name, + fields, + is_const, + .. + } => { + if let Some(fields) = fields { + let resolved_dtypes = fields + .iter() + .map(|f| f.deref().clone().resolve_structs(structs, tempid_counter)) + .collect::, _>>()?; + + assert_eq!(fields.len(), resolved_dtypes.len()); + let fields = izip!(fields, resolved_dtypes) + .map(|(f, d)| Named::new(f.name().cloned(), d)) + .collect::>(); + + let name = if let Some(name) = name { + name.clone() + } else { + let tempid = *tempid_counter; + *tempid_counter += 1; + format!("%t{}", tempid) + }; + let resolved_struct = Self::structure(Some(name.clone()), Some(fields)); + let filled_struct = + resolved_struct.fill_size_align_offsets_of_struct(structs)?; + + let prev_dtype = structs.insert(name.clone(), Some(filled_struct)); + if let Some(prev_dtype) = prev_dtype { + if prev_dtype.is_some() { + return Err(DtypeError::Misc { + message: format!("redefinition of {}", name), + }); + } + } + + Self::structure(Some(name), None).set_const(*is_const) + } else { + let name = name.as_ref().expect("`name` must be exist"); + let struct_type = structs.get(name).ok_or_else(|| DtypeError::Misc { + message: format!("unknown struct name `{}`", name), + })?; + if struct_type.is_none() { + return Err(DtypeError::Misc { + message: format!("variable has incomplete type 'struct {}'", name), + }); + } + + self + } + } + Self::Function { ret, params } => { + let ret = ret + .deref() + .clone() + .resolve_structs(structs, tempid_counter)?; + let params = params + .iter() + .map(|p| p.clone().resolve_structs(structs, tempid_counter)) + .collect::, _>>()?; + + Self::function(ret, params) + } + Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"), + }; + + Ok(dtype) + } } impl fmt::Display for Dtype { @@ -957,31 +1261,33 @@ impl fmt::Display for Dtype { name, fields, is_const, + .. } => { - let fields = fields - .iter() - .map(|f| { - format!( - "{}:{}", - if let Some(name) = f.name() { - name - } else { - "%anonymous" - }, - f.deref() - ) - }) - .collect::>() - .join(", "); + let fields = if let Some(fields) = fields { + let fields = fields + .iter() + .map(|f| { + format!( + "{}:{}", + if let Some(name) = f.name() { + name + } else { + "%anon" + }, + f.deref() + ) + }) + .collect::>() + .join(", "); + format!(":<{}>", fields) + } else { + "".to_string() + }; write!( f, - "{} struct {}:<{}>", + "{} struct {}{}", if *is_const { "const" } else { "" }, - if let Some(name) = name { - name - } else { - "%anonymous" - }, + if let Some(name) = name { name } else { "%anon" }, fields ) } @@ -1008,3 +1314,26 @@ impl Default for Dtype { Self::INT } } + +#[inline] +fn check_no_duplicate_field(fields: &[Named], field_names: &mut HashSet) -> bool { + for field in fields { + if let Some(name) = field.name() { + if !field_names.insert(name.clone()) { + return false; + } + } else { + let field_dtype = field.deref(); + let fields = field_dtype + .get_struct_fields() + .expect("`field_dtype` must be struct type") + .as_ref() + .expect("struct type must have its definition"); + if !check_no_duplicate_field(&fields, field_names) { + return false; + } + } + } + + true +} diff --git a/src/ir/interp.rs b/src/ir/interp.rs index 582b80d..ea38c68 100644 --- a/src/ir/interp.rs +++ b/src/ir/interp.rs @@ -45,6 +45,10 @@ pub enum Value { inner_dtype: Dtype, values: Vec, }, + Struct { + name: String, + fields: Vec>, + }, } impl TryFrom for Value { @@ -74,43 +78,6 @@ impl TryFrom for Value { } } -impl TryFrom<(&ast::Initializer, &Dtype)> for Value { - type Error = (); - - fn try_from((initializer, dtype): (&ast::Initializer, &Dtype)) -> Result { - match initializer { - ast::Initializer::Expression(expr) => match dtype { - Dtype::Int { .. } | Dtype::Float { .. } | Dtype::Pointer { .. } => { - let constant = Constant::try_from(&expr.node)?; - let value = Self::try_from(constant)?; - - calculator::calculate_typecast(value, dtype.clone()) - } - _ => Err(()), - }, - ast::Initializer::List(items) => match dtype { - Dtype::Array { inner, size } => { - let inner_dtype = inner.deref().clone(); - let num_of_items = items.len(); - let values = (0..*size) - .map(|i| { - if i < num_of_items { - Self::try_from((&items[i].node.initializer.node, &inner_dtype)) - } else { - Self::default_from_dtype(&inner_dtype) - } - }) - .collect::, _>>()?; - - Ok(Self::array(inner_dtype, values)) - } - Dtype::Struct { .. } => todo!(), - _ => todo!(), - }, - } - } -} - impl HasDtype for Value { fn dtype(&self) -> Dtype { match self { @@ -125,6 +92,13 @@ impl HasDtype for Value { inner_dtype, values, } => Dtype::array(inner_dtype.clone(), values.len()), + Self::Struct { name, fields } => { + let fields = fields + .iter() + .map(|f| Named::new(f.name().cloned(), f.deref().dtype())) + .collect(); + Dtype::structure(Some(name.clone()), Some(fields)) + } } } } @@ -167,6 +141,11 @@ impl Value { } } + #[inline] + fn structure(name: String, fields: Vec>) -> Self { + Self::Struct { name, fields } + } + #[inline] pub fn get_int(self) -> Option<(u128, usize, bool)> { if let Value::Int { @@ -200,7 +179,10 @@ impl Value { } #[inline] - fn default_from_dtype(dtype: &Dtype) -> Result { + fn default_from_dtype( + dtype: &Dtype, + structs: &HashMap>, + ) -> Result { let value = match dtype { Dtype::Unit { .. } => Self::unit(), Dtype::Int { @@ -209,18 +191,113 @@ impl Value { Dtype::Float { width, .. } => Self::float(f64::default(), *width), Dtype::Pointer { inner, .. } => Self::nullptr(inner.deref().clone()), Dtype::Array { inner, size } => { - let values = iter::repeat(Self::default_from_dtype(inner)) + let values = iter::repeat(Self::default_from_dtype(inner, structs)) .take(*size) .collect::, _>>()?; Self::array(inner.deref().clone(), values) } - Dtype::Struct { .. } => todo!(), + Dtype::Struct { name, .. } => { + let name = name.as_ref().expect("struct should have its name"); + let struct_type = structs + .get(name) + .expect("struct type matched with `name` must exist") + .as_ref() + .expect("`struct_type` must have its definition"); + let fields = struct_type + .get_struct_fields() + .expect("`struct_type` must be struct type") + .as_ref() + .expect("`fields` must be `Some`"); + + let fields = fields + .iter() + .map(|f| { + let value = Self::default_from_dtype(f.deref(), structs)?; + Ok(Named::new(f.name().cloned(), value)) + }) + .collect::, _>>()?; + + Self::structure(name.clone(), fields) + } Dtype::Function { .. } => panic!("function type does not have a default value"), Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"), }; Ok(value) } + + fn try_from_initializer( + initializer: &ast::Initializer, + dtype: &Dtype, + structs: &HashMap>, + ) -> Result { + match initializer { + ast::Initializer::Expression(expr) => match dtype { + Dtype::Int { .. } | Dtype::Float { .. } | Dtype::Pointer { .. } => { + let constant = Constant::try_from(&expr.node)?; + let value = Self::try_from(constant)?; + + calculator::calculate_typecast(value, dtype.clone()) + } + _ => Err(()), + }, + ast::Initializer::List(items) => match dtype { + Dtype::Array { inner, size } => { + let inner_dtype = inner.deref().clone(); + let num_of_items = items.len(); + let values = (0..*size) + .map(|i| { + if i < num_of_items { + Self::try_from_initializer( + &items[i].node.initializer.node, + &inner_dtype, + structs, + ) + } else { + Self::default_from_dtype(&inner_dtype, structs) + } + }) + .collect::, _>>()?; + + Ok(Self::array(inner_dtype, values)) + } + Dtype::Struct { name, .. } => { + let name = name.as_ref().expect("struct should have its name"); + let struct_type = structs + .get(name) + .expect("struct type matched with `name` must exist") + .as_ref() + .expect("`struct_type` must have its definition"); + let fields = struct_type + .get_struct_fields() + .expect("`struct_type` must be struct type") + .as_ref() + .expect("`fields` must be `Some`"); + + let fields = fields + .iter() + .enumerate() + .map(|(i, f)| { + let value = if let Some(item) = items.get(i) { + Self::try_from_initializer( + &item.node.initializer.node, + f.deref(), + structs, + )? + } else { + Self::default_from_dtype(f.deref(), structs)? + }; + + Ok(Named::new(f.name().cloned(), value)) + }) + .collect::, _>>()?; + + Ok(Self::structure(name.clone(), fields)) + } + _ => Err(()), + }, + } + } } #[derive(Debug, PartialEq, Fail)] @@ -231,6 +308,11 @@ pub enum InterpreterError { NoMainFunction, #[fail(display = "ir has no function definition of {} function", func_name)] NoFunctionDefinition { func_name: String }, + #[fail( + display = "ir has no structure definition of {} structure", + struct_name + )] + NoStructureDefinition { struct_name: String }, #[fail(display = "{}:{} / {}", func_name, pc, msg)] Misc { func_name: String, @@ -669,8 +751,8 @@ impl Byte { } } - fn block_from_dtype(dtype: &Dtype) -> Vec { - let size = dtype.size_align_of().unwrap().0; + fn block_from_dtype(dtype: &Dtype, structs: &HashMap>) -> Vec { + let size = dtype.size_align_of(structs).unwrap().0; iter::repeat(Self::Undef).take(size).collect() } @@ -695,7 +777,11 @@ impl Byte { u128::from_le_bytes(array) } - fn bytes_to_value<'b, I>(bytes: &mut I, dtype: &Dtype) -> Result + fn bytes_to_value<'b, I>( + bytes: &mut I, + dtype: &Dtype, + structs: &HashMap>, + ) -> Result where I: Iterator, { @@ -704,10 +790,11 @@ impl Byte { Dtype::Int { width, is_signed, .. } => { + let size = dtype.size_align_of(structs).unwrap().0; + let bytes = bytes.by_ref().take(size).collect::>(); let value = some_or!( bytes - .by_ref() - .take(*width) + .iter() .map(|b| b.get_concrete()) .collect::>>(), return Ok(Value::undef(dtype.clone())) @@ -716,11 +803,11 @@ impl Byte { Ok(Value::int(value, *width, *is_signed)) } Dtype::Float { width, .. } => { - let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE; + let size = dtype.size_align_of(structs).unwrap().0; + let bytes = bytes.by_ref().take(size).collect::>(); let value = some_or!( bytes - .by_ref() - .take(*width) + .iter() .map(|b| b.get_concrete()) .collect::>>(), return Ok(Value::undef(dtype.clone())) @@ -735,10 +822,13 @@ impl Byte { Ok(Value::float(value, *width)) } Dtype::Pointer { inner, .. } => { + let bytes = bytes + .by_ref() + .take(Dtype::SIZE_OF_POINTER) + .collect::>(); let value = some_or!( bytes - .by_ref() - .take(Dtype::SIZE_OF_POINTER) + .iter() .map(|b| b.get_pointer()) .collect::>>(), return Ok(Value::undef(dtype.clone())) @@ -759,12 +849,14 @@ impl Byte { ) } Dtype::Array { inner, size } => { - let (inner_size, inner_align) = inner.size_align_of().unwrap(); + let (inner_size, inner_align) = inner.size_align_of(structs).unwrap(); let padding = std::cmp::max(inner_size, inner_align) - inner_size; let values = (0..*size) .map(|_| { - let value = Self::bytes_to_value(bytes, inner)?; - let _ = bytes.by_ref().take(padding); + let value = Self::bytes_to_value(bytes, inner, structs)?; + if padding > 0 { + let _ = bytes.by_ref().nth(padding - 1); + } Ok(value) }) .collect::, InterpreterError>>()?; @@ -773,28 +865,64 @@ impl Byte { values, }) } - Dtype::Struct { .. } => todo!(), + Dtype::Struct { name, .. } => { + let name = name.as_ref().expect("struct should have its name"); + let struct_type = structs + .get(name) + .expect("struct type matched with `name` must exist") + .as_ref() + .expect("`struct_type` must have its definition"); + let fields = struct_type + .get_struct_fields() + .expect("`struct_type` must be struct type") + .as_ref() + .expect("`fields` must be `Some`"); + let (size, _, offsets) = struct_type + .get_struct_size_align_offsets() + .expect("`struct_type` must be struct type") + .as_ref() + .expect("`offsets` must be `Some`"); + let bytes = bytes.by_ref().take(*size).cloned().collect::>(); + + assert_eq!(fields.len(), offsets.len()); + let fields = izip!(fields, offsets) + .map(|(f, o)| { + let mut sub_bytes = bytes[*o..].iter(); + let value = Self::bytes_to_value(&mut sub_bytes, f.deref(), structs)?; + Ok(Named::new(f.name().cloned(), value)) + }) + .collect::, InterpreterError>>()?; + + Ok(Value::Struct { + name: name.clone(), + fields, + }) + } Dtype::Function { .. } => panic!("function value cannot be constructed from bytes"), Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"), } } - fn value_to_bytes(value: &Value) -> Vec { + fn value_to_bytes(value: &Value, structs: &HashMap>) -> Vec { match value { - Value::Undef { dtype } => Self::block_from_dtype(dtype), + Value::Undef { dtype } => Self::block_from_dtype(dtype, structs), Value::Unit => Vec::new(), - Value::Int { value, width, .. } => { - let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE; - Self::u128_to_bytes(*value, size) + Value::Int { + value: int_value, .. + } => { + let size = value.dtype().size_align_of(structs).unwrap().0; + Self::u128_to_bytes(*int_value, size) .iter() .map(|b| Self::concrete(*b)) .collect::>() } - Value::Float { value, width } => { - let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE; + Value::Float { + value: float_value, .. + } => { + let size = value.dtype().size_align_of(structs).unwrap().0; 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, + Dtype::SIZE_OF_FLOAT => (*float_value as f32).to_bits() as u128, + Dtype::SIZE_OF_DOUBLE => (*float_value as f64).to_bits() as u128, _ => panic!("value_to_bytes: {} is not a valid float size", size), }; @@ -810,26 +938,53 @@ impl Byte { inner_dtype, values, } => { - let (inner_size, inner_align) = inner_dtype.size_align_of().unwrap(); + let (inner_size, inner_align) = inner_dtype.size_align_of(structs).unwrap(); let padding = std::cmp::max(inner_size, inner_align) - inner_size; values .iter() .map(|v| { - let mut result = Self::value_to_bytes(v); + let mut result = Self::value_to_bytes(v, structs); result.extend(iter::repeat(Byte::Undef).take(padding)); result }) .flatten() .collect() } + Value::Struct { name, fields } => { + let struct_type = structs + .get(name) + .expect("struct type matched with `name` must exist") + .as_ref() + .expect("`struct_type` must have its definition"); + let (size_of, _, offsets) = struct_type + .get_struct_size_align_offsets() + .expect("`struct_type` must be struct type") + .as_ref() + .expect("`offsets` must be `Some`"); + let mut values = iter::repeat(Byte::Undef).take(*size_of).collect::>(); + + assert_eq!(fields.len(), offsets.len()); + izip!(fields, offsets).for_each(|(f, o)| { + let result = Self::value_to_bytes(f.deref(), structs); + let size_of_data = f.deref().dtype().size_align_of(structs).unwrap().0; + values.splice(*o..(*o + size_of_data), result.iter().cloned()); + }); + + values + } } } } impl Memory { - fn alloc(&mut self, dtype: &Dtype) -> Result { + fn alloc( + &mut self, + dtype: &Dtype, + structs: &HashMap>, + ) -> Result { let bid = self.inner.len(); - self.inner.push(Some(Byte::block_from_dtype(dtype))); + self.inner + .push(Some(Byte::block_from_dtype(dtype, structs))); Ok(bid) } @@ -838,34 +993,47 @@ impl Memory { bid: usize, offset: isize, dtype: &Dtype, + structs: &HashMap>, ) -> Result<(), InterpreterError> { let block = &mut self.inner[bid]; assert_eq!(offset, 0); assert_eq!( block.as_mut().unwrap().len(), - dtype.size_align_of().unwrap().0 + dtype.size_align_of(structs).unwrap().0 ); *block = None; Ok(()) } - fn load(&self, bid: usize, offset: isize, dtype: &Dtype) -> Result { - let size = dtype.size_align_of().unwrap().0; + fn load( + &self, + bid: usize, + offset: isize, + dtype: &Dtype, + structs: &HashMap>, + ) -> Result { + let size = dtype.size_align_of(structs).unwrap().0; let end = offset as usize + size; let block = self.inner[bid].as_ref().unwrap(); if 0 <= offset && end <= block.len() { let mut iter = block[offset as usize..end].iter(); - Byte::bytes_to_value(&mut iter, dtype) + Byte::bytes_to_value(&mut iter, dtype, structs) } else { Ok(Value::undef(dtype.clone())) } } - fn store(&mut self, bid: usize, offset: isize, value: &Value) -> Result<(), ()> { - let size = value.dtype().size_align_of().unwrap().0; + fn store( + &mut self, + bid: usize, + offset: isize, + value: &Value, + structs: &HashMap>, + ) -> Result<(), ()> { + let size = value.dtype().size_align_of(structs).unwrap().0; let end = offset as usize + size; - let bytes = Byte::value_to_bytes(value); + let bytes = Byte::value_to_bytes(value, structs); let block = self.inner[bid].as_mut().unwrap(); if 0 <= offset && end <= block.len() { @@ -928,30 +1096,30 @@ impl<'i> State<'i> { fn alloc_global_variables(&mut self) -> Result<(), InterpreterError> { for (name, decl) in &self.ir.decls { // Memory allocation - let bid = self.memory.alloc(&decl.dtype())?; + let bid = self.memory.alloc(&decl.dtype(), &self.ir.structs)?; self.global_map.insert(name.clone(), bid)?; // Initialize allocated memory space match decl { Declaration::Variable { dtype, initializer } => { let value = if let Some(initializer) = initializer { - Value::try_from((initializer, dtype)).map_err(|_| { - InterpreterError::Misc { + Value::try_from_initializer(initializer, dtype, &self.ir.structs).map_err( + |_| InterpreterError::Misc { func_name: self.stack_frame.func_name.clone(), pc: self.stack_frame.pc, msg: format!( "fail to translate `Initializer` and `{}` to `Value`", dtype ), - } - })? + }, + )? } else { - Value::default_from_dtype(&dtype) + Value::default_from_dtype(&dtype, &self.ir.structs) .expect("default value must be derived from `dtype`") }; self.memory - .store(bid, 0, &value) + .store(bid, 0, &value, &self.ir.structs) .map_err(|_| InterpreterError::Misc { func_name: self.stack_frame.func_name.clone(), pc: self.stack_frame.pc, @@ -972,7 +1140,7 @@ impl<'i> State<'i> { fn alloc_local_variables(&mut self) -> Result<(), InterpreterError> { // add alloc register for (id, allocation) in self.stack_frame.func_def.allocations.iter().enumerate() { - let bid = self.memory.alloc(&allocation)?; + let bid = self.memory.alloc(&allocation, &self.ir.structs)?; let ptr = Value::pointer(Some(bid), 0, allocation.deref().clone()); let rid = RegisterId::local(id); @@ -1020,7 +1188,8 @@ impl<'i> State<'i> { .get_pointer() .unwrap(); assert_eq!(d.deref(), dtype); - self.memory.dealloc(bid.unwrap(), *offset, dtype)?; + self.memory + .dealloc(bid.unwrap(), *offset, dtype, &self.ir.structs)?; } // restore previous state @@ -1153,7 +1322,7 @@ impl<'i> State<'i> { let value = self.interp_operand(value.clone())?; let (bid, offset, _) = self.interp_ptr(&ptr)?; self.memory - .store(bid, offset, &value) + .store(bid, offset, &value, &self.ir.structs) .map_err(|_| InterpreterError::Misc { func_name: self.stack_frame.func_name.clone(), pc: self.stack_frame.pc, @@ -1167,7 +1336,7 @@ impl<'i> State<'i> { Instruction::Load { ptr, .. } => { let ptr = self.interp_operand(ptr.clone())?; let (bid, offset, dtype) = self.interp_ptr(&ptr)?; - self.memory.load(bid, offset, &dtype)? + self.memory.load(bid, offset, &dtype, &self.ir.structs)? } Instruction::Call { callee, args, .. } => { let ptr = self.interp_operand(callee.clone())?; diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 3e1f317..3ffc4a3 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -19,6 +19,7 @@ pub use parse::Parse; #[derive(Debug, Clone, PartialEq)] pub struct TranslationUnit { pub decls: HashMap, + pub structs: HashMap>, } #[derive(Debug, Clone, PartialEq)] @@ -262,12 +263,12 @@ pub enum BlockExit { }, ConditionalJump { condition: Operand, - arg_then: JumpArg, - arg_else: JumpArg, + arg_then: Box, + arg_else: Box, }, Switch { value: Operand, - default: JumpArg, + default: Box, cases: Vec<(Constant, JumpArg)>, }, Return { diff --git a/src/ir/parse.rs b/src/ir/parse.rs index 2938b86..bbe415c 100644 --- a/src/ir/parse.rs +++ b/src/ir/parse.rs @@ -21,7 +21,7 @@ peg::parser! { let result = decls.insert(decl.name.unwrap(), decl.inner); assert!(result.is_none()); } - TranslationUnit { decls } + TranslationUnit { decls, structs: HashMap::new() } } rule named_decl() -> Named = @@ -130,12 +130,8 @@ peg::parser! { / expected!("instruction") rule instruction_inner() -> Instruction = - "call" __ callee:operand() _ "(" _ args:(operand() ** (_ "," _)) _ ")" { - Instruction::Call { - callee, - args, - return_type: Dtype::unit(), // TODO - } + "nop" { + Instruction::Nop } / "load" __ ptr:operand() { @@ -145,6 +141,14 @@ peg::parser! { "store" __ value:operand() __ ptr:operand() { Instruction::Store { ptr, value } } + / + "call" __ callee:operand() _ "(" _ args:(operand() ** (_ "," _)) _ ")" { + Instruction::Call { + callee, + args, + return_type: Dtype::unit(), // TODO + } + } / "typecast" __ value:operand() __ "to" __ target_dtype:dtype() { Instruction::TypeCast { value, target_dtype } @@ -209,11 +213,11 @@ peg::parser! { } / "br" __ condition:operand() __ arg_then:jump_arg() __ arg_else:jump_arg() { - BlockExit::ConditionalJump { condition, arg_then, arg_else } + BlockExit::ConditionalJump { condition, arg_then: Box::new(arg_then), arg_else: Box::new(arg_else) } } / "switch" __ value:operand() __ "default" __ default:jump_arg() _ "[" _ cases:(switch_case() ** __) _ "]" { - BlockExit::Switch { value, default, cases } + BlockExit::Switch { value, default: Box::new(default), cases } } / "ret" __ value:operand() { diff --git a/src/ir/write_ir.rs b/src/ir/write_ir.rs index 79dedd4..08ca305 100644 --- a/src/ir/write_ir.rs +++ b/src/ir/write_ir.rs @@ -9,6 +9,29 @@ use lang_c::ast; impl WriteLine for TranslationUnit { fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> { + // TODO: consider KECC IR parser in the future. + for (name, struct_type) in &self.structs { + let definition = if let Some(struct_type) = struct_type { + let fields = struct_type + .get_struct_fields() + .expect("`struct_type` must be struct type") + .as_ref() + .expect("`fields` must be `Some`"); + + let fields = fields + .iter() + .map(|f| f.deref().to_string()) + .collect::>() + .join(", "); + + format!("{{ {} }}", fields) + } else { + "opaque".to_string() + }; + + writeln!(write, "struct {} : {}", name, definition)?; + } + for (name, decl) in &self.decls { let _ = some_or!(decl.get_variable(), continue); (name, decl).write_line(indent, write)?; diff --git a/tests/fuzz.py b/tests/fuzz.py index bc962a2..5ed95f9 100644 --- a/tests/fuzz.py +++ b/tests/fuzz.py @@ -43,23 +43,9 @@ REPLACE_DICT = { "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", - # "FILE *": "void *", - # "typedef __fpos_t fpos_t;": "", - # "fpos_t *": "void *", + # todo: need to consider the case below in the future: + # avoid compile-time constant expressed as complex expression such as `1 + 1` + "char _unused2[^;]*;": "char _unused2[10];", } CSMITH_DIR = "csmith-2.3.0" diff --git a/tests/reduce-criteria-template.sh b/tests/reduce-criteria-template.sh index af857de..79af9ec 100644 --- a/tests/reduce-criteria-template.sh +++ b/tests/reduce-criteria-template.sh @@ -28,6 +28,7 @@ if grep 'pointer-bool-conversion' out.txt ||\ grep 'non-void function does not return a value' out.txt ||\ grep 'too many arguments in call' out.txt ||\ + grep 'declaration does not declare anything' out.txt ||\ ! gcc -Wall -Wextra -O2 test_reduced.c > outa.txt 2>&1 ||\ grep 'uninitialized' outa.txt ||\ grep 'without a cast' outa.txt ||\ diff --git a/tests/test_examples.rs b/tests/test_examples.rs index 4bec451..59c79f2 100644 --- a/tests/test_examples.rs +++ b/tests/test_examples.rs @@ -34,7 +34,6 @@ where #[test] fn test_examples_write_c() { test_dir(Path::new("examples/"), &OsStr::new("c"), test_write_c); - test_dir(Path::new("examples/hw1"), &OsStr::new("c"), test_write_c); } #[test] @@ -75,3 +74,12 @@ fn test_examples_simplify_cfg() { &mut FunctionPass::::default(), ); } + +#[test] +fn test_examples_mem2reg() { + test_opt( + &Path::new("examples/mem2reg/mem2reg.input.ir"), + &Path::new("examples/mem2reg/mem2reg.output.ir"), + &mut Mem2reg::default(), + ); +}