mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-15 23:18: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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,512 @@
|
||||
use crate::asm::Asm;
|
||||
use crate::asm;
|
||||
use crate::ir;
|
||||
use crate::ir::HasDtype;
|
||||
use crate::Translate;
|
||||
|
||||
use core::ops::Deref;
|
||||
use std::collections::HashMap;
|
||||
|
||||
struct StackInfo {
|
||||
/// Function name for generating the block labels
|
||||
name: String,
|
||||
/// Minimum stack size required to execute function
|
||||
stack_size: usize,
|
||||
/// Stack offsets of registers
|
||||
stack_offsets_registers: HashMap<ir::RegisterId, usize>,
|
||||
/// Stack offset for previous `ra` value
|
||||
stack_offset_ra: usize,
|
||||
/// Stack offset for previous `s0` value
|
||||
stack_offset_s0: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Asmgen {}
|
||||
|
||||
impl Translate<ir::TranslationUnit> for Asmgen {
|
||||
type Target = Asm;
|
||||
type Target = asm::Asm;
|
||||
type Error = ();
|
||||
|
||||
fn translate(&mut self, _source: &ir::TranslationUnit) -> Result<Self::Target, Self::Error> {
|
||||
todo!()
|
||||
fn translate(&mut self, source: &ir::TranslationUnit) -> Result<Self::Target, Self::Error> {
|
||||
let mut functions = Vec::new();
|
||||
let mut _variables = Vec::new();
|
||||
for (name, decl) in &source.decls {
|
||||
match decl {
|
||||
ir::Declaration::Variable { .. } => todo!(),
|
||||
ir::Declaration::Function {
|
||||
signature,
|
||||
definition,
|
||||
} => {
|
||||
let definition = definition
|
||||
.as_ref()
|
||||
.expect("function must have its definition");
|
||||
let function = self.translate_function(
|
||||
name.clone(),
|
||||
signature,
|
||||
definition,
|
||||
&source.structs,
|
||||
)?;
|
||||
functions.push(function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(asm::Asm {
|
||||
unit: asm::TranslationUnit {
|
||||
functions,
|
||||
variables: _variables,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Asmgen {
|
||||
const STACK_ALIGNMENT: usize = 16;
|
||||
const SIZE_OF_S_ZERO: usize = 8;
|
||||
const SIZE_OF_RETURN_ADDRESS: usize = 8;
|
||||
|
||||
fn translate_function(
|
||||
&self,
|
||||
name: String,
|
||||
_signature: &ir::FunctionSignature,
|
||||
definition: &ir::FunctionDefinition,
|
||||
structs: &HashMap<String, Option<ir::Dtype>>,
|
||||
) -> Result<asm::Section<asm::Function>, ()> {
|
||||
// TODO: need to map function parameters and memory address
|
||||
// Map `RegisterId` and its memory address. The memory address is represented
|
||||
// by a distance from the stack pointer.
|
||||
let mut stack_offsets_registers = HashMap::new();
|
||||
|
||||
// Allocate memory for return address and s0(frame pointer).
|
||||
let mut required_size = Self::SIZE_OF_RETURN_ADDRESS + Self::SIZE_OF_S_ZERO;
|
||||
|
||||
// Allocate memory for local variables.
|
||||
for (i, alloc) in definition.allocations.iter().enumerate() {
|
||||
required_size += alloc.deref().size_align_of(structs).unwrap().0;
|
||||
let rid = ir::RegisterId::local(i);
|
||||
assert_eq!(stack_offsets_registers.insert(rid, required_size), None);
|
||||
}
|
||||
|
||||
// Allocate memory for saved registers.
|
||||
let required_size = definition
|
||||
.blocks
|
||||
.iter()
|
||||
.fold(required_size, |size, (bid, block)| {
|
||||
block
|
||||
.instructions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(size, |size, (i, instr)| {
|
||||
let size = size + instr.deref().dtype().size_align_of(structs).unwrap().0;
|
||||
let rid = ir::RegisterId::temp(*bid, i);
|
||||
assert_eq!(stack_offsets_registers.insert(rid, size), None);
|
||||
size
|
||||
})
|
||||
});
|
||||
|
||||
// TODO: Allocate memory for next function args
|
||||
|
||||
let stack_size = ((required_size - 1) / Self::STACK_ALIGNMENT + 1) * Self::STACK_ALIGNMENT;
|
||||
let stack_offset_ra = stack_size - Self::SIZE_OF_RETURN_ADDRESS;
|
||||
let stack_offset_s0 = stack_offset_ra - Self::SIZE_OF_S_ZERO;
|
||||
let stack_info = StackInfo {
|
||||
name,
|
||||
stack_size,
|
||||
stack_offsets_registers,
|
||||
stack_offset_ra,
|
||||
stack_offset_s0,
|
||||
};
|
||||
|
||||
let mut function_section = asm::Section::new(Vec::new(), asm::Function::new(Vec::new()));
|
||||
|
||||
self.translate_section_header(&stack_info, &mut function_section)?;
|
||||
self.translate_init_block(&stack_info, &mut function_section)?;
|
||||
definition
|
||||
.blocks
|
||||
.iter()
|
||||
.map(|(bid, block)| {
|
||||
self.translate_block(&stack_info, *bid, block, &mut function_section)
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
self.translate_final_block(&stack_info, &mut function_section)?;
|
||||
|
||||
Ok(function_section)
|
||||
}
|
||||
|
||||
fn translate_section_header(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
function_section: &mut asm::Section<asm::Function>,
|
||||
) -> Result<(), ()> {
|
||||
// .globl func_name
|
||||
function_section
|
||||
.header
|
||||
.push(asm::Directive::Globl(asm::Label(stack_info.name.clone())));
|
||||
// .type func_name, @function
|
||||
function_section.header.push(asm::Directive::Type(
|
||||
asm::Label(stack_info.name.clone()),
|
||||
asm::SymbolType::Function,
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn translate_init_block(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
function_section: &mut asm::Section<asm::Function>,
|
||||
) -> Result<(), ()> {
|
||||
let mut instrs = Vec::new();
|
||||
// addi sp, sp, -stack_size
|
||||
instrs.push(asm::Instruction::IType {
|
||||
instr: asm::IType::ADDI,
|
||||
rd: asm::Register::Sp,
|
||||
rs1: asm::Register::Sp,
|
||||
imm: -(stack_info.stack_size as isize),
|
||||
});
|
||||
// sd ra, stack_offset_ra(sp)
|
||||
instrs.push(asm::Instruction::SType {
|
||||
instr: asm::SType::SD,
|
||||
rs2: asm::Register::Ra,
|
||||
rs1: asm::Register::Sp,
|
||||
imm: stack_info.stack_offset_ra as isize,
|
||||
});
|
||||
// sd s0, stack_offset_s0(sp)
|
||||
instrs.push(asm::Instruction::SType {
|
||||
instr: asm::SType::SD,
|
||||
rs2: asm::Register::S0,
|
||||
rs1: asm::Register::Sp,
|
||||
imm: stack_info.stack_offset_s0 as isize,
|
||||
});
|
||||
// addi s0, sp, stack_size
|
||||
instrs.push(asm::Instruction::IType {
|
||||
instr: asm::IType::ADDI,
|
||||
rd: asm::Register::S0,
|
||||
rs1: asm::Register::Sp,
|
||||
imm: stack_info.stack_size as isize,
|
||||
});
|
||||
|
||||
let enter_block = asm::Block::new(Some(asm::Label(stack_info.name.clone())), instrs);
|
||||
function_section.body.blocks.push(enter_block);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn translate_final_block(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
function_section: &mut asm::Section<asm::Function>,
|
||||
) -> Result<(), ()> {
|
||||
let mut instrs = Vec::new();
|
||||
// ld ra, stack_offset_ra(sp)
|
||||
instrs.push(asm::Instruction::IType {
|
||||
instr: asm::IType::LD,
|
||||
rd: asm::Register::Ra,
|
||||
rs1: asm::Register::Sp,
|
||||
imm: stack_info.stack_offset_ra as isize,
|
||||
});
|
||||
// ld s0, stack_offset_s0(sp)
|
||||
instrs.push(asm::Instruction::IType {
|
||||
instr: asm::IType::LD,
|
||||
rd: asm::Register::S0,
|
||||
rs1: asm::Register::Sp,
|
||||
imm: stack_info.stack_offset_s0 as isize,
|
||||
});
|
||||
// addi sp, sp, stack_size
|
||||
instrs.push(asm::Instruction::IType {
|
||||
instr: asm::IType::ADDI,
|
||||
rd: asm::Register::Sp,
|
||||
rs1: asm::Register::Sp,
|
||||
imm: stack_info.stack_size as isize,
|
||||
});
|
||||
// jr ra
|
||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Jr {
|
||||
rs: asm::Register::Ra,
|
||||
}));
|
||||
|
||||
let exit_label = asm::Label(format!(".{}_END", stack_info.name));
|
||||
let exit_block = asm::Block::new(Some(exit_label), instrs);
|
||||
function_section.body.blocks.push(exit_block);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn translate_block(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
bid: ir::BlockId,
|
||||
block: &ir::Block,
|
||||
function_section: &mut asm::Section<asm::Function>,
|
||||
) -> Result<(), ()> {
|
||||
let instrs_for_phinodes = block
|
||||
.phinodes
|
||||
.iter()
|
||||
.map(|p| self.translate_phinode(stack_info, p.deref()))
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
let instrs_for_instructions = block
|
||||
.instructions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(iid, instr)| {
|
||||
let dest_rid = ir::RegisterId::temp(bid, iid);
|
||||
self.translate_instruction(stack_info, dest_rid, instr.deref())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
let instrs_for_block_exit = self.translate_block_exit(stack_info, &block.exit)?;
|
||||
|
||||
let instrs = vec![
|
||||
instrs_for_phinodes,
|
||||
instrs_for_instructions,
|
||||
instrs_for_block_exit,
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
let label = asm::Label::new(&stack_info.name, bid);
|
||||
let block = asm::Block::new(Some(label), instrs);
|
||||
function_section.body.blocks.push(block);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn translate_phinode(
|
||||
&self,
|
||||
_stack_info: &StackInfo,
|
||||
_phinode: &ir::Dtype,
|
||||
) -> Result<Vec<asm::Instruction>, ()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn translate_instruction(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
dest_rid: ir::RegisterId,
|
||||
instruction: &ir::Instruction,
|
||||
) -> Result<Vec<asm::Instruction>, ()> {
|
||||
let (mut instrs, rd) = match instruction {
|
||||
ir::Instruction::Store { ptr, value } => {
|
||||
let instrs = self.translate_store(stack_info, ptr, value)?;
|
||||
return Ok(instrs);
|
||||
}
|
||||
ir::Instruction::Load { ptr } => self.translate_load(stack_info, ptr)?,
|
||||
ir::Instruction::Call { callee, args, .. } => self.translate_call(callee, args)?,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
// Store `rd` into memory
|
||||
let dtype = instruction.dtype();
|
||||
let dist_s0_to_ptr = stack_info
|
||||
.stack_offsets_registers
|
||||
.get(&dest_rid)
|
||||
.expect("address matched with `rid` must exist");
|
||||
let store_instr = asm::SType::store(dtype);
|
||||
|
||||
instrs.push(asm::Instruction::SType {
|
||||
instr: store_instr,
|
||||
rs2: rd,
|
||||
rs1: asm::Register::S0,
|
||||
imm: -(*dist_s0_to_ptr as isize),
|
||||
});
|
||||
|
||||
Ok(instrs)
|
||||
}
|
||||
|
||||
fn translate_store(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
ptr: &ir::Operand,
|
||||
value: &ir::Operand,
|
||||
) -> Result<Vec<asm::Instruction>, ()> {
|
||||
let (instrs_for_value, reg_of_value) = self.translate_operand(stack_info, value)?;
|
||||
|
||||
let mut instrs = Vec::new();
|
||||
let (rid, dtype) = ptr.get_register().expect("`ptr` must be register");
|
||||
let dist_s0_to_ptr = stack_info
|
||||
.stack_offsets_registers
|
||||
.get(rid)
|
||||
.expect("address matched with `rid` must exist");
|
||||
let inner_dtype = dtype
|
||||
.get_pointer_inner()
|
||||
.expect("`dtype` must be pointer type");
|
||||
let store_instr = asm::SType::store(inner_dtype.clone());
|
||||
|
||||
instrs.push(asm::Instruction::SType {
|
||||
instr: store_instr,
|
||||
rs2: reg_of_value,
|
||||
rs1: asm::Register::S0,
|
||||
imm: -(*dist_s0_to_ptr as isize),
|
||||
});
|
||||
|
||||
let instrs = vec![instrs_for_value, instrs]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
Ok(instrs)
|
||||
}
|
||||
|
||||
fn translate_load(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
ptr: &ir::Operand,
|
||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
||||
let mut instrs = Vec::new();
|
||||
|
||||
let (rid, dtype) = ptr.get_register().expect("`ptr` must be register");
|
||||
let dist_s0_to_ptr = stack_info
|
||||
.stack_offsets_registers
|
||||
.get(rid)
|
||||
.expect("address matched with `rid` must exist");
|
||||
let inner_dtype = dtype
|
||||
.get_pointer_inner()
|
||||
.expect("`dtype` must be pointer type");
|
||||
let load_instr = asm::IType::load(inner_dtype.clone());
|
||||
|
||||
// TODO: select register which is not occupied
|
||||
let rd = asm::Register::A5;
|
||||
instrs.push(asm::Instruction::IType {
|
||||
instr: load_instr,
|
||||
rd,
|
||||
rs1: asm::Register::S0,
|
||||
imm: -(*dist_s0_to_ptr as isize),
|
||||
});
|
||||
|
||||
Ok((instrs, rd))
|
||||
}
|
||||
|
||||
fn translate_call(
|
||||
&self,
|
||||
callee: &ir::Operand,
|
||||
_args: &[ir::Operand],
|
||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
||||
let mut instrs = Vec::new();
|
||||
|
||||
// TODO: translate pass the args
|
||||
|
||||
match callee {
|
||||
ir::Operand::Constant(constant) => {
|
||||
if let ir::Constant::GlobalVariable { name, dtype } = constant {
|
||||
assert!(dtype.get_function_inner().is_some());
|
||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Call {
|
||||
offset: asm::Label(name.clone()),
|
||||
}));
|
||||
} else {
|
||||
panic!("`callee` must be `GlobalVariable`")
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
// TODO: select register which is not occupied
|
||||
let rd = asm::Register::A5;
|
||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Mv {
|
||||
rs: asm::Register::A0,
|
||||
rd,
|
||||
}));
|
||||
|
||||
Ok((instrs, rd))
|
||||
}
|
||||
|
||||
fn translate_block_exit(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
block_exit: &ir::BlockExit,
|
||||
) -> Result<Vec<asm::Instruction>, ()> {
|
||||
match block_exit {
|
||||
ir::BlockExit::Return { value } => self.translate_return(stack_info, value),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_return(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
value: &ir::Operand,
|
||||
) -> Result<Vec<asm::Instruction>, ()> {
|
||||
let (instrs_for_value, rs) = self.translate_operand(stack_info, value)?;
|
||||
|
||||
let mut instrs = Vec::new();
|
||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Mv {
|
||||
rs,
|
||||
rd: asm::Register::A0,
|
||||
}));
|
||||
// Jump to exit block
|
||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::J {
|
||||
offset: asm::Label(format!(".{}_END", stack_info.name)),
|
||||
}));
|
||||
|
||||
let instrs = vec![instrs_for_value, instrs]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
Ok(instrs)
|
||||
}
|
||||
|
||||
fn translate_operand(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
operand: &ir::Operand,
|
||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
||||
match operand {
|
||||
ir::Operand::Constant(constant) => self.translate_constant(stack_info, constant),
|
||||
ir::Operand::Register { rid, dtype } => self.translate_register(stack_info, rid, dtype),
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_constant(
|
||||
&self,
|
||||
_stack_info: &StackInfo,
|
||||
constant: &ir::Constant,
|
||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
||||
let mut instrs = Vec::new();
|
||||
|
||||
match constant {
|
||||
// TODO: consider width and signed option in the future
|
||||
ir::Constant::Int { value, .. } => {
|
||||
// TODO: select register which is not occupied
|
||||
let rd = asm::Register::A5;
|
||||
instrs.push(asm::Instruction::Pseudo(asm::Pseudo::Li {
|
||||
rd,
|
||||
imm: *value as isize,
|
||||
}));
|
||||
Ok((instrs, rd))
|
||||
}
|
||||
ir::Constant::Undef { .. } => {
|
||||
// TODO: select register which is not occupied
|
||||
Ok((instrs, asm::Register::A5))
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_register(
|
||||
&self,
|
||||
stack_info: &StackInfo,
|
||||
rid: &ir::RegisterId,
|
||||
dtype: &ir::Dtype,
|
||||
) -> Result<(Vec<asm::Instruction>, asm::Register), ()> {
|
||||
let mut instrs = Vec::new();
|
||||
|
||||
let dist_s0_to_ptr = stack_info
|
||||
.stack_offsets_registers
|
||||
.get(rid)
|
||||
.expect("address matched with `rid` must exist");
|
||||
let load_instr = asm::IType::load(dtype.clone());
|
||||
|
||||
// TODO: select register which is not occupied
|
||||
let rd = asm::Register::A5;
|
||||
instrs.push(asm::Instruction::IType {
|
||||
instr: load_instr,
|
||||
rd,
|
||||
rs1: asm::Register::S0,
|
||||
imm: -(*dist_s0_to_ptr as isize),
|
||||
});
|
||||
|
||||
Ok((instrs, rd))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,6 +573,15 @@ impl Dtype {
|
||||
);
|
||||
|
||||
let fields = fields.unwrap();
|
||||
if fields.is_empty() {
|
||||
return Ok(Self::Struct {
|
||||
name,
|
||||
fields: Some(fields),
|
||||
is_const,
|
||||
size_align_offsets: Some((0, 1, Vec::new())),
|
||||
});
|
||||
}
|
||||
|
||||
let align_of = fields
|
||||
.iter()
|
||||
.map(|f| f.deref().size_align_of(structs))
|
||||
@@ -709,6 +718,7 @@ impl Dtype {
|
||||
Self::Unit { .. } => todo!(),
|
||||
Self::Int { .. } => true,
|
||||
Self::Float { .. } => true,
|
||||
Self::Pointer { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,13 +644,28 @@ mod calculator {
|
||||
|
||||
calculate_float_binary_operator_expression(op, lhs, rhs, lhs_w)
|
||||
}
|
||||
(Value::Pointer { bid, .. }, Value::Pointer { bid: other_bid, .. }) => match op {
|
||||
(
|
||||
Value::Pointer { bid, offset, .. },
|
||||
Value::Pointer {
|
||||
bid: other_bid,
|
||||
offset: other_offset,
|
||||
..
|
||||
},
|
||||
) => match op {
|
||||
ast::BinaryOperator::Equals => {
|
||||
let result = if bid == other_bid { 1 } else { 0 };
|
||||
let result = if bid == other_bid && offset == other_offset {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Ok(Value::int(result, 1, false))
|
||||
}
|
||||
ast::BinaryOperator::NotEquals => {
|
||||
let result = if bid != other_bid { 1 } else { 0 };
|
||||
let result = if !(bid == other_bid && offset == other_offset) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Ok(Value::int(result, 1, false))
|
||||
}
|
||||
_ => todo!(
|
||||
|
||||
Reference in New Issue
Block a user