WORKING ASM FUNCTION CALLS
This commit is contained in:
@@ -48,6 +48,7 @@ pub struct SectionHeader {
|
|||||||
entry_size: u64,
|
entry_size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is currently specialized for riscv64; obviously add params later
|
||||||
pub fn create(program: Vec<u8>, start_offset: u64) -> Vec<u8> {
|
pub fn create(program: Vec<u8>, start_offset: u64) -> Vec<u8> {
|
||||||
let addr_start = 0x1000;
|
let addr_start = 0x1000;
|
||||||
let page_size = 0x1000;
|
let page_size = 0x1000;
|
||||||
@@ -5,13 +5,14 @@ use std::{
|
|||||||
process::Command,
|
process::Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod riscv64;
|
mod elf;
|
||||||
mod program;
|
mod program;
|
||||||
|
mod riscv64;
|
||||||
mod target;
|
mod target;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
let dir = Path::new("build");
|
let dir = Path::new("./build");
|
||||||
create_dir_all(dir).expect("Failed to create or confirm build directory");
|
create_dir_all(dir).expect("Failed to create or confirm build directory");
|
||||||
let name = Path::new("test");
|
let name = Path::new("test");
|
||||||
let path = dir.join(name);
|
let path = dir.join(name);
|
||||||
@@ -26,9 +27,35 @@ pub fn main() {
|
|||||||
file.write_all(&riscv64::gen())
|
file.write_all(&riscv64::gen())
|
||||||
.expect("Failed to write to file");
|
.expect("Failed to write to file");
|
||||||
file.sync_all().expect("Failed to sync file");
|
file.sync_all().expect("Failed to sync file");
|
||||||
if let Ok(mut process) = Command::new("qemu-riscv64").arg(path).spawn() {
|
let mut p = Command::new("qemu-riscv64");
|
||||||
|
let run_gdb = std::env::args().nth(1).is_some_and(|a| a == "d");
|
||||||
|
let proc = if run_gdb {
|
||||||
|
p.arg("-g").arg("1234").arg(path).spawn()
|
||||||
|
} else {
|
||||||
|
p.arg(path).spawn()
|
||||||
|
};
|
||||||
|
if let Ok(mut process) = proc {
|
||||||
|
let mut print_exit = true;
|
||||||
|
if run_gdb {
|
||||||
|
match Command::new("gdb")
|
||||||
|
.arg("-q")
|
||||||
|
.arg("-ex")
|
||||||
|
.arg("target remote :1234")
|
||||||
|
.arg(path)
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
Ok(mut gdb) => {
|
||||||
|
gdb.wait().expect("xd");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
print_exit = false;
|
||||||
|
println!("gdb error: {e:?}");
|
||||||
|
process.kill().expect("uh oh");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Ok(status) = process.wait() {
|
if let Ok(status) = process.wait() {
|
||||||
if status.code().is_none_or(|c| c != 0) {
|
if print_exit && status.code().is_none_or(|c| c != 0) {
|
||||||
println!("{}", status);
|
println!("{}", status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,4 +66,3 @@ pub fn main() {
|
|||||||
// riscv64-linux-gnu-gdb -q \
|
// riscv64-linux-gnu-gdb -q \
|
||||||
// -ex "target remote :1234" \
|
// -ex "target remote :1234" \
|
||||||
// test
|
// test
|
||||||
|
|
||||||
|
|||||||
@@ -1,53 +1,35 @@
|
|||||||
use std::collections::HashMap;
|
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>(
|
pub fn create_program<I: Instr>(
|
||||||
ro_data: HashMap<String, Vec<u8>>,
|
ro_data: HashMap<String, Vec<u8>>,
|
||||||
functions: Vec<Function<I>>,
|
functions: Vec<Function<I>>,
|
||||||
) -> Vec<u8> {
|
) -> (Vec<u8>, Option<u64>) {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let mut ro_map = HashMap::new();
|
let mut sym_map = HashMap::new();
|
||||||
for (key, val) in ro_data {
|
for (key, val) in ro_data {
|
||||||
ro_map.insert(key, data.len());
|
sym_map.insert(key, data.len() as u64);
|
||||||
data.extend(val);
|
data.extend(val);
|
||||||
}
|
}
|
||||||
// let mut fn_map = HashMap::new();
|
let mut start = None;
|
||||||
for fun in functions {
|
for fun in functions {
|
||||||
|
if fun.label == "_start" {
|
||||||
|
start = Some(data.len() as u64);
|
||||||
|
}
|
||||||
|
sym_map.insert(fun.label, data.len() as u64);
|
||||||
for i in fun.instructions {
|
for i in fun.instructions {
|
||||||
data.extend(i.to_le_bytes());
|
let pos = data.len() as u64;
|
||||||
|
i.push(&mut data, &sym_map, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data
|
(data, start)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Function<I: Instr> {
|
pub struct Function<I: Instr> {
|
||||||
label: String,
|
pub label: String,
|
||||||
instructions: Vec<I>,
|
pub instructions: Vec<I>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instr {
|
pub trait Instr {
|
||||||
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8>;
|
fn push(&self, data: &mut Vec<u8>, ro_map: &HashMap<String, u64>, pos: u64) -> Option<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SymbolInstr {
|
|
||||||
i: usize
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,2 +1,57 @@
|
|||||||
pub enum Instruction {
|
use crate::compiler::program::Instr;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub enum AsmInstruction {
|
||||||
|
Addi(Reg, Reg, i32),
|
||||||
|
La(Reg, String),
|
||||||
|
Jal(Reg, i32),
|
||||||
|
Jala(String),
|
||||||
|
Ja(String),
|
||||||
|
Ret,
|
||||||
|
Ecall,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instr for AsmInstruction {
|
||||||
|
fn push(
|
||||||
|
&self,
|
||||||
|
data: &mut Vec<u8>,
|
||||||
|
sym_map: &std::collections::HashMap<String, u64>,
|
||||||
|
pos: u64,
|
||||||
|
) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Self::Addi(dest, src, imm) => {
|
||||||
|
data.extend(addi(*dest, *src, BitsI32::new(*imm)).to_le_bytes());
|
||||||
|
}
|
||||||
|
Self::La(dest, sym) => {
|
||||||
|
if let Some(addr) = sym_map.get(sym) {
|
||||||
|
let offset = *addr as i32 - pos as i32;
|
||||||
|
data.extend(auipc(*dest, BitsI32::new(0)).to_le_bytes());
|
||||||
|
data.extend(addi(*dest, *dest, BitsI32::new(offset)).to_le_bytes());
|
||||||
|
} else {
|
||||||
|
return Some(sym.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Jal(dest, offset) => data.extend(jal(*dest, BitsI32::new(*offset)).to_le_bytes()),
|
||||||
|
Self::Ja(sym) => {
|
||||||
|
if let Some(addr) = sym_map.get(sym) {
|
||||||
|
let offset = *addr as i32 - pos as i32;
|
||||||
|
data.extend(j(BitsI32::new(offset)).to_le_bytes());
|
||||||
|
} else {
|
||||||
|
return Some(sym.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Jala(sym) => {
|
||||||
|
if let Some(addr) = sym_map.get(sym) {
|
||||||
|
let offset = *addr as i32 - pos as i32;
|
||||||
|
data.extend(jal(ra, BitsI32::new(offset)).to_le_bytes());
|
||||||
|
} else {
|
||||||
|
return Some(sym.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Ret => data.extend(ret().to_le_bytes()),
|
||||||
|
Self::Ecall => data.extend(ecall().to_le_bytes()),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
78
src/compiler/riscv64/base.rs
Normal file
78
src/compiler/riscv64/base.rs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
use crate::util::Bits32;
|
||||||
|
|
||||||
|
use super::Reg;
|
||||||
|
|
||||||
|
pub struct Instruction(u32);
|
||||||
|
|
||||||
|
use Instruction as I;
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
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: u32,
|
||||||
|
rs2: Reg,
|
||||||
|
rs1: Reg,
|
||||||
|
funct3: Bits32<2, 0>,
|
||||||
|
rd: Reg,
|
||||||
|
opcode: u32,
|
||||||
|
) -> I {
|
||||||
|
I((funct7 << 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)
|
||||||
|
}
|
||||||
24
src/compiler/riscv64/funct.rs
Normal file
24
src/compiler/riscv64/funct.rs
Normal 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);
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,33 +1,81 @@
|
|||||||
use crate::compiler::program::Instr;
|
|
||||||
mod elf;
|
|
||||||
mod instruction;
|
|
||||||
mod asm;
|
mod asm;
|
||||||
|
mod base;
|
||||||
|
mod funct;
|
||||||
|
mod opcode;
|
||||||
|
mod reg;
|
||||||
|
mod single;
|
||||||
|
|
||||||
use instruction::*;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::{elf, program::{create_program, Function}};
|
||||||
|
use crate::util::BitsI32;
|
||||||
|
use base::*;
|
||||||
|
use funct::{op::*, width};
|
||||||
|
use opcode::*;
|
||||||
|
pub use reg::*;
|
||||||
|
|
||||||
|
use single::*;
|
||||||
|
|
||||||
pub fn gen() -> Vec<u8> {
|
pub fn gen() -> Vec<u8> {
|
||||||
let mut program = Vec::new();
|
use asm::AsmInstruction as I;
|
||||||
|
// 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, BitsI32::new(0)),
|
||||||
|
// addi(t0, t0, BitsI32::new(-(start as i32))),
|
||||||
|
// addi(a0, zero, BitsI32::new(1)),
|
||||||
|
// mv(a1, t0),
|
||||||
|
// addi(a2, zero, BitsI32::new(msg.len() as i32)),
|
||||||
|
// addi(a7, zero, BitsI32::new(64)),
|
||||||
|
// addi(t0, zero, const { BitsI32::new(-10) }),
|
||||||
|
// ecall(),
|
||||||
|
// // exit
|
||||||
|
// addi(a0, zero, BitsI32::new(0)),
|
||||||
|
// addi(a7, zero, BitsI32::new(93)),
|
||||||
|
// ecall(),
|
||||||
|
// j(BitsI32::new(0)),
|
||||||
|
// ];
|
||||||
|
// for i in instructions {
|
||||||
|
// program.extend(i.to_le_bytes());
|
||||||
|
// }
|
||||||
let msg = b"Hello world!\n";
|
let msg = b"Hello world!\n";
|
||||||
program.extend(msg);
|
let msg2 = b"IT WORKS!!!!\n";
|
||||||
program.resize(((program.len() - 1) / 4 + 1) * 4, 0);
|
let ro_data = HashMap::from([
|
||||||
let start = program.len() as u64;
|
("msg".to_string(), msg.to_vec()),
|
||||||
let instructions = [
|
("msg2".to_string(), msg2.to_vec()),
|
||||||
auipc(t0, 0),
|
]);
|
||||||
addi(t0, t0, -(start as i32)),
|
let functions = vec![
|
||||||
addi(a0, zero, 1),
|
Function {
|
||||||
mv(a1, t0),
|
label: "print_stuff".to_string(),
|
||||||
addi(a2, zero, msg.len() as i32),
|
instructions: vec![
|
||||||
addi(a7, zero, 64),
|
I::Addi(a0, zero, 1),
|
||||||
addi(t0, zero, 200),
|
I::La(a1, "msg".to_string()),
|
||||||
ecall(),
|
I::Addi(a2, zero, msg.len() as i32),
|
||||||
// exit
|
I::Addi(a7, zero, 64),
|
||||||
addi(a0, zero, 0),
|
I::Ecall,
|
||||||
addi(a7, zero, 93),
|
I::Addi(a0, zero, 1),
|
||||||
ecall(),
|
I::La(a1, "msg2".to_string()),
|
||||||
j(0),
|
I::Addi(a2, zero, msg2.len() as i32),
|
||||||
|
I::Addi(a7, zero, 64),
|
||||||
|
I::Ecall,
|
||||||
|
I::Ret,
|
||||||
|
]
|
||||||
|
},
|
||||||
|
Function {
|
||||||
|
label: "_start".to_string(),
|
||||||
|
instructions: vec![
|
||||||
|
I::Jala("print_stuff".to_string()),
|
||||||
|
I::Ecall,
|
||||||
|
I::Addi(a0, zero, 0),
|
||||||
|
I::Addi(a7, zero, 93),
|
||||||
|
I::Ecall,
|
||||||
|
I::Jal(zero, 0),
|
||||||
|
]
|
||||||
|
},
|
||||||
];
|
];
|
||||||
for i in instructions {
|
let (program, start) = create_program(ro_data, functions);
|
||||||
program.extend(i.to_le_bytes());
|
elf::create(program, start.expect("no start!"))
|
||||||
}
|
|
||||||
elf::create(program, start)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ pub const AUIPC : u32 = 0b0010111;
|
|||||||
pub const IMM_OP: u32 = 0b0010011;
|
pub const IMM_OP: u32 = 0b0010011;
|
||||||
pub const OP : u32 = 0b0110011;
|
pub const OP : u32 = 0b0110011;
|
||||||
pub const JAL : u32 = 0b1101111;
|
pub const JAL : u32 = 0b1101111;
|
||||||
|
pub const JALR : u32 = 0b1100111;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Reg(u8);
|
pub struct Reg(u8);
|
||||||
|
|
||||||
/// hard wired 0
|
/// hard wired 0
|
||||||
51
src/compiler/riscv64/single.rs
Normal file
51
src/compiler/riscv64/single.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use crate::util::{Bits32, BitsI32};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use Instruction 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 addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> I {
|
||||||
|
i_type(imm.to_u(), src, ADD, 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)
|
||||||
|
}
|
||||||
137
src/util/bits.rs
137
src/util/bits.rs
@@ -2,74 +2,59 @@ pub const fn u(x: i32) -> u32 {
|
|||||||
unsafe { std::mem::transmute(x) }
|
unsafe { std::mem::transmute(x) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn low_mask(high: u32, low: u32) -> u32 {
|
pub const fn base_mask(len: u8) -> u32 {
|
||||||
(2u32.unbounded_shl(high - low)).wrapping_sub(1)
|
(2 << len) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn mask(high: u32, low: u32) -> u32 {
|
pub const fn mask(h: u8, l: u8) -> u32 {
|
||||||
low_mask(high, low).unbounded_shl(low)
|
base_mask(h - l) << l
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn bits(x: i32, high: u32, low: u32) -> u32 {
|
#[derive(Debug, Clone, Copy)]
|
||||||
let x = u(x);
|
pub struct Bits32<const H: u8, const L: u8>(u32);
|
||||||
x.unbounded_shr(low) & low_mask(high, low)
|
#[derive(Debug, Clone, Copy)]
|
||||||
}
|
pub struct BitsI32<const H: u8, const L: u8>(u32);
|
||||||
|
|
||||||
pub const fn bit(x: i32, i: u32) -> u32 {
|
impl<const H: u8, const L: u8> Bits32<H, L> {
|
||||||
let x = u(x);
|
pub const fn new(val: u32) -> Self {
|
||||||
x.unbounded_shr(i) & 2u32 << i
|
let lsh = 31 - H;
|
||||||
}
|
let rsh = lsh + L;
|
||||||
|
debug_assert!(((val << lsh) >> rsh) == (val >> L));
|
||||||
pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
|
Self(val)
|
||||||
if x < 0 {
|
}
|
||||||
if high == low {
|
pub const fn val(&self) -> u32 {
|
||||||
return false;
|
self.0
|
||||||
}
|
}
|
||||||
(bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
|
pub const fn bit(&self, i: u8) -> u32 {
|
||||||
} else {
|
(self.0 >> i) & 1
|
||||||
bits(x, high, low) << low == u(x)
|
}
|
||||||
|
pub const fn bits(&self, h: u8, l: u8) -> u32 {
|
||||||
|
(self.0 >> l) & base_mask(h - l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use std::ops::{Add, Shl, Shr};
|
impl<const H: u8, const L: u8> BitsI32<H, L> {
|
||||||
//
|
pub const fn new(val: i32) -> Self {
|
||||||
// pub const fn u(x: i32) -> u32 {
|
let lsh = 31 - H;
|
||||||
// unsafe { std::mem::transmute(x) }
|
let rsh = lsh + L;
|
||||||
// }
|
assert!(((val << lsh) >> rsh) == (val >> L));
|
||||||
//
|
Self(u(val) & mask(H, L))
|
||||||
// pub const fn low_mask(high: u8, low: u8) -> u32 {
|
}
|
||||||
// (2u32.unbounded_shl(high - low)).wrapping_sub(1)
|
pub const fn tryy(val: i32) -> Option<Self> {
|
||||||
// }
|
let lsh = 31 - H;
|
||||||
//
|
let rsh = lsh + L;
|
||||||
// pub const fn mask(high: u8, low: u8) -> u32 {
|
if ((val << lsh) >> rsh) == (val >> L) {
|
||||||
// low_mask(high, low).unbounded_shl(low)
|
Some(Self(u(val) & mask(H, L)))
|
||||||
// }
|
} else {
|
||||||
//
|
None
|
||||||
// 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 to_u(self) -> Bits32<H, L> {
|
||||||
// }
|
Bits32(self.0)
|
||||||
//
|
}
|
||||||
// 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)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
// I hate (love) rust https://github.com/rust-lang/rust-project-goals/issues/106
|
||||||
// pub struct Bits<
|
// pub struct Bits<
|
||||||
// T: Shl<u8, Output = T> + Shr<u8, Output = T>,
|
// T: Shl<u8, Output = T> + Shr<u8, Output = T>,
|
||||||
// const S: bool,
|
// const S: bool,
|
||||||
@@ -78,7 +63,7 @@ pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
|
|||||||
// >(T);
|
// >(T);
|
||||||
// pub struct U32Bits<const H: u32, const L: u32>(u32);
|
// 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>
|
// impl<T: const Shl<u8, Output = T> + const Shr<u8, Output = T>, const S: bool, const H: u8, const L: u8>
|
||||||
// Bits<T, S, H, L>
|
// Bits<T, S, H, L>
|
||||||
// {
|
// {
|
||||||
// pub const fn new(val: T) -> Self {
|
// pub const fn new(val: T) -> Self {
|
||||||
@@ -86,35 +71,3 @@ pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
|
|||||||
// Self(val + 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user