mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-14 22:38:46 +00:00
Update skeleton
This commit is contained in:
@@ -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 <test-name> # run a particular test
|
||||
RUST_MIN_STACK=8388608 cargo test <test-name> # run a particular test
|
||||
```
|
||||
|
||||
`<test-name>` can be `test_examples_write_c`, `test_examples_irgen`, ...
|
||||
`RUST_MIN_STACK=8388608` is necessary for deep call stack for irgen tests. `<test-name>` can be
|
||||
`test_examples_write_c`, `test_examples_irgen`, ...
|
||||
|
||||
|
||||
## Fuzzing
|
||||
|
||||
50
examples/mem2reg/mem2reg.input.ir
Normal file
50
examples/mem2reg/mem2reg.input.ir
Normal file
@@ -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()
|
||||
}
|
||||
50
examples/mem2reg/mem2reg.output.ir
Normal file
50
examples/mem2reg/mem2reg.output.ir
Normal file
@@ -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()
|
||||
}
|
||||
26
examples/struct.c
Normal file
26
examples/struct.c
Normal file
@@ -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;
|
||||
}
|
||||
18
examples/struct2.c
Normal file
18
examples/struct2.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
455
src/ir/dtype.rs
455
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<String>,
|
||||
fields: Vec<Named<Dtype>>,
|
||||
fields: Option<Vec<Named<Dtype>>>,
|
||||
is_const: bool,
|
||||
size_align_offsets: Option<(usize, usize, Vec<usize>)>,
|
||||
},
|
||||
Function {
|
||||
ret: Box<Dtype>,
|
||||
@@ -323,26 +325,27 @@ impl TryFrom<BaseDtype> 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::<Result<Vec<_>, _>>()?
|
||||
.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<String>, fields: Vec<Named<Self>>) -> Self {
|
||||
pub fn structure(name: Option<String>, fields: Option<Vec<Named<Self>>>) -> Self {
|
||||
Self::Struct {
|
||||
name,
|
||||
fields,
|
||||
is_const: false,
|
||||
size_align_offsets: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_size_align_offsets_of_struct(
|
||||
self,
|
||||
structs: &HashMap<String, Option<Dtype>>,
|
||||
) -> Result<Self, DtypeError> {
|
||||
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::<Result<Vec<_>, _>>()?
|
||||
.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<Named<Self>>> {
|
||||
pub fn get_struct_name(&self) -> Option<&Option<String>> {
|
||||
if let Self::Struct { name, .. } = self {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_struct_fields(&self) -> Option<&Option<Vec<Named<Self>>>> {
|
||||
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<usize>)>> {
|
||||
if let Self::Struct {
|
||||
size_align_offsets, ..
|
||||
} = self
|
||||
{
|
||||
Some(size_align_offsets)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_function_inner(&self) -> Option<(&Self, &Vec<Self>)> {
|
||||
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<String, Option<Dtype>>) -> 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<String, Option<Dtype>>,
|
||||
) -> 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<String, Option<Dtype>>,
|
||||
) -> 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<String, Option<Dtype>>,
|
||||
) -> 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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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<String, Dtype>) -> Result<Self, DtypeError> {
|
||||
pub fn resolve_typedefs(
|
||||
self,
|
||||
typedefs: &HashMap<String, Dtype>,
|
||||
structs: &HashMap<String, Option<Dtype>>,
|
||||
) -> Result<Self, DtypeError> {
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
if let Some(fields) = fields {
|
||||
let resolved_dtypes = fields
|
||||
.iter()
|
||||
.map(|f| f.deref().clone().resolve_typedefs(typedefs, structs))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
assert_eq!(fields.len(), resolved_dtypes.len());
|
||||
let fields = izip!(fields, resolved_dtypes)
|
||||
.map(|(f, d)| Named::new(f.name().cloned(), d))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(fields.len(), resolved_dtypes.len());
|
||||
let fields = izip!(fields, resolved_dtypes)
|
||||
.map(|(f, d)| Named::new(f.name().cloned(), d))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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<String, Option<Dtype>>,
|
||||
tempid_counter: &mut usize,
|
||||
) -> Result<Self, DtypeError> {
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
assert_eq!(fields.len(), resolved_dtypes.len());
|
||||
let fields = izip!(fields, resolved_dtypes)
|
||||
.map(|(f, d)| Named::new(f.name().cloned(), d))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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::<Vec<_>>()
|
||||
.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::<Vec<_>>()
|
||||
.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<Dtype>], field_names: &mut HashSet<String>) -> 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
|
||||
}
|
||||
|
||||
339
src/ir/interp.rs
339
src/ir/interp.rs
@@ -45,6 +45,10 @@ pub enum Value {
|
||||
inner_dtype: Dtype,
|
||||
values: Vec<Value>,
|
||||
},
|
||||
Struct {
|
||||
name: String,
|
||||
fields: Vec<Named<Value>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<Constant> for Value {
|
||||
@@ -74,43 +78,6 @@ impl TryFrom<Constant> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(&ast::Initializer, &Dtype)> for Value {
|
||||
type Error = ();
|
||||
|
||||
fn try_from((initializer, dtype): (&ast::Initializer, &Dtype)) -> Result<Self, Self::Error> {
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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<Named<Value>>) -> 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<Self, ()> {
|
||||
fn default_from_dtype(
|
||||
dtype: &Dtype,
|
||||
structs: &HashMap<String, Option<Dtype>>,
|
||||
) -> Result<Self, ()> {
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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<String, Option<Dtype>>,
|
||||
) -> Result<Self, ()> {
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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<Self> {
|
||||
let size = dtype.size_align_of().unwrap().0;
|
||||
fn block_from_dtype(dtype: &Dtype, structs: &HashMap<String, Option<Dtype>>) -> Vec<Self> {
|
||||
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<Value, InterpreterError>
|
||||
fn bytes_to_value<'b, I>(
|
||||
bytes: &mut I,
|
||||
dtype: &Dtype,
|
||||
structs: &HashMap<String, Option<Dtype>>,
|
||||
) -> Result<Value, InterpreterError>
|
||||
where
|
||||
I: Iterator<Item = &'b Self>,
|
||||
{
|
||||
@@ -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::<Vec<_>>();
|
||||
let value = some_or!(
|
||||
bytes
|
||||
.by_ref()
|
||||
.take(*width)
|
||||
.iter()
|
||||
.map(|b| b.get_concrete())
|
||||
.collect::<Option<Vec<_>>>(),
|
||||
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::<Vec<_>>();
|
||||
let value = some_or!(
|
||||
bytes
|
||||
.by_ref()
|
||||
.take(*width)
|
||||
.iter()
|
||||
.map(|b| b.get_concrete())
|
||||
.collect::<Option<Vec<_>>>(),
|
||||
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::<Vec<_>>();
|
||||
let value = some_or!(
|
||||
bytes
|
||||
.by_ref()
|
||||
.take(Dtype::SIZE_OF_POINTER)
|
||||
.iter()
|
||||
.map(|b| b.get_pointer())
|
||||
.collect::<Option<Vec<_>>>(),
|
||||
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::<Result<Vec<_>, 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::<Vec<_>>();
|
||||
|
||||
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::<Result<Vec<_>, 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<Self> {
|
||||
fn value_to_bytes(value: &Value, structs: &HashMap<String, Option<Dtype>>) -> Vec<Self> {
|
||||
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::<Vec<_>>()
|
||||
}
|
||||
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::<Vec<_>>();
|
||||
|
||||
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<usize, InterpreterError> {
|
||||
fn alloc(
|
||||
&mut self,
|
||||
dtype: &Dtype,
|
||||
structs: &HashMap<String, Option<Dtype>>,
|
||||
) -> Result<usize, InterpreterError> {
|
||||
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<String, Option<Dtype>>,
|
||||
) -> 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<Value, InterpreterError> {
|
||||
let size = dtype.size_align_of().unwrap().0;
|
||||
fn load(
|
||||
&self,
|
||||
bid: usize,
|
||||
offset: isize,
|
||||
dtype: &Dtype,
|
||||
structs: &HashMap<String, Option<Dtype>>,
|
||||
) -> Result<Value, InterpreterError> {
|
||||
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<String, Option<Dtype>>,
|
||||
) -> 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())?;
|
||||
|
||||
@@ -19,6 +19,7 @@ pub use parse::Parse;
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TranslationUnit {
|
||||
pub decls: HashMap<String, Declaration>,
|
||||
pub structs: HashMap<String, Option<Dtype>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -262,12 +263,12 @@ pub enum BlockExit {
|
||||
},
|
||||
ConditionalJump {
|
||||
condition: Operand,
|
||||
arg_then: JumpArg,
|
||||
arg_else: JumpArg,
|
||||
arg_then: Box<JumpArg>,
|
||||
arg_else: Box<JumpArg>,
|
||||
},
|
||||
Switch {
|
||||
value: Operand,
|
||||
default: JumpArg,
|
||||
default: Box<JumpArg>,
|
||||
cases: Vec<(Constant, JumpArg)>,
|
||||
},
|
||||
Return {
|
||||
|
||||
@@ -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<Declaration> =
|
||||
@@ -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() {
|
||||
|
||||
@@ -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::<Vec<_>>()
|
||||
.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)?;
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 ||\
|
||||
|
||||
@@ -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::<SimplifyCfgEmpty>::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(),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user