Update skeleton

This commit is contained in:
Jeehoon Kang
2020-05-18 18:57:21 +09:00
parent 773eeda067
commit e81247ba54
58 changed files with 49447 additions and 16 deletions

View File

@@ -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)
}
}

View File

@@ -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),
}
}
}

View File

@@ -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))
}
}

View File

@@ -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,
}
}

View File

@@ -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!(