START OF COMPILER
This commit is contained in:
42
src/compiler/mod.rs
Normal file
42
src/compiler/mod.rs
Normal 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
53
src/compiler/program.rs
Normal 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
|
||||
}
|
||||
2
src/compiler/riscv64/asm.rs
Normal file
2
src/compiler/riscv64/asm.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub enum Instruction {
|
||||
}
|
||||
101
src/compiler/riscv64/elf.rs
Normal file
101
src/compiler/riscv64/elf.rs
Normal 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>())
|
||||
}
|
||||
58
src/compiler/riscv64/instruction/base.rs
Normal file
58
src/compiler/riscv64/instruction/base.rs
Normal 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)
|
||||
}
|
||||
20
src/compiler/riscv64/instruction/func.rs
Normal file
20
src/compiler/riscv64/instruction/func.rs
Normal 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;
|
||||
}
|
||||
53
src/compiler/riscv64/instruction/mod.rs
Normal file
53
src/compiler/riscv64/instruction/mod.rs
Normal 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)
|
||||
}
|
||||
9
src/compiler/riscv64/instruction/opcode.rs
Normal file
9
src/compiler/riscv64/instruction/opcode.rs
Normal 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;
|
||||
93
src/compiler/riscv64/instruction/reg.rs
Normal file
93
src/compiler/riscv64/instruction/reg.rs
Normal 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
|
||||
}
|
||||
}
|
||||
33
src/compiler/riscv64/mod.rs
Normal file
33
src/compiler/riscv64/mod.rs
Normal 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
7
src/compiler/target.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub trait Target {
|
||||
type Reg;
|
||||
}
|
||||
|
||||
pub trait RegType {
|
||||
type Size;
|
||||
}
|
||||
Reference in New Issue
Block a user