This commit is contained in:
Janggun Lee
2025-01-06 18:45:28 +09:00
parent 021f2cd240
commit fcb6ca2538
15 changed files with 253 additions and 423 deletions

View File

@@ -1,12 +1,11 @@
mod write_asm;
use crate::ir;
use crate::write_base::*;
use core::convert::TryFrom;
use core::fmt;
/// TODO
/// An assembly file.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Asm {
pub unit: TranslationUnit,
@@ -20,9 +19,9 @@ pub struct TranslationUnit {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Section<T> {
/// Section Headers provide size, offset, type, alignment and flags of the sections
/// Headers provide size, offset, type, alignment and flag information.
///
/// For more details: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#section-header>
/// For more information: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#section-header>
pub header: Vec<Directive>,
pub body: T,
}
@@ -30,7 +29,7 @@ pub struct Section<T> {
/// An object file is made up of multiple sections, with each section corresponding to distinct
/// types of executable code or data.
///
/// For more details: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#sections>
/// For more information: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#sections>
impl<T> Section<T> {
pub fn new(header: Vec<Directive>, body: T) -> Self {
Self { header, body }
@@ -174,38 +173,29 @@ impl fmt::Display for SymbolType {
}
}
/// RISC-V Base Instructions Set.
///
/// See Volume 1 of [RISC-V Technical Specifications](https://lf-riscv.atlassian.net/wiki/spaces/HOME/pages/16154769/RISC-V+Technical+Specifications).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Instruction {
/// R-type instruction format
///
/// For more details: <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (104p)
RType {
instr: RType,
rd: Register,
rs1: Register,
rs2: Option<Register>,
},
/// I-type instruction format
///
/// For more details: <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (104p)
IType {
instr: IType,
rd: Register,
rs1: Register,
imm: Immediate,
},
/// S-type instruction format
///
/// For more details: <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (104p)
SType {
instr: SType,
rs1: Register,
rs2: Register,
imm: Immediate,
},
/// B-type instruction format
///
/// For more details: <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (104p)
BType {
instr: BType,
rs1: Register,
@@ -280,8 +270,7 @@ impl fmt::Display for Instruction {
}
}
/// If the enum variant contains `bool`,
/// It means that different instructions exist
/// If the enum variant contains `is_signed : bool`, it corresponds to different instructions
/// depending on whether the operand is signed or not.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RType {
@@ -512,13 +501,20 @@ impl RType {
impl fmt::Display for RType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let filter_word = |data_size: &DataSize| -> String {
if data_size.is_word() {
data_size.to_string()
} else {
"".to_string()
}
};
match self {
Self::Add(data_size) => write!(f, "add{}", data_size.word().write_string()),
Self::Sub(data_size) => write!(f, "sub{}", data_size.word().write_string()),
Self::Sll(data_size) => write!(f, "sll{}", data_size.word().write_string()),
Self::Srl(data_size) => write!(f, "srl{}", data_size.word().write_string()),
Self::Sra(data_size) => write!(f, "sra{}", data_size.word().write_string()),
Self::Mul(data_size) => write!(f, "mul{}", data_size.word().write_string()),
Self::Add(data_size) => write!(f, "add{}", filter_word(data_size)),
Self::Sub(data_size) => write!(f, "sub{}", filter_word(data_size)),
Self::Sll(data_size) => write!(f, "sll{}", filter_word(data_size)),
Self::Srl(data_size) => write!(f, "srl{}", filter_word(data_size)),
Self::Sra(data_size) => write!(f, "sra{}", filter_word(data_size)),
Self::Mul(data_size) => write!(f, "mul{}", filter_word(data_size)),
Self::Div {
data_size,
is_signed,
@@ -526,7 +522,7 @@ impl fmt::Display for RType {
f,
"div{}{}",
if *is_signed { "" } else { "u" },
data_size.word().write_string()
filter_word(data_size)
),
Self::Rem {
data_size,
@@ -535,7 +531,7 @@ impl fmt::Display for RType {
f,
"rem{}{}",
if *is_signed { "" } else { "u" },
data_size.word().write_string()
filter_word(data_size)
),
Self::Slt { is_signed } => write!(f, "slt{}", if *is_signed { "" } else { "u" }),
Self::Xor => write!(f, "xor"),
@@ -696,6 +692,13 @@ impl IType {
impl fmt::Display for IType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let filter_word = |data_size: &DataSize| -> String {
if data_size.is_word() {
data_size.to_string()
} else {
"".to_string()
}
};
match self {
Self::Load {
data_size,
@@ -715,13 +718,13 @@ impl fmt::Display for IType {
)
}
}
Self::Addi(data_size) => write!(f, "addi{}", data_size.word().write_string()),
Self::Addi(data_size) => write!(f, "addi{}", filter_word(data_size)),
Self::Xori => write!(f, "xori"),
Self::Ori => write!(f, "ori"),
Self::Andi => write!(f, "andi"),
Self::Slli(data_size) => write!(f, "slli{}", data_size.word().write_string()),
Self::Srli(data_size) => write!(f, "srli{}", data_size.word().write_string()),
Self::Srai(data_size) => write!(f, "srai{}", data_size.word().write_string()),
Self::Slli(data_size) => write!(f, "slli{}", filter_word(data_size)),
Self::Srli(data_size) => write!(f, "srli{}", filter_word(data_size)),
Self::Srai(data_size) => write!(f, "srai{}", filter_word(data_size)),
Self::Slti { is_signed } => write!(f, "slti{}", if *is_signed { "" } else { "u" }),
}
}
@@ -805,9 +808,7 @@ impl fmt::Display for UType {
/// instructions in the base ISA, but have implicit arguments or reversed arguments that result in
/// distinct semantics.
///
/// For more information:
/// - <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#assembler-pseudo-instructions>
/// - <https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (110p)
/// For more information: <https://github.com/michaeljclark/michaeljclark.github.io/blob/master/asm.md#assembler-pseudo-instructions>
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Pseudo {
/// la rd, symbol
@@ -876,13 +877,20 @@ impl Pseudo {
impl fmt::Display for Pseudo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let filter_word = |data_size: &DataSize| -> String {
if data_size.is_word() {
data_size.to_string()
} else {
"".to_string()
}
};
match self {
Self::La { rd, symbol } => write!(f, "la\t{rd},{symbol}"),
Self::Li { rd, imm } => write!(f, "li\t{},{}", rd, *imm as i64),
Self::Mv { rd, rs } => write!(f, "mv\t{rd},{rs}"),
Self::Fmv { data_size, rd, rs } => write!(f, "fmv.{data_size}\t{rd},{rs}"),
Self::Neg { data_size, rd, rs } => {
write!(f, "neg{}\t{},{}", data_size.word().write_string(), rd, rs)
write!(f, "neg{}\t{},{}", filter_word(data_size), rd, rs)
}
Self::SextW { rs, rd } => write!(f, "sext.w\t{rd},{rs}"),
Self::Seqz { rd, rs } => write!(f, "seqz\t{rd},{rs}"),
@@ -929,8 +937,8 @@ impl fmt::Display for Immediate {
}
}
/// The relocation function creates synthesize operand values that are resolved
/// at program link time and are used as immediate parameters for specific instructions.
/// The relocation function creates synthesize operand values that are resolved at program link time
/// and are used as immediate parameters for specific instructions.
///
/// For more details: <https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -1023,12 +1031,8 @@ impl DataSize {
matches!(self, Self::SinglePrecision | Self::DoublePrecision)
}
fn word(self) -> Option<Self> {
if self == DataSize::Word {
Some(self)
} else {
None
}
fn is_word(&self) -> bool {
matches!(self, Self::Word)
}
}
@@ -1049,7 +1053,7 @@ impl fmt::Display for DataSize {
}
}
/// ABI name for RISC-V integer and floating-point register
/// ABI name for RISC-V integer and floating-point register.
///
/// For more details: <https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf> (109p)
// TODO: Add calling convention information (caller/callee-save registers)

View File

@@ -73,87 +73,3 @@ impl WriteLine for Block {
Ok(())
}
}
impl WriteString for Directive {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for SectionType {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for SymbolType {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for Instruction {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for RType {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for IType {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for SType {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for BType {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for UType {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for Pseudo {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for Immediate {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for RelocationFunction {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for DataSize {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for Register {
fn write_string(&self) -> String {
format!("{self}")
}
}

View File

@@ -18,31 +18,9 @@ impl<T: WriteString> WriteString for Node<T> {
}
}
impl<T: WriteString> WriteString for Box<T> {
fn write_string(&self) -> String {
self.deref().write_string()
}
}
impl<T: WriteString> WriteString for &T {
fn write_string(&self) -> String {
(*self).write_string()
}
}
impl<T: WriteString> WriteString for Option<T> {
fn write_string(&self) -> String {
if let Some(this) = self {
this.write_string()
} else {
"".to_string()
}
}
}
impl WriteLine for TranslationUnit {
fn write_line(&self, _indent: usize, _write: &mut dyn Write) -> Result<()> {
todo!("homework 1")
todo!("Homework: write C")
}
}

View File

@@ -921,7 +921,7 @@ impl Dtype {
match self {
Self::Unit { .. } => Ok((0, 1)),
Self::Int { width, .. } | Self::Float { width, .. } => {
let size_of = (*width + Self::BITS_OF_BYTE - 1) / Self::BITS_OF_BYTE;
let size_of = (*width).div_ceil(Self::BITS_OF_BYTE);
let align_of = size_of;
Ok((size_of, align_of))

View File

@@ -1155,7 +1155,7 @@ 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<'i>, InterpreterError> {
// Interpreter starts with the main function
let func_name = String::from("main");
let func = ir

View File

@@ -17,7 +17,6 @@ use ordered_float::OrderedFloat;
use std::collections::{BTreeMap, HashMap};
use std::hash::{Hash, Hasher};
use crate::write_base::*;
pub use dtype::{Dtype, DtypeError, HasDtype};
pub use interp::{interp, Value};
pub use parse::Parse;
@@ -260,6 +259,16 @@ impl Instruction {
}
}
/// Format `lang_c::ast::{Binary,Unary}Operations` into KECC-IR.
///
/// Most cases, `fmt::Display` is used to format a type to a string. However, in some cases, we
/// can't implement `fmt::Display` for a type as it is defined in another crate. In such cases, we
/// can implement this trait to format the type to a string.
pub trait WriteOp {
/// Change operations into a String.
fn write_operation(&self) -> String;
}
impl WriteOp for ast::BinaryOperator {
fn write_operation(&self) -> String {
match self {

View File

@@ -157,21 +157,3 @@ impl WriteLine for (&BlockId, &Block) {
Ok(())
}
}
impl WriteString for Instruction {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for Operand {
fn write_string(&self) -> String {
format!("{self}")
}
}
impl WriteString for BlockExit {
fn write_string(&self) -> String {
format!("{self}")
}
}

View File

@@ -590,8 +590,11 @@ impl IrgenFunc<'_> {
///
/// ```C
/// int foo(int x, int y, int z) {
/// if (x == y) { return y; }
/// else { return z; }
/// if (x == y) {
/// return y;
/// } else {
/// return z;
/// }
/// }
/// ```
///
@@ -615,9 +618,10 @@ impl IrgenFunc<'_> {
/// bid_init = 0
/// name_of_params = ["x", "y", "z"]
/// context = // omitted
/// ```
/// ```
///
/// The resulting IR after this function should be roughly follows :
///
/// Resulting IR after this function should be roughly follows:
/// ```text
/// fun i32 @foo (i32, i32, i32) {
/// init:
@@ -635,7 +639,7 @@ impl IrgenFunc<'_> {
/// %b0:i1:unit = store %b0:p1:i32 %l1:i32*
/// %b0:i2:unit = store %b0:p2:i32 %l2:i32*
/// ```
///
///
/// In particular, note that it is added to the local allocation list and store them to the
/// initial phinodes.
///

View File

@@ -21,7 +21,6 @@
// #![deny(missing_docs)]
#![deny(non_ascii_idents)]
#![deny(noop_method_call)]
#![deny(pointer_structural_match)]
#![deny(rust_2021_incompatible_closure_captures)]
#![deny(rust_2021_incompatible_or_patterns)]
#![deny(rust_2021_prefixes_incompatible_syntax)]

View File

@@ -1,30 +1,52 @@
use std::io::{Result, Write};
/// TODO(document)
/// Write `indent` number of double spaces to `write`.
#[inline]
pub fn write_indent(indent: usize, write: &mut dyn Write) -> Result<()> {
write!(write, "{}", " ".repeat(indent))
}
/// TODO(document)
/// A trait for writing a type to a `Write` stream with a new line.
pub trait WriteLine {
/// TODO(document)
/// Write `self` to `write`, starting at `indent` number of double spaces, with a newline at the
/// ned.
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()>;
}
/// TODO(document)
/// Format types to a String.
///
/// Most cases, `fmt::Display` is used to format a type to a string. However, in some cases, we
/// can't implement `fmt::Display` for a type as it is defined in another crate. In such cases, we
/// can implement this trait to format the type to a string.
pub trait WriteString {
/// TODO(document)
/// Change a type into a String.
fn write_string(&self) -> String;
}
/// TODO(document)
pub trait WriteOp {
/// TODO(document)
fn write_operation(&self) -> String;
impl<T: WriteString> WriteString for Box<T> {
fn write_string(&self) -> String {
use core::ops::Deref;
self.deref().write_string()
}
}
/// TODO(document)
impl<T: WriteString> WriteString for &T {
fn write_string(&self) -> String {
(*self).write_string()
}
}
impl<T: WriteString> WriteString for Option<T> {
fn write_string(&self) -> String {
if let Some(this) = self {
this.write_string()
} else {
"".to_string()
}
}
}
/// Write `t` to `write`.
pub fn write<T: WriteLine>(t: &T, write: &mut dyn Write) -> Result<()> {
t.write_line(0, write)
}