type checking !?!?

This commit is contained in:
2025-03-22 14:40:32 -04:00
parent 606cb30c6b
commit 7f809d797c
44 changed files with 664 additions and 314 deletions

View File

@@ -1,17 +1,33 @@
use crate::{compiler::arch::riscv64::*, ir::VarID};
use crate::{
compiler::arch::riscv64::*,
ir::{VarID, VarInst},
};
#[derive(Copy, Clone)]
pub enum RV64Instruction {
Ecall,
Li { dest: RegRef, imm: i64 },
Mv { dest: RegRef, src: RegRef },
La { dest: RegRef, src: VarID },
Ld { dest: RegRef, offset: i64, base: RegRef },
Li {
dest: RegRef,
imm: i64,
},
Mv {
dest: RegRef,
src: RegRef,
},
La {
dest: RegRef,
src: VarInst,
},
Ld {
dest: RegRef,
offset: i64,
base: RegRef,
},
}
#[derive(Copy, Clone)]
pub enum RegRef {
Var(VarID),
Var(VarInst),
Reg(Reg),
}

View File

@@ -1,80 +0,0 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FilePos {
pub line: usize,
pub col: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct FileSpan {
pub start: FilePos,
pub end: FilePos,
}
impl FilePos {
pub fn start() -> Self {
Self { line: 0, col: 0 }
}
}
impl FilePos {
pub fn to(self, end: FilePos) -> FileSpan {
FileSpan { start: self, end }
}
pub fn char_span(self) -> FileSpan {
FileSpan::at(self)
}
}
const BEFORE: usize = 1;
const AFTER: usize = 0;
impl FileSpan {
pub fn at(pos: FilePos) -> Self {
Self {
start: pos,
end: pos,
}
}
pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> {
let start = self.start.line.saturating_sub(BEFORE);
let num_before = self.start.line - start;
let mut lines = file.lines().skip(start);
let width = format!("{}", self.end.line + AFTER).len();
let same_line = self.start.line == self.end.line;
for i in 0..num_before {
writeln!(writer, "{:>width$} | {}", start + i, lines.next().unwrap())?;
}
let line = lines.next().unwrap();
writeln!(writer, "{:>width$} | {}", self.start.line, line)?;
let len = if same_line {
self.end.col - self.start.col + 1
} else {
line.len() - self.start.col
};
writeln!(
writer,
"{} | {}",
" ".repeat(width),
" ".repeat(self.start.col) + &"^".repeat(len)
)?;
if !same_line {
for _ in 0..self.end.line - self.start.line - 1 {
lines.next();
}
let line = lines.next().unwrap();
writeln!(writer, "{:>width$} | {}", self.end.line, line)?;
writeln!(
writer,
"{} | {}",
" ".repeat(width),
"^".repeat(self.end.col + 1)
)?;
}
// for i in 0..AFTER {
// if let Some(next) = lines.next() {
// writeln!(writer, "{:>width$} | {}", self.end.line + i + 1, next)?;
// }
// }
Ok(())
}
}

View File

@@ -1,8 +1,8 @@
use std::collections::HashMap;
use crate::ir::{FnID, SymbolSpace};
use crate::ir::SymbolSpace;
use super::{IRLFunction, IRLInstruction, IRUInstruction, Len, Namespace, Symbol, VarID};
use super::{IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, Type, VarID};
pub struct IRLProgram {
sym_space: SymbolSpace,
@@ -12,110 +12,116 @@ pub struct IRLProgram {
// NOTE: there are THREE places here where I specify size (8)
impl IRLProgram {
pub fn create(ns: &Namespace) -> Option<Self> {
pub fn create(p: &IRUProgram) -> Result<Self, String> {
let mut start = None;
for (i, f) in ns.iter_fns() {
if f?.name == "start" {
for (i, f) in p.iter_fns() {
if f.name == "start" {
start = Some(i);
}
}
let start = start?;
let start = start.ok_or("no start method found")?;
let mut builder = SymbolSpace::with_entries(&[start]);
let entry = builder.func(&start);
while let Some((sym, i)) = builder.pop_fn() {
let f = ns.fns[i.0].as_ref().unwrap();
let f = p.fns[i.0].as_ref().unwrap();
let mut instrs = Vec::new();
let mut stack = HashMap::new();
let mut makes_call = false;
let mut alloc_stack = |i: &VarID| -> bool {
let mut alloc_stack = |i: VarID| -> bool {
let size = *stack
.entry(*i)
.or_insert(ns.size_of_var(i).expect("unsized type"));
.entry(i)
.or_insert(p.size_of_var(i).expect("unsized type"));
size == 0
};
for i in &f.instructions {
match i {
match &i.i {
IRUInstruction::Mv { dest, src } => {
if alloc_stack(dest) {
if alloc_stack(dest.id) {
continue;
}
instrs.push(IRLInstruction::Mv {
dest: *dest,
src: *src,
dest: dest.id,
src: src.id,
});
}
IRUInstruction::Ref { dest, src } => {
if alloc_stack(dest) {
if alloc_stack(dest.id) {
continue;
}
instrs.push(IRLInstruction::Ref {
dest: *dest,
src: *src,
dest: dest.id,
src: src.id,
});
}
IRUInstruction::LoadData { dest, src } => {
if alloc_stack(dest) {
if alloc_stack(dest.id) {
continue;
}
let data = &ns.data[src.0];
let data = &p.data[src.0];
let sym = builder.ro_data(src, data);
instrs.push(IRLInstruction::LoadData {
dest: *dest,
dest: dest.id,
offset: 0,
len: data.len() as Len,
src: sym,
});
}
IRUInstruction::LoadSlice { dest, src, len } => {
if alloc_stack(dest) {
IRUInstruction::LoadSlice { dest, src } => {
if alloc_stack(dest.id) {
continue;
}
let sym = builder.ro_data(src, &ns.data[src.0]);
let data = &p.data[src.0];
let def = p.get_data(*src);
let Type::Array(ty, len) = &def.ty else {
return Err(format!("tried to load {} as slice", p.type_name(&def.ty)));
};
let sym = builder.ro_data(src, data);
instrs.push(IRLInstruction::LoadAddr {
dest: *dest,
dest: dest.id,
offset: 0,
src: sym,
});
let sym = builder.anon_ro_data(&(*len as u64).to_le_bytes());
instrs.push(IRLInstruction::LoadData {
dest: *dest,
dest: dest.id,
offset: 8,
len: 8,
src: sym,
});
}
IRUInstruction::LoadFn { dest, src } => {
if alloc_stack(dest) {
if alloc_stack(dest.id) {
continue;
}
let sym = builder.func(src);
instrs.push(IRLInstruction::LoadAddr {
dest: *dest,
dest: dest.id,
offset: 0,
src: sym,
});
}
IRUInstruction::Call { dest, f, args } => {
alloc_stack(dest);
alloc_stack(dest.id);
makes_call = true;
let fid = &ns.fn_map[f];
let fid = &p.fn_map[&f.id];
let sym = builder.func(fid);
instrs.push(IRLInstruction::Call {
dest: *dest,
dest: dest.id,
f: sym,
args: args
.iter()
.map(|a| (*a, ns.size_of_var(a).expect("unsized type")))
.map(|a| (a.id, p.size_of_var(a.id).expect("unsized type")))
.collect(),
});
}
IRUInstruction::AsmBlock { instructions, args } => {
instrs.push(IRLInstruction::AsmBlock {
instructions: instructions.clone(),
args: args.clone(),
args: args.iter().cloned().map(|(r, v)| (r, v.id)).collect(),
})
}
IRUInstruction::Ret { src } => instrs.push(IRLInstruction::Ret { src: *src }),
IRUInstruction::Ret { src } => instrs.push(IRLInstruction::Ret { src: src.id }),
};
}
builder.write_fn(
@@ -127,7 +133,7 @@ impl IRLProgram {
args: f
.args
.iter()
.map(|a| (*a, ns.size_of_var(a).expect("unsized type")))
.map(|a| (*a, p.size_of_var(*a).expect("unsized type")))
.collect(),
stack,
},
@@ -139,7 +145,7 @@ impl IRLProgram {
// println!(" {:?}: {}", a, f.name);
// }
// println!("datas: {}", sym_space.ro_data().len());
Some(Self { sym_space, entry })
Ok(Self { sym_space, entry })
}
pub fn entry(&self) -> Symbol {

View File

@@ -1,5 +1,4 @@
mod upper;
mod file;
mod lower;
mod id;
mod asm;
@@ -7,5 +6,4 @@ pub mod arch;
pub use upper::*;
pub use lower::*;
pub use file::*;
pub use id::*;

View File

@@ -1,4 +1,6 @@
use super::{FileSpan, Type};
use crate::common::FileSpan;
use super::Type;
use std::fmt::Debug;
#[derive(Clone)]
@@ -23,6 +25,12 @@ pub struct VarDef {
pub origin: Origin,
}
#[derive(Clone)]
pub struct DataDef {
pub ty: Type,
pub origin: Origin,
}
#[derive(Debug, Clone, Copy)]
pub enum Origin {
Builtin,

19
src/ir/upper/error.rs Normal file
View File

@@ -0,0 +1,19 @@
use crate::common::{CompilerMsg, CompilerOutput, FileSpan};
use super::{IRUProgram, Type};
impl CompilerOutput {
pub fn check_assign(&mut self, p: &IRUProgram, src: &Type, dest: &Type, span: FileSpan) {
// TODO: spans
if src != dest {
self.err(CompilerMsg {
msg: format!(
"Cannot assign type '{}' to '{}'",
p.type_name(src),
p.type_name(dest)
),
spans: vec![span],
});
}
}
}

View File

@@ -1,52 +1,51 @@
use std::fmt::Write;
use super::{arch::riscv64::RV64Instruction, DataID, FnID, Len, VarID};
use crate::{compiler::arch::riscv64::Reg, util::Padder};
use super::{arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, VarID};
use crate::{common::FileSpan, compiler::arch::riscv64::Reg, util::Padder};
pub struct IRUFunction {
pub name: String,
pub args: Vec<VarID>,
pub instructions: Vec<IRUInstruction>,
pub instructions: Vec<IRUInstrInst>,
}
pub enum IRUInstruction {
Mv {
dest: VarID,
src: VarID,
dest: VarInst,
src: VarInst,
},
Ref {
dest: VarID,
src: VarID,
dest: VarInst,
src: VarInst,
},
LoadData {
dest: VarID,
dest: VarInst,
src: DataID,
},
LoadSlice {
dest: VarID,
dest: VarInst,
src: DataID,
len: Len,
},
LoadFn {
dest: VarID,
dest: VarInst,
src: FnID,
},
Call {
dest: VarID,
f: VarID,
args: Vec<VarID>,
dest: VarInst,
f: VarInst,
args: Vec<VarInst>,
},
AsmBlock {
instructions: Vec<RV64Instruction>,
args: Vec<(Reg, VarID)>,
args: Vec<(Reg, VarInst)>,
},
Ret {
src: VarID,
src: VarInst,
},
}
pub struct IRInstructions {
vec: Vec<IRUInstruction>,
vec: Vec<IRUInstrInst>,
}
impl IRUFunction {
@@ -63,8 +62,8 @@ impl IRInstructions {
pub fn new() -> Self {
Self { vec: Vec::new() }
}
pub fn push(&mut self, i: IRUInstruction) {
self.vec.push(i);
pub fn push(&mut self, i: IRUInstruction, span: FileSpan) {
self.vec.push(IRUInstrInst { i, span });
}
}
@@ -75,7 +74,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::LoadSlice { dest, src } => write!(f, "{dest:?} <- &[{src:?}]"),
Self::Call {
dest,
f: func,

27
src/ir/upper/inst.rs Normal file
View File

@@ -0,0 +1,27 @@
use crate::{common::FileSpan, ir::VarID};
use std::fmt::Debug;
use super::IRUInstruction;
#[derive(Clone, Copy)]
pub struct VarInst {
pub id: VarID,
pub span: FileSpan,
}
pub struct IRUInstrInst {
pub i: IRUInstruction,
pub span: FileSpan,
}
impl Debug for VarInst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.id)
}
}
impl Debug for IRUInstrInst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.i)
}
}

View File

@@ -1,10 +1,14 @@
mod def;
mod func;
mod ty;
mod namespace;
mod program;
mod validate;
mod error;
mod inst;
use super::*;
pub use def::*;
pub use func::*;
pub use ty::*;
pub use namespace::*;
pub use program::*;
pub use inst::*;

View File

@@ -4,12 +4,15 @@ use std::{
ops::{Deref, DerefMut},
};
use super::*;
use crate::common::FileSpan;
pub struct Namespace {
use super::{inst::VarInst, *};
pub struct IRUProgram {
pub fn_defs: Vec<FnDef>,
pub var_defs: Vec<VarDef>,
pub type_defs: Vec<TypeDef>,
pub data_defs: Vec<DataDef>,
pub fns: Vec<Option<IRUFunction>>,
pub data: Vec<Vec<u8>>,
pub fn_map: HashMap<VarID, FnID>,
@@ -17,12 +20,13 @@ pub struct Namespace {
pub stack: Vec<HashMap<String, Idents>>,
}
impl Namespace {
impl IRUProgram {
pub fn new() -> Self {
Self {
fn_defs: Vec::new(),
var_defs: Vec::new(),
type_defs: Vec::new(),
data_defs: Vec::new(),
data: Vec::new(),
fn_map: HashMap::new(),
fns: Vec::new(),
@@ -49,6 +53,9 @@ impl Namespace {
pub fn get_fn(&self, id: FnID) -> &FnDef {
&self.fn_defs[id.0]
}
pub fn get_data(&self, id: DataID) -> &DataDef {
&self.data_defs[id.0]
}
pub fn get_fn_var(&self, id: VarID) -> Option<&FnDef> {
Some(&self.fn_defs[self.fn_map.get(&id)?.0])
}
@@ -90,17 +97,20 @@ impl Namespace {
Type::Unit => 0,
})
}
pub fn size_of_var(&self, var: &VarID) -> Option<Size> {
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 {
pub fn temp_var(&mut self, origin: FileSpan, ty: Type) -> VarInst {
let v = self.def_var(VarDef {
name: format!("temp{}", self.temp),
origin: super::Origin::File(origin),
ty,
});
self.temp += 1;
v
VarInst {
id: v,
span: origin,
}
}
pub fn def_fn(&mut self, def: FnDef) -> FnID {
let i = self.fn_defs.len();
@@ -128,8 +138,9 @@ impl Namespace {
self.type_defs.push(def);
id
}
pub fn def_data(&mut self, bytes: Vec<u8>) -> DataID {
pub fn def_data(&mut self, def: DataDef, bytes: Vec<u8>) -> DataID {
let i = self.data.len();
self.data_defs.push(def);
self.data.push(bytes);
DataID(i)
}
@@ -187,18 +198,17 @@ impl Namespace {
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())
self.var_defs.iter().enumerate().map(|(i, v)| (VarID(i), v))
}
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 fn iter_fns(&self) -> impl Iterator<Item = (FnID, &IRUFunction)> {
self.fns
.iter()
.enumerate()
.flat_map(|(i, f)| Some((FnID(i), f.as_ref()?)))
}
}
pub struct NamespaceGuard<'a>(&'a mut Namespace);
pub struct NamespaceGuard<'a>(&'a mut IRUProgram);
impl Drop for NamespaceGuard<'_> {
fn drop(&mut self) {
@@ -207,7 +217,7 @@ impl Drop for NamespaceGuard<'_> {
}
impl Deref for NamespaceGuard<'_> {
type Target = Namespace;
type Target = IRUProgram;
fn deref(&self) -> &Self::Target {
self.0
}

View File

@@ -1,6 +1,6 @@
use super::{Len, TypeID};
use super::{IRUInstruction, IRUProgram, Len, TypeID};
#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub enum Type {
Concrete(TypeID),
Bits(u32),
@@ -25,3 +25,20 @@ impl Type {
Self::Slice(Box::new(self))
}
}
pub fn resolve_types(ns: &IRUProgram) {
for (i, f) in ns.iter_fns() {
for inst in &f.instructions {
match &inst.i {
IRUInstruction::Mv { dest, src } => todo!(),
IRUInstruction::Ref { dest, src } => todo!(),
IRUInstruction::LoadData { dest, src } => todo!(),
IRUInstruction::LoadSlice { dest, src } => todo!(),
IRUInstruction::LoadFn { dest, src } => todo!(),
IRUInstruction::Call { dest, f, args } => todo!(),
IRUInstruction::AsmBlock { instructions, args } => todo!(),
IRUInstruction::Ret { src } => todo!(),
}
}
}
}

52
src/ir/upper/validate.rs Normal file
View File

@@ -0,0 +1,52 @@
// TODO: move this into ir, not parser
use super::{IRUProgram, Type};
use crate::common::CompilerOutput;
impl IRUProgram {
pub fn validate(&self) -> CompilerOutput {
let mut output = CompilerOutput::new();
for f in self.fns.iter().flatten() {
for i in &f.instructions {
match &i.i {
super::IRUInstruction::Mv { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_var(src.id);
output.check_assign(self, &src.ty, &dest.ty, i.span);
}
super::IRUInstruction::Ref { dest, src } => todo!(),
super::IRUInstruction::LoadData { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_data(*src);
output.check_assign(self, &src.ty, &dest.ty, i.span);
}
super::IRUInstruction::LoadSlice { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_data(*src);
let Type::Array(srcty, ..) = &src.ty else {
todo!()
};
output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.span);
}
super::IRUInstruction::LoadFn { dest, src } => todo!(),
super::IRUInstruction::Call { dest, f, args } => {
let destty = &self.get_var(dest.id).ty;
let f = self.get_var(f.id);
let Type::Fn { args: argtys, ret } = &f.ty else {
todo!()
};
output.check_assign(self, ret, destty, dest.span);
for (argv, argt) in args.iter().zip(argtys) {
let dest = self.get_var(argv.id);
output.check_assign(self, argt, &dest.ty, argv.span);
}
}
super::IRUInstruction::AsmBlock { instructions, args } => {
// TODO
}
super::IRUInstruction::Ret { src } => todo!(),
}
}
}
output
}
}