BRANCHING (TURING COMPLETE????)
This commit is contained in:
@@ -7,7 +7,20 @@ fn start() {
|
|||||||
println("Helld!");
|
println("Helld!");
|
||||||
println("Hello World!!!!!");
|
println("Hello World!!!!!");
|
||||||
thinger();
|
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());
|
print(tester());
|
||||||
let test = Test {
|
let test = Test {
|
||||||
a: 10,
|
a: 10,
|
||||||
@@ -77,6 +90,27 @@ fn sub(a: 64, b: 64) -> 64 {
|
|||||||
c
|
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>) {
|
fn arger(a: slice<8>, b: slice<8>, c: slice<8>) {
|
||||||
print(a);
|
print(a);
|
||||||
print(b);
|
print(b);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
compiler::program::{Addr, Instr, SymTable}, ir::Symbol, util::LabeledFmt
|
compiler::program::{Addr, Instr, SymTable},
|
||||||
|
ir::Symbol,
|
||||||
|
util::LabeledFmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -52,6 +54,12 @@ pub enum LinkerInstruction<R = Reg, S = Symbol> {
|
|||||||
},
|
},
|
||||||
Call(S),
|
Call(S),
|
||||||
J(S),
|
J(S),
|
||||||
|
Branch {
|
||||||
|
to: S,
|
||||||
|
typ: Funct3,
|
||||||
|
left: R,
|
||||||
|
right: R,
|
||||||
|
},
|
||||||
Ret,
|
Ret,
|
||||||
ECall,
|
ECall,
|
||||||
EBreak,
|
EBreak,
|
||||||
@@ -66,10 +74,10 @@ pub fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Instr for LinkerInstruction {
|
impl Instr for LinkerInstruction {
|
||||||
fn push(
|
fn push_to(
|
||||||
&self,
|
&self,
|
||||||
data: &mut Vec<u8>,
|
data: &mut Vec<u8>,
|
||||||
sym_map: &SymTable,
|
sym_map: &mut SymTable,
|
||||||
pos: Addr,
|
pos: Addr,
|
||||||
missing: bool,
|
missing: bool,
|
||||||
) -> Option<Symbol> {
|
) -> Option<Symbol> {
|
||||||
@@ -135,6 +143,20 @@ impl Instr for LinkerInstruction {
|
|||||||
Self::ECall => ecall(),
|
Self::ECall => ecall(),
|
||||||
Self::EBreak => ebreak(),
|
Self::EBreak => ebreak(),
|
||||||
Self::Li { dest, imm } => addi(*dest, zero, BitsI32::new(*imm)),
|
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());
|
data.extend(last.to_le_bytes());
|
||||||
None
|
None
|
||||||
@@ -181,7 +203,11 @@ pub struct DebugInstr<'a, R, S, L: Fn(&mut std::fmt::Formatter<'_>, &S) -> std::
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction<R, S> {
|
impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction<R, S> {
|
||||||
fn fmt_label(&self, f: &mut std::fmt::Formatter<'_>, label: &dyn crate::util::Labeler<S>) -> std::fmt::Result {
|
fn fmt_label(
|
||||||
|
&self,
|
||||||
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
label: &dyn crate::util::Labeler<S>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::ECall => write!(f, "ecall"),
|
Self::ECall => write!(f, "ecall"),
|
||||||
Self::EBreak => write!(f, "ebreak"),
|
Self::EBreak => write!(f, "ebreak"),
|
||||||
@@ -190,7 +216,7 @@ impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction
|
|||||||
Self::La { dest, src } => {
|
Self::La { dest, src } => {
|
||||||
write!(f, "la {dest:?}, @")?;
|
write!(f, "la {dest:?}, @")?;
|
||||||
label(f, src)
|
label(f, src)
|
||||||
},
|
}
|
||||||
Self::Load {
|
Self::Load {
|
||||||
width,
|
width,
|
||||||
dest,
|
dest,
|
||||||
@@ -229,6 +255,12 @@ impl<R: std::fmt::Debug, S: std::fmt::Debug> LabeledFmt<S> for LinkerInstruction
|
|||||||
write!(f, "j ")?;
|
write!(f, "j ")?;
|
||||||
label(f, s)
|
label(f, s)
|
||||||
}
|
}
|
||||||
|
Self::Branch {
|
||||||
|
to,
|
||||||
|
typ,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
} => write!(f, "b{} {left:?} {right:?} {to:?}", branch::str(*typ)),
|
||||||
Self::Ret => write!(f, "ret"),
|
Self::Ret => write!(f, "ret"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedProgram},
|
compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedFunction, UnlinkedProgram},
|
||||||
ir::{
|
ir::{
|
||||||
arch::riscv64::{RV64Instruction as AI, RegRef},
|
arch::riscv64::{RV64Instruction as AI, RegRef},
|
||||||
IRLInstruction as IRI, IRLProgram, Len, Size,
|
IRLInstruction as IRI, IRLProgram, Len, Size,
|
||||||
@@ -84,6 +84,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
v.push(LI::sd(ra, stack_ra, sp));
|
v.push(LI::sd(ra, stack_ra, sp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut locations = HashMap::new();
|
||||||
let mut irli = Vec::new();
|
let mut irli = Vec::new();
|
||||||
for i in &f.instructions {
|
for i in &f.instructions {
|
||||||
irli.push((v.len(), format!("{i:?}")));
|
irli.push((v.len(), format!("{i:?}")));
|
||||||
@@ -147,7 +148,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
}
|
}
|
||||||
fn r(rr: RegRef) -> Reg {
|
fn r(rr: RegRef) -> Reg {
|
||||||
match rr {
|
match rr {
|
||||||
RegRef::Var(var_ident) => todo!(),
|
RegRef::Var(..) => todo!(),
|
||||||
RegRef::Reg(reg) => reg,
|
RegRef::Reg(reg) => reg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +161,7 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
dest: r(dest),
|
dest: r(dest),
|
||||||
src: r(src),
|
src: r(src),
|
||||||
}),
|
}),
|
||||||
AI::La { dest, src } => todo!(),
|
AI::La { .. } => todo!(),
|
||||||
AI::Load {
|
AI::Load {
|
||||||
width,
|
width,
|
||||||
dest,
|
dest,
|
||||||
@@ -216,9 +217,10 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
imm,
|
imm,
|
||||||
}),
|
}),
|
||||||
AI::Ret => v.push(LI::Ret),
|
AI::Ret => v.push(LI::Ret),
|
||||||
AI::Call(s) => todo!(),
|
AI::Call(..) => todo!(),
|
||||||
AI::Jal { dest, offset } => todo!(),
|
AI::Jal { .. } => todo!(),
|
||||||
AI::J(s) => todo!(),
|
AI::J(..) => todo!(),
|
||||||
|
AI::Branch { .. } => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,6 +231,21 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
v.push(LI::ld(t0, rva, sp));
|
v.push(LI::ld(t0, rva, sp));
|
||||||
mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32);
|
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);
|
dbg.push_fn(irli);
|
||||||
@@ -239,12 +256,17 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
|
|||||||
v.push(LI::addi(sp, sp, stack_len));
|
v.push(LI::addi(sp, sp, stack_len));
|
||||||
}
|
}
|
||||||
v.push(LI::Ret);
|
v.push(LI::Ret);
|
||||||
fns.push((v, *sym));
|
fns.push(UnlinkedFunction {
|
||||||
|
instrs: v,
|
||||||
|
sym: *sym,
|
||||||
|
locations,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
UnlinkedProgram {
|
UnlinkedProgram {
|
||||||
fns: fns.into_iter().map(|(v, s, ..)| (v, s)).collect(),
|
fns,
|
||||||
ro_data: data,
|
ro_data: data,
|
||||||
start: Some(program.entry()),
|
start: Some(program.entry()),
|
||||||
dbg,
|
dbg,
|
||||||
|
sym_count: program.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ pub const IMM_OP: u32 = 0b0010011;
|
|||||||
pub const OP: u32 = 0b0110011;
|
pub const OP: u32 = 0b0110011;
|
||||||
pub const JAL: u32 = 0b1101111;
|
pub const JAL: u32 = 0b1101111;
|
||||||
pub const JALR: u32 = 0b1100111;
|
pub const JALR: u32 = 0b1100111;
|
||||||
|
pub const BRANCH: u32 = 0b1100011;
|
||||||
|
|
||||||
pub type Funct3 = Bits32<2, 0>;
|
pub type Funct3 = Bits32<2, 0>;
|
||||||
pub type Funct7 = Bits32<6, 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)
|
+ (imm.bits(10, 5) << 25)
|
||||||
+ (rs2.val() << 20)
|
+ (rs2.val() << 20)
|
||||||
+ (rs1.val() << 15)
|
+ (rs1.val() << 15)
|
||||||
+ (funct3.val() << 8)
|
+ (funct3.val() << 12)
|
||||||
+ (imm.bits(4, 1) << 8)
|
+ (imm.bits(4, 1) << 8)
|
||||||
+ (imm.bit(11) << 7)
|
+ (imm.bit(11) << 7)
|
||||||
+ opcode)
|
+ opcode)
|
||||||
|
|||||||
@@ -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 {
|
pub const fn ecall() -> RawInstruction {
|
||||||
i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM)
|
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 {
|
pub const fn ret() -> RawInstruction {
|
||||||
jalr(zero, BitsI32::new(0), ra)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,40 +13,54 @@ pub struct LinkedProgram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct UnlinkedProgram<I: Instr> {
|
pub struct UnlinkedProgram<I: Instr> {
|
||||||
pub fns: Vec<(Vec<I>, Symbol)>,
|
pub fns: Vec<UnlinkedFunction<I>>,
|
||||||
pub ro_data: Vec<(Vec<u8>, Symbol)>,
|
pub ro_data: Vec<(Vec<u8>, Symbol)>,
|
||||||
|
pub sym_count: usize,
|
||||||
pub start: Option<Symbol>,
|
pub start: Option<Symbol>,
|
||||||
pub dbg: DebugInfo,
|
pub dbg: DebugInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct UnlinkedFunction<I: Instr> {
|
||||||
|
pub instrs: Vec<I>,
|
||||||
|
pub sym: Symbol,
|
||||||
|
pub locations: HashMap<usize, Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<I: Instr> UnlinkedProgram<I> {
|
impl<I: Instr> UnlinkedProgram<I> {
|
||||||
pub fn link(self) -> LinkedProgram {
|
pub fn link(self) -> LinkedProgram {
|
||||||
let mut data = Vec::new();
|
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::<Symbol, Vec<(Addr, I)>>::new();
|
let mut missing = HashMap::<Symbol, Vec<(Addr, I)>>::new();
|
||||||
for (val, id) in self.ro_data {
|
for (val, id) in self.ro_data {
|
||||||
sym_table.insert(id, Addr(data.len() as u64));
|
sym_table.insert(id, Addr(data.len() as u64));
|
||||||
data.extend(val);
|
data.extend(val);
|
||||||
}
|
}
|
||||||
data.resize(data.len() + (4 - data.len() % 4), 0);
|
data.resize(data.len() + (4 - data.len() % 4), 0);
|
||||||
for (fun, id) in self.fns {
|
for f in self.fns {
|
||||||
sym_table.insert(id, Addr(data.len() as u64));
|
let mut added = vec![f.sym];
|
||||||
for i in fun {
|
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);
|
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) {
|
if let Some(vec) = missing.get_mut(&sym) {
|
||||||
vec.push((i_pos, i));
|
vec.push((i_pos, instr));
|
||||||
} else {
|
} else {
|
||||||
missing.insert(sym, vec![(i_pos, i)]);
|
missing.insert(sym, vec![(i_pos, instr)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(vec) = missing.remove(&id) {
|
for add in added {
|
||||||
for (addr, i) in vec {
|
if let Some(vec) = missing.remove(&add) {
|
||||||
let mut replace = Vec::new();
|
for (addr, i) in vec {
|
||||||
i.push(&mut replace, &sym_table, addr, true);
|
let mut replace = Vec::new();
|
||||||
let pos = addr.val() as usize;
|
i.push_to(&mut replace, &mut sym_table, addr, true);
|
||||||
data[pos..pos + replace.len()].copy_from_slice(&replace);
|
let pos = addr.val() as usize;
|
||||||
|
data[pos..pos + replace.len()].copy_from_slice(&replace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,8 +75,13 @@ impl<I: Instr> UnlinkedProgram<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instr {
|
pub trait Instr {
|
||||||
fn push(&self, data: &mut Vec<u8>, syms: &SymTable, pos: Addr, missing: bool)
|
fn push_to(
|
||||||
-> Option<Symbol>;
|
&self,
|
||||||
|
data: &mut Vec<u8>,
|
||||||
|
syms: &mut SymTable,
|
||||||
|
pos: Addr,
|
||||||
|
missing: bool,
|
||||||
|
) -> Option<Symbol>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
@@ -92,16 +111,16 @@ impl SymTable {
|
|||||||
|
|
||||||
impl<I: Instr + Labelable<Symbol> + LabeledFmt<Symbol>> std::fmt::Debug for UnlinkedProgram<I> {
|
impl<I: Instr + Labelable<Symbol> + LabeledFmt<Symbol>> std::fmt::Debug for UnlinkedProgram<I> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
for ((v, s), irli) in self.fns.iter().zip(&self.dbg.ir_lower) {
|
for (fun, irli) in self.fns.iter().zip(&self.dbg.ir_lower) {
|
||||||
writeln!(f, "{}:", self.dbg.sym_label(*s).unwrap())?;
|
writeln!(f, "{}:", self.dbg.sym_label(fun.sym).unwrap())?;
|
||||||
let mut liter = irli.iter();
|
let mut liter = irli.iter();
|
||||||
let mut cur = liter.next();
|
let mut cur = liter.next();
|
||||||
for (i, instr) in v.iter().enumerate() {
|
for (i, instr) in fun.instrs.iter().enumerate() {
|
||||||
if let Some(c) = cur {
|
while let Some(c) = cur
|
||||||
if i == c.0 {
|
&& i == c.0
|
||||||
writeln!(f, " {}:", c.1)?;
|
{
|
||||||
cur = liter.next();
|
writeln!(f, " {}:", c.1)?;
|
||||||
}
|
cur = liter.next();
|
||||||
}
|
}
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
|
|||||||
@@ -47,4 +47,12 @@ pub enum IRLInstruction {
|
|||||||
Ret {
|
Ret {
|
||||||
src: VarID,
|
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),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use std::collections::HashMap;
|
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 {
|
pub struct IRLProgram {
|
||||||
sym_space: SymbolSpace,
|
sym_space: SymbolSpace,
|
||||||
@@ -20,179 +23,18 @@ impl IRLProgram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let start = start.ok_or("no start method found")?;
|
let start = start.ok_or("no start method found")?;
|
||||||
let mut builder = SymbolSpace::with_entries(&[start]);
|
let mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]);
|
||||||
let entry = builder.func(&start);
|
let entry = ssbuilder.func(&start);
|
||||||
while let Some((sym, i)) = builder.pop_fn() {
|
while let Some((sym, i)) = ssbuilder.pop_fn() {
|
||||||
let f = p.fns[i.0].as_ref().unwrap();
|
let f = p.fns[i.0].as_ref().unwrap();
|
||||||
let mut instrs = Vec::new();
|
let mut fbuilder = IRLFunctionBuilder::new(p, &mut ssbuilder);
|
||||||
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
|
|
||||||
};
|
|
||||||
for i in &f.instructions {
|
for i in &f.instructions {
|
||||||
match &i.i {
|
fbuilder.insert_instr(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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
builder.write_fn(
|
let res = fbuilder.finish(f);
|
||||||
sym,
|
ssbuilder.write_fn(sym, res, Some(f.name.clone()));
|
||||||
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 sym_space = builder.finish().expect("we failed the mission");
|
let sym_space = ssbuilder.finish().expect("we failed the mission");
|
||||||
Ok(Self { sym_space, entry })
|
Ok(Self { sym_space, entry })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +43,220 @@ impl IRLProgram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct IRLFunctionBuilder<'a> {
|
||||||
|
program: &'a IRUProgram,
|
||||||
|
builder: &'a mut SymbolSpaceBuilder,
|
||||||
|
instrs: Vec<IRLInstruction>,
|
||||||
|
stack: HashMap<VarID, Size>,
|
||||||
|
makes_call: bool,
|
||||||
|
outer: Option<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Option<String>> {
|
||||||
|
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 {
|
impl std::ops::Deref for IRLProgram {
|
||||||
type Target = SymbolSpace;
|
type Target = SymbolSpace;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ impl std::ops::Deref for WritableSymbol {
|
|||||||
pub struct SymbolSpace {
|
pub struct SymbolSpace {
|
||||||
ro_data: Vec<(Symbol, Vec<u8>)>,
|
ro_data: Vec<(Symbol, Vec<u8>)>,
|
||||||
fns: Vec<(Symbol, IRLFunction)>,
|
fns: Vec<(Symbol, IRLFunction)>,
|
||||||
|
len: usize,
|
||||||
labels: Vec<Option<String>>,
|
labels: Vec<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,21 +33,6 @@ pub struct SymbolSpaceBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolSpace {
|
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<u8>)] {
|
pub fn ro_data(&self) -> &[(Symbol, Vec<u8>)] {
|
||||||
&self.ro_data
|
&self.ro_data
|
||||||
}
|
}
|
||||||
@@ -56,9 +42,30 @@ impl SymbolSpace {
|
|||||||
pub fn labels(&self) -> &[Option<String>] {
|
pub fn labels(&self) -> &[Option<String>] {
|
||||||
&self.labels
|
&self.labels
|
||||||
}
|
}
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolSpaceBuilder {
|
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)> {
|
pub fn pop_fn(&mut self) -> Option<(WritableSymbol, FnID)> {
|
||||||
self.unwritten_fns.pop()
|
self.unwritten_fns.pop()
|
||||||
}
|
}
|
||||||
@@ -94,7 +101,6 @@ impl SymbolSpaceBuilder {
|
|||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
) -> Symbol {
|
) -> Symbol {
|
||||||
let data = data.into();
|
|
||||||
self.ro_data.push((*sym, data));
|
self.ro_data.push((*sym, data));
|
||||||
self.labels[sym.0 .0] = name;
|
self.labels[sym.0 .0] = name;
|
||||||
*sym
|
*sym
|
||||||
@@ -116,11 +122,12 @@ impl SymbolSpaceBuilder {
|
|||||||
WritableSymbol(Symbol(val))
|
WritableSymbol(Symbol(val))
|
||||||
}
|
}
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.fns.len() + self.ro_data.len()
|
self.symbols
|
||||||
}
|
}
|
||||||
pub fn finish(self) -> Option<SymbolSpace> {
|
pub fn finish(self) -> Option<SymbolSpace> {
|
||||||
if self.unwritten_fns.is_empty() {
|
if self.unwritten_fns.is_empty() {
|
||||||
Some(SymbolSpace {
|
Some(SymbolSpace {
|
||||||
|
len: self.symbols,
|
||||||
fns: self.fns,
|
fns: self.fns,
|
||||||
ro_data: self.ro_data,
|
ro_data: self.ro_data,
|
||||||
labels: self.labels,
|
labels: self.labels,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Write};
|
|||||||
use super::{
|
use super::{
|
||||||
arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, Type, VarID,
|
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 struct IRUFunction {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -54,50 +54,64 @@ pub enum IRUInstruction {
|
|||||||
dest: VarInst,
|
dest: VarInst,
|
||||||
fields: HashMap<String, VarInst>,
|
fields: HashMap<String, VarInst>,
|
||||||
},
|
},
|
||||||
}
|
If {
|
||||||
|
cond: VarInst,
|
||||||
pub struct IRInstructions {
|
body: Vec<IRUInstrInst>,
|
||||||
vec: Vec<IRUInstrInst>,
|
},
|
||||||
}
|
Loop {
|
||||||
|
body: Vec<IRUInstrInst>,
|
||||||
impl IRUFunction {
|
},
|
||||||
pub fn new(name: String, args: Vec<VarID>, ret: Type, instructions: IRInstructions) -> Self {
|
Break,
|
||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for IRUInstruction {
|
impl std::fmt::Debug for IRUInstruction {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Mv { dest, src } => write!(f, "{dest:?} <- {src:?}"),
|
Self::Mv { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||||
Self::Ref { dest, src } => write!(f, "{dest:?} <- &{src:?}"),
|
Self::Ref { dest, src } => write!(f, "{dest:?} <- &{src:?}")?,
|
||||||
Self::LoadData { dest, src } => write!(f, "{dest:?} <- {src:?}"),
|
Self::LoadData { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||||
Self::LoadFn { dest, src } => write!(f, "{dest:?} <- {src:?}"),
|
Self::LoadFn { dest, src } => write!(f, "{dest:?} <- {src:?}")?,
|
||||||
Self::LoadSlice { dest, src } => write!(f, "{dest:?} <- &[{src:?}]"),
|
Self::LoadSlice { dest, src } => write!(f, "{dest:?} <- &[{src:?}]")?,
|
||||||
Self::Call {
|
Self::Call {
|
||||||
dest,
|
dest,
|
||||||
f: func,
|
f: func,
|
||||||
args,
|
args,
|
||||||
} => write!(f, "{dest:?} <- {func:?}({args:?})"),
|
} => write!(f, "{dest:?} <- {func:?}({args:?})")?,
|
||||||
Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}"),
|
Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}")?,
|
||||||
Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish(),
|
Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?,
|
||||||
Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}"),
|
Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}")?,
|
||||||
Self::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}"),
|
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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::FileSpan;
|
use crate::common::FileSpan;
|
||||||
@@ -34,9 +33,11 @@ impl IRUProgram {
|
|||||||
stack: vec![HashMap::new()],
|
stack: vec![HashMap::new()],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn push(&mut self) -> NamespaceGuard {
|
pub fn push(&mut self) {
|
||||||
self.stack.push(HashMap::new());
|
self.stack.push(HashMap::new());
|
||||||
NamespaceGuard(self)
|
}
|
||||||
|
pub fn pop(&mut self) {
|
||||||
|
self.stack.pop();
|
||||||
}
|
}
|
||||||
pub fn get(&self, name: &str) -> Option<Idents> {
|
pub fn get(&self, name: &str) -> Option<Idents> {
|
||||||
for map in self.stack.iter().rev() {
|
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Ident {
|
pub enum Ident {
|
||||||
Var(VarID),
|
Var(VarID),
|
||||||
|
|||||||
@@ -1,114 +1,140 @@
|
|||||||
// TODO: move this into ir, not parser
|
// TODO: move this into ir, not parser
|
||||||
use super::{IRUInstruction, IRUProgram, Type};
|
use super::{IRUInstrInst, IRUInstruction, IRUProgram, Type};
|
||||||
use crate::common::{CompilerMsg, CompilerOutput};
|
use crate::common::{CompilerMsg, CompilerOutput};
|
||||||
|
|
||||||
impl IRUProgram {
|
impl IRUProgram {
|
||||||
pub fn validate(&self) -> CompilerOutput {
|
pub fn validate(&self) -> CompilerOutput {
|
||||||
let mut output = CompilerOutput::new();
|
let mut output = CompilerOutput::new();
|
||||||
for (f, fd) in self.fns.iter().flatten().zip(&self.fn_defs) {
|
for (f, fd) in self.fns.iter().flatten().zip(&self.fn_defs) {
|
||||||
for i in &f.instructions {
|
self.validate_fn(&f.instructions, &fd.ret, &mut output, false);
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
output
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ fn run_file(file: &str, gdb: bool, asm: bool) {
|
|||||||
// println!("Parsed:");
|
// println!("Parsed:");
|
||||||
// println!("{:#?}", res.node);
|
// println!("{:#?}", res.node);
|
||||||
if let Some(module) = res.node.as_ref() {
|
if let Some(module) = res.node.as_ref() {
|
||||||
let mut namespace = IRUProgram::new();
|
let mut program = IRUProgram::new();
|
||||||
module.lower(&mut namespace.push(), &mut ctx.output);
|
module.lower(&mut program, &mut ctx.output);
|
||||||
if ctx.output.errs.is_empty() {
|
if ctx.output.errs.is_empty() {
|
||||||
// println!("vars:");
|
// println!("vars:");
|
||||||
// for (id, def) in namespace.iter_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() {
|
// for (id, f) in namespace.iter_fns() {
|
||||||
// println!("{id:?} = {:#?}", f.unwrap());
|
// println!("{id:?} = {:#?}", f.unwrap());
|
||||||
// }
|
// }
|
||||||
let output = namespace.validate();
|
let output = program.validate();
|
||||||
output.write_for(&mut stdout(), file);
|
output.write_for(&mut stdout(), file);
|
||||||
if output.errs.is_empty() {
|
if output.errs.is_empty() {
|
||||||
let program = IRLProgram::create(&namespace).expect("morir");
|
let program = IRLProgram::create(&program).expect("morir");
|
||||||
let unlinked = compiler::compile(&program);
|
let unlinked = compiler::compile(&program);
|
||||||
if asm {
|
if asm {
|
||||||
println!("{:?}", unlinked);
|
println!("{:?}", unlinked);
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ impl RV64Instruction {
|
|||||||
pub fn parse(inst: &PInstruction, ctx: &mut FnLowerCtx) -> Option<Self> {
|
pub fn parse(inst: &PInstruction, ctx: &mut FnLowerCtx) -> Option<Self> {
|
||||||
let args = &inst.args[..];
|
let args = &inst.args[..];
|
||||||
let opstr = &**inst.op.inner.as_ref()?;
|
let opstr = &**inst.op.inner.as_ref()?;
|
||||||
let opi = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3| -> Option<Self> {
|
// TODO: surely this can be abstracted...
|
||||||
|
let opi = |ctx: &mut FnLowerCtx<'_>, op: Funct3| -> Option<Self> {
|
||||||
let [dest, src, imm] = args else {
|
let [dest, src, imm] = args else {
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||||
return None;
|
return None;
|
||||||
@@ -21,7 +22,7 @@ impl RV64Instruction {
|
|||||||
let imm = i32_from_arg(imm, ctx)?;
|
let imm = i32_from_arg(imm, ctx)?;
|
||||||
Some(Self::OpImm { op, dest, src, imm })
|
Some(Self::OpImm { op, dest, src, imm })
|
||||||
};
|
};
|
||||||
let op = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
let op = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
||||||
let [dest, src1, src2] = args else {
|
let [dest, src1, src2] = args else {
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||||
return None;
|
return None;
|
||||||
@@ -37,7 +38,7 @@ impl RV64Instruction {
|
|||||||
src2,
|
src2,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let opif7 = |ctx: &mut FnLowerCtx<'_, '_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
let opif7 = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option<Self> {
|
||||||
let [dest, src, imm] = args else {
|
let [dest, src, imm] = args else {
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||||
return None;
|
return None;
|
||||||
@@ -53,7 +54,7 @@ impl RV64Instruction {
|
|||||||
imm,
|
imm,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let store = |ctx: &mut FnLowerCtx<'_, '_>, width: Funct3| -> Option<Self> {
|
let store = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
|
||||||
let [src, offset, base] = args else {
|
let [src, offset, base] = args else {
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||||
return None;
|
return None;
|
||||||
@@ -68,7 +69,7 @@ impl RV64Instruction {
|
|||||||
base,
|
base,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let load = |ctx: &mut FnLowerCtx<'_, '_>, width: Funct3| -> Option<Self> {
|
let load = |ctx: &mut FnLowerCtx<'_>, width: Funct3| -> Option<Self> {
|
||||||
let [dest, offset, base] = args else {
|
let [dest, offset, base] = args else {
|
||||||
ctx.err(format!("{opstr} requires 3 arguments"));
|
ctx.err(format!("{opstr} requires 3 arguments"));
|
||||||
return None;
|
return None;
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ use super::{FnLowerCtx, FnLowerable, PBlock, PStatement};
|
|||||||
impl FnLowerable for PBlock {
|
impl FnLowerable for PBlock {
|
||||||
type Output = VarInst;
|
type Output = VarInst;
|
||||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
|
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
|
||||||
let ctx = &mut ctx.sub();
|
ctx.program.push();
|
||||||
for statement in &self.statements {
|
for statement in &self.statements {
|
||||||
statement.lower(ctx);
|
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<VarInst> {
|
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
|
||||||
match self {
|
match self {
|
||||||
super::PStatement::Let(def, e) => {
|
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);
|
let res = e.lower(ctx);
|
||||||
if let Some(res) = res {
|
if let Some(res) = res {
|
||||||
ctx.map.name_var(&def, res.id);
|
ctx.program.name_var(&def, res.id);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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};
|
use super::{CompilerMsg, CompilerOutput, FileSpan, Node, PType, PVarDef};
|
||||||
|
|
||||||
impl Node<PVarDef> {
|
impl Node<PVarDef> {
|
||||||
pub fn lower(
|
pub fn lower(
|
||||||
&self,
|
&self,
|
||||||
namespace: &mut NamespaceGuard,
|
program: &mut IRUProgram,
|
||||||
output: &mut CompilerOutput,
|
output: &mut CompilerOutput,
|
||||||
) -> Option<VarDef> {
|
) -> Option<VarDef> {
|
||||||
let s = self.as_ref()?;
|
let s = self.as_ref()?;
|
||||||
let name = s.name.as_ref()?.to_string();
|
let name = s.name.as_ref()?.to_string();
|
||||||
let ty = match &s.ty {
|
let ty = match &s.ty {
|
||||||
Some(ty) => ty.lower(namespace, output),
|
Some(ty) => ty.lower(program, output),
|
||||||
None => Type::Infer,
|
None => Type::Infer,
|
||||||
};
|
};
|
||||||
Some(VarDef {
|
Some(VarDef {
|
||||||
@@ -23,7 +23,7 @@ impl Node<PVarDef> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Node<PType> {
|
impl Node<PType> {
|
||||||
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()
|
self.as_ref()
|
||||||
.map(|t| t.lower(namespace, output, self.span))
|
.map(|t| t.lower(namespace, output, self.span))
|
||||||
.unwrap_or(Type::Error)
|
.unwrap_or(Type::Error)
|
||||||
@@ -33,7 +33,7 @@ impl Node<PType> {
|
|||||||
impl PType {
|
impl PType {
|
||||||
pub fn lower(
|
pub fn lower(
|
||||||
&self,
|
&self,
|
||||||
namespace: &mut NamespaceGuard,
|
namespace: &mut IRUProgram,
|
||||||
output: &mut CompilerOutput,
|
output: &mut CompilerOutput,
|
||||||
span: FileSpan,
|
span: FileSpan,
|
||||||
) -> Type {
|
) -> Type {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ impl FnLowerable for PExpr {
|
|||||||
Some(match self {
|
Some(match self {
|
||||||
PExpr::Lit(l) => match l.as_ref()? {
|
PExpr::Lit(l) => match l.as_ref()? {
|
||||||
super::PLiteral::String(s) => {
|
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 data = s.as_bytes().to_vec();
|
||||||
let src = ctx.map.def_data(
|
let src = ctx.program.def_data(
|
||||||
DataDef {
|
DataDef {
|
||||||
ty: Type::Bits(8).arr(data.len() as u32),
|
ty: Type::Bits(8).arr(data.len() as u32),
|
||||||
origin: Origin::File(l.span),
|
origin: Origin::File(l.span),
|
||||||
@@ -25,8 +25,8 @@ impl FnLowerable for PExpr {
|
|||||||
}
|
}
|
||||||
super::PLiteral::Char(c) => {
|
super::PLiteral::Char(c) => {
|
||||||
let ty = Type::Bits(8);
|
let ty = Type::Bits(8);
|
||||||
let dest = ctx.map.temp_var(l.span, ty.clone());
|
let dest = ctx.program.temp_var(l.span, ty.clone());
|
||||||
let src = ctx.map.def_data(
|
let src = ctx.program.def_data(
|
||||||
DataDef {
|
DataDef {
|
||||||
ty,
|
ty,
|
||||||
origin: Origin::File(l.span),
|
origin: Origin::File(l.span),
|
||||||
@@ -40,8 +40,8 @@ impl FnLowerable for PExpr {
|
|||||||
super::PLiteral::Number(n) => {
|
super::PLiteral::Number(n) => {
|
||||||
// TODO: temp
|
// TODO: temp
|
||||||
let ty = Type::Bits(64);
|
let ty = Type::Bits(64);
|
||||||
let dest = ctx.map.temp_var(l.span, Type::Bits(64));
|
let dest = ctx.program.temp_var(l.span, Type::Bits(64));
|
||||||
let src = ctx.map.def_data(
|
let src = ctx.program.def_data(
|
||||||
DataDef {
|
DataDef {
|
||||||
ty,
|
ty,
|
||||||
origin: Origin::File(l.span),
|
origin: Origin::File(l.span),
|
||||||
@@ -60,12 +60,15 @@ impl FnLowerable for PExpr {
|
|||||||
PExpr::BinaryOp(op, e1, e2) => {
|
PExpr::BinaryOp(op, e1, e2) => {
|
||||||
let res1 = e1.lower(ctx)?;
|
let res1 = e1.lower(ctx)?;
|
||||||
if *op == PInfixOp::Access {
|
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 {
|
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;
|
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 {
|
let Some(box PExpr::Ident(ident)) = &e2.inner else {
|
||||||
ctx.err(format!("Field accesses must be identifiers",));
|
ctx.err(format!("Field accesses must be identifiers",));
|
||||||
return None;
|
return None;
|
||||||
@@ -84,14 +87,29 @@ impl FnLowerable for PExpr {
|
|||||||
temp
|
temp
|
||||||
} else {
|
} else {
|
||||||
let res2 = e2.lower(ctx)?;
|
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) => {
|
PExpr::UnaryOp(op, e) => {
|
||||||
let res = e.lower(ctx)?;
|
let res = e.lower(ctx)?;
|
||||||
match op {
|
match op {
|
||||||
UnaryOp::Ref => {
|
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 {
|
ctx.push(IRUInstruction::Ref {
|
||||||
dest: temp,
|
dest: temp,
|
||||||
src: res,
|
src: res,
|
||||||
@@ -99,11 +117,11 @@ impl FnLowerable for PExpr {
|
|||||||
temp
|
temp
|
||||||
}
|
}
|
||||||
UnaryOp::Deref => {
|
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 {
|
let Type::Ref(inner) = t else {
|
||||||
ctx.err(format!(
|
ctx.err(format!(
|
||||||
"Cannot dereference type {:?}",
|
"Cannot dereference type {:?}",
|
||||||
ctx.map.type_name(t)
|
ctx.program.type_name(t)
|
||||||
));
|
));
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
@@ -124,7 +142,7 @@ impl FnLowerable for PExpr {
|
|||||||
let arg = arg.lower(ctx)?;
|
let arg = arg.lower(ctx)?;
|
||||||
nargs.push(arg);
|
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 {
|
let ty = match def {
|
||||||
Some(def) => def.ret.clone(),
|
Some(def) => def.ret.clone(),
|
||||||
None => {
|
None => {
|
||||||
@@ -132,7 +150,7 @@ impl FnLowerable for PExpr {
|
|||||||
e.span,
|
e.span,
|
||||||
format!(
|
format!(
|
||||||
"Expected function, found {}",
|
"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
|
Type::Error
|
||||||
@@ -148,6 +166,29 @@ impl FnLowerable for PExpr {
|
|||||||
}
|
}
|
||||||
PExpr::Group(e) => e.lower(ctx)?,
|
PExpr::Group(e) => e.lower(ctx)?,
|
||||||
PExpr::Construct(c) => c.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;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction};
|
use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction};
|
||||||
use crate::{
|
use crate::{
|
||||||
ir::{
|
ir::{
|
||||||
FnDef, FnID, IRInstructions, IRUFunction, IRUInstruction, Idents, NamespaceGuard, Origin,
|
FnDef, FnID, IRUFunction, IRUInstrInst, IRUInstruction, IRUProgram, Idents, Origin, Type,
|
||||||
Type, VarDef, VarInst,
|
VarDef, VarInst,
|
||||||
},
|
},
|
||||||
parser,
|
parser,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Node<PFunction> {
|
impl Node<PFunction> {
|
||||||
pub fn lower_header(
|
pub fn lower_header(&self, map: &mut IRUProgram, output: &mut CompilerOutput) -> Option<FnID> {
|
||||||
&self,
|
|
||||||
map: &mut NamespaceGuard,
|
|
||||||
output: &mut CompilerOutput,
|
|
||||||
) -> Option<FnID> {
|
|
||||||
self.as_ref()?.lower_header(map, output)
|
self.as_ref()?.lower_header(map, output)
|
||||||
}
|
}
|
||||||
pub fn lower_body(
|
pub fn lower_body(
|
||||||
&self,
|
&self,
|
||||||
id: FnID,
|
id: FnID,
|
||||||
map: &mut NamespaceGuard,
|
map: &mut IRUProgram,
|
||||||
output: &mut CompilerOutput,
|
output: &mut CompilerOutput,
|
||||||
) -> Option<IRUFunction> {
|
) -> Option<IRUFunction> {
|
||||||
Some(self.as_ref()?.lower_body(id, map, output))
|
Some(self.as_ref()?.lower_body(id, map, output))
|
||||||
@@ -26,11 +22,7 @@ impl Node<PFunction> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PFunction {
|
impl PFunction {
|
||||||
pub fn lower_header(
|
pub fn lower_header(&self, map: &mut IRUProgram, output: &mut CompilerOutput) -> Option<FnID> {
|
||||||
&self,
|
|
||||||
map: &mut NamespaceGuard,
|
|
||||||
output: &mut CompilerOutput,
|
|
||||||
) -> Option<FnID> {
|
|
||||||
let header = self.header.as_ref()?;
|
let header = self.header.as_ref()?;
|
||||||
let name = header.name.as_ref()?;
|
let name = header.name.as_ref()?;
|
||||||
let args = header
|
let args = header
|
||||||
@@ -58,44 +50,43 @@ impl PFunction {
|
|||||||
pub fn lower_body(
|
pub fn lower_body(
|
||||||
&self,
|
&self,
|
||||||
id: FnID,
|
id: FnID,
|
||||||
map: &mut NamespaceGuard,
|
map: &mut IRUProgram,
|
||||||
output: &mut CompilerOutput,
|
output: &mut CompilerOutput,
|
||||||
) -> IRUFunction {
|
) -> IRUFunction {
|
||||||
let mut instructions = IRInstructions::new();
|
|
||||||
let def = map.get_fn(id).clone();
|
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 {
|
let mut ctx = FnLowerCtx {
|
||||||
instructions: &mut instructions,
|
instructions: Vec::new(),
|
||||||
map,
|
program: map,
|
||||||
output,
|
output,
|
||||||
span: self.body.span,
|
span: self.body.span,
|
||||||
};
|
};
|
||||||
if let Some(src) = self.body.lower(&mut ctx) {
|
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 struct FnLowerCtx<'a> {
|
||||||
pub map: &'a mut NamespaceGuard<'n>,
|
pub program: &'a mut IRUProgram,
|
||||||
pub instructions: &'a mut IRInstructions,
|
pub instructions: Vec<IRUInstrInst>,
|
||||||
pub output: &'a mut CompilerOutput,
|
pub output: &'a mut CompilerOutput,
|
||||||
pub span: FileSpan,
|
pub span: FileSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n> FnLowerCtx<'_, 'n> {
|
impl FnLowerCtx<'_> {
|
||||||
pub fn span<'b>(&'b mut self, span: FileSpan) -> FnLowerCtx<'b, 'n> {
|
|
||||||
FnLowerCtx {
|
|
||||||
map: self.map,
|
|
||||||
instructions: self.instructions,
|
|
||||||
output: self.output,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get(&mut self, node: &Node<parser::PIdent>) -> Option<Idents> {
|
pub fn get(&mut self, node: &Node<parser::PIdent>) -> Option<Idents> {
|
||||||
let name = node.inner.as_ref()?;
|
let name = node.inner.as_ref()?;
|
||||||
let res = self.map.get(name);
|
let res = self.program.get(name);
|
||||||
if res.is_none() {
|
if res.is_none() {
|
||||||
self.err_at(node.span, format!("Identifier '{}' not found", name));
|
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))
|
self.output.err(CompilerMsg::from_span(span, msg))
|
||||||
}
|
}
|
||||||
pub fn temp(&mut self, ty: Type) -> VarInst {
|
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) {
|
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) {
|
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 {
|
FnLowerCtx {
|
||||||
map: self.map,
|
program: self.program,
|
||||||
instructions: self.instructions,
|
instructions: Vec::new(),
|
||||||
output: self.output,
|
output: self.output,
|
||||||
span: self.span,
|
span: self.span,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ pub trait FnLowerable {
|
|||||||
impl<T: FnLowerable> FnLowerable for Node<T> {
|
impl<T: FnLowerable> FnLowerable for Node<T> {
|
||||||
type Output = T::Output;
|
type Output = T::Output;
|
||||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
|
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use crate::ir::NamespaceGuard;
|
use crate::ir::IRUProgram;
|
||||||
|
|
||||||
use super::{PModule, CompilerOutput};
|
use super::{PModule, CompilerOutput};
|
||||||
|
|
||||||
impl PModule {
|
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 {
|
for s in &self.structs {
|
||||||
s.lower(map, output);
|
s.lower(p, output);
|
||||||
}
|
}
|
||||||
let mut fns = Vec::new();
|
let mut fns = Vec::new();
|
||||||
for f in &self.functions {
|
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));
|
fns.push(Some(id));
|
||||||
} else {
|
} else {
|
||||||
fns.push(None)
|
fns.push(None)
|
||||||
@@ -17,8 +17,8 @@ impl PModule {
|
|||||||
}
|
}
|
||||||
for (f, id) in self.functions.iter().zip(fns) {
|
for (f, id) in self.functions.iter().zip(fns) {
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
if let Some(res) = f.lower_body(id, map, output) {
|
if let Some(res) = f.lower_body(id, p, output) {
|
||||||
map.write_fn(id, res);
|
p.write_fn(id, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{CompilerMsg, CompilerOutput, FileSpan},
|
common::{CompilerMsg, CompilerOutput, FileSpan},
|
||||||
ir::{IRUInstruction, NamespaceGuard, Origin, StructDef, StructField, VarInst},
|
ir::{IRUInstruction, IRUProgram, Origin, StructDef, StructField, VarInst},
|
||||||
parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields},
|
parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ use super::{FnLowerCtx, FnLowerable};
|
|||||||
impl FnLowerable for PConstruct {
|
impl FnLowerable for PConstruct {
|
||||||
type Output = VarInst;
|
type Output = VarInst;
|
||||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
|
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
|
||||||
let ty = self.name.lower(ctx.map, ctx.output);
|
let ty = self.name.lower(ctx.program, ctx.output);
|
||||||
let fields = match &self.fields {
|
let fields = match &self.fields {
|
||||||
PConstructFields::Named(nodes) => nodes
|
PConstructFields::Named(nodes) => nodes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -41,7 +41,7 @@ impl FnLowerable for PConstruct {
|
|||||||
impl PStruct {
|
impl PStruct {
|
||||||
pub fn lower(
|
pub fn lower(
|
||||||
&self,
|
&self,
|
||||||
map: &mut NamespaceGuard,
|
p: &mut IRUProgram,
|
||||||
output: &mut CompilerOutput,
|
output: &mut CompilerOutput,
|
||||||
span: FileSpan,
|
span: FileSpan,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
@@ -53,10 +53,10 @@ impl PStruct {
|
|||||||
let def = n.as_ref()?;
|
let def = n.as_ref()?;
|
||||||
let name = def.name.as_ref()?.to_string();
|
let name = def.name.as_ref()?.to_string();
|
||||||
let tynode = def.ty.as_ref()?;
|
let tynode = def.ty.as_ref()?;
|
||||||
let ty = tynode.lower(map, output);
|
let ty = tynode.lower(p, output);
|
||||||
let size = map.size_of_type(&ty).unwrap_or_else(|| {
|
let size = p.size_of_type(&ty).unwrap_or_else(|| {
|
||||||
output.err(CompilerMsg {
|
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],
|
spans: vec![tynode.span],
|
||||||
});
|
});
|
||||||
0
|
0
|
||||||
@@ -70,8 +70,8 @@ impl PStruct {
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(i, n)| {
|
.flat_map(|(i, n)| {
|
||||||
let ty = n.as_ref()?.lower(map, output, span);
|
let ty = n.as_ref()?.lower(p, output, span);
|
||||||
let size = map.size_of_type(&ty)?;
|
let size = p.size_of_type(&ty)?;
|
||||||
let res = Some((format!("{i}"), StructField { ty, offset }));
|
let res = Some((format!("{i}"), StructField { ty, offset }));
|
||||||
offset += size;
|
offset += size;
|
||||||
res
|
res
|
||||||
@@ -79,7 +79,7 @@ impl PStruct {
|
|||||||
.collect(),
|
.collect(),
|
||||||
PStructFields::None => HashMap::new(),
|
PStructFields::None => HashMap::new(),
|
||||||
};
|
};
|
||||||
map.def_type(StructDef {
|
p.def_type(StructDef {
|
||||||
name: self.name.as_ref()?.to_string(),
|
name: self.name.as_ref()?.to_string(),
|
||||||
origin: Origin::File(span),
|
origin: Origin::File(span),
|
||||||
size: offset,
|
size: offset,
|
||||||
@@ -90,7 +90,7 @@ impl PStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Node<PStruct> {
|
impl Node<PStruct> {
|
||||||
pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) {
|
pub fn lower(&self, p: &mut IRUProgram, output: &mut CompilerOutput) {
|
||||||
self.as_ref().map(|i| i.lower(map, output, self.span));
|
self.as_ref().map(|i| i.lower(p, output, self.span));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 struct PInstruction {
|
||||||
pub op: Node<PIdent>,
|
pub op: Node<PIdent>,
|
||||||
@@ -36,7 +36,19 @@ impl Parsable for PAsmArg {
|
|||||||
return ParseResult::Ok(Self::Value(ident));
|
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::<PIdent>() {
|
||||||
|
// 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) {
|
if !next.is_symbol(Symbol::OpenCurly) {
|
||||||
return ParseResult::Err(CompilerMsg::unexpected_token(
|
return ParseResult::Err(CompilerMsg::unexpected_token(
|
||||||
next,
|
next,
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ pub enum PExpr {
|
|||||||
Group(BoxNode),
|
Group(BoxNode),
|
||||||
AsmBlock(Node<PAsmBlock>),
|
AsmBlock(Node<PAsmBlock>),
|
||||||
Construct(Node<PConstruct>),
|
Construct(Node<PConstruct>),
|
||||||
|
If(BoxNode, BoxNode),
|
||||||
|
Loop(BoxNode),
|
||||||
|
Break,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parsable for PExpr {
|
impl Parsable for PExpr {
|
||||||
@@ -42,6 +45,18 @@ impl Parsable for PExpr {
|
|||||||
Self::Group(res.node.bx())
|
Self::Group(res.node.bx())
|
||||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||||
Self::Block(PBlock::parse_node(ctx)?)
|
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) {
|
} else if next.is_keyword(Keyword::Asm) {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
Self::AsmBlock(ctx.parse()?)
|
Self::AsmBlock(ctx.parse()?)
|
||||||
@@ -143,14 +158,13 @@ impl Debug for PExpr {
|
|||||||
}
|
}
|
||||||
f.write_char(')')?;
|
f.write_char(')')?;
|
||||||
}
|
}
|
||||||
PExpr::UnaryOp(op, e) => {
|
PExpr::UnaryOp(op, e) => write!(f, "({}{:?})", op.str(), e)?,
|
||||||
write!(f, "(")?;
|
|
||||||
write!(f, "{}", op.str())?;
|
|
||||||
write!(f, "{:?})", *e)?;
|
|
||||||
}
|
|
||||||
PExpr::Group(inner) => inner.fmt(f)?,
|
PExpr::Group(inner) => inner.fmt(f)?,
|
||||||
PExpr::AsmBlock(inner) => inner.fmt(f)?,
|
PExpr::AsmBlock(inner) => inner.fmt(f)?,
|
||||||
PExpr::Construct(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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ impl MaybeParsable for PLiteral {
|
|||||||
if !first.is_ascii_digit() {
|
if !first.is_ascii_digit() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let (whole, ty) = parse_whole_num(&text);
|
let (whole, ty) = parse_whole_num(text);
|
||||||
let mut num = PNumber {
|
let mut num = PNumber {
|
||||||
whole,
|
whole,
|
||||||
decimal: None,
|
decimal: None,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ pub enum Keyword {
|
|||||||
Let,
|
Let,
|
||||||
If,
|
If,
|
||||||
Return,
|
Return,
|
||||||
|
Break,
|
||||||
|
Loop,
|
||||||
Struct,
|
Struct,
|
||||||
Trait,
|
Trait,
|
||||||
Impl,
|
Impl,
|
||||||
@@ -21,6 +23,8 @@ impl Keyword {
|
|||||||
"if" => Self::If,
|
"if" => Self::If,
|
||||||
"for" => Self::For,
|
"for" => Self::For,
|
||||||
"return" => Self::Return,
|
"return" => Self::Return,
|
||||||
|
"break" => Self::Break,
|
||||||
|
"loop" => Self::Loop,
|
||||||
"trait" => Self::Trait,
|
"trait" => Self::Trait,
|
||||||
"impl" => Self::Impl,
|
"impl" => Self::Impl,
|
||||||
"asm" => Self::Asm,
|
"asm" => Self::Asm,
|
||||||
|
|||||||
Reference in New Issue
Block a user