mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-16 15:38:48 +00:00
Update skeleton
This commit is contained in:
257
src/asm/mod.rs
257
src/asm/mod.rs
@@ -1,4 +1,259 @@
|
||||
mod write_asm;
|
||||
|
||||
use crate::ir;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TODO {}
|
||||
|
||||
/// TODO
|
||||
pub struct Asm {}
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Asm {
|
||||
pub unit: TranslationUnit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TranslationUnit {
|
||||
pub functions: Vec<Section<Function>>,
|
||||
pub variables: Vec<Section<Variable>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Section<T> {
|
||||
/// Section Headers provice size, offset, type, alignment and flags of the sections
|
||||
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#section-header
|
||||
pub header: Vec<Directive>,
|
||||
pub body: T,
|
||||
}
|
||||
|
||||
/// An object file is made up of multiple sections, with each section corresponding to
|
||||
/// distinct types of executable code or data.
|
||||
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#sections
|
||||
impl<T> Section<T> {
|
||||
pub fn new(header: Vec<Directive>, body: T) -> Self {
|
||||
Self { header, body }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Function {
|
||||
pub blocks: Vec<Block>,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn new(blocks: Vec<Block>) -> Self {
|
||||
Self { blocks }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Variable {
|
||||
todo: TODO,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Block {
|
||||
pub label: Option<Label>,
|
||||
pub instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub fn new(label: Option<Label>, instructions: Vec<Instruction>) -> Self {
|
||||
Self {
|
||||
label,
|
||||
instructions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The assembler implements a number of directives that control the assembly of instructions
|
||||
/// into an object file.
|
||||
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#assembler-directives
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Directive {
|
||||
/// .globl symbol
|
||||
Globl(Label),
|
||||
/// .type symbol, symbol_type
|
||||
Type(Label, SymbolType),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SymbolType {
|
||||
Function,
|
||||
Object,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Instruction {
|
||||
/// R-type instruction format
|
||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (11p, 104p)
|
||||
RType {
|
||||
instr: RType,
|
||||
rd: Register,
|
||||
rs1: Register,
|
||||
rs2: Register,
|
||||
},
|
||||
/// I-type instruction format
|
||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (11p, 104p)
|
||||
IType {
|
||||
instr: IType,
|
||||
rd: Register,
|
||||
rs1: Register,
|
||||
imm: isize,
|
||||
},
|
||||
/// S-type instruction format
|
||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (11p, 104p)
|
||||
SType {
|
||||
instr: SType,
|
||||
rs1: Register,
|
||||
rs2: Register,
|
||||
imm: isize,
|
||||
},
|
||||
Pseudo(Pseudo),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RType {
|
||||
Add(Option<DataSize>),
|
||||
Mul(Option<DataSize>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum IType {
|
||||
Load(DataSize),
|
||||
Addi(Option<DataSize>),
|
||||
}
|
||||
|
||||
impl IType {
|
||||
pub const LW: Self = Self::Load(DataSize::Word);
|
||||
pub const LD: Self = Self::Load(DataSize::Double);
|
||||
pub const ADDI: Self = Self::Addi(None);
|
||||
|
||||
pub fn load(dtype: ir::Dtype) -> Self {
|
||||
let data_align =
|
||||
DataSize::try_from(dtype).expect("`data_align` must be derived from `dtype`");
|
||||
Self::Load(data_align)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SType {
|
||||
Store(DataSize),
|
||||
}
|
||||
|
||||
impl SType {
|
||||
pub const SW: Self = Self::Store(DataSize::Word);
|
||||
pub const SD: Self = Self::Store(DataSize::Double);
|
||||
|
||||
pub fn store(dtype: ir::Dtype) -> Self {
|
||||
let data_align =
|
||||
DataSize::try_from(dtype).expect("`data_align` must be derived from `dtype`");
|
||||
Self::Store(data_align)
|
||||
}
|
||||
}
|
||||
|
||||
/// The assembler implements a number of convenience psuedo-instructions that are formed from
|
||||
/// instructions in the base ISA, but have implicit arguments or in some case reversed arguments,
|
||||
/// that result in distinct semantics.
|
||||
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#assembler-pseudo-instructions
|
||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (110p)
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Pseudo {
|
||||
/// li rd, immediate
|
||||
Li { rd: Register, imm: isize },
|
||||
/// mv rd, rs
|
||||
Mv { rs: Register, rd: Register },
|
||||
/// sext.w rd, rs
|
||||
SextW { rs: Register, rd: Register },
|
||||
/// j offset
|
||||
J { offset: Label },
|
||||
/// jr rs
|
||||
Jr { rs: Register },
|
||||
/// ret
|
||||
Ret,
|
||||
/// call offset
|
||||
Call { offset: Label },
|
||||
}
|
||||
|
||||
/// `Label` is used as branch, unconditional jump targets and symbol offsets.
|
||||
/// https://github.com/rv8-io/rv8-io.github.io/blob/master/asm.md#labels
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Label(pub String);
|
||||
|
||||
impl Label {
|
||||
pub fn new(name: &str, block_id: ir::BlockId) -> Self {
|
||||
let id = block_id.0;
|
||||
Self(format!(".{}_L{}", name, id))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum DataSize {
|
||||
Byte,
|
||||
Half,
|
||||
Word,
|
||||
Double,
|
||||
}
|
||||
|
||||
impl TryFrom<ir::Dtype> for DataSize {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(dtype: ir::Dtype) -> Result<Self, Self::Error> {
|
||||
let width = match dtype {
|
||||
ir::Dtype::Int { width, .. } => width,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
let size = (width - 1) / ir::Dtype::BITS_OF_BYTE + 1;
|
||||
let align = match size {
|
||||
ir::Dtype::SIZE_OF_CHAR => Self::Byte,
|
||||
ir::Dtype::SIZE_OF_SHORT => Self::Half,
|
||||
ir::Dtype::SIZE_OF_INT => Self::Word,
|
||||
ir::Dtype::SIZE_OF_LONG => Self::Double,
|
||||
_ => panic!("there is no other possible case"),
|
||||
};
|
||||
|
||||
Ok(align)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add calling convention information (caller/callee-save registers)
|
||||
/// ABI name for RISC-V integer and floating-point register
|
||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (109p)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||
pub enum Register {
|
||||
Zero,
|
||||
Ra,
|
||||
Sp,
|
||||
Gp,
|
||||
Tp,
|
||||
/// E.g., t0
|
||||
Temp(usize),
|
||||
/// E.g., s0
|
||||
Saved(usize),
|
||||
/// E.g., a0
|
||||
Arg(usize),
|
||||
}
|
||||
|
||||
impl Register {
|
||||
// TODO: add all possible registers in the future
|
||||
pub const S0: Self = Self::Saved(0);
|
||||
pub const A0: Self = Self::Arg(0);
|
||||
pub const A5: Self = Self::Arg(5);
|
||||
|
||||
pub fn temp(id: usize) -> Self {
|
||||
assert!(id <= 6);
|
||||
Self::Temp(id)
|
||||
}
|
||||
|
||||
pub fn saved(id: usize) -> Self {
|
||||
assert!(id <= 11);
|
||||
Self::Saved(id)
|
||||
}
|
||||
|
||||
pub fn arg(id: usize) -> Self {
|
||||
assert!(id <= 7);
|
||||
Self::Arg(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,213 @@
|
||||
use std::io::{Result, Write};
|
||||
|
||||
use crate::asm::Asm;
|
||||
use crate::write_base::WriteLine;
|
||||
use crate::asm::*;
|
||||
use crate::write_base::*;
|
||||
|
||||
const INDENT: usize = 4;
|
||||
|
||||
impl WriteLine for Asm {
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
self.unit.write_line(indent, write)
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteLine for TranslationUnit {
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
for function in &self.functions {
|
||||
function.write_line(indent, write)?;
|
||||
}
|
||||
|
||||
for variable in &self.variables {
|
||||
variable.write_line(indent, write)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WriteLine> WriteLine for Section<T> {
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
for directive in &self.header {
|
||||
write_indent(indent + INDENT, write)?;
|
||||
writeln!(write, "{}", directive.write_string())?;
|
||||
}
|
||||
self.body.write_line(indent, write)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteLine for Function {
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
for block in &self.blocks {
|
||||
block.write_line(indent, write)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteLine for Variable {
|
||||
fn write_line(&self, _indent: usize, _write: &mut dyn Write) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteLine for Block {
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
if let Some(label) = &self.label {
|
||||
writeln!(write, "{}:", label.0)?;
|
||||
}
|
||||
|
||||
for instruction in &self.instructions {
|
||||
write_indent(indent + INDENT, write)?;
|
||||
writeln!(write, "{}", instruction.write_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Directive {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Globl(label) => format!(".globl\t{}", label.0),
|
||||
Self::Type(symbol, symbol_type) => {
|
||||
format!(".type\t{}, {}", symbol.0, symbol_type.write_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for SymbolType {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Function => "@function",
|
||||
Self::Object => "@object",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Instruction {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::RType {
|
||||
instr,
|
||||
rd,
|
||||
rs1,
|
||||
rs2,
|
||||
} => format!(
|
||||
"{}\t{},{},{}",
|
||||
instr.write_string(),
|
||||
rd.write_string(),
|
||||
rs1.write_string(),
|
||||
rs2.write_string()
|
||||
),
|
||||
Self::IType {
|
||||
instr,
|
||||
rd,
|
||||
rs1,
|
||||
imm,
|
||||
} => {
|
||||
if let IType::Load(_) = instr {
|
||||
format!(
|
||||
"{}\t{},{}({})",
|
||||
instr.write_string(),
|
||||
rd.write_string(),
|
||||
imm,
|
||||
rs1.write_string()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}\t{},{},{}",
|
||||
instr.write_string(),
|
||||
rd.write_string(),
|
||||
rs1.write_string(),
|
||||
imm
|
||||
)
|
||||
}
|
||||
}
|
||||
Self::SType {
|
||||
instr,
|
||||
rs1,
|
||||
rs2,
|
||||
imm,
|
||||
} => format!(
|
||||
"{}\t{},{}({})",
|
||||
instr.write_string(),
|
||||
rs2.write_string(),
|
||||
imm,
|
||||
rs1.write_string()
|
||||
),
|
||||
Self::Pseudo(pseudo) => pseudo.write_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for RType {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Add(data_size) => format!("add{}", data_size.write_string()),
|
||||
Self::Mul(data_size) => format!("mul{}", data_size.write_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for IType {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Load(data_size) => format!("l{}", data_size.write_string()),
|
||||
Self::Addi(data_size) => format!("addi{}", data_size.write_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for SType {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Store(data_size) => format!("s{}", data_size.write_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Pseudo {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Li { rd, imm } => format!("li\t{},{}", rd.write_string(), imm),
|
||||
Self::Mv { rs, rd } => format!("mv\t{},{}", rd.write_string(), rs.write_string()),
|
||||
Self::SextW { .. } => todo!(),
|
||||
Self::J { offset } => format!("j\t{}", offset.0),
|
||||
Self::Jr { rs } => format!("jr\t{}", rs.write_string()),
|
||||
Self::Ret => "ret".to_string(),
|
||||
Self::Call { offset } => format!("call\t{}", offset.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for DataSize {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Byte => "b",
|
||||
Self::Half => "h",
|
||||
Self::Word => "w",
|
||||
Self::Double => "d",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for Register {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Zero => "zero".to_string(),
|
||||
Self::Ra => "ra".to_string(),
|
||||
Self::Sp => "sp".to_string(),
|
||||
Self::Gp => "gp".to_string(),
|
||||
Self::Tp => "tp".to_string(),
|
||||
Self::Temp(id) => format!("t{}", id),
|
||||
Self::Saved(id) => format!("s{}", id),
|
||||
Self::Arg(id) => format!("a{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user