mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-16 15:38:48 +00:00
Issue homework 2: irgen
This commit is contained in:
366
src/ir/dtype.rs
366
src/ir/dtype.rs
@@ -1,12 +1,13 @@
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::ops::Deref;
|
||||
use itertools::izip;
|
||||
use failure::Fail;
|
||||
use lang_c::ast;
|
||||
use lang_c::span::Node;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
|
||||
use failure::Fail;
|
||||
use crate::ir::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Fail)]
|
||||
pub enum DtypeError {
|
||||
@@ -22,8 +23,11 @@ pub trait HasDtype {
|
||||
#[derive(Default)]
|
||||
struct BaseDtype {
|
||||
scalar: Option<ast::TypeSpecifier>,
|
||||
size_modifiers: Option<ast::TypeSpecifier>,
|
||||
signed_option: Option<ast::TypeSpecifier>,
|
||||
typedef_name: Option<String>,
|
||||
is_const: bool,
|
||||
is_typedef: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
@@ -52,9 +56,40 @@ pub enum Dtype {
|
||||
ret: Box<Dtype>,
|
||||
params: Vec<Dtype>,
|
||||
},
|
||||
Typedef {
|
||||
name: String,
|
||||
is_const: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl BaseDtype {
|
||||
/// Apply `StorageClassSpecifier` to `BaseDtype`
|
||||
///
|
||||
/// let's say declaration is `typedef int i32_t;`, if `self` represents `int`
|
||||
/// and `type_qualifier` represents `typedef`, `self` is transformed to
|
||||
/// representing `typedef int` after function performs.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `self` - Part that has been converted to 'BaseDtype' on the declaration
|
||||
/// * `storage_class` - storage class requiring apply to 'self' immediately
|
||||
///
|
||||
#[inline]
|
||||
fn apply_storage_class(
|
||||
&mut self,
|
||||
storage_class: &ast::StorageClassSpecifier,
|
||||
) -> Result<(), DtypeError> {
|
||||
match storage_class {
|
||||
ast::StorageClassSpecifier::Typedef => {
|
||||
// duplicate `typedef` is allowed
|
||||
self.is_typedef = true;
|
||||
}
|
||||
_ => panic!("unsupported storage class"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply `TypeSpecifier` to `BaseDtype`
|
||||
///
|
||||
/// let's say declaration is `const int a;`, if `self` represents `int`
|
||||
@@ -81,9 +116,11 @@ impl BaseDtype {
|
||||
self.signed_option = Some(type_specifier.clone());
|
||||
}
|
||||
ast::TypeSpecifier::Void
|
||||
| ast::TypeSpecifier::Bool
|
||||
| ast::TypeSpecifier::Char
|
||||
| ast::TypeSpecifier::Int
|
||||
| ast::TypeSpecifier::Float => {
|
||||
| ast::TypeSpecifier::Float
|
||||
| ast::TypeSpecifier::Double => {
|
||||
if self.scalar.is_some() {
|
||||
return Err(DtypeError::Misc {
|
||||
message: "two or more scalar types in declaration specifiers".to_string(),
|
||||
@@ -91,7 +128,23 @@ impl BaseDtype {
|
||||
}
|
||||
self.scalar = Some(type_specifier.clone());
|
||||
}
|
||||
_ => todo!("support more like `double` in the future"),
|
||||
ast::TypeSpecifier::Short | ast::TypeSpecifier::Long => {
|
||||
if self.size_modifiers.is_some() {
|
||||
return Err(DtypeError::Misc {
|
||||
message: "two or more size modifiers in declaration specifiers".to_string(),
|
||||
});
|
||||
}
|
||||
self.size_modifiers = Some(type_specifier.clone());
|
||||
}
|
||||
ast::TypeSpecifier::TypedefName(identifier) => {
|
||||
if self.typedef_name.is_some() {
|
||||
return Err(DtypeError::Misc {
|
||||
message: "two or more typedef names in declaration specifiers".to_string(),
|
||||
});
|
||||
}
|
||||
self.typedef_name = Some(identifier.node.name.clone());
|
||||
}
|
||||
_ => todo!("apply_type_specifier: support {:?}", type_specifier),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -145,9 +198,8 @@ impl BaseDtype {
|
||||
declaration_specifier: &ast::DeclarationSpecifier,
|
||||
) -> Result<(), DtypeError> {
|
||||
match declaration_specifier {
|
||||
// TODO: `dtype` must be defined taking into account all specifier information.
|
||||
ast::DeclarationSpecifier::StorageClass(_storage_class_spec) => {
|
||||
todo!("analyze storage class specifier keyword to create correct `dtype`")
|
||||
ast::DeclarationSpecifier::StorageClass(storage_class) => {
|
||||
self.apply_storage_class(&storage_class.node)?
|
||||
}
|
||||
ast::DeclarationSpecifier::TypeSpecifier(type_specifier) => {
|
||||
self.apply_type_specifier(&type_specifier.node)?
|
||||
@@ -221,48 +273,80 @@ impl TryFrom<BaseDtype> for Dtype {
|
||||
/// # Example
|
||||
///
|
||||
/// For declaration is `const unsigned int * p`, `specifiers` is `const unsigned int`,
|
||||
/// and the result is `Dtype::Int{ width: 32, is_signed: false, is_const: ture }`
|
||||
/// and the result is `Dtype::Int{ width: 4, is_signed: false, is_const: ture }`
|
||||
fn try_from(spec: BaseDtype) -> Result<Self, DtypeError> {
|
||||
assert!(
|
||||
!(spec.scalar.is_none() && spec.signed_option.is_none() && !spec.is_const),
|
||||
!(spec.scalar.is_none()
|
||||
&& spec.size_modifiers.is_none()
|
||||
&& spec.signed_option.is_none()
|
||||
&& spec.typedef_name.is_none()
|
||||
&& !spec.is_const),
|
||||
"BaseDtype is empty"
|
||||
);
|
||||
|
||||
// Creates `dtype` from scalar.
|
||||
let mut dtype = if let Some(t) = spec.scalar {
|
||||
match t {
|
||||
ast::TypeSpecifier::Void => Self::unit(),
|
||||
ast::TypeSpecifier::Unsigned | ast::TypeSpecifier::Signed => {
|
||||
panic!("Signed option to scalar is not supported")
|
||||
}
|
||||
ast::TypeSpecifier::Bool => Self::BOOL,
|
||||
ast::TypeSpecifier::Char => Self::CHAR,
|
||||
ast::TypeSpecifier::Short => Self::SHORT,
|
||||
ast::TypeSpecifier::Int => Self::INT,
|
||||
ast::TypeSpecifier::Long => Self::LONG,
|
||||
ast::TypeSpecifier::Float => Self::FLOAT,
|
||||
ast::TypeSpecifier::Double => Self::DOUBLE,
|
||||
_ => panic!("Unsupported ast::TypeSpecifier"),
|
||||
let mut dtype = if let Some(name) = spec.typedef_name {
|
||||
if spec.scalar.is_some() || spec.signed_option.is_some() {
|
||||
return Err(DtypeError::Misc {
|
||||
message: "typedef' cannot be used with scalar type or signed option"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Dtype::default()
|
||||
};
|
||||
|
||||
// Applies signedness.
|
||||
if let Some(signed_option) = spec.signed_option {
|
||||
let is_signed = match signed_option {
|
||||
ast::TypeSpecifier::Signed => true,
|
||||
ast::TypeSpecifier::Unsigned => false,
|
||||
_ => panic!(
|
||||
"`signed_option` must be `TypeSpecifier::Signed` or `TypeSpecifier::Unsigned`"
|
||||
),
|
||||
Self::typedef(name)
|
||||
} else {
|
||||
// Creates `dtype` from scalar.
|
||||
let mut dtype = if let Some(t) = spec.scalar {
|
||||
match t {
|
||||
ast::TypeSpecifier::Void => Self::unit(),
|
||||
ast::TypeSpecifier::Bool => Self::BOOL,
|
||||
ast::TypeSpecifier::Char => Self::CHAR,
|
||||
ast::TypeSpecifier::Int => Self::INT,
|
||||
ast::TypeSpecifier::Float => Self::FLOAT,
|
||||
ast::TypeSpecifier::Double => Self::DOUBLE,
|
||||
ast::TypeSpecifier::Unsigned | ast::TypeSpecifier::Signed => {
|
||||
panic!("Signed option to scalar is not supported")
|
||||
}
|
||||
_ => panic!("Dtype::try_from::<BaseDtype>: {:?} is not a scalar type", t),
|
||||
}
|
||||
} else {
|
||||
Self::default()
|
||||
};
|
||||
|
||||
dtype = dtype.set_signed(is_signed);
|
||||
}
|
||||
// Applies size modifier
|
||||
if let Some(size_modifiers) = spec.size_modifiers {
|
||||
if dtype != Self::INT {
|
||||
return Err(DtypeError::Misc {
|
||||
message: "size modifier can only be used with `int`".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
dtype = match size_modifiers {
|
||||
ast::TypeSpecifier::Short => Self::SHORT,
|
||||
ast::TypeSpecifier::Long => Self::LONG,
|
||||
_ => panic!(
|
||||
"Dtype::try_from::<BaseDtype>: {:?} is not a size modifier",
|
||||
size_modifiers
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// Applies signedness.
|
||||
if let Some(signed_option) = spec.signed_option {
|
||||
let is_signed = match signed_option {
|
||||
ast::TypeSpecifier::Signed => true,
|
||||
ast::TypeSpecifier::Unsigned => false,
|
||||
_ => panic!(
|
||||
"Dtype::try_from::<BaseDtype>: {:?} is not a signed option",
|
||||
signed_option
|
||||
),
|
||||
};
|
||||
|
||||
dtype = dtype.set_signed(is_signed);
|
||||
}
|
||||
|
||||
dtype
|
||||
};
|
||||
|
||||
// Applies constness.
|
||||
assert!(!dtype.is_const());
|
||||
dtype = dtype.set_const(spec.is_const);
|
||||
|
||||
Ok(dtype)
|
||||
@@ -296,25 +380,48 @@ impl TryFrom<&ast::ParameterDeclaration> for Dtype {
|
||||
|
||||
if let Some(declarator) = ¶meter_decl.declarator {
|
||||
dtype = dtype.with_ast_declarator(&declarator.node)?;
|
||||
|
||||
// A function call with an array argument performs array-to-pointer conversion.
|
||||
// For this reason, when `declarator` is from function parameter declaration
|
||||
// and `base_dtype` is `Dtype::Array`, `base_dtype` is transformed to pointer type.
|
||||
// https://www.eskimo.com/~scs/cclass/notes/sx10f.html
|
||||
if let Some(inner) = dtype.get_array_inner() {
|
||||
dtype = Self::pointer(inner.clone());
|
||||
}
|
||||
}
|
||||
Ok(dtype)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dtype {
|
||||
pub const BOOL: Self = Self::int(1);
|
||||
pub const CHAR: Self = Self::int(8);
|
||||
pub const SHORT: Self = Self::int(16);
|
||||
pub const INT: Self = Self::int(32);
|
||||
pub const LONG: Self = Self::int(64);
|
||||
pub const LONGLONG: Self = Self::int(64);
|
||||
|
||||
pub const FLOAT: Self = Self::float(32);
|
||||
pub const DOUBLE: Self = Self::float(64);
|
||||
|
||||
const WIDTH_OF_BYTE: usize = 8;
|
||||
pub const BITS_OF_BYTE: usize = 8;
|
||||
pub const SIZE_OF_BYTE: usize = 1;
|
||||
// TODO: consider architecture dependency in the future
|
||||
const WIDTH_OF_POINTER: usize = 32;
|
||||
pub const SIZE_OF_POINTER: usize = 4;
|
||||
|
||||
pub const SIZE_OF_CHAR: usize = 1;
|
||||
pub const SIZE_OF_SHORT: usize = 2;
|
||||
pub const SIZE_OF_INT: usize = 4;
|
||||
pub const SIZE_OF_LONG: usize = 8;
|
||||
pub const SIZE_OF_LONGLONG: usize = 8;
|
||||
|
||||
pub const SIZE_OF_FLOAT: usize = 4;
|
||||
pub const SIZE_OF_DOUBLE: usize = 8;
|
||||
|
||||
// signed option cannot be applied to boolean value
|
||||
pub const BOOL: Self = Self::Int {
|
||||
width: 1,
|
||||
is_signed: false,
|
||||
is_const: false,
|
||||
};
|
||||
pub const CHAR: Self = Self::int(Self::SIZE_OF_CHAR * Self::BITS_OF_BYTE);
|
||||
pub const SHORT: Self = Self::int(Self::SIZE_OF_SHORT * Self::BITS_OF_BYTE);
|
||||
pub const INT: Self = Self::int(Self::SIZE_OF_INT * Self::BITS_OF_BYTE);
|
||||
pub const LONG: Self = Self::int(Self::SIZE_OF_LONG * Self::BITS_OF_BYTE);
|
||||
pub const LONGLONG: Self = Self::int(Self::SIZE_OF_LONGLONG * Self::BITS_OF_BYTE);
|
||||
|
||||
pub const FLOAT: Self = Self::float(Self::SIZE_OF_FLOAT * Self::BITS_OF_BYTE);
|
||||
pub const DOUBLE: Self = Self::float(Self::SIZE_OF_DOUBLE * Self::BITS_OF_BYTE);
|
||||
|
||||
#[inline]
|
||||
pub const fn unit() -> Self {
|
||||
@@ -378,6 +485,14 @@ impl Dtype {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn typedef(name: String) -> Self {
|
||||
Self::Typedef {
|
||||
name,
|
||||
is_const: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_int_width(&self) -> Option<usize> {
|
||||
if let Self::Int { width, .. } = self {
|
||||
@@ -405,6 +520,15 @@ impl Dtype {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_array_inner(&self) -> Option<&Dtype> {
|
||||
if let Self::Array { inner, .. } = self {
|
||||
Some(inner.deref())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_function_inner(&self) -> Option<(&Dtype, &Vec<Dtype>)> {
|
||||
if let Self::Function { ret, params } = self {
|
||||
@@ -441,6 +565,7 @@ impl Dtype {
|
||||
Self::Pointer { is_const, .. } => *is_const,
|
||||
Self::Array { .. } => true,
|
||||
Self::Function { .. } => true,
|
||||
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,6 +583,7 @@ impl Dtype {
|
||||
Self::Pointer { inner, .. } => Self::Pointer { inner, is_const },
|
||||
Self::Array { .. } => self,
|
||||
Self::Function { .. } => self,
|
||||
Self::Typedef { name, .. } => Self::Typedef { name, is_const },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,17 +591,12 @@ impl Dtype {
|
||||
match self {
|
||||
Self::Unit { .. } => Ok((0, 1)),
|
||||
Self::Int { width, .. } | Self::Float { width, .. } => {
|
||||
let align_of = *width / Self::WIDTH_OF_BYTE;
|
||||
let size_of = align_of;
|
||||
|
||||
Ok((size_of, align_of))
|
||||
}
|
||||
Self::Pointer { .. } => {
|
||||
let align_of = Self::WIDTH_OF_POINTER / Self::WIDTH_OF_BYTE;
|
||||
let size_of = align_of;
|
||||
let size_of = (*width + Self::BITS_OF_BYTE - 1) / Self::BITS_OF_BYTE;
|
||||
let align_of = size_of;
|
||||
|
||||
Ok((size_of, align_of))
|
||||
}
|
||||
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()?;
|
||||
|
||||
@@ -484,9 +605,8 @@ impl Dtype {
|
||||
align_of_inner,
|
||||
))
|
||||
}
|
||||
Self::Function { .. } => Err(DtypeError::Misc {
|
||||
message: "`size_align_of` cannot be used with function types".to_string(),
|
||||
}),
|
||||
Self::Function { .. } => Ok((0, 1)),
|
||||
Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,22 +626,26 @@ impl Dtype {
|
||||
/// Derive a data type from declaration specifiers.
|
||||
pub fn try_from_ast_declaration_specifiers(
|
||||
specifiers: &[Node<ast::DeclarationSpecifier>],
|
||||
) -> Result<Self, DtypeError> {
|
||||
) -> Result<(Self, bool), DtypeError> {
|
||||
let mut spec = BaseDtype::default();
|
||||
BaseDtype::apply_declaration_specifiers(&mut spec, specifiers)?;
|
||||
Self::try_from(spec)
|
||||
let is_typedef = spec.is_typedef;
|
||||
let dtype = Self::try_from(spec)?;
|
||||
|
||||
Ok((dtype, is_typedef))
|
||||
}
|
||||
|
||||
/// Generate `Dtype` based on declarator and `base_dtype` 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;`.
|
||||
/// In general `base_dtype` start with `const int` which has scalar type and
|
||||
/// In general `self` start with `const int` which has scalar type and
|
||||
/// `declarator` represents `* const * const` with `ast::Declarator`
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `declarator` - Parts requiring conversion to 'Dtype' on the declaration
|
||||
/// * `base_dtype` - Part that has been converted to 'Dtype' on the declaration
|
||||
/// * `decl_spec` - Containing information that should be referenced
|
||||
/// when creating `Dtype` from `Declarator`.
|
||||
///
|
||||
pub fn with_ast_declarator(mut self, declarator: &ast::Declarator) -> Result<Self, DtypeError> {
|
||||
for derived_decl in &declarator.derived {
|
||||
@@ -533,7 +657,10 @@ impl Dtype {
|
||||
}
|
||||
Self::pointer(self).set_const(specifier.is_const)
|
||||
}
|
||||
ast::DerivedDeclarator::Array(_array_decl) => todo!(),
|
||||
ast::DerivedDeclarator::Array(array_decl) => {
|
||||
assert!(array_decl.node.qualifiers.is_empty());
|
||||
self.with_ast_array_size(&array_decl.node.size)?
|
||||
}
|
||||
ast::DerivedDeclarator::Function(func_decl) => {
|
||||
let params = func_decl
|
||||
.node
|
||||
@@ -561,37 +688,81 @@ impl Dtype {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether type conflict exists between the two `Dtype` objects.
|
||||
/// Generates `Dtype` based on declarator and `self` which has scalar type.
|
||||
///
|
||||
/// let's say expression is `const int a = 0; int b = 0; int c = a + b`.
|
||||
/// Although `const int` of `a` and `int` of `b` looks different, `Plus`(+) operations between
|
||||
/// these two types are possible without any type-casting. There is no conflict between
|
||||
/// `const int` and `int`.
|
||||
/// Let's say the AST declaration is `int a[2][3]`; `self` represents `int [2]`; and
|
||||
/// `array_size` is `[3]`. Then this function should return `int [2][3]`.
|
||||
///
|
||||
/// However, only the outermost const is ignored.
|
||||
/// If check equivalence between `const int *const` and `int *`, result is false. Because
|
||||
/// the second `const` (means left most `const`) of the `const int *const` is missed in `int *`.
|
||||
/// By the way, outermost `const` (means right most `const`) is not a consideration here.
|
||||
pub fn is_compatible(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Unit { .. }, Self::Unit { .. })
|
||||
| (Self::Int { .. }, Self::Int { .. })
|
||||
| (Self::Float { .. }, Self::Float { .. })
|
||||
| (Self::Pointer { .. }, Self::Pointer { .. }) => {
|
||||
self.clone().set_const(false) == other.clone().set_const(false)
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `array_size` - the array size to add to the dtype `self`
|
||||
///
|
||||
pub fn with_ast_array_size(self, array_size: &ast::ArraySize) -> Result<Self, DtypeError> {
|
||||
let expr = if let ast::ArraySize::VariableExpression(expr) = array_size {
|
||||
&expr.node
|
||||
} else {
|
||||
panic!("`ArraySize` is unsupported except `ArraySize::VariableExpression`")
|
||||
};
|
||||
|
||||
let constant = Constant::try_from(expr)
|
||||
.expect("expression of `ArraySize::VariableExpression` must be constant value");
|
||||
|
||||
let (value, _, is_signed) = constant.get_int().ok_or_else(|| DtypeError::Misc {
|
||||
message: "expression is not an integer constant expression".to_string(),
|
||||
})?;
|
||||
|
||||
if is_signed && (value as i128) < 0 {
|
||||
return Err(DtypeError::Misc {
|
||||
message: "declared as an array with a negative size".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Self::array(self, value as usize))
|
||||
}
|
||||
|
||||
pub fn resolve_typedefs(self, typedefs: &HashMap<String, 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)?;
|
||||
Dtype::pointer(inner).set_const(*is_const)
|
||||
}
|
||||
(
|
||||
Self::Function { ret, params },
|
||||
Self::Function {
|
||||
ret: other_ret,
|
||||
params: other_params,
|
||||
},
|
||||
) => {
|
||||
ret == other_ret
|
||||
&& params.len() == other_params.len()
|
||||
&& izip!(params, other_params).all(|(l, r)| l.is_compatible(r))
|
||||
Self::Array { inner, size } => {
|
||||
let inner = inner.deref().clone().resolve_typedefs(typedefs)?;
|
||||
Dtype::array(inner, *size)
|
||||
}
|
||||
_ => false,
|
||||
Self::Function { ret, params } => {
|
||||
let ret = ret.deref().clone().resolve_typedefs(typedefs)?;
|
||||
let params = params
|
||||
.iter()
|
||||
.map(|p| p.clone().resolve_typedefs(typedefs))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Dtype::function(ret, params)
|
||||
}
|
||||
Self::Typedef { name, is_const } => {
|
||||
let dtype = typedefs
|
||||
.get(name)
|
||||
.ok_or_else(|| DtypeError::Misc {
|
||||
message: format!("unknown type name `{}`", name),
|
||||
})?
|
||||
.clone();
|
||||
let is_const = dtype.is_const() || *is_const;
|
||||
|
||||
dtype.set_const(is_const)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(dtype)
|
||||
}
|
||||
|
||||
pub fn merge(self, other: Self) -> Result<Self, DtypeError> {
|
||||
if self == other {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(DtypeError::Misc {
|
||||
message: format!("Dtype::merge({:?}, {:?}) failed", self, other),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -615,7 +786,7 @@ impl fmt::Display for Dtype {
|
||||
write!(f, "{}f{}", if *is_const { "const " } else { "" }, width)
|
||||
}
|
||||
Self::Pointer { inner, is_const } => {
|
||||
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::Function { ret, params } => write!(
|
||||
@@ -628,6 +799,9 @@ impl fmt::Display for Dtype {
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
Self::Typedef { name, is_const } => {
|
||||
write!(f, "{}{}", if *is_const { "const " } else { "" }, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
376
src/ir/interp.rs
376
src/ir/interp.rs
@@ -1,4 +1,5 @@
|
||||
use core::fmt;
|
||||
use core::iter;
|
||||
use core::mem;
|
||||
use failure::Fail;
|
||||
use std::collections::HashMap;
|
||||
@@ -31,11 +32,39 @@ pub enum Value {
|
||||
},
|
||||
Pointer {
|
||||
bid: Option<usize>,
|
||||
offset: usize,
|
||||
offset: isize,
|
||||
dtype: Dtype,
|
||||
},
|
||||
Array {
|
||||
inner_dtype: Dtype,
|
||||
values: Vec<Value>,
|
||||
},
|
||||
}
|
||||
|
||||
impl HasDtype for Value {
|
||||
fn dtype(&self) -> Dtype {
|
||||
match self {
|
||||
Self::Undef { dtype } => dtype.clone(),
|
||||
Self::Unit => Dtype::unit(),
|
||||
Self::Int {
|
||||
width, is_signed, ..
|
||||
} => Dtype::int(*width).set_signed(*is_signed),
|
||||
Self::Float { width, .. } => Dtype::float(*width),
|
||||
Self::Pointer { dtype, .. } => Dtype::pointer(dtype.clone()),
|
||||
Self::Array {
|
||||
inner_dtype,
|
||||
values,
|
||||
} => Dtype::array(inner_dtype.clone(), values.len()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
#[inline]
|
||||
fn undef(dtype: Dtype) -> Self {
|
||||
Self::Undef { dtype }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unit() -> Self {
|
||||
Self::Unit
|
||||
@@ -56,8 +85,8 @@ impl Value {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pointer(bid: Option<usize>, offset: usize) -> Self {
|
||||
Self::Pointer { bid, offset }
|
||||
fn pointer(bid: Option<usize>, offset: isize, dtype: Dtype) -> Self {
|
||||
Self::Pointer { bid, offset, dtype }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -75,19 +104,20 @@ impl Value {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_pointer(self) -> Option<(Option<usize>, usize)> {
|
||||
if let Value::Pointer { bid, offset } = self {
|
||||
Some((bid, offset))
|
||||
fn get_pointer(&self) -> Option<(&Option<usize>, &isize, &Dtype)> {
|
||||
if let Value::Pointer { bid, offset, dtype } = self {
|
||||
Some((bid, offset, dtype))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn nullptr() -> Self {
|
||||
fn nullptr(dtype: Dtype) -> Self {
|
||||
Self::Pointer {
|
||||
bid: None,
|
||||
offset: 0,
|
||||
dtype,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +129,10 @@ impl Value {
|
||||
width, is_signed, ..
|
||||
} => Self::int(u128::default(), *width, *is_signed),
|
||||
ir::Dtype::Float { width, .. } => Self::float(f64::default(), *width),
|
||||
ir::Dtype::Pointer { .. } => Self::nullptr(),
|
||||
ir::Dtype::Pointer { inner, .. } => Self::nullptr(inner.deref().clone()),
|
||||
ir::Dtype::Array { .. } => panic!("array type does not have a default value"),
|
||||
ir::Dtype::Function { .. } => panic!("function type does not have a default value"),
|
||||
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,9 +279,11 @@ mod calculator {
|
||||
assert_eq!(lhs_s, rhs_s);
|
||||
|
||||
match op {
|
||||
// TODO: consider signed value in the future
|
||||
ast::BinaryOperator::Plus => Ok(Value::int(lhs + rhs, lhs_w, lhs_s)),
|
||||
ast::BinaryOperator::Minus => Ok(Value::int(lhs - rhs, lhs_w, lhs_s)),
|
||||
ast::BinaryOperator::Multiply => Ok(Value::int(lhs * rhs, lhs_w, lhs_s)),
|
||||
ast::BinaryOperator::Modulo => Ok(Value::int(lhs % rhs, lhs_w, lhs_s)),
|
||||
ast::BinaryOperator::Equals => {
|
||||
let result = if lhs == rhs { 1 } else { 0 };
|
||||
Ok(Value::int(result, 1, lhs_s))
|
||||
@@ -263,11 +296,28 @@ mod calculator {
|
||||
let result = if lhs < rhs { 1 } else { 0 };
|
||||
Ok(Value::int(result, 1, lhs_s))
|
||||
}
|
||||
ast::BinaryOperator::Greater => {
|
||||
let result = if lhs > rhs { 1 } else { 0 };
|
||||
Ok(Value::int(result, 1, lhs_s))
|
||||
}
|
||||
ast::BinaryOperator::LessOrEqual => {
|
||||
let result = if lhs <= rhs { 1 } else { 0 };
|
||||
Ok(Value::int(result, 1, lhs_s))
|
||||
}
|
||||
ast::BinaryOperator::GreaterOrEqual => {
|
||||
let result = if lhs >= rhs { 1 } else { 0 };
|
||||
Ok(Value::int(result, 1, lhs_s))
|
||||
}
|
||||
_ => todo!("will be covered all operator"),
|
||||
ast::BinaryOperator::LogicalAnd => {
|
||||
assert!(lhs < 2);
|
||||
assert!(rhs < 2);
|
||||
let result = lhs | rhs;
|
||||
Ok(Value::int(result, 1, lhs_s))
|
||||
}
|
||||
_ => todo!(
|
||||
"calculate_binary_operator_expression: not supported operator {:?}",
|
||||
op
|
||||
),
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
@@ -336,45 +386,246 @@ mod calculator {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
enum Byte {
|
||||
Undef,
|
||||
Concrete(u8),
|
||||
Pointer {
|
||||
bid: usize,
|
||||
offset: isize,
|
||||
index: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq)]
|
||||
struct Memory {
|
||||
// TODO: memory type should change to Vec<Vec<Byte>>
|
||||
inner: Vec<Vec<Value>>,
|
||||
inner: Vec<Option<Vec<Byte>>>,
|
||||
}
|
||||
|
||||
impl Byte {
|
||||
#[inline]
|
||||
fn concrete(byte: u8) -> Self {
|
||||
Self::Concrete(byte)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pointer(bid: usize, offset: isize, index: usize) -> Self {
|
||||
Self::Pointer { bid, offset, index }
|
||||
}
|
||||
|
||||
fn get_concrete(&self) -> Option<u8> {
|
||||
if let Self::Concrete(byte) = self {
|
||||
Some(*byte)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pointer(&self) -> Option<(usize, isize, usize)> {
|
||||
if let Self::Pointer { bid, offset, index } = self {
|
||||
Some((*bid, *offset, *index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn block_from_dtype(dtype: &Dtype) -> Vec<Self> {
|
||||
let size = dtype.size_align_of().unwrap().0;
|
||||
iter::repeat(Self::Undef).take(size).collect()
|
||||
}
|
||||
|
||||
fn u128_to_bytes(mut value: u128, size: usize) -> Vec<u8> {
|
||||
let divisor = 1u128 << Dtype::BITS_OF_BYTE;
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..size {
|
||||
bytes.push((value % divisor) as u8);
|
||||
value /= divisor;
|
||||
}
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
fn bytes_to_u128(bytes: &[u8], is_signed: bool) -> u128 {
|
||||
let width = bytes.len();
|
||||
assert!(0 < width && width <= 16);
|
||||
|
||||
let is_negative = is_signed && *bytes.last().unwrap() >= 128;
|
||||
let mut array = [if is_negative { 255 } else { 0 }; 16];
|
||||
array[0..width].copy_from_slice(bytes);
|
||||
u128::from_le_bytes(array)
|
||||
}
|
||||
|
||||
fn bytes_to_value<'b, I>(bytes: &mut I, dtype: &Dtype) -> Result<Value, InterpreterError>
|
||||
where
|
||||
I: Iterator<Item = &'b Self>,
|
||||
{
|
||||
match dtype {
|
||||
ir::Dtype::Unit { .. } => Ok(Value::Unit),
|
||||
ir::Dtype::Int {
|
||||
width, is_signed, ..
|
||||
} => {
|
||||
let value = some_or!(
|
||||
bytes
|
||||
.by_ref()
|
||||
.take(*width)
|
||||
.map(|b| b.get_concrete())
|
||||
.collect::<Option<Vec<_>>>(),
|
||||
return Ok(Value::undef(dtype.clone()))
|
||||
);
|
||||
let value = Self::bytes_to_u128(&value, *is_signed);
|
||||
Ok(Value::int(value, *width, *is_signed))
|
||||
}
|
||||
ir::Dtype::Float { width, .. } => {
|
||||
let value = some_or!(
|
||||
bytes
|
||||
.by_ref()
|
||||
.take(*width)
|
||||
.map(|b| b.get_concrete())
|
||||
.collect::<Option<Vec<_>>>(),
|
||||
return Ok(Value::undef(dtype.clone()))
|
||||
);
|
||||
let value = Self::bytes_to_u128(&value, false);
|
||||
let value = if *width == Dtype::SIZE_OF_FLOAT {
|
||||
f32::from_bits(value as u32) as f64
|
||||
} else {
|
||||
f64::from_bits(value as u64)
|
||||
};
|
||||
|
||||
Ok(Value::float(value, *width))
|
||||
}
|
||||
ir::Dtype::Pointer { inner, .. } => {
|
||||
let value = some_or!(
|
||||
bytes
|
||||
.by_ref()
|
||||
.take(Dtype::SIZE_OF_POINTER)
|
||||
.map(|b| b.get_pointer())
|
||||
.collect::<Option<Vec<_>>>(),
|
||||
return Ok(Value::undef(dtype.clone()))
|
||||
);
|
||||
|
||||
let (bid, offset, _) = value.first().expect("not empty");
|
||||
|
||||
Ok(
|
||||
if !value
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(idx, ptr)| *ptr == (*bid, *offset, idx))
|
||||
{
|
||||
Value::undef(inner.deref().clone())
|
||||
} else {
|
||||
Value::pointer(Some(*bid), *offset, inner.deref().clone())
|
||||
},
|
||||
)
|
||||
}
|
||||
ir::Dtype::Array { inner, size } => {
|
||||
let (inner_size, inner_align) = inner.size_align_of().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);
|
||||
Ok(value)
|
||||
})
|
||||
.collect::<Result<Vec<_>, InterpreterError>>()?;
|
||||
Ok(Value::Array {
|
||||
inner_dtype: inner.deref().clone(),
|
||||
values,
|
||||
})
|
||||
}
|
||||
ir::Dtype::Function { .. } => panic!("function value cannot be constructed from bytes"),
|
||||
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||
}
|
||||
}
|
||||
|
||||
fn value_to_bytes(value: &Value) -> Vec<Self> {
|
||||
match value {
|
||||
Value::Undef { dtype } => Self::block_from_dtype(dtype),
|
||||
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)
|
||||
.iter()
|
||||
.map(|b| Self::concrete(*b))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
Value::Float { value, width } => {
|
||||
let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE;
|
||||
let value: u128 = match size {
|
||||
Dtype::SIZE_OF_FLOAT => (*value as f32).to_bits() as u128,
|
||||
Dtype::SIZE_OF_DOUBLE => (*value as f64).to_bits() as u128,
|
||||
_ => panic!("value_to_bytes: {} is not a valid float size", size),
|
||||
};
|
||||
|
||||
Self::u128_to_bytes(value, size)
|
||||
.iter()
|
||||
.map(|b| Self::concrete(*b))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
Value::Pointer { bid, offset, .. } => (0..Dtype::SIZE_OF_POINTER)
|
||||
.map(|i| Self::pointer(bid.unwrap(), *offset, i))
|
||||
.collect(),
|
||||
Value::Array {
|
||||
inner_dtype,
|
||||
values,
|
||||
} => {
|
||||
let (inner_size, inner_align) = inner_dtype.size_align_of().unwrap();
|
||||
let padding = std::cmp::max(inner_size, inner_align) - inner_size;
|
||||
values
|
||||
.iter()
|
||||
.map(|v| {
|
||||
let mut result = Self::value_to_bytes(v);
|
||||
result.extend(iter::repeat(Byte::Undef).take(padding));
|
||||
result
|
||||
})
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
fn alloc(&mut self, dtype: &Dtype) -> Result<usize, InterpreterError> {
|
||||
let memory_block = Self::block_from_dtype(dtype);
|
||||
self.inner.push(memory_block);
|
||||
|
||||
Ok(self.inner.len() - 1)
|
||||
let bid = self.inner.len();
|
||||
self.inner.push(Some(Byte::block_from_dtype(dtype)));
|
||||
Ok(bid)
|
||||
}
|
||||
|
||||
fn load(&self, bid: usize, offset: usize) -> &Value {
|
||||
&self.inner[bid][offset]
|
||||
fn dealloc(
|
||||
&mut self,
|
||||
bid: usize,
|
||||
offset: isize,
|
||||
dtype: &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
|
||||
);
|
||||
*block = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn store(&mut self, bid: usize, offset: usize, value: Value) {
|
||||
self.inner[bid][offset] = value;
|
||||
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 mut iter = self.inner[bid].as_ref().unwrap()[offset..(offset + size)].iter();
|
||||
Byte::bytes_to_value(&mut iter, dtype)
|
||||
}
|
||||
|
||||
fn block_from_dtype(dtype: &Dtype) -> Vec<Value> {
|
||||
match dtype {
|
||||
ir::Dtype::Unit { .. } => vec![],
|
||||
ir::Dtype::Int { .. } | ir::Dtype::Float { .. } | ir::Dtype::Pointer { .. } => {
|
||||
vec![Value::Undef {
|
||||
dtype: dtype.clone(),
|
||||
}]
|
||||
}
|
||||
ir::Dtype::Array { inner, size, .. } => {
|
||||
let sub_vec = Self::block_from_dtype(inner.deref());
|
||||
(0..*size).fold(vec![], |mut result, _| {
|
||||
result.append(&mut sub_vec.clone());
|
||||
result
|
||||
})
|
||||
}
|
||||
ir::Dtype::Function { .. } => vec![],
|
||||
}
|
||||
fn store(&mut self, bid: usize, offset: isize, value: &Value) {
|
||||
assert!(0 <= offset);
|
||||
let offset = offset as usize;
|
||||
let size = value.dtype().size_align_of().unwrap().0;
|
||||
let bytes = Byte::value_to_bytes(value);
|
||||
self.inner[bid]
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.splice(offset..(offset + size), bytes.iter().cloned());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,10 +694,11 @@ impl<'i> State<'i> {
|
||||
Value::default_from_dtype(&dtype)
|
||||
};
|
||||
|
||||
self.memory.store(bid, 0, value);
|
||||
self.memory.store(bid, 0, &value);
|
||||
}
|
||||
ir::Dtype::Array { .. } => todo!("Initializer::List is needed"),
|
||||
ir::Dtype::Function { .. } => panic!("function variable does not exist"),
|
||||
ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||
},
|
||||
// If functin declaration, skip initialization
|
||||
Declaration::Function { .. } => (),
|
||||
@@ -460,8 +712,8 @@ impl<'i> State<'i> {
|
||||
// add alloc register
|
||||
for (id, allocation) in self.stack_frame.func_def.allocations.iter().enumerate() {
|
||||
let bid = self.memory.alloc(&allocation)?;
|
||||
let ptr = Value::pointer(Some(bid), 0);
|
||||
let rid = RegisterId::local("".to_string(), id);
|
||||
let ptr = Value::pointer(Some(bid), 0, allocation.deref().clone());
|
||||
let rid = RegisterId::local(id);
|
||||
|
||||
self.stack_frame.registers.write(rid, ptr)
|
||||
}
|
||||
@@ -498,7 +750,17 @@ impl<'i> State<'i> {
|
||||
|
||||
// If it's returning from a function, pop the stack frame.
|
||||
|
||||
// TODO: free memory allocated in the callee
|
||||
// Frees memory allocated in the callee
|
||||
for (i, d) in self.stack_frame.func_def.allocations.iter().enumerate() {
|
||||
let (bid, offset, dtype) = self
|
||||
.stack_frame
|
||||
.registers
|
||||
.read(RegisterId::local(i))
|
||||
.get_pointer()
|
||||
.unwrap();
|
||||
assert_eq!(d.deref(), dtype);
|
||||
self.memory.dealloc(bid.unwrap(), *offset, dtype)?;
|
||||
}
|
||||
|
||||
// restore previous state
|
||||
let prev_stack_frame = some_or!(self.stack.pop(), return Ok(Some(return_value)));
|
||||
@@ -526,7 +788,8 @@ impl<'i> State<'i> {
|
||||
) -> Result<Vec<Value>, InterpreterError> {
|
||||
// Check that the dtype of each args matches the expected
|
||||
if !(args.len() == signature.params.len()
|
||||
&& izip!(args, &signature.params).all(|(a, d)| a.dtype().is_compatible(d)))
|
||||
&& izip!(args, &signature.params)
|
||||
.all(|(a, d)| a.dtype().set_const(false) == d.clone().set_const(false)))
|
||||
{
|
||||
panic!("dtype of args and params must be compatible")
|
||||
}
|
||||
@@ -546,16 +809,14 @@ impl<'i> State<'i> {
|
||||
|
||||
assert_eq!(arg.args.len(), block.phinodes.len());
|
||||
for (a, d) in izip!(&arg.args, &block.phinodes) {
|
||||
assert!(a.dtype().is_compatible(&d));
|
||||
assert!(a.dtype().set_const(false) == d.deref().clone().set_const(false));
|
||||
}
|
||||
|
||||
for (i, a) in arg.args.iter().enumerate() {
|
||||
let v = self.interp_operand(a.clone()).unwrap();
|
||||
self.stack_frame
|
||||
.registers
|
||||
.inner
|
||||
.insert(RegisterId::arg(arg.bid, i), v)
|
||||
.unwrap();
|
||||
.write(RegisterId::arg(arg.bid, i), v);
|
||||
}
|
||||
|
||||
self.stack_frame.pc = Pc::new(arg.bid);
|
||||
@@ -629,21 +890,20 @@ impl<'i> State<'i> {
|
||||
Instruction::Store { ptr, value, .. } => {
|
||||
let ptr = self.interp_operand(ptr.clone())?;
|
||||
let value = self.interp_operand(value.clone())?;
|
||||
let (bid, offset) = self.interp_ptr(ptr)?;
|
||||
self.memory.store(bid, offset, value);
|
||||
|
||||
let (bid, offset, _) = self.interp_ptr(&ptr)?;
|
||||
self.memory.store(bid, offset, &value);
|
||||
Value::Unit
|
||||
}
|
||||
Instruction::Load { ptr, .. } => {
|
||||
let ptr = self.interp_operand(ptr.clone())?;
|
||||
let (bid, offset) = self.interp_ptr(ptr)?;
|
||||
self.memory.load(bid, offset).clone()
|
||||
let (bid, offset, dtype) = self.interp_ptr(&ptr)?;
|
||||
self.memory.load(bid, offset, &dtype)?
|
||||
}
|
||||
Instruction::Call { callee, args, .. } => {
|
||||
let ptr = self.interp_operand(callee.clone())?;
|
||||
|
||||
// Get function name from pointer
|
||||
let (bid, _) = ptr.get_pointer().expect("`ptr` must be `Value::Pointer`");
|
||||
let (bid, _, _) = ptr.get_pointer().expect("`ptr` must be `Value::Pointer`");
|
||||
let bid = bid.expect("pointer for global variable must have bid value");
|
||||
let callee_name = self
|
||||
.global_map
|
||||
@@ -721,8 +981,11 @@ impl<'i> State<'i> {
|
||||
width,
|
||||
is_signed,
|
||||
},
|
||||
Constant::Float { value, width } => Value::Float { value, width },
|
||||
Constant::GlobalVariable { name, .. } => {
|
||||
Constant::Float { value, width } => Value::Float {
|
||||
value: value.into_inner(),
|
||||
width,
|
||||
},
|
||||
Constant::GlobalVariable { name, dtype } => {
|
||||
let bid = self
|
||||
.global_map
|
||||
.get_bid(&name)
|
||||
@@ -732,13 +995,14 @@ impl<'i> State<'i> {
|
||||
Value::Pointer {
|
||||
bid: Some(bid),
|
||||
offset: 0,
|
||||
dtype,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn interp_ptr(&mut self, pointer: Value) -> Result<(usize, usize), InterpreterError> {
|
||||
let (bid, offset) = pointer
|
||||
fn interp_ptr(&mut self, pointer: &Value) -> Result<(usize, isize, Dtype), InterpreterError> {
|
||||
let (bid, offset, dtype) = pointer
|
||||
.get_pointer()
|
||||
.ok_or_else(|| InterpreterError::Misc {
|
||||
func_name: self.stack_frame.func_name.clone(),
|
||||
@@ -752,7 +1016,7 @@ impl<'i> State<'i> {
|
||||
msg: "Accessing memory with constant pointer".into(),
|
||||
})?;
|
||||
|
||||
Ok((bid, offset))
|
||||
Ok((bid, *offset, dtype.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
465
src/ir/mod.rs
465
src/ir/mod.rs
@@ -4,8 +4,9 @@ mod write_ir;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::ops::Deref;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use lang_c::ast;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
@@ -60,6 +61,7 @@ impl TryFrom<Dtype> for Declaration {
|
||||
signature: FunctionSignature::new(dtype),
|
||||
definition: None,
|
||||
}),
|
||||
Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,10 +102,6 @@ impl Declaration {
|
||||
}
|
||||
|
||||
/// Check if type is conflicting for pre-declared one
|
||||
///
|
||||
/// In case of `Variable`, need to check if the two types are exactly the same.
|
||||
/// On the other hand, in the case of `Function`, outermost `const` of return type and
|
||||
/// parameters one is not an issue of concern.
|
||||
pub fn is_compatible(&self, other: &Declaration) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Variable { dtype, .. }, Self::Variable { dtype: other, .. }) => dtype == other,
|
||||
@@ -112,7 +110,7 @@ impl Declaration {
|
||||
Self::Function {
|
||||
signature: other, ..
|
||||
},
|
||||
) => signature.dtype().is_compatible(&other.dtype()),
|
||||
) => signature.dtype() == other.dtype(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -127,19 +125,6 @@ impl HasDtype for Declaration {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct FunctionDefinition {
|
||||
/// Memory allocations for local variables. The allocation is performed at the beginning of a
|
||||
/// function invocation.
|
||||
pub allocations: Vec<Dtype>,
|
||||
|
||||
/// Basic blocks.
|
||||
pub blocks: HashMap<BlockId, Block>,
|
||||
|
||||
/// The initial block id.
|
||||
pub bid_init: BlockId,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct FunctionSignature {
|
||||
pub ret: Dtype,
|
||||
@@ -164,15 +149,229 @@ impl HasDtype for FunctionSignature {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct FunctionDefinition {
|
||||
/// Memory allocations for local variables. The allocation is performed at the beginning of a
|
||||
/// function invocation.
|
||||
pub allocations: Vec<Named<Dtype>>,
|
||||
|
||||
/// Basic blocks.
|
||||
pub blocks: HashMap<BlockId, Block>,
|
||||
|
||||
/// The initial block id.
|
||||
pub bid_init: BlockId,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub struct BlockId(pub usize);
|
||||
|
||||
impl fmt::Display for BlockId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "b{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Block {
|
||||
pub phinodes: Vec<Named<Dtype>>,
|
||||
pub instructions: Vec<Named<Instruction>>,
|
||||
pub exit: BlockExit,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Instruction {
|
||||
// TODO: the variants of Instruction will be added in the future
|
||||
Nop,
|
||||
BinOp {
|
||||
op: ast::BinaryOperator,
|
||||
lhs: Operand,
|
||||
rhs: Operand,
|
||||
dtype: Dtype,
|
||||
},
|
||||
UnaryOp {
|
||||
op: ast::UnaryOperator,
|
||||
operand: Operand,
|
||||
dtype: Dtype,
|
||||
},
|
||||
Store {
|
||||
ptr: Operand,
|
||||
value: Operand,
|
||||
},
|
||||
Load {
|
||||
ptr: Operand,
|
||||
},
|
||||
Call {
|
||||
callee: Operand,
|
||||
args: Vec<Operand>,
|
||||
return_type: Dtype,
|
||||
},
|
||||
TypeCast {
|
||||
value: Operand,
|
||||
target_dtype: Dtype,
|
||||
},
|
||||
}
|
||||
|
||||
impl HasDtype for Instruction {
|
||||
fn dtype(&self) -> Dtype {
|
||||
match self {
|
||||
Self::Nop => Dtype::unit(),
|
||||
Self::BinOp { dtype, .. } => dtype.clone(),
|
||||
Self::UnaryOp { dtype, .. } => dtype.clone(),
|
||||
Self::Store { .. } => Dtype::unit(),
|
||||
Self::Load { ptr } => ptr
|
||||
.dtype()
|
||||
.get_pointer_inner()
|
||||
.expect("Load instruction must have pointer value as operand")
|
||||
.deref()
|
||||
.clone()
|
||||
.set_const(false),
|
||||
Self::Call { return_type, .. } => return_type.clone(),
|
||||
Self::TypeCast { target_dtype, .. } => target_dtype.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub fn is_pure(&self) -> bool {
|
||||
match self {
|
||||
Self::Store { .. } => false,
|
||||
Self::Call { .. } => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum BlockExit {
|
||||
Jump {
|
||||
arg: JumpArg,
|
||||
},
|
||||
ConditionalJump {
|
||||
condition: Operand,
|
||||
arg_then: JumpArg,
|
||||
arg_else: JumpArg,
|
||||
},
|
||||
Switch {
|
||||
value: Operand,
|
||||
default: JumpArg,
|
||||
cases: Vec<(Constant, JumpArg)>,
|
||||
},
|
||||
Return {
|
||||
value: Operand,
|
||||
},
|
||||
Unreachable,
|
||||
}
|
||||
|
||||
impl BlockExit {
|
||||
pub fn walk_jump_args<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(&mut JumpArg),
|
||||
{
|
||||
match self {
|
||||
Self::Jump { arg } => f(arg),
|
||||
Self::ConditionalJump {
|
||||
arg_then, arg_else, ..
|
||||
} => {
|
||||
f(arg_then);
|
||||
f(arg_else);
|
||||
}
|
||||
Self::Switch { default, cases, .. } => {
|
||||
f(default);
|
||||
for (_, arg) in cases {
|
||||
f(arg);
|
||||
}
|
||||
}
|
||||
Self::Return { .. } | Self::Unreachable => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct JumpArg {
|
||||
pub bid: BlockId,
|
||||
pub args: Vec<Operand>,
|
||||
}
|
||||
|
||||
impl JumpArg {
|
||||
pub fn new(bid: BlockId, args: Vec<Operand>) -> Self {
|
||||
Self { bid, args }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for JumpArg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}({:?})", self.bid, self.args)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Operand {
|
||||
Constant(Constant),
|
||||
Register { rid: RegisterId, dtype: Dtype },
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
pub fn constant(value: Constant) -> Self {
|
||||
Self::Constant(value)
|
||||
}
|
||||
|
||||
pub fn register(rid: RegisterId, dtype: Dtype) -> Self {
|
||||
Self::Register { rid, dtype }
|
||||
}
|
||||
|
||||
pub fn get_constant(&self) -> Option<&Constant> {
|
||||
if let Self::Constant(constant) = self {
|
||||
Some(constant)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_register(&self) -> Option<(&RegisterId, &Dtype)> {
|
||||
if let Self::Register { rid, dtype } = self {
|
||||
Some((rid, dtype))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_register_mut(&mut self) -> Option<(&mut RegisterId, &mut Dtype)> {
|
||||
if let Self::Register { rid, dtype } = self {
|
||||
Some((rid, dtype))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Operand {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Constant(value) => write!(f, "{}", value),
|
||||
Self::Register { rid, .. } => write!(f, "{}", rid),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasDtype for Operand {
|
||||
fn dtype(&self) -> Dtype {
|
||||
match self {
|
||||
Self::Constant(value) => value.dtype(),
|
||||
Self::Register { dtype, .. } => dtype.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Clone)]
|
||||
pub enum RegisterId {
|
||||
/// Registers holding pointers to local allocations.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// - `name`: only for debugging purposes.
|
||||
/// - `id`: local allocation id.
|
||||
Local { name: String, id: usize },
|
||||
/// - `aid`: local allocation id.
|
||||
Local { aid: usize },
|
||||
|
||||
/// Registers holding block arguments.
|
||||
///
|
||||
@@ -193,8 +392,8 @@ pub enum RegisterId {
|
||||
}
|
||||
|
||||
impl RegisterId {
|
||||
pub fn local(name: String, id: usize) -> Self {
|
||||
Self::Local { name, id }
|
||||
pub fn local(aid: usize) -> Self {
|
||||
Self::Local { aid }
|
||||
}
|
||||
|
||||
pub fn arg(bid: BlockId, aid: usize) -> Self {
|
||||
@@ -209,9 +408,9 @@ impl RegisterId {
|
||||
impl fmt::Display for RegisterId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Local { name, id } => write!(f, "%(local:{}:{})", name, id),
|
||||
Self::Arg { bid, aid } => write!(f, "%(arg:{}:{})", bid, aid),
|
||||
Self::Temp { bid, iid } => write!(f, "%({}:{})", bid, iid),
|
||||
Self::Local { aid } => write!(f, "%l{}", aid),
|
||||
Self::Arg { bid, aid } => write!(f, "%{}:p{}", bid, aid),
|
||||
Self::Temp { bid, iid } => write!(f, "%{}:i{}", bid, iid),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,7 +418,7 @@ impl fmt::Display for RegisterId {
|
||||
impl PartialEq<RegisterId> for RegisterId {
|
||||
fn eq(&self, other: &RegisterId) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Local { id, .. }, Self::Local { id: other_id, .. }) => id == other_id,
|
||||
(Self::Local { aid }, Self::Local { aid: other_aid }) => aid == other_aid,
|
||||
(
|
||||
Self::Arg { bid, aid },
|
||||
Self::Arg {
|
||||
@@ -242,7 +441,7 @@ impl PartialEq<RegisterId> for RegisterId {
|
||||
impl Hash for RegisterId {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::Local { id, .. } => id.hash(state),
|
||||
Self::Local { aid } => aid.hash(state),
|
||||
Self::Arg { bid, aid } => {
|
||||
// TODO: needs to distinguish arg/temp?
|
||||
bid.hash(state);
|
||||
@@ -256,7 +455,7 @@ impl Hash for RegisterId {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Constant {
|
||||
Undef {
|
||||
dtype: Dtype,
|
||||
@@ -273,7 +472,7 @@ pub enum Constant {
|
||||
/// * Casting from an f32 to an f64 is perfect and lossless (f32 -> f64)
|
||||
/// * Casting from an f64 to an f32 will produce the closest possible value (f64 -> f32)
|
||||
/// https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#type-cast-expressions
|
||||
value: f64,
|
||||
value: OrderedFloat<f64>,
|
||||
width: usize,
|
||||
},
|
||||
GlobalVariable {
|
||||
@@ -397,7 +596,10 @@ impl Constant {
|
||||
.get_float_width()
|
||||
.expect("`dtype` must be `Dtype::Float`");
|
||||
|
||||
Self::Float { value, width }
|
||||
Self::Float {
|
||||
value: value.into(),
|
||||
width,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -405,6 +607,20 @@ impl Constant {
|
||||
Self::GlobalVariable { name, dtype }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_int(&self) -> Option<(u128, usize, bool)> {
|
||||
if let Self::Int {
|
||||
value,
|
||||
width,
|
||||
is_signed,
|
||||
} = self
|
||||
{
|
||||
Some((*value, *width, *is_signed))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_undef(&self) -> bool {
|
||||
if let Self::Undef { .. } = self {
|
||||
true
|
||||
@@ -441,179 +657,30 @@ impl HasDtype for Constant {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Operand {
|
||||
Constant(Constant),
|
||||
Register { rid: RegisterId, dtype: Dtype },
|
||||
pub struct Named<T> {
|
||||
name: Option<String>,
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
pub fn constant(value: Constant) -> Self {
|
||||
Self::Constant(value)
|
||||
}
|
||||
impl<T> Deref for Named<T> {
|
||||
type Target = T;
|
||||
|
||||
pub fn register(rid: RegisterId, dtype: Dtype) -> Self {
|
||||
Self::Register { rid, dtype }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn get_constant(&self) -> Option<&Constant> {
|
||||
if let Self::Constant(constant) = self {
|
||||
Some(constant)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_register(&self) -> Option<(&RegisterId, &Dtype)> {
|
||||
if let Self::Register { rid, dtype } = self {
|
||||
Some((rid, dtype))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_register_mut(&mut self) -> Option<(&mut RegisterId, &mut Dtype)> {
|
||||
if let Self::Register { rid, dtype } = self {
|
||||
Some((rid, dtype))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl<T> DerefMut for Named<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Operand {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Constant(value) => write!(f, "{}", value),
|
||||
Self::Register { rid, .. } => write!(f, "{}", rid),
|
||||
}
|
||||
impl<T> Named<T> {
|
||||
pub fn new(name: Option<String>, inner: T) -> Self {
|
||||
Self { name, inner }
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<&String> {
|
||||
self.name.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasDtype for Operand {
|
||||
fn dtype(&self) -> Dtype {
|
||||
match self {
|
||||
Self::Constant(value) => value.dtype(),
|
||||
Self::Register { dtype, .. } => dtype.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Instruction {
|
||||
// TODO: the variants of Instruction will be added in the future
|
||||
Nop,
|
||||
BinOp {
|
||||
op: ast::BinaryOperator,
|
||||
lhs: Operand,
|
||||
rhs: Operand,
|
||||
dtype: Dtype,
|
||||
},
|
||||
UnaryOp {
|
||||
op: ast::UnaryOperator,
|
||||
operand: Operand,
|
||||
dtype: Dtype,
|
||||
},
|
||||
Store {
|
||||
ptr: Operand,
|
||||
value: Operand,
|
||||
},
|
||||
Load {
|
||||
ptr: Operand,
|
||||
},
|
||||
Call {
|
||||
callee: Operand,
|
||||
args: Vec<Operand>,
|
||||
return_type: Dtype,
|
||||
},
|
||||
TypeCast {
|
||||
value: Operand,
|
||||
target_dtype: Dtype,
|
||||
},
|
||||
}
|
||||
|
||||
impl HasDtype for Instruction {
|
||||
fn dtype(&self) -> Dtype {
|
||||
match self {
|
||||
Self::Nop => Dtype::unit(),
|
||||
Self::BinOp { dtype, .. } => dtype.clone(),
|
||||
Self::UnaryOp { dtype, .. } => dtype.clone(),
|
||||
Self::Store { .. } => Dtype::unit(),
|
||||
Self::Load { ptr } => ptr
|
||||
.dtype()
|
||||
.get_pointer_inner()
|
||||
.expect("Load instruction must have pointer value as operand")
|
||||
.deref()
|
||||
.clone()
|
||||
.set_const(false),
|
||||
Self::Call { return_type, .. } => return_type.clone(),
|
||||
Self::TypeCast { target_dtype, .. } => target_dtype.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub fn is_pure(&self) -> bool {
|
||||
match self {
|
||||
Self::Store { .. } => false,
|
||||
Self::Call { .. } => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub struct BlockId(pub usize);
|
||||
|
||||
impl fmt::Display for BlockId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "b{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct JumpArg {
|
||||
pub bid: BlockId,
|
||||
pub args: Vec<Operand>,
|
||||
}
|
||||
|
||||
impl JumpArg {
|
||||
pub fn new(bid: BlockId, args: Vec<Operand>) -> Self {
|
||||
Self { bid, args }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for JumpArg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}({:?})", self.bid, self.args)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum BlockExit {
|
||||
Jump {
|
||||
arg: JumpArg,
|
||||
},
|
||||
ConditionalJump {
|
||||
condition: Operand,
|
||||
arg_then: JumpArg,
|
||||
arg_else: JumpArg,
|
||||
},
|
||||
Switch {
|
||||
value: Operand,
|
||||
default: JumpArg,
|
||||
cases: Vec<(Constant, JumpArg)>,
|
||||
},
|
||||
Return {
|
||||
value: Operand,
|
||||
},
|
||||
Unreachable,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Block {
|
||||
pub phinodes: Vec<Dtype>,
|
||||
pub instructions: Vec<Instruction>,
|
||||
pub exit: BlockExit,
|
||||
}
|
||||
|
||||
@@ -58,25 +58,33 @@ impl WriteLine for (&String, &Declaration) {
|
||||
|
||||
match definition.as_ref() {
|
||||
Some(defintion) => {
|
||||
// print function definition
|
||||
writeln!(write, "define {} {{", declaration)?;
|
||||
// print meta data for function
|
||||
writeln!(
|
||||
write,
|
||||
"; function meta data:\n; bid_init: {}\n; allocations: {}",
|
||||
"init:\n bid: {}\n allocations: \n{}\n",
|
||||
defintion.bid_init,
|
||||
defintion
|
||||
.allocations
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, a)| format!("{}:{}", i, a))
|
||||
.map(|(i, a)| format!(
|
||||
" %l{}:{}{}",
|
||||
i,
|
||||
a.deref(),
|
||||
if let Some(name) = a.name() {
|
||||
format!(":{}", name)
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
.join("\n")
|
||||
)?;
|
||||
|
||||
// print function definition
|
||||
writeln!(write, "define {} {{", declaration)?;
|
||||
|
||||
for (id, block) in &defintion.blocks {
|
||||
writeln!(write, "; <BoxId> {}", id)?;
|
||||
writeln!(write, "block {}", id)?;
|
||||
(id, block).write_line(indent + 1, write)?;
|
||||
writeln!(write)?;
|
||||
}
|
||||
@@ -99,13 +107,33 @@ impl WriteLine for (&String, &Declaration) {
|
||||
|
||||
impl WriteLine for (&BlockId, &Block) {
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
for (i, phi) in self.1.phinodes.iter().enumerate() {
|
||||
write_indent(indent, write)?;
|
||||
writeln!(
|
||||
write,
|
||||
"{}:{}{}",
|
||||
RegisterId::arg(*self.0, i),
|
||||
phi.deref(),
|
||||
if let Some(name) = phi.name() {
|
||||
format!(":{}", name)
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
)?;
|
||||
}
|
||||
|
||||
for (i, instr) in self.1.instructions.iter().enumerate() {
|
||||
write_indent(indent, write)?;
|
||||
writeln!(
|
||||
write,
|
||||
"{}:{} = {}",
|
||||
"{}:{}{} = {}",
|
||||
RegisterId::temp(*self.0, i),
|
||||
instr.dtype(),
|
||||
if let Some(name) = instr.name() {
|
||||
format!(":{}", name)
|
||||
} else {
|
||||
"".into()
|
||||
},
|
||||
instr.write_string()
|
||||
)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user