START OF COMPILER

This commit is contained in:
2024-10-11 17:31:03 -04:00
parent bb3a0ad113
commit de79445ede
39 changed files with 710 additions and 94 deletions

42
src/compiler/mod.rs Normal file
View File

@@ -0,0 +1,42 @@
use std::{
fs::{create_dir_all, OpenOptions},
os::unix::fs::OpenOptionsExt,
path::Path,
process::Command,
};
mod riscv64;
mod program;
mod target;
pub fn main() {
use std::io::prelude::*;
let dir = Path::new("build");
create_dir_all(dir).expect("Failed to create or confirm build directory");
let name = Path::new("test");
let path = dir.join(name);
let path = path.as_os_str();
let mut file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.mode(0o750)
.open(path)
.expect("Failed to create file");
file.write_all(&riscv64::gen())
.expect("Failed to write to file");
file.sync_all().expect("Failed to sync file");
if let Ok(mut process) = Command::new("qemu-riscv64").arg(path).spawn() {
if let Ok(status) = process.wait() {
if status.code().is_none_or(|c| c != 0) {
println!("{}", status);
}
}
}
}
// qemu-riscv64 -g 1234 test &
// riscv64-linux-gnu-gdb -q \
// -ex "target remote :1234" \
// test

53
src/compiler/program.rs Normal file
View File

@@ -0,0 +1,53 @@
use std::collections::HashMap;
pub struct Program {
data: Vec<u8>,
ro_map: HashMap<String, usize>,
}
impl Program {
pub fn new(data: HashMap<String, Vec<u8>>) -> Self {
let mut ro_data = Vec::new();
let mut ro_map = HashMap::new();
for (key, val) in data {
ro_map.insert(key, ro_data.len());
ro_data.extend(val);
}
Self {
data: ro_data,
ro_map,
}
}
}
pub fn create_program<I: Instr>(
ro_data: HashMap<String, Vec<u8>>,
functions: Vec<Function<I>>,
) -> Vec<u8> {
let mut data = Vec::new();
let mut ro_map = HashMap::new();
for (key, val) in ro_data {
ro_map.insert(key, data.len());
data.extend(val);
}
// let mut fn_map = HashMap::new();
for fun in functions {
for i in fun.instructions {
data.extend(i.to_le_bytes());
}
}
data
}
pub struct Function<I: Instr> {
label: String,
instructions: Vec<I>,
}
pub trait Instr {
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8>;
}
struct SymbolInstr {
i: usize
}

View File

@@ -0,0 +1,2 @@
pub enum Instruction {
}

101
src/compiler/riscv64/elf.rs Normal file
View File

@@ -0,0 +1,101 @@
#[repr(C)]
pub struct ELF64Header {
magic: u32,
class: u8,
endianness: u8,
ei_version: u8,
os_abi: u8,
os_abi_ver: u8,
pad: [u8; 7],
ty: u16,
machine: u16,
e_version: u32,
entry: u64,
program_header_offset: u64,
section_header_offset: u64,
flags: u32,
header_size: u16,
program_header_entry_size: u16,
program_header_num: u16,
section_header_entry_size: u16,
section_header_num: u16,
section_header_str_idx: u16,
}
#[repr(C)]
pub struct ProgramHeader {
ty: u32,
flags: u32,
offset: u64,
vaddr: u64,
paddr: u64,
filesz: u64,
memsz: u64,
align: u64,
}
#[repr(C)]
pub struct SectionHeader {
name_idx: u32,
ty: u32,
flags: u64,
addr: u64,
offset: u64,
size: u64,
link: u32,
info: u32,
addr_align: u64,
entry_size: u64,
}
pub fn create(program: Vec<u8>, start_offset: u64) -> Vec<u8> {
let addr_start = 0x1000;
let page_size = 0x1000;
let progam_size = std::mem::size_of_val(&program[..]) as u64;
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,
align: page_size,
};
let header_len = (size_of::<ELF64Header>() + size_of::<ProgramHeader>()) as u64;
let program_pos = header_len;
let header = ELF64Header {
magic: 0x7f_45_4c_46u32.swap_bytes(),
class: 0x2, // 64 bit
endianness: 0x1, // little endian
ei_version: 0x1,
os_abi: 0x0, // system-v
os_abi_ver: 0x0,
pad: [0x0; 7],
ty: 0x2, // executable
machine: 0xf3, // risc-v
e_version: 0x1,
entry: addr_start + program_pos + start_offset,
program_header_offset: size_of::<ELF64Header>() as u64,
section_header_offset: 0x0,
// C ABI (16 bit instruction align) + double precision floats
flags: 0x1 | 0x4,
header_size: size_of::<ELF64Header>() as u16,
program_header_entry_size: size_of::<ProgramHeader>() as u16,
program_header_num: 0x1,
section_header_entry_size: size_of::<SectionHeader>() as u16,
section_header_num: 0x0,
section_header_str_idx: 0x0,
};
let mut bytes: Vec<u8> = Vec::new();
unsafe {
bytes.extend(as_u8_slice(&header));
bytes.extend(as_u8_slice(&program_header));
bytes.extend(program);
}
bytes
}
unsafe fn as_u8_slice<T: Sized>(p: &T) -> &[u8] {
core::slice::from_raw_parts((p as *const T) as *const u8, size_of::<T>())
}

View File

@@ -0,0 +1,58 @@
use super::{Reg, OPCODE_MASK};
use crate::{compiler::program::Instr, util::{bit, bits, in_bit_range}};
pub struct Instruction(u32);
use Instruction as I;
impl Instr for Instruction {
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
self.0.to_le_bytes().into_iter()
}
}
pub const fn r_type(funct7: u32, rs2: Reg, rs1: Reg, funct3: u32, rd: Reg, opcode: u32) -> I {
I((funct7 << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3 << 12)
+ (rd.val() << 7)
+ opcode)
}
pub const fn i_type(imm: i32, rs1: Reg, funct3: u32, rd: Reg, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 11, 0));
I((bits(imm, 11, 0) << 20) + (rs1.val() << 15) + (funct3 << 12) + (rd.val() << 7) + opcode)
}
pub const fn s_type(rs2: Reg, rs1: Reg, funct3: u32, imm: i32, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 11, 0));
I((bits(imm, 11, 5) << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3 << 12)
+ (bits(imm, 4, 0) << 7)
+ opcode)
}
pub const fn b_type(rs2: Reg, rs1: Reg, funct3: u32, imm: i32, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 12, 1));
I((bit(imm, 12) << 31)
+ (bits(imm, 10, 5) << 25)
+ (rs2.val() << 20)
+ (rs1.val() << 15)
+ (funct3 << 8)
+ (bits(imm, 4, 1) << 8)
+ (bit(imm, 11) << 7)
+ opcode)
}
pub const fn u_type(imm: i32, rd: Reg, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 31, 12));
I((bits(imm, 31, 12) << 12) + (rd.val() << 7) + opcode)
}
pub const fn j_type(imm: i32, rd: Reg, opcode: u32) -> I {
debug_assert!(in_bit_range(imm, 20, 1));
I((bit(imm, 20) << 31)
+ (bits(imm, 10, 1) << 21)
+ (bit(imm, 11) << 20)
+ (bits(imm, 19, 12) << 12)
+ (rd.val() << 7)
+ opcode)
}

View File

@@ -0,0 +1,20 @@
pub mod op {
pub const ADD : u32 = 0b000;
pub const SLL : u32 = 0b001;
pub const SLT : u32 = 0b010;
pub const SLTU: u32 = 0b011;
pub const XOR : u32 = 0b100;
pub const SR : u32 = 0b101;
pub const OR : u32 = 0b110;
pub const AND : u32 = 0b111;
}
pub mod width {
pub const B : u32 = 0b000;
pub const H : u32 = 0b001;
pub const W : u32 = 0b010;
pub const D : u32 = 0b011;
pub const BU: u32 = 0b100;
pub const HU: u32 = 0b101;
pub const WU: u32 = 0b110;
}

View File

@@ -0,0 +1,53 @@
mod base;
mod reg;
mod opcode;
mod func;
use base::*;
pub use reg::*;
use Instruction as I;
use opcode::*;
use func::{op::*, width};
pub const fn ecall() -> I {
i_type(0, zero, 0, zero, SYSTEM)
}
pub const fn ebreak() -> I {
i_type(1, zero, 0, zero, SYSTEM)
}
pub const fn auipc(dest: Reg, imm: i32) -> I {
u_type(imm, dest, AUIPC)
}
pub const fn ld(dest: Reg, offset: i32, base: Reg) -> I {
i_type(offset, base, width::D, dest, LOAD)
}
pub const fn lw(dest: Reg, offset: i32, base: Reg) -> I {
i_type(offset, base, width::W, dest, LOAD)
}
pub const fn lb(dest: Reg, offset: i32, base: Reg) -> I {
i_type(offset, base, width::B, dest, LOAD)
}
pub const fn sb(src: Reg, offset: i32, base: Reg) -> I {
s_type(src, base, width::B, offset, STORE)
}
pub const fn sw(src: Reg, offset: i32, base: Reg) -> I {
s_type(src, base, width::W, offset, STORE)
}
pub const fn sd(src: Reg, offset: i32, base: Reg) -> I {
s_type(src, base, width::D, offset, STORE)
}
pub const fn addi(dest: Reg, src: Reg, imm: i32) -> I {
i_type(imm, src, ADD, dest, IMM_OP)
}
pub const fn jal(offset: i32, dest: Reg) -> I {
j_type(offset, dest, JAL)
}
// pseudo instructions that map to a single instruction
pub const fn j(offset: i32) -> I {
jal(offset, zero)
}
pub const fn mv(dest: Reg, src: Reg) -> I {
addi(dest, src, 0)
}

View File

@@ -0,0 +1,9 @@
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;

View File

@@ -0,0 +1,93 @@
#![allow(non_upper_case_globals)]
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
}
}

View File

@@ -0,0 +1,33 @@
use crate::compiler::program::Instr;
mod elf;
mod instruction;
mod asm;
use instruction::*;
pub fn gen() -> Vec<u8> {
let mut program = Vec::new();
let msg = b"Hello world!\n";
program.extend(msg);
program.resize(((program.len() - 1) / 4 + 1) * 4, 0);
let start = program.len() as u64;
let instructions = [
auipc(t0, 0),
addi(t0, t0, -(start as i32)),
addi(a0, zero, 1),
mv(a1, t0),
addi(a2, zero, msg.len() as i32),
addi(a7, zero, 64),
addi(t0, zero, 200),
ecall(),
// exit
addi(a0, zero, 0),
addi(a7, zero, 93),
ecall(),
j(0),
];
for i in instructions {
program.extend(i.to_le_bytes());
}
elf::create(program, start)
}

7
src/compiler/target.rs Normal file
View File

@@ -0,0 +1,7 @@
pub trait Target {
type Reg;
}
pub trait RegType {
type Size;
}