diff --git a/data/test.lang b/data/test.lang index e04867f..4d7d83f 100644 --- a/data/test.lang +++ b/data/test.lang @@ -7,7 +7,20 @@ fn start() { println("Helld!"); println("Hello World!!!!!"); thinger(); - println("what"); + let x = 3; + if not(not(lt(x, 5))) { + println("tada!"); + }; + println("before:"); + x = 0; + loop { + if not(lt(x, 5)) { + break; + }; + println("RAAAAA"); + x = add(x, 1); + }; + println("after"); print(tester()); let test = Test { a: 10, @@ -77,6 +90,27 @@ fn sub(a: 64, b: 64) -> 64 { c } +fn lt(a: 64, b: 64) -> 64 { + let c: 64 = 0; + asm (t0 = a, t1 = b, a0 = c) { + ld t0, 0, t0 + ld t1, 0, t1 + slt t0, t0, t1 + sd t0, 0, a0 + }; + c +} + +fn not(a: 64) -> 64 { + let c: 64 = 0; + asm (t0 = a, a0 = c) { + ld t0, 0, t0 + xori t0, t0, 1 + sd t0, 0, a0 + }; + c +} + fn arger(a: slice<8>, b: slice<8>, c: slice<8>) { print(a); print(b); diff --git a/src/compiler/arch/riscv/asm.rs b/src/compiler/arch/riscv/asm.rs index 62369a3..fc53274 100644 --- a/src/compiler/arch/riscv/asm.rs +++ b/src/compiler/arch/riscv/asm.rs @@ -1,5 +1,7 @@ use crate::{ - compiler::program::{Addr, Instr, SymTable}, ir::Symbol, util::LabeledFmt + compiler::program::{Addr, Instr, SymTable}, + ir::Symbol, + util::LabeledFmt, }; use super::*; @@ -52,6 +54,12 @@ pub enum LinkerInstruction { }, Call(S), J(S), + Branch { + to: S, + typ: Funct3, + left: R, + right: R, + }, Ret, ECall, EBreak, @@ -66,10 +74,10 @@ pub fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction { } impl Instr for LinkerInstruction { - fn push( + fn push_to( &self, data: &mut Vec, - sym_map: &SymTable, + sym_map: &mut SymTable, pos: Addr, missing: bool, ) -> Option { @@ -135,6 +143,20 @@ impl Instr for LinkerInstruction { Self::ECall => ecall(), Self::EBreak => ebreak(), Self::Li { dest, imm } => addi(*dest, zero, BitsI32::new(*imm)), + Self::Branch { + to, + typ, + left, + right, + } => { + if let Some(addr) = sym_map.get(*to) { + let offset = addr.val() as i32 - pos.val() as i32; + branch(*typ, *left, *right, BitsI32::new(offset)) + } else { + data.extend_from_slice(&[0; 4]); + return Some(*to); + } + } }; data.extend(last.to_le_bytes()); None @@ -181,7 +203,11 @@ pub struct DebugInstr<'a, R, S, L: Fn(&mut std::fmt::Formatter<'_>, &S) -> std:: } impl LabeledFmt for LinkerInstruction { - fn fmt_label(&self, f: &mut std::fmt::Formatter<'_>, label: &dyn crate::util::Labeler) -> std::fmt::Result { + fn fmt_label( + &self, + f: &mut std::fmt::Formatter<'_>, + label: &dyn crate::util::Labeler, + ) -> std::fmt::Result { match self { Self::ECall => write!(f, "ecall"), Self::EBreak => write!(f, "ebreak"), @@ -190,7 +216,7 @@ impl LabeledFmt for LinkerInstruction Self::La { dest, src } => { write!(f, "la {dest:?}, @")?; label(f, src) - }, + } Self::Load { width, dest, @@ -229,6 +255,12 @@ impl LabeledFmt for LinkerInstruction write!(f, "j ")?; label(f, s) } + Self::Branch { + to, + typ, + left, + right, + } => write!(f, "b{} {left:?} {right:?} {to:?}", branch::str(*typ)), Self::Ret => write!(f, "ret"), } } diff --git a/src/compiler/arch/riscv/compile.rs b/src/compiler/arch/riscv/compile.rs index 7f29d1e..978c296 100644 --- a/src/compiler/arch/riscv/compile.rs +++ b/src/compiler/arch/riscv/compile.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::{ - compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedProgram}, + compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedFunction, UnlinkedProgram}, ir::{ arch::riscv64::{RV64Instruction as AI, RegRef}, IRLInstruction as IRI, IRLProgram, Len, Size, @@ -84,6 +84,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { v.push(LI::sd(ra, stack_ra, sp)); } } + let mut locations = HashMap::new(); let mut irli = Vec::new(); for i in &f.instructions { irli.push((v.len(), format!("{i:?}"))); @@ -147,7 +148,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { } fn r(rr: RegRef) -> Reg { match rr { - RegRef::Var(var_ident) => todo!(), + RegRef::Var(..) => todo!(), RegRef::Reg(reg) => reg, } } @@ -160,7 +161,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { dest: r(dest), src: r(src), }), - AI::La { dest, src } => todo!(), + AI::La { .. } => todo!(), AI::Load { width, dest, @@ -216,9 +217,10 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { imm, }), AI::Ret => v.push(LI::Ret), - AI::Call(s) => todo!(), - AI::Jal { dest, offset } => todo!(), - AI::J(s) => todo!(), + AI::Call(..) => todo!(), + AI::Jal { .. } => todo!(), + AI::J(..) => todo!(), + AI::Branch { .. } => todo!(), } } } @@ -229,6 +231,21 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { v.push(LI::ld(t0, rva, sp)); mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32); } + IRI::Jump(location) => { + v.push(LI::J(*location)); + } + IRI::Branch { to, cond } => { + v.push(LI::ld(t0, stack[cond], sp)); + v.push(LI::Branch { + to: *to, + typ: branch::EQ, + left: t0, + right: zero, + }) + } + IRI::Mark(location) => { + locations.insert(v.len(), *location); + } } } dbg.push_fn(irli); @@ -239,12 +256,17 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { v.push(LI::addi(sp, sp, stack_len)); } v.push(LI::Ret); - fns.push((v, *sym)); + fns.push(UnlinkedFunction { + instrs: v, + sym: *sym, + locations, + }); } UnlinkedProgram { - fns: fns.into_iter().map(|(v, s, ..)| (v, s)).collect(), + fns, ro_data: data, start: Some(program.entry()), dbg, + sym_count: program.len(), } } diff --git a/src/compiler/arch/riscv/instr/base.rs b/src/compiler/arch/riscv/instr/base.rs index 79722a1..9f22ecb 100644 --- a/src/compiler/arch/riscv/instr/base.rs +++ b/src/compiler/arch/riscv/instr/base.rs @@ -22,6 +22,7 @@ pub const IMM_OP: u32 = 0b0010011; pub const OP: u32 = 0b0110011; pub const JAL: u32 = 0b1101111; pub const JALR: u32 = 0b1100111; +pub const BRANCH: u32 = 0b1100011; pub type Funct3 = Bits32<2, 0>; pub type Funct7 = Bits32<6, 0>; @@ -59,7 +60,7 @@ pub const fn b_type(rs2: Reg, rs1: Reg, funct3: Funct3, imm: Bits32<12, 1>, opco + (imm.bits(10, 5) << 25) + (rs2.val() << 20) + (rs1.val() << 15) - + (funct3.val() << 8) + + (funct3.val() << 12) + (imm.bits(4, 1) << 8) + (imm.bit(11) << 7) + opcode) diff --git a/src/compiler/arch/riscv/instr/rv32i.rs b/src/compiler/arch/riscv/instr/rv32i.rs index a3eeb26..be87993 100644 --- a/src/compiler/arch/riscv/instr/rv32i.rs +++ b/src/compiler/arch/riscv/instr/rv32i.rs @@ -63,6 +63,28 @@ pub mod width { } } +pub mod branch { + use super::*; + pub const EQ: Funct3 = Funct3::new(0b000); + pub const NE: Funct3 = Funct3::new(0b001); + pub const LT: Funct3 = Funct3::new(0b100); + pub const GE: Funct3 = Funct3::new(0b101); + pub const LTU: Funct3 = Funct3::new(0b110); + pub const GEU: Funct3 = Funct3::new(0b111); + + pub fn str(f: Funct3) -> &'static str { + match f { + EQ => "eq", + NE => "ne", + LT => "lt", + GE => "ge", + LTU => "ltu", + GEU => "geu", + _ => "?", + } + } +} + pub const fn ecall() -> RawInstruction { i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM) } @@ -94,3 +116,7 @@ pub const fn j(offset: BitsI32<20, 1>) -> RawInstruction { pub const fn ret() -> RawInstruction { jalr(zero, BitsI32::new(0), ra) } + +pub const fn branch(typ: Funct3, left: Reg, right: Reg, offset: BitsI32<12, 1>) -> RawInstruction { + b_type(right, left, typ, offset.to_u(), BRANCH) +} diff --git a/src/compiler/program.rs b/src/compiler/program.rs index 800710c..3af2f2b 100644 --- a/src/compiler/program.rs +++ b/src/compiler/program.rs @@ -13,40 +13,54 @@ pub struct LinkedProgram { } pub struct UnlinkedProgram { - pub fns: Vec<(Vec, Symbol)>, + pub fns: Vec>, pub ro_data: Vec<(Vec, Symbol)>, + pub sym_count: usize, pub start: Option, pub dbg: DebugInfo, } +pub struct UnlinkedFunction { + pub instrs: Vec, + pub sym: Symbol, + pub locations: HashMap, +} + impl UnlinkedProgram { pub fn link(self) -> LinkedProgram { let mut data = Vec::new(); - let mut sym_table = SymTable::new(self.fns.len() + self.ro_data.len()); + let mut sym_table = SymTable::new(self.sym_count); let mut missing = HashMap::>::new(); for (val, id) in self.ro_data { sym_table.insert(id, Addr(data.len() as u64)); data.extend(val); } data.resize(data.len() + (4 - data.len() % 4), 0); - for (fun, id) in self.fns { - sym_table.insert(id, Addr(data.len() as u64)); - for i in fun { + for f in self.fns { + let mut added = vec![f.sym]; + sym_table.insert(f.sym, Addr(data.len() as u64)); + for (i, instr) in f.instrs.into_iter().enumerate() { let i_pos = Addr(data.len() as u64); - if let Some(sym) = i.push(&mut data, &sym_table, i_pos, false) { + if let Some(sym) = f.locations.get(&i) { + sym_table.insert(*sym, i_pos); + added.push(*sym); + } + if let Some(sym) = instr.push_to(&mut data, &mut sym_table, i_pos, false) { if let Some(vec) = missing.get_mut(&sym) { - vec.push((i_pos, i)); + vec.push((i_pos, instr)); } else { - missing.insert(sym, vec![(i_pos, i)]); + missing.insert(sym, vec![(i_pos, instr)]); } } } - if let Some(vec) = missing.remove(&id) { - for (addr, i) in vec { - let mut replace = Vec::new(); - i.push(&mut replace, &sym_table, addr, true); - let pos = addr.val() as usize; - data[pos..pos + replace.len()].copy_from_slice(&replace); + for add in added { + if let Some(vec) = missing.remove(&add) { + for (addr, i) in vec { + let mut replace = Vec::new(); + i.push_to(&mut replace, &mut sym_table, addr, true); + let pos = addr.val() as usize; + data[pos..pos + replace.len()].copy_from_slice(&replace); + } } } } @@ -61,8 +75,13 @@ impl UnlinkedProgram { } pub trait Instr { - fn push(&self, data: &mut Vec, syms: &SymTable, pos: Addr, missing: bool) - -> Option; + fn push_to( + &self, + data: &mut Vec, + syms: &mut SymTable, + pos: Addr, + missing: bool, + ) -> Option; } #[derive(Debug, Clone, Copy, PartialEq)] @@ -92,16 +111,16 @@ impl SymTable { impl + LabeledFmt> std::fmt::Debug for UnlinkedProgram { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for ((v, s), irli) in self.fns.iter().zip(&self.dbg.ir_lower) { - writeln!(f, "{}:", self.dbg.sym_label(*s).unwrap())?; + for (fun, irli) in self.fns.iter().zip(&self.dbg.ir_lower) { + writeln!(f, "{}:", self.dbg.sym_label(fun.sym).unwrap())?; let mut liter = irli.iter(); let mut cur = liter.next(); - for (i, instr) in v.iter().enumerate() { - if let Some(c) = cur { - if i == c.0 { - writeln!(f, " {}:", c.1)?; - cur = liter.next(); - } + for (i, instr) in fun.instrs.iter().enumerate() { + while let Some(c) = cur + && i == c.0 + { + writeln!(f, " {}:", c.1)?; + cur = liter.next(); } writeln!( f, diff --git a/src/ir/lower/func.rs b/src/ir/lower/func.rs index 6f1dc9c..7b00e8c 100644 --- a/src/ir/lower/func.rs +++ b/src/ir/lower/func.rs @@ -47,4 +47,12 @@ pub enum IRLInstruction { Ret { src: VarID, }, + // TODO I feel like this should be turned into control flow instructions, maybe... + // not sure but LLVM has them so might be right play; seems optimal for optimization + Jump(Symbol), + Branch { + to: Symbol, + cond: VarID, + }, + Mark(Symbol), } diff --git a/src/ir/lower/program.rs b/src/ir/lower/program.rs index efc393b..b9650d3 100644 --- a/src/ir/lower/program.rs +++ b/src/ir/lower/program.rs @@ -1,8 +1,11 @@ use std::collections::HashMap; -use crate::ir::SymbolSpace; +use crate::ir::{IRUFunction, IRUInstrInst, Size, SymbolSpace}; -use super::{IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, Type, VarID}; +use super::{ + IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, SymbolSpaceBuilder, Type, + VarID, +}; pub struct IRLProgram { sym_space: SymbolSpace, @@ -20,179 +23,18 @@ impl IRLProgram { } } 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 mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]); + let entry = ssbuilder.func(&start); + while let Some((sym, i)) = ssbuilder.pop_fn() { 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 size = *stack - .entry(i) - .or_insert(p.size_of_var(i).expect("unsized type")); - size == 0 - }; + let mut fbuilder = IRLFunctionBuilder::new(p, &mut ssbuilder); for i in &f.instructions { - match &i.i { - IRUInstruction::Mv { dest, src } => { - if alloc_stack(dest.id) { - continue; - } - instrs.push(IRLInstruction::Mv { - dest: dest.id, - dest_offset: 0, - src: src.id, - src_offset: 0, - }); - } - IRUInstruction::Ref { dest, src } => { - if alloc_stack(dest.id) { - continue; - } - instrs.push(IRLInstruction::Ref { - dest: dest.id, - src: src.id, - }); - } - IRUInstruction::LoadData { dest, src } => { - if alloc_stack(dest.id) { - continue; - } - let data = &p.data[src.0]; - let ddef = p.get_data(*src); - let sym = builder.ro_data(src, data, Some(ddef.label.clone())); - instrs.push(IRLInstruction::LoadData { - dest: dest.id, - offset: 0, - len: data.len() as Len, - src: sym, - }); - } - IRUInstruction::LoadSlice { dest, src } => { - if alloc_stack(dest.id) { - continue; - } - 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, Some(def.label.clone())); - instrs.push(IRLInstruction::LoadAddr { - dest: dest.id, - offset: 0, - src: sym, - }); - - let sym = builder.anon_ro_data( - &(*len as u64).to_le_bytes(), - Some(format!("len: {}", len)), - ); - instrs.push(IRLInstruction::LoadData { - dest: dest.id, - offset: 8, - len: 8, - src: sym, - }); - } - IRUInstruction::LoadFn { dest, src } => { - if alloc_stack(dest.id) { - continue; - } - let sym = builder.func(src); - instrs.push(IRLInstruction::LoadAddr { - dest: dest.id, - offset: 0, - src: sym, - }); - } - IRUInstruction::Call { dest, f, args } => { - alloc_stack(dest.id); - makes_call = true; - let fid = &p.fn_map[&f.id]; - let sym = builder.func(fid); - let ret_size = p.size_of_var(dest.id).expect("unsized type"); - let dest = if ret_size > 0 { - Some((dest.id, ret_size)) - } else { - None - }; - instrs.push(IRLInstruction::Call { - dest, - f: sym, - args: args - .iter() - .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.iter().cloned().map(|(r, v)| (r, v.id)).collect(), - }) - } - IRUInstruction::Ret { src } => instrs.push(IRLInstruction::Ret { src: src.id }), - IRUInstruction::Construct { dest, fields } => { - if alloc_stack(dest.id) { - continue; - } - let ty = &p.get_var(dest.id).ty; - let Type::Concrete(id) = ty else { - return Err(format!("Failed to contruct type {}", p.type_name(ty))); - }; - let struc = p.get_struct(*id); - for (name, var) in fields { - instrs.push(IRLInstruction::Mv { - dest: dest.id, - src: var.id, - dest_offset: struc.fields[name].offset, - src_offset: 0, - }) - } - } - IRUInstruction::Access { dest, src, field } => { - if alloc_stack(dest.id) { - continue; - } - let ty = &p.get_var(src.id).ty; - let Type::Concrete(id) = ty else { - return Err(format!( - "Failed to access field of struct {}", - p.type_name(ty) - )); - }; - let struc = p.get_struct(*id); - let Some(field) = struc.fields.get(field) else { - return Err(format!("No field {field} in struct {}", p.type_name(ty))); - }; - instrs.push(IRLInstruction::Mv { - dest: dest.id, - src: src.id, - src_offset: field.offset, - dest_offset: 0, - }) - } - }; + fbuilder.insert_instr(i); } - builder.write_fn( - sym, - IRLFunction { - instructions: instrs, - makes_call, - args: f - .args - .iter() - .map(|a| (*a, p.size_of_var(*a).expect("unsized type"))) - .collect(), - ret_size: p.size_of_type(&f.ret).expect("unsized type"), - stack, - }, - Some(f.name.clone()), - ); + let res = fbuilder.finish(f); + ssbuilder.write_fn(sym, res, Some(f.name.clone())); } - let sym_space = builder.finish().expect("we failed the mission"); + let sym_space = ssbuilder.finish().expect("we failed the mission"); Ok(Self { sym_space, entry }) } @@ -201,6 +43,220 @@ impl IRLProgram { } } +pub struct IRLFunctionBuilder<'a> { + program: &'a IRUProgram, + builder: &'a mut SymbolSpaceBuilder, + instrs: Vec, + stack: HashMap, + makes_call: bool, + outer: Option, +} + +impl<'a> IRLFunctionBuilder<'a> { + pub fn new(program: &'a IRUProgram, builder: &'a mut SymbolSpaceBuilder) -> Self { + Self { + instrs: Vec::new(), + stack: HashMap::new(), + makes_call: false, + program, + builder, + outer: None, + } + } + pub fn alloc_stack(&mut self, i: VarID) -> Option<()> { + let size = *self + .stack + .entry(i) + .or_insert(self.program.size_of_var(i).expect("unsized type")); + if size == 0 { + None + } else { + Some(()) + } + } + pub fn insert_instr(&mut self, i: &IRUInstrInst) -> Option> { + match &i.i { + IRUInstruction::Mv { dest, src } => { + self.alloc_stack(dest.id)?; + self.instrs.push(IRLInstruction::Mv { + dest: dest.id, + dest_offset: 0, + src: src.id, + src_offset: 0, + }); + } + IRUInstruction::Ref { dest, src } => { + self.alloc_stack(dest.id)?; + self.instrs.push(IRLInstruction::Ref { + dest: dest.id, + src: src.id, + }); + } + IRUInstruction::LoadData { dest, src } => { + self.alloc_stack(dest.id)?; + let data = &self.program.data[src.0]; + let ddef = self.program.get_data(*src); + let sym = self.builder.ro_data(src, data, Some(ddef.label.clone())); + self.instrs.push(IRLInstruction::LoadData { + dest: dest.id, + offset: 0, + len: data.len() as Len, + src: sym, + }); + } + IRUInstruction::LoadSlice { dest, src } => { + self.alloc_stack(dest.id)?; + let data = &self.program.data[src.0]; + let def = self.program.get_data(*src); + let Type::Array(_, len) = &def.ty else { + return Some(Some(format!( + "tried to load {} as slice", + self.program.type_name(&def.ty) + ))); + }; + let sym = self.builder.ro_data(src, data, Some(def.label.clone())); + self.instrs.push(IRLInstruction::LoadAddr { + dest: dest.id, + offset: 0, + src: sym, + }); + + let sym = self + .builder + .anon_ro_data(&(*len as u64).to_le_bytes(), Some(format!("len: {}", len))); + self.instrs.push(IRLInstruction::LoadData { + dest: dest.id, + offset: 8, + len: 8, + src: sym, + }); + } + IRUInstruction::LoadFn { dest, src } => { + self.alloc_stack(dest.id)?; + let sym = self.builder.func(src); + self.instrs.push(IRLInstruction::LoadAddr { + dest: dest.id, + offset: 0, + src: sym, + }); + } + IRUInstruction::Call { dest, f, args } => { + self.alloc_stack(dest.id); + self.makes_call = true; + let fid = &self.program.fn_map[&f.id]; + let sym = self.builder.func(fid); + let ret_size = self.program.size_of_var(dest.id).expect("unsized type"); + let dest = if ret_size > 0 { + Some((dest.id, ret_size)) + } else { + None + }; + self.instrs.push(IRLInstruction::Call { + dest, + f: sym, + args: args + .iter() + .map(|a| (a.id, self.program.size_of_var(a.id).expect("unsized type"))) + .collect(), + }); + } + IRUInstruction::AsmBlock { instructions, args } => { + self.instrs.push(IRLInstruction::AsmBlock { + instructions: instructions.clone(), + args: args.iter().cloned().map(|(r, v)| (r, v.id)).collect(), + }) + } + IRUInstruction::Ret { src } => self.instrs.push(IRLInstruction::Ret { src: src.id }), + IRUInstruction::Construct { dest, fields } => { + self.alloc_stack(dest.id)?; + let ty = &self.program.get_var(dest.id).ty; + let Type::Concrete(id) = ty else { + return Some(Some(format!( + "Failed to contruct type {}", + self.program.type_name(ty) + ))); + }; + let struc = self.program.get_struct(*id); + for (name, var) in fields { + self.instrs.push(IRLInstruction::Mv { + dest: dest.id, + src: var.id, + dest_offset: struc.fields[name].offset, + src_offset: 0, + }) + } + } + IRUInstruction::Access { dest, src, field } => { + self.alloc_stack(dest.id)?; + let ty = &self.program.get_var(src.id).ty; + let Type::Concrete(id) = ty else { + return Some(Some(format!( + "Failed to access field of struct {}", + self.program.type_name(ty) + ))); + }; + let struc = self.program.get_struct(*id); + let Some(field) = struc.fields.get(field) else { + return Some(Some(format!( + "No field {field} in struct {}", + self.program.type_name(ty) + ))); + }; + self.instrs.push(IRLInstruction::Mv { + dest: dest.id, + src: src.id, + src_offset: field.offset, + dest_offset: 0, + }) + } + IRUInstruction::If { cond, body } => { + let sym = self.builder.reserve(); + self.instrs.push(IRLInstruction::Branch { + to: *sym, + cond: cond.id, + }); + for i in body { + self.insert_instr(i); + } + self.instrs.push(IRLInstruction::Mark(*sym)); + } + IRUInstruction::Loop { body } => { + let top = self.builder.reserve(); + let bot = self.builder.reserve(); + let old = self.outer; + self.outer = Some(*bot); + self.instrs.push(IRLInstruction::Mark(*top)); + for i in body { + self.insert_instr(i); + } + self.instrs.push(IRLInstruction::Jump(*top)); + self.instrs.push(IRLInstruction::Mark(*bot)); + self.outer = old; + } + IRUInstruction::Break => { + self.instrs.push(IRLInstruction::Jump( + self.outer.expect("Tried to break outside of loop"), + )); + } + }; + Some(None) + } + + pub fn finish(self, f: &IRUFunction) -> IRLFunction { + IRLFunction { + instructions: self.instrs, + makes_call: self.makes_call, + args: f + .args + .iter() + .map(|a| (*a, self.program.size_of_var(*a).expect("unsized type"))) + .collect(), + ret_size: self.program.size_of_type(&f.ret).expect("unsized type"), + stack: self.stack, + } + } +} + impl std::ops::Deref for IRLProgram { type Target = SymbolSpace; diff --git a/src/ir/lower/symbol.rs b/src/ir/lower/symbol.rs index 6702d8f..7d29d09 100644 --- a/src/ir/lower/symbol.rs +++ b/src/ir/lower/symbol.rs @@ -18,6 +18,7 @@ impl std::ops::Deref for WritableSymbol { pub struct SymbolSpace { ro_data: Vec<(Symbol, Vec)>, fns: Vec<(Symbol, IRLFunction)>, + len: usize, labels: Vec>, } @@ -32,21 +33,6 @@ pub struct SymbolSpaceBuilder { } impl SymbolSpace { - pub fn with_entries(entries: &[FnID]) -> SymbolSpaceBuilder { - let mut s = SymbolSpaceBuilder { - symbols: 0, - unwritten_fns: Vec::new(), - fn_map: HashMap::new(), - data_map: HashMap::new(), - ro_data: Vec::new(), - fns: Vec::new(), - labels: Vec::new(), - }; - for e in entries { - s.func(e); - } - s - } pub fn ro_data(&self) -> &[(Symbol, Vec)] { &self.ro_data } @@ -56,9 +42,30 @@ impl SymbolSpace { pub fn labels(&self) -> &[Option] { &self.labels } + pub fn len(&self) -> usize { + self.len + } } impl SymbolSpaceBuilder { + pub fn new() -> Self { + Self { + symbols: 0, + unwritten_fns: Vec::new(), + fn_map: HashMap::new(), + data_map: HashMap::new(), + ro_data: Vec::new(), + fns: Vec::new(), + labels: Vec::new(), + } + } + pub fn with_entries(entries: &[FnID]) -> SymbolSpaceBuilder { + let mut s = Self::new(); + for e in entries { + s.func(e); + } + s + } pub fn pop_fn(&mut self) -> Option<(WritableSymbol, FnID)> { self.unwritten_fns.pop() } @@ -94,7 +101,6 @@ impl SymbolSpaceBuilder { data: Vec, name: Option, ) -> Symbol { - let data = data.into(); self.ro_data.push((*sym, data)); self.labels[sym.0 .0] = name; *sym @@ -116,11 +122,12 @@ impl SymbolSpaceBuilder { WritableSymbol(Symbol(val)) } pub fn len(&self) -> usize { - self.fns.len() + self.ro_data.len() + self.symbols } pub fn finish(self) -> Option { if self.unwritten_fns.is_empty() { Some(SymbolSpace { + len: self.symbols, fns: self.fns, ro_data: self.ro_data, labels: self.labels, diff --git a/src/ir/upper/func.rs b/src/ir/upper/func.rs index 2ad9fa6..e44ef1f 100644 --- a/src/ir/upper/func.rs +++ b/src/ir/upper/func.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Write}; use super::{ arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, Type, VarID, }; -use crate::{common::FileSpan, compiler::arch::riscv::Reg, util::Padder}; +use crate::{compiler::arch::riscv::Reg, util::Padder}; pub struct IRUFunction { pub name: String, @@ -54,50 +54,64 @@ pub enum IRUInstruction { dest: VarInst, fields: HashMap, }, -} - -pub struct IRInstructions { - vec: Vec, -} - -impl IRUFunction { - pub fn new(name: String, args: Vec, ret: Type, instructions: IRInstructions) -> Self { - Self { - name, - ret, - args, - instructions: instructions.vec, - } - } -} - -impl IRInstructions { - pub fn new() -> Self { - Self { vec: Vec::new() } - } - pub fn push(&mut self, i: IRUInstruction, span: FileSpan) { - self.vec.push(IRUInstrInst { i, span }); - } + If { + cond: VarInst, + body: Vec, + }, + Loop { + body: Vec, + }, + Break, } impl std::fmt::Debug for IRUInstruction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Mv { dest, src } => write!(f, "{dest:?} <- {src:?}"), - 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 } => write!(f, "{dest:?} <- &[{src:?}]"), + Self::Mv { dest, src } => write!(f, "{dest:?} <- {src:?}")?, + 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 } => write!(f, "{dest:?} <- &[{src:?}]")?, Self::Call { dest, f: func, args, - } => write!(f, "{dest:?} <- {func:?}({args:?})"), - Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}"), - Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish(), - Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}"), - Self::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}"), + } => write!(f, "{dest:?} <- {func:?}({args:?})")?, + Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}")?, + Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?, + Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}")?, + Self::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}")?, + Self::If { cond, body } => { + write!(f, "if {cond:?}:")?; + if !body.is_empty() { + f.write_str("{\n ")?; + let mut padder = Padder::new(f); + for i in body { + // they don't expose wrap_buf :grief: + padder.write_str(&format!("{i:?};\n"))?; + } + f.write_char('}')?; + } else { + f.write_str("{}")?; + } + } + Self::Loop { body } => { + write!(f, "loop:")?; + if !body.is_empty() { + f.write_str("{\n ")?; + let mut padder = Padder::new(f); + for i in body { + // they don't expose wrap_buf :grief: + padder.write_str(&format!("{i:?};\n"))?; + } + f.write_char('}')?; + } else { + f.write_str("{}")?; + } + } + Self::Break => write!(f, "break")?, } + Ok(()) } } diff --git a/src/ir/upper/program.rs b/src/ir/upper/program.rs index 86c5d12..ab4c0e2 100644 --- a/src/ir/upper/program.rs +++ b/src/ir/upper/program.rs @@ -1,7 +1,6 @@ use std::{ collections::HashMap, fmt::Debug, - ops::{Deref, DerefMut}, }; use crate::common::FileSpan; @@ -34,9 +33,11 @@ impl IRUProgram { stack: vec![HashMap::new()], } } - pub fn push(&mut self) -> NamespaceGuard { + pub fn push(&mut self) { self.stack.push(HashMap::new()); - NamespaceGuard(self) + } + pub fn pop(&mut self) { + self.stack.pop(); } pub fn get(&self, name: &str) -> Option { for map in self.stack.iter().rev() { @@ -205,27 +206,6 @@ impl IRUProgram { } } -pub struct NamespaceGuard<'a>(&'a mut IRUProgram); - -impl Drop for NamespaceGuard<'_> { - fn drop(&mut self) { - self.0.stack.pop(); - } -} - -impl Deref for NamespaceGuard<'_> { - type Target = IRUProgram; - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl DerefMut for NamespaceGuard<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } -} - #[derive(Debug, Clone, Copy)] pub enum Ident { Var(VarID), diff --git a/src/ir/upper/validate.rs b/src/ir/upper/validate.rs index e2d041a..70bc0ed 100644 --- a/src/ir/upper/validate.rs +++ b/src/ir/upper/validate.rs @@ -1,114 +1,140 @@ // TODO: move this into ir, not parser -use super::{IRUInstruction, IRUProgram, Type}; +use super::{IRUInstrInst, IRUInstruction, IRUProgram, Type}; use crate::common::{CompilerMsg, CompilerOutput}; impl IRUProgram { pub fn validate(&self) -> CompilerOutput { let mut output = CompilerOutput::new(); for (f, fd) in self.fns.iter().flatten().zip(&self.fn_defs) { - for i in &f.instructions { - match &i.i { - 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); - } - IRUInstruction::Ref { dest, src } => todo!(), - 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); - } - 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); - } - IRUInstruction::LoadFn { dest, src } => todo!(), - 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); - if args.len() != argtys.len() { - output.err(CompilerMsg { - msg: "Wrong number of arguments to function".to_string(), - spans: vec![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); - } - } - IRUInstruction::AsmBlock { instructions, args } => { - // TODO - } - IRUInstruction::Ret { src } => { - let srcty = &self.get_var(src.id).ty; - output.check_assign(self, srcty, &fd.ret, src.span); - } - IRUInstruction::Construct { dest, fields } => { - let dest_def = self.get_var(dest.id); - let tyid = match dest_def.ty { - Type::Concrete(id) => id, - _ => { - output.err(CompilerMsg { - msg: "uhh type is not struct".to_string(), - spans: vec![dest.span], - }); - continue; - } - }; - let def = self.get_struct(tyid); - for (name, field) in &def.fields { - if let Some(var) = fields.get(name) { - let ety = &self.get_var(var.id).ty; - output.check_assign(self, &field.ty, ety, var.span); - } else { - output.err(CompilerMsg { - msg: format!("field '{name}' missing from struct"), - spans: vec![dest.span], - }); - } - } - for name in fields.keys() { - if !def.fields.contains_key(name) { - output.err(CompilerMsg { - msg: format!("field '{name}' not in struct"), - spans: vec![dest.span], - }); - } - } - } - IRUInstruction::Access { dest, src, field } => { - let dest_def = self.get_var(dest.id); - let src_def = self.get_var(src.id); - let tyid = match src_def.ty { - Type::Concrete(id) => id, - _ => { - output.err(CompilerMsg { - msg: "uhh type is not struct".to_string(), - spans: vec![dest.span], - }); - continue; - } - }; - let def = self.get_struct(tyid); - let field = def.fields.get(field).expect( - "already validated during parse lowering... probably shouldn't be?", - ); - output.check_assign(self, &field.ty, &dest_def.ty, i.span); - // TODO - } - } - } + self.validate_fn(&f.instructions, &fd.ret, &mut output, false); } output } + + pub fn validate_fn( + &self, + instructions: &[IRUInstrInst], + ret: &Type, + output: &mut CompilerOutput, + breakable: bool, + ) { + for i in instructions { + match &i.i { + 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); + } + IRUInstruction::Ref { dest, src } => todo!(), + 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); + } + 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); + } + IRUInstruction::LoadFn { dest, src } => todo!(), + 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); + if args.len() != argtys.len() { + output.err(CompilerMsg { + msg: "Wrong number of arguments to function".to_string(), + spans: vec![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); + } + } + IRUInstruction::AsmBlock { instructions, args } => { + // TODO + } + IRUInstruction::Ret { src } => { + let srcty = &self.get_var(src.id).ty; + output.check_assign(self, srcty, ret, src.span); + } + IRUInstruction::Construct { dest, fields } => { + let dest_def = self.get_var(dest.id); + let tyid = match dest_def.ty { + Type::Concrete(id) => id, + _ => { + output.err(CompilerMsg { + msg: "uhh type is not struct".to_string(), + spans: vec![dest.span], + }); + continue; + } + }; + let def = self.get_struct(tyid); + for (name, field) in &def.fields { + if let Some(var) = fields.get(name) { + let ety = &self.get_var(var.id).ty; + output.check_assign(self, &field.ty, ety, var.span); + } else { + output.err(CompilerMsg { + msg: format!("field '{name}' missing from struct"), + spans: vec![dest.span], + }); + } + } + for name in fields.keys() { + if !def.fields.contains_key(name) { + output.err(CompilerMsg { + msg: format!("field '{name}' not in struct"), + spans: vec![dest.span], + }); + } + } + } + IRUInstruction::Access { dest, src, field } => { + let dest_def = self.get_var(dest.id); + let src_def = self.get_var(src.id); + let tyid = match src_def.ty { + Type::Concrete(id) => id, + _ => { + output.err(CompilerMsg { + msg: "uhh type is not struct".to_string(), + spans: vec![dest.span], + }); + continue; + } + }; + let def = self.get_struct(tyid); + let field = def.fields.get(field).expect( + "already validated during parse lowering... probably shouldn't be?", + ); + output.check_assign(self, &field.ty, &dest_def.ty, i.span); + } + IRUInstruction::If { cond, body } => { + let cond = self.get_var(cond.id); + output.check_assign(self, &cond.ty, &Type::Bits(64), i.span); + self.validate_fn(body, ret, output, breakable); + } + IRUInstruction::Loop { body } => { + self.validate_fn(body, ret, output, true); + } + IRUInstruction::Break => { + if !breakable { + output.err(CompilerMsg { + msg: "Can't break here (outside of loop)".to_string(), + spans: vec![i.span], + }); + } + // TODO + } + } + } + } } diff --git a/src/main.rs b/src/main.rs index 7398f94..d0a0a39 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,8 +41,8 @@ fn run_file(file: &str, gdb: bool, asm: bool) { // println!("Parsed:"); // println!("{:#?}", res.node); if let Some(module) = res.node.as_ref() { - let mut namespace = IRUProgram::new(); - module.lower(&mut namespace.push(), &mut ctx.output); + let mut program = IRUProgram::new(); + module.lower(&mut program, &mut ctx.output); if ctx.output.errs.is_empty() { // println!("vars:"); // for (id, def) in namespace.iter_vars() { @@ -51,10 +51,10 @@ fn run_file(file: &str, gdb: bool, asm: bool) { // for (id, f) in namespace.iter_fns() { // println!("{id:?} = {:#?}", f.unwrap()); // } - let output = namespace.validate(); + let output = program.validate(); output.write_for(&mut stdout(), file); if output.errs.is_empty() { - let program = IRLProgram::create(&namespace).expect("morir"); + let program = IRLProgram::create(&program).expect("morir"); let unlinked = compiler::compile(&program); if asm { println!("{:?}", unlinked); diff --git a/src/parser/v3/lower/arch/riscv64.rs b/src/parser/v3/lower/arch/riscv64.rs index f4b4401..d1864ce 100644 --- a/src/parser/v3/lower/arch/riscv64.rs +++ b/src/parser/v3/lower/arch/riscv64.rs @@ -11,7 +11,8 @@ impl RV64Instruction { pub fn parse(inst: &PInstruction, ctx: &mut FnLowerCtx) -> Option { let args = &inst.args[..]; let opstr = &**inst.op.inner.as_ref()?; - let opi = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3| -> Option { + // TODO: surely this can be abstracted... + let opi = |ctx: &mut FnLowerCtx<'_>, op: Funct3| -> Option { let [dest, src, imm] = args else { ctx.err(format!("{opstr} requires 3 arguments")); return None; @@ -21,7 +22,7 @@ impl RV64Instruction { let imm = i32_from_arg(imm, ctx)?; Some(Self::OpImm { op, dest, src, imm }) }; - let op = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3, funct: Funct7| -> Option { + let op = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option { let [dest, src1, src2] = args else { ctx.err(format!("{opstr} requires 3 arguments")); return None; @@ -37,7 +38,7 @@ impl RV64Instruction { src2, }) }; - let opif7 = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3, funct: Funct7| -> Option { + let opif7 = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option { let [dest, src, imm] = args else { ctx.err(format!("{opstr} requires 3 arguments")); return None; @@ -53,7 +54,7 @@ impl RV64Instruction { imm, }) }; - let store = |ctx: &mut FnLowerCtx<'_, '_>, width: Funct3| -> Option { + let store = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option { let [src, offset, base] = args else { ctx.err(format!("{opstr} requires 3 arguments")); return None; @@ -68,7 +69,7 @@ impl RV64Instruction { base, }) }; - let load = |ctx: &mut FnLowerCtx<'_, '_>, width: Funct3| -> Option { + let load = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option { let [dest, offset, base] = args else { ctx.err(format!("{opstr} requires 3 arguments")); return None; diff --git a/src/parser/v3/lower/block.rs b/src/parser/v3/lower/block.rs index b735f91..6f652c7 100644 --- a/src/parser/v3/lower/block.rs +++ b/src/parser/v3/lower/block.rs @@ -5,11 +5,13 @@ use super::{FnLowerCtx, FnLowerable, PBlock, PStatement}; impl FnLowerable for PBlock { type Output = VarInst; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { - let ctx = &mut ctx.sub(); + ctx.program.push(); for statement in &self.statements { statement.lower(ctx); } - self.result.as_ref()?.lower(ctx) + let res = self.result.as_ref().map(|r| r.lower(ctx)).flatten(); + ctx.program.pop(); + res } } @@ -18,10 +20,10 @@ impl FnLowerable for PStatement { fn lower(&self, ctx: &mut FnLowerCtx) -> Option { match self { super::PStatement::Let(def, e) => { - let def = def.lower(ctx.map, ctx.output)?; + let def = def.lower(ctx.program, ctx.output)?; let res = e.lower(ctx); if let Some(res) = res { - ctx.map.name_var(&def, res.id); + ctx.program.name_var(&def, res.id); } None } diff --git a/src/parser/v3/lower/def.rs b/src/parser/v3/lower/def.rs index 93eaf29..91022d9 100644 --- a/src/parser/v3/lower/def.rs +++ b/src/parser/v3/lower/def.rs @@ -1,17 +1,17 @@ -use crate::ir::{NamespaceGuard, Origin, Type, VarDef}; +use crate::ir::{IRUProgram, Origin, Type, VarDef}; use super::{CompilerMsg, CompilerOutput, FileSpan, Node, PType, PVarDef}; impl Node { pub fn lower( &self, - namespace: &mut NamespaceGuard, + program: &mut IRUProgram, output: &mut CompilerOutput, ) -> Option { let s = self.as_ref()?; let name = s.name.as_ref()?.to_string(); let ty = match &s.ty { - Some(ty) => ty.lower(namespace, output), + Some(ty) => ty.lower(program, output), None => Type::Infer, }; Some(VarDef { @@ -23,7 +23,7 @@ impl Node { } impl Node { - pub fn lower(&self, namespace: &mut NamespaceGuard, output: &mut CompilerOutput) -> Type { + pub fn lower(&self, namespace: &mut IRUProgram, output: &mut CompilerOutput) -> Type { self.as_ref() .map(|t| t.lower(namespace, output, self.span)) .unwrap_or(Type::Error) @@ -33,7 +33,7 @@ impl Node { impl PType { pub fn lower( &self, - namespace: &mut NamespaceGuard, + namespace: &mut IRUProgram, output: &mut CompilerOutput, span: FileSpan, ) -> Type { diff --git a/src/parser/v3/lower/expr.rs b/src/parser/v3/lower/expr.rs index 6d2c85f..07d79c8 100644 --- a/src/parser/v3/lower/expr.rs +++ b/src/parser/v3/lower/expr.rs @@ -10,9 +10,9 @@ 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(8).slice()); + let dest = ctx.program.temp_var(l.span, Type::Bits(8).slice()); let data = s.as_bytes().to_vec(); - let src = ctx.map.def_data( + let src = ctx.program.def_data( DataDef { ty: Type::Bits(8).arr(data.len() as u32), origin: Origin::File(l.span), @@ -25,8 +25,8 @@ impl FnLowerable for PExpr { } super::PLiteral::Char(c) => { let ty = Type::Bits(8); - let dest = ctx.map.temp_var(l.span, ty.clone()); - let src = ctx.map.def_data( + let dest = ctx.program.temp_var(l.span, ty.clone()); + let src = ctx.program.def_data( DataDef { ty, origin: Origin::File(l.span), @@ -40,8 +40,8 @@ impl FnLowerable for PExpr { super::PLiteral::Number(n) => { // TODO: temp let ty = Type::Bits(64); - let dest = ctx.map.temp_var(l.span, Type::Bits(64)); - let src = ctx.map.def_data( + let dest = ctx.program.temp_var(l.span, Type::Bits(64)); + let src = ctx.program.def_data( DataDef { ty, origin: Origin::File(l.span), @@ -60,12 +60,15 @@ impl FnLowerable for PExpr { PExpr::BinaryOp(op, e1, e2) => { let res1 = e1.lower(ctx)?; if *op == PInfixOp::Access { - let sty = &ctx.map.get_var(res1.id).ty; + let sty = &ctx.program.get_var(res1.id).ty; let Type::Concrete(tid) = sty else { - ctx.err(format!("Type {:?} has no fields", ctx.map.type_name(sty))); + ctx.err(format!( + "Type {:?} has no fields", + ctx.program.type_name(sty) + )); return None; }; - let struc = ctx.map.get_struct(*tid); + let struc = ctx.program.get_struct(*tid); let Some(box PExpr::Ident(ident)) = &e2.inner else { ctx.err(format!("Field accesses must be identifiers",)); return None; @@ -84,14 +87,29 @@ impl FnLowerable for PExpr { temp } else { let res2 = e2.lower(ctx)?; - todo!() + match op { + PInfixOp::Add => todo!(), + PInfixOp::Sub => todo!(), + PInfixOp::Mul => todo!(), + PInfixOp::Div => todo!(), + PInfixOp::LessThan => todo!(), + PInfixOp::GreaterThan => todo!(), + PInfixOp::Access => todo!(), + PInfixOp::Assign => { + ctx.push(IRUInstruction::Mv { + dest: res1, + src: res2, + }); + res1 + } + } } } PExpr::UnaryOp(op, e) => { let res = e.lower(ctx)?; match op { UnaryOp::Ref => { - let temp = ctx.temp(ctx.map.get_var(res.id).ty.clone().rf()); + let temp = ctx.temp(ctx.program.get_var(res.id).ty.clone().rf()); ctx.push(IRUInstruction::Ref { dest: temp, src: res, @@ -99,11 +117,11 @@ impl FnLowerable for PExpr { temp } UnaryOp::Deref => { - let t = &ctx.map.get_var(res.id).ty; + let t = &ctx.program.get_var(res.id).ty; let Type::Ref(inner) = t else { ctx.err(format!( "Cannot dereference type {:?}", - ctx.map.type_name(t) + ctx.program.type_name(t) )); return None; }; @@ -124,7 +142,7 @@ impl FnLowerable for PExpr { let arg = arg.lower(ctx)?; nargs.push(arg); } - let def = ctx.map.get_fn_var(fe.id); + let def = ctx.program.get_fn_var(fe.id); let ty = match def { Some(def) => def.ret.clone(), None => { @@ -132,7 +150,7 @@ impl FnLowerable for PExpr { e.span, format!( "Expected function, found {}", - ctx.map.type_name(&ctx.map.get_var(fe.id).ty) + ctx.program.type_name(&ctx.program.get_var(fe.id).ty) ), ); Type::Error @@ -148,6 +166,29 @@ impl FnLowerable for PExpr { } PExpr::Group(e) => e.lower(ctx)?, PExpr::Construct(c) => c.lower(ctx)?, + PExpr::If(cond, body) => { + let cond = cond.lower(ctx)?; + ctx.program.push(); + let mut body_ctx = ctx.branch(); + body.lower(&mut body_ctx); + let body = body_ctx.instructions; + ctx.program.pop(); + ctx.push(IRUInstruction::If { cond, body }); + return None; + } + PExpr::Loop(body) => { + ctx.program.push(); + let mut body_ctx = ctx.branch(); + body.lower(&mut body_ctx); + let body = body_ctx.instructions; + ctx.program.pop(); + ctx.push(IRUInstruction::Loop { body }); + return None; + } + PExpr::Break => { + ctx.push(IRUInstruction::Break); + return None; + } }) } } diff --git a/src/parser/v3/lower/func.rs b/src/parser/v3/lower/func.rs index b6cb655..83efbd8 100644 --- a/src/parser/v3/lower/func.rs +++ b/src/parser/v3/lower/func.rs @@ -1,24 +1,20 @@ use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction}; use crate::{ ir::{ - FnDef, FnID, IRInstructions, IRUFunction, IRUInstruction, Idents, NamespaceGuard, Origin, - Type, VarDef, VarInst, + FnDef, FnID, IRUFunction, IRUInstrInst, IRUInstruction, IRUProgram, Idents, Origin, Type, + VarDef, VarInst, }, parser, }; impl Node { - pub fn lower_header( - &self, - map: &mut NamespaceGuard, - output: &mut CompilerOutput, - ) -> Option { + pub fn lower_header(&self, map: &mut IRUProgram, output: &mut CompilerOutput) -> Option { self.as_ref()?.lower_header(map, output) } pub fn lower_body( &self, id: FnID, - map: &mut NamespaceGuard, + map: &mut IRUProgram, output: &mut CompilerOutput, ) -> Option { Some(self.as_ref()?.lower_body(id, map, output)) @@ -26,11 +22,7 @@ impl Node { } impl PFunction { - pub fn lower_header( - &self, - map: &mut NamespaceGuard, - output: &mut CompilerOutput, - ) -> Option { + pub fn lower_header(&self, map: &mut IRUProgram, output: &mut CompilerOutput) -> Option { let header = self.header.as_ref()?; let name = header.name.as_ref()?; let args = header @@ -58,44 +50,43 @@ impl PFunction { pub fn lower_body( &self, id: FnID, - map: &mut NamespaceGuard, + map: &mut IRUProgram, output: &mut CompilerOutput, ) -> 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 mut ctx = FnLowerCtx { - instructions: &mut instructions, - map, + instructions: Vec::new(), + program: map, output, span: self.body.span, }; if let Some(src) = self.body.lower(&mut ctx) { - instructions.push(IRUInstruction::Ret { src }, src.span); + ctx.instructions.push(IRUInstrInst { + i: IRUInstruction::Ret { src }, + span: src.span, + }); + } + IRUFunction { + name: def.name.clone(), + args, + ret: def.ret, + instructions: ctx.instructions, } - IRUFunction::new(def.name.clone(), args, def.ret, instructions) } } -pub struct FnLowerCtx<'a, 'n> { - pub map: &'a mut NamespaceGuard<'n>, - pub instructions: &'a mut IRInstructions, +pub struct FnLowerCtx<'a> { + pub program: &'a mut IRUProgram, + pub instructions: Vec, pub output: &'a mut CompilerOutput, pub span: FileSpan, } -impl<'n> FnLowerCtx<'_, 'n> { - pub fn span<'b>(&'b mut self, span: FileSpan) -> FnLowerCtx<'b, 'n> { - FnLowerCtx { - map: self.map, - instructions: self.instructions, - output: self.output, - span, - } - } +impl FnLowerCtx<'_> { pub fn get(&mut self, node: &Node) -> Option { let name = node.inner.as_ref()?; - let res = self.map.get(name); + let res = self.program.get(name); if res.is_none() { self.err_at(node.span, format!("Identifier '{}' not found", name)); } @@ -124,18 +115,18 @@ impl<'n> FnLowerCtx<'_, 'n> { self.output.err(CompilerMsg::from_span(span, msg)) } pub fn temp(&mut self, ty: Type) -> VarInst { - self.map.temp_var(self.span, ty) + self.program.temp_var(self.span, ty) } pub fn push(&mut self, i: IRUInstruction) { - self.instructions.push(i, self.span); + self.instructions.push(IRUInstrInst { i, span: self.span }); } pub fn push_at(&mut self, i: IRUInstruction, span: FileSpan) { - self.instructions.push(i, span); + self.instructions.push(IRUInstrInst { i, span }); } - pub fn sub<'b>(&'b mut self) -> FnLowerCtx<'b, 'n> { + pub fn branch<'a>(&'a mut self) -> FnLowerCtx<'a> { FnLowerCtx { - map: self.map, - instructions: self.instructions, + program: self.program, + instructions: Vec::new(), output: self.output, span: self.span, } diff --git a/src/parser/v3/lower/mod.rs b/src/parser/v3/lower/mod.rs index bec9736..7aa67aa 100644 --- a/src/parser/v3/lower/mod.rs +++ b/src/parser/v3/lower/mod.rs @@ -19,7 +19,11 @@ pub trait FnLowerable { impl FnLowerable for Node { type Output = T::Output; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { - self.as_ref()?.lower(&mut ctx.span(self.span)) + let old_span = ctx.span; + ctx.span = self.span; + let res = self.as_ref()?.lower(ctx); + ctx.span = old_span; + res } } diff --git a/src/parser/v3/lower/module.rs b/src/parser/v3/lower/module.rs index 9fad98f..c9c5e4b 100644 --- a/src/parser/v3/lower/module.rs +++ b/src/parser/v3/lower/module.rs @@ -1,15 +1,15 @@ -use crate::ir::NamespaceGuard; +use crate::ir::IRUProgram; use super::{PModule, CompilerOutput}; impl PModule { - pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) { + pub fn lower(&self, p: &mut IRUProgram, output: &mut CompilerOutput) { for s in &self.structs { - s.lower(map, output); + s.lower(p, output); } let mut fns = Vec::new(); for f in &self.functions { - if let Some(id) = f.lower_header(map, output) { + if let Some(id) = f.lower_header(p, output) { fns.push(Some(id)); } else { fns.push(None) @@ -17,8 +17,8 @@ impl PModule { } for (f, id) in self.functions.iter().zip(fns) { if let Some(id) = id { - if let Some(res) = f.lower_body(id, map, output) { - map.write_fn(id, res); + if let Some(res) = f.lower_body(id, p, output) { + p.write_fn(id, res); } } } diff --git a/src/parser/v3/lower/struc.rs b/src/parser/v3/lower/struc.rs index a214c2b..ab6edb0 100644 --- a/src/parser/v3/lower/struc.rs +++ b/src/parser/v3/lower/struc.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::{ common::{CompilerMsg, CompilerOutput, FileSpan}, - ir::{IRUInstruction, NamespaceGuard, Origin, StructDef, StructField, VarInst}, + ir::{IRUInstruction, IRUProgram, Origin, StructDef, StructField, VarInst}, parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields}, }; @@ -11,7 +11,7 @@ use super::{FnLowerCtx, FnLowerable}; impl FnLowerable for PConstruct { type Output = VarInst; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { - let ty = self.name.lower(ctx.map, ctx.output); + let ty = self.name.lower(ctx.program, ctx.output); let fields = match &self.fields { PConstructFields::Named(nodes) => nodes .iter() @@ -41,7 +41,7 @@ impl FnLowerable for PConstruct { impl PStruct { pub fn lower( &self, - map: &mut NamespaceGuard, + p: &mut IRUProgram, output: &mut CompilerOutput, span: FileSpan, ) -> Option<()> { @@ -53,10 +53,10 @@ impl PStruct { let def = n.as_ref()?; let name = def.name.as_ref()?.to_string(); let tynode = def.ty.as_ref()?; - let ty = tynode.lower(map, output); - let size = map.size_of_type(&ty).unwrap_or_else(|| { + let ty = tynode.lower(p, output); + let size = p.size_of_type(&ty).unwrap_or_else(|| { output.err(CompilerMsg { - msg: format!("Size of type '{}' unknown", map.type_name(&ty)), + msg: format!("Size of type '{}' unknown", p.type_name(&ty)), spans: vec![tynode.span], }); 0 @@ -70,8 +70,8 @@ impl PStruct { .iter() .enumerate() .flat_map(|(i, n)| { - let ty = n.as_ref()?.lower(map, output, span); - let size = map.size_of_type(&ty)?; + let ty = n.as_ref()?.lower(p, output, span); + let size = p.size_of_type(&ty)?; let res = Some((format!("{i}"), StructField { ty, offset })); offset += size; res @@ -79,7 +79,7 @@ impl PStruct { .collect(), PStructFields::None => HashMap::new(), }; - map.def_type(StructDef { + p.def_type(StructDef { name: self.name.as_ref()?.to_string(), origin: Origin::File(span), size: offset, @@ -90,7 +90,7 @@ impl PStruct { } impl Node { - pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) { - self.as_ref().map(|i| i.lower(map, output, self.span)); + pub fn lower(&self, p: &mut IRUProgram, output: &mut CompilerOutput) { + self.as_ref().map(|i| i.lower(p, output, self.span)); } } diff --git a/src/parser/v3/nodes/asm_instr.rs b/src/parser/v3/nodes/asm_instr.rs index 4fdb04b..6573daf 100644 --- a/src/parser/v3/nodes/asm_instr.rs +++ b/src/parser/v3/nodes/asm_instr.rs @@ -1,4 +1,4 @@ -use super::{Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol, CompilerMsg}; +use super::{CompilerMsg, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol}; pub struct PInstruction { pub op: Node, @@ -36,7 +36,19 @@ impl Parsable for PAsmArg { return ParseResult::Ok(Self::Value(ident)); } - let next = ctx.expect_peek()?; + let mut next = ctx.expect_peek()?; + if next.is_symbol(Symbol::Minus) { + ctx.next(); + if let Some(mut ident) = ctx.maybe_parse::() { + // TODO: this is so messed up + if let Some(i) = ident.as_mut() { + i.0.insert(0, '-') + } + return ParseResult::Ok(Self::Value(ident)); + } + next = ctx.expect_peek()?; + } + if !next.is_symbol(Symbol::OpenCurly) { return ParseResult::Err(CompilerMsg::unexpected_token( next, diff --git a/src/parser/v3/nodes/expr.rs b/src/parser/v3/nodes/expr.rs index adb2d7b..d71494e 100644 --- a/src/parser/v3/nodes/expr.rs +++ b/src/parser/v3/nodes/expr.rs @@ -19,6 +19,9 @@ pub enum PExpr { Group(BoxNode), AsmBlock(Node), Construct(Node), + If(BoxNode, BoxNode), + Loop(BoxNode), + Break, } impl Parsable for PExpr { @@ -42,6 +45,18 @@ impl Parsable for PExpr { Self::Group(res.node.bx()) } else if next.is_symbol(Symbol::OpenCurly) { Self::Block(PBlock::parse_node(ctx)?) + } else if next.is_keyword(Keyword::If) { + ctx.next(); + let cond = ctx.parse()?.bx(); + let body = ctx.parse()?.bx(); + Self::If(cond, body) + } else if next.is_keyword(Keyword::Loop) { + ctx.next(); + let body = ctx.parse()?.bx(); + Self::Loop(body) + } else if next.is_keyword(Keyword::Break) { + ctx.next(); + Self::Break } else if next.is_keyword(Keyword::Asm) { ctx.next(); Self::AsmBlock(ctx.parse()?) @@ -143,14 +158,13 @@ impl Debug for PExpr { } f.write_char(')')?; } - PExpr::UnaryOp(op, e) => { - write!(f, "(")?; - write!(f, "{}", op.str())?; - write!(f, "{:?})", *e)?; - } + PExpr::UnaryOp(op, e) => write!(f, "({}{:?})", op.str(), e)?, PExpr::Group(inner) => inner.fmt(f)?, PExpr::AsmBlock(inner) => inner.fmt(f)?, PExpr::Construct(inner) => inner.fmt(f)?, + PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?, + PExpr::Loop(res) => write!(f, "loop -> {res:?}")?, + PExpr::Break => write!(f, "break")?, } Ok(()) } diff --git a/src/parser/v3/nodes/lit.rs b/src/parser/v3/nodes/lit.rs index dba05c1..bd3aaf3 100644 --- a/src/parser/v3/nodes/lit.rs +++ b/src/parser/v3/nodes/lit.rs @@ -37,7 +37,7 @@ impl MaybeParsable for PLiteral { if !first.is_ascii_digit() { return Ok(None); } - let (whole, ty) = parse_whole_num(&text); + let (whole, ty) = parse_whole_num(text); let mut num = PNumber { whole, decimal: None, diff --git a/src/parser/v3/token/keyword.rs b/src/parser/v3/token/keyword.rs index a0c696d..261aa76 100644 --- a/src/parser/v3/token/keyword.rs +++ b/src/parser/v3/token/keyword.rs @@ -4,6 +4,8 @@ pub enum Keyword { Let, If, Return, + Break, + Loop, Struct, Trait, Impl, @@ -21,6 +23,8 @@ impl Keyword { "if" => Self::If, "for" => Self::For, "return" => Self::Return, + "break" => Self::Break, + "loop" => Self::Loop, "trait" => Self::Trait, "impl" => Self::Impl, "asm" => Self::Asm,