mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-16 23:48:48 +00:00
Update skeleton
This commit is contained in:
164
src/asm/mod.rs
164
src/asm/mod.rs
@@ -87,7 +87,7 @@ pub enum SymbolType {
|
||||
#[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)
|
||||
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||
RType {
|
||||
instr: RType,
|
||||
rd: Register,
|
||||
@@ -95,7 +95,7 @@ pub enum Instruction {
|
||||
rs2: Register,
|
||||
},
|
||||
/// I-type instruction format
|
||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (11p, 104p)
|
||||
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||
IType {
|
||||
instr: IType,
|
||||
rd: Register,
|
||||
@@ -103,26 +103,109 @@ pub enum Instruction {
|
||||
imm: isize,
|
||||
},
|
||||
/// S-type instruction format
|
||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (11p, 104p)
|
||||
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||
SType {
|
||||
instr: SType,
|
||||
rs1: Register,
|
||||
rs2: Register,
|
||||
imm: isize,
|
||||
},
|
||||
/// B-type instruction format
|
||||
/// https://riscv.org/specifications/isa-spec-pdf/ (16p, 129p)
|
||||
BType {
|
||||
instr: BType,
|
||||
rs1: Register,
|
||||
rs2: Register,
|
||||
imm: Label,
|
||||
},
|
||||
Pseudo(Pseudo),
|
||||
}
|
||||
|
||||
/// If the enum variant contains `Option<DataSize>`,
|
||||
/// it means that the instructions used may vary according to `DataSize`.
|
||||
/// Use 'Some' if RISC-V ISA provides instruction to support a specific 'DataSize',
|
||||
/// if not, 'None' which means to use default instruction.
|
||||
/// Because KECC uses RV64 (RISC-V ISA for 64-bit architecture),
|
||||
/// KECC uses `Some` if `DataSize` is `Word`, if not, use `None`.
|
||||
/// https://riscv.org/specifications/isa-spec-pdf/ (35p)
|
||||
///
|
||||
/// If the enum variant contains `bool`,
|
||||
/// It means that different instructions exist
|
||||
/// depending on whether the operand is signed or not.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RType {
|
||||
Add(Option<DataSize>),
|
||||
Sub(Option<DataSize>),
|
||||
Mul(Option<DataSize>),
|
||||
Div(Option<DataSize>, bool),
|
||||
Slt(bool),
|
||||
Xor,
|
||||
}
|
||||
|
||||
impl RType {
|
||||
pub fn add(dtype: ir::Dtype) -> Self {
|
||||
let data_size =
|
||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||
let data_size = if data_size == DataSize::Word {
|
||||
Some(data_size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self::Add(data_size)
|
||||
}
|
||||
|
||||
pub fn sub(dtype: ir::Dtype) -> Self {
|
||||
let data_size =
|
||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||
let data_size = if data_size == DataSize::Word {
|
||||
Some(data_size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self::Sub(data_size)
|
||||
}
|
||||
|
||||
pub fn mul(dtype: ir::Dtype) -> Self {
|
||||
let data_size =
|
||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||
let data_size = if data_size == DataSize::Word {
|
||||
Some(data_size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self::Mul(data_size)
|
||||
}
|
||||
|
||||
pub fn div(dtype: ir::Dtype, is_signed: bool) -> Self {
|
||||
let data_size =
|
||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||
let data_size = if data_size == DataSize::Word {
|
||||
Some(data_size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self::Div(data_size, is_signed)
|
||||
}
|
||||
}
|
||||
|
||||
/// If the enum variant contains `Option<DataSize>`,
|
||||
/// it means that the instructions used may vary according to `DataSize`.
|
||||
/// Use 'Some' if RISC-V ISA provides instruction to support a specific 'DataSize',
|
||||
/// if not, 'None' which means to use default instruction.
|
||||
/// Because KECC uses RV64 (RISC-V ISA for 64-bit architecture),
|
||||
/// KECC uses `Some` if `DataSize` is `Word`, if not, use `None`.
|
||||
/// https://riscv.org/specifications/isa-spec-pdf/ (35p)
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum IType {
|
||||
Load(DataSize),
|
||||
Addi(Option<DataSize>),
|
||||
Andi,
|
||||
Slli,
|
||||
Srli,
|
||||
}
|
||||
|
||||
impl IType {
|
||||
@@ -131,9 +214,9 @@ impl IType {
|
||||
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)
|
||||
let data_size =
|
||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||
Self::Load(data_size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,12 +230,20 @@ impl SType {
|
||||
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)
|
||||
let data_size =
|
||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||
Self::Store(data_size)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum BType {
|
||||
Beq,
|
||||
Bne,
|
||||
Blt(bool),
|
||||
Bge(bool),
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -163,9 +254,17 @@ pub enum Pseudo {
|
||||
/// li rd, immediate
|
||||
Li { rd: Register, imm: isize },
|
||||
/// mv rd, rs
|
||||
Mv { rs: Register, rd: Register },
|
||||
Mv { rd: Register, rs: Register },
|
||||
/// neg(w) rd, rs
|
||||
Neg {
|
||||
data_size: Option<DataSize>,
|
||||
rs: Register,
|
||||
rd: Register,
|
||||
},
|
||||
/// sext.w rd, rs
|
||||
SextW { rs: Register, rd: Register },
|
||||
SextW { rd: Register, rs: Register },
|
||||
/// seqz rd, rs
|
||||
Seqz { rd: Register, rs: Register },
|
||||
/// j offset
|
||||
J { offset: Label },
|
||||
/// jr rs
|
||||
@@ -176,6 +275,20 @@ pub enum Pseudo {
|
||||
Call { offset: Label },
|
||||
}
|
||||
|
||||
impl Pseudo {
|
||||
pub fn neg(dtype: ir::Dtype, rs: Register, rd: Register) -> Self {
|
||||
let data_size =
|
||||
DataSize::try_from(dtype).expect("`data_size` must be derived from `dtype`");
|
||||
let data_size = if data_size == DataSize::Word {
|
||||
Some(data_size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self::Neg { data_size, rs, rd }
|
||||
}
|
||||
}
|
||||
|
||||
/// `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)]
|
||||
@@ -220,7 +333,7 @@ impl TryFrom<ir::Dtype> for DataSize {
|
||||
|
||||
// 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)
|
||||
/// https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf (155p)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||
pub enum Register {
|
||||
Zero,
|
||||
@@ -237,10 +350,35 @@ pub enum Register {
|
||||
}
|
||||
|
||||
impl Register {
|
||||
// TODO: add all possible registers in the future
|
||||
pub const T0: Self = Self::Temp(0);
|
||||
pub const T1: Self = Self::Temp(1);
|
||||
pub const T2: Self = Self::Temp(2);
|
||||
pub const T3: Self = Self::Temp(3);
|
||||
pub const T4: Self = Self::Temp(4);
|
||||
pub const T5: Self = Self::Temp(5);
|
||||
pub const T6: Self = Self::Temp(6);
|
||||
|
||||
pub const S0: Self = Self::Saved(0);
|
||||
pub const S1: Self = Self::Saved(1);
|
||||
pub const S2: Self = Self::Saved(2);
|
||||
pub const S3: Self = Self::Saved(3);
|
||||
pub const S4: Self = Self::Saved(4);
|
||||
pub const S5: Self = Self::Saved(5);
|
||||
pub const S6: Self = Self::Saved(6);
|
||||
pub const S7: Self = Self::Saved(7);
|
||||
pub const S8: Self = Self::Saved(8);
|
||||
pub const S9: Self = Self::Saved(9);
|
||||
pub const S10: Self = Self::Saved(10);
|
||||
pub const S11: Self = Self::Saved(11);
|
||||
|
||||
pub const A0: Self = Self::Arg(0);
|
||||
pub const A1: Self = Self::Arg(1);
|
||||
pub const A2: Self = Self::Arg(2);
|
||||
pub const A3: Self = Self::Arg(3);
|
||||
pub const A4: Self = Self::Arg(4);
|
||||
pub const A5: Self = Self::Arg(5);
|
||||
pub const A6: Self = Self::Arg(6);
|
||||
pub const A7: Self = Self::Arg(7);
|
||||
|
||||
pub fn temp(id: usize) -> Self {
|
||||
assert!(id <= 6);
|
||||
|
||||
@@ -137,9 +137,21 @@ impl WriteString for Instruction {
|
||||
"{}\t{},{}({})",
|
||||
instr.write_string(),
|
||||
rs2.write_string(),
|
||||
imm,
|
||||
imm.to_string(),
|
||||
rs1.write_string()
|
||||
),
|
||||
Self::BType {
|
||||
instr,
|
||||
rs1,
|
||||
rs2,
|
||||
imm,
|
||||
} => format!(
|
||||
"{}\t{},{}, {}",
|
||||
instr.write_string(),
|
||||
rs1.write_string(),
|
||||
rs2.write_string(),
|
||||
imm.0,
|
||||
),
|
||||
Self::Pseudo(pseudo) => pseudo.write_string(),
|
||||
}
|
||||
}
|
||||
@@ -149,7 +161,15 @@ impl WriteString for RType {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Add(data_size) => format!("add{}", data_size.write_string()),
|
||||
Self::Sub(data_size) => format!("sub{}", data_size.write_string()),
|
||||
Self::Mul(data_size) => format!("mul{}", data_size.write_string()),
|
||||
Self::Div(data_size, is_signed) => format!(
|
||||
"div{}{}",
|
||||
if *is_signed { "" } else { "u" },
|
||||
data_size.write_string()
|
||||
),
|
||||
Self::Slt(is_signed) => format!("slt{}", if *is_signed { "" } else { "u" }),
|
||||
Self::Xor => "xor".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +179,9 @@ impl WriteString for IType {
|
||||
match self {
|
||||
Self::Load(data_size) => format!("l{}", data_size.write_string()),
|
||||
Self::Addi(data_size) => format!("addi{}", data_size.write_string()),
|
||||
Self::Andi => "andi".to_string(),
|
||||
Self::Slli => "slli".to_string(),
|
||||
Self::Srli => "srli".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,12 +194,32 @@ impl WriteString for SType {
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteString for BType {
|
||||
fn write_string(&self) -> String {
|
||||
match self {
|
||||
Self::Beq => "beq".to_string(),
|
||||
Self::Bne => "bne".to_string(),
|
||||
Self::Blt(is_signed) => format!("blt{}", if *is_signed { "" } else { "u" }),
|
||||
Self::Bge(is_signed) => format!("bge{}", if *is_signed { "" } else { "u" }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::Neg { data_size, rs, rd } => format!(
|
||||
"neg{}\t{},{}",
|
||||
data_size.write_string(),
|
||||
rd.write_string(),
|
||||
rs.write_string()
|
||||
),
|
||||
Self::SextW { rs, rd } => {
|
||||
format!("sext.w\t{},{}", rd.write_string(), rs.write_string())
|
||||
}
|
||||
Self::Seqz { rs, rd } => format!("seqz\t{},{}", rd.write_string(), rs.write_string()),
|
||||
Self::J { offset } => format!("j\t{}", offset.0),
|
||||
Self::Jr { rs } => format!("jr\t{}", rs.write_string()),
|
||||
Self::Ret => "ret".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user