mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-16 07:28:52 +00:00
Update skeleton
This commit is contained in:
@@ -1,10 +1,13 @@
|
|||||||
|
int g_a[5] = {1, 2, 3};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
int init = 1;
|
int init = 1;
|
||||||
int a[5] = {init, 2, 3, 4, -5};
|
int a[5] = {init, 2, 3, 4, -5, 6};
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
|
|
||||||
for(int i = 0; i < 5; i++) {
|
for(int i = 0; i < 5; i++) {
|
||||||
sum += a[i];
|
sum += a[i];
|
||||||
|
sum += g_a[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ impl AssertSupported for StructField {
|
|||||||
impl AssertSupported for StructDeclarator {
|
impl AssertSupported for StructDeclarator {
|
||||||
fn assert_supported(&self) {
|
fn assert_supported(&self) {
|
||||||
self.declarator.assert_supported();
|
self.declarator.assert_supported();
|
||||||
assert_eq!(true, self.bit_width.is_none());
|
assert!(self.bit_width.is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
238
src/ir/dtype.rs
238
src/ir/dtype.rs
@@ -4,9 +4,11 @@ use core::ops::Deref;
|
|||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
use lang_c::ast;
|
use lang_c::ast;
|
||||||
use lang_c::span::Node;
|
use lang_c::span::Node;
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
use itertools::izip;
|
||||||
|
|
||||||
use crate::ir::*;
|
use crate::ir::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Fail)]
|
#[derive(Debug, PartialEq, Fail)]
|
||||||
@@ -26,6 +28,7 @@ struct BaseDtype {
|
|||||||
size_modifiers: Vec<ast::TypeSpecifier>,
|
size_modifiers: Vec<ast::TypeSpecifier>,
|
||||||
signed_option: Option<ast::TypeSpecifier>,
|
signed_option: Option<ast::TypeSpecifier>,
|
||||||
typedef_name: Option<String>,
|
typedef_name: Option<String>,
|
||||||
|
struct_type: Option<ast::StructType>,
|
||||||
is_const: bool,
|
is_const: bool,
|
||||||
is_typedef: bool,
|
is_typedef: bool,
|
||||||
}
|
}
|
||||||
@@ -52,6 +55,11 @@ pub enum Dtype {
|
|||||||
inner: Box<Dtype>,
|
inner: Box<Dtype>,
|
||||||
size: usize,
|
size: usize,
|
||||||
},
|
},
|
||||||
|
Struct {
|
||||||
|
name: Option<String>,
|
||||||
|
fields: Vec<Named<Dtype>>,
|
||||||
|
is_const: bool,
|
||||||
|
},
|
||||||
Function {
|
Function {
|
||||||
ret: Box<Dtype>,
|
ret: Box<Dtype>,
|
||||||
params: Vec<Dtype>,
|
params: Vec<Dtype>,
|
||||||
@@ -139,6 +147,14 @@ impl BaseDtype {
|
|||||||
}
|
}
|
||||||
self.typedef_name = Some(identifier.node.name.clone());
|
self.typedef_name = Some(identifier.node.name.clone());
|
||||||
}
|
}
|
||||||
|
ast::TypeSpecifier::Struct(struct_type) => {
|
||||||
|
if self.struct_type.is_some() {
|
||||||
|
return Err(DtypeError::Misc {
|
||||||
|
message: "two or more struct type in declaration specifiers".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.struct_type = Some(struct_type.node.clone());
|
||||||
|
}
|
||||||
_ => todo!("apply_type_specifier: support {:?}", type_specifier),
|
_ => todo!("apply_type_specifier: support {:?}", type_specifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +188,7 @@ impl BaseDtype {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_typename_specifier(
|
pub fn apply_specifier_qualifier(
|
||||||
&mut self,
|
&mut self,
|
||||||
typename_specifier: &ast::SpecifierQualifier,
|
typename_specifier: &ast::SpecifierQualifier,
|
||||||
) -> Result<(), DtypeError> {
|
) -> Result<(), DtypeError> {
|
||||||
@@ -237,12 +253,12 @@ impl BaseDtype {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_typename_specifiers(
|
pub fn apply_specifier_qualifiers(
|
||||||
&mut self,
|
&mut self,
|
||||||
typename_specifiers: &[Node<ast::SpecifierQualifier>],
|
typename_specifiers: &[Node<ast::SpecifierQualifier>],
|
||||||
) -> Result<(), DtypeError> {
|
) -> Result<(), DtypeError> {
|
||||||
for ast_spec in typename_specifiers {
|
for ast_spec in typename_specifiers {
|
||||||
self.apply_typename_specifier(&ast_spec.node)?;
|
self.apply_specifier_qualifier(&ast_spec.node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -275,20 +291,66 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
&& spec.size_modifiers.is_empty()
|
&& spec.size_modifiers.is_empty()
|
||||||
&& spec.signed_option.is_none()
|
&& spec.signed_option.is_none()
|
||||||
&& spec.typedef_name.is_none()
|
&& spec.typedef_name.is_none()
|
||||||
|
&& spec.struct_type.is_none()
|
||||||
&& !spec.is_const),
|
&& !spec.is_const),
|
||||||
"BaseDtype is empty"
|
"BaseDtype is empty"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut dtype = if let Some(name) = spec.typedef_name {
|
if let Some(name) = spec.typedef_name {
|
||||||
if spec.scalar.is_some() || spec.signed_option.is_some() {
|
if !(spec.scalar.is_none()
|
||||||
|
&& spec.size_modifiers.is_empty()
|
||||||
|
&& spec.signed_option.is_none()
|
||||||
|
&& spec.struct_type.is_none())
|
||||||
|
{
|
||||||
return Err(DtypeError::Misc {
|
return Err(DtypeError::Misc {
|
||||||
message: "typedef' cannot be used with scalar type or signed option"
|
message: "`typedef` can only be used with `const`".to_string(),
|
||||||
.to_string(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::typedef(name)
|
let dtype = Self::typedef(name).set_const(spec.is_const);
|
||||||
|
|
||||||
|
return Ok(dtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(struct_type) = spec.struct_type {
|
||||||
|
if !(spec.scalar.is_none()
|
||||||
|
&& spec.size_modifiers.is_empty()
|
||||||
|
&& spec.signed_option.is_none()
|
||||||
|
&& spec.typedef_name.is_none())
|
||||||
|
{
|
||||||
|
return Err(DtypeError::Misc {
|
||||||
|
message: "`struct` can only be used with `const`".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
.iter()
|
||||||
|
.map(|d| Self::try_from_ast_struct_declaration(&d.node))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
|
.concat()
|
||||||
} else {
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dtype = Self::structure(struct_name, fields).set_const(spec.is_const);
|
||||||
|
|
||||||
|
return Ok(dtype);
|
||||||
|
}
|
||||||
|
|
||||||
// Creates `dtype` from scalar.
|
// Creates `dtype` from scalar.
|
||||||
let mut dtype = if let Some(t) = spec.scalar {
|
let mut dtype = if let Some(t) = spec.scalar {
|
||||||
match t {
|
match t {
|
||||||
@@ -320,8 +382,7 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
|| spec.size_modifiers[1] != ast::TypeSpecifier::Long
|
|| spec.size_modifiers[1] != ast::TypeSpecifier::Long
|
||||||
{
|
{
|
||||||
return Err(DtypeError::Misc {
|
return Err(DtypeError::Misc {
|
||||||
message: "two or more size modifiers in declaration specifiers"
|
message: "two or more size modifiers in declaration specifiers".to_string(),
|
||||||
.to_string(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Self::LONGLONG
|
Self::LONGLONG
|
||||||
@@ -346,17 +407,13 @@ impl TryFrom<BaseDtype> for Dtype {
|
|||||||
|
|
||||||
if dtype.get_int_width().is_none() {
|
if dtype.get_int_width().is_none() {
|
||||||
return Err(DtypeError::Misc {
|
return Err(DtypeError::Misc {
|
||||||
message: "`signed` and `unsigned` only be applied to `Dtype::Int`"
|
message: "`signed` and `unsigned` only be applied to `Dtype::Int`".to_string(),
|
||||||
.to_string(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dtype = dtype.set_signed(is_signed);
|
dtype = dtype.set_signed(is_signed);
|
||||||
}
|
}
|
||||||
|
|
||||||
dtype
|
|
||||||
};
|
|
||||||
|
|
||||||
dtype = dtype.set_const(spec.is_const);
|
dtype = dtype.set_const(spec.is_const);
|
||||||
|
|
||||||
Ok(dtype)
|
Ok(dtype)
|
||||||
@@ -369,11 +426,11 @@ impl TryFrom<&ast::TypeName> for Dtype {
|
|||||||
/// Derive a data type from typename.
|
/// Derive a data type from typename.
|
||||||
fn try_from(type_name: &ast::TypeName) -> Result<Self, Self::Error> {
|
fn try_from(type_name: &ast::TypeName) -> Result<Self, Self::Error> {
|
||||||
let mut spec = BaseDtype::default();
|
let mut spec = BaseDtype::default();
|
||||||
BaseDtype::apply_typename_specifiers(&mut spec, &type_name.specifiers)?;
|
BaseDtype::apply_specifier_qualifiers(&mut spec, &type_name.specifiers)?;
|
||||||
let mut dtype = Self::try_from(spec)?;
|
let mut dtype = Self::try_from(spec)?;
|
||||||
|
|
||||||
if let Some(declarator) = &type_name.declarator {
|
if let Some(declarator) = &type_name.declarator {
|
||||||
dtype = dtype.with_ast_declarator(&declarator.node)?;
|
dtype = dtype.with_ast_declarator(&declarator.node)?.deref().clone();
|
||||||
}
|
}
|
||||||
Ok(dtype)
|
Ok(dtype)
|
||||||
}
|
}
|
||||||
@@ -389,7 +446,7 @@ impl TryFrom<&ast::ParameterDeclaration> for Dtype {
|
|||||||
let mut dtype = Self::try_from(spec)?;
|
let mut dtype = Self::try_from(spec)?;
|
||||||
|
|
||||||
if let Some(declarator) = ¶meter_decl.declarator {
|
if let Some(declarator) = ¶meter_decl.declarator {
|
||||||
dtype = dtype.with_ast_declarator(&declarator.node)?;
|
dtype = dtype.with_ast_declarator(&declarator.node)?.deref().clone();
|
||||||
|
|
||||||
// A function call with an array argument performs array-to-pointer conversion.
|
// A function call with an array argument performs array-to-pointer conversion.
|
||||||
// For this reason, when `declarator` is from function parameter declaration
|
// For this reason, when `declarator` is from function parameter declaration
|
||||||
@@ -487,6 +544,15 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn structure(name: Option<String>, fields: Vec<Named<Self>>) -> Self {
|
||||||
|
Self::Struct {
|
||||||
|
name,
|
||||||
|
fields,
|
||||||
|
is_const: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn function(ret: Dtype, params: Vec<Dtype>) -> Self {
|
pub fn function(ret: Dtype, params: Vec<Dtype>) -> Self {
|
||||||
Self::Function {
|
Self::Function {
|
||||||
@@ -522,7 +588,7 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_pointer_inner(&self) -> Option<&Dtype> {
|
pub fn get_pointer_inner(&self) -> Option<&Self> {
|
||||||
if let Self::Pointer { inner, .. } = self {
|
if let Self::Pointer { inner, .. } = self {
|
||||||
Some(inner.deref())
|
Some(inner.deref())
|
||||||
} else {
|
} else {
|
||||||
@@ -531,7 +597,7 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_array_inner(&self) -> Option<&Dtype> {
|
pub fn get_array_inner(&self) -> Option<&Self> {
|
||||||
if let Self::Array { inner, .. } = self {
|
if let Self::Array { inner, .. } = self {
|
||||||
Some(inner.deref())
|
Some(inner.deref())
|
||||||
} else {
|
} else {
|
||||||
@@ -540,7 +606,16 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_function_inner(&self) -> Option<(&Dtype, &Vec<Dtype>)> {
|
pub fn get_struct_fields(&self) -> Option<&Vec<Named<Self>>> {
|
||||||
|
if let Self::Struct { fields, .. } = self {
|
||||||
|
Some(fields)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_function_inner(&self) -> Option<(&Self, &Vec<Self>)> {
|
||||||
if let Self::Function { ret, params } = self {
|
if let Self::Function { ret, params } = self {
|
||||||
Some((ret.deref(), params))
|
Some((ret.deref(), params))
|
||||||
} else {
|
} else {
|
||||||
@@ -567,6 +642,7 @@ impl Dtype {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Unit { is_const } => *is_const,
|
Self::Unit { is_const } => *is_const,
|
||||||
@@ -574,6 +650,21 @@ impl Dtype {
|
|||||||
Self::Float { is_const, .. } => *is_const,
|
Self::Float { is_const, .. } => *is_const,
|
||||||
Self::Pointer { is_const, .. } => *is_const,
|
Self::Pointer { is_const, .. } => *is_const,
|
||||||
Self::Array { .. } => true,
|
Self::Array { .. } => true,
|
||||||
|
Self::Struct {
|
||||||
|
fields, is_const, ..
|
||||||
|
} => {
|
||||||
|
*is_const
|
||||||
|
// If any of the fields in the structure type is constant, return `true`.
|
||||||
|
|| 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()
|
||||||
|
} else {
|
||||||
|
f.deref().is_const()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Self::Function { .. } => true,
|
Self::Function { .. } => true,
|
||||||
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||||
}
|
}
|
||||||
@@ -592,6 +683,11 @@ impl Dtype {
|
|||||||
Self::Float { width, .. } => Self::Float { width, is_const },
|
Self::Float { width, .. } => Self::Float { width, is_const },
|
||||||
Self::Pointer { inner, .. } => Self::Pointer { inner, is_const },
|
Self::Pointer { inner, .. } => Self::Pointer { inner, is_const },
|
||||||
Self::Array { .. } => self,
|
Self::Array { .. } => self,
|
||||||
|
Self::Struct { name, fields, .. } => Self::Struct {
|
||||||
|
name,
|
||||||
|
fields,
|
||||||
|
is_const,
|
||||||
|
},
|
||||||
Self::Function { .. } => self,
|
Self::Function { .. } => self,
|
||||||
Self::Typedef { name, .. } => Self::Typedef { name, is_const },
|
Self::Typedef { name, .. } => Self::Typedef { name, is_const },
|
||||||
}
|
}
|
||||||
@@ -615,6 +711,7 @@ impl Dtype {
|
|||||||
align_of_inner,
|
align_of_inner,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
Self::Struct { .. } => todo!(),
|
||||||
Self::Function { .. } => Ok((0, 1)),
|
Self::Function { .. } => Ok((0, 1)),
|
||||||
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||||
}
|
}
|
||||||
@@ -645,6 +742,38 @@ impl Dtype {
|
|||||||
Ok((dtype, is_typedef))
|
Ok((dtype, is_typedef))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derive a data type and its name from struct declaration
|
||||||
|
pub fn try_from_ast_struct_declaration(
|
||||||
|
declaration: &ast::StructDeclaration,
|
||||||
|
) -> Result<Vec<Named<Self>>, DtypeError> {
|
||||||
|
let field_decl = if let ast::StructDeclaration::Field(field_decl) = declaration {
|
||||||
|
&field_decl.node
|
||||||
|
} else {
|
||||||
|
panic!("ast::StructDeclaration::StaticAssert is unsupported")
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut spec = BaseDtype::default();
|
||||||
|
BaseDtype::apply_specifier_qualifiers(&mut spec, &field_decl.specifiers)?;
|
||||||
|
let dtype = Self::try_from(spec)?;
|
||||||
|
|
||||||
|
let fields = field_decl
|
||||||
|
.declarators
|
||||||
|
.iter()
|
||||||
|
.map(|d| {
|
||||||
|
dtype
|
||||||
|
.clone()
|
||||||
|
.with_ast_declarator(&d.node.declarator.as_ref().unwrap().node)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
if fields.is_empty() {
|
||||||
|
// Add anonymous field
|
||||||
|
Ok(vec![Named::new(None, dtype)])
|
||||||
|
} else {
|
||||||
|
Ok(fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate `Dtype` based on declarator and `self` which has scalar type.
|
/// Generate `Dtype` based on declarator and `self` which has scalar type.
|
||||||
///
|
///
|
||||||
/// let's say declaration is `const int * const * const a;`.
|
/// let's say declaration is `const int * const * const a;`.
|
||||||
@@ -657,7 +786,10 @@ impl Dtype {
|
|||||||
/// * `decl_spec` - Containing information that should be referenced
|
/// * `decl_spec` - Containing information that should be referenced
|
||||||
/// when creating `Dtype` from `Declarator`.
|
/// when creating `Dtype` from `Declarator`.
|
||||||
///
|
///
|
||||||
pub fn with_ast_declarator(mut self, declarator: &ast::Declarator) -> Result<Self, DtypeError> {
|
pub fn with_ast_declarator(
|
||||||
|
mut self,
|
||||||
|
declarator: &ast::Declarator,
|
||||||
|
) -> Result<Named<Self>, DtypeError> {
|
||||||
for derived_decl in &declarator.derived {
|
for derived_decl in &declarator.derived {
|
||||||
self = match &derived_decl.node {
|
self = match &derived_decl.node {
|
||||||
ast::DerivedDeclarator::Pointer(pointer_qualifiers) => {
|
ast::DerivedDeclarator::Pointer(pointer_qualifiers) => {
|
||||||
@@ -696,7 +828,10 @@ impl Dtype {
|
|||||||
|
|
||||||
let declarator_kind = &declarator.kind;
|
let declarator_kind = &declarator.kind;
|
||||||
match &declarator_kind.node {
|
match &declarator_kind.node {
|
||||||
ast::DeclaratorKind::Abstract | ast::DeclaratorKind::Identifier(_) => Ok(self),
|
ast::DeclaratorKind::Abstract => Ok(Named::new(None, self)),
|
||||||
|
ast::DeclaratorKind::Identifier(identifier) => {
|
||||||
|
Ok(Named::new(Some(identifier.node.name.clone()), self))
|
||||||
|
}
|
||||||
ast::DeclaratorKind::Declarator(declarator) => {
|
ast::DeclaratorKind::Declarator(declarator) => {
|
||||||
self.with_ast_declarator(&declarator.node)
|
self.with_ast_declarator(&declarator.node)
|
||||||
}
|
}
|
||||||
@@ -740,15 +875,32 @@ impl Dtype {
|
|||||||
Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self,
|
Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self,
|
||||||
Self::Pointer { inner, is_const } => {
|
Self::Pointer { inner, is_const } => {
|
||||||
let inner = inner.deref().clone().resolve_typedefs(typedefs)?;
|
let inner = inner.deref().clone().resolve_typedefs(typedefs)?;
|
||||||
Dtype::pointer(inner).set_const(*is_const)
|
Self::pointer(inner).set_const(*is_const)
|
||||||
}
|
}
|
||||||
Self::Array { inner, size } => {
|
Self::Array { inner, size } => {
|
||||||
let inner = inner.deref().clone().resolve_typedefs(typedefs)?;
|
let inner = inner.deref().clone().resolve_typedefs(typedefs)?;
|
||||||
Dtype::Array {
|
Self::Array {
|
||||||
inner: Box::new(inner),
|
inner: Box::new(inner),
|
||||||
size: *size,
|
size: *size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Self::Struct {
|
||||||
|
name,
|
||||||
|
fields,
|
||||||
|
is_const,
|
||||||
|
} => {
|
||||||
|
let resolved_dtypes = fields
|
||||||
|
.iter()
|
||||||
|
.map(|f| f.deref().clone().resolve_typedefs(typedefs))
|
||||||
|
.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<_>>();
|
||||||
|
|
||||||
|
Self::structure(name.clone(), fields).set_const(*is_const)
|
||||||
|
}
|
||||||
Self::Function { ret, params } => {
|
Self::Function { ret, params } => {
|
||||||
let ret = ret.deref().clone().resolve_typedefs(typedefs)?;
|
let ret = ret.deref().clone().resolve_typedefs(typedefs)?;
|
||||||
let params = params
|
let params = params
|
||||||
@@ -756,7 +908,7 @@ impl Dtype {
|
|||||||
.map(|p| p.clone().resolve_typedefs(typedefs))
|
.map(|p| p.clone().resolve_typedefs(typedefs))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
Dtype::function(ret, params)
|
Self::function(ret, params)
|
||||||
}
|
}
|
||||||
Self::Typedef { name, is_const } => {
|
Self::Typedef { name, is_const } => {
|
||||||
let dtype = typedefs
|
let dtype = typedefs
|
||||||
@@ -797,6 +949,38 @@ impl fmt::Display for Dtype {
|
|||||||
write!(f, "{}*{}", inner, if *is_const { "const" } else { "" })
|
write!(f, "{}*{}", inner, if *is_const { "const" } else { "" })
|
||||||
}
|
}
|
||||||
Self::Array { inner, size, .. } => write!(f, "[{} x {}]", size, inner,),
|
Self::Array { inner, size, .. } => write!(f, "[{} x {}]", size, inner,),
|
||||||
|
Self::Struct {
|
||||||
|
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(", ");
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} struct {}:<{}>",
|
||||||
|
if *is_const { "const" } else { "" },
|
||||||
|
if let Some(name) = name {
|
||||||
|
name
|
||||||
|
} else {
|
||||||
|
"%anonymous"
|
||||||
|
},
|
||||||
|
fields
|
||||||
|
)
|
||||||
|
}
|
||||||
Self::Function { ret, params } => write!(
|
Self::Function { ret, params } => write!(
|
||||||
f,
|
f,
|
||||||
"{} ({})",
|
"{} ({})",
|
||||||
|
|||||||
220
src/ir/interp.rs
220
src/ir/interp.rs
@@ -9,18 +9,24 @@ use itertools::izip;
|
|||||||
use crate::ir::*;
|
use crate::ir::*;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
// TODO: the variants of Value will be added in the future
|
// TODO: delete `allow(dead_code)`
|
||||||
|
/// Even though `Undef`, `Int`, `Float` are constructed and actively used at run-time,
|
||||||
|
/// the rust compiler analyzes these elements are dead code.
|
||||||
|
/// For this reason, we add `allow(dead_code)` mark above these elements respectively.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
|
#[allow(dead_code)]
|
||||||
Undef {
|
Undef {
|
||||||
dtype: Dtype,
|
dtype: Dtype,
|
||||||
},
|
},
|
||||||
Unit,
|
Unit,
|
||||||
|
#[allow(dead_code)]
|
||||||
Int {
|
Int {
|
||||||
value: u128,
|
value: u128,
|
||||||
width: usize,
|
width: usize,
|
||||||
is_signed: bool,
|
is_signed: bool,
|
||||||
},
|
},
|
||||||
|
#[allow(dead_code)]
|
||||||
Float {
|
Float {
|
||||||
/// `value` may be `f32`, but it is possible to consider it as `f64`.
|
/// `value` may be `f32`, but it is possible to consider it as `f64`.
|
||||||
///
|
///
|
||||||
@@ -41,6 +47,70 @@ pub enum Value {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Constant> for Value {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(constant: Constant) -> Result<Self, Self::Error> {
|
||||||
|
let value = match constant {
|
||||||
|
Constant::Undef { dtype } => Self::Undef { dtype },
|
||||||
|
Constant::Unit => Self::Unit,
|
||||||
|
Constant::Int {
|
||||||
|
value,
|
||||||
|
width,
|
||||||
|
is_signed,
|
||||||
|
} => Self::Int {
|
||||||
|
value,
|
||||||
|
width,
|
||||||
|
is_signed,
|
||||||
|
},
|
||||||
|
Constant::Float { value, width } => Self::Float {
|
||||||
|
value: value.into_inner(),
|
||||||
|
width,
|
||||||
|
},
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(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 {
|
impl HasDtype for Value {
|
||||||
fn dtype(&self) -> Dtype {
|
fn dtype(&self) -> Dtype {
|
||||||
match self {
|
match self {
|
||||||
@@ -71,7 +141,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn int(value: u128, width: usize, is_signed: bool) -> Self {
|
fn int(value: u128, width: usize, is_signed: bool) -> Self {
|
||||||
Self::Int {
|
Self::Int {
|
||||||
value,
|
value,
|
||||||
width,
|
width,
|
||||||
@@ -98,7 +168,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_int(self) -> Option<(u128, usize, bool)> {
|
pub fn get_int(self) -> Option<(u128, usize, bool)> {
|
||||||
if let Value::Int {
|
if let Value::Int {
|
||||||
value,
|
value,
|
||||||
width,
|
width,
|
||||||
@@ -130,23 +200,26 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default_from_dtype(dtype: &Dtype) -> Self {
|
fn default_from_dtype(dtype: &Dtype) -> Result<Self, ()> {
|
||||||
match dtype {
|
let value = match dtype {
|
||||||
ir::Dtype::Unit { .. } => Self::unit(),
|
Dtype::Unit { .. } => Self::unit(),
|
||||||
ir::Dtype::Int {
|
Dtype::Int {
|
||||||
width, is_signed, ..
|
width, is_signed, ..
|
||||||
} => Self::int(u128::default(), *width, *is_signed),
|
} => Self::int(u128::default(), *width, *is_signed),
|
||||||
ir::Dtype::Float { width, .. } => Self::float(f64::default(), *width),
|
Dtype::Float { width, .. } => Self::float(f64::default(), *width),
|
||||||
ir::Dtype::Pointer { inner, .. } => Self::nullptr(inner.deref().clone()),
|
Dtype::Pointer { inner, .. } => Self::nullptr(inner.deref().clone()),
|
||||||
ir::Dtype::Array { inner, size } => {
|
Dtype::Array { inner, size } => {
|
||||||
let values = (0..*size)
|
let values = iter::repeat(Self::default_from_dtype(inner))
|
||||||
.map(|_| Self::default_from_dtype(inner))
|
.take(*size)
|
||||||
.collect();
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
Self::array(inner.deref().clone(), values)
|
Self::array(inner.deref().clone(), values)
|
||||||
}
|
}
|
||||||
ir::Dtype::Function { .. } => panic!("function type does not have a default value"),
|
Dtype::Struct { .. } => todo!(),
|
||||||
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
Dtype::Function { .. } => panic!("function type does not have a default value"),
|
||||||
}
|
Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,8 +700,8 @@ impl Byte {
|
|||||||
I: Iterator<Item = &'b Self>,
|
I: Iterator<Item = &'b Self>,
|
||||||
{
|
{
|
||||||
match dtype {
|
match dtype {
|
||||||
ir::Dtype::Unit { .. } => Ok(Value::Unit),
|
Dtype::Unit { .. } => Ok(Value::Unit),
|
||||||
ir::Dtype::Int {
|
Dtype::Int {
|
||||||
width, is_signed, ..
|
width, is_signed, ..
|
||||||
} => {
|
} => {
|
||||||
let value = some_or!(
|
let value = some_or!(
|
||||||
@@ -642,7 +715,7 @@ impl Byte {
|
|||||||
let value = Self::bytes_to_u128(&value, *is_signed);
|
let value = Self::bytes_to_u128(&value, *is_signed);
|
||||||
Ok(Value::int(value, *width, *is_signed))
|
Ok(Value::int(value, *width, *is_signed))
|
||||||
}
|
}
|
||||||
ir::Dtype::Float { width, .. } => {
|
Dtype::Float { width, .. } => {
|
||||||
let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE;
|
let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE;
|
||||||
let value = some_or!(
|
let value = some_or!(
|
||||||
bytes
|
bytes
|
||||||
@@ -661,7 +734,7 @@ impl Byte {
|
|||||||
|
|
||||||
Ok(Value::float(value, *width))
|
Ok(Value::float(value, *width))
|
||||||
}
|
}
|
||||||
ir::Dtype::Pointer { inner, .. } => {
|
Dtype::Pointer { inner, .. } => {
|
||||||
let value = some_or!(
|
let value = some_or!(
|
||||||
bytes
|
bytes
|
||||||
.by_ref()
|
.by_ref()
|
||||||
@@ -685,7 +758,7 @@ impl Byte {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ir::Dtype::Array { inner, size } => {
|
Dtype::Array { inner, size } => {
|
||||||
let (inner_size, inner_align) = inner.size_align_of().unwrap();
|
let (inner_size, inner_align) = inner.size_align_of().unwrap();
|
||||||
let padding = std::cmp::max(inner_size, inner_align) - inner_size;
|
let padding = std::cmp::max(inner_size, inner_align) - inner_size;
|
||||||
let values = (0..*size)
|
let values = (0..*size)
|
||||||
@@ -700,8 +773,9 @@ impl Byte {
|
|||||||
values,
|
values,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ir::Dtype::Function { .. } => panic!("function value cannot be constructed from bytes"),
|
Dtype::Struct { .. } => todo!(),
|
||||||
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
Dtype::Function { .. } => panic!("function value cannot be constructed from bytes"),
|
||||||
|
Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,22 +850,30 @@ impl Memory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self, bid: usize, offset: isize, dtype: &Dtype) -> Result<Value, InterpreterError> {
|
fn load(&self, bid: usize, offset: isize, dtype: &Dtype) -> Result<Value, InterpreterError> {
|
||||||
assert!(0 <= offset);
|
|
||||||
let offset = offset as usize;
|
|
||||||
let size = dtype.size_align_of().unwrap().0;
|
let size = dtype.size_align_of().unwrap().0;
|
||||||
let mut iter = self.inner[bid].as_ref().unwrap()[offset..(offset + size)].iter();
|
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)
|
||||||
|
} else {
|
||||||
|
Ok(Value::undef(dtype.clone()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store(&mut self, bid: usize, offset: isize, value: &Value) {
|
fn store(&mut self, bid: usize, offset: isize, value: &Value) -> Result<(), ()> {
|
||||||
assert!(0 <= offset);
|
|
||||||
let offset = offset as usize;
|
|
||||||
let size = value.dtype().size_align_of().unwrap().0;
|
let size = value.dtype().size_align_of().unwrap().0;
|
||||||
|
let end = offset as usize + size;
|
||||||
let bytes = Byte::value_to_bytes(value);
|
let bytes = Byte::value_to_bytes(value);
|
||||||
self.inner[bid]
|
let block = self.inner[bid].as_mut().unwrap();
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
if 0 <= offset && end <= block.len() {
|
||||||
.splice(offset..(offset + size), bytes.iter().cloned());
|
block.splice(offset as usize..end, bytes.iter().cloned());
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -851,41 +933,34 @@ impl<'i> State<'i> {
|
|||||||
|
|
||||||
// Initialize allocated memory space
|
// Initialize allocated memory space
|
||||||
match decl {
|
match decl {
|
||||||
Declaration::Variable { dtype, initializer } => match &dtype {
|
Declaration::Variable { dtype, initializer } => {
|
||||||
ir::Dtype::Unit { .. } => (),
|
let value = if let Some(initializer) = initializer {
|
||||||
ir::Dtype::Int { .. } | ir::Dtype::Float { .. } | ir::Dtype::Pointer { .. } => {
|
Value::try_from((initializer, dtype)).map_err(|_| {
|
||||||
let value = if let Some(constant) = initializer {
|
|
||||||
self.interp_constant(constant.clone())
|
|
||||||
} else {
|
|
||||||
Value::default_from_dtype(&dtype)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Type cast
|
|
||||||
let value =
|
|
||||||
calculator::calculate_typecast(value, dtype.clone()).map_err(|_| {
|
|
||||||
InterpreterError::Misc {
|
InterpreterError::Misc {
|
||||||
func_name: self.stack_frame.func_name.clone(),
|
func_name: self.stack_frame.func_name.clone(),
|
||||||
pc: self.stack_frame.pc,
|
pc: self.stack_frame.pc,
|
||||||
msg: "calculate_typecast when initialize global variable"
|
msg: format!(
|
||||||
.into(),
|
"fail to translate `Initializer` and `{}` to `Value`",
|
||||||
|
dtype
|
||||||
|
),
|
||||||
}
|
}
|
||||||
})?;
|
})?
|
||||||
|
|
||||||
self.memory.store(bid, 0, &value);
|
|
||||||
}
|
|
||||||
ir::Dtype::Array { .. } => {
|
|
||||||
let value = if let Some(_list_init) = initializer {
|
|
||||||
// TODO: is type cast required?
|
|
||||||
todo!("Initializer::List is needed")
|
|
||||||
} else {
|
} else {
|
||||||
Value::default_from_dtype(&dtype)
|
Value::default_from_dtype(&dtype)
|
||||||
|
.expect("default value must be derived from `dtype`")
|
||||||
};
|
};
|
||||||
|
|
||||||
self.memory.store(bid, 0, &value);
|
self.memory
|
||||||
|
.store(bid, 0, &value)
|
||||||
|
.map_err(|_| InterpreterError::Misc {
|
||||||
|
func_name: self.stack_frame.func_name.clone(),
|
||||||
|
pc: self.stack_frame.pc,
|
||||||
|
msg: format!(
|
||||||
|
"fail to store {:?} into memory with bid: {}, offset: {}",
|
||||||
|
value, bid, 0,
|
||||||
|
),
|
||||||
|
})?
|
||||||
}
|
}
|
||||||
ir::Dtype::Function { .. } => panic!("function variable does not exist"),
|
|
||||||
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
|
||||||
},
|
|
||||||
// If functin declaration, skip initialization
|
// If functin declaration, skip initialization
|
||||||
Declaration::Function { .. } => (),
|
Declaration::Function { .. } => (),
|
||||||
}
|
}
|
||||||
@@ -1077,7 +1152,16 @@ impl<'i> State<'i> {
|
|||||||
let ptr = self.interp_operand(ptr.clone())?;
|
let ptr = self.interp_operand(ptr.clone())?;
|
||||||
let value = self.interp_operand(value.clone())?;
|
let value = self.interp_operand(value.clone())?;
|
||||||
let (bid, offset, _) = self.interp_ptr(&ptr)?;
|
let (bid, offset, _) = self.interp_ptr(&ptr)?;
|
||||||
self.memory.store(bid, offset, &value);
|
self.memory
|
||||||
|
.store(bid, offset, &value)
|
||||||
|
.map_err(|_| InterpreterError::Misc {
|
||||||
|
func_name: self.stack_frame.func_name.clone(),
|
||||||
|
pc: self.stack_frame.pc,
|
||||||
|
msg: format!(
|
||||||
|
"fail to store {:?} into memory with bid: {}, offset: {}",
|
||||||
|
value, bid, offset,
|
||||||
|
),
|
||||||
|
})?;
|
||||||
Value::Unit
|
Value::Unit
|
||||||
}
|
}
|
||||||
Instruction::Load { ptr, .. } => {
|
Instruction::Load { ptr, .. } => {
|
||||||
@@ -1177,21 +1261,6 @@ impl<'i> State<'i> {
|
|||||||
|
|
||||||
fn interp_constant(&self, value: Constant) -> Value {
|
fn interp_constant(&self, value: Constant) -> Value {
|
||||||
match value {
|
match value {
|
||||||
Constant::Undef { dtype } => Value::Undef { dtype },
|
|
||||||
Constant::Unit => Value::Unit,
|
|
||||||
Constant::Int {
|
|
||||||
value,
|
|
||||||
width,
|
|
||||||
is_signed,
|
|
||||||
} => Value::Int {
|
|
||||||
value,
|
|
||||||
width,
|
|
||||||
is_signed,
|
|
||||||
},
|
|
||||||
Constant::Float { value, width } => Value::Float {
|
|
||||||
value: value.into_inner(),
|
|
||||||
width,
|
|
||||||
},
|
|
||||||
Constant::GlobalVariable { name, dtype } => {
|
Constant::GlobalVariable { name, dtype } => {
|
||||||
let bid = self
|
let bid = self
|
||||||
.global_map
|
.global_map
|
||||||
@@ -1205,6 +1274,7 @@ impl<'i> State<'i> {
|
|||||||
dtype,
|
dtype,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
constant => Value::try_from(constant).expect("constant must be transformed to value"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ pub struct TranslationUnit {
|
|||||||
pub enum Declaration {
|
pub enum Declaration {
|
||||||
Variable {
|
Variable {
|
||||||
dtype: Dtype,
|
dtype: Dtype,
|
||||||
initializer: Option<Constant>,
|
initializer: Option<ast::Initializer>,
|
||||||
},
|
},
|
||||||
Function {
|
Function {
|
||||||
signature: FunctionSignature,
|
signature: FunctionSignature,
|
||||||
@@ -54,7 +54,8 @@ impl TryFrom<Dtype> for Declaration {
|
|||||||
Dtype::Int { .. }
|
Dtype::Int { .. }
|
||||||
| Dtype::Float { .. }
|
| Dtype::Float { .. }
|
||||||
| Dtype::Pointer { .. }
|
| Dtype::Pointer { .. }
|
||||||
| Dtype::Array { .. } => Ok(Declaration::Variable {
|
| Dtype::Array { .. }
|
||||||
|
| Dtype::Struct { .. } => Ok(Declaration::Variable {
|
||||||
dtype,
|
dtype,
|
||||||
initializer: None,
|
initializer: None,
|
||||||
}),
|
}),
|
||||||
@@ -68,7 +69,7 @@ impl TryFrom<Dtype> for Declaration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Declaration {
|
impl Declaration {
|
||||||
pub fn get_variable(&self) -> Option<(&Dtype, &Option<Constant>)> {
|
pub fn get_variable(&self) -> Option<(&Dtype, &Option<ast::Initializer>)> {
|
||||||
if let Self::Variable { dtype, initializer } = self {
|
if let Self::Variable { dtype, initializer } = self {
|
||||||
Some((dtype, initializer))
|
Some((dtype, initializer))
|
||||||
} else {
|
} else {
|
||||||
@@ -607,17 +608,6 @@ impl TryFrom<&ast::Expression> for Constant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&ast::Initializer> for Constant {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(initializer: &ast::Initializer) -> Result<Self, Self::Error> {
|
|
||||||
match initializer {
|
|
||||||
ast::Initializer::Expression(expr) => Self::try_from(&expr.node),
|
|
||||||
ast::Initializer::List(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Constant {
|
impl Constant {
|
||||||
const DECIMAL: u32 = 10;
|
const DECIMAL: u32 = 10;
|
||||||
const OCTAL: u32 = 8;
|
const OCTAL: u32 = 8;
|
||||||
@@ -757,7 +747,7 @@ impl HasDtype for Constant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
pub struct Named<T> {
|
pub struct Named<T> {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
inner: T,
|
inner: T,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ impl WriteLine for (&String, &Declaration) {
|
|||||||
"{} = {} {}",
|
"{} = {} {}",
|
||||||
name,
|
name,
|
||||||
if let Some(init) = initializer {
|
if let Some(init) = initializer {
|
||||||
init.to_string()
|
init.write_string()
|
||||||
} else {
|
} else {
|
||||||
"default".to_string()
|
"default".to_string()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod write_base;
|
|||||||
|
|
||||||
mod asm;
|
mod asm;
|
||||||
mod c;
|
mod c;
|
||||||
mod ir;
|
pub mod ir;
|
||||||
|
|
||||||
mod asmgen;
|
mod asmgen;
|
||||||
mod irgen;
|
mod irgen;
|
||||||
|
|||||||
17
src/tests.rs
17
src/tests.rs
@@ -78,12 +78,15 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
|
|||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(interp_error) => panic!("{}", interp_error),
|
Err(interp_error) => panic!("{}", interp_error),
|
||||||
};
|
};
|
||||||
let result = if let ir::Value::Int { .. } = &result {
|
// We only allow main function whose return type is `int`
|
||||||
result
|
let (value, width, is_signed) = result.get_int().expect("non-integer value occurs");
|
||||||
} else {
|
assert_eq!(width, 32);
|
||||||
panic!("non-integer value occurs")
|
assert!(is_signed);
|
||||||
};
|
|
||||||
|
|
||||||
println!("kecc: {:?}\ngcc: {:?}", result, status);
|
// When obtain status from `gcc` executable process, value is truncated to byte size.
|
||||||
assert_eq!(result, ir::Value::int(status as u128, 32, true));
|
// For this reason, we make `fuzzer` generate the C source code which returns value
|
||||||
|
// typecasted to `unsigned char`. However, during `creduce` reduce the code, typecasting
|
||||||
|
// may be deleted. So, we truncate result value to byte size one more time here.
|
||||||
|
println!("gcc: {}, kecc: {}", status as i8, value as i8);
|
||||||
|
assert_eq!(status as i8, value as i8);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ REPLACE_DICT = {
|
|||||||
"extern ": "",
|
"extern ": "",
|
||||||
"__restrict": "",
|
"__restrict": "",
|
||||||
"long __undefined;": "",
|
"long __undefined;": "",
|
||||||
"return 0;": "return crc32_context % 128;",
|
"return 0;": "return (unsigned char)(crc32_context);",
|
||||||
r"__attribute__\s*\(\(.*\)\)": "",
|
r"__attribute__\s*\(\(.*\)\)": "",
|
||||||
"_Float128": "double",
|
"_Float128": "double",
|
||||||
"long double": "double",
|
"long double": "double",
|
||||||
|
|||||||
@@ -1,4 +1,56 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
rm -f out*.txt
|
||||||
|
|
||||||
|
#ulimit -t 3000
|
||||||
|
#ulimit -v 2000000
|
||||||
|
|
||||||
|
if
|
||||||
|
[ $FUZZ_ARG = '-i' ] &&\
|
||||||
|
(! clang -pedantic -Wall -Werror=strict-prototypes -O1 -c test_reduced.c > out.txt 2>&1 ||\
|
||||||
|
grep 'main-return-type' out.txt ||\
|
||||||
|
grep 'conversions than data arguments' out.txt ||\
|
||||||
|
grep 'int-conversion' out.txt ||\
|
||||||
|
grep 'incompatible redeclaration' out.txt ||\
|
||||||
|
grep 'ordered comparison between pointer and zero' out.txt ||\
|
||||||
|
grep 'ordered comparison between pointer and integer' out.txt ||\
|
||||||
|
grep 'eliding middle term' out.txt ||\
|
||||||
|
grep 'end of non-void function' out.txt ||\
|
||||||
|
grep 'invalid in C99' out.txt ||\
|
||||||
|
grep 'specifies type' out.txt ||\
|
||||||
|
grep 'should return a value' out.txt ||\
|
||||||
|
grep 'uninitialized' out.txt ||\
|
||||||
|
grep 'incompatible pointer to' out.txt ||\
|
||||||
|
grep 'incompatible integer to' out.txt ||\
|
||||||
|
grep 'type specifier missing' out.txt ||\
|
||||||
|
grep 'implicit-function-declaration' out.txt ||\
|
||||||
|
grep 'infinite-recursion' out.txt ||\
|
||||||
|
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 ||\
|
||||||
|
! gcc -Wall -Wextra -O2 test_reduced.c > outa.txt 2>&1 ||\
|
||||||
|
grep 'uninitialized' outa.txt ||\
|
||||||
|
grep 'without a cast' outa.txt ||\
|
||||||
|
grep 'control reaches end' outa.txt ||\
|
||||||
|
grep 'return type defaults' outa.txt ||\
|
||||||
|
grep 'cast from pointer to integer' outa.txt ||\
|
||||||
|
grep 'useless type name in empty declaration' outa.txt ||\
|
||||||
|
grep 'no semicolon at end' outa.txt ||\
|
||||||
|
grep 'type defaults to' outa.txt ||\
|
||||||
|
grep 'too few arguments for format' outa.txt ||\
|
||||||
|
grep 'incompatible pointer' outa.txt ||\
|
||||||
|
grep 'ordered comparison of pointer with integer' outa.txt ||\
|
||||||
|
grep 'declaration does not declare anything' outa.txt ||\
|
||||||
|
grep 'expects type' outa.txt ||\
|
||||||
|
grep 'pointer from integer' outa.txt ||\
|
||||||
|
grep 'incompatible implicit' outa.txt ||\
|
||||||
|
grep 'excess elements in struct initializer' outa.txt ||\
|
||||||
|
grep 'comparison between pointer and integer' outa.txt ||\
|
||||||
|
! gcc -O1 test_reduced.c > cc_out1.txt 2>&1 ||\
|
||||||
|
! gcc -O2 test_reduced.c > cc_out2.txt 2>&1)
|
||||||
|
then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release -- --parse test_reduced.c >/dev/null 2>&1 &&\
|
cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release -- --parse test_reduced.c >/dev/null 2>&1 &&\
|
||||||
! cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release --bin fuzz -- $FUZZ_ARG test_reduced.c
|
! cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release --bin fuzz -- $FUZZ_ARG test_reduced.c
|
||||||
|
|||||||
Reference in New Issue
Block a user