actually compiles and does stuff now

This commit is contained in:
2024-12-06 19:44:33 -05:00
parent 31c197e991
commit 620c4557e9
67 changed files with 1931 additions and 1287 deletions
+3
View File
@@ -0,0 +1,3 @@
pub mod riscv64;
use super::*;
+77
View File
@@ -0,0 +1,77 @@
use crate::{compiler::program::{Addr, Instr, SymTable}, ir::AddrID};
use super::*;
#[derive(Debug, Clone)]
pub enum LinkerInstruction {
Add { dest: Reg, src1: Reg, src2: Reg },
Addi { dest: Reg, src: Reg, imm: i32 },
Andi { dest: Reg, src: Reg, imm: i32 },
Slli { dest: Reg, src: Reg, imm: i32 },
Srli { dest: Reg, src: Reg, imm: i32 },
Sd { src: Reg, offset: i32, base: Reg },
Ld { dest: Reg, offset: i32, base: Reg },
Mv { dest: Reg, src: Reg },
La { dest: Reg, src: AddrID },
Jal { dest: Reg, offset: i32 },
Call(AddrID),
J(AddrID),
Ret,
Ecall,
Li { dest: Reg, imm: i64 },
}
impl Instr for LinkerInstruction {
fn push(
&self,
data: &mut Vec<u8>,
sym_map: &SymTable,
pos: Addr,
missing: bool,
) -> Option<AddrID> {
let last = match self {
Self::Add { dest, src1, src2 } => add(*dest, *src1, *src2),
Self::Addi { dest, src, imm } => addi(*dest, *src, BitsI32::new(*imm)),
Self::Andi { dest, src, imm } => andi(*dest, *src, BitsI32::new(*imm)),
Self::Slli { dest, src, imm } => slli(*dest, *src, BitsI32::new(*imm)),
Self::Srli { dest, src, imm } => srli(*dest, *src, BitsI32::new(*imm)),
Self::Sd { src, offset, base } => sd(*src, BitsI32::new(*offset), *base),
Self::Ld { dest, offset, base } => ld(*dest, BitsI32::new(*offset), *base),
Self::Mv { dest, src } => addi(*dest, *src, BitsI32::new(0)),
Self::La { dest, src } => {
if let Some(addr) = sym_map.get(*src) {
let offset = addr.val() as i32 - pos.val() as i32;
data.extend(auipc(*dest, BitsI32::new(0)).to_le_bytes());
addi(*dest, *dest, BitsI32::new(offset))
} else {
data.extend_from_slice(&[0; 2 * 4]);
return Some(*src);
}
}
Self::Jal { dest, offset } => jal(*dest, BitsI32::new(*offset)),
Self::J(sym) => {
if let Some(addr) = sym_map.get(*sym) {
let offset = addr.val() as i32 - pos.val() as i32;
j(BitsI32::new(offset))
} else {
data.extend_from_slice(&[0; 4]);
return Some(*sym);
}
}
Self::Call(sym) => {
if let Some(addr) = sym_map.get(*sym) {
let offset = addr.val() as i32 - pos.val() as i32;
jal(ra, BitsI32::new(offset))
} else {
data.extend_from_slice(&[0; 4]);
return Some(*sym);
}
}
Self::Ret => ret(),
Self::Ecall => ecall(),
Self::Li { dest, imm } => addi(*dest, zero, BitsI32::new(*imm as i32)),
};
data.extend(last.to_le_bytes());
None
}
}
+78
View File
@@ -0,0 +1,78 @@
use crate::util::Bits32;
use super::Reg;
pub struct RawInstruction(u32);
use RawInstruction as I;
impl RawInstruction {
pub fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
self.0.to_le_bytes().into_iter()
}
pub fn to_be_bytes(&self) -> impl IntoIterator<Item = u8> {
self.0.to_be_bytes().into_iter()
}
}
pub type Funct3 = Bits32<2, 0>;
pub const fn r_type(
funct7: Bits32<6, 0>,
rs2: Reg,
rs1: Reg,
funct3: Bits32<2, 0>,
rd: Reg,
opcode: u32,
) -> I {
I((funct7.val() << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3.val() << 12)
+ (rd.val() << 7)
+ opcode)
}
pub const fn i_type(imm: Bits32<11, 0>, rs1: Reg, funct: Funct3, rd: Reg, opcode: u32) -> I {
I((imm.val() << 20) + (rs1.val() << 15) + (funct.val() << 12) + (rd.val() << 7) + opcode)
}
pub const fn s_type(
rs2: Reg,
rs1: Reg,
funct3: Funct3,
imm: Bits32<11, 0>,
opcode: u32,
) -> I {
I((imm.bits(11, 5) << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3.val() << 12)
+ (imm.bits(4, 0) << 7)
+ opcode)
}
pub const fn b_type(
rs2: Reg,
rs1: Reg,
funct3: Funct3,
imm: Bits32<12, 1>,
opcode: u32,
) -> I {
I((imm.bit(12) << 31)
+ (imm.bits(10, 5) << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3.val() << 8)
+ (imm.bits(4, 1) << 8)
+ (imm.bit(11) << 7)
+ opcode)
}
pub const fn u_type(imm: Bits32<31, 12>, rd: Reg, opcode: u32) -> I {
I((imm.bits(31, 12) << 12) + (rd.val() << 7) + opcode)
}
pub const fn j_type(imm: Bits32<20, 1>, rd: Reg, opcode: u32) -> I {
I((imm.bit(20) << 31)
+ (imm.bits(10, 1) << 21)
+ (imm.bit(11) << 20)
+ (imm.bits(19, 12) << 12)
+ (rd.val() << 7)
+ opcode)
}
+117
View File
@@ -0,0 +1,117 @@
use std::collections::HashMap;
use crate::{
compiler::{arch::riscv64::Reg, create_program, Addr},
ir::{
arch::riscv64::{RV64Instruction as AI, RegRef},
IRLInstruction as IRI, Program,
},
};
use super::{LinkerInstruction as LI, *};
pub fn compile(program: Program) -> (Vec<u8>, Option<Addr>) {
let mut fns = Vec::new();
let mut data = Vec::new();
for d in program.data {
data.push((d.data, d.addr));
}
let mut start = None;
for f in program.fns {
let mut v = Vec::new();
let mut stack = HashMap::new();
let mut stack_len = 0;
if !f.stack.is_empty() || !f.args.is_empty() {
for (id, s) in &f.stack {
stack.insert(id, stack_len);
stack_len += *s as i32;
}
for (id, s) in f.args.iter().rev() {
stack.insert(id, stack_len);
stack_len += *s as i32;
}
v.push(LI::Addi {
dest: sp,
src: sp,
imm: -stack_len,
});
}
for i in &f.instructions {
match i {
IRI::Mv { dest, src } => todo!(),
IRI::Ref { dest, src } => todo!(),
IRI::LoadAddr { dest, src } => {
v.extend([
LI::La {
dest: t0,
src: *src,
},
LI::Sd {
src: t0,
offset: stack[dest],
base: sp,
},
]);
}
IRI::Call { dest, f, args } => {
let mut offset = 0;
for (arg, s) in args {
offset -= *s as i32;
v.extend([
LI::Ld {
dest: t0,
offset: stack[arg],
base: sp,
},
LI::Sd {
src: t0,
offset,
base: sp,
},
]);
}
v.push(LI::Call(*f));
}
IRI::AsmBlock { args, instructions } => {
for (reg, var) in args {
v.push(LI::Ld {
dest: *reg,
offset: stack[var],
base: sp,
});
}
fn r(rr: RegRef) -> Reg {
match rr {
RegRef::Var(var_ident) => todo!(),
RegRef::Reg(reg) => reg,
}
}
for i in instructions {
match *i {
AI::Ecall => v.push(LI::Ecall),
AI::Li { dest, imm } => v.push(LI::Li { dest: r(dest), imm }),
AI::Mv { dest, src } => v.push(LI::Mv {
dest: r(dest),
src: r(src),
}),
AI::La { dest, src } => todo!(),
AI::Ld { dest, base, offset } => v.push(LI::Ld {
dest: r(dest),
offset: offset as i32,
base: r(base),
}),
}
}
}
IRI::Ret { src } => todo!(),
}
}
if f.name == "start" {
start = Some(f.addr);
} else {
v.push(LI::Ret);
}
fns.push((v, f.addr));
}
create_program(fns, data, start)
}
+24
View File
@@ -0,0 +1,24 @@
use super::Funct3;
pub mod op {
use super::*;
pub const ADD : Funct3 = Funct3::new(0b000);
pub const SLL : Funct3 = Funct3::new(0b001);
pub const SLT : Funct3 = Funct3::new(0b010);
pub const SLTU: Funct3 = Funct3::new(0b011);
pub const XOR : Funct3 = Funct3::new(0b100);
pub const SR : Funct3 = Funct3::new(0b101);
pub const OR : Funct3 = Funct3::new(0b110);
pub const AND : Funct3 = Funct3::new(0b111);
}
pub mod width {
use super::*;
pub const B : Funct3 = Funct3::new(0b000);
pub const H : Funct3 = Funct3::new(0b001);
pub const W : Funct3 = Funct3::new(0b010);
pub const D : Funct3 = Funct3::new(0b011);
pub const BU: Funct3 = Funct3::new(0b100);
pub const HU: Funct3 = Funct3::new(0b101);
pub const WU: Funct3 = Funct3::new(0b110);
}
+63
View File
@@ -0,0 +1,63 @@
mod asm;
mod base;
mod funct;
mod opcode;
mod reg;
mod single;
mod compile;
use crate::util::BitsI32;
pub use asm::*;
use base::*;
use funct::{op::*, width};
use opcode::*;
pub use reg::*;
pub use compile::*;
use single::*;
pub fn gen() -> Vec<u8> {
// use asm::LinkerInstruction as I;
// let mut table = SymMap::new();
// let (msg, len) = table.push_ro_data_size(b"Hello world!\n".to_vec());
// let (msg2, len2) = table.push_ro_data_size(b"IT WORKS!!!!\n".to_vec());
// let print_stuff = table.reserve();
// let start = table.push_fn(vec![
// I::Call(*print_stuff),
// I::Li { dest: a0, imm: 0 },
// I::Li { dest: a7, imm: 93 },
// I::Ecall,
// I::Jal {
// dest: zero,
// offset: 0,
// },
// ]);
// table.write_fn(
// print_stuff,
// vec![
// I::Li { dest: a0, imm: 1 },
// I::La { dest: a1, src: msg },
// I::Li {
// dest: a2,
// imm: len as i64,
// },
// I::Li { dest: a7, imm: 64 },
// I::Ecall,
// I::Li { dest: a0, imm: 1 },
// I::La {
// dest: a1,
// src: msg2,
// },
// I::Li {
// dest: a2,
// imm: len2 as i64,
// },
// I::Li { dest: a7, imm: 64 },
// I::Ecall,
// I::Ret,
// ],
// );
// let (program, start) = create_program(table, Some(start));
// elf::create(program, start.expect("no start!"))
todo!("remove this");
}
+10
View File
@@ -0,0 +1,10 @@
pub const OPCODE_MASK: u32 = 0b1111111;
pub const SYSTEM: u32 = 0b1110011;
pub const LOAD : u32 = 0b0000011;
pub const STORE : u32 = 0b0100011;
pub const AUIPC : u32 = 0b0010111;
pub const IMM_OP: u32 = 0b0010011;
pub const OP : u32 = 0b0110011;
pub const JAL : u32 = 0b1101111;
pub const JALR : u32 = 0b1100111;
+179
View File
@@ -0,0 +1,179 @@
#![allow(non_upper_case_globals)]
#[derive(Clone, Copy)]
pub struct Reg(u8);
/// hard wired 0
pub const zero: Reg = Reg(0);
/// return address
pub const ra: Reg = Reg(1);
/// stack pointer
pub const sp: Reg = Reg(2);
/// global pointer
pub const gp: Reg = Reg(3);
/// thread pointer
pub const tp: Reg = Reg(4);
/// temp / alternate link
pub const t0: Reg = Reg(5);
pub const t1: Reg = Reg(6);
pub const t2: Reg = Reg(7);
pub const fp: Reg = Reg(8);
pub const s0: Reg = Reg(8);
pub const s1: Reg = Reg(9);
pub const a0: Reg = Reg(10);
pub const a1: Reg = Reg(11);
pub const a2: Reg = Reg(12);
pub const a3: Reg = Reg(13);
pub const a4: Reg = Reg(14);
pub const a5: Reg = Reg(15);
pub const a6: Reg = Reg(16);
pub const a7: Reg = Reg(17);
pub const s2: Reg = Reg(18);
pub const s3: Reg = Reg(19);
pub const s4: Reg = Reg(20);
pub const s5: Reg = Reg(21);
pub const s6: Reg = Reg(22);
pub const s7: Reg = Reg(23);
pub const s8: Reg = Reg(24);
pub const s9: Reg = Reg(25);
pub const s10: Reg = Reg(26);
pub const s11: Reg = Reg(27);
pub const t3: Reg = Reg(28);
pub const t4: Reg = Reg(29);
pub const t5: Reg = Reg(30);
pub const t6: Reg = Reg(31);
// pub const ft0: Reg = Reg(0);
// pub const ft1: Reg = Reg(1);
// pub const ft2: Reg = Reg(2);
// pub const ft3: Reg = Reg(3);
// pub const ft4: Reg = Reg(4);
// pub const ft5: Reg = Reg(5);
// pub const ft6: Reg = Reg(6);
// pub const ft7: Reg = Reg(7);
//
// pub const fs0: Reg = Reg(8);
// pub const fs1: Reg = Reg(9);
//
// pub const fa0: Reg = Reg(10);
// pub const fa1: Reg = Reg(11);
// pub const fa2: Reg = Reg(12);
// pub const fa3: Reg = Reg(13);
// pub const fa4: Reg = Reg(14);
// pub const fa5: Reg = Reg(15);
// pub const fa6: Reg = Reg(16);
// pub const fa7: Reg = Reg(17);
//
// pub const fs2: Reg = Reg(18);
// pub const fs3: Reg = Reg(19);
// pub const fs4: Reg = Reg(20);
// pub const fs5: Reg = Reg(21);
// pub const fs6: Reg = Reg(22);
// pub const fs7: Reg = Reg(23);
// pub const fs8: Reg = Reg(24);
// pub const fs9: Reg = Reg(25);
// pub const fs10: Reg = Reg(26);
// pub const fs11: Reg = Reg(27);
//
// pub const ft8: Reg = Reg(28);
// pub const ft9: Reg = Reg(29);
// pub const ft10: Reg = Reg(30);
// pub const ft11: Reg = Reg(31);
impl Reg {
#[inline]
pub const fn val(&self) -> u32 {
self.0 as u32
}
}
impl Reg {
pub fn from_str(str: &str) -> Option<Self> {
Some(match str {
"zero" => zero,
"ra" => ra,
"sp" => sp,
"gp" => gp,
"tp" => tp,
"t0" => t0,
"t1" => t1,
"t2" => t2,
"fp" => fp,
"s0" => s0,
"s1" => s1,
"a0" => a0,
"a1" => a1,
"a2" => a2,
"a3" => a3,
"a4" => a4,
"a5" => a5,
"a6" => a6,
"a7" => a7,
"s2" => s2,
"s3" => s3,
"s4" => s4,
"s5" => s5,
"s6" => s6,
"s7" => s7,
"s8" => s8,
"s9" => s9,
"s10" => s10,
"s11" => s11,
"t3" => t3,
"t4" => t4,
"t5" => t5,
"t6" => t6,
_ => {
return None;
}
})
}
}
impl std::fmt::Debug for Reg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self.0 {
0 => "zero",
1 => "ra",
2 => "sp",
3 => "gp",
4 => "tp",
5 => "t0",
6 => "t1",
7 => "t2",
8 => "fp",
9 => "s1",
10 => "a0",
11 => "a1",
12 => "a2",
13 => "a3",
14 => "a4",
15 => "a5",
16 => "a6",
17 => "a7",
18 => "s2",
19 => "s3",
20 => "s4",
21 => "s5",
22 => "s6",
23 => "s7",
24 => "s8",
25 => "s9",
26 => "s10",
27 => "s11",
28 => "t3",
29 => "t4",
30 => "t5",
31 => "t6",
_ => "unknown",
}
)
}
}
+63
View File
@@ -0,0 +1,63 @@
use crate::util::{Bits32, BitsI32};
use super::*;
use RawInstruction as I;
pub const fn ecall() -> I {
i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM)
}
pub const fn ebreak() -> I {
i_type(Bits32::new(1), zero, Bits32::new(0), zero, SYSTEM)
}
pub const fn auipc(dest: Reg, imm: BitsI32<31, 12>) -> I {
u_type(imm.to_u(), dest, AUIPC)
}
pub const fn ld(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
i_type(offset.to_u(), base, width::D, dest, LOAD)
}
pub const fn lw(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
i_type(offset.to_u(), base, width::W, dest, LOAD)
}
pub const fn lb(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
i_type(offset.to_u(), base, width::B, dest, LOAD)
}
pub const fn sb(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
s_type(src, base, width::B, offset.to_u(), STORE)
}
pub const fn sw(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
s_type(src, base, width::W, offset.to_u(), STORE)
}
pub const fn sd(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
s_type(src, base, width::D, offset.to_u(), STORE)
}
pub const fn add(dest: Reg, src1: Reg, src2: Reg) -> I {
r_type(Bits32::new(0), src2, src1, ADD, dest, OP)
}
pub const fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> I {
i_type(imm.to_u(), src, ADD, dest, IMM_OP)
}
pub const fn andi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> I {
i_type(imm.to_u(), src, AND, dest, IMM_OP)
}
pub const fn slli(dest: Reg, src: Reg, imm: BitsI32<4, 0>) -> I {
i_type(Bits32::new(imm.to_u().val()), src, SLL, dest, IMM_OP)
}
pub const fn srli(dest: Reg, src: Reg, imm: BitsI32<4, 0>) -> I {
i_type(Bits32::new(imm.to_u().val()), src, SR, dest, IMM_OP)
}
pub const fn jal(dest: Reg, offset: BitsI32<20, 1>) -> I {
j_type(offset.to_u(), dest, JAL)
}
pub const fn jalr(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
i_type(offset.to_u(), base, Bits32::new(0), dest, JALR)
}
// pseudoinstructions that map to a single instruction
pub const fn j(offset: BitsI32<20, 1>) -> I {
jal(zero, offset)
}
pub const fn ret() -> I {
jalr(zero, BitsI32::new(0), ra)
}