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 BY DSC COMPILER
|
||||||
// Generated at 2026-08-20 11:13:23
|
// Generated at 2026-08-21 19:34:30
|
||||||
|
|
||||||
// Imports
|
// Imports
|
||||||
include alloc: "./lib/memory/block_alloc.dsa"
|
include alloc: "./lib/memory/block_alloc.dsa"
|
||||||
@@ -30,7 +30,7 @@ _ret:
|
|||||||
pop bpr
|
pop bpr
|
||||||
ret
|
ret
|
||||||
|
|
||||||
db str_4: "successful free of ptr"
|
db str_8: "successful free of ptr"
|
||||||
// fn main() -> u32
|
// fn main() -> u32
|
||||||
main:
|
main:
|
||||||
push bpr
|
push bpr
|
||||||
@@ -121,18 +121,18 @@ main:
|
|||||||
ldw spr, rg2, 8
|
ldw spr, rg2, 8
|
||||||
lli 0, rg4
|
lli 0, rg4
|
||||||
ieq rg2, rg4, rg5
|
ieq rg2, rg4, rg5
|
||||||
jez rg5, _else_2
|
jez rg5, _else_6
|
||||||
_then_1:
|
_then_5:
|
||||||
lwi str_4, rg4
|
lwi str_8, rg4
|
||||||
stw rg2, spr, 8
|
stw rg2, spr, 8
|
||||||
// push arg 0
|
// push arg 0
|
||||||
push rg4
|
push rg4
|
||||||
call print::print
|
call print::print
|
||||||
pop zero
|
pop zero
|
||||||
jmp _end_3
|
jmp _end_7
|
||||||
_else_2:
|
_else_6:
|
||||||
nop
|
nop
|
||||||
_end_3:
|
_end_7:
|
||||||
lli 0, rg4
|
lli 0, rg4
|
||||||
stw rg4, bpr, 8
|
stw rg4, bpr, 8
|
||||||
jmp _ret
|
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::instruction::InsBlock as IB;
|
||||||
use super::registers::{Register, RegisterAllocator};
|
use super::registers::RegisterAllocator;
|
||||||
use crate::backend::instruction::Comparison::{
|
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,
|
Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual,
|
||||||
};
|
};
|
||||||
use crate::model::{
|
use crate::model::instruction::{Instruction as I, Label};
|
||||||
AssignmentOperator, BinaryOperator, Call, CompilerError, ConstExpr, Declaration, Dependency,
|
use crate::model::register::Register;
|
||||||
Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
|
||||||
};
|
|
||||||
use chrono::{DateTime, Local};
|
use chrono::{DateTime, Local};
|
||||||
use dsa_tasks::ProcessAgent;
|
use dsa_tasks::ProcessAgent;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use std::fmt;
|
use crate::model::instruction::Instruction;
|
||||||
|
|
||||||
use crate::backend::registers::Register;
|
|
||||||
|
|
||||||
pub struct InsBlock {
|
pub struct InsBlock {
|
||||||
instructions: Vec<Instruction>,
|
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 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::model::ast::{
|
||||||
use crate::backend::intermediate_rep::{BasicBlock, Label, Terminator, VirtualReg};
|
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 {
|
pub struct IrLoweringCodeGenerator {
|
||||||
ast: Program,
|
ast: Program,
|
||||||
|
|
||||||
functions: Vec<IR::Function>
|
functions: Vec<IR::Function>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrLoweringCodeGenerator {
|
impl IrLoweringCodeGenerator {
|
||||||
pub fn new(ast: Program) -> Self {
|
pub fn new(ast: Program) -> Self {
|
||||||
Self { ast, functions: vec![] }
|
Self {
|
||||||
}
|
ast,
|
||||||
|
functions: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
pub fn run(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
||||||
for block in self.ast.clone().declarations {
|
for block in self.ast.clone().declarations {
|
||||||
match block {
|
match block {
|
||||||
Declaration::Variable { ..} => todo!(),
|
Declaration::Variable { .. } => {}
|
||||||
Declaration::Dependency(_) => todo!(),
|
Declaration::Dependency(_) => {}
|
||||||
Declaration::Function { name, ..} => todo!(),
|
Declaration::Function { name, .. } => {}
|
||||||
Declaration::Struct { .. } => todo!(),
|
Declaration::Struct { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for block in self.ast.clone().declarations {
|
for block in self.ast.clone().declarations {
|
||||||
self.generate_block(&block)?;
|
self.generate_block(&block)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.output(ctx)
|
self.output(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
pub fn output(&mut self, ctx: &ProcessAgent) -> Result<IR::IRProgram, CompilerError> {
|
||||||
todo!()
|
Ok(IRProgram {
|
||||||
}
|
functions: self.functions.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate_block(&mut self, decl: &Declaration) -> Result<(), CompilerError> {
|
pub fn generate_block(&mut self, decl: &Declaration) -> Result<(), CompilerError> {
|
||||||
match decl {
|
match decl {
|
||||||
Declaration::Function { name, params, body, return_type } => {
|
Declaration::Function {
|
||||||
let function = self.generate_function(name, params, body, return_type)?;
|
name,
|
||||||
self.functions.push(function);
|
params,
|
||||||
},
|
body,
|
||||||
Declaration::Struct { .. } => todo!("no codegen required for structs yet"),
|
return_type,
|
||||||
Declaration::Dependency(_) => todo!("no codegen required for dependencies yet"),
|
} => {
|
||||||
Declaration::Variable { .. } => todo!("codegen for statics and constants"),
|
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(
|
pub fn generate_function(
|
||||||
&mut self, function_name: &str,
|
&mut self,
|
||||||
params: &[Variable],
|
function_name: &str,
|
||||||
body: &Block,
|
params: &[Variable],
|
||||||
return_type: &TypeId
|
body: &Block,
|
||||||
) -> Result<IR::Function, CompilerError> {
|
return_type: &TypeId,
|
||||||
let mut context = FunctionContext::new(Label::new(function_name.to_string()));
|
) -> Result<IR::Function, CompilerError> {
|
||||||
|
let mut context = FunctionContext::new(Label::new(function_name.to_string()));
|
||||||
|
|
||||||
let params = (0..params.len())
|
let params = (0..params.len()).map(|_| context.new_vreg()).collect();
|
||||||
.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 {
|
Ok(IR::Function {
|
||||||
name: function_name.to_string(), params, blocks,
|
name: function_name.to_string(),
|
||||||
})
|
params,
|
||||||
}
|
blocks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn collapse_block(
|
fn collapse_block(
|
||||||
&self,
|
&self,
|
||||||
block: &Block,
|
block: &Block,
|
||||||
ctx: &mut FunctionContext,
|
ctx: &mut FunctionContext,
|
||||||
) -> Result<(), CompilerError> {
|
) -> Result<(), CompilerError> {
|
||||||
for statement in block {
|
for statement in block {
|
||||||
self.collapse_statement(statement, ctx)?;
|
self.collapse_statement(statement, ctx)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collapse_statement(
|
fn collapse_statement(
|
||||||
&self,
|
&self,
|
||||||
stmt: &Statement,
|
stmt: &Statement,
|
||||||
ctx: &mut FunctionContext,
|
ctx: &mut FunctionContext,
|
||||||
) -> Result<(), CompilerError> {
|
) -> Result<(), CompilerError> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Block(block) => {
|
Statement::Block(block) => self.collapse_block(block, ctx),
|
||||||
self.collapse_block(block, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
Statement::If { condition, then_stmt, else_stmt } => {
|
Statement::If {
|
||||||
/* if statement expansion. produces the following layout
|
condition,
|
||||||
reg_cond = ... expansion of {condition} ...
|
then_stmt,
|
||||||
if reg_cond goto then_{id} else goto else_{id}
|
else_stmt,
|
||||||
then_{id}:
|
} => {
|
||||||
... expansion of {then_stmt} ...
|
/* if statement expansion. produces the following layout
|
||||||
goto end_{id}
|
reg_cond = ... expansion of {condition} ...
|
||||||
else_{id}:
|
if reg_cond goto then_{id} else goto else_{id}
|
||||||
... expansion of {else_stmt} ...
|
then_{id}:
|
||||||
goto end_{id}
|
... expansion of {then_stmt} ...
|
||||||
end_{id}:
|
goto end_{id}
|
||||||
... not our responsibility ...
|
else_{id}:
|
||||||
*/
|
... expansion of {else_stmt} ...
|
||||||
let id = Label::get_new_id();
|
goto end_{id}
|
||||||
let then_label = Label::new_with_id("then", id);
|
end_{id}:
|
||||||
let else_label = Label::new_with_id("else", id);
|
... not our responsibility ...
|
||||||
let end_label = Label::new_with_id("endif", id);
|
*/
|
||||||
|
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(
|
ctx.next_block(
|
||||||
Terminator::ConditionalJump {
|
Terminator::ConditionalJump {
|
||||||
if_flag: eval_result,
|
if_flag: eval_result,
|
||||||
then_goto: then_label.clone(),
|
then_goto: then_label.clone(),
|
||||||
else_goto: else_label.clone(),
|
else_goto: else_label.clone(),
|
||||||
},
|
},
|
||||||
then_label.clone()
|
then_label.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.collapse_block(then_stmt, ctx)?;
|
self.collapse_block(then_stmt, ctx)?;
|
||||||
|
|
||||||
ctx.next_block(
|
ctx.next_block(Terminator::Jump(end_label.clone()), else_label.clone());
|
||||||
Terminator::Jump(end_label.clone()),
|
|
||||||
else_label.clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
self.collapse_block(else_stmt, ctx)?;
|
self.collapse_block(else_stmt, ctx)?;
|
||||||
|
|
||||||
ctx.next_block(
|
ctx.next_block(Terminator::Jump(end_label.clone()), end_label.clone());
|
||||||
Terminator::Jump(end_label.clone()),
|
|
||||||
end_label.clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::Defer(call) => {
|
Statement::Defer(call) => {
|
||||||
// defers stmt. all currently in scope defers are called in FILO order on return
|
// defers stmt. all currently in scope defers are called in FILO order on return
|
||||||
ctx.defer(call.clone());
|
ctx.defer(call.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
|
|
||||||
Statement::Return(expr) => {
|
Statement::Return(expr) => {
|
||||||
// return stmt. calls relevant defer methods and expands the body of the return statement.
|
// 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 {
|
// run cleanup logic (defers)
|
||||||
self.collapse_expression(value, ctx)?
|
for call in ctx.get_deferred_calls() {
|
||||||
} else { VirtualReg::void() };
|
self.collapse_call(&call, ctx, VirtualReg::void())?;
|
||||||
|
}
|
||||||
|
|
||||||
// run cleanup logic (defers)
|
ctx.next_block(Terminator::Return(result), Label::new("unreachable"));
|
||||||
for call in ctx.get_deferred_calls() {
|
|
||||||
self.collapse_call(&call, ctx)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 } => {
|
Statement::Continue => {
|
||||||
// generate the code for the expression but discard the result
|
// continue stmt. goes to the 'start' block of the loop
|
||||||
// let the optimiser remove it if it provably has no side effects.
|
|
||||||
|
|
||||||
let _ = self.collapse_expression(expr, ctx)?;
|
let Some(scope) = ctx.current_scope() else {
|
||||||
Ok(())
|
return Err(CompilerError::generic(
|
||||||
}
|
"Invalid 'Continue' statement (not inside any loop)",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
Statement::Continue => {
|
ctx.next_block(Terminator::Jump(scope.start), Label::new("unreachable"));
|
||||||
// continue stmt. goes to the 'start' block of the loop
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
let Some(scope) = ctx.current_scope() else {
|
Statement::Break => {
|
||||||
return Err(CompilerError::generic("Invalid 'Continue' statement (not inside any loop)"));
|
// break stmt. jumps to the 'end' block of the loop
|
||||||
};
|
|
||||||
|
|
||||||
ctx.next_block(Terminator::Jump(scope.start), Label::new("unreachable"));
|
let Some(scope) = ctx.current_scope() else {
|
||||||
Ok(())
|
return Err(CompilerError::generic(
|
||||||
},
|
"Invalid 'Continue' statement (not inside any loop)",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
Statement::Break => {
|
ctx.next_block(Terminator::Jump(scope.end), Label::new("unreachable"));
|
||||||
// break stmt. jumps to the 'end' block of the loop
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
let Some(scope) = ctx.current_scope() else {
|
Statement::While { condition, body } => {
|
||||||
return Err(CompilerError::generic("Invalid 'Continue' statement (not inside any loop)"));
|
/* 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"));
|
let id = Label::get_new_id();
|
||||||
Ok(())
|
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 } => {
|
ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone());
|
||||||
/* 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 ...
|
|
||||||
*/
|
|
||||||
|
|
||||||
let id = Label::get_new_id();
|
let if_flag = ctx.new_vreg();
|
||||||
let check_label = Label::new_with_id("while_check", id);
|
self.collapse_expression(condition, ctx, if_flag)?;
|
||||||
let body_label = Label::new_with_id("while_body", id);
|
|
||||||
let end_label = Label::new_with_id("while_end", id);
|
|
||||||
|
|
||||||
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(
|
ctx.next_block(Terminator::Jump(check_label.clone()), end_label.clone());
|
||||||
Terminator::ConditionalJump {
|
|
||||||
if_flag: result,
|
|
||||||
then_goto: body_label.clone(),
|
|
||||||
else_goto: end_label.clone()
|
|
||||||
},
|
|
||||||
body_label.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.collapse_block(body, ctx)?;
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
ctx.next_block(
|
Statement::Loop(block) => {
|
||||||
Terminator::Jump(check_label.clone()),
|
/* indefinite loop. produces the following layout
|
||||||
end_label.clone()
|
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) => {
|
ctx.next_block(Terminator::Jump(start_label.clone()), start_label.clone());
|
||||||
/* indefinite loop. produces the following layout
|
|
||||||
goto start_loop_{id}
|
|
||||||
start_loop_{id}:
|
|
||||||
goto start_loop_{id}
|
|
||||||
while_end_{id}:
|
|
||||||
... not our responsibility ...
|
|
||||||
*/
|
|
||||||
|
|
||||||
let id = Label::get_new_id();
|
ctx.enter_scope(Scope::new(start_label.clone(), end_label.clone()));
|
||||||
let start_label = Label::new_with_id("start_loop", id);
|
|
||||||
let end_label = Label::new_with_id("end_loop", id);
|
|
||||||
|
|
||||||
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(())
|
let v_reg = ctx.new_vreg();
|
||||||
},
|
self.collapse_expression(expr, ctx, v_reg)?;
|
||||||
_ => todo!()
|
ctx.new_local(v_reg, var.clone());
|
||||||
}
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collapse_call(
|
Statement::Assign {
|
||||||
&self,
|
varname,
|
||||||
call: &Call,
|
value,
|
||||||
ctx: &mut FunctionContext
|
operator,
|
||||||
) -> Result<VirtualReg, CompilerError> {
|
} => {
|
||||||
let reg = ctx.new_vreg();
|
let Some(v_reg) = ctx.find_local(varname) else {
|
||||||
|
return Err(CompilerError::generic(format!(
|
||||||
|
"Variable not defined: {varname}"
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
let args = call.args.iter()
|
self.collapse_expression(value, ctx, v_reg)?;
|
||||||
.map(|arg| self.collapse_expression(&arg, ctx))
|
Ok(())
|
||||||
.collect::<Result<Vec<VirtualReg>, _>>()?;
|
} // todo!!!!
|
||||||
|
|
||||||
ctx.push_stmt(IR::Statement::Call {
|
Statement::PtrWrite { ptr, value } => {
|
||||||
return_value: reg,
|
let ptr_reg = ctx.new_vreg();
|
||||||
func: call.name.to_string(),
|
self.collapse_expression(&ptr, ctx, ptr_reg)?;
|
||||||
args
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(reg)
|
let value_reg = ctx.new_vreg();
|
||||||
}
|
self.collapse_expression(&value, ctx, value_reg)?;
|
||||||
|
|
||||||
fn collapse_expression(
|
ctx.push_stmt(IR::Statement::Store {
|
||||||
&self,
|
value: value_reg,
|
||||||
expr: &Expression,
|
address: ptr_reg,
|
||||||
ctx: &mut FunctionContext,
|
});
|
||||||
) -> Result<VirtualReg, CompilerError> {
|
|
||||||
todo!()
|
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 {
|
struct BasicBlockBuilder {
|
||||||
label: Label,
|
label: Label,
|
||||||
statements: Vec<IR::Statement>
|
statements: Vec<IR::Statement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasicBlockBuilder {
|
impl BasicBlockBuilder {
|
||||||
fn new(label: Label) -> Self {
|
fn new(label: Label) -> Self {
|
||||||
Self {
|
Self {
|
||||||
label,
|
label,
|
||||||
statements: vec![]
|
statements: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, statement: IR::Statement) {
|
pub fn push(&mut self, statement: IR::Statement) {
|
||||||
self.statements.push(statement);
|
self.statements.push(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_block(&mut self, terminator: Terminator, label: Label) -> BasicBlock {
|
pub fn next_block(&mut self, terminator: Terminator, label: Label) -> BasicBlock {
|
||||||
BasicBlock {
|
BasicBlock {
|
||||||
label: std::mem::replace(&mut self.label, label),
|
label: std::mem::replace(&mut self.label, label),
|
||||||
statements: std::mem::take(&mut self.statements),
|
statements: std::mem::take(&mut self.statements),
|
||||||
terminator,
|
terminator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminate_block(self, terminator: Terminator) -> BasicBlock {
|
pub fn terminate_block(self, terminator: Terminator) -> BasicBlock {
|
||||||
BasicBlock {
|
BasicBlock {
|
||||||
label: self.label,
|
label: self.label,
|
||||||
statements: self.statements,
|
statements: self.statements,
|
||||||
terminator,
|
terminator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
start: Label,
|
start: Label,
|
||||||
end: Label,
|
end: Label,
|
||||||
|
|
||||||
defers: Vec<Call>,
|
defers: Vec<Call>,
|
||||||
locals: Vec<(VirtualReg, Variable)>,
|
locals: Vec<(VirtualReg, Variable)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
pub fn new(start: Label, end: Label) -> Self {
|
pub fn new(start: Label, end: Label) -> Self {
|
||||||
Self { start, end, defers: vec![], locals: vec![] }
|
Self {
|
||||||
}
|
start,
|
||||||
|
end,
|
||||||
|
defers: vec![],
|
||||||
|
locals: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FunctionContext {
|
struct FunctionContext {
|
||||||
entrypoint: Label,
|
entrypoint: Label,
|
||||||
scopes: Vec<Scope>,
|
scopes: Vec<Scope>,
|
||||||
blocks: Vec<BasicBlock>,
|
blocks: Vec<BasicBlock>,
|
||||||
|
|
||||||
builder: BasicBlockBuilder,
|
builder: BasicBlockBuilder,
|
||||||
|
|
||||||
v_register_idx: usize,
|
v_register_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionContext {
|
impl FunctionContext {
|
||||||
pub fn new(entrypoint: Label) -> Self {
|
pub fn new(entrypoint: Label) -> Self {
|
||||||
Self {
|
Self {
|
||||||
entrypoint: entrypoint.clone(),
|
entrypoint: entrypoint.clone(),
|
||||||
blocks: Vec::new(),
|
blocks: Vec::new(),
|
||||||
scopes: vec![Scope::new(entrypoint.clone(), Label::new("unreachable"))],
|
scopes: vec![Scope::new(entrypoint.clone(), Label::new("unreachable"))],
|
||||||
builder: BasicBlockBuilder::new(entrypoint),
|
builder: BasicBlockBuilder::new(entrypoint),
|
||||||
v_register_idx: 0,
|
v_register_idx: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_vreg(&mut self) -> VirtualReg {
|
pub fn new_vreg(&mut self) -> VirtualReg {
|
||||||
self.v_register_idx += 1;
|
self.v_register_idx += 1;
|
||||||
VirtualReg(self.v_register_idx as isize - 1)
|
VirtualReg(self.v_register_idx as isize - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn defer(&mut self, call: Call) {
|
pub fn defer(&mut self, call: Call) {
|
||||||
self.scopes.last_mut().unwrap().defers.push(call);
|
self.scopes.last_mut().unwrap().defers.push(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_local(&mut self, variable: Variable) -> VirtualReg {
|
pub fn new_local(&mut self, v_reg: VirtualReg, variable: Variable) {
|
||||||
let vreg = self.new_vreg();
|
self.scopes
|
||||||
self.scopes.last_mut().unwrap().locals.push((vreg, variable));
|
.last_mut()
|
||||||
vreg
|
.unwrap()
|
||||||
}
|
.locals
|
||||||
|
.push((v_reg, variable));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn enter_scope(&mut self, scope: Scope) {
|
pub fn enter_scope(&mut self, scope: Scope) {
|
||||||
self.scopes.push(scope);
|
self.scopes.push(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_scope(&mut self) -> Option<Scope> {
|
pub fn current_scope(&mut self) -> Option<Scope> {
|
||||||
self.scopes.pop()
|
self.scopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_deferred_calls(&self) -> Vec<Call> {
|
pub fn get_deferred_calls(&self) -> Vec<Call> {
|
||||||
self.scopes
|
self.scopes
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.flat_map(|s|
|
.flat_map(|s| s.defers.iter().cloned().rev())
|
||||||
s.defers
|
.collect()
|
||||||
.iter()
|
}
|
||||||
.cloned()
|
|
||||||
.rev()
|
|
||||||
).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_local(&self, var: &Variable) -> Option<VirtualReg> {
|
pub fn find_local(&self, var: &String) -> Option<VirtualReg> {
|
||||||
for scope in self.scopes.iter().rev() {
|
for scope in self.scopes.iter().rev() {
|
||||||
for local in &scope.locals {
|
for local in &scope.locals {
|
||||||
if local.1 == *var {
|
if local.1.name == *var {
|
||||||
return Some(local.0)
|
return Some(local.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exit_scope(&mut self) {
|
pub fn exit_scope(&mut self) {
|
||||||
self.scopes.pop().expect("(BUG) Cannot exit outermost scope");
|
self.scopes
|
||||||
}
|
.pop()
|
||||||
|
.expect("(BUG) Cannot exit outermost scope");
|
||||||
|
}
|
||||||
|
|
||||||
pub fn next_block(&mut self, terminator: Terminator, label: Label) {
|
pub fn next_block(&mut self, terminator: Terminator, label: Label) {
|
||||||
self.blocks.push(self.builder.next_block(terminator, label));
|
self.blocks.push(self.builder.next_block(terminator, label));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_stmt(&mut self, stmt: IR::Statement) {
|
pub fn push_stmt(&mut self, stmt: IR::Statement) {
|
||||||
self.builder.push(stmt);
|
self.builder.push(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminate(mut self, terminator: Terminator) -> Vec<IR::BasicBlock> {
|
pub fn terminate(mut self, terminator: Terminator) -> Vec<IR::BasicBlock> {
|
||||||
self.blocks.push(self.builder.terminate_block(terminator));
|
self.blocks.push(self.builder.terminate_block(terminator));
|
||||||
self.blocks
|
self.blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take(&mut self) -> Vec<IR::BasicBlock> {
|
pub fn take(&mut self) -> Vec<IR::BasicBlock> {
|
||||||
std::mem::take(&mut self.blocks)
|
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;
|
use dsa_tasks::ProcessAgent;
|
||||||
|
|
||||||
mod codegen;
|
mod codegen;
|
||||||
mod instruction;
|
mod instruction;
|
||||||
|
pub mod ir_lowering_codegen;
|
||||||
mod registers;
|
mod registers;
|
||||||
mod scope;
|
mod scope;
|
||||||
pub mod intermediate_rep;
|
|
||||||
pub mod ir_lowering_codegen;
|
|
||||||
|
|
||||||
pub fn generate_code(ast: &Program, ctx: &ProcessAgent) -> Result<String, CompilerError> {
|
pub fn generate_code(ast: &Program, ctx: &ProcessAgent) -> Result<String, CompilerError> {
|
||||||
let mut codegen = codegen::CodeGenerator::new(ast.clone());
|
let mut codegen_test = IrLoweringCodeGenerator::new(ast.clone());
|
||||||
codegen.generate(ctx)
|
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::CompilerError;
|
||||||
|
use crate::model::instruction::Instruction;
|
||||||
|
use crate::model::register::{Location, Register};
|
||||||
|
|
||||||
/// Register allocator for DSA assembly generation
|
/// Register allocator for DSA assembly generation
|
||||||
/// Manages general-purpose registers (rg0-rgf) and handles stack spilling
|
/// Manages general-purpose registers (rg0-rgf) and handles stack spilling
|
||||||
@@ -21,28 +23,6 @@ pub struct RegisterAllocator {
|
|||||||
in_use: Vec<(Register, bool)>,
|
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 {
|
impl RegisterAllocator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Initialize with available GP registers (rg0-rgf = 16 registers)
|
// Initialize with available GP registers (rg0-rgf = 16 registers)
|
||||||
@@ -427,119 +407,3 @@ impl RegisterAllocator {
|
|||||||
.collect()
|
.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 std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
use crate::backend::instruction::Instruction;
|
use crate::error;
|
||||||
use crate::backend::registers::Register;
|
use crate::model::CompilerError;
|
||||||
use crate::{
|
use crate::model::ast::{Name, TypeId};
|
||||||
error,
|
use crate::model::instruction::Instruction;
|
||||||
model::{CompilerError, Name, TypeId},
|
use crate::model::register::Register;
|
||||||
};
|
|
||||||
|
|
||||||
/// scope object
|
/// scope object
|
||||||
pub struct Scope<'a> {
|
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::iter::Peekable;
|
||||||
use std::str::Chars;
|
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> {
|
pub struct Lexer<'a> {
|
||||||
chars: Peekable<Chars<'a>>,
|
chars: Peekable<Chars<'a>>,
|
||||||
current: Option<char>,
|
current: Option<char>,
|
||||||
@@ -407,12 +238,8 @@ impl<'a> Lexer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
value as i32
|
value as i32
|
||||||
};
|
};
|
||||||
if signed_val < i8::MIN as i32 || signed_val > i8::MAX 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));
|
||||||
self.error(&format!(
|
|
||||||
"Value {} out of range for i8",
|
|
||||||
signed_val
|
|
||||||
));
|
|
||||||
return Token::SignedInt(0, Some(TypeId::I8));
|
return Token::SignedInt(0, Some(TypeId::I8));
|
||||||
}
|
}
|
||||||
Token::SignedInt(signed_val, Some(TypeId::I8))
|
Token::SignedInt(signed_val, Some(TypeId::I8))
|
||||||
@@ -423,13 +250,8 @@ impl<'a> Lexer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
value as i32
|
value as i32
|
||||||
};
|
};
|
||||||
if signed_val < i16::MIN as i32
|
if signed_val < i16::MIN as i32 || signed_val > i16::MAX as i32 {
|
||||||
|| signed_val > i16::MAX as i32
|
self.error(&format!("Value {} out of range for i16", signed_val));
|
||||||
{
|
|
||||||
self.error(&format!(
|
|
||||||
"Value {} out of range for i16",
|
|
||||||
signed_val
|
|
||||||
));
|
|
||||||
return Token::SignedInt(0, Some(TypeId::I16));
|
return Token::SignedInt(0, Some(TypeId::I16));
|
||||||
}
|
}
|
||||||
Token::SignedInt(signed_val, Some(TypeId::I16))
|
Token::SignedInt(signed_val, Some(TypeId::I16))
|
||||||
@@ -437,19 +259,13 @@ impl<'a> Lexer<'a> {
|
|||||||
TypeId::I32 => {
|
TypeId::I32 => {
|
||||||
let signed_val = if is_negative {
|
let signed_val = if is_negative {
|
||||||
if value > i32::MAX as u64 + 1 {
|
if value > i32::MAX as u64 + 1 {
|
||||||
self.error(&format!(
|
self.error(&format!("Value -{} out of range for i32", value));
|
||||||
"Value -{} out of range for i32",
|
|
||||||
value
|
|
||||||
));
|
|
||||||
return Token::SignedInt(0, Some(TypeId::I32));
|
return Token::SignedInt(0, Some(TypeId::I32));
|
||||||
}
|
}
|
||||||
-(value as i32)
|
-(value as i32)
|
||||||
} else {
|
} else {
|
||||||
if value > i32::MAX as u64 {
|
if value > i32::MAX as u64 {
|
||||||
self.error(&format!(
|
self.error(&format!("Value {} out of range for i32", value));
|
||||||
"Value {} out of range for i32",
|
|
||||||
value
|
|
||||||
));
|
|
||||||
return Token::SignedInt(0, Some(TypeId::I32));
|
return Token::SignedInt(0, Some(TypeId::I32));
|
||||||
}
|
}
|
||||||
value as i32
|
value as i32
|
||||||
@@ -462,10 +278,7 @@ impl<'a> Lexer<'a> {
|
|||||||
return Token::UnsignedInt(0, Some(TypeId::U8));
|
return Token::UnsignedInt(0, Some(TypeId::U8));
|
||||||
}
|
}
|
||||||
if value > u8::MAX as u64 {
|
if value > u8::MAX as u64 {
|
||||||
self.error(&format!(
|
self.error(&format!("Value {} out of range for u8", value));
|
||||||
"Value {} out of range for u8",
|
|
||||||
value
|
|
||||||
));
|
|
||||||
return Token::UnsignedInt(0, Some(TypeId::U8));
|
return Token::UnsignedInt(0, Some(TypeId::U8));
|
||||||
}
|
}
|
||||||
Token::UnsignedInt(value as u32, 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));
|
return Token::UnsignedInt(0, Some(TypeId::U16));
|
||||||
}
|
}
|
||||||
if value > u16::MAX as u64 {
|
if value > u16::MAX as u64 {
|
||||||
self.error(&format!(
|
self.error(&format!("Value {} out of range for u16", value));
|
||||||
"Value {} out of range for u16",
|
|
||||||
value
|
|
||||||
));
|
|
||||||
return Token::UnsignedInt(0, Some(TypeId::U16));
|
return Token::UnsignedInt(0, Some(TypeId::U16));
|
||||||
}
|
}
|
||||||
Token::UnsignedInt(value as u32, 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));
|
return Token::UnsignedInt(0, Some(TypeId::U32));
|
||||||
}
|
}
|
||||||
if value > u32::MAX as u64 {
|
if value > u32::MAX as u64 {
|
||||||
self.error(&format!(
|
self.error(&format!("Value {} out of range for u32", value));
|
||||||
"Value {} out of range for u32",
|
|
||||||
value
|
|
||||||
));
|
|
||||||
return Token::UnsignedInt(0, Some(TypeId::U32));
|
return Token::UnsignedInt(0, Some(TypeId::U32));
|
||||||
}
|
}
|
||||||
Token::UnsignedInt(value as u32, Some(TypeId::U32))
|
Token::UnsignedInt(value as u32, Some(TypeId::U32))
|
||||||
@@ -592,8 +399,7 @@ impl<'a> Lexer<'a> {
|
|||||||
if c.is_ascii_digit() {
|
if c.is_ascii_digit() {
|
||||||
self.advance();
|
self.advance();
|
||||||
num_str.push(c);
|
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
|
// Allow underscores as separators only between digits
|
||||||
self.advance();
|
self.advance();
|
||||||
} else {
|
} else {
|
||||||
@@ -662,8 +468,7 @@ impl<'a> Lexer<'a> {
|
|||||||
return Err("Invalid binary number: no digits after 0b".to_string());
|
return Err("Invalid binary number: no digits after 0b".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
u64::from_str_radix(&num_str, 2)
|
u64::from_str_radix(&num_str, 2).map_err(|_| format!("Invalid binary number: {}", num_str))
|
||||||
.map_err(|_| format!("Invalid binary number: {}", num_str))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
@@ -713,10 +518,7 @@ impl<'a> Lexer<'a> {
|
|||||||
'\'' => '\'',
|
'\'' => '\'',
|
||||||
'0' => '\0',
|
'0' => '\0',
|
||||||
_ => {
|
_ => {
|
||||||
return Err(format!(
|
return Err(format!("Invalid escape sequence: \\{}", escaped));
|
||||||
"Invalid escape sequence: \\{}",
|
|
||||||
escaped
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
s.push(escaped_char);
|
s.push(escaped_char);
|
||||||
@@ -767,9 +569,7 @@ impl<'a> Lexer<'a> {
|
|||||||
Some('0') => '\0',
|
Some('0') => '\0',
|
||||||
Some(c) => return Err(format!("Invalid escape sequence: \\{}", c)),
|
Some(c) => return Err(format!("Invalid escape sequence: \\{}", c)),
|
||||||
None => {
|
None => {
|
||||||
return Err(
|
return Err("Unexpected end after escape in char literal".to_string());
|
||||||
"Unexpected end after escape in char literal".to_string()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -782,9 +582,7 @@ impl<'a> Lexer<'a> {
|
|||||||
self.advance(); // Move to closing quote
|
self.advance(); // Move to closing quote
|
||||||
|
|
||||||
if self.current != Some('\'') {
|
if self.current != Some('\'') {
|
||||||
return Err(
|
return Err("Character literal must contain exactly one character".to_string());
|
||||||
"Character literal must contain exactly one character".to_string()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ch)
|
Ok(ch)
|
||||||
@@ -998,7 +796,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_operators() {
|
fn test_operators() {
|
||||||
let input = "+ ++ += - -- -= * *= / /= % %= & &= && | |= || ^ ^= ! != = == < <= << <<= > >= >> >>=";
|
let input =
|
||||||
|
"+ ++ += - -- -= * *= / /= % %= & &= && | |= || ^ ^= ! != = == < <= << <<= > >= >> >>=";
|
||||||
let mut lexer = Lexer::new(input);
|
let mut lexer = Lexer::new(input);
|
||||||
|
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::frontend::parser::{ParseResult, Parser};
|
use crate::frontend::parser::{ParseResult, Parser};
|
||||||
use crate::model::{CompilerError, Program};
|
use crate::model::CompilerError;
|
||||||
|
use crate::model::ast::Program;
|
||||||
use dsa_tasks::ProcessAgent;
|
use dsa_tasks::ProcessAgent;
|
||||||
|
|
||||||
// mod c;
|
// mod c;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use super::lexer::Token;
|
use crate::model::CompilerError;
|
||||||
use crate::model::{
|
use crate::model::ast::{
|
||||||
AssignmentOperator, BinaryOperator, Block, Call, CompilerError, ConstExpr, Declaration,
|
AssignmentOperator, BinaryOperator, Block, Call, ConstExpr, Declaration, Dependency,
|
||||||
Dependency, Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
|
||||||
};
|
};
|
||||||
|
use crate::model::tokens::Token;
|
||||||
use crate::{expect_tt, expect_value};
|
use crate::{expect_tt, expect_value};
|
||||||
use std::ops::{ControlFlow, FromResidual, Residual, Try};
|
use std::ops::{ControlFlow, FromResidual, Residual, Try};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![feature(try_trait_v2)]
|
#![feature(try_trait_v2)]
|
||||||
#![feature(try_trait_v2_residual)]
|
#![feature(try_trait_v2_residual)]
|
||||||
|
|
||||||
use crate::model::CompilerError;
|
|
||||||
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
||||||
|
use model::CompilerError;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
|
|||||||
@@ -1,38 +1,12 @@
|
|||||||
use core::fmt;
|
use crate::model::CompilerError;
|
||||||
|
use std::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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Name {
|
pub struct Name {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub namespace: Option<String>,
|
pub namespace: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Name {
|
impl Name {
|
||||||
pub fn new(name: impl Into<String>, namespace: Option<String>) -> Self {
|
pub fn new(name: impl Into<String>, namespace: Option<String>) -> Self {
|
||||||
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