slices (offsets now real in backend)

This commit is contained in:
2024-12-07 23:56:00 -05:00
parent 0e0dbd647d
commit 606cb30c6b
14 changed files with 305 additions and 215 deletions

View File

@@ -4,12 +4,44 @@ use crate::{
compiler::{arch::riscv64::Reg, create_program, Addr},
ir::{
arch::riscv64::{RV64Instruction as AI, RegRef},
IRLInstruction as IRI, IRLProgram,
IRLInstruction as IRI, IRLProgram, Len, Size, Symbol, VarID,
},
};
use super::{LinkerInstruction as LI, *};
fn align(s: &Size) -> i32 {
(*s as i32 - 1).div_euclid(8) + 1
}
fn mov_mem(
v: &mut Vec<LI>,
src: Reg,
src_offset: i32,
dest: Reg,
dest_offset: i32,
temp: Reg,
mut len: Len,
) {
let mut off = 0;
while len >= 8 {
v.extend([
LI::Ld {
dest: temp,
offset: src_offset + off,
base: src,
},
LI::Sd {
src: temp,
offset: dest_offset + off,
base: dest,
},
]);
len -= 8;
off += 8;
}
}
pub fn compile(program: IRLProgram) -> (Vec<u8>, Option<Addr>) {
let mut fns = Vec::new();
let mut data = Vec::new();
@@ -20,26 +52,36 @@ pub fn compile(program: IRLProgram) -> (Vec<u8>, Option<Addr>) {
let mut v = Vec::new();
let mut stack = HashMap::new();
let mut stack_len = 0;
if !f.stack.is_empty() || !f.args.is_empty() {
let has_stack = !f.stack.is_empty() || !f.args.is_empty() || f.makes_call;
let mut stack_ra = None;
if has_stack {
if f.makes_call {
// return addr
stack_ra = Some(stack_len);
stack_len += 8;
}
for (id, s) in &f.stack {
stack.insert(id, stack_len);
stack_len += *s as i32;
stack_len += align(s);
}
for (id, s) in f.args.iter().rev() {
stack.insert(id, stack_len);
stack_len += *s as i32;
stack_len += align(s);
}
v.push(LI::Addi {
dest: sp,
src: sp,
imm: -stack_len,
});
if let Some(stack_ra) = stack_ra {
v.push(LI::Sd { src: ra, offset: stack_ra, base: sp });
}
}
for i in &f.instructions {
match i {
IRI::Mv { dest, src } => todo!(),
IRI::Ref { dest, src } => todo!(),
IRI::LoadAddr { dest, src } => {
IRI::LoadAddr { dest, offset, src } => {
v.extend([
LI::La {
dest: t0,
@@ -47,36 +89,38 @@ pub fn compile(program: IRLProgram) -> (Vec<u8>, Option<Addr>) {
},
LI::Sd {
src: t0,
offset: stack[dest],
offset: stack[dest] + *offset as i32,
base: sp,
},
]);
}
IRI::LoadData {
dest,
offset,
src,
len,
} => {
v.push(LI::La {
dest: t0,
src: *src,
});
mov_mem(&mut v, t0, 0, sp, stack[dest] + *offset as i32, t1, *len);
}
IRI::Call { dest, f, args } => {
let mut offset = 0;
for (arg, s) in args {
offset -= *s as i32;
v.extend([
LI::Ld {
dest: t0,
offset: stack[arg],
base: sp,
},
LI::Sd {
src: t0,
offset,
base: sp,
},
]);
let bs = align(s);
offset -= bs;
mov_mem(&mut v, sp, stack[arg], sp, offset, t0, bs as Len);
}
v.push(LI::Call(*f));
}
IRI::AsmBlock { args, instructions } => {
for (reg, var) in args {
v.push(LI::Ld {
v.push(LI::Addi {
dest: *reg,
offset: stack[var],
base: sp,
imm: stack[var],
src: sp,
});
}
fn r(rr: RegRef) -> Reg {
@@ -105,9 +149,17 @@ pub fn compile(program: IRLProgram) -> (Vec<u8>, Option<Addr>) {
IRI::Ret { src } => todo!(),
}
}
if *sym != program.entry() {
v.push(LI::Ret);
if has_stack {
if let Some(stack_ra) = stack_ra {
v.push(LI::Ld { dest: ra, offset: stack_ra, base: sp });
}
v.push(LI::Addi {
dest: sp,
src: sp,
imm: stack_len,
});
}
v.push(LI::Ret);
fns.push((v, *sym));
}
create_program(fns, data, Some(program.entry()))

View File

@@ -9,6 +9,9 @@ pub struct FnID(pub usize);
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct DataID(pub usize);
pub type Size = u32;
pub type Len = u32;
impl Debug for VarID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "var{}", self.0)

View File

@@ -7,8 +7,9 @@ use std::collections::HashMap;
pub struct IRLFunction {
pub name: String,
pub instructions: Vec<IRLInstruction>,
pub stack: HashMap<VarID, usize>,
pub args: Vec<(VarID, usize)>,
pub stack: HashMap<VarID, Size>,
pub args: Vec<(VarID, Size)>,
pub makes_call: bool,
}
#[derive(Debug)]
@@ -23,12 +24,19 @@ pub enum IRLInstruction {
},
LoadAddr {
dest: VarID,
offset: Size,
src: Symbol,
},
LoadData {
dest: VarID,
offset: Size,
src: Symbol,
len: Len,
},
Call {
dest: VarID,
f: Symbol,
args: Vec<(VarID, usize)>,
args: Vec<(VarID, Size)>,
},
AsmBlock {
instructions: Vec<RV64Instruction>,

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::ir::{FnID, SymbolSpace};
use super::{IRLFunction, IRLInstruction, IRUInstruction, Namespace, Symbol, VarID};
use super::{IRLFunction, IRLInstruction, IRUInstruction, Len, Namespace, Symbol, VarID};
pub struct IRLProgram {
sym_space: SymbolSpace,
@@ -14,10 +14,9 @@ pub struct IRLProgram {
impl IRLProgram {
pub fn create(ns: &Namespace) -> Option<Self> {
let mut start = None;
for (i, f) in ns.fns.iter().enumerate() {
let f = f.as_ref()?;
if f.name == "start" {
start = Some(FnID(i));
for (i, f) in ns.iter_fns() {
if f?.name == "start" {
start = Some(i);
}
}
let start = start?;
@@ -25,78 +24,121 @@ impl IRLProgram {
let entry = builder.func(&start);
while let Some((sym, i)) = builder.pop_fn() {
let f = ns.fns[i.0].as_ref().unwrap();
let mut instructions = Vec::new();
let mut instrs = Vec::new();
let mut stack = HashMap::new();
let mut alloc_stack = |i: &VarID| {
if !stack.contains_key(i) {
stack.insert(*i, 8);
}
let mut makes_call = false;
let mut alloc_stack = |i: &VarID| -> bool {
let size = *stack
.entry(*i)
.or_insert(ns.size_of_var(i).expect("unsized type"));
size == 0
};
for i in &f.instructions {
instructions.push(match i {
match i {
IRUInstruction::Mv { dest, src } => {
alloc_stack(dest);
IRLInstruction::Mv {
if alloc_stack(dest) {
continue;
}
instrs.push(IRLInstruction::Mv {
dest: *dest,
src: *src,
}
});
}
IRUInstruction::Ref { dest, src } => {
alloc_stack(dest);
IRLInstruction::Ref {
if alloc_stack(dest) {
continue;
}
instrs.push(IRLInstruction::Ref {
dest: *dest,
src: *src,
}
});
}
IRUInstruction::LoadData { dest, src } => {
alloc_stack(dest);
let addr = builder.ro_data(src, &ns.data[src.0]);
IRLInstruction::LoadAddr {
dest: *dest,
src: addr,
if alloc_stack(dest) {
continue;
}
let data = &ns.data[src.0];
let sym = builder.ro_data(src, data);
instrs.push(IRLInstruction::LoadData {
dest: *dest,
offset: 0,
len: data.len() as Len,
src: sym,
});
}
IRUInstruction::LoadSlice { dest, src, len } => {
if alloc_stack(dest) {
continue;
}
let sym = builder.ro_data(src, &ns.data[src.0]);
instrs.push(IRLInstruction::LoadAddr {
dest: *dest,
offset: 0,
src: sym,
});
let sym = builder.anon_ro_data(&(*len as u64).to_le_bytes());
instrs.push(IRLInstruction::LoadData {
dest: *dest,
offset: 8,
len: 8,
src: sym,
});
}
IRUInstruction::LoadFn { dest, src } => {
alloc_stack(dest);
let sym = builder.func(src);
IRLInstruction::LoadAddr {
dest: *dest,
src: sym,
if alloc_stack(dest) {
continue;
}
let sym = builder.func(src);
instrs.push(IRLInstruction::LoadAddr {
dest: *dest,
offset: 0,
src: sym,
});
}
IRUInstruction::Call { dest, f, args } => {
alloc_stack(dest);
makes_call = true;
let fid = &ns.fn_map[f];
let sym = builder.func(fid);
IRLInstruction::Call {
instrs.push(IRLInstruction::Call {
dest: *dest,
f: sym,
args: args.iter().map(|a| (*a, 8)).collect(),
}
args: args
.iter()
.map(|a| (*a, ns.size_of_var(a).expect("unsized type")))
.collect(),
});
}
IRUInstruction::AsmBlock { instructions, args } => IRLInstruction::AsmBlock {
instructions: instructions.clone(),
args: args.clone(),
},
IRUInstruction::Ret { src } => IRLInstruction::Ret { src: *src },
});
IRUInstruction::AsmBlock { instructions, args } => {
instrs.push(IRLInstruction::AsmBlock {
instructions: instructions.clone(),
args: args.clone(),
})
}
IRUInstruction::Ret { src } => instrs.push(IRLInstruction::Ret { src: *src }),
};
}
builder.write_fn(
sym,
IRLFunction {
name: f.name.clone(),
instructions,
args: f.args.iter().map(|a| (*a, 8)).collect(),
instructions: instrs,
makes_call,
args: f
.args
.iter()
.map(|a| (*a, ns.size_of_var(a).expect("unsized type")))
.collect(),
stack,
},
);
}
let sym_space = builder.finish().expect("we failed the mission");
println!("fns:");
for (a, f) in sym_space.fns() {
println!(" {:?}: {}", a, f.name);
}
println!("datas: {}", sym_space.ro_data().len());
// println!("fns:");
// for (a, f) in sym_space.fns() {
// println!(" {:?}: {}", a, f.name);
// }
// println!("datas: {}", sym_space.ro_data().len());
Some(Self { sym_space, entry })
}

View File

@@ -56,13 +56,17 @@ impl SymbolSpaceBuilder {
pub fn pop_fn(&mut self) -> Option<(WritableSymbol, FnID)> {
self.unwritten_fns.pop()
}
pub fn ro_data(&mut self, id: &DataID, data: &Vec<u8>) -> Symbol {
pub fn anon_ro_data(&mut self, data: &[u8]) -> Symbol {
let sym = self.reserve();
self.write_ro_data(sym, data.to_vec())
}
pub fn ro_data(&mut self, id: &DataID, data: &[u8]) -> Symbol {
match self.data_map.get(id) {
Some(s) => *s,
None => {
let sym = self.reserve();
self.data_map.insert(*id, *sym);
self.write_ro_data(sym, data.clone())
self.write_ro_data(sym, data.to_vec())
}
}
}

View File

@@ -1,8 +1,8 @@
use crate::compiler::arch::riscv64::Reg;
use std::fmt::Write;
use super::{arch::riscv64::RV64Instruction, DataID, FnID, VarID};
use super::{arch::riscv64::RV64Instruction, DataID, FnID, Len, VarID};
use crate::{compiler::arch::riscv64::Reg, util::Padder};
#[derive(Debug)]
pub struct IRUFunction {
pub name: String,
pub args: Vec<VarID>,
@@ -22,6 +22,11 @@ pub enum IRUInstruction {
dest: VarID,
src: DataID,
},
LoadSlice {
dest: VarID,
src: DataID,
len: Len,
},
LoadFn {
dest: VarID,
src: FnID,
@@ -70,6 +75,7 @@ impl std::fmt::Debug for IRUInstruction {
Self::Ref { dest, src } => write!(f, "{dest:?} <- &{src:?}"),
Self::LoadData { dest, src } => write!(f, "{dest:?} <- {src:?}"),
Self::LoadFn { dest, src } => write!(f, "{dest:?} <- {src:?}"),
Self::LoadSlice { dest, src, len } => write!(f, "{dest:?} <- &[{src:?}; {len}]"),
Self::Call {
dest,
f: func,
@@ -80,3 +86,21 @@ impl std::fmt::Debug for IRUInstruction {
}
}
}
impl std::fmt::Debug for IRUFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{:?}", &self.name, self.args)?;
if !self.instructions.is_empty() {
f.write_str("{\n ")?;
let mut padder = Padder::new(f);
for i in &self.instructions {
// they don't expose wrap_buf :grief:
padder.write_str(&format!("{i:?};\n"))?;
}
f.write_char('}')?;
} else {
f.write_str("{}")?;
}
Ok(())
}
}

View File

@@ -19,7 +19,7 @@ pub struct Namespace {
impl Namespace {
pub fn new() -> Self {
let mut s = Self {
Self {
fn_defs: Vec::new(),
var_defs: Vec::new(),
type_defs: Vec::new(),
@@ -28,11 +28,7 @@ impl Namespace {
fns: Vec::new(),
temp: 0,
stack: vec![HashMap::new()],
};
for b in BuiltinType::enumerate() {
s.def_type(b.def());
}
s
}
pub fn push(&mut self) -> NamespaceGuard {
self.stack.push(HashMap::new());
@@ -76,6 +72,27 @@ impl Namespace {
self.var_defs.push(var);
VarID(i)
}
pub fn size_of_type(&self, ty: &Type) -> Option<Size> {
// TODO: target matters
Some(match ty {
Type::Concrete(id) => {
let def = &self.type_defs[id.0];
todo!()
}
Type::Bits(b) => *b,
Type::Generic { base, args } => todo!(),
Type::Fn { args, ret } => todo!(),
Type::Ref(_) => 64,
Type::Array(ty, len) => self.size_of_type(ty)? * len,
Type::Slice(_) => 128,
Type::Infer => return None,
Type::Error => return None,
Type::Unit => 0,
})
}
pub fn size_of_var(&self, var: &VarID) -> Option<Size> {
self.size_of_type(&self.var_defs[var.0].ty)
}
pub fn temp_var(&mut self, origin: FileSpan, ty: Type) -> VarID {
let v = self.def_var(VarDef {
name: format!("temp{}", self.temp),
@@ -151,7 +168,9 @@ impl Namespace {
Type::Error => str += "{error}",
Type::Infer => str += "{inferred}",
Type::Bits(size) => str += &format!("b{}", size),
Type::Array(t) => str += &format!("[{}]", self.type_name(t)),
Type::Array(t, len) => str += &format!("[{}; {len}]", self.type_name(t)),
Type::Unit => str += "()",
Type::Slice(t) => str += &format!("&[{}]", self.type_name(t)),
}
str
}
@@ -167,6 +186,16 @@ impl Namespace {
pub fn write_fn(&mut self, id: FnID, f: IRUFunction) {
self.fns[id.0] = Some(f);
}
pub fn iter_vars(&self) -> impl Iterator<Item = (VarID, &VarDef)> {
(0..self.var_defs.len())
.map(|i| VarID(i))
.zip(self.var_defs.iter())
}
pub fn iter_fns(&self) -> impl Iterator<Item = (FnID, Option<&IRUFunction>)> {
(0..self.fns.len())
.map(|i| FnID(i))
.zip(self.fns.iter().map(|f| f.as_ref()))
}
}
pub struct NamespaceGuard<'a>(&'a mut Namespace);

View File

@@ -1,4 +1,4 @@
use super::{Origin, TypeDef, TypeID};
use super::{Len, TypeID};
#[derive(Clone)]
pub enum Type {
@@ -7,47 +7,21 @@ pub enum Type {
Generic { base: TypeID, args: Vec<Type> },
Fn { args: Vec<Type>, ret: Box<Type> },
Ref(Box<Type>),
Array(Box<Type>),
Slice(Box<Type>),
Array(Box<Type>, Len),
Infer,
Error,
Unit,
}
impl Type {
pub fn rf(self) -> Self {
Self::Ref(Box::new(self))
}
pub fn arr(self) -> Self {
Self::Array(Box::new(self))
pub fn arr(self, len: Len) -> Self {
Self::Array(Box::new(self), len)
}
pub fn slice(self) -> Self {
Self::Slice(Box::new(self))
}
}
#[repr(usize)]
#[derive(Debug, Clone, Copy)]
pub enum BuiltinType {
Unit,
}
impl BuiltinType {
pub fn enumerate() -> &'static [Self; 1] {
&[Self::Unit]
}
pub fn def(&self) -> TypeDef {
match self {
BuiltinType::Unit => TypeDef {
name: "()".to_string(),
args: 0,
origin: Origin::Builtin,
},
}
}
pub fn id(&self) -> TypeID {
TypeID::builtin(self)
}
}
impl TypeID {
pub fn builtin(ty: &BuiltinType) -> Self {
Self(*ty as usize)
}
}

View File

@@ -37,10 +37,13 @@ fn run_file(file: &str, gdb: bool) {
let mut namespace = Namespace::new();
module.lower(&mut namespace.push(), &mut ctx.output);
if ctx.output.errs.is_empty() {
println!("vars:");
for def in &namespace.var_defs {
println!(" {}: {}", def.name, namespace.type_name(&def.ty));
}
// println!("vars:");
// for (id, def) in namespace.iter_vars() {
// println!(" {id:?} = {}: {}", def.name, namespace.type_name(&def.ty));
// }
// for (id, f) in namespace.iter_fns() {
// println!("{id:?} = {:#?}", f.unwrap());
// }
let program = IRLProgram::create(&namespace);
let bin = compiler::compile(program.expect("morir"));
println!("compiled");

View File

@@ -1,6 +1,6 @@
use crate::ir::{FileSpan, NamespaceGuard, Origin, Type, VarDef};
use super::{Node, ParserMsg, ParserOutput, PType, PVarDef};
use super::{Node, PType, PVarDef, ParserMsg, ParserOutput};
impl Node<PVarDef> {
pub fn lower(
@@ -54,8 +54,16 @@ impl PType {
if let Ok(num) = self.name.parse::<u32>() {
Type::Bits(num)
} else {
output.err(ParserMsg::from_span(span, "Type not found".to_string()));
Type::Error
match self.name.as_str() {
"slice" => {
let inner = self.args[0].lower(namespace, output);
Type::Slice(Box::new(inner))
}
_ => {
output.err(ParserMsg::from_span(span, "Type not found".to_string()));
Type::Error
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
use super::{func::FnLowerCtx, FnLowerable, PExpr, UnaryOp};
use crate::ir::{IRUInstruction, Type, VarID};
use crate::ir::{IRUInstruction, Size, Type, VarID};
impl FnLowerable for PExpr {
type Output = VarID;
@@ -7,13 +7,15 @@ impl FnLowerable for PExpr {
Some(match self {
PExpr::Lit(l) => match l.as_ref()? {
super::PLiteral::String(s) => {
let dest = ctx.map.temp_var(l.span, Type::Bits(64).arr().rf());
let src = ctx.map.def_data(s.as_bytes().to_vec());
ctx.push(IRUInstruction::LoadData { dest, src });
let dest = ctx.map.temp_var(l.span, Type::Bits(8).slice());
let data = s.as_bytes().to_vec();
let len = data.len() as Size;
let src = ctx.map.def_data(data);
ctx.push(IRUInstruction::LoadSlice { dest, src, len });
dest
}
super::PLiteral::Char(c) => {
let dest = ctx.map.temp_var(l.span, Type::Bits(64).arr().rf());
let dest = ctx.map.temp_var(l.span, Type::Bits(8).slice());
let src = ctx.map.def_data(c.to_string().as_bytes().to_vec());
ctx.push(IRUInstruction::LoadData { dest, src });
dest
@@ -79,9 +81,15 @@ impl FnLowerable for PExpr {
let ty = match def {
Some(def) => def.ret.clone(),
None => {
ctx.err_at(e.span, format!("Expected function, found {}", ctx.map.type_name(&ctx.map.get_var(fe).ty)));
ctx.err_at(
e.span,
format!(
"Expected function, found {}",
ctx.map.type_name(&ctx.map.get_var(fe).ty)
),
);
Type::Error
},
}
};
let temp = ctx.temp(ty);
ctx.push(IRUInstruction::Call {

View File

@@ -1,8 +1,8 @@
use super::{FnLowerable, PFunction, Node, ParserMsg, ParserOutput};
use super::{FnLowerable, Node, PFunction, ParserMsg, ParserOutput};
use crate::{
ir::{
BuiltinType, FileSpan, FnDef, FnID, IRUFunction, Idents, IRUInstruction, IRInstructions,
NamespaceGuard, Origin, Type, VarDef, VarID,
FileSpan, FnDef, FnID, IRInstructions, IRUFunction, IRUInstruction, Idents, NamespaceGuard,
Origin, Type, VarDef, VarID,
},
parser,
};
@@ -46,7 +46,7 @@ impl PFunction {
.collect();
let ret = match &header.ret {
Some(ty) => ty.lower(map, output),
None => Type::Concrete(BuiltinType::Unit.id()),
None => Type::Unit,
};
Some(map.def_fn(FnDef {
name: name.to_string(),
@@ -63,9 +63,7 @@ impl PFunction {
) -> IRUFunction {
let mut instructions = IRInstructions::new();
let def = map.get_fn(id).clone();
let args = def.args.iter().map(|a| {
map.named_var(a.clone())
}).collect();
let args = def.args.iter().map(|a| map.named_var(a.clone())).collect();
let mut ctx = FnLowerCtx {
instructions: &mut instructions,
map,
@@ -79,67 +77,6 @@ impl PFunction {
}
}
// impl Node<AsmFunction> {
// pub fn lower_header(
// &self,
// map: &mut NamespaceGuard,
// output: &mut ParserOutput,
// ) -> Option<FnIdent> {
// self.as_ref()?.lower_header(map, output)
// }
// pub fn lower_body(
// &self,
// map: &mut NamespaceGuard,
// output: &mut ParserOutput,
// ) -> Option<Function> {
// Some(self.as_ref()?.lower_body(map, output))
// }
// }
//
// impl AsmFunction {
// pub fn lower_header(
// &self,
// map: &mut NamespaceGuard,
// output: &mut ParserOutput,
// ) -> Option<FnIdent> {
// let header = self.header.as_ref()?;
// let name = header.name.as_ref()?;
// let args = header
// .args
// .iter()
// .map(|a| {
// // a.lower(map, output).unwrap_or(VarDef {
// // name: "{error}".to_string(),
// // origin: Origin::File(a.span),
// // ty: Type::Error,
// // })
// })
// .collect();
// // let ret = match &header.ret {
// // Some(ty) => ty.lower(map, output),
// // None => Type::Concrete(BuiltinType::Unit.id()),
// // };
// let ret = Type::Concrete(BuiltinType::Unit.id());
// Some(map.def_fn(FnDef {
// name: name.to_string(),
// origin: Origin::File(self.header.span),
// args,
// ret,
// }))
// }
// pub fn lower_body(&self, map: &mut NamespaceGuard, output: &mut ParserOutput) -> Function {
// let mut instructions = Instructions::new();
// let mut ctx = FnLowerCtx {
// instructions: &mut instructions,
// map,
// output,
// span: self.body.span,
// };
// self.body.lower(&mut ctx);
// Function::new(instructions)
// }
// }
pub struct FnLowerCtx<'a, 'n> {
pub map: &'a mut NamespaceGuard<'n>,
pub instructions: &'a mut IRInstructions,

View File

@@ -7,15 +7,6 @@ pub struct PType {
pub args: Vec<Node<PType>>,
}
impl PType {
pub fn unit() -> Self {
Self {
name: "()".to_string(),
args: Vec::new(),
}
}
}
impl Parsable for PType {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let next = ctx.expect_peek()?;