DSCS IR generation progress
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
__main_0:
|
||||
_ = print::print(v_0)
|
||||
return
|
||||
}
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
// GENERATED BY DSC COMPILER
|
||||
// Generated at 2026-08-20 11:13:23
|
||||
// Generated at 2026-08-21 19:34:30
|
||||
|
||||
// Imports
|
||||
include alloc: "./lib/memory/block_alloc.dsa"
|
||||
@@ -30,7 +30,7 @@ _ret:
|
||||
pop bpr
|
||||
ret
|
||||
|
||||
db str_4: "successful free of ptr"
|
||||
db str_8: "successful free of ptr"
|
||||
// fn main() -> u32
|
||||
main:
|
||||
push bpr
|
||||
@@ -121,18 +121,18 @@ main:
|
||||
ldw spr, rg2, 8
|
||||
lli 0, rg4
|
||||
ieq rg2, rg4, rg5
|
||||
jez rg5, _else_2
|
||||
_then_1:
|
||||
lwi str_4, rg4
|
||||
jez rg5, _else_6
|
||||
_then_5:
|
||||
lwi str_8, rg4
|
||||
stw rg2, spr, 8
|
||||
// push arg 0
|
||||
push rg4
|
||||
call print::print
|
||||
pop zero
|
||||
jmp _end_3
|
||||
_else_2:
|
||||
jmp _end_7
|
||||
_else_6:
|
||||
nop
|
||||
_end_3:
|
||||
_end_7:
|
||||
lli 0, rg4
|
||||
stw rg4, bpr, 8
|
||||
jmp _ret
|
||||
@@ -0,0 +1,44 @@
|
||||
fn main() {
|
||||
__main_0:
|
||||
v_1 = literal 64
|
||||
v_2 = literal 32
|
||||
v_0 = alloc::init(v_1, v_2)
|
||||
v_3 = copy v_0
|
||||
_ = print::print_hex_word(v_3)
|
||||
_ = print::print_newline()
|
||||
v_5 = copy v_0
|
||||
v_4 = alloc::alloc(v_5)
|
||||
v_6 = copy v_4
|
||||
_ = print::print_hex_word(v_6)
|
||||
v_7 = copy v_4
|
||||
v_8 = literal 200
|
||||
store v_7, v_8
|
||||
_ = print::print_newline()
|
||||
v_10 = copy v_0
|
||||
v_9 = alloc::alloc(v_10)
|
||||
v_11 = copy v_9
|
||||
_ = print::print_hex_word(v_11)
|
||||
_ = print::print_newline()
|
||||
_ = print::print_num(v_12)
|
||||
v_13 = copy v_0
|
||||
_ = alloc::free(v_13, v_14)
|
||||
v_16 = copy v_0
|
||||
v_15 = alloc::alloc(v_16)
|
||||
_ = print::print_newline()
|
||||
v_17 = copy v_15
|
||||
_ = print::print_hex_word(v_17)
|
||||
_ = print::print_newline()
|
||||
v_18 = copy v_4
|
||||
_ = print::print_hex_word(v_18)
|
||||
if v_19 then __then_2 else __else_2
|
||||
__then_2:
|
||||
_ = print::print(v_20)
|
||||
jump __endif_2
|
||||
__else_2:
|
||||
jump __endif_2
|
||||
__endif_2:
|
||||
v_21 = literal 0
|
||||
return v_21
|
||||
__unreachable_3:
|
||||
return
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
use super::instruction::{InsBlock as IB, Instruction as I, Label};
|
||||
use super::registers::{Register, RegisterAllocator};
|
||||
use crate::backend::instruction::Comparison::{
|
||||
use super::instruction::InsBlock as IB;
|
||||
use super::registers::RegisterAllocator;
|
||||
use crate::model::CompilerError;
|
||||
use crate::model::ast::{
|
||||
AssignmentOperator, BinaryOperator, Call, ConstExpr, Declaration, Dependency, Expression,
|
||||
Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
||||
};
|
||||
use crate::model::instruction::Comparison::{
|
||||
Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual,
|
||||
};
|
||||
use crate::model::{
|
||||
AssignmentOperator, BinaryOperator, Call, CompilerError, ConstExpr, Declaration, Dependency,
|
||||
Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
||||
};
|
||||
use crate::model::instruction::{Instruction as I, Label};
|
||||
use crate::model::register::Register;
|
||||
use chrono::{DateTime, Local};
|
||||
use dsa_tasks::ProcessAgent;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::backend::registers::Register;
|
||||
use crate::model::instruction::Instruction;
|
||||
|
||||
pub struct InsBlock {
|
||||
instructions: Vec<Instruction>,
|
||||
@@ -55,804 +53,3 @@ impl From<Instruction> for InsBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Comparison {
|
||||
Greater,
|
||||
GreaterEqual,
|
||||
Equal,
|
||||
NotEqual,
|
||||
Less,
|
||||
LessEqual,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Instruction {
|
||||
// Labels and comments
|
||||
Label(Label),
|
||||
Comment {
|
||||
text: String,
|
||||
top_level: bool,
|
||||
},
|
||||
Newline,
|
||||
|
||||
// Data Directives
|
||||
Db {
|
||||
label: String,
|
||||
data: Vec<u8>,
|
||||
},
|
||||
Dh {
|
||||
label: String,
|
||||
data: Vec<u16>,
|
||||
},
|
||||
Dw {
|
||||
label: String,
|
||||
data: Vec<u32>,
|
||||
},
|
||||
DString {
|
||||
// alias for db.
|
||||
label: String,
|
||||
data: String,
|
||||
},
|
||||
|
||||
Resx {
|
||||
label: String,
|
||||
size: u32,
|
||||
},
|
||||
|
||||
// Include
|
||||
Include {
|
||||
name: String,
|
||||
path: String,
|
||||
},
|
||||
|
||||
// Data movement
|
||||
Mov {
|
||||
src: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Movs {
|
||||
src: Register,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
// Memory operations
|
||||
Ldb {
|
||||
src: MemOperand,
|
||||
dest: Register,
|
||||
},
|
||||
Ldh {
|
||||
src: MemOperand,
|
||||
dest: Register,
|
||||
},
|
||||
Ldw {
|
||||
src: MemOperand,
|
||||
dest: Register,
|
||||
},
|
||||
Stb {
|
||||
src: Register,
|
||||
dest: MemOperand,
|
||||
},
|
||||
Sth {
|
||||
src: Register,
|
||||
dest: MemOperand,
|
||||
},
|
||||
Stw {
|
||||
src: Register,
|
||||
dest: MemOperand,
|
||||
},
|
||||
|
||||
// Immediate loads
|
||||
Lli {
|
||||
imm: Imm,
|
||||
dest: Register,
|
||||
},
|
||||
Lui {
|
||||
imm: Imm,
|
||||
dest: Register,
|
||||
},
|
||||
Lwi {
|
||||
imm: Imm,
|
||||
dest: Register,
|
||||
},
|
||||
LwiLabel {
|
||||
label: String,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
// Arithmetic
|
||||
Add {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Sub {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
AddI {
|
||||
src: Register,
|
||||
imm: Imm,
|
||||
dest: Option<Register>,
|
||||
},
|
||||
SubI {
|
||||
src: Register,
|
||||
imm: Imm,
|
||||
dest: Option<Register>,
|
||||
},
|
||||
|
||||
// Bitwise
|
||||
And {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Or {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Xor {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Not {
|
||||
src: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Nand {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Nor {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Xnor {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
// Shifts
|
||||
Shl {
|
||||
src1: Register,
|
||||
r_shamt: Register,
|
||||
i_shamt: u16,
|
||||
dest: Register,
|
||||
},
|
||||
Shr {
|
||||
src1: Register,
|
||||
r_shamt: Register,
|
||||
i_shamt: u16,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
Ieq {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Ine {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Ilt {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Ile {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Igt {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Ige {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
// Jumps
|
||||
Jmp {
|
||||
target: Label,
|
||||
},
|
||||
|
||||
Jnz {
|
||||
src: Register,
|
||||
target: Label,
|
||||
},
|
||||
Jez {
|
||||
src: Register,
|
||||
target: Label,
|
||||
},
|
||||
|
||||
Jic {
|
||||
target: Label,
|
||||
},
|
||||
Jnc {
|
||||
target: Label,
|
||||
},
|
||||
|
||||
// Stack
|
||||
Push {
|
||||
reg: Register,
|
||||
},
|
||||
Pop {
|
||||
reg: Register,
|
||||
},
|
||||
|
||||
// Function calls
|
||||
Call {
|
||||
target: String,
|
||||
}, // namespace::function
|
||||
Return,
|
||||
|
||||
// System
|
||||
Hlt,
|
||||
Nop,
|
||||
Int {
|
||||
code: u8,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum DataDirective {
|
||||
U8(Vec<u8>),
|
||||
U16(Vec<u16>),
|
||||
U32(Vec<u32>),
|
||||
String(String),
|
||||
Char(char),
|
||||
}
|
||||
|
||||
impl fmt::Display for Instruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Label(l) => write!(f, "{}:", l),
|
||||
Self::Newline => write!(f, ""), /* empty string as newlines are inserted */
|
||||
// automatically.
|
||||
Self::Comment { text, top_level } => write!(
|
||||
f,
|
||||
"{}",
|
||||
text.lines()
|
||||
.map(|line| format!(
|
||||
"{}// {}",
|
||||
if *top_level { "" } else { " " },
|
||||
line.trim(),
|
||||
))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
),
|
||||
|
||||
Self::Include { name, path } => write!(f, "include {name}: \"{}\"", path),
|
||||
|
||||
Self::Db { label, data } => write!(
|
||||
f,
|
||||
"db {}: {}",
|
||||
label,
|
||||
data.iter()
|
||||
.map(|&b| format!("{:#04X}", b))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
Self::Dh { label, data } => write!(
|
||||
f,
|
||||
"dh {}: {}",
|
||||
label,
|
||||
data.iter()
|
||||
.map(|&b| format!("{:#06X}", b))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
Self::Dw { label, data } => write!(
|
||||
f,
|
||||
"dw {}: {}",
|
||||
label,
|
||||
data.iter()
|
||||
.map(|&b| format!("{:#08X}", b))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
Self::DString { label, data } => write!(f, "db {}: \"{}\"", label, data),
|
||||
|
||||
Self::Resx { label, size } => write!(f, "resx {}: {}", label, size),
|
||||
|
||||
Self::Mov { src, dest } => write!(f, " mov {}, {}", src, dest),
|
||||
Self::Movs { src, dest } => write!(f, " movs {}, {}", src, dest),
|
||||
|
||||
Self::Ldb { src: addr, dest } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " ldb {}, {}, {}", reg, dest, offset)
|
||||
}
|
||||
Self::Ldh { src: addr, dest } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " ldh {}, {}, {}", reg, dest, offset)
|
||||
}
|
||||
Self::Ldw { src, dest } => {
|
||||
let (reg, offset) = reg_and_offset(src);
|
||||
write!(f, " ldw {}, {}, {}", reg, dest, offset)
|
||||
}
|
||||
// Self::Ldbs { addr, dest } => {
|
||||
// write!(f, " ldbs {}, {}", format_mem_operand(addr), dest)
|
||||
// }
|
||||
// Self::Ldhs { addr, dest } => {
|
||||
// write!(f, " ldhs {}, {}", format_mem_operand(addr), dest)
|
||||
// }
|
||||
// Self::Ldws { addr, dest } => {
|
||||
// write!(f, " ldws {}, {}", format_mem_operand(addr), dest)
|
||||
// }
|
||||
Self::Stb { src, dest: addr } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " stb {}, {}, {}", src, reg, offset)
|
||||
}
|
||||
Self::Sth { src, dest: addr } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " sth {}, {}, {}", src, reg, offset)
|
||||
}
|
||||
Self::Stw { src, dest: addr } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " stw {}, {}, {}", src, reg, offset)
|
||||
}
|
||||
|
||||
Self::Lli { imm, dest } => write!(f, " lli {}, {}", imm, dest),
|
||||
Self::Lui { imm, dest } => write!(f, " lui {}, {}", imm, dest),
|
||||
Self::Lwi { imm, dest } => write!(f, " lwi {}, {}", imm, dest),
|
||||
Self::LwiLabel { label, dest } => write!(f, " lwi {}, {}", label, dest),
|
||||
|
||||
// arithmetic
|
||||
Self::Add { src1, src2, dest } => {
|
||||
write!(f, " add {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Sub { src1, src2, dest } => {
|
||||
write!(f, " sub {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::And { src1, src2, dest } => {
|
||||
write!(f, " and {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Or { src1, src2, dest } => {
|
||||
write!(f, " or {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Nand { src1, src2, dest } => {
|
||||
write!(f, " nand {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Xor { src1, src2, dest } => {
|
||||
write!(f, " xor {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Nor { src1, src2, dest } => {
|
||||
write!(f, " nor {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Not { src, dest } => {
|
||||
write!(f, " not {} {}", src, dest)
|
||||
}
|
||||
Self::Xnor { src1, src2, dest } => {
|
||||
write!(f, " xnor {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::AddI { src, imm, dest } => {
|
||||
if let Some(d) = dest {
|
||||
write!(f, " addi {}, {}, {}", src, imm, d)
|
||||
} else {
|
||||
write!(f, " addi {}, {}", src, imm)
|
||||
}
|
||||
}
|
||||
Self::SubI { src, imm, dest } => {
|
||||
if let Some(d) = dest {
|
||||
write!(f, " subi {}, {}, {}", src, imm, d)
|
||||
} else {
|
||||
write!(f, " subi {}, {}", src, imm)
|
||||
}
|
||||
}
|
||||
|
||||
// shift instructions
|
||||
Self::Shl {
|
||||
src1,
|
||||
r_shamt,
|
||||
i_shamt,
|
||||
dest,
|
||||
} => {
|
||||
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
|
||||
}
|
||||
Self::Shr {
|
||||
src1,
|
||||
r_shamt,
|
||||
i_shamt,
|
||||
dest,
|
||||
} => {
|
||||
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
|
||||
}
|
||||
|
||||
Self::Ieq { src1, src2, dest } => write!(f, " ieq {}, {}, {}", src1, src2, dest),
|
||||
Self::Ine { src1, src2, dest } => write!(f, " ine {}, {}, {}", src1, src2, dest),
|
||||
Self::Ilt { src1, src2, dest } => write!(f, " ilt {}, {}, {}", src1, src2, dest),
|
||||
Self::Ile { src1, src2, dest } => write!(f, " ile {}, {}, {}", src1, src2, dest),
|
||||
Self::Igt { src1, src2, dest } => write!(f, " igt {}, {}, {}", src1, src2, dest),
|
||||
Self::Ige { src1, src2, dest } => write!(f, " ige {}, {}, {}", src1, src2, dest),
|
||||
|
||||
// jump instructions
|
||||
Self::Jmp { target } => write!(f, " jmp {}", target),
|
||||
Self::Jic { target } => write!(f, " jic {}", target),
|
||||
Self::Jnc { target } => write!(f, " jnc {}", target),
|
||||
Self::Jez { src, target } => write!(f, " jez {}, {}", src, target),
|
||||
Self::Jnz { src, target } => write!(f, " jnz {}, {}", src, target),
|
||||
|
||||
// stack instructions
|
||||
Self::Push { reg } => write!(f, " push {}", reg),
|
||||
Self::Pop { reg } => write!(f, " pop {}", reg),
|
||||
|
||||
// call & return instructions
|
||||
Self::Call { target } => write!(f, " call {}", target),
|
||||
Self::Return => write!(f, " ret"),
|
||||
|
||||
// misc instructions
|
||||
Self::Int { code } => write!(f, " int {}", code),
|
||||
Self::Hlt => write!(f, " hlt"),
|
||||
Self::Nop => write!(f, " nop"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
// data directives
|
||||
pub fn db_string(label: impl Into<String>, data: impl Into<String>) -> Self {
|
||||
Self::DString {
|
||||
label: label.into(),
|
||||
data: data.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn db_word(label: impl Into<String>, data: u32) -> Self {
|
||||
Self::Dw {
|
||||
label: label.into(),
|
||||
data: vec![data],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn db_bytes(label: impl Into<String>, data: &[u8]) -> Self {
|
||||
Self::Db {
|
||||
label: label.into(),
|
||||
data: data.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
// Movement
|
||||
pub fn mov<R1, R2>(src: R1, dest: R2) -> Self
|
||||
where
|
||||
R1: Into<Register>,
|
||||
R2: Into<Register>,
|
||||
{
|
||||
Self::Mov {
|
||||
src: src.into(),
|
||||
dest: dest.into(),
|
||||
}
|
||||
}
|
||||
|
||||
// Memory loads
|
||||
pub fn ldw_reg<R>(base: R, dest: Register) -> Self
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
Self::Ldw {
|
||||
src: MemOperand::RegIndirect(base.into()),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ldw_reg_offset<R>(base: R, dest: Register, offset: i32) -> Self
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
Self::Ldw {
|
||||
src: MemOperand::RegOffset(base.into(), offset),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ldw_label(label: impl Into<Label>, dest: Register) -> Self {
|
||||
Self::Ldw {
|
||||
src: MemOperand::Label(label.into()),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
// Memory stores
|
||||
pub fn stw_reg<R>(src: Register, base: R) -> Self
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
Self::Stw {
|
||||
src,
|
||||
dest: MemOperand::RegIndirect(base.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stw_reg_offset<R>(src: Register, base: R, offset: i32) -> Self
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
Self::Stw {
|
||||
src,
|
||||
dest: MemOperand::RegOffset(base.into(), offset),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stw_label(src: Register, label: impl Into<Label>) -> Self {
|
||||
Self::Stw {
|
||||
src,
|
||||
dest: MemOperand::Label(label.into()),
|
||||
}
|
||||
}
|
||||
|
||||
// Arithmetic
|
||||
pub fn add(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::Add { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn sub(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::Sub { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn and(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::And { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn or(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::Or { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn xor(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::Xor { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn not(src: Register, dest: Register) -> Self {
|
||||
Self::Not { src, dest }
|
||||
}
|
||||
|
||||
pub fn shl(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
|
||||
Self::Shl {
|
||||
src1,
|
||||
r_shamt,
|
||||
i_shamt,
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shr(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
|
||||
Self::Shr {
|
||||
src1,
|
||||
r_shamt,
|
||||
i_shamt,
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iadd(src: Register, value: i64) -> Self {
|
||||
let imm = Imm(value.unsigned_abs() as u32);
|
||||
|
||||
if value < 0 {
|
||||
Self::SubI {
|
||||
src,
|
||||
imm,
|
||||
dest: None,
|
||||
}
|
||||
} else {
|
||||
Self::AddI {
|
||||
src,
|
||||
imm,
|
||||
dest: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iadd_dest(src: Register, value: i32, dest: Register) -> Self {
|
||||
let imm = Imm(value.unsigned_abs());
|
||||
|
||||
if value < 0 {
|
||||
Self::SubI {
|
||||
src,
|
||||
imm,
|
||||
dest: Some(dest),
|
||||
}
|
||||
} else {
|
||||
Self::AddI {
|
||||
src,
|
||||
imm,
|
||||
dest: Some(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inc(reg: Register) -> Self {
|
||||
Self::AddI {
|
||||
src: reg,
|
||||
imm: Imm(1),
|
||||
dest: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dec(reg: Register) -> Self {
|
||||
Self::SubI {
|
||||
src: reg,
|
||||
imm: Imm(1),
|
||||
dest: None,
|
||||
}
|
||||
}
|
||||
|
||||
// Immediate loads
|
||||
pub fn lwi(value: u32, dest: Register) -> Self {
|
||||
if value > 0xFFFF {
|
||||
Self::Lwi {
|
||||
imm: Imm(value),
|
||||
dest,
|
||||
}
|
||||
} else {
|
||||
Self::Lli {
|
||||
imm: Imm(value),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lwi_label(label: impl Into<String>, dest: Register) -> Self {
|
||||
Self::LwiLabel {
|
||||
label: label.into(),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
// Control flow
|
||||
pub fn label(name: impl Into<String>) -> Self {
|
||||
Self::Label(Label(name.into()))
|
||||
}
|
||||
|
||||
pub fn cmp(operand: Comparison, src1: Register, src2: Register, dest: Register) -> Self {
|
||||
match operand {
|
||||
Comparison::Equal => Self::Ieq { src1, src2, dest },
|
||||
Comparison::NotEqual => Self::Ine { src1, src2, dest },
|
||||
Comparison::Greater => Self::Igt { src1, src2, dest },
|
||||
Comparison::GreaterEqual => Self::Ige { src1, src2, dest },
|
||||
Comparison::Less => Self::Ilt { src1, src2, dest },
|
||||
Comparison::LessEqual => Self::Ile { src1, src2, dest },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jmp(target: impl Into<Label>) -> Self {
|
||||
Self::Jmp {
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jnz(src: Register, target: impl Into<Label>) -> Self {
|
||||
Self::Jnz {
|
||||
src,
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jez(src: Register, target: impl Into<Label>) -> Self {
|
||||
Self::Jez {
|
||||
src,
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jic(target: impl Into<Label>) -> Self {
|
||||
Self::Jic {
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jnc(target: impl Into<Label>) -> Self {
|
||||
Self::Jnc {
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
// Stack
|
||||
pub fn push(reg: Register) -> Self {
|
||||
Self::Push { reg }
|
||||
}
|
||||
|
||||
pub fn pop(reg: Register) -> Self {
|
||||
Self::Pop { reg }
|
||||
}
|
||||
|
||||
// Functions
|
||||
pub fn call(target: impl Into<String>) -> Self {
|
||||
Self::Call {
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn int(code: u8) -> Self {
|
||||
Self::Int { code }
|
||||
}
|
||||
|
||||
pub fn ret() -> Self {
|
||||
Self::Return
|
||||
}
|
||||
|
||||
// Utilities
|
||||
pub fn comment(text: impl Into<String>) -> Self {
|
||||
Self::Comment {
|
||||
text: text.into(),
|
||||
top_level: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn global_comment(text: impl Into<String>) -> Self {
|
||||
Self::Comment {
|
||||
text: text.into(),
|
||||
top_level: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn include(name: impl Into<String>, path: impl Into<String>) -> Self {
|
||||
Self::Include {
|
||||
name: name.into(),
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience trait for Label conversion
|
||||
impl From<String> for Label {
|
||||
fn from(s: String) -> Self {
|
||||
Label(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Label {
|
||||
fn from(s: &str) -> Self {
|
||||
Label(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn reg_and_offset(op: &MemOperand) -> (String, i32) {
|
||||
match op {
|
||||
MemOperand::RegIndirect(reg) => (reg.to_string(), 0),
|
||||
MemOperand::RegOffset(reg, offset) => (reg.to_string(), *offset),
|
||||
MemOperand::Label(label) => (label.to_string(), 0),
|
||||
MemOperand::LabelOffset(label, offset) => (label.to_string(), *offset),
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory operand for loads/stores
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MemOperand {
|
||||
/// Register indirect: [reg]
|
||||
RegIndirect(Register),
|
||||
/// Register with offset: [reg + offset]
|
||||
RegOffset(Register, i32),
|
||||
/// Label: [label]
|
||||
Label(Label),
|
||||
/// Label with offset: [label + offset]
|
||||
LabelOffset(Label, i32),
|
||||
}
|
||||
|
||||
/// Immediate value (16-bit or 32-bit)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Imm(pub u32);
|
||||
|
||||
impl fmt::Display for Imm {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Label reference
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Label(pub String);
|
||||
|
||||
impl fmt::Display for Label {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
pub enum Statement {
|
||||
Const(VirtualReg, Literal),
|
||||
BinaryOp { left: VirtualReg, op: PrimitiveOperation,
|
||||
right: VirtualReg, result: VirtualReg },
|
||||
AddressOf {
|
||||
value: VirtualReg, result: VirtualReg },
|
||||
Dereference {
|
||||
address: VirtualReg, result: VirtualReg },
|
||||
GetStructField {
|
||||
struct_addr: VirtualReg, result: VirtualReg, offset: usize },
|
||||
SetStructField {
|
||||
struct_addr: VirtualReg, value: VirtualReg, offset: usize },
|
||||
Call {
|
||||
return_value: VirtualReg, func: FunctionName, args: Vec<VirtualReg> },
|
||||
}
|
||||
|
||||
pub struct IRProgram {
|
||||
pub functions: Vec<Function>,
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub name: FunctionName,
|
||||
pub params: Vec<VirtualReg>,
|
||||
pub blocks: Vec<BasicBlock>,
|
||||
}
|
||||
|
||||
pub struct BasicBlock {
|
||||
pub label: Label,
|
||||
pub statements: Vec<Statement>,
|
||||
pub terminator: Terminator,
|
||||
}
|
||||
|
||||
pub enum Terminator {
|
||||
Jump(Label),
|
||||
ConditionalJump { if_flag: VirtualReg, then_goto: Label, else_goto: Label },
|
||||
Return(VirtualReg),
|
||||
ReturnVoid,
|
||||
}
|
||||
|
||||
pub type FunctionName = String;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Label {
|
||||
id: usize,
|
||||
name: Rc<String>
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new_with_id(name: impl Into<String>, id: usize) -> Self {
|
||||
Self { id, name: Rc::new(name.into()) }
|
||||
}
|
||||
|
||||
pub fn new(name: impl Into<String>) -> Self {
|
||||
Self { id: Self::get_new_id(), name: Rc::new(name.into()) }
|
||||
}
|
||||
|
||||
pub fn get_new_id() -> usize {
|
||||
LABEL_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Label {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "__{}_{}", self.name, self.id)
|
||||
}
|
||||
}
|
||||
|
||||
static LABEL_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct VirtualReg(pub isize);
|
||||
|
||||
impl VirtualReg {
|
||||
pub fn void() -> Self {
|
||||
Self(-1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub enum Literal {
|
||||
UInt(usize),
|
||||
Int(isize),
|
||||
Char(char),
|
||||
}
|
||||
|
||||
pub enum PrimitiveOperation {
|
||||
Add,
|
||||
Sub,
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Nor,
|
||||
Nand
|
||||
}
|
||||
|
||||
@@ -1,445 +1,540 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use clap::builder::Resettable::Value;
|
||||
use dsa_tasks::ProcessAgent;
|
||||
use crate::backend::scope::Allocator;
|
||||
use crate::model::{Block, Call, CompilerError, Declaration, Expression, Program, Statement, TypeId, Variable};
|
||||
|
||||
use crate::backend::intermediate_rep as IR;
|
||||
use crate::backend::intermediate_rep::{BasicBlock, Label, Terminator, VirtualReg};
|
||||
use crate::model::ast::{
|
||||
Block, Call, Declaration, Expression, Number, Program, Statement, TypeId, Variable,
|
||||
};
|
||||
use crate::model::dsa_ir::{BasicBlock, IRProgram, Label, Literal, Terminator, VirtualReg};
|
||||
use crate::model::{CompilerError, dsa_ir as IR};
|
||||
|
||||
pub struct IrLoweringCodeGenerator {
|
||||
ast: Program,
|
||||
ast: Program,
|
||||
|
||||
functions: Vec<IR::Function>
|
||||
functions: Vec<IR::Function>,
|
||||
}
|
||||
|
||||
impl IrLoweringCodeGenerator {
|
||||
pub fn new(ast: Program) -> Self {
|
||||
Self { ast, functions: vec![] }
|
||||
}
|
||||
pub fn new(ast: Program) -> Self {
|
||||
Self {
|
||||
ast,
|
||||
functions: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
||||
for block in self.ast.clone().declarations {
|
||||
match block {
|
||||
Declaration::Variable { ..} => todo!(),
|
||||
Declaration::Dependency(_) => todo!(),
|
||||
Declaration::Function { name, ..} => todo!(),
|
||||
Declaration::Struct { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
pub fn run(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
||||
for block in self.ast.clone().declarations {
|
||||
match block {
|
||||
Declaration::Variable { .. } => {}
|
||||
Declaration::Dependency(_) => {}
|
||||
Declaration::Function { name, .. } => {}
|
||||
Declaration::Struct { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
for block in self.ast.clone().declarations {
|
||||
self.generate_block(&block)?;
|
||||
}
|
||||
for block in self.ast.clone().declarations {
|
||||
self.generate_block(&block)?;
|
||||
}
|
||||
|
||||
self.output(ctx)
|
||||
}
|
||||
self.output(ctx)
|
||||
}
|
||||
|
||||
pub fn output(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
||||
todo!()
|
||||
}
|
||||
pub fn output(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
||||
Ok(IRProgram {
|
||||
functions: self.functions.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_block(&mut self, decl: &Declaration) -> Result<(), CompilerError> {
|
||||
match decl {
|
||||
Declaration::Function { name, params, body, return_type } => {
|
||||
let function = self.generate_function(name, params, body, return_type)?;
|
||||
self.functions.push(function);
|
||||
},
|
||||
Declaration::Struct { .. } => todo!("no codegen required for structs yet"),
|
||||
Declaration::Dependency(_) => todo!("no codegen required for dependencies yet"),
|
||||
Declaration::Variable { .. } => todo!("codegen for statics and constants"),
|
||||
}
|
||||
pub fn generate_block(&mut self, decl: &Declaration) -> Result<(), CompilerError> {
|
||||
match decl {
|
||||
Declaration::Function {
|
||||
name,
|
||||
params,
|
||||
body,
|
||||
return_type,
|
||||
} => {
|
||||
let function = self.generate_function(name, params, body, return_type)?;
|
||||
self.functions.push(function);
|
||||
}
|
||||
// Declaration::Struct { .. } => todo!("no codegen required for structs yet"),
|
||||
// Declaration::Dependency(_) => todo!("no codegen required for dependencies yet"),
|
||||
// Declaration::Variable { .. } => todo!("codegen for statics and constants"),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_function(
|
||||
&mut self, function_name: &str,
|
||||
params: &[Variable],
|
||||
body: &Block,
|
||||
return_type: &TypeId
|
||||
) -> Result<IR::Function, CompilerError> {
|
||||
let mut context = FunctionContext::new(Label::new(function_name.to_string()));
|
||||
pub fn generate_function(
|
||||
&mut self,
|
||||
function_name: &str,
|
||||
params: &[Variable],
|
||||
body: &Block,
|
||||
return_type: &TypeId,
|
||||
) -> Result<IR::Function, CompilerError> {
|
||||
let mut context = FunctionContext::new(Label::new(function_name.to_string()));
|
||||
|
||||
let params = (0..params.len())
|
||||
.map(|_| context.new_vreg()).collect();
|
||||
let params = (0..params.len()).map(|_| context.new_vreg()).collect();
|
||||
|
||||
self.collapse_block(body, &mut context)?;
|
||||
self.collapse_block(body, &mut context)?;
|
||||
|
||||
let blocks = context.terminate(IR::Terminator::ReturnVoid);
|
||||
let blocks = context.terminate(IR::Terminator::ReturnVoid);
|
||||
|
||||
Ok(IR::Function {
|
||||
name: function_name.to_string(), params, blocks,
|
||||
})
|
||||
}
|
||||
Ok(IR::Function {
|
||||
name: function_name.to_string(),
|
||||
params,
|
||||
blocks,
|
||||
})
|
||||
}
|
||||
|
||||
fn collapse_block(
|
||||
&self,
|
||||
block: &Block,
|
||||
ctx: &mut FunctionContext,
|
||||
) -> Result<(), CompilerError> {
|
||||
for statement in block {
|
||||
self.collapse_statement(statement, ctx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn collapse_block(
|
||||
&self,
|
||||
block: &Block,
|
||||
ctx: &mut FunctionContext,
|
||||
) -> Result<(), CompilerError> {
|
||||
for statement in block {
|
||||
self.collapse_statement(statement, ctx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collapse_statement(
|
||||
&self,
|
||||
stmt: &Statement,
|
||||
ctx: &mut FunctionContext,
|
||||
) -> Result<(), CompilerError> {
|
||||
match stmt {
|
||||
Statement::Block(block) => {
|
||||
self.collapse_block(block, ctx)
|
||||
}
|
||||
fn collapse_statement(
|
||||
&self,
|
||||
stmt: &Statement,
|
||||
ctx: &mut FunctionContext,
|
||||
) -> Result<(), CompilerError> {
|
||||
match stmt {
|
||||
Statement::Block(block) => self.collapse_block(block, ctx),
|
||||
|
||||
Statement::If { condition, then_stmt, else_stmt } => {
|
||||
/* if statement expansion. produces the following layout
|
||||
reg_cond = ... expansion of {condition} ...
|
||||
if reg_cond goto then_{id} else goto else_{id}
|
||||
then_{id}:
|
||||
... expansion of {then_stmt} ...
|
||||
goto end_{id}
|
||||
else_{id}:
|
||||
... expansion of {else_stmt} ...
|
||||
goto end_{id}
|
||||
end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
let id = Label::get_new_id();
|
||||
let then_label = Label::new_with_id("then", id);
|
||||
let else_label = Label::new_with_id("else", id);
|
||||
let end_label = Label::new_with_id("endif", id);
|
||||
Statement::If {
|
||||
condition,
|
||||
then_stmt,
|
||||
else_stmt,
|
||||
} => {
|
||||
/* if statement expansion. produces the following layout
|
||||
reg_cond = ... expansion of {condition} ...
|
||||
if reg_cond goto then_{id} else goto else_{id}
|
||||
then_{id}:
|
||||
... expansion of {then_stmt} ...
|
||||
goto end_{id}
|
||||
else_{id}:
|
||||
... expansion of {else_stmt} ...
|
||||
goto end_{id}
|
||||
end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
let id = Label::get_new_id();
|
||||
let then_label = Label::new_with_id("then", id);
|
||||
let else_label = Label::new_with_id("else", id);
|
||||
let end_label = Label::new_with_id("endif", id);
|
||||
|
||||
let eval_result = self.collapse_expression(condition, ctx)?;
|
||||
let eval_result = ctx.new_vreg();
|
||||
self.collapse_expression(condition, ctx, eval_result)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::ConditionalJump {
|
||||
if_flag: eval_result,
|
||||
then_goto: then_label.clone(),
|
||||
else_goto: else_label.clone(),
|
||||
},
|
||||
then_label.clone()
|
||||
);
|
||||
ctx.next_block(
|
||||
Terminator::ConditionalJump {
|
||||
if_flag: eval_result,
|
||||
then_goto: then_label.clone(),
|
||||
else_goto: else_label.clone(),
|
||||
},
|
||||
then_label.clone(),
|
||||
);
|
||||
|
||||
self.collapse_block(then_stmt, ctx)?;
|
||||
self.collapse_block(then_stmt, ctx)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::Jump(end_label.clone()),
|
||||
else_label.clone()
|
||||
);
|
||||
ctx.next_block(Terminator::Jump(end_label.clone()), else_label.clone());
|
||||
|
||||
self.collapse_block(else_stmt, ctx)?;
|
||||
self.collapse_block(else_stmt, ctx)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::Jump(end_label.clone()),
|
||||
end_label.clone()
|
||||
);
|
||||
ctx.next_block(Terminator::Jump(end_label.clone()), end_label.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Statement::Defer(call) => {
|
||||
// defers stmt. all currently in scope defers are called in FILO order on return
|
||||
ctx.defer(call.clone());
|
||||
Ok(())
|
||||
},
|
||||
Statement::Defer(call) => {
|
||||
// defers stmt. all currently in scope defers are called in FILO order on return
|
||||
ctx.defer(call.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Statement::Return(expr) => {
|
||||
// return stmt. calls relevant defer methods and expands the body of the return statement.
|
||||
Statement::Return(expr) => {
|
||||
// return stmt. calls relevant defer methods and expands the body of the return statement.
|
||||
let result = if let Some(value) = expr {
|
||||
let result_reg = ctx.new_vreg();
|
||||
self.collapse_expression(value, ctx, result_reg)?;
|
||||
result_reg
|
||||
} else {
|
||||
VirtualReg::void()
|
||||
};
|
||||
|
||||
let result = if let Some(value) = expr {
|
||||
self.collapse_expression(value, ctx)?
|
||||
} else { VirtualReg::void() };
|
||||
// run cleanup logic (defers)
|
||||
for call in ctx.get_deferred_calls() {
|
||||
self.collapse_call(&call, ctx, VirtualReg::void())?;
|
||||
}
|
||||
|
||||
// run cleanup logic (defers)
|
||||
for call in ctx.get_deferred_calls() {
|
||||
self.collapse_call(&call, ctx)?;
|
||||
}
|
||||
ctx.next_block(Terminator::Return(result), Label::new("unreachable"));
|
||||
|
||||
ctx.next_block(Terminator::Return(result), Label::new("unreachable"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Statement::Expression { expr } => {
|
||||
// generate the code for the expression but discard the result
|
||||
// let the optimiser remove it if it provably has no side effects.
|
||||
self.collapse_expression(expr, ctx, VirtualReg::void())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Statement::Expression { expr } => {
|
||||
// generate the code for the expression but discard the result
|
||||
// let the optimiser remove it if it provably has no side effects.
|
||||
Statement::Continue => {
|
||||
// continue stmt. goes to the 'start' block of the loop
|
||||
|
||||
let _ = self.collapse_expression(expr, ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
let Some(scope) = ctx.current_scope() else {
|
||||
return Err(CompilerError::generic(
|
||||
"Invalid 'Continue' statement (not inside any loop)",
|
||||
));
|
||||
};
|
||||
|
||||
Statement::Continue => {
|
||||
// continue stmt. goes to the 'start' block of the loop
|
||||
ctx.next_block(Terminator::Jump(scope.start), Label::new("unreachable"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let Some(scope) = ctx.current_scope() else {
|
||||
return Err(CompilerError::generic("Invalid 'Continue' statement (not inside any loop)"));
|
||||
};
|
||||
Statement::Break => {
|
||||
// break stmt. jumps to the 'end' block of the loop
|
||||
|
||||
ctx.next_block(Terminator::Jump(scope.start), Label::new("unreachable"));
|
||||
Ok(())
|
||||
},
|
||||
let Some(scope) = ctx.current_scope() else {
|
||||
return Err(CompilerError::generic(
|
||||
"Invalid 'Continue' statement (not inside any loop)",
|
||||
));
|
||||
};
|
||||
|
||||
Statement::Break => {
|
||||
// break stmt. jumps to the 'end' block of the loop
|
||||
ctx.next_block(Terminator::Jump(scope.end), Label::new("unreachable"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let Some(scope) = ctx.current_scope() else {
|
||||
return Err(CompilerError::generic("Invalid 'Continue' statement (not inside any loop)"));
|
||||
};
|
||||
Statement::While { condition, body } => {
|
||||
/* while loop. produces the following layout
|
||||
goto while_check_{id}
|
||||
while_check_{id}:
|
||||
if {condition} goto while_body_{id} else goto while_end_{id}
|
||||
while_body_{id}:
|
||||
... expansion of {body} ...
|
||||
goto while_check_{id}
|
||||
while_end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
|
||||
ctx.next_block(Terminator::Jump(scope.end), Label::new("unreachable"));
|
||||
Ok(())
|
||||
},
|
||||
let id = Label::get_new_id();
|
||||
let check_label = Label::new_with_id("while_check", id);
|
||||
let body_label = Label::new_with_id("while_body", id);
|
||||
let end_label = Label::new_with_id("while_end", id);
|
||||
|
||||
Statement::While { condition, body } => {
|
||||
/* while loop. produces the following layout
|
||||
goto while_check_{id}
|
||||
while_check_{id}:
|
||||
if {condition} goto while_body_{id} else goto while_end_{id}
|
||||
while_body_{id}:
|
||||
... expansion of {body} ...
|
||||
goto while_check_{id}
|
||||
while_end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone());
|
||||
|
||||
let id = Label::get_new_id();
|
||||
let check_label = Label::new_with_id("while_check", id);
|
||||
let body_label = Label::new_with_id("while_body", id);
|
||||
let end_label = Label::new_with_id("while_end", id);
|
||||
let if_flag = ctx.new_vreg();
|
||||
self.collapse_expression(condition, ctx, if_flag)?;
|
||||
|
||||
ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone());
|
||||
ctx.next_block(
|
||||
Terminator::ConditionalJump {
|
||||
if_flag,
|
||||
then_goto: body_label.clone(),
|
||||
else_goto: end_label.clone(),
|
||||
},
|
||||
body_label.clone(),
|
||||
);
|
||||
|
||||
let result = self.collapse_expression(condition, ctx)?;
|
||||
self.collapse_block(body, ctx)?;
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::ConditionalJump {
|
||||
if_flag: result,
|
||||
then_goto: body_label.clone(),
|
||||
else_goto: end_label.clone()
|
||||
},
|
||||
body_label.clone(),
|
||||
);
|
||||
ctx.next_block(Terminator::Jump(check_label.clone()), end_label.clone());
|
||||
|
||||
self.collapse_block(body, ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ctx.next_block(
|
||||
Terminator::Jump(check_label.clone()),
|
||||
end_label.clone()
|
||||
);
|
||||
Statement::Loop(block) => {
|
||||
/* indefinite loop. produces the following layout
|
||||
goto start_loop_{id}
|
||||
start_loop_{id}:
|
||||
goto start_loop_{id}
|
||||
while_end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
let id = Label::get_new_id();
|
||||
let start_label = Label::new_with_id("start_loop", id);
|
||||
let end_label = Label::new_with_id("end_loop", id);
|
||||
|
||||
Statement::Loop(block) => {
|
||||
/* indefinite loop. produces the following layout
|
||||
goto start_loop_{id}
|
||||
start_loop_{id}:
|
||||
goto start_loop_{id}
|
||||
while_end_{id}:
|
||||
... not our responsibility ...
|
||||
*/
|
||||
ctx.next_block(Terminator::Jump(start_label.clone()), start_label.clone());
|
||||
|
||||
let id = Label::get_new_id();
|
||||
let start_label = Label::new_with_id("start_loop", id);
|
||||
let end_label = Label::new_with_id("end_loop", id);
|
||||
ctx.enter_scope(Scope::new(start_label.clone(), end_label.clone()));
|
||||
|
||||
ctx.next_block(Terminator::Jump(start_label.clone()), start_label.clone());
|
||||
self.collapse_block(block, ctx)?;
|
||||
|
||||
ctx.enter_scope(Scope::new(start_label.clone(), end_label.clone()));
|
||||
ctx.exit_scope();
|
||||
|
||||
self.collapse_block(block, ctx)?;
|
||||
ctx.next_block(Terminator::Jump(start_label.clone()), end_label);
|
||||
|
||||
ctx.exit_scope();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ctx.next_block(Terminator::Jump(start_label.clone()), end_label);
|
||||
Statement::Declaration { var, value } => {
|
||||
let Some(expr) = value else {
|
||||
ctx.new_local(VirtualReg::void(), var.clone());
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
Ok(())
|
||||
},
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
let v_reg = ctx.new_vreg();
|
||||
self.collapse_expression(expr, ctx, v_reg)?;
|
||||
ctx.new_local(v_reg, var.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collapse_call(
|
||||
&self,
|
||||
call: &Call,
|
||||
ctx: &mut FunctionContext
|
||||
) -> Result<VirtualReg, CompilerError> {
|
||||
let reg = ctx.new_vreg();
|
||||
Statement::Assign {
|
||||
varname,
|
||||
value,
|
||||
operator,
|
||||
} => {
|
||||
let Some(v_reg) = ctx.find_local(varname) else {
|
||||
return Err(CompilerError::generic(format!(
|
||||
"Variable not defined: {varname}"
|
||||
)));
|
||||
};
|
||||
|
||||
let args = call.args.iter()
|
||||
.map(|arg| self.collapse_expression(&arg, ctx))
|
||||
.collect::<Result<Vec<VirtualReg>, _>>()?;
|
||||
self.collapse_expression(value, ctx, v_reg)?;
|
||||
Ok(())
|
||||
} // todo!!!!
|
||||
|
||||
ctx.push_stmt(IR::Statement::Call {
|
||||
return_value: reg,
|
||||
func: call.name.to_string(),
|
||||
args
|
||||
});
|
||||
Statement::PtrWrite { ptr, value } => {
|
||||
let ptr_reg = ctx.new_vreg();
|
||||
self.collapse_expression(&ptr, ctx, ptr_reg)?;
|
||||
|
||||
Ok(reg)
|
||||
}
|
||||
let value_reg = ctx.new_vreg();
|
||||
self.collapse_expression(&value, ctx, value_reg)?;
|
||||
|
||||
fn collapse_expression(
|
||||
&self,
|
||||
expr: &Expression,
|
||||
ctx: &mut FunctionContext,
|
||||
) -> Result<VirtualReg, CompilerError> {
|
||||
todo!()
|
||||
}
|
||||
ctx.push_stmt(IR::Statement::Store {
|
||||
value: value_reg,
|
||||
address: ptr_reg,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collapse_call(
|
||||
&self,
|
||||
call: &Call,
|
||||
ctx: &mut FunctionContext,
|
||||
v_reg: VirtualReg,
|
||||
) -> Result<(), CompilerError> {
|
||||
let args = call
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let v_reg = ctx.new_vreg();
|
||||
self.collapse_expression(&arg, ctx, v_reg).map(|_| v_reg)
|
||||
})
|
||||
.collect::<Result<Vec<VirtualReg>, _>>()?;
|
||||
|
||||
ctx.push_stmt(IR::Statement::Call {
|
||||
return_value: v_reg,
|
||||
func: call.name.to_string(),
|
||||
args,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collapse_expression(
|
||||
&self,
|
||||
expr: &Expression,
|
||||
ctx: &mut FunctionContext,
|
||||
v_reg: VirtualReg,
|
||||
) -> Result<(), CompilerError> {
|
||||
match expr {
|
||||
Expression::Empty => Ok(()),
|
||||
Expression::Call { func, .. } => {
|
||||
self.collapse_call(func, ctx, v_reg)?;
|
||||
Ok(())
|
||||
}
|
||||
Expression::CharLiteral(c) => {
|
||||
ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Char(*c)));
|
||||
Ok(())
|
||||
}
|
||||
Expression::Number(num) => {
|
||||
match num {
|
||||
Number::Signed(val, ..) => {
|
||||
ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Int(*val as isize)));
|
||||
}
|
||||
Number::Unsigned(val, ..) => {
|
||||
ctx.push_stmt(IR::Statement::Const(v_reg, Literal::UInt(*val as usize)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Expression::Variable { name, expr_type } => {
|
||||
let Some(var) = ctx.find_local(&name.name) else {
|
||||
return Err(CompilerError::generic(format!(
|
||||
"Variable not defined: {name}"
|
||||
)));
|
||||
};
|
||||
|
||||
ctx.push_stmt(IR::Statement::Copy {
|
||||
src: var,
|
||||
dst: v_reg,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO! all of the below:
|
||||
Expression::Binary { .. } => Ok(()),
|
||||
Expression::Unary { .. } => Ok(()),
|
||||
Expression::UnaryPostfix { .. } => Ok(()),
|
||||
Expression::TypeCast { .. } => Ok(()),
|
||||
Expression::IndexAccess { .. } => Ok(()),
|
||||
Expression::MemberAccess { .. } => Ok(()),
|
||||
Expression::StringLiteral(_) => Ok(()),
|
||||
Expression::ArrayLiteral { .. } => Ok(()),
|
||||
Expression::StructLiteral { .. } => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BasicBlockBuilder {
|
||||
label: Label,
|
||||
statements: Vec<IR::Statement>
|
||||
label: Label,
|
||||
statements: Vec<IR::Statement>,
|
||||
}
|
||||
|
||||
impl BasicBlockBuilder {
|
||||
fn new(label: Label) -> Self {
|
||||
Self {
|
||||
label,
|
||||
statements: vec![]
|
||||
}
|
||||
}
|
||||
fn new(label: Label) -> Self {
|
||||
Self {
|
||||
label,
|
||||
statements: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, statement: IR::Statement) {
|
||||
self.statements.push(statement);
|
||||
}
|
||||
pub fn push(&mut self, statement: IR::Statement) {
|
||||
self.statements.push(statement);
|
||||
}
|
||||
|
||||
pub fn next_block(&mut self, terminator: Terminator, label: Label) -> BasicBlock {
|
||||
BasicBlock {
|
||||
label: std::mem::replace(&mut self.label, label),
|
||||
statements: std::mem::take(&mut self.statements),
|
||||
terminator,
|
||||
}
|
||||
}
|
||||
pub fn next_block(&mut self, terminator: Terminator, label: Label) -> BasicBlock {
|
||||
BasicBlock {
|
||||
label: std::mem::replace(&mut self.label, label),
|
||||
statements: std::mem::take(&mut self.statements),
|
||||
terminator,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate_block(self, terminator: Terminator) -> BasicBlock {
|
||||
BasicBlock {
|
||||
label: self.label,
|
||||
statements: self.statements,
|
||||
terminator,
|
||||
}
|
||||
}
|
||||
pub fn terminate_block(self, terminator: Terminator) -> BasicBlock {
|
||||
BasicBlock {
|
||||
label: self.label,
|
||||
statements: self.statements,
|
||||
terminator,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Scope {
|
||||
start: Label,
|
||||
end: Label,
|
||||
start: Label,
|
||||
end: Label,
|
||||
|
||||
defers: Vec<Call>,
|
||||
locals: Vec<(VirtualReg, Variable)>,
|
||||
defers: Vec<Call>,
|
||||
locals: Vec<(VirtualReg, Variable)>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn new(start: Label, end: Label) -> Self {
|
||||
Self { start, end, defers: vec![], locals: vec![] }
|
||||
}
|
||||
pub fn new(start: Label, end: Label) -> Self {
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
defers: vec![],
|
||||
locals: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FunctionContext {
|
||||
entrypoint: Label,
|
||||
scopes: Vec<Scope>,
|
||||
blocks: Vec<BasicBlock>,
|
||||
entrypoint: Label,
|
||||
scopes: Vec<Scope>,
|
||||
blocks: Vec<BasicBlock>,
|
||||
|
||||
builder: BasicBlockBuilder,
|
||||
builder: BasicBlockBuilder,
|
||||
|
||||
v_register_idx: usize,
|
||||
v_register_idx: usize,
|
||||
}
|
||||
|
||||
impl FunctionContext {
|
||||
pub fn new(entrypoint: Label) -> Self {
|
||||
Self {
|
||||
entrypoint: entrypoint.clone(),
|
||||
blocks: Vec::new(),
|
||||
scopes: vec![Scope::new(entrypoint.clone(), Label::new("unreachable"))],
|
||||
builder: BasicBlockBuilder::new(entrypoint),
|
||||
v_register_idx: 0,
|
||||
}
|
||||
}
|
||||
pub fn new(entrypoint: Label) -> Self {
|
||||
Self {
|
||||
entrypoint: entrypoint.clone(),
|
||||
blocks: Vec::new(),
|
||||
scopes: vec![Scope::new(entrypoint.clone(), Label::new("unreachable"))],
|
||||
builder: BasicBlockBuilder::new(entrypoint),
|
||||
v_register_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_vreg(&mut self) -> VirtualReg {
|
||||
self.v_register_idx += 1;
|
||||
VirtualReg(self.v_register_idx as isize - 1)
|
||||
}
|
||||
pub fn new_vreg(&mut self) -> VirtualReg {
|
||||
self.v_register_idx += 1;
|
||||
VirtualReg(self.v_register_idx as isize - 1)
|
||||
}
|
||||
|
||||
pub fn defer(&mut self, call: Call) {
|
||||
self.scopes.last_mut().unwrap().defers.push(call);
|
||||
}
|
||||
pub fn defer(&mut self, call: Call) {
|
||||
self.scopes.last_mut().unwrap().defers.push(call);
|
||||
}
|
||||
|
||||
pub fn new_local(&mut self, variable: Variable) -> VirtualReg {
|
||||
let vreg = self.new_vreg();
|
||||
self.scopes.last_mut().unwrap().locals.push((vreg, variable));
|
||||
vreg
|
||||
}
|
||||
pub fn new_local(&mut self, v_reg: VirtualReg, variable: Variable) {
|
||||
self.scopes
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.locals
|
||||
.push((v_reg, variable));
|
||||
}
|
||||
|
||||
pub fn enter_scope(&mut self, scope: Scope) {
|
||||
self.scopes.push(scope);
|
||||
}
|
||||
pub fn enter_scope(&mut self, scope: Scope) {
|
||||
self.scopes.push(scope);
|
||||
}
|
||||
|
||||
pub fn current_scope(&mut self) -> Option<Scope> {
|
||||
self.scopes.pop()
|
||||
}
|
||||
pub fn current_scope(&mut self) -> Option<Scope> {
|
||||
self.scopes.pop()
|
||||
}
|
||||
|
||||
pub fn get_deferred_calls(&self) -> Vec<Call> {
|
||||
self.scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|s|
|
||||
s.defers
|
||||
.iter()
|
||||
.cloned()
|
||||
.rev()
|
||||
).collect()
|
||||
}
|
||||
pub fn get_deferred_calls(&self) -> Vec<Call> {
|
||||
self.scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|s| s.defers.iter().cloned().rev())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn find_local(&self, var: &Variable) -> Option<VirtualReg> {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
for local in &scope.locals {
|
||||
if local.1 == *var {
|
||||
return Some(local.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn find_local(&self, var: &String) -> Option<VirtualReg> {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
for local in &scope.locals {
|
||||
if local.1.name == *var {
|
||||
return Some(local.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn exit_scope(&mut self) {
|
||||
self.scopes.pop().expect("(BUG) Cannot exit outermost scope");
|
||||
}
|
||||
pub fn exit_scope(&mut self) {
|
||||
self.scopes
|
||||
.pop()
|
||||
.expect("(BUG) Cannot exit outermost scope");
|
||||
}
|
||||
|
||||
pub fn next_block(&mut self, terminator: Terminator, label: Label) {
|
||||
self.blocks.push(self.builder.next_block(terminator, label));
|
||||
}
|
||||
pub fn next_block(&mut self, terminator: Terminator, label: Label) {
|
||||
self.blocks.push(self.builder.next_block(terminator, label));
|
||||
}
|
||||
|
||||
pub fn push_stmt(&mut self, stmt: IR::Statement) {
|
||||
self.builder.push(stmt);
|
||||
}
|
||||
pub fn push_stmt(&mut self, stmt: IR::Statement) {
|
||||
self.builder.push(stmt);
|
||||
}
|
||||
|
||||
pub fn terminate(mut self, terminator: Terminator) -> Vec<IR::BasicBlock> {
|
||||
self.blocks.push(self.builder.terminate_block(terminator));
|
||||
self.blocks
|
||||
}
|
||||
pub fn terminate(mut self, terminator: Terminator) -> Vec<IR::BasicBlock> {
|
||||
self.blocks.push(self.builder.terminate_block(terminator));
|
||||
self.blocks
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> Vec<IR::BasicBlock> {
|
||||
std::mem::take(&mut self.blocks)
|
||||
}
|
||||
pub fn take(&mut self) -> Vec<IR::BasicBlock> {
|
||||
std::mem::take(&mut self.blocks)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
use crate::model::{CompilerError, Program};
|
||||
use crate::backend::ir_lowering_codegen::IrLoweringCodeGenerator;
|
||||
use crate::model::CompilerError;
|
||||
use crate::model::ast::Program;
|
||||
use dsa_tasks::ProcessAgent;
|
||||
|
||||
mod codegen;
|
||||
mod instruction;
|
||||
pub mod ir_lowering_codegen;
|
||||
mod registers;
|
||||
mod scope;
|
||||
pub mod intermediate_rep;
|
||||
pub mod ir_lowering_codegen;
|
||||
|
||||
pub fn generate_code(ast: &Program, ctx: &ProcessAgent) -> Result<String, CompilerError> {
|
||||
let mut codegen = codegen::CodeGenerator::new(ast.clone());
|
||||
codegen.generate(ctx)
|
||||
let mut codegen_test = IrLoweringCodeGenerator::new(ast.clone());
|
||||
let result = codegen_test.run(ctx)?;
|
||||
|
||||
Ok(result.to_string())
|
||||
|
||||
// let mut codegen = codegen::CodeGenerator::new(ast.clone());
|
||||
// codegen.generate(ctx)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::{collections::HashMap, fmt};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::backend::instruction::{InsBlock, Instruction};
|
||||
use crate::backend::instruction::InsBlock;
|
||||
use crate::model::CompilerError;
|
||||
use crate::model::instruction::Instruction;
|
||||
use crate::model::register::{Location, Register};
|
||||
|
||||
/// Register allocator for DSA assembly generation
|
||||
/// Manages general-purpose registers (rg0-rgf) and handles stack spilling
|
||||
@@ -21,28 +23,6 @@ pub struct RegisterAllocator {
|
||||
in_use: Vec<(Register, bool)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Location {
|
||||
register: Option<Register>,
|
||||
stack: Option<i32>,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
pub fn stack(offset: i32) -> Self {
|
||||
Location {
|
||||
register: None,
|
||||
stack: Some(offset),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(register: Register) -> Self {
|
||||
Location {
|
||||
register: Some(register),
|
||||
stack: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAllocator {
|
||||
pub fn new() -> Self {
|
||||
// Initialize with available GP registers (rg0-rgf = 16 registers)
|
||||
@@ -427,119 +407,3 @@ impl RegisterAllocator {
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Register {
|
||||
// general purpose
|
||||
Rg0 = 0,
|
||||
Rg1 = 1,
|
||||
Rg2 = 2,
|
||||
Rg3 = 3,
|
||||
Rg4 = 4,
|
||||
Rg5 = 5,
|
||||
Rg6 = 6,
|
||||
Rg7 = 7,
|
||||
Rg8 = 8,
|
||||
Rg9 = 9,
|
||||
Rga = 10,
|
||||
Rgb = 11,
|
||||
Rgc = 12,
|
||||
Rgd = 13,
|
||||
Rge = 14,
|
||||
Rgf = 15,
|
||||
|
||||
// special
|
||||
Bpr,
|
||||
Spr,
|
||||
Ret,
|
||||
Acc,
|
||||
|
||||
// read only
|
||||
Pcx,
|
||||
Zero,
|
||||
|
||||
// null
|
||||
Null,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub fn get_gp() -> [Register; 16] {
|
||||
[
|
||||
Register::Rg0,
|
||||
Register::Rg1,
|
||||
Register::Rg2,
|
||||
Register::Rg3,
|
||||
Register::Rg4,
|
||||
Register::Rg5,
|
||||
Register::Rg6,
|
||||
Register::Rg7,
|
||||
Register::Rg8,
|
||||
Register::Rg9,
|
||||
Register::Rga,
|
||||
Register::Rgb,
|
||||
Register::Rgc,
|
||||
Register::Rgd,
|
||||
Register::Rge,
|
||||
Register::Rgf,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn is_gp(&self) -> bool {
|
||||
(*self as u8) < 16
|
||||
}
|
||||
|
||||
pub fn from_index(idx: usize) -> Register {
|
||||
match idx {
|
||||
0 => Register::Rg0,
|
||||
1 => Register::Rg1,
|
||||
2 => Register::Rg2,
|
||||
3 => Register::Rg3,
|
||||
4 => Register::Rg4,
|
||||
5 => Register::Rg5,
|
||||
6 => Register::Rg6,
|
||||
7 => Register::Rg7,
|
||||
8 => Register::Rg8,
|
||||
9 => Register::Rg9,
|
||||
10 => Register::Rga,
|
||||
11 => Register::Rgb,
|
||||
12 => Register::Rgc,
|
||||
13 => Register::Rgd,
|
||||
14 => Register::Rge,
|
||||
15 => Register::Rgf,
|
||||
_ => unreachable!("this function shouldn't ever be called with idx>15"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Register {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Rg0 => write!(f, "rg0"),
|
||||
Self::Rg1 => write!(f, "rg1"),
|
||||
Self::Rg2 => write!(f, "rg2"),
|
||||
Self::Rg3 => write!(f, "rg3"),
|
||||
Self::Rg4 => write!(f, "rg4"),
|
||||
Self::Rg5 => write!(f, "rg5"),
|
||||
Self::Rg6 => write!(f, "rg6"),
|
||||
Self::Rg7 => write!(f, "rg7"),
|
||||
Self::Rg8 => write!(f, "rg8"),
|
||||
Self::Rg9 => write!(f, "rg9"),
|
||||
Self::Rga => write!(f, "rga"),
|
||||
Self::Rgb => write!(f, "rgb"),
|
||||
Self::Rgc => write!(f, "rgc"),
|
||||
Self::Rgd => write!(f, "rgd"),
|
||||
Self::Rge => write!(f, "rge"),
|
||||
Self::Rgf => write!(f, "rgf"),
|
||||
|
||||
Self::Acc => write!(f, "acc"),
|
||||
Self::Ret => write!(f, "ret"),
|
||||
Self::Bpr => write!(f, "bpr"),
|
||||
Self::Spr => write!(f, "spr"),
|
||||
|
||||
Self::Zero => write!(f, "zero"),
|
||||
Self::Pcx => write!(f, "pcx"),
|
||||
|
||||
Self::Null => write!(f, "null"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
|
||||
|
||||
use crate::backend::instruction::Instruction;
|
||||
use crate::backend::registers::Register;
|
||||
use crate::{
|
||||
error,
|
||||
model::{CompilerError, Name, TypeId},
|
||||
};
|
||||
use crate::error;
|
||||
use crate::model::CompilerError;
|
||||
use crate::model::ast::{Name, TypeId};
|
||||
use crate::model::instruction::Instruction;
|
||||
use crate::model::register::Register;
|
||||
|
||||
/// scope object
|
||||
pub struct Scope<'a> {
|
||||
|
||||
@@ -1,177 +1,8 @@
|
||||
use crate::model::ast::{Name, TypeId};
|
||||
use crate::model::tokens::Token;
|
||||
use std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Token {
|
||||
// Keywords
|
||||
Fn,
|
||||
Let,
|
||||
If,
|
||||
Else,
|
||||
Loop,
|
||||
While,
|
||||
Break,
|
||||
Return,
|
||||
Continue,
|
||||
Include,
|
||||
Static,
|
||||
Const,
|
||||
As,
|
||||
SizeOf,
|
||||
Struct,
|
||||
|
||||
// Identifiers and literals
|
||||
Identifier(Name),
|
||||
String(String),
|
||||
Char(char),
|
||||
SignedInt(i32, Option<TypeId>),
|
||||
UnsignedInt(u32, Option<TypeId>),
|
||||
|
||||
// Delimiters
|
||||
LeftParen, // (
|
||||
RightParen, // )
|
||||
LeftBrace, // {
|
||||
RightBrace, // }
|
||||
LeftBracket, // [
|
||||
RightBracket, // ]
|
||||
Semicolon, // ;
|
||||
Colon, // :
|
||||
Comma, // ,
|
||||
Dot, // .
|
||||
RightArrow, // ->
|
||||
|
||||
// Arithmetic operators
|
||||
Plus, // +
|
||||
Minus, // -
|
||||
Star, // *
|
||||
Slash, // /
|
||||
Percent, // %
|
||||
PlusPlus, // ++
|
||||
MinusMinus, // --
|
||||
|
||||
// Bitwise operators
|
||||
Ampersand, // &
|
||||
Pipe, // |
|
||||
Caret, // ^
|
||||
Tilde, // ~
|
||||
LeftShift, // <<
|
||||
RightShift, // >>
|
||||
|
||||
// Logical operators
|
||||
Bang, // !
|
||||
LogicalAnd, // &&
|
||||
LogicalOr, // ||
|
||||
|
||||
// Comparison operators
|
||||
EqualEqual, // ==
|
||||
BangEqual, // !=
|
||||
Less, // <
|
||||
LessEqual, // <=
|
||||
Greater, // >
|
||||
GreaterEqual, // >=
|
||||
|
||||
// Assignment operators
|
||||
Assign, // =
|
||||
PlusEqual, // +=
|
||||
MinusEqual, // -=
|
||||
StarEqual, // *=
|
||||
SlashEqual, // /=
|
||||
PercentEqual, // %=
|
||||
AndEqual, // &=
|
||||
OrEqual, // |=
|
||||
XorEqual, // ^=
|
||||
ShlEqual, // <<=
|
||||
ShrEqual, // >>=
|
||||
|
||||
// Special
|
||||
Eof,
|
||||
}
|
||||
|
||||
use crate::model::{Name, TypeId};
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(ref ns) = self.namespace {
|
||||
write!(f, "{}::{}", ns, self.name)
|
||||
} else {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn tt(&self) -> &str {
|
||||
match self {
|
||||
Token::Const => "Const",
|
||||
Token::Static => "Static",
|
||||
Token::Include => "Include",
|
||||
Token::Struct => "Struct",
|
||||
Token::Fn => "Fn",
|
||||
Token::If => "If",
|
||||
Token::Let => "Let",
|
||||
Token::Else => "Else",
|
||||
Token::Loop => "Loop",
|
||||
Token::While => "While",
|
||||
Token::Break => "Break",
|
||||
Token::Return => "Return",
|
||||
Token::Continue => "Continue",
|
||||
Token::As => "As",
|
||||
Token::Identifier(_) => "Identifier",
|
||||
Token::String(_) => "String",
|
||||
Token::UnsignedInt(_, _) => "UnsignedInt",
|
||||
Token::SignedInt(_, _) => "SignedInt",
|
||||
Token::Char(_) => "Char",
|
||||
Token::LeftParen => "LeftParen",
|
||||
Token::RightParen => "RightParen",
|
||||
Token::LeftBrace => "LeftBrace",
|
||||
Token::RightBrace => "RightBrace",
|
||||
Token::LeftBracket => "LeftBracket",
|
||||
Token::RightBracket => "RightBracket",
|
||||
Token::Semicolon => "Semicolon",
|
||||
Token::Colon => "Colon",
|
||||
Token::Comma => "Comma",
|
||||
Token::Dot => "Dot",
|
||||
Token::RightArrow => "RightArrow",
|
||||
Token::Plus => "Plus",
|
||||
Token::Minus => "Minus",
|
||||
Token::Star => "Star",
|
||||
Token::Slash => "Slash",
|
||||
Token::Percent => "Percent",
|
||||
Token::PlusPlus => "PlusPlus",
|
||||
Token::MinusMinus => "MinusMinus",
|
||||
Token::Ampersand => "Ampersand",
|
||||
Token::Pipe => "Pipe",
|
||||
Token::Caret => "Caret",
|
||||
Token::Tilde => "Tilde",
|
||||
Token::LeftShift => "LeftShift",
|
||||
Token::RightShift => "RightShift",
|
||||
Token::Bang => "Bang",
|
||||
Token::LogicalAnd => "LogicalAnd",
|
||||
Token::LogicalOr => "LogicalOr",
|
||||
Token::EqualEqual => "EqualEqual",
|
||||
Token::BangEqual => "BangEqual",
|
||||
Token::Less => "Less",
|
||||
Token::LessEqual => "LessEqual",
|
||||
Token::Greater => "Greater",
|
||||
Token::GreaterEqual => "GreaterEqual",
|
||||
Token::Assign => "Assign",
|
||||
Token::PlusEqual => "PlusEqual",
|
||||
Token::MinusEqual => "MinusEqual",
|
||||
Token::StarEqual => "StarEqual",
|
||||
Token::SlashEqual => "SlashEqual",
|
||||
Token::PercentEqual => "PercentEqual",
|
||||
Token::AndEqual => "AndEqual",
|
||||
Token::OrEqual => "OrEqual",
|
||||
Token::XorEqual => "XorEqual",
|
||||
Token::ShlEqual => "ShlEqual",
|
||||
Token::ShrEqual => "ShrEqual",
|
||||
Token::SizeOf => "SizeOf",
|
||||
Token::Eof => "Eof",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lexer<'a> {
|
||||
chars: Peekable<Chars<'a>>,
|
||||
current: Option<char>,
|
||||
@@ -407,12 +238,8 @@ impl<'a> Lexer<'a> {
|
||||
} else {
|
||||
value as i32
|
||||
};
|
||||
if signed_val < i8::MIN as i32 || signed_val > i8::MAX as i32
|
||||
{
|
||||
self.error(&format!(
|
||||
"Value {} out of range for i8",
|
||||
signed_val
|
||||
));
|
||||
if signed_val < i8::MIN as i32 || signed_val > i8::MAX as i32 {
|
||||
self.error(&format!("Value {} out of range for i8", signed_val));
|
||||
return Token::SignedInt(0, Some(TypeId::I8));
|
||||
}
|
||||
Token::SignedInt(signed_val, Some(TypeId::I8))
|
||||
@@ -423,13 +250,8 @@ impl<'a> Lexer<'a> {
|
||||
} else {
|
||||
value as i32
|
||||
};
|
||||
if signed_val < i16::MIN as i32
|
||||
|| signed_val > i16::MAX as i32
|
||||
{
|
||||
self.error(&format!(
|
||||
"Value {} out of range for i16",
|
||||
signed_val
|
||||
));
|
||||
if signed_val < i16::MIN as i32 || signed_val > i16::MAX as i32 {
|
||||
self.error(&format!("Value {} out of range for i16", signed_val));
|
||||
return Token::SignedInt(0, Some(TypeId::I16));
|
||||
}
|
||||
Token::SignedInt(signed_val, Some(TypeId::I16))
|
||||
@@ -437,19 +259,13 @@ impl<'a> Lexer<'a> {
|
||||
TypeId::I32 => {
|
||||
let signed_val = if is_negative {
|
||||
if value > i32::MAX as u64 + 1 {
|
||||
self.error(&format!(
|
||||
"Value -{} out of range for i32",
|
||||
value
|
||||
));
|
||||
self.error(&format!("Value -{} out of range for i32", value));
|
||||
return Token::SignedInt(0, Some(TypeId::I32));
|
||||
}
|
||||
-(value as i32)
|
||||
} else {
|
||||
if value > i32::MAX as u64 {
|
||||
self.error(&format!(
|
||||
"Value {} out of range for i32",
|
||||
value
|
||||
));
|
||||
self.error(&format!("Value {} out of range for i32", value));
|
||||
return Token::SignedInt(0, Some(TypeId::I32));
|
||||
}
|
||||
value as i32
|
||||
@@ -462,10 +278,7 @@ impl<'a> Lexer<'a> {
|
||||
return Token::UnsignedInt(0, Some(TypeId::U8));
|
||||
}
|
||||
if value > u8::MAX as u64 {
|
||||
self.error(&format!(
|
||||
"Value {} out of range for u8",
|
||||
value
|
||||
));
|
||||
self.error(&format!("Value {} out of range for u8", value));
|
||||
return Token::UnsignedInt(0, Some(TypeId::U8));
|
||||
}
|
||||
Token::UnsignedInt(value as u32, Some(TypeId::U8))
|
||||
@@ -476,10 +289,7 @@ impl<'a> Lexer<'a> {
|
||||
return Token::UnsignedInt(0, Some(TypeId::U16));
|
||||
}
|
||||
if value > u16::MAX as u64 {
|
||||
self.error(&format!(
|
||||
"Value {} out of range for u16",
|
||||
value
|
||||
));
|
||||
self.error(&format!("Value {} out of range for u16", value));
|
||||
return Token::UnsignedInt(0, Some(TypeId::U16));
|
||||
}
|
||||
Token::UnsignedInt(value as u32, Some(TypeId::U16))
|
||||
@@ -490,10 +300,7 @@ impl<'a> Lexer<'a> {
|
||||
return Token::UnsignedInt(0, Some(TypeId::U32));
|
||||
}
|
||||
if value > u32::MAX as u64 {
|
||||
self.error(&format!(
|
||||
"Value {} out of range for u32",
|
||||
value
|
||||
));
|
||||
self.error(&format!("Value {} out of range for u32", value));
|
||||
return Token::UnsignedInt(0, Some(TypeId::U32));
|
||||
}
|
||||
Token::UnsignedInt(value as u32, Some(TypeId::U32))
|
||||
@@ -592,8 +399,7 @@ impl<'a> Lexer<'a> {
|
||||
if c.is_ascii_digit() {
|
||||
self.advance();
|
||||
num_str.push(c);
|
||||
} else if c == '_' && self.peek_second().is_some_and(|ch| ch.is_ascii_digit())
|
||||
{
|
||||
} else if c == '_' && self.peek_second().is_some_and(|ch| ch.is_ascii_digit()) {
|
||||
// Allow underscores as separators only between digits
|
||||
self.advance();
|
||||
} else {
|
||||
@@ -662,8 +468,7 @@ impl<'a> Lexer<'a> {
|
||||
return Err("Invalid binary number: no digits after 0b".to_string());
|
||||
}
|
||||
|
||||
u64::from_str_radix(&num_str, 2)
|
||||
.map_err(|_| format!("Invalid binary number: {}", num_str))
|
||||
u64::from_str_radix(&num_str, 2).map_err(|_| format!("Invalid binary number: {}", num_str))
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
@@ -713,10 +518,7 @@ impl<'a> Lexer<'a> {
|
||||
'\'' => '\'',
|
||||
'0' => '\0',
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Invalid escape sequence: \\{}",
|
||||
escaped
|
||||
));
|
||||
return Err(format!("Invalid escape sequence: \\{}", escaped));
|
||||
}
|
||||
};
|
||||
s.push(escaped_char);
|
||||
@@ -767,9 +569,7 @@ impl<'a> Lexer<'a> {
|
||||
Some('0') => '\0',
|
||||
Some(c) => return Err(format!("Invalid escape sequence: \\{}", c)),
|
||||
None => {
|
||||
return Err(
|
||||
"Unexpected end after escape in char literal".to_string()
|
||||
);
|
||||
return Err("Unexpected end after escape in char literal".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -782,9 +582,7 @@ impl<'a> Lexer<'a> {
|
||||
self.advance(); // Move to closing quote
|
||||
|
||||
if self.current != Some('\'') {
|
||||
return Err(
|
||||
"Character literal must contain exactly one character".to_string()
|
||||
);
|
||||
return Err("Character literal must contain exactly one character".to_string());
|
||||
}
|
||||
|
||||
Ok(ch)
|
||||
@@ -998,7 +796,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_operators() {
|
||||
let input = "+ ++ += - -- -= * *= / /= % %= & &= && | |= || ^ ^= ! != = == < <= << <<= > >= >> >>=";
|
||||
let input =
|
||||
"+ ++ += - -- -= * *= / /= % %= & &= && | |= || ^ ^= ! != = == < <= << <<= > >= >> >>=";
|
||||
let mut lexer = Lexer::new(input);
|
||||
|
||||
let expected = vec![
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::frontend::parser::{ParseResult, Parser};
|
||||
use crate::model::{CompilerError, Program};
|
||||
use crate::model::CompilerError;
|
||||
use crate::model::ast::Program;
|
||||
use dsa_tasks::ProcessAgent;
|
||||
|
||||
// mod c;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use super::lexer::Token;
|
||||
use crate::model::{
|
||||
AssignmentOperator, BinaryOperator, Block, Call, CompilerError, ConstExpr, Declaration,
|
||||
Dependency, Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
||||
use crate::model::CompilerError;
|
||||
use crate::model::ast::{
|
||||
AssignmentOperator, BinaryOperator, Block, Call, ConstExpr, Declaration, Dependency,
|
||||
Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
||||
};
|
||||
use crate::model::tokens::Token;
|
||||
use crate::{expect_tt, expect_value};
|
||||
use std::ops::{ControlFlow, FromResidual, Residual, Try};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#![feature(try_trait_v2)]
|
||||
#![feature(try_trait_v2_residual)]
|
||||
|
||||
use crate::model::CompilerError;
|
||||
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
||||
use model::CompilerError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod backend;
|
||||
|
||||
@@ -1,38 +1,12 @@
|
||||
use core::fmt;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CompilerError {
|
||||
UnexpectedToken(String),
|
||||
UnexpectedEndOfInput,
|
||||
UnexpectedCharacter(char),
|
||||
Undefined(Name),
|
||||
InvalidSyntax(String),
|
||||
Generic(String),
|
||||
UnknownType,
|
||||
TypeMismatch(TypeId, TypeId),
|
||||
Unimplemented(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for CompilerError {}
|
||||
|
||||
impl fmt::Display for CompilerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerError {
|
||||
pub fn generic(msg: impl Into<String>) -> Self {
|
||||
Self::Generic(msg.into())
|
||||
}
|
||||
}
|
||||
use crate::model::CompilerError;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Name {
|
||||
pub name: String,
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
impl Name {
|
||||
pub fn new(name: impl Into<String>, namespace: Option<String>) -> Self {
|
||||
Self {
|
||||
@@ -499,3 +473,13 @@ impl fmt::Display for UnaryOperator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(ref ns) = self.namespace {
|
||||
write!(f, "{}::{}", ns, self.name)
|
||||
} else {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Statement {
|
||||
Const(VirtualReg, Literal),
|
||||
BinaryOp {
|
||||
left: VirtualReg,
|
||||
op: PrimitiveOperation,
|
||||
right: VirtualReg,
|
||||
result: VirtualReg,
|
||||
},
|
||||
Copy {
|
||||
src: VirtualReg,
|
||||
dst: VirtualReg,
|
||||
},
|
||||
Store {
|
||||
value: VirtualReg,
|
||||
address: VirtualReg,
|
||||
},
|
||||
Load {
|
||||
address: VirtualReg,
|
||||
result: VirtualReg,
|
||||
},
|
||||
GetStructField {
|
||||
struct_addr: VirtualReg,
|
||||
result: VirtualReg,
|
||||
offset: usize,
|
||||
},
|
||||
SetStructField {
|
||||
struct_addr: VirtualReg,
|
||||
value: VirtualReg,
|
||||
offset: usize,
|
||||
},
|
||||
Call {
|
||||
return_value: VirtualReg,
|
||||
func: FunctionName,
|
||||
args: Vec<VirtualReg>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IRProgram {
|
||||
pub functions: Vec<Function>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Function {
|
||||
pub name: FunctionName,
|
||||
pub params: Vec<VirtualReg>,
|
||||
pub blocks: Vec<BasicBlock>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BasicBlock {
|
||||
pub label: Label,
|
||||
pub statements: Vec<Statement>,
|
||||
pub terminator: Terminator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Terminator {
|
||||
Jump(Label),
|
||||
ConditionalJump {
|
||||
if_flag: VirtualReg,
|
||||
then_goto: Label,
|
||||
else_goto: Label,
|
||||
},
|
||||
Return(VirtualReg),
|
||||
ReturnVoid,
|
||||
}
|
||||
|
||||
pub type FunctionName = String;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Label {
|
||||
id: usize,
|
||||
name: Rc<String>,
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new_with_id(name: impl Into<String>, id: usize) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name: Rc::new(name.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(name: impl Into<String>) -> Self {
|
||||
Self {
|
||||
id: Self::get_new_id(),
|
||||
name: Rc::new(name.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_new_id() -> usize {
|
||||
LABEL_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Label {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "__{}_{}", self.name, self.id)
|
||||
}
|
||||
}
|
||||
|
||||
static LABEL_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct VirtualReg(pub isize);
|
||||
|
||||
impl VirtualReg {
|
||||
pub fn void() -> Self {
|
||||
Self(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Literal {
|
||||
UInt(usize),
|
||||
Int(isize),
|
||||
Char(char),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PrimitiveOperation {
|
||||
Add,
|
||||
Sub,
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Nor,
|
||||
Nand,
|
||||
}
|
||||
|
||||
impl fmt::Display for VirtualReg {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if self.0 == -1 {
|
||||
write!(f, "_")
|
||||
} else {
|
||||
write!(f, "v_{}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Literal {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Literal::UInt(v) => write!(f, "{}", v),
|
||||
Literal::Int(v) => write!(f, "{}", v),
|
||||
Literal::Char(c) => write!(f, "'{}'", c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PrimitiveOperation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let s = match self {
|
||||
PrimitiveOperation::Add => "+",
|
||||
PrimitiveOperation::Sub => "-",
|
||||
PrimitiveOperation::And => "&",
|
||||
PrimitiveOperation::Or => "|",
|
||||
PrimitiveOperation::Xor => "^",
|
||||
PrimitiveOperation::Nor => "nor",
|
||||
PrimitiveOperation::Nand => "nand",
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Statement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Statement::Const(dst, lit) => write!(f, "{} = literal {}", dst, lit),
|
||||
Statement::BinaryOp {
|
||||
left,
|
||||
op,
|
||||
right,
|
||||
result,
|
||||
} => {
|
||||
write!(f, "{} = {} {} {}", result, left, op, right)
|
||||
}
|
||||
Statement::Copy { src, dst } => write!(f, "{} = copy {}", dst, src),
|
||||
Statement::Store { value, address } => write!(f, "store {}, {}", address, value),
|
||||
Statement::Load { address, result } => write!(f, "{} = load {}", result, address),
|
||||
Statement::GetStructField {
|
||||
struct_addr,
|
||||
result,
|
||||
offset,
|
||||
} => {
|
||||
write!(f, "{} = {}.{}", result, struct_addr, offset)
|
||||
}
|
||||
Statement::SetStructField {
|
||||
struct_addr,
|
||||
value,
|
||||
offset,
|
||||
} => {
|
||||
write!(f, "{}.{} = {}", struct_addr, offset, value)
|
||||
}
|
||||
Statement::Call {
|
||||
return_value,
|
||||
func,
|
||||
args,
|
||||
} => {
|
||||
let args_str = args
|
||||
.iter()
|
||||
.map(|a| a.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
write!(f, "{} = {}({})", return_value, func, args_str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Terminator {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Terminator::Jump(label) => write!(f, "jump {}", label),
|
||||
Terminator::ConditionalJump {
|
||||
if_flag,
|
||||
then_goto,
|
||||
else_goto,
|
||||
} => {
|
||||
write!(f, "if {} then {} else {}", if_flag, then_goto, else_goto)
|
||||
}
|
||||
Terminator::Return(reg) => write!(f, "return {}", reg),
|
||||
Terminator::ReturnVoid => write!(f, "return"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BasicBlock {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "{}:", self.label)?;
|
||||
for stmt in &self.statements {
|
||||
writeln!(f, " {}", stmt)?;
|
||||
}
|
||||
writeln!(f, " {}", self.terminator)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Function {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let params_str = self
|
||||
.params
|
||||
.iter()
|
||||
.map(|p| p.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
writeln!(f, "fn {}({}) {{", self.name, params_str)?;
|
||||
for block in &self.blocks {
|
||||
write!(f, "{}", block)?;
|
||||
}
|
||||
writeln!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IRProgram {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
for (i, func) in self.functions.iter().enumerate() {
|
||||
if i > 0 {
|
||||
writeln!(f)?;
|
||||
}
|
||||
write!(f, "{}", func)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,803 @@
|
||||
use crate::model::register::Register;
|
||||
use std::fmt;
|
||||
|
||||
pub enum Comparison {
|
||||
Greater,
|
||||
GreaterEqual,
|
||||
Equal,
|
||||
NotEqual,
|
||||
Less,
|
||||
LessEqual,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Instruction {
|
||||
// Labels and comments
|
||||
Label(Label),
|
||||
Comment {
|
||||
text: String,
|
||||
top_level: bool,
|
||||
},
|
||||
Newline,
|
||||
|
||||
// Data Directives
|
||||
Db {
|
||||
label: String,
|
||||
data: Vec<u8>,
|
||||
},
|
||||
Dh {
|
||||
label: String,
|
||||
data: Vec<u16>,
|
||||
},
|
||||
Dw {
|
||||
label: String,
|
||||
data: Vec<u32>,
|
||||
},
|
||||
DString {
|
||||
// alias for db.
|
||||
label: String,
|
||||
data: String,
|
||||
},
|
||||
|
||||
Resx {
|
||||
label: String,
|
||||
size: u32,
|
||||
},
|
||||
|
||||
// Include
|
||||
Include {
|
||||
name: String,
|
||||
path: String,
|
||||
},
|
||||
|
||||
// Data movement
|
||||
Mov {
|
||||
src: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Movs {
|
||||
src: Register,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
// Memory operations
|
||||
Ldb {
|
||||
src: MemOperand,
|
||||
dest: Register,
|
||||
},
|
||||
Ldh {
|
||||
src: MemOperand,
|
||||
dest: Register,
|
||||
},
|
||||
Ldw {
|
||||
src: MemOperand,
|
||||
dest: Register,
|
||||
},
|
||||
Stb {
|
||||
src: Register,
|
||||
dest: MemOperand,
|
||||
},
|
||||
Sth {
|
||||
src: Register,
|
||||
dest: MemOperand,
|
||||
},
|
||||
Stw {
|
||||
src: Register,
|
||||
dest: MemOperand,
|
||||
},
|
||||
|
||||
// Immediate loads
|
||||
Lli {
|
||||
imm: Imm,
|
||||
dest: Register,
|
||||
},
|
||||
Lui {
|
||||
imm: Imm,
|
||||
dest: Register,
|
||||
},
|
||||
Lwi {
|
||||
imm: Imm,
|
||||
dest: Register,
|
||||
},
|
||||
LwiLabel {
|
||||
label: String,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
// Arithmetic
|
||||
Add {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Sub {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
AddI {
|
||||
src: Register,
|
||||
imm: Imm,
|
||||
dest: Option<Register>,
|
||||
},
|
||||
SubI {
|
||||
src: Register,
|
||||
imm: Imm,
|
||||
dest: Option<Register>,
|
||||
},
|
||||
|
||||
// Bitwise
|
||||
And {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Or {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Xor {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Not {
|
||||
src: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Nand {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Nor {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Xnor {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
// Shifts
|
||||
Shl {
|
||||
src1: Register,
|
||||
r_shamt: Register,
|
||||
i_shamt: u16,
|
||||
dest: Register,
|
||||
},
|
||||
Shr {
|
||||
src1: Register,
|
||||
r_shamt: Register,
|
||||
i_shamt: u16,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
Ieq {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Ine {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Ilt {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Ile {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Igt {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
Ige {
|
||||
src1: Register,
|
||||
src2: Register,
|
||||
dest: Register,
|
||||
},
|
||||
|
||||
// Jumps
|
||||
Jmp {
|
||||
target: Label,
|
||||
},
|
||||
|
||||
Jnz {
|
||||
src: Register,
|
||||
target: Label,
|
||||
},
|
||||
Jez {
|
||||
src: Register,
|
||||
target: Label,
|
||||
},
|
||||
|
||||
Jic {
|
||||
target: Label,
|
||||
},
|
||||
Jnc {
|
||||
target: Label,
|
||||
},
|
||||
|
||||
// Stack
|
||||
Push {
|
||||
reg: Register,
|
||||
},
|
||||
Pop {
|
||||
reg: Register,
|
||||
},
|
||||
|
||||
// Function calls
|
||||
Call {
|
||||
target: String,
|
||||
}, // namespace::function
|
||||
Return,
|
||||
|
||||
// System
|
||||
Hlt,
|
||||
Nop,
|
||||
Int {
|
||||
code: u8,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum DataDirective {
|
||||
U8(Vec<u8>),
|
||||
U16(Vec<u16>),
|
||||
U32(Vec<u32>),
|
||||
String(String),
|
||||
Char(char),
|
||||
}
|
||||
|
||||
impl fmt::Display for Instruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Label(l) => write!(f, "{}:", l),
|
||||
Self::Newline => write!(f, ""), /* empty string as newlines are inserted */
|
||||
// automatically.
|
||||
Self::Comment { text, top_level } => write!(
|
||||
f,
|
||||
"{}",
|
||||
text.lines()
|
||||
.map(|line| format!(
|
||||
"{}// {}",
|
||||
if *top_level { "" } else { " " },
|
||||
line.trim(),
|
||||
))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
),
|
||||
|
||||
Self::Include { name, path } => write!(f, "include {name}: \"{}\"", path),
|
||||
|
||||
Self::Db { label, data } => write!(
|
||||
f,
|
||||
"db {}: {}",
|
||||
label,
|
||||
data.iter()
|
||||
.map(|&b| format!("{:#04X}", b))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
Self::Dh { label, data } => write!(
|
||||
f,
|
||||
"dh {}: {}",
|
||||
label,
|
||||
data.iter()
|
||||
.map(|&b| format!("{:#06X}", b))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
Self::Dw { label, data } => write!(
|
||||
f,
|
||||
"dw {}: {}",
|
||||
label,
|
||||
data.iter()
|
||||
.map(|&b| format!("{:#08X}", b))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
Self::DString { label, data } => write!(f, "db {}: \"{}\"", label, data),
|
||||
|
||||
Self::Resx { label, size } => write!(f, "resx {}: {}", label, size),
|
||||
|
||||
Self::Mov { src, dest } => write!(f, " mov {}, {}", src, dest),
|
||||
Self::Movs { src, dest } => write!(f, " movs {}, {}", src, dest),
|
||||
|
||||
Self::Ldb { src: addr, dest } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " ldb {}, {}, {}", reg, dest, offset)
|
||||
}
|
||||
Self::Ldh { src: addr, dest } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " ldh {}, {}, {}", reg, dest, offset)
|
||||
}
|
||||
Self::Ldw { src, dest } => {
|
||||
let (reg, offset) = reg_and_offset(src);
|
||||
write!(f, " ldw {}, {}, {}", reg, dest, offset)
|
||||
}
|
||||
// Self::Ldbs { addr, dest } => {
|
||||
// write!(f, " ldbs {}, {}", format_mem_operand(addr), dest)
|
||||
// }
|
||||
// Self::Ldhs { addr, dest } => {
|
||||
// write!(f, " ldhs {}, {}", format_mem_operand(addr), dest)
|
||||
// }
|
||||
// Self::Ldws { addr, dest } => {
|
||||
// write!(f, " ldws {}, {}", format_mem_operand(addr), dest)
|
||||
// }
|
||||
Self::Stb { src, dest: addr } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " stb {}, {}, {}", src, reg, offset)
|
||||
}
|
||||
Self::Sth { src, dest: addr } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " sth {}, {}, {}", src, reg, offset)
|
||||
}
|
||||
Self::Stw { src, dest: addr } => {
|
||||
let (reg, offset) = reg_and_offset(addr);
|
||||
write!(f, " stw {}, {}, {}", src, reg, offset)
|
||||
}
|
||||
|
||||
Self::Lli { imm, dest } => write!(f, " lli {}, {}", imm, dest),
|
||||
Self::Lui { imm, dest } => write!(f, " lui {}, {}", imm, dest),
|
||||
Self::Lwi { imm, dest } => write!(f, " lwi {}, {}", imm, dest),
|
||||
Self::LwiLabel { label, dest } => write!(f, " lwi {}, {}", label, dest),
|
||||
|
||||
// arithmetic
|
||||
Self::Add { src1, src2, dest } => {
|
||||
write!(f, " add {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Sub { src1, src2, dest } => {
|
||||
write!(f, " sub {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::And { src1, src2, dest } => {
|
||||
write!(f, " and {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Or { src1, src2, dest } => {
|
||||
write!(f, " or {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Nand { src1, src2, dest } => {
|
||||
write!(f, " nand {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Xor { src1, src2, dest } => {
|
||||
write!(f, " xor {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Nor { src1, src2, dest } => {
|
||||
write!(f, " nor {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::Not { src, dest } => {
|
||||
write!(f, " not {} {}", src, dest)
|
||||
}
|
||||
Self::Xnor { src1, src2, dest } => {
|
||||
write!(f, " xnor {}, {}, {}", src1, src2, dest)
|
||||
}
|
||||
Self::AddI { src, imm, dest } => {
|
||||
if let Some(d) = dest {
|
||||
write!(f, " addi {}, {}, {}", src, imm, d)
|
||||
} else {
|
||||
write!(f, " addi {}, {}", src, imm)
|
||||
}
|
||||
}
|
||||
Self::SubI { src, imm, dest } => {
|
||||
if let Some(d) = dest {
|
||||
write!(f, " subi {}, {}, {}", src, imm, d)
|
||||
} else {
|
||||
write!(f, " subi {}, {}", src, imm)
|
||||
}
|
||||
}
|
||||
|
||||
// shift instructions
|
||||
Self::Shl {
|
||||
src1,
|
||||
r_shamt,
|
||||
i_shamt,
|
||||
dest,
|
||||
} => {
|
||||
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
|
||||
}
|
||||
Self::Shr {
|
||||
src1,
|
||||
r_shamt,
|
||||
i_shamt,
|
||||
dest,
|
||||
} => {
|
||||
write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
|
||||
}
|
||||
|
||||
Self::Ieq { src1, src2, dest } => write!(f, " ieq {}, {}, {}", src1, src2, dest),
|
||||
Self::Ine { src1, src2, dest } => write!(f, " ine {}, {}, {}", src1, src2, dest),
|
||||
Self::Ilt { src1, src2, dest } => write!(f, " ilt {}, {}, {}", src1, src2, dest),
|
||||
Self::Ile { src1, src2, dest } => write!(f, " ile {}, {}, {}", src1, src2, dest),
|
||||
Self::Igt { src1, src2, dest } => write!(f, " igt {}, {}, {}", src1, src2, dest),
|
||||
Self::Ige { src1, src2, dest } => write!(f, " ige {}, {}, {}", src1, src2, dest),
|
||||
|
||||
// jump instructions
|
||||
Self::Jmp { target } => write!(f, " jmp {}", target),
|
||||
Self::Jic { target } => write!(f, " jic {}", target),
|
||||
Self::Jnc { target } => write!(f, " jnc {}", target),
|
||||
Self::Jez { src, target } => write!(f, " jez {}, {}", src, target),
|
||||
Self::Jnz { src, target } => write!(f, " jnz {}, {}", src, target),
|
||||
|
||||
// stack instructions
|
||||
Self::Push { reg } => write!(f, " push {}", reg),
|
||||
Self::Pop { reg } => write!(f, " pop {}", reg),
|
||||
|
||||
// call & return instructions
|
||||
Self::Call { target } => write!(f, " call {}", target),
|
||||
Self::Return => write!(f, " ret"),
|
||||
|
||||
// misc instructions
|
||||
Self::Int { code } => write!(f, " int {}", code),
|
||||
Self::Hlt => write!(f, " hlt"),
|
||||
Self::Nop => write!(f, " nop"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
// data directives
|
||||
pub fn db_string(label: impl Into<String>, data: impl Into<String>) -> Self {
|
||||
Self::DString {
|
||||
label: label.into(),
|
||||
data: data.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn db_word(label: impl Into<String>, data: u32) -> Self {
|
||||
Self::Dw {
|
||||
label: label.into(),
|
||||
data: vec![data],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn db_bytes(label: impl Into<String>, data: &[u8]) -> Self {
|
||||
Self::Db {
|
||||
label: label.into(),
|
||||
data: data.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
// Movement
|
||||
pub fn mov<R1, R2>(src: R1, dest: R2) -> Self
|
||||
where
|
||||
R1: Into<Register>,
|
||||
R2: Into<Register>,
|
||||
{
|
||||
Self::Mov {
|
||||
src: src.into(),
|
||||
dest: dest.into(),
|
||||
}
|
||||
}
|
||||
|
||||
// Memory loads
|
||||
pub fn ldw_reg<R>(base: R, dest: Register) -> Self
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
Self::Ldw {
|
||||
src: MemOperand::RegIndirect(base.into()),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ldw_reg_offset<R>(base: R, dest: Register, offset: i32) -> Self
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
Self::Ldw {
|
||||
src: MemOperand::RegOffset(base.into(), offset),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ldw_label(label: impl Into<Label>, dest: Register) -> Self {
|
||||
Self::Ldw {
|
||||
src: MemOperand::Label(label.into()),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
// Memory stores
|
||||
pub fn stw_reg<R>(src: Register, base: R) -> Self
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
Self::Stw {
|
||||
src,
|
||||
dest: MemOperand::RegIndirect(base.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stw_reg_offset<R>(src: Register, base: R, offset: i32) -> Self
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
Self::Stw {
|
||||
src,
|
||||
dest: MemOperand::RegOffset(base.into(), offset),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stw_label(src: Register, label: impl Into<Label>) -> Self {
|
||||
Self::Stw {
|
||||
src,
|
||||
dest: MemOperand::Label(label.into()),
|
||||
}
|
||||
}
|
||||
|
||||
// Arithmetic
|
||||
pub fn add(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::Add { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn sub(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::Sub { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn and(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::And { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn or(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::Or { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn xor(src1: Register, src2: Register, dest: Register) -> Self {
|
||||
Self::Xor { src1, src2, dest }
|
||||
}
|
||||
|
||||
pub fn not(src: Register, dest: Register) -> Self {
|
||||
Self::Not { src, dest }
|
||||
}
|
||||
|
||||
pub fn shl(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
|
||||
Self::Shl {
|
||||
src1,
|
||||
r_shamt,
|
||||
i_shamt,
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shr(src1: Register, r_shamt: Register, i_shamt: u16, dest: Register) -> Self {
|
||||
Self::Shr {
|
||||
src1,
|
||||
r_shamt,
|
||||
i_shamt,
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iadd(src: Register, value: i64) -> Self {
|
||||
let imm = Imm(value.unsigned_abs() as u32);
|
||||
|
||||
if value < 0 {
|
||||
Self::SubI {
|
||||
src,
|
||||
imm,
|
||||
dest: None,
|
||||
}
|
||||
} else {
|
||||
Self::AddI {
|
||||
src,
|
||||
imm,
|
||||
dest: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iadd_dest(src: Register, value: i32, dest: Register) -> Self {
|
||||
let imm = Imm(value.unsigned_abs());
|
||||
|
||||
if value < 0 {
|
||||
Self::SubI {
|
||||
src,
|
||||
imm,
|
||||
dest: Some(dest),
|
||||
}
|
||||
} else {
|
||||
Self::AddI {
|
||||
src,
|
||||
imm,
|
||||
dest: Some(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inc(reg: Register) -> Self {
|
||||
Self::AddI {
|
||||
src: reg,
|
||||
imm: Imm(1),
|
||||
dest: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dec(reg: Register) -> Self {
|
||||
Self::SubI {
|
||||
src: reg,
|
||||
imm: Imm(1),
|
||||
dest: None,
|
||||
}
|
||||
}
|
||||
|
||||
// Immediate loads
|
||||
pub fn lwi(value: u32, dest: Register) -> Self {
|
||||
if value > 0xFFFF {
|
||||
Self::Lwi {
|
||||
imm: Imm(value),
|
||||
dest,
|
||||
}
|
||||
} else {
|
||||
Self::Lli {
|
||||
imm: Imm(value),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lwi_label(label: impl Into<String>, dest: Register) -> Self {
|
||||
Self::LwiLabel {
|
||||
label: label.into(),
|
||||
dest,
|
||||
}
|
||||
}
|
||||
|
||||
// Control flow
|
||||
pub fn label(name: impl Into<String>) -> Self {
|
||||
Self::Label(Label(name.into()))
|
||||
}
|
||||
|
||||
pub fn cmp(operand: Comparison, src1: Register, src2: Register, dest: Register) -> Self {
|
||||
match operand {
|
||||
Comparison::Equal => Self::Ieq { src1, src2, dest },
|
||||
Comparison::NotEqual => Self::Ine { src1, src2, dest },
|
||||
Comparison::Greater => Self::Igt { src1, src2, dest },
|
||||
Comparison::GreaterEqual => Self::Ige { src1, src2, dest },
|
||||
Comparison::Less => Self::Ilt { src1, src2, dest },
|
||||
Comparison::LessEqual => Self::Ile { src1, src2, dest },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jmp(target: impl Into<Label>) -> Self {
|
||||
Self::Jmp {
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jnz(src: Register, target: impl Into<Label>) -> Self {
|
||||
Self::Jnz {
|
||||
src,
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jez(src: Register, target: impl Into<Label>) -> Self {
|
||||
Self::Jez {
|
||||
src,
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jic(target: impl Into<Label>) -> Self {
|
||||
Self::Jic {
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jnc(target: impl Into<Label>) -> Self {
|
||||
Self::Jnc {
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
// Stack
|
||||
pub fn push(reg: Register) -> Self {
|
||||
Self::Push { reg }
|
||||
}
|
||||
|
||||
pub fn pop(reg: Register) -> Self {
|
||||
Self::Pop { reg }
|
||||
}
|
||||
|
||||
// Functions
|
||||
pub fn call(target: impl Into<String>) -> Self {
|
||||
Self::Call {
|
||||
target: target.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn int(code: u8) -> Self {
|
||||
Self::Int { code }
|
||||
}
|
||||
|
||||
pub fn ret() -> Self {
|
||||
Self::Return
|
||||
}
|
||||
|
||||
// Utilities
|
||||
pub fn comment(text: impl Into<String>) -> Self {
|
||||
Self::Comment {
|
||||
text: text.into(),
|
||||
top_level: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn global_comment(text: impl Into<String>) -> Self {
|
||||
Self::Comment {
|
||||
text: text.into(),
|
||||
top_level: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn include(name: impl Into<String>, path: impl Into<String>) -> Self {
|
||||
Self::Include {
|
||||
name: name.into(),
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience trait for Label conversion
|
||||
impl From<String> for Label {
|
||||
fn from(s: String) -> Self {
|
||||
Label(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Label {
|
||||
fn from(s: &str) -> Self {
|
||||
Label(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn reg_and_offset(op: &MemOperand) -> (String, i32) {
|
||||
match op {
|
||||
MemOperand::RegIndirect(reg) => (reg.to_string(), 0),
|
||||
MemOperand::RegOffset(reg, offset) => (reg.to_string(), *offset),
|
||||
MemOperand::Label(label) => (label.to_string(), 0),
|
||||
MemOperand::LabelOffset(label, offset) => (label.to_string(), *offset),
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory operand for loads/stores
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MemOperand {
|
||||
/// Register indirect: [reg]
|
||||
RegIndirect(Register),
|
||||
/// Register with offset: [reg + offset]
|
||||
RegOffset(Register, i32),
|
||||
/// Label: [label]
|
||||
Label(Label),
|
||||
/// Label with offset: [label + offset]
|
||||
LabelOffset(Label, i32),
|
||||
}
|
||||
|
||||
/// Immediate value (16-bit or 32-bit)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Imm(pub u32);
|
||||
|
||||
impl fmt::Display for Imm {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Label reference
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Label(pub String);
|
||||
|
||||
impl fmt::Display for Label {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use ast::{Name, TypeId};
|
||||
use std::fmt;
|
||||
|
||||
pub mod ast;
|
||||
pub mod dsa_ir;
|
||||
pub mod instruction;
|
||||
pub mod register;
|
||||
pub mod tokens;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CompilerError {
|
||||
UnexpectedToken(String),
|
||||
UnexpectedEndOfInput,
|
||||
UnexpectedCharacter(char),
|
||||
Undefined(Name),
|
||||
InvalidSyntax(String),
|
||||
Generic(String),
|
||||
UnknownType,
|
||||
TypeMismatch(TypeId, TypeId),
|
||||
Unimplemented(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for CompilerError {}
|
||||
|
||||
impl fmt::Display for CompilerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerError {
|
||||
pub fn generic(msg: impl Into<String>) -> Self {
|
||||
Self::Generic(msg.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Location {
|
||||
pub register: Option<Register>,
|
||||
pub stack: Option<i32>,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
pub fn stack(offset: i32) -> Self {
|
||||
Location {
|
||||
register: None,
|
||||
stack: Some(offset),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(register: Register) -> Self {
|
||||
Location {
|
||||
register: Some(register),
|
||||
stack: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Register {
|
||||
// general purpose
|
||||
Rg0 = 0,
|
||||
Rg1 = 1,
|
||||
Rg2 = 2,
|
||||
Rg3 = 3,
|
||||
Rg4 = 4,
|
||||
Rg5 = 5,
|
||||
Rg6 = 6,
|
||||
Rg7 = 7,
|
||||
Rg8 = 8,
|
||||
Rg9 = 9,
|
||||
Rga = 10,
|
||||
Rgb = 11,
|
||||
Rgc = 12,
|
||||
Rgd = 13,
|
||||
Rge = 14,
|
||||
Rgf = 15,
|
||||
|
||||
// special
|
||||
Bpr,
|
||||
Spr,
|
||||
Ret,
|
||||
Acc,
|
||||
|
||||
// read only
|
||||
Pcx,
|
||||
Zero,
|
||||
|
||||
// null
|
||||
Null,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub fn get_gp() -> [Register; 16] {
|
||||
[
|
||||
Register::Rg0,
|
||||
Register::Rg1,
|
||||
Register::Rg2,
|
||||
Register::Rg3,
|
||||
Register::Rg4,
|
||||
Register::Rg5,
|
||||
Register::Rg6,
|
||||
Register::Rg7,
|
||||
Register::Rg8,
|
||||
Register::Rg9,
|
||||
Register::Rga,
|
||||
Register::Rgb,
|
||||
Register::Rgc,
|
||||
Register::Rgd,
|
||||
Register::Rge,
|
||||
Register::Rgf,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn is_gp(&self) -> bool {
|
||||
(*self as u8) < 16
|
||||
}
|
||||
|
||||
pub fn from_index(idx: usize) -> Register {
|
||||
match idx {
|
||||
0 => Register::Rg0,
|
||||
1 => Register::Rg1,
|
||||
2 => Register::Rg2,
|
||||
3 => Register::Rg3,
|
||||
4 => Register::Rg4,
|
||||
5 => Register::Rg5,
|
||||
6 => Register::Rg6,
|
||||
7 => Register::Rg7,
|
||||
8 => Register::Rg8,
|
||||
9 => Register::Rg9,
|
||||
10 => Register::Rga,
|
||||
11 => Register::Rgb,
|
||||
12 => Register::Rgc,
|
||||
13 => Register::Rgd,
|
||||
14 => Register::Rge,
|
||||
15 => Register::Rgf,
|
||||
_ => unreachable!("this function shouldn't ever be called with idx>15"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Register {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Rg0 => write!(f, "rg0"),
|
||||
Self::Rg1 => write!(f, "rg1"),
|
||||
Self::Rg2 => write!(f, "rg2"),
|
||||
Self::Rg3 => write!(f, "rg3"),
|
||||
Self::Rg4 => write!(f, "rg4"),
|
||||
Self::Rg5 => write!(f, "rg5"),
|
||||
Self::Rg6 => write!(f, "rg6"),
|
||||
Self::Rg7 => write!(f, "rg7"),
|
||||
Self::Rg8 => write!(f, "rg8"),
|
||||
Self::Rg9 => write!(f, "rg9"),
|
||||
Self::Rga => write!(f, "rga"),
|
||||
Self::Rgb => write!(f, "rgb"),
|
||||
Self::Rgc => write!(f, "rgc"),
|
||||
Self::Rgd => write!(f, "rgd"),
|
||||
Self::Rge => write!(f, "rge"),
|
||||
Self::Rgf => write!(f, "rgf"),
|
||||
|
||||
Self::Acc => write!(f, "acc"),
|
||||
Self::Ret => write!(f, "ret"),
|
||||
Self::Bpr => write!(f, "bpr"),
|
||||
Self::Spr => write!(f, "spr"),
|
||||
|
||||
Self::Zero => write!(f, "zero"),
|
||||
Self::Pcx => write!(f, "pcx"),
|
||||
|
||||
Self::Null => write!(f, "null"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
use crate::model::ast::{Name, TypeId};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Token {
|
||||
// Keywords
|
||||
Fn,
|
||||
Let,
|
||||
If,
|
||||
Else,
|
||||
Loop,
|
||||
While,
|
||||
Break,
|
||||
Return,
|
||||
Continue,
|
||||
Include,
|
||||
Static,
|
||||
Const,
|
||||
As,
|
||||
SizeOf,
|
||||
Struct,
|
||||
|
||||
// Identifiers and literals
|
||||
Identifier(Name),
|
||||
String(String),
|
||||
Char(char),
|
||||
SignedInt(i32, Option<TypeId>),
|
||||
UnsignedInt(u32, Option<TypeId>),
|
||||
|
||||
// Delimiters
|
||||
LeftParen, // (
|
||||
RightParen, // )
|
||||
LeftBrace, // {
|
||||
RightBrace, // }
|
||||
LeftBracket, // [
|
||||
RightBracket, // ]
|
||||
Semicolon, // ;
|
||||
Colon, // :
|
||||
Comma, // ,
|
||||
Dot, // .
|
||||
RightArrow, // ->
|
||||
|
||||
// Arithmetic operators
|
||||
Plus, // +
|
||||
Minus, // -
|
||||
Star, // *
|
||||
Slash, // /
|
||||
Percent, // %
|
||||
PlusPlus, // ++
|
||||
MinusMinus, // --
|
||||
|
||||
// Bitwise operators
|
||||
Ampersand, // &
|
||||
Pipe, // |
|
||||
Caret, // ^
|
||||
Tilde, // ~
|
||||
LeftShift, // <<
|
||||
RightShift, // >>
|
||||
|
||||
// Logical operators
|
||||
Bang, // !
|
||||
LogicalAnd, // &&
|
||||
LogicalOr, // ||
|
||||
|
||||
// Comparison operators
|
||||
EqualEqual, // ==
|
||||
BangEqual, // !=
|
||||
Less, // <
|
||||
LessEqual, // <=
|
||||
Greater, // >
|
||||
GreaterEqual, // >=
|
||||
|
||||
// Assignment operators
|
||||
Assign, // =
|
||||
PlusEqual, // +=
|
||||
MinusEqual, // -=
|
||||
StarEqual, // *=
|
||||
SlashEqual, // /=
|
||||
PercentEqual, // %=
|
||||
AndEqual, // &=
|
||||
OrEqual, // |=
|
||||
XorEqual, // ^=
|
||||
ShlEqual, // <<=
|
||||
ShrEqual, // >>=
|
||||
|
||||
// Special
|
||||
Eof,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn tt(&self) -> &str {
|
||||
match self {
|
||||
Token::Const => "Const",
|
||||
Token::Static => "Static",
|
||||
Token::Include => "Include",
|
||||
Token::Struct => "Struct",
|
||||
Token::Fn => "Fn",
|
||||
Token::If => "If",
|
||||
Token::Let => "Let",
|
||||
Token::Else => "Else",
|
||||
Token::Loop => "Loop",
|
||||
Token::While => "While",
|
||||
Token::Break => "Break",
|
||||
Token::Return => "Return",
|
||||
Token::Continue => "Continue",
|
||||
Token::As => "As",
|
||||
Token::Identifier(_) => "Identifier",
|
||||
Token::String(_) => "String",
|
||||
Token::UnsignedInt(_, _) => "UnsignedInt",
|
||||
Token::SignedInt(_, _) => "SignedInt",
|
||||
Token::Char(_) => "Char",
|
||||
Token::LeftParen => "LeftParen",
|
||||
Token::RightParen => "RightParen",
|
||||
Token::LeftBrace => "LeftBrace",
|
||||
Token::RightBrace => "RightBrace",
|
||||
Token::LeftBracket => "LeftBracket",
|
||||
Token::RightBracket => "RightBracket",
|
||||
Token::Semicolon => "Semicolon",
|
||||
Token::Colon => "Colon",
|
||||
Token::Comma => "Comma",
|
||||
Token::Dot => "Dot",
|
||||
Token::RightArrow => "RightArrow",
|
||||
Token::Plus => "Plus",
|
||||
Token::Minus => "Minus",
|
||||
Token::Star => "Star",
|
||||
Token::Slash => "Slash",
|
||||
Token::Percent => "Percent",
|
||||
Token::PlusPlus => "PlusPlus",
|
||||
Token::MinusMinus => "MinusMinus",
|
||||
Token::Ampersand => "Ampersand",
|
||||
Token::Pipe => "Pipe",
|
||||
Token::Caret => "Caret",
|
||||
Token::Tilde => "Tilde",
|
||||
Token::LeftShift => "LeftShift",
|
||||
Token::RightShift => "RightShift",
|
||||
Token::Bang => "Bang",
|
||||
Token::LogicalAnd => "LogicalAnd",
|
||||
Token::LogicalOr => "LogicalOr",
|
||||
Token::EqualEqual => "EqualEqual",
|
||||
Token::BangEqual => "BangEqual",
|
||||
Token::Less => "Less",
|
||||
Token::LessEqual => "LessEqual",
|
||||
Token::Greater => "Greater",
|
||||
Token::GreaterEqual => "GreaterEqual",
|
||||
Token::Assign => "Assign",
|
||||
Token::PlusEqual => "PlusEqual",
|
||||
Token::MinusEqual => "MinusEqual",
|
||||
Token::StarEqual => "StarEqual",
|
||||
Token::SlashEqual => "SlashEqual",
|
||||
Token::PercentEqual => "PercentEqual",
|
||||
Token::AndEqual => "AndEqual",
|
||||
Token::OrEqual => "OrEqual",
|
||||
Token::XorEqual => "XorEqual",
|
||||
Token::ShlEqual => "ShlEqual",
|
||||
Token::ShrEqual => "ShrEqual",
|
||||
Token::SizeOf => "SizeOf",
|
||||
Token::Eof => "Eof",
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user