START OF COMPILER
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/build
|
||||||
|
|||||||
@@ -20,3 +20,12 @@ fn main() {
|
|||||||
b
|
b
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let r = 3;
|
||||||
|
let a = }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test2() {
|
||||||
|
|
||||||
|
}
|
||||||
4
data/test.lang
Normal file
4
data/test.lang
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
fn main() {
|
||||||
|
let x = 3;
|
||||||
|
print(x);
|
||||||
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
16
src/main.rs
16
src/main.rs
@@ -1,17 +1,11 @@
|
|||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
|
#![feature(const_unbounded_shifts)]
|
||||||
|
#![feature(unbounded_shifts)]
|
||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
mod v1;
|
mod compiler;
|
||||||
mod v2;
|
mod parser;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let arg = std::env::args_os().nth(1);
|
compiler::main();
|
||||||
if let Some(path) = arg {
|
|
||||||
let file = std::fs::read_to_string(path).expect("failed to read file");
|
|
||||||
println!("{file}");
|
|
||||||
v1::parse_file(&file);
|
|
||||||
// v2::parse_file(&file);
|
|
||||||
} else {
|
|
||||||
v1::run_stdin();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/parser/mod.rs
Normal file
14
src/parser/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
mod v1;
|
||||||
|
mod v2;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let arg = std::env::args_os().nth(1);
|
||||||
|
if let Some(path) = arg {
|
||||||
|
let file = std::fs::read_to_string(path).expect("failed to read file");
|
||||||
|
println!("{file}");
|
||||||
|
v1::parse_file(&file);
|
||||||
|
// v2::parse_file(&file);
|
||||||
|
} else {
|
||||||
|
v1::run_stdin();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
use std::fmt::{Debug, Write};
|
use std::fmt::{Debug, Write};
|
||||||
|
|
||||||
use super::token::{Symbol, Token};
|
use super::token::{Symbol, Token};
|
||||||
use super::{Body, Node, Parsable, ParserError, ParserErrors, TokenCursor, Val};
|
use super::{Body, Node, Parsable, ParserError, ParserErrors, TokenCursor, Literal};
|
||||||
|
|
||||||
pub type ExprNode = Node<Box<Expr>>;
|
pub type ExprNode = Node<Box<Expr>>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Val(Node<Val>),
|
Lit(Node<Literal>),
|
||||||
Ident(String),
|
Ident(String),
|
||||||
BinaryOp(Operator, ExprNode, ExprNode),
|
BinaryOp(Operator, ExprNode, ExprNode),
|
||||||
Block(Node<Body>),
|
Block(Node<Body>),
|
||||||
@@ -30,7 +30,7 @@ pub enum Operator {
|
|||||||
impl Expr {
|
impl Expr {
|
||||||
pub fn ended_with_error(&self) -> bool {
|
pub fn ended_with_error(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Expr::Val(_) => false,
|
Expr::Lit(_) => false,
|
||||||
Expr::Ident(_) => false,
|
Expr::Ident(_) => false,
|
||||||
Expr::BinaryOp(_, _, e) => e.is_err() || e.as_ref().is_ok_and(|e| e.ended_with_error()),
|
Expr::BinaryOp(_, _, e) => e.is_err() || e.as_ref().is_ok_and(|e| e.ended_with_error()),
|
||||||
Expr::Block(b) => b.is_err(),
|
Expr::Block(b) => b.is_err(),
|
||||||
@@ -48,8 +48,8 @@ impl Parsable for Expr {
|
|||||||
cursor.next();
|
cursor.next();
|
||||||
if cursor.expect_peek()?.is_symbol(Symbol::CloseParen) {
|
if cursor.expect_peek()?.is_symbol(Symbol::CloseParen) {
|
||||||
cursor.next();
|
cursor.next();
|
||||||
return Ok(Expr::Val(Node::new(
|
return Ok(Expr::Lit(Node::new(
|
||||||
Val::Unit,
|
Literal::Unit,
|
||||||
cursor.next_pos().char_span(),
|
cursor.next_pos().char_span(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ impl Parsable for Expr {
|
|||||||
} else if next.is_symbol(Symbol::OpenCurly) {
|
} else if next.is_symbol(Symbol::OpenCurly) {
|
||||||
Self::Block(Node::parse(cursor, errors))
|
Self::Block(Node::parse(cursor, errors))
|
||||||
} else if let Some(val) = Node::maybe_parse(cursor, errors) {
|
} else if let Some(val) = Node::maybe_parse(cursor, errors) {
|
||||||
Self::Val(val)
|
Self::Lit(val)
|
||||||
} else {
|
} else {
|
||||||
let next = cursor.peek().unwrap();
|
let next = cursor.peek().unwrap();
|
||||||
match &next.token {
|
match &next.token {
|
||||||
@@ -172,7 +172,7 @@ impl Operator {
|
|||||||
impl Debug for Expr {
|
impl Debug for Expr {
|
||||||
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 {
|
||||||
Expr::Val(c) => c.fmt(f)?,
|
Expr::Lit(c) => c.fmt(f)?,
|
||||||
Expr::Ident(n) => f.write_str(n)?,
|
Expr::Ident(n) => f.write_str(n)?,
|
||||||
Expr::Block(b) => b.fmt(f)?,
|
Expr::Block(b) => b.fmt(f)?,
|
||||||
Expr::BinaryOp(op, e1, e2) => {
|
Expr::BinaryOp(op, e1, e2) => {
|
||||||
@@ -1,8 +1,22 @@
|
|||||||
use std::io::{stdout, BufRead, BufReader};
|
use std::io::{stdout, BufRead, BufReader};
|
||||||
|
|
||||||
mod parser;
|
mod body;
|
||||||
|
mod cursor;
|
||||||
|
mod error;
|
||||||
|
mod expr;
|
||||||
|
mod module;
|
||||||
|
mod node;
|
||||||
|
mod token;
|
||||||
|
mod val;
|
||||||
|
|
||||||
use parser::{Module, Node, ParserErrors, Statement, TokenCursor};
|
pub use body::*;
|
||||||
|
pub use cursor::*;
|
||||||
|
pub use error::*;
|
||||||
|
pub use expr::*;
|
||||||
|
pub use module::*;
|
||||||
|
pub use node::*;
|
||||||
|
pub use val::*;
|
||||||
|
use token::*;
|
||||||
|
|
||||||
pub fn parse_file(file: &str) {
|
pub fn parse_file(file: &str) {
|
||||||
let mut errors = ParserErrors::new();
|
let mut errors = ParserErrors::new();
|
||||||
@@ -10,8 +24,9 @@ pub fn parse_file(file: &str) {
|
|||||||
if let Ok(module) = node.as_ref() {
|
if let Ok(module) = node.as_ref() {
|
||||||
println!("{module:#?}");
|
println!("{module:#?}");
|
||||||
};
|
};
|
||||||
|
let out = &mut stdout();
|
||||||
for err in errors.errs {
|
for err in errors.errs {
|
||||||
err.write_for(&mut stdout(), file).unwrap();
|
err.write_for(out, file).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,12 +35,12 @@ pub fn run_stdin() {
|
|||||||
let mut errors = ParserErrors::new();
|
let mut errors = ParserErrors::new();
|
||||||
let str = &line.expect("failed to read line");
|
let str = &line.expect("failed to read line");
|
||||||
let mut cursor = TokenCursor::from(&str[..]);
|
let mut cursor = TokenCursor::from(&str[..]);
|
||||||
let out = &mut stdout();
|
|
||||||
if let Ok(expr) = Node::<Statement>::parse(&mut cursor, &mut errors).as_ref() {
|
if let Ok(expr) = Node::<Statement>::parse(&mut cursor, &mut errors).as_ref() {
|
||||||
println!("{:?}", expr);
|
println!("{:?}", expr);
|
||||||
}
|
}
|
||||||
|
let out = &mut stdout();
|
||||||
for err in errors.errs {
|
for err in errors.errs {
|
||||||
err.write_for(&mut stdout(), str).unwrap();
|
err.write_for(out, str).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,6 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
mod body;
|
use super::{token::*, Body, Node, Parsable, ParserError, ParserErrors, TokenCursor};
|
||||||
mod cursor;
|
|
||||||
mod error;
|
|
||||||
mod expr;
|
|
||||||
mod token;
|
|
||||||
mod val;
|
|
||||||
mod node;
|
|
||||||
|
|
||||||
pub use body::*;
|
|
||||||
pub use cursor::*;
|
|
||||||
pub use error::*;
|
|
||||||
pub use expr::*;
|
|
||||||
pub use val::*;
|
|
||||||
pub use node::*;
|
|
||||||
|
|
||||||
use token::*;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
@@ -2,7 +2,7 @@ use super::{CharCursor, MaybeParsable, ParserError, ParserErrors, Symbol, Token,
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub enum Val {
|
pub enum Literal {
|
||||||
String(String),
|
String(String),
|
||||||
Char(char),
|
Char(char),
|
||||||
Number(Number),
|
Number(Number),
|
||||||
@@ -16,7 +16,7 @@ pub struct Number {
|
|||||||
pub ty: Option<String>,
|
pub ty: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaybeParsable for Val {
|
impl MaybeParsable for Literal {
|
||||||
fn maybe_parse(cursor: &mut TokenCursor, _: &mut ParserErrors) -> Result<Option<Self>, ParserError> {
|
fn maybe_parse(cursor: &mut TokenCursor, _: &mut ParserErrors) -> Result<Option<Self>, ParserError> {
|
||||||
let inst = cursor.expect_peek()?;
|
let inst = cursor.expect_peek()?;
|
||||||
let mut res = match &inst.token {
|
let mut res = match &inst.token {
|
||||||
@@ -85,7 +85,7 @@ pub fn string_from(cursor: &mut CharCursor) -> Result<String, ParserError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Val {
|
impl Debug for Literal {
|
||||||
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::String(str) => str.fmt(f),
|
Self::String(str) => str.fmt(f),
|
||||||
@@ -1,8 +1,17 @@
|
|||||||
use std::{ffi::OsStr, io::{BufRead, BufReader}};
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
use parser::{print_error, CharCursor, Module, Statement};
|
mod body;
|
||||||
|
mod cursor;
|
||||||
|
mod error;
|
||||||
|
mod expr;
|
||||||
|
mod module;
|
||||||
|
mod util;
|
||||||
|
|
||||||
mod parser;
|
pub use body::*;
|
||||||
|
pub use cursor::*;
|
||||||
|
pub use error::*;
|
||||||
|
pub use expr::*;
|
||||||
|
pub use module::*;
|
||||||
|
|
||||||
pub fn parse_file(file: &str) {
|
pub fn parse_file(file: &str) {
|
||||||
match Module::parse(&mut CharCursor::from(file)) {
|
match Module::parse(&mut CharCursor::from(file)) {
|
||||||
@@ -1,16 +1,5 @@
|
|||||||
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
use std::{collections::HashSet, fmt::Debug, sync::LazyLock};
|
||||||
|
use super::{util::WHITESPACE_SET, Body, CharCursor, ParserError};
|
||||||
mod body;
|
|
||||||
mod cursor;
|
|
||||||
mod error;
|
|
||||||
mod expr;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
pub use body::*;
|
|
||||||
pub use cursor::*;
|
|
||||||
pub use error::*;
|
|
||||||
pub use expr::*;
|
|
||||||
use util::WHITESPACE_SET;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
120
src/util/bits.rs
Normal file
120
src/util/bits.rs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
pub const fn u(x: i32) -> u32 {
|
||||||
|
unsafe { std::mem::transmute(x) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn low_mask(high: u32, low: u32) -> u32 {
|
||||||
|
(2u32.unbounded_shl(high - low)).wrapping_sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn mask(high: u32, low: u32) -> u32 {
|
||||||
|
low_mask(high, low).unbounded_shl(low)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn bits(x: i32, high: u32, low: u32) -> u32 {
|
||||||
|
let x = u(x);
|
||||||
|
x.unbounded_shr(low) & low_mask(high, low)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn bit(x: i32, i: u32) -> u32 {
|
||||||
|
let x = u(x);
|
||||||
|
x.unbounded_shr(i) & 2u32 << i
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
|
||||||
|
if x < 0 {
|
||||||
|
if high == low {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
(bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
|
||||||
|
} else {
|
||||||
|
bits(x, high, low) << low == u(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use std::ops::{Add, Shl, Shr};
|
||||||
|
//
|
||||||
|
// pub const fn u(x: i32) -> u32 {
|
||||||
|
// unsafe { std::mem::transmute(x) }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub const fn low_mask(high: u8, low: u8) -> u32 {
|
||||||
|
// (2u32.unbounded_shl(high - low)).wrapping_sub(1)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub const fn mask(high: u8, low: u8) -> u32 {
|
||||||
|
// low_mask(high, low).unbounded_shl(low)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub const fn bits(x: i32, high: u8, low: u8) -> u32 {
|
||||||
|
// let x = u(x);
|
||||||
|
// x.unbounded_shr(low) & low_mask(high, low)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub const fn bit(x: i32, i: u32) -> u32 {
|
||||||
|
// let x = u(x);
|
||||||
|
// x.unbounded_shr(i) & 2u32 << i
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub const fn in_bit_range<T: Shl<u8, Output = T> + Shr<u8, Output = T>>(
|
||||||
|
// x: T,
|
||||||
|
// high: u8,
|
||||||
|
// low: u8,
|
||||||
|
// ) -> bool {
|
||||||
|
// if x < 0 {
|
||||||
|
// if high == low {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// (bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
|
||||||
|
// } else {
|
||||||
|
// bits(x, high, low) << low == u(x)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct Bits<
|
||||||
|
// T: Shl<u8, Output = T> + Shr<u8, Output = T>,
|
||||||
|
// const S: bool,
|
||||||
|
// const H: u8,
|
||||||
|
// const L: u8,
|
||||||
|
// >(T);
|
||||||
|
// pub struct U32Bits<const H: u32, const L: u32>(u32);
|
||||||
|
//
|
||||||
|
// impl<T: Shl<u8, Output = T> + Shr<u8, Output = T> + Add<u8, Output = T>, const S: bool, const H: u8, const L: u8>
|
||||||
|
// Bits<T, S, H, L>
|
||||||
|
// {
|
||||||
|
// pub const fn new(val: T) -> Self {
|
||||||
|
// assert!(in_bit_range(val, H, L));
|
||||||
|
// Self(val + L)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_bits() {
|
||||||
|
assert_eq!(bits(0b10111010, 5, 3), 0b111);
|
||||||
|
assert_eq!(bits(0b10111010, 7, 5), 0b101);
|
||||||
|
assert_eq!(bits(0b10111010, 2, 0), 0b010);
|
||||||
|
assert_eq!(bits(0b10111010, 7, 7), 0b1);
|
||||||
|
assert_eq!(bits(0b1, 0, 0), 0b1);
|
||||||
|
assert_eq!(bits(0b1, 1, 1), 0b0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range() {
|
||||||
|
assert!(!in_bit_range(0b00111100, 5, 3));
|
||||||
|
assert!(!in_bit_range(0b00111100, 4, 2));
|
||||||
|
assert!(in_bit_range(0b000111100, 5, 2));
|
||||||
|
assert!(in_bit_range(0b000000001, 0, 0));
|
||||||
|
assert!(in_bit_range(0b000001000, 3, 3));
|
||||||
|
|
||||||
|
assert!(!in_bit_range(-3, 1, 0));
|
||||||
|
assert!(!in_bit_range(-5, 2, 2));
|
||||||
|
assert!(!in_bit_range(-5, 4, 3));
|
||||||
|
assert!(in_bit_range(-1, 1, 0));
|
||||||
|
assert!(in_bit_range(-4, 2, 0));
|
||||||
|
assert!(in_bit_range(-5, 3, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +1,5 @@
|
|||||||
use core::fmt;
|
mod padder;
|
||||||
|
mod bits;
|
||||||
|
|
||||||
pub struct Padder<'buf> {
|
pub use padder::*;
|
||||||
buf: &'buf mut (dyn fmt::Write + 'buf),
|
pub use bits::*;
|
||||||
on_newline: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Write for Padder<'_> {
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
for s in s.split_inclusive('\n') {
|
|
||||||
if self.on_newline {
|
|
||||||
self.buf.write_str(" ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.on_newline = s.ends_with('\n');
|
|
||||||
self.buf.write_str(s)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
|
||||||
if self.on_newline {
|
|
||||||
self.buf.write_str(" ")?;
|
|
||||||
}
|
|
||||||
self.on_newline = c == '\n';
|
|
||||||
self.buf.write_char(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'buf> Padder<'buf> {
|
|
||||||
pub fn new(buf: &'buf mut (dyn fmt::Write + 'buf)) -> Self {
|
|
||||||
Self {
|
|
||||||
buf,
|
|
||||||
on_newline: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
38
src/util/padder.rs
Normal file
38
src/util/padder.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
pub struct Padder<'buf> {
|
||||||
|
buf: &'buf mut (dyn fmt::Write + 'buf),
|
||||||
|
on_newline: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for Padder<'_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for s in s.split_inclusive('\n') {
|
||||||
|
if self.on_newline {
|
||||||
|
self.buf.write_str(" ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.on_newline = s.ends_with('\n');
|
||||||
|
self.buf.write_str(s)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||||
|
if self.on_newline {
|
||||||
|
self.buf.write_str(" ")?;
|
||||||
|
}
|
||||||
|
self.on_newline = c == '\n';
|
||||||
|
self.buf.write_char(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'buf> Padder<'buf> {
|
||||||
|
pub fn new(buf: &'buf mut (dyn fmt::Write + 'buf)) -> Self {
|
||||||
|
Self {
|
||||||
|
buf,
|
||||||
|
on_newline: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user