Update skeleton

This commit is contained in:
Jeehoon Kang
2020-04-29 23:10:15 +09:00
parent 61b2162408
commit 3bef06455e
14 changed files with 3699 additions and 237 deletions

View File

@@ -45,3 +45,9 @@ impl WriteLine for TranslationUnit {
todo!("homework 1")
}
}
impl WriteString for Initializer {
fn write_string(&self) -> String {
todo!()
}
}

View File

@@ -721,9 +721,22 @@ impl Dtype {
}
}
pub fn is_const(&self) -> bool {
match self {
Self::Unit { is_const } => *is_const,
Self::Int { is_const, .. } => *is_const,
Self::Float { is_const, .. } => *is_const,
Self::Pointer { is_const, .. } => *is_const,
Self::Array { .. } => true,
Self::Struct { is_const, .. } => *is_const,
Self::Function { .. } => true,
Self::Typedef { is_const, .. } => *is_const,
}
}
#[inline]
/// Check if `Dtype` is constant. if it is constant, the variable of `Dtype` is not assignable.
pub fn is_const(&self, structs: &HashMap<String, Option<Dtype>>) -> bool {
pub fn is_immutable(&self, structs: &HashMap<String, Option<Dtype>>) -> bool {
match self {
Self::Unit { is_const } => *is_const,
Self::Int { is_const, .. } => *is_const,
@@ -751,9 +764,9 @@ impl Dtype {
// 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_for_array_struct_field_inner(structs)
inner.is_immutable_for_array_struct_field_inner(structs)
} else {
f.deref().is_const(structs)
f.deref().is_immutable(structs)
}
})
}
@@ -762,14 +775,14 @@ impl Dtype {
}
}
fn is_const_for_array_struct_field_inner(
fn is_immutable_for_array_struct_field_inner(
&self,
structs: &HashMap<String, Option<Dtype>>,
) -> bool {
if let Self::Array { inner, .. } = self {
inner.is_const_for_array_struct_field_inner(structs)
inner.is_immutable_for_array_struct_field_inner(structs)
} else {
self.is_const(structs)
self.is_immutable(structs)
}
}
@@ -850,16 +863,23 @@ impl Dtype {
field_name: &str,
structs: &HashMap<String, Option<Dtype>>,
) -> Option<(usize, Self)> {
if let Self::Struct {
fields,
size_align_offsets,
..
} = self
{
let fields = fields.as_ref().expect("struct should have its definition");
let (_, _, offsets) = size_align_offsets
if let Self::Struct { name, .. } = self {
let struct_name = name.as_ref().expect("`self` must have its name");
let struct_type = structs
.get(struct_name)
.expect("`structs` must have value matched with `struct_name`")
.as_ref()
.expect("struct should have `offsets` as `Some`");
.expect("`struct_type` must have its definition");
let fields = struct_type
.get_struct_fields()
.expect("`struct_type` must be struct type")
.as_ref()
.expect("`fields` must be `Some`");
let (_, _, offsets) = struct_type
.get_struct_size_align_offsets()
.expect("`struct_type` must be struct type")
.as_ref()
.expect("`offsets` must be `Some`");
assert_eq!(fields.len(), offsets.len());
for (field, offset) in izip!(fields, offsets) {
@@ -869,19 +889,8 @@ impl Dtype {
}
} else {
let field_dtype = field.deref();
let struct_name = field_dtype
.get_struct_name()
.expect("`field_dtype` must be struct type")
.as_ref()
.expect("structure type must have its name");
let struct_type = structs
.get(struct_name)
.expect("`structs` must have value matched with `struct_name`")
.as_ref()
.expect("`struct_type` must have its definition");
let (offset_inner, dtype) = some_or!(
struct_type.get_offset_struct_field(field_name, structs),
field_dtype.get_offset_struct_field(field_name, structs),
continue
);
return Some((*offset + offset_inner, dtype));
@@ -1115,7 +1124,7 @@ impl Dtype {
message: format!("unknown type name `{}`", name),
})?
.clone();
let is_const = dtype.is_const(structs) || *is_const;
let is_const = dtype.is_const() || *is_const;
dtype.set_const(is_const)
}

View File

@@ -2,6 +2,7 @@ use core::fmt;
use core::iter;
use core::mem;
use failure::Fail;
use ordered_float::OrderedFloat;
use std::collections::HashMap;
use itertools::izip;
@@ -33,7 +34,7 @@ pub enum Value {
/// * 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,
},
Pointer {
@@ -67,10 +68,7 @@ impl TryFrom<Constant> for Value {
width,
is_signed,
},
Constant::Float { value, width } => Self::Float {
value: value.into_inner(),
width,
},
Constant::Float { value, width } => Self::Float { value, width },
_ => panic!(),
};
@@ -125,7 +123,10 @@ impl Value {
#[inline]
fn float(value: f64, width: usize) -> Self {
Self::Float { value, width }
Self::Float {
value: value.into(),
width,
}
}
#[inline]
@@ -421,6 +422,187 @@ mod calculator {
use super::Value;
use crate::ir::*;
use lang_c::ast;
use std::cmp::Ordering;
fn calculate_integer_binary_operator_expression(
op: &ast::BinaryOperator,
lhs: u128,
rhs: u128,
width: usize,
is_signed: bool,
) -> Result<Value, ()> {
let result = match op {
// TODO: explain why plus & minus do not need to consider `is_signed'
ast::BinaryOperator::Plus => (lhs as i128 + rhs as i128) as u128,
ast::BinaryOperator::Minus => (lhs as i128 - rhs as i128) as u128,
ast::BinaryOperator::Multiply => {
if is_signed {
(lhs as i128 * rhs as i128) as u128
} else {
lhs * rhs
}
}
ast::BinaryOperator::Divide => {
assert!(rhs != 0);
if is_signed {
(lhs as i128 / rhs as i128) as u128
} else {
lhs / rhs
}
}
ast::BinaryOperator::Modulo => {
assert!(rhs != 0);
if is_signed {
(lhs as i128 % rhs as i128) as u128
} else {
lhs % rhs
}
}
ast::BinaryOperator::ShiftLeft => {
let rhs = if is_signed {
let rhs = rhs as i128;
assert!(rhs >= 0);
assert!(rhs < (width as i128));
rhs as u128
} else {
assert!(rhs < (width as u128));
rhs
};
lhs << rhs
}
ast::BinaryOperator::ShiftRight => {
if is_signed {
// arithmetic shift right
let rhs = rhs as i128;
assert!(rhs >= 0);
assert!(rhs < (width as i128));
((lhs as i128) >> rhs) as u128
} else {
// logical shift right
assert!(rhs < (width as u128));
let bit_mask = (1u128 << width as u128) - 1;
let lhs = lhs & bit_mask;
lhs >> rhs
}
}
ast::BinaryOperator::BitwiseAnd => lhs & rhs,
ast::BinaryOperator::BitwiseXor => lhs ^ rhs,
ast::BinaryOperator::BitwiseOr => lhs | rhs,
ast::BinaryOperator::Equals => {
let result = if lhs == rhs { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::NotEquals => {
let result = if lhs != rhs { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::Less => {
let condition = if is_signed {
(lhs as i128) < (rhs as i128)
} else {
lhs < rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::Greater => {
let condition = if is_signed {
(lhs as i128) > (rhs as i128)
} else {
lhs > rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::LessOrEqual => {
let condition = if is_signed {
(lhs as i128) <= (rhs as i128)
} else {
lhs <= rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::GreaterOrEqual => {
let condition = if is_signed {
(lhs as i128) >= (rhs as i128)
} else {
lhs >= rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
_ => todo!(
"calculate_binary_operator_expression: not supported operator {:?}",
op
),
};
let result = if is_signed {
sign_extension(result, width as u128)
} else {
trim_unnecessary_bits(result, width as u128)
};
Ok(Value::int(result, width, is_signed))
}
fn calculate_float_binary_operator_expression(
op: &ast::BinaryOperator,
lhs: OrderedFloat<f64>,
rhs: OrderedFloat<f64>,
width: usize,
) -> Result<Value, ()> {
let result = match op {
ast::BinaryOperator::Plus => lhs.into_inner() + rhs.into_inner(),
ast::BinaryOperator::Minus => lhs.into_inner() - rhs.into_inner(),
ast::BinaryOperator::Multiply => lhs.into_inner() * rhs.into_inner(),
ast::BinaryOperator::Divide => {
assert!(rhs.into_inner() != 0.0);
lhs.into_inner() / rhs.into_inner()
}
ast::BinaryOperator::Equals => {
let order = lhs
.partial_cmp(&rhs)
.expect("`lhs` and `rhs` must be not NAN");
let result = if Ordering::Equal == order { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::NotEquals => {
let order = lhs
.partial_cmp(&rhs)
.expect("`lhs` and `rhs` must be not NAN");
let result = if Ordering::Equal != order { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::Less => {
let result = if lhs.lt(&rhs) { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::Greater => {
let result = if lhs.gt(&rhs) { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::LessOrEqual => {
let result = if lhs.le(&rhs) { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::GreaterOrEqual => {
let result = if lhs.ge(&rhs) { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
_ => todo!(
"calculate_binary_operator_expression: not supported case for \
{:?} {:?} {:?}",
op,
lhs,
rhs
),
};
Ok(Value::float(result, width))
}
// TODO: change to template function in the future
pub fn calculate_binary_operator_expression(
@@ -428,11 +610,10 @@ mod calculator {
lhs: Value,
rhs: Value,
) -> Result<Value, ()> {
match (op, lhs, rhs) {
(_, Value::Undef { .. }, _) => Err(()),
(_, _, Value::Undef { .. }) => Err(()),
match (lhs, rhs) {
(Value::Undef { .. }, _) => Err(()),
(_, Value::Undef { .. }) => Err(()),
(
op,
Value::Int {
value: lhs,
width: lhs_w,
@@ -447,121 +628,38 @@ mod calculator {
assert_eq!(lhs_w, rhs_w);
assert_eq!(lhs_s, rhs_s);
let result = match op {
// TODO: explain why plus & minus do not need to consider `is_signed'
ast::BinaryOperator::Plus => (lhs as i128 + rhs as i128) as u128,
ast::BinaryOperator::Minus => (lhs as i128 - rhs as i128) as u128,
ast::BinaryOperator::Multiply => {
if lhs_s {
(lhs as i128 * rhs as i128) as u128
} else {
lhs * rhs
}
}
ast::BinaryOperator::Divide => {
if lhs_s {
(lhs as i128 / rhs as i128) as u128
} else {
lhs / rhs
}
}
ast::BinaryOperator::Modulo => {
if lhs_s {
(lhs as i128 % rhs as i128) as u128
} else {
lhs % rhs
}
}
ast::BinaryOperator::ShiftLeft => {
let rhs = if lhs_s {
let rhs = rhs as i128;
assert!(rhs >= 0);
assert!(rhs < (lhs_w as i128));
rhs as u128
} else {
assert!(rhs < (lhs_w as u128));
rhs
};
lhs << rhs
}
ast::BinaryOperator::ShiftRight => {
if lhs_s {
// arithmetic shift right
let rhs = rhs as i128;
assert!(rhs >= 0);
assert!(rhs < (lhs_w as i128));
((lhs as i128) >> rhs) as u128
} else {
// logical shift right
assert!(rhs < (lhs_w as u128));
let bit_mask = (1u128 << lhs_w as u128) - 1;
let lhs = lhs & bit_mask;
lhs >> rhs
}
}
ast::BinaryOperator::BitwiseAnd => lhs & rhs,
ast::BinaryOperator::BitwiseXor => lhs ^ rhs,
ast::BinaryOperator::BitwiseOr => lhs | rhs,
ast::BinaryOperator::Equals => {
let result = if lhs == rhs { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::NotEquals => {
let result = if lhs != rhs { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::Less => {
let condition = if lhs_s {
(lhs as i128) < (rhs as i128)
} else {
lhs < rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::Greater => {
let condition = if lhs_s {
(lhs as i128) > (rhs as i128)
} else {
lhs > rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::LessOrEqual => {
let condition = if lhs_s {
(lhs as i128) <= (rhs as i128)
} else {
lhs <= rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
ast::BinaryOperator::GreaterOrEqual => {
let condition = if lhs_s {
(lhs as i128) >= (rhs as i128)
} else {
lhs >= rhs
};
let result = if condition { 1 } else { 0 };
return Ok(Value::int(result, 1, false));
}
_ => todo!(
"calculate_binary_operator_expression: not supported operator {:?}",
op
),
};
let result = if lhs_s {
sign_extension(result, lhs_w as u128)
} else {
trim_unnecessary_bits(result, lhs_w as u128)
};
Ok(Value::int(result, lhs_w, lhs_s))
calculate_integer_binary_operator_expression(op, lhs, rhs, lhs_w, lhs_s)
}
(op, lhs, rhs) => todo!(
(
Value::Float {
value: lhs,
width: lhs_w,
},
Value::Float {
value: rhs,
width: rhs_w,
},
) => {
assert_eq!(lhs_w, rhs_w);
calculate_float_binary_operator_expression(op, lhs, rhs, lhs_w)
}
(Value::Pointer { bid, .. }, Value::Pointer { bid: other_bid, .. }) => match op {
ast::BinaryOperator::Equals => {
let result = if bid == other_bid { 1 } else { 0 };
Ok(Value::int(result, 1, false))
}
ast::BinaryOperator::NotEquals => {
let result = if bid != other_bid { 1 } else { 0 };
Ok(Value::int(result, 1, false))
}
_ => todo!(
"calculate_binary_operator_expression: not supported case for \
{:?} between pointer and integer value",
op,
),
},
(lhs, rhs) => todo!(
"calculate_binary_operator_expression: not supported case for {:?} {:?} {:?}",
op,
lhs,
@@ -574,48 +672,47 @@ mod calculator {
op: &ast::UnaryOperator,
operand: Value,
) -> Result<Value, ()> {
match (op, operand) {
(_, Value::Undef { .. }) => Err(()),
(
ast::UnaryOperator::Plus,
Value::Int {
value,
width,
is_signed,
},
) => Ok(Value::int(value, width, is_signed)),
(
ast::UnaryOperator::Minus,
Value::Int {
value,
width,
is_signed,
},
) => {
// TODO: check what is exact behavior of appling minus to unsigned value
let result = if is_signed {
(-(value as i128)) as u128
} else {
let result = (-(value as i128)) as u128;
let bit_mask = (1u128 << (width as u128)) - 1;
result & bit_mask
};
Ok(Value::int(result as u128, width, is_signed))
match operand {
Value::Undef { .. } => Err(()),
Value::Int {
value,
width,
is_signed,
} => {
match op {
ast::UnaryOperator::Plus => Ok(Value::int(value, width, is_signed)),
ast::UnaryOperator::Minus => {
let result = if is_signed {
(-(value as i128)) as u128
} else {
let value = (-(value as i128)) as u128;
trim_unnecessary_bits(value, width as u128)
};
Ok(Value::int(result as u128, width, is_signed))
}
ast::UnaryOperator::Negate => {
// Check if it is boolean
assert!(width == 1);
let result = if value == 0 { 1 } else { 0 };
Ok(Value::int(result, width, is_signed))
}
_ => todo!(
"calculate_unary_operator_expression: not supported case for {:?} {:?}",
op,
operand,
),
}
}
(
ast::UnaryOperator::Negate,
Value::Int {
value,
width,
is_signed,
},
) => {
// Check if it is boolean
assert!(width == 1);
let result = if value == 0 { 1 } else { 0 };
Ok(Value::int(result, width, is_signed))
}
(op, operand) => todo!(
Value::Float { value, width } => match op {
ast::UnaryOperator::Plus => Ok(Value::float(value.into_inner(), width)),
ast::UnaryOperator::Minus => Ok(Value::float(-value.into_inner(), width)),
_ => todo!(
"calculate_unary_operator_expression: not supported case for {:?} {:?}",
op,
operand,
),
},
_ => todo!(
"calculate_unary_operator_expression: not supported case for {:?} {:?}",
op,
operand,
@@ -630,8 +727,6 @@ mod calculator {
match (value, dtype) {
(Value::Undef { .. }, _) => Err(()),
// TODO: distinguish zero/signed extension in the future
// TODO: consider truncate in the future
(
Value::Int { value, width, .. },
Dtype::Int {
@@ -640,7 +735,6 @@ mod calculator {
..
},
) => {
// Other cases
let result = if target_signed {
if width >= target_width {
// TODO: explain the logic in the future
@@ -668,6 +762,17 @@ mod calculator {
};
Ok(Value::float(casted_value, width))
}
(Value::Int { value, .. }, Dtype::Pointer { inner, .. }) => {
if value == 0 {
Ok(Value::pointer(None, 0, inner.deref().clone()))
} else {
panic!(format!(
"calculate_typecast: not support case \
typecast int to pointer when `value` is {}",
value
))
}
}
(
Value::Float { value, .. },
Dtype::Int {
@@ -675,14 +780,14 @@ mod calculator {
},
) => {
let casted_value = if is_signed {
value as i128 as u128
value.into_inner() as i128 as u128
} else {
value as u128
value.into_inner() as u128
};
Ok(Value::int(casted_value, width, is_signed))
}
(Value::Float { value, .. }, Dtype::Float { width, .. }) => {
Ok(Value::float(value, width))
Ok(Value::float(value.into_inner(), width))
}
(value, dtype) => todo!("calculate_typecast ({:?}) {:?}", value, dtype),
}
@@ -706,12 +811,15 @@ mod calculator {
}
}
// TODO
#[allow(dead_code)]
// TODO: delete `allow(dead_code)`
/// Even though `Pointer` variant is constructed and actively used at run-time,
/// the rust compiler analyzes it is dead code.
/// For this reason, we add `allow(dead_code)` mark.
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
enum Byte {
Undef,
Concrete(u8),
#[allow(dead_code)]
Pointer {
bid: Option<usize>,
offset: isize,
@@ -921,8 +1029,8 @@ impl Byte {
} => {
let size = value.dtype().size_align_of(structs).unwrap().0;
let value_bits: u128 = match size {
Dtype::SIZE_OF_FLOAT => (*float_value as f32).to_bits() as u128,
Dtype::SIZE_OF_DOUBLE => (*float_value as f64).to_bits() as u128,
Dtype::SIZE_OF_FLOAT => (float_value.into_inner() as f32).to_bits() as u128,
Dtype::SIZE_OF_DOUBLE => (float_value.into_inner() as f64).to_bits() as u128,
_ => panic!("value_to_bytes: {} is not a valid float size", size),
};
@@ -1045,8 +1153,6 @@ impl Memory {
}
}
// TODO: allocation fields will be added in the future
// TODO: program fields will be added in the future
#[derive(Debug, PartialEq)]
struct State<'i> {
/// A data structure that maps each global variable to a pointer value

View File

@@ -186,7 +186,6 @@ pub struct Block {
#[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,
@@ -255,7 +254,6 @@ impl Instruction {
}
}
// TODO
#[derive(Debug, PartialEq, Clone)]
pub enum BlockExit {
Jump {

View File

@@ -198,7 +198,6 @@ impl WriteString for Operand {
impl WriteOp for ast::BinaryOperator {
fn write_operation(&self) -> String {
// TODO: represent signed & unsigned if necessary
match self {
Self::Multiply => "mul",
Self::Divide => "div",

View File

@@ -8,6 +8,10 @@ use wait_timeout::ChildExt;
use crate::*;
// Rust sets an exit code of 101 when the process panicked.
// So, we decide KECC sets an exit code of 102 after 101 when the test skipped.
pub const SKIP_TEST: i32 = 102;
pub fn test_write_c(unit: &TranslationUnit, _path: &Path) {
let temp_dir = tempdir().expect("temp dir creation failed");
let temp_file_path = temp_dir.path().join("temp.c");
@@ -43,7 +47,7 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
.unwrap()
.success()
{
return;
::std::process::exit(SKIP_TEST);
}
// Execute compiled executable
@@ -64,10 +68,10 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
println!("timeout occurs");
child.kill().unwrap();
child.wait().unwrap();
return;
::std::process::exit(SKIP_TEST);
}
);
let status = some_or!(status.code(), return);
let status = some_or_exit!(status.code(), SKIP_TEST);
let ir = match Irgen::default().translate(unit) {
Ok(ir) => ir,