diff --git a/README.md b/README.md
new file mode 100644
index 0000000..15dd0d5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# the
+
+my child (programming language)
+
+everything is subject to change rn, and this probably isn't up to date
+
+`cargo run -- data/test.lang`
+
+currently working!!:
+- functions (arguments, returning)
+- assembly blocks (input, output for expression)
+- kind of structs (construction, field access, but not modifying lmao)
+
+todo:
+- actually handle jumps & LIs that are too large
+- iterators?
+- borrow checking
+- multiple var instances for struct fields / fix all that stuff
+- basic optimization: use registers, remove temp var moves
diff --git a/data/test.lang b/data/test.lang
index fb0ce53..1960e98 100644
--- a/data/test.lang
+++ b/data/test.lang
@@ -1,6 +1,7 @@
struct Test {
a: 64,
b: 64,
+ c: 64,
}
fn start() {
@@ -11,7 +12,7 @@ fn start() {
thinger();
let x = 3;
if not(not(lt(x, 5))) {
- println("tada!");
+ println("tada!");
};
println("before:");
x = 0;
@@ -26,11 +27,13 @@ fn start() {
print(tester());
let test = Test {
a: 10,
- b: 9,
+ b: 4,
+ c: 0,
};
+ structer(test);
arger("a", "b", "c");
let z = sub(test.a, 10);
- // exit(add(mul(sub(add(5, test.b), 1), 3), z));
+ print_hex(add(mul(sub(add(10, test.b), 1), 3), z));
print("test: 0x");
print_hex(31);
println("");
@@ -38,6 +41,17 @@ fn start() {
}
fn structer(test: Test) {
+ print("test {\n a: ");
+ print_dec(test.a);
+ print("\n b: ");
+ print_dec(test.b);
+ print("\n c: ");
+ print_dec(test.c);
+ println("\n}");
+ print("update c: ");
+ test.c = add(test.a, test.b);
+ print_dec(test.c);
+ println("");
}
fn thinger() {
@@ -64,7 +78,7 @@ fn print(msg: slice<8>) {
}
fn print_hex(x: 64) {
- let i = 32;
+ let i = 64;
loop {
i = sub(i, 4);
let c = and(shr(x, i), 15);
@@ -84,6 +98,45 @@ fn print_hex(x: 64) {
}
}
+fn print_dec(x: 64) {
+ let i = 1;
+ loop {
+ if gt(i, x) {
+ if lt(i, 2) {
+ print("0");
+ return;
+ };
+ break;
+ };
+ i = mul(i, 10);
+ };
+ let found = 0;
+ loop {
+ i = div(i, 10);
+ let c = rem(div(x, i), 10);
+ if and(lt(c, 1), not(found)) {
+ continue;
+ };
+ found = 1;
+ if gt(c, 9) {
+ c = add(c, 7);
+ };
+ c = add(c, 48);
+ asm (a1 = &c) {
+ li a2, 1
+ li a0, 1
+ li a7, 64
+ ecall
+ };
+ if lt(i, 2) {
+ break;
+ };
+ };
+ if not(found) {
+ print("0");
+ }
+}
+
fn add(a: 64, b: 64) -> 64 {
asm (t0 = a, t1 = b, out = t0) {
add t0, t0, t1
diff --git a/src/compiler/arch/riscv/compile.rs b/src/compiler/arch/riscv/compile.rs
index 33318ff..d14f19c 100644
--- a/src/compiler/arch/riscv/compile.rs
+++ b/src/compiler/arch/riscv/compile.rs
@@ -78,6 +78,10 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
{
stack_len += align(&f.ret_size);
}
v.push(LI::addi(sp, sp, -stack_len));
+ for (id, var) in &f.subvar_map {
+ // TODO: ALIGN DOES NOT MAKE SENSE HERE!!! need to choose to decide in lower or asm
+ stack.insert(id, stack[&var.id] + align(&var.offset));
+ }
let has_stack = stack_len > 0;
if has_stack {
if let Some(stack_ra) = stack_ra {
@@ -86,6 +90,15 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
{
}
let mut locations = HashMap::new();
let mut irli = Vec::new();
+ let mut ret = Vec::new();
+ if has_stack {
+ if let Some(stack_ra) = stack_ra {
+ ret.push(LI::ld(ra, stack_ra, sp));
+ }
+ ret.push(LI::addi(sp, sp, stack_len));
+ }
+ ret.push(LI::Ret);
+
for i in &f.instructions {
irli.push((v.len(), format!("{i:?}")));
match i {
@@ -235,11 +248,14 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
{
}
}
IRI::Ret { src } => {
- let Some(rva) = stack_rva else {
- panic!("no return value address on stack!")
- };
- v.push(LI::ld(t0, rva, sp));
- mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32);
+ if let Some(src) = src {
+ let Some(rva) = stack_rva else {
+ panic!("no return value address on stack!")
+ };
+ v.push(LI::ld(t0, rva, sp));
+ mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32);
+ }
+ v.extend(&ret);
}
IRI::Jump(location) => {
v.push(LI::J(*location));
@@ -259,13 +275,6 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
{
}
}
dbg.push_fn(irli);
- if has_stack {
- if let Some(stack_ra) = stack_ra {
- v.push(LI::ld(ra, stack_ra, sp));
- }
- v.push(LI::addi(sp, sp, stack_len));
- }
- v.push(LI::Ret);
fns.push(UnlinkedFunction {
instrs: v,
sym: *sym,
diff --git a/src/compiler/elf.rs b/src/compiler/elf.rs
index 6f0ef1c..20ff70a 100644
--- a/src/compiler/elf.rs
+++ b/src/compiler/elf.rs
@@ -51,18 +51,19 @@ pub struct SectionHeader {
}
// this is currently specialized for riscv64; obviously add params later
-pub fn create(program: Vec, start_offset: Addr) -> Vec {
+pub fn create(program: &[u8], start_offset: Addr) -> Vec {
let addr_start = 0x1000;
let page_size = 0x1000;
- let progam_size = std::mem::size_of_val(&program[..]) as u64;
+ // I don't know if I have to add addr_start here, idk how it maps the memory
+ let program_size = std::mem::size_of_val(program) as u64 + addr_start;
let program_header = ProgramHeader {
ty: 0x1, // LOAD
flags: 0b101, // executable, readable
offset: 0x0,
vaddr: addr_start,
paddr: addr_start,
- filesz: progam_size,
- memsz: progam_size,
+ filesz: program_size,
+ memsz: program_size,
align: page_size,
};
let header_len = (size_of::() + size_of::()) as u64;
@@ -104,7 +105,7 @@ unsafe fn as_u8_slice(p: &T) -> &[u8] {
}
impl LinkedProgram {
- pub fn to_elf(self) -> Vec {
- create(self.code, self.start.expect("no start found"))
+ pub fn to_elf(&self) -> Vec {
+ create(&self.code, self.start.expect("no start found"))
}
}
diff --git a/src/compiler/program.rs b/src/compiler/program.rs
index 3af2f2b..dcc0236 100644
--- a/src/compiler/program.rs
+++ b/src/compiler/program.rs
@@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::{
ir::Symbol,
- util::{Labelable, LabeledFmt, Labeler},
+ util::{Labelable, LabeledFmt},
};
use super::debug::DebugInfo;
@@ -26,7 +26,7 @@ pub struct UnlinkedFunction {
pub locations: HashMap,
}
-impl UnlinkedProgram {
+impl UnlinkedProgram {
pub fn link(self) -> LinkedProgram {
let mut data = Vec::new();
let mut sym_table = SymTable::new(self.sym_count);
diff --git a/src/ir/id.rs b/src/ir/id.rs
index ad8dcc2..0b06019 100644
--- a/src/ir/id.rs
+++ b/src/ir/id.rs
@@ -1,14 +1,17 @@
use std::fmt::Debug;
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
-pub struct TypeID(pub usize);
+pub struct StructID(pub usize);
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct VarID(pub usize);
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct FnID(pub usize);
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct DataID(pub usize);
+#[derive(Clone, Copy, Eq, Hash, PartialEq)]
+pub struct FieldID(pub usize);
+// I had an idea for why these were different... now I don't
pub type Size = u32;
pub type Len = u32;
@@ -18,7 +21,7 @@ impl Debug for VarID {
}
}
-impl Debug for TypeID {
+impl Debug for StructID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ty{}", self.0)
}
@@ -35,3 +38,9 @@ impl Debug for DataID {
write!(f, "data{}", self.0)
}
}
+
+impl Debug for FieldID {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "field{}", self.0)
+ }
+}
diff --git a/src/ir/lower/func.rs b/src/ir/lower/func.rs
index a1d58fa..b1b6ab4 100644
--- a/src/ir/lower/func.rs
+++ b/src/ir/lower/func.rs
@@ -7,6 +7,7 @@ use std::collections::HashMap;
pub struct IRLFunction {
pub instructions: Vec,
pub stack: HashMap,
+ pub subvar_map: HashMap,
pub args: Vec<(VarID, Size)>,
pub ret_size: Size,
pub makes_call: bool,
@@ -46,7 +47,7 @@ pub enum IRLInstruction {
outputs: Vec<(Reg, VarID)>,
},
Ret {
- src: VarID,
+ src: Option,
},
// 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
@@ -58,3 +59,11 @@ pub enum IRLInstruction {
Mark(Symbol),
}
+impl IRLInstruction {
+ pub fn is_ret(&self) -> bool {
+ match self {
+ Self::Ret { .. } => true,
+ _ => false,
+ }
+ }
+}
diff --git a/src/ir/lower/program.rs b/src/ir/lower/program.rs
index 611e76d..eada48b 100644
--- a/src/ir/lower/program.rs
+++ b/src/ir/lower/program.rs
@@ -1,6 +1,6 @@
use std::collections::HashMap;
-use crate::ir::{AsmBlockArgType, IRUFunction, IRUInstrInst, Size, SymbolSpace};
+use crate::ir::{AsmBlockArgType, IRUFunction, IRUInstrInst, Size, SymbolSpace, VarOffset};
use super::{
IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, SymbolSpaceBuilder, Type,
@@ -31,6 +31,9 @@ impl IRLProgram {
for i in &f.instructions {
fbuilder.insert_instr(i);
}
+ if fbuilder.instrs.last().is_none_or(|i| !i.is_ret()) {
+ fbuilder.instrs.push(IRLInstruction::Ret { src: None });
+ }
let res = fbuilder.finish(f);
ssbuilder.write_fn(sym, res, Some(f.name.clone()));
}
@@ -48,8 +51,15 @@ pub struct IRLFunctionBuilder<'a> {
builder: &'a mut SymbolSpaceBuilder,
instrs: Vec,
stack: HashMap,
+ subvar_map: HashMap,
makes_call: bool,
- outer: Option,
+ loopp: Option,
+}
+
+#[derive(Clone, Copy)]
+pub struct LoopCtx {
+ top: Symbol,
+ bot: Symbol,
}
impl<'a> IRLFunctionBuilder<'a> {
@@ -57,27 +67,36 @@ impl<'a> IRLFunctionBuilder<'a> {
Self {
instrs: Vec::new(),
stack: HashMap::new(),
+ subvar_map: HashMap::new(),
makes_call: false,
program,
builder,
- outer: None,
+ loopp: None,
}
}
pub fn alloc_stack(&mut self, i: VarID) -> Option<()> {
- let size = *self
+ if self.program.size_of_var(i).expect("unsized type") == 0 {
+ return None;
+ };
+ self.map_subvar(i);
+ let var = self.program.var_offset(i).expect("var offset");
+ *self
.stack
- .entry(i)
- .or_insert(self.program.size_of_var(i).expect("unsized type"));
- if size == 0 {
- None
- } else {
- Some(())
+ .entry(var.id)
+ .or_insert(self.program.size_of_var(var.id).expect("unsized type"));
+ Some(())
+ }
+ pub fn map_subvar(&mut self, i: VarID) {
+ let off = self.program.var_offset(i).expect("var offset");
+ if off.id != i {
+ self.subvar_map.insert(i, off);
}
}
pub fn insert_instr(&mut self, i: &IRUInstrInst) -> Option