mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-15 23:18:48 +00:00
Update
This commit is contained in:
@@ -5,7 +5,7 @@ use crate::ir;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TODO {}
|
||||
pub struct Todo {}
|
||||
|
||||
/// TODO
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -616,9 +616,9 @@ impl Immediate {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RelocationFunction {
|
||||
/// %hi
|
||||
HI20,
|
||||
Hi20,
|
||||
/// %lo
|
||||
LO12,
|
||||
Lo12,
|
||||
}
|
||||
|
||||
/// `Label` is used as branch, unconditional jump targets and symbol offsets.
|
||||
@@ -676,17 +676,11 @@ impl TryFrom<ir::Dtype> for DataSize {
|
||||
|
||||
impl DataSize {
|
||||
pub fn is_integer(&self) -> bool {
|
||||
match self {
|
||||
Self::Byte | Self::Half | Self::Word | Self::Double => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, Self::Byte | Self::Half | Self::Word | Self::Double)
|
||||
}
|
||||
|
||||
pub fn is_floating_point(&self) -> bool {
|
||||
match self {
|
||||
Self::SinglePrecision | Self::DoublePrecision => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, Self::SinglePrecision | Self::DoublePrecision)
|
||||
}
|
||||
|
||||
fn word(self) -> Option<Self> {
|
||||
|
||||
@@ -434,8 +434,8 @@ impl WriteString for Immediate {
|
||||
impl WriteString for RelocationFunction {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::HI20 => "%hi",
|
||||
Self::LO12 => "%lo",
|
||||
Self::Hi20 => "%hi",
|
||||
Self::Lo12 => "%lo",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ impl IsEquiv for DeclaratorKind {
|
||||
(Self::Identifier(identifier), Self::Identifier(other_identifier)) => {
|
||||
identifier.node.name == other_identifier.node.name
|
||||
}
|
||||
(Self::Declarator(decl), Self::Declarator(other_decl)) => decl.is_equiv(&other_decl),
|
||||
(Self::Declarator(decl), Self::Declarator(other_decl)) => decl.is_equiv(other_decl),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ impl IsEquiv for DerivedDeclarator {
|
||||
params.is_equiv(other_params)
|
||||
}
|
||||
(Self::KRFunction(kr_func_decl), Self::KRFunction(other_kr_func_decl)) => {
|
||||
kr_func_decl.is_equiv(&other_kr_func_decl)
|
||||
kr_func_decl.is_equiv(other_kr_func_decl)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
@@ -128,10 +128,10 @@ impl IsEquiv for ArraySize {
|
||||
(Self::Unknown, Self::Unknown) => true,
|
||||
(Self::VariableUnknown, Self::VariableUnknown) => true,
|
||||
(Self::VariableExpression(expr), Self::VariableExpression(other_expr)) => {
|
||||
expr.is_equiv(&other_expr)
|
||||
expr.is_equiv(other_expr)
|
||||
}
|
||||
(Self::StaticExpression(expr), Self::StaticExpression(other_expr)) => {
|
||||
expr.is_equiv(&other_expr)
|
||||
expr.is_equiv(other_expr)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
@@ -492,6 +492,7 @@ impl IsEquiv for Enumerator {
|
||||
|
||||
impl IsEquiv for TypeQualifier {
|
||||
fn is_equiv(&self, other: &Self) -> bool {
|
||||
#[allow(clippy::match_like_matches_macro)]
|
||||
match (self, other) {
|
||||
(Self::Const, Self::Const) => true,
|
||||
_ => false,
|
||||
|
||||
@@ -8,6 +8,7 @@ use lang_c::span::Node;
|
||||
use crate::utils::AssertSupported;
|
||||
use crate::Translate;
|
||||
|
||||
/// TODO(document)
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
ParseError(ParseError),
|
||||
@@ -15,7 +16,8 @@ pub enum Error {
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
/// TODO(document)
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Parse {}
|
||||
|
||||
impl<P: AsRef<Path>> Translate<P> for Parse {
|
||||
@@ -182,7 +184,7 @@ impl AssertSupported for AlignmentSpecifier {
|
||||
fn assert_supported(&self) {
|
||||
match self {
|
||||
Self::Type(typename) => typename.assert_supported(),
|
||||
Self::Constant(_) => panic!(AlignmentSpecifier::Constant),
|
||||
Self::Constant(_) => std::panic::panic_any(AlignmentSpecifier::Constant),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,7 +227,7 @@ impl AssertSupported for DerivedDeclarator {
|
||||
Self::Array(array_decl) => array_decl.assert_supported(),
|
||||
Self::Function(func_decl) => func_decl.assert_supported(),
|
||||
// Support when K&R function has no parameter
|
||||
Self::KRFunction(kr_func_decl) => assert_eq!(true, kr_func_decl.is_empty()),
|
||||
Self::KRFunction(kr_func_decl) => assert!(kr_func_decl.is_empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -513,6 +515,7 @@ impl AssertSupported for SpecifierQualifier {
|
||||
match self {
|
||||
Self::TypeSpecifier(type_specifier) => type_specifier.assert_supported(),
|
||||
Self::TypeQualifier(type_qualifier) => type_qualifier.assert_supported(),
|
||||
Self::Extension(_) => panic!("SpecifierQualifier::Extension"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,14 +554,14 @@ impl AssertSupported for Constant {
|
||||
|
||||
impl AssertSupported for Integer {
|
||||
fn assert_supported(&self) {
|
||||
assert_eq!(false, self.suffix.imaginary);
|
||||
assert!(!self.suffix.imaginary);
|
||||
}
|
||||
}
|
||||
|
||||
impl AssertSupported for Float {
|
||||
fn assert_supported(&self) {
|
||||
self.suffix.format.assert_supported();
|
||||
assert_eq!(false, self.suffix.imaginary);
|
||||
assert!(!self.suffix.imaginary);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,20 @@ use itertools::izip;
|
||||
use crate::ir::*;
|
||||
use crate::some_or;
|
||||
|
||||
/// TODO(document)
|
||||
#[derive(Debug, PartialEq, Fail)]
|
||||
pub enum DtypeError {
|
||||
/// For uncommon error
|
||||
#[fail(display = "{}", message)]
|
||||
Misc { message: String },
|
||||
Misc {
|
||||
/// TODO(document)
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
pub trait HasDtype {
|
||||
/// TODO(document)
|
||||
fn dtype(&self) -> Dtype;
|
||||
}
|
||||
|
||||
@@ -34,40 +40,77 @@ struct BaseDtype {
|
||||
is_typedef: bool,
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub enum Dtype {
|
||||
/// TODO(document)
|
||||
Unit {
|
||||
/// TODO(document)
|
||||
is_const: bool,
|
||||
},
|
||||
/// TODO(document)
|
||||
Int {
|
||||
/// TODO(document)
|
||||
width: usize,
|
||||
|
||||
/// TODO(document)
|
||||
is_signed: bool,
|
||||
|
||||
/// TODO(document)
|
||||
is_const: bool,
|
||||
},
|
||||
/// TODO(document)
|
||||
Float {
|
||||
/// TODO(document)
|
||||
width: usize,
|
||||
|
||||
/// TODO(document)
|
||||
is_const: bool,
|
||||
},
|
||||
/// TODO(document)
|
||||
Pointer {
|
||||
/// TODO(document)
|
||||
inner: Box<Dtype>,
|
||||
|
||||
/// TODO(document)
|
||||
is_const: bool,
|
||||
},
|
||||
/// TODO(document)
|
||||
Array {
|
||||
/// TODO(document)
|
||||
inner: Box<Dtype>,
|
||||
|
||||
/// TODO(document)
|
||||
size: usize,
|
||||
},
|
||||
/// TODO(document)
|
||||
Struct {
|
||||
/// TODO(document)
|
||||
name: Option<String>,
|
||||
|
||||
/// TODO(document)
|
||||
fields: Option<Vec<Named<Dtype>>>,
|
||||
|
||||
/// TODO(document)
|
||||
is_const: bool,
|
||||
|
||||
/// TODO(document)
|
||||
size_align_offsets: Option<(usize, usize, Vec<usize>)>,
|
||||
},
|
||||
/// TODO(document)
|
||||
Function {
|
||||
/// TODO(document)
|
||||
ret: Box<Dtype>,
|
||||
|
||||
/// TODO(document)
|
||||
params: Vec<Dtype>,
|
||||
},
|
||||
/// TODO(document)
|
||||
Typedef {
|
||||
/// TODO(document)
|
||||
name: String,
|
||||
|
||||
/// TODO(document)
|
||||
is_const: bool,
|
||||
},
|
||||
}
|
||||
@@ -201,6 +244,7 @@ impl BaseDtype {
|
||||
ast::SpecifierQualifier::TypeQualifier(type_qualifier) => {
|
||||
self.apply_type_qualifier(&type_qualifier.node)?
|
||||
}
|
||||
ast::SpecifierQualifier::Extension(_) => panic!("unsupported specifier qualifier"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -464,40 +508,73 @@ impl TryFrom<&ast::ParameterDeclaration> for Dtype {
|
||||
}
|
||||
|
||||
impl Dtype {
|
||||
/// TODO(document)
|
||||
pub const BITS_OF_BYTE: usize = 8;
|
||||
|
||||
/// TODO(document)
|
||||
pub const SIZE_OF_BYTE: usize = 1;
|
||||
|
||||
/// TODO(document)
|
||||
// TODO: consider architecture dependency (current: 64-bit architecture)
|
||||
pub const SIZE_OF_POINTER: usize = 8;
|
||||
|
||||
/// TODO(document)
|
||||
pub const SIZE_OF_CHAR: usize = 1;
|
||||
|
||||
/// TODO(document)
|
||||
pub const SIZE_OF_SHORT: usize = 2;
|
||||
|
||||
/// TODO(document)
|
||||
pub const SIZE_OF_INT: usize = 4;
|
||||
|
||||
/// TODO(document)
|
||||
pub const SIZE_OF_LONG: usize = 8;
|
||||
|
||||
/// TODO(document)
|
||||
pub const SIZE_OF_LONGLONG: usize = 8;
|
||||
|
||||
/// TODO(document)
|
||||
pub const SIZE_OF_FLOAT: usize = 4;
|
||||
|
||||
/// TODO(document)
|
||||
pub const SIZE_OF_DOUBLE: usize = 8;
|
||||
|
||||
/// TODO(document)
|
||||
// signed option cannot be applied to boolean value
|
||||
pub const BOOL: Self = Self::Int {
|
||||
width: 1,
|
||||
is_signed: false,
|
||||
is_const: false,
|
||||
};
|
||||
|
||||
/// TODO(document)
|
||||
pub const CHAR: Self = Self::int(Self::SIZE_OF_CHAR * Self::BITS_OF_BYTE);
|
||||
|
||||
/// TODO(document)
|
||||
pub const SHORT: Self = Self::int(Self::SIZE_OF_SHORT * Self::BITS_OF_BYTE);
|
||||
|
||||
/// TODO(document)
|
||||
pub const INT: Self = Self::int(Self::SIZE_OF_INT * Self::BITS_OF_BYTE);
|
||||
|
||||
/// TODO(document)
|
||||
pub const LONG: Self = Self::int(Self::SIZE_OF_LONG * Self::BITS_OF_BYTE);
|
||||
|
||||
/// TODO(document)
|
||||
pub const LONGLONG: Self = Self::int(Self::SIZE_OF_LONGLONG * Self::BITS_OF_BYTE);
|
||||
|
||||
/// TODO(document)
|
||||
pub const FLOAT: Self = Self::float(Self::SIZE_OF_FLOAT * Self::BITS_OF_BYTE);
|
||||
|
||||
/// TODO(document)
|
||||
pub const DOUBLE: Self = Self::float(Self::SIZE_OF_DOUBLE * Self::BITS_OF_BYTE);
|
||||
|
||||
/// TODO(document)
|
||||
#[inline]
|
||||
pub const fn unit() -> Self {
|
||||
Self::Unit { is_const: false }
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
#[inline]
|
||||
pub const fn int(width: usize) -> Self {
|
||||
Self::Int {
|
||||
@@ -507,6 +584,7 @@ impl Dtype {
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
#[inline]
|
||||
pub const fn float(width: usize) -> Self {
|
||||
Self::Float {
|
||||
@@ -515,6 +593,7 @@ impl Dtype {
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
#[inline]
|
||||
pub fn pointer(inner: Dtype) -> Self {
|
||||
Self::Pointer {
|
||||
@@ -523,9 +602,14 @@ impl Dtype {
|
||||
}
|
||||
}
|
||||
|
||||
// Suppose the C declaration is `int *a[2][3]`. Then `a`'s `ir::Dtype` should be `[2 x [3 x int*]]`.
|
||||
// But in the AST, it is parsed as `Array(3, Array(2, Pointer(int)))`, reversing the order of `2` and `3`.
|
||||
// In the recursive translation of declaration into Dtype, we need to insert `3` inside `[2 * int*]`.
|
||||
/// TODO(document)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Suppose the C declaration is `int *a[2][3]`. Then `a`'s `ir::Dtype` should be `[2 x [3 x
|
||||
/// int*]]`. But in the AST, it is parsed as `Array(3, Array(2, Pointer(int)))`, reversing the
|
||||
/// order of `2` and `3`. In the recursive translation of declaration into Dtype, we need to
|
||||
/// insert `3` inside `[2 * int*]`.
|
||||
pub fn array(base_dtype: Dtype, size: usize) -> Self {
|
||||
match base_dtype {
|
||||
Self::Array {
|
||||
@@ -547,6 +631,7 @@ impl Dtype {
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
#[inline]
|
||||
pub fn structure(name: Option<String>, fields: Option<Vec<Named<Self>>>) -> Self {
|
||||
Self::Struct {
|
||||
@@ -1344,7 +1429,7 @@ fn check_no_duplicate_field(fields: &[Named<Dtype>], field_names: &mut HashSet<S
|
||||
.expect("`field_dtype` must be struct type")
|
||||
.as_ref()
|
||||
.expect("struct type must have its definition");
|
||||
if !check_no_duplicate_field(&fields, field_names) {
|
||||
if !check_no_duplicate_field(fields, field_names) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use itertools::izip;
|
||||
|
||||
use crate::ir::*;
|
||||
use crate::utils::IsEquiv;
|
||||
use crate::*;
|
||||
|
||||
impl IsEquiv for TranslationUnit {
|
||||
fn is_equiv(&self, other: &Self) -> bool {
|
||||
@@ -30,7 +29,7 @@ impl IsEquiv for TranslationUnit {
|
||||
}
|
||||
}
|
||||
|
||||
impl IsEquiv for ir::Declaration {
|
||||
impl IsEquiv for Declaration {
|
||||
fn is_equiv(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(
|
||||
@@ -328,7 +327,7 @@ fn is_equiv_arg(lhs: &JumpArg, rhs: &JumpArg, map: &HashMap<BlockId, BlockId>) -
|
||||
true
|
||||
}
|
||||
|
||||
impl IsEquiv for ir::FunctionDefinition {
|
||||
impl IsEquiv for FunctionDefinition {
|
||||
fn is_equiv(&self, other: &Self) -> bool {
|
||||
if self.allocations != other.allocations {
|
||||
return false;
|
||||
@@ -348,7 +347,7 @@ impl IsEquiv for ir::FunctionDefinition {
|
||||
|
||||
let mut map = HashMap::new();
|
||||
for (f, t) in izip!(&preorder, &preorder_other) {
|
||||
map.insert(*f, *t);
|
||||
let _ = map.insert(*f, *t);
|
||||
}
|
||||
|
||||
if map.get(&self.bid_init) != Some(&other.bid_init) {
|
||||
|
||||
@@ -703,7 +703,7 @@ mod calculator {
|
||||
let value = (-(value as i128)) as u128;
|
||||
trim_unnecessary_bits(value, width as u128)
|
||||
};
|
||||
Ok(Value::int(result as u128, width, is_signed))
|
||||
Ok(Value::int(result, width, is_signed))
|
||||
}
|
||||
ast::UnaryOperator::Negate => {
|
||||
// Check if it is boolean
|
||||
@@ -781,11 +781,11 @@ mod calculator {
|
||||
if value == 0 {
|
||||
Ok(Value::pointer(None, 0, inner.deref().clone()))
|
||||
} else {
|
||||
panic!(format!(
|
||||
panic!(
|
||||
"calculate_typecast: not support case \
|
||||
typecast int to pointer when `value` is {}",
|
||||
typecast int to pointer when `value` is {}",
|
||||
value
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
(
|
||||
@@ -1028,7 +1028,7 @@ impl Byte {
|
||||
let size = value.dtype().size_align_of(structs).unwrap().0;
|
||||
let value_bits: u128 = match size {
|
||||
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,
|
||||
Dtype::SIZE_OF_DOUBLE => (float_value.into_inner()).to_bits() as u128,
|
||||
_ => panic!("value_to_bytes: {} is not a valid float size", size),
|
||||
};
|
||||
|
||||
@@ -1073,7 +1073,7 @@ impl Byte {
|
||||
izip!(fields, offsets).for_each(|(f, o)| {
|
||||
let result = Self::value_to_bytes(f.deref(), structs);
|
||||
let size_of_data = f.deref().dtype().size_align_of(structs).unwrap().0;
|
||||
values.splice(*o..(*o + size_of_data), result.iter().cloned());
|
||||
let _ = values.splice(*o..(*o + size_of_data), result.iter().cloned());
|
||||
});
|
||||
|
||||
values
|
||||
@@ -1143,7 +1143,7 @@ impl Memory {
|
||||
let block = self.inner[bid].as_mut().unwrap();
|
||||
|
||||
if 0 <= offset && end <= block.len() {
|
||||
block.splice(offset as usize..end, bytes.iter().cloned());
|
||||
let _ = block.splice(offset as usize..end, bytes.iter().cloned());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
@@ -1163,16 +1163,16 @@ struct State<'i> {
|
||||
}
|
||||
|
||||
impl<'i> State<'i> {
|
||||
fn new(ir: &'i TranslationUnit, args: Vec<Value>) -> Result<State, InterpreterError> {
|
||||
fn new(ir: &'i TranslationUnit, args: Vec<Value>) -> Result<State<'_>, InterpreterError> {
|
||||
// Interpreter starts with the main function
|
||||
let func_name = String::from("main");
|
||||
let func = ir
|
||||
.decls
|
||||
.get(&func_name)
|
||||
.ok_or_else(|| InterpreterError::NoMainFunction)?;
|
||||
.ok_or(InterpreterError::NoMainFunction)?;
|
||||
let (_, func_def) = func
|
||||
.get_function()
|
||||
.ok_or_else(|| InterpreterError::NoMainFunction)?;
|
||||
.ok_or(InterpreterError::NoMainFunction)?;
|
||||
let func_def = func_def
|
||||
.as_ref()
|
||||
.ok_or_else(|| InterpreterError::NoFunctionDefinition {
|
||||
@@ -1218,7 +1218,7 @@ impl<'i> State<'i> {
|
||||
},
|
||||
)?
|
||||
} else {
|
||||
Value::default_from_dtype(&dtype, &self.ir.structs)
|
||||
Value::default_from_dtype(dtype, &self.ir.structs)
|
||||
.expect("default value must be derived from `dtype`")
|
||||
};
|
||||
|
||||
@@ -1244,7 +1244,7 @@ impl<'i> State<'i> {
|
||||
fn alloc_local_variables(&mut self) -> Result<(), InterpreterError> {
|
||||
// add alloc register
|
||||
for (id, allocation) in self.stack_frame.func_def.allocations.iter().enumerate() {
|
||||
let bid = self.memory.alloc(&allocation, &self.ir.structs)?;
|
||||
let bid = self.memory.alloc(allocation, &self.ir.structs)?;
|
||||
let ptr = Value::pointer(Some(bid), 0, allocation.deref().clone());
|
||||
let rid = RegisterId::local(id);
|
||||
|
||||
@@ -1407,7 +1407,7 @@ impl<'i> State<'i> {
|
||||
let lhs = self.interp_operand(lhs.clone())?;
|
||||
let rhs = self.interp_operand(rhs.clone())?;
|
||||
|
||||
calculator::calculate_binary_operator_expression(&op, lhs, rhs).map_err(|_| {
|
||||
calculator::calculate_binary_operator_expression(op, lhs, rhs).map_err(|_| {
|
||||
InterpreterError::Misc {
|
||||
func_name: self.stack_frame.func_name.clone(),
|
||||
pc: self.stack_frame.pc,
|
||||
@@ -1418,7 +1418,7 @@ impl<'i> State<'i> {
|
||||
Instruction::UnaryOp { op, operand, .. } => {
|
||||
let operand = self.interp_operand(operand.clone())?;
|
||||
|
||||
calculator::calculate_unary_operator_expression(&op, operand).map_err(|_| {
|
||||
calculator::calculate_unary_operator_expression(op, operand).map_err(|_| {
|
||||
InterpreterError::Misc {
|
||||
func_name: self.stack_frame.func_name.clone(),
|
||||
pc: self.stack_frame.pc,
|
||||
@@ -1517,7 +1517,7 @@ impl<'i> State<'i> {
|
||||
let offset = prev_offset + value as isize;
|
||||
assert!(0 <= offset);
|
||||
|
||||
Value::pointer(*bid, offset as isize, inner_dtype.clone())
|
||||
Value::pointer(*bid, offset, inner_dtype.clone())
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
//! The intermediate representation.
|
||||
|
||||
mod dtype;
|
||||
mod equiv;
|
||||
mod interp;
|
||||
#[allow(clippy::all)]
|
||||
mod parse;
|
||||
mod write_ir;
|
||||
|
||||
@@ -247,11 +250,7 @@ impl HasDtype for Instruction {
|
||||
|
||||
impl Instruction {
|
||||
pub fn is_pure(&self) -> bool {
|
||||
match self {
|
||||
Self::Store { .. } => false,
|
||||
Self::Call { .. } => false,
|
||||
_ => true,
|
||||
}
|
||||
!matches!(self, Self::Store { .. } | Self::Call { .. })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,6 +525,7 @@ impl TryFrom<&ast::Constant> for Constant {
|
||||
ast::IntegerBase::Decimal => Self::DECIMAL,
|
||||
ast::IntegerBase::Octal => Self::OCTAL,
|
||||
ast::IntegerBase::Hexadecimal => Self::HEXADECIMAL,
|
||||
ast::IntegerBase::Binary => Self::BINARY,
|
||||
};
|
||||
|
||||
let value = if integer.suffix.unsigned {
|
||||
@@ -636,14 +636,11 @@ impl Constant {
|
||||
const DECIMAL: u32 = 10;
|
||||
const OCTAL: u32 = 8;
|
||||
const HEXADECIMAL: u32 = 16;
|
||||
const BINARY: u32 = 2;
|
||||
|
||||
#[inline]
|
||||
pub fn is_integer_constant(&self) -> bool {
|
||||
if let Self::Int { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Self::Int { .. })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -730,17 +727,13 @@ impl Constant {
|
||||
}
|
||||
_ => panic!(
|
||||
"constant value generated by `Constant::from_ast_expression` \
|
||||
must be `Constant{{Int, Float}}`"
|
||||
must be `Constant(Int, Float)`"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_undef(&self) -> bool {
|
||||
if let Self::Undef { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Self::Undef { .. })
|
||||
}
|
||||
|
||||
pub fn typecast(self, target_dtype: Dtype) -> Self {
|
||||
|
||||
@@ -660,7 +660,7 @@ pub enum Error {
|
||||
ResolveError,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Parse {}
|
||||
|
||||
impl<P: AsRef<Path>> Translate<P> for Parse {
|
||||
|
||||
37
src/lib.rs
37
src/lib.rs
@@ -1,9 +1,44 @@
|
||||
//! KECC: KAIST Educational C Compiler.
|
||||
|
||||
// Tries to deny all lints (`rustc -W help`).
|
||||
#![deny(warnings)]
|
||||
// Neccessary for skeleton code.
|
||||
#![deny(absolute_paths_not_starting_with_crate)]
|
||||
#![deny(anonymous_parameters)]
|
||||
// #![deny(box_pointers)]
|
||||
#![deny(deprecated_in_future)]
|
||||
#![deny(elided_lifetimes_in_paths)]
|
||||
#![deny(explicit_outlives_requirements)]
|
||||
#![deny(invalid_html_tags)]
|
||||
#![deny(keyword_idents)]
|
||||
#![deny(macro_use_extern_crate)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
// #![deny(missing_docs)] TODO
|
||||
#![deny(missing_doc_code_examples)]
|
||||
#![deny(non_ascii_idents)]
|
||||
#![deny(pointer_structural_match)]
|
||||
// #![deny(single_use_lifetimes)]
|
||||
#![deny(trivial_numeric_casts)]
|
||||
#![deny(unaligned_references)]
|
||||
// #![deny(unreachable_pub)]
|
||||
#![deny(unstable_features)]
|
||||
// Necessary for `build-bin` trick.
|
||||
// #![deny(unused_crate_dependencies)]
|
||||
#![deny(unused_extern_crates)]
|
||||
#![deny(unused_import_braces)]
|
||||
#![deny(unused_lifetimes)]
|
||||
#![deny(unused_qualifications)]
|
||||
#![deny(unused_results)]
|
||||
// #![deny(variant_size_differences)]
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rustdoc::all)]
|
||||
// Necessary for skeleton code.
|
||||
#![allow(unreachable_code)]
|
||||
// Necessary to allow `iter.fold(false, |l, r| l || r)`. It's used when iteration should not be
|
||||
// short-circuited.
|
||||
#![allow(clippy::unnecessary_fold)]
|
||||
#![allow(clippy::result_unit_err)]
|
||||
#![allow(clippy::vec_init_then_push)]
|
||||
#![allow(clippy::collapsible_match)]
|
||||
|
||||
mod tests;
|
||||
mod utils;
|
||||
|
||||
@@ -22,15 +22,15 @@ pub trait Optimize<T> {
|
||||
pub type O0 = Null;
|
||||
pub type O1 = Repeat<(SimplifyCfg, (Mem2reg, (Gvn, Deadcode)))>;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Null {}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Repeat<O> {
|
||||
inner: O,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct FunctionPass<T: Optimize<ir::FunctionDefinition>> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
47
src/tests.rs
47
src/tests.rs
@@ -14,7 +14,8 @@ const NONCE_NAME: &str = "nonce";
|
||||
fn modify_c(path: &Path, rand_num: i32) -> String {
|
||||
let mut src = File::open(path).expect("`path` must exist");
|
||||
let mut data = String::new();
|
||||
src.read_to_string(&mut data)
|
||||
let _ = src
|
||||
.read_to_string(&mut data)
|
||||
.expect("`src` must be converted to string");
|
||||
drop(src);
|
||||
|
||||
@@ -69,8 +70,9 @@ fn modify_asm(unit: &mut asm::Asm, rand_num: i32) {
|
||||
|
||||
// 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;
|
||||
const SKIP_TEST: i32 = 102;
|
||||
|
||||
/// Tests write_c.
|
||||
pub fn test_write_c(path: &Path) {
|
||||
// Check if the file has .c extension
|
||||
assert_eq!(path.extension(), Some(std::ffi::OsStr::new("c")));
|
||||
@@ -82,7 +84,7 @@ pub fn test_write_c(path: &Path) {
|
||||
let temp_file_path = temp_dir.path().join("temp.c");
|
||||
let mut temp_file = File::create(&temp_file_path).unwrap();
|
||||
|
||||
crate::write(&unit, &mut temp_file).unwrap();
|
||||
write(&unit, &mut temp_file).unwrap();
|
||||
|
||||
let new_unit = c::Parse::default()
|
||||
.translate(&temp_file_path.as_path())
|
||||
@@ -92,6 +94,7 @@ pub fn test_write_c(path: &Path) {
|
||||
temp_dir.close().expect("temp dir deletion failed");
|
||||
}
|
||||
|
||||
/// Tests irgen.
|
||||
pub fn test_irgen(path: &Path) {
|
||||
// Check if the file has .c extension
|
||||
assert_eq!(path.extension(), Some(std::ffi::OsStr::new("c")));
|
||||
@@ -103,7 +106,7 @@ pub fn test_irgen(path: &Path) {
|
||||
.translate(&unit)
|
||||
.unwrap_or_else(|irgen_error| panic!("{}", irgen_error));
|
||||
|
||||
let rand_num = rand::thread_rng().gen_range(1, 100);
|
||||
let rand_num = rand::thread_rng().gen_range(1..100);
|
||||
let new_c = modify_c(path, rand_num);
|
||||
modify_ir(&mut ir, rand_num);
|
||||
|
||||
@@ -151,7 +154,7 @@ pub fn test_irgen(path: &Path) {
|
||||
{
|
||||
println!("timeout occurs");
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap();
|
||||
let _ = child.wait().unwrap();
|
||||
::std::process::exit(SKIP_TEST);
|
||||
}
|
||||
);
|
||||
@@ -187,6 +190,7 @@ pub fn test_irgen(path: &Path) {
|
||||
assert_eq!(status as u8, value as u8);
|
||||
}
|
||||
|
||||
/// Tests irparse.
|
||||
pub fn test_irparse(path: &Path) {
|
||||
// Check if the file has .c extension
|
||||
assert_eq!(path.extension(), Some(std::ffi::OsStr::new("c")));
|
||||
@@ -195,7 +199,7 @@ pub fn test_irparse(path: &Path) {
|
||||
.unwrap_or_else(|_| panic!("parse failed {}", path.display()));
|
||||
|
||||
// Test parse
|
||||
c::Parse::default()
|
||||
let _ = c::Parse::default()
|
||||
.translate(&path)
|
||||
.expect("failed to parse the given program");
|
||||
|
||||
@@ -207,7 +211,7 @@ pub fn test_irparse(path: &Path) {
|
||||
.unwrap_or_else(|irgen_error| panic!("{}", irgen_error));
|
||||
let temp_file_path = temp_dir.path().join("ir0.ir");
|
||||
let mut temp_file = File::create(&temp_file_path).unwrap();
|
||||
crate::write(&ir, &mut temp_file).unwrap();
|
||||
write(&ir, &mut temp_file).unwrap();
|
||||
|
||||
let ir0 = ir::Parse::default()
|
||||
.translate(&temp_file_path.as_path())
|
||||
@@ -233,9 +237,9 @@ fn test_irparse_for_optimized_ir<O: Optimize<ir::TranslationUnit>>(
|
||||
temp_file_path: &Path,
|
||||
mut opt: O,
|
||||
) -> ir::TranslationUnit {
|
||||
opt.optimize(&mut ir);
|
||||
let _ = opt.optimize(&mut ir);
|
||||
let mut temp_file = File::create(temp_file_path).unwrap();
|
||||
crate::write(&ir, &mut temp_file).unwrap();
|
||||
write(&ir, &mut temp_file).unwrap();
|
||||
|
||||
let optimized_ir = ir::Parse::default()
|
||||
.translate(&temp_file_path)
|
||||
@@ -246,6 +250,7 @@ fn test_irparse_for_optimized_ir<O: Optimize<ir::TranslationUnit>>(
|
||||
optimized_ir
|
||||
}
|
||||
|
||||
/// Tests optimizations.
|
||||
pub fn test_opt<P1: AsRef<Path>, P2: AsRef<Path>, O: Optimize<ir::TranslationUnit>>(
|
||||
from: &P1,
|
||||
to: &P2,
|
||||
@@ -258,7 +263,7 @@ pub fn test_opt<P1: AsRef<Path>, P2: AsRef<Path>, O: Optimize<ir::TranslationUni
|
||||
let to = ir::Parse::default()
|
||||
.translate(to)
|
||||
.expect("parse failed while parsing the output from implemented printer");
|
||||
opt.optimize(&mut ir);
|
||||
let _ = opt.optimize(&mut ir);
|
||||
|
||||
if !ir.is_equiv(&to) {
|
||||
stderr()
|
||||
@@ -267,21 +272,22 @@ pub fn test_opt<P1: AsRef<Path>, P2: AsRef<Path>, O: Optimize<ir::TranslationUni
|
||||
"[test_opt] actual outcome mismatches with the expected outcome.\n\n[before opt]"
|
||||
))
|
||||
.unwrap();
|
||||
crate::write(&from, &mut stderr()).unwrap();
|
||||
write(&from, &mut stderr()).unwrap();
|
||||
stderr()
|
||||
.lock()
|
||||
.write_fmt(format_args!("\n[after opt]"))
|
||||
.unwrap();
|
||||
crate::write(&ir, &mut stderr()).unwrap();
|
||||
write(&ir, &mut stderr()).unwrap();
|
||||
stderr()
|
||||
.lock()
|
||||
.write_fmt(format_args!("\n[after opt (expected)]"))
|
||||
.unwrap();
|
||||
crate::write(&to, &mut stderr()).unwrap();
|
||||
write(&to, &mut stderr()).unwrap();
|
||||
panic!("[test_opt]");
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests asmgen.
|
||||
pub fn test_asmgen(path: &Path) {
|
||||
// Check if the file has .ir extension
|
||||
assert_eq!(path.extension(), Some(std::ffi::OsStr::new("ir")));
|
||||
@@ -294,7 +300,7 @@ pub fn test_asmgen(path: &Path) {
|
||||
.translate(&ir)
|
||||
.expect("fail to create riscv assembly code");
|
||||
|
||||
let rand_num = rand::thread_rng().gen_range(1, 100);
|
||||
let rand_num = rand::thread_rng().gen_range(1..100);
|
||||
modify_ir(&mut ir, rand_num);
|
||||
modify_asm(&mut asm, rand_num);
|
||||
|
||||
@@ -320,7 +326,7 @@ pub fn test_asmgen(path: &Path) {
|
||||
write(&asm, &mut buffer).unwrap();
|
||||
|
||||
// Compile the assembly code
|
||||
if !Command::new("riscv64-linux-gnu-gcc-10")
|
||||
if !Command::new("riscv64-linux-gnu-gcc")
|
||||
.args(&["-static", &asm_path_str, "-o", &bin_path_str])
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
@@ -344,7 +350,7 @@ pub fn test_asmgen(path: &Path) {
|
||||
{
|
||||
println!("timeout occurs");
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap();
|
||||
let _ = child.wait().unwrap();
|
||||
::std::process::exit(SKIP_TEST);
|
||||
}
|
||||
);
|
||||
@@ -368,6 +374,7 @@ pub fn test_asmgen(path: &Path) {
|
||||
assert_eq!(value as u8, qemu_status as u8);
|
||||
}
|
||||
|
||||
/// Tests end-to-end translation.
|
||||
// TODO: test all the way down to assembly
|
||||
pub fn test_end_to_end(path: &Path) {
|
||||
// Check if the file has .c extension
|
||||
@@ -377,7 +384,7 @@ pub fn test_end_to_end(path: &Path) {
|
||||
.unwrap_or_else(|_| panic!("parse failed {}", path.display()));
|
||||
|
||||
// Test parse
|
||||
c::Parse::default()
|
||||
let _ = c::Parse::default()
|
||||
.translate(&path)
|
||||
.expect("failed to parse the given program");
|
||||
|
||||
@@ -412,7 +419,7 @@ pub fn test_end_to_end(path: &Path) {
|
||||
.spawn()
|
||||
.expect("failed to execute the compiled executable");
|
||||
|
||||
Command::new("rm")
|
||||
let _ = Command::new("rm")
|
||||
.arg(bin_path)
|
||||
.status()
|
||||
.expect("failed to remove compiled executable");
|
||||
@@ -424,7 +431,7 @@ pub fn test_end_to_end(path: &Path) {
|
||||
{
|
||||
println!("timeout occurs");
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap();
|
||||
let _ = child.wait().unwrap();
|
||||
::std::process::exit(SKIP_TEST);
|
||||
}
|
||||
);
|
||||
@@ -445,7 +452,7 @@ pub fn test_end_to_end(path: &Path) {
|
||||
let mut ir = Irgen::default()
|
||||
.translate(&unit)
|
||||
.unwrap_or_else(|irgen_error| panic!("{}", irgen_error));
|
||||
O1::default().optimize(&mut ir);
|
||||
let _ = O1::default().optimize(&mut ir);
|
||||
let args = Vec::new();
|
||||
let result = ir::interp(&ir, args).unwrap_or_else(|interp_error| panic!("{}", interp_error));
|
||||
// We only allow main function whose return type is `int`
|
||||
|
||||
@@ -49,18 +49,27 @@ macro_rules! some_or_exit {
|
||||
}};
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
pub trait Translate<S> {
|
||||
/// TODO(document)
|
||||
type Target;
|
||||
|
||||
/// TODO(document)
|
||||
type Error;
|
||||
|
||||
/// TODO(document)
|
||||
fn translate(&mut self, source: &S) -> Result<Self::Target, Self::Error>;
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
pub trait AssertSupported {
|
||||
/// TODO(document)
|
||||
fn assert_supported(&self);
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
pub trait IsEquiv {
|
||||
/// TODO(document)
|
||||
fn is_equiv(&self, other: &Self) -> bool;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
use std::io::{Result, Write};
|
||||
|
||||
/// TODO(document)
|
||||
#[inline]
|
||||
pub fn write_indent(indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
write!(write, "{}", " ".repeat(indent))
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
pub trait WriteLine {
|
||||
/// TODO(document)
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()>;
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
pub trait WriteString {
|
||||
/// TODO(document)
|
||||
fn write_string(&self) -> String;
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
pub trait WriteOp {
|
||||
/// TODO(document)
|
||||
fn write_operation(&self) -> String;
|
||||
}
|
||||
|
||||
/// TODO(document)
|
||||
pub fn write<T: WriteLine>(t: &T, write: &mut dyn Write) -> Result<()> {
|
||||
t.write_line(0, write)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user