work
This commit is contained in:
@@ -9,5 +9,6 @@ pub trait Arch: Sized {
|
|||||||
const NAME: &str;
|
const NAME: &str;
|
||||||
type Asm;
|
type Asm;
|
||||||
type Addr: Addr;
|
type Addr: Addr;
|
||||||
|
type CallConv;
|
||||||
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg>;
|
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg>;
|
||||||
}
|
}
|
||||||
|
|||||||
+188
-8
@@ -1,14 +1,28 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backend::{LibImport, LinkedProgram, SymImport, SymTable, Symbol};
|
use crate::backend::{Body, LibImport, LinkedProgram, SymImport, SymTable, Symbol, VarId};
|
||||||
use util::*;
|
|
||||||
|
|
||||||
pub struct Encoder<'a> {
|
pub struct Encoder<'a> {
|
||||||
pub code: Code,
|
pub code: Code,
|
||||||
pub sym_tab: SymTable<u64>,
|
pub sym_tab: SymTable<u64>,
|
||||||
pub sym_refs: HashMap<Symbol, Vec<usize>>,
|
pub sym_refs: HashMap<Symbol, Vec<usize>>,
|
||||||
pub program: &'a Program<X86_64>,
|
pub program: &'a Program<X86_64>,
|
||||||
|
pub vars: VarMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct VarUse {
|
||||||
|
pos: usize,
|
||||||
|
reg: Option<Reg>,
|
||||||
|
}
|
||||||
|
pub type VarUses = HashMap<VarId, VecDeque<VarUse>>;
|
||||||
|
pub type RegUses = [VecDeque<usize>; 16];
|
||||||
|
|
||||||
|
impl VarUse {
|
||||||
|
pub fn min(self, other: Self) -> Self {
|
||||||
|
if self.pos <= other.pos { self } else { other }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
|
pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
|
||||||
@@ -19,7 +33,14 @@ pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
|
|||||||
for f in &p.funcs {
|
for f in &p.funcs {
|
||||||
let addr = encoder.code.bytes.len();
|
let addr = encoder.code.bytes.len();
|
||||||
encoder.sym_tab.insert(f.sym, addr as u64);
|
encoder.sym_tab.insert(f.sym, addr as u64);
|
||||||
for instr in &f.instrs {
|
let mut segments = Vec::new();
|
||||||
|
calc_uses(&mut segments, &f.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
for f in &p.funcs {
|
||||||
|
let addr = encoder.code.bytes.len();
|
||||||
|
encoder.sym_tab.insert(f.sym, addr as u64);
|
||||||
|
for instr in &f.body {
|
||||||
encoder.compile_instr(instr)?;
|
encoder.compile_instr(instr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,24 +81,183 @@ pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SegUses {
|
||||||
|
var: VarUses,
|
||||||
|
reg: RegUses,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calc_uses(segments: &mut Vec<SegUses>, body: &Body<X86_64>) -> (usize, usize) {
|
||||||
|
let mut pos = 0;
|
||||||
|
let seg_i = segments.len();
|
||||||
|
segments.push(Default::default());
|
||||||
|
let mut vars = VarUses::default();
|
||||||
|
let mut regs = RegUses::default();
|
||||||
|
let volatile = [
|
||||||
|
rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15,
|
||||||
|
]
|
||||||
|
.map(|r| r.reg());
|
||||||
|
macro_rules! push {
|
||||||
|
($var:ident) => {
|
||||||
|
push!($var, VarUse { pos: 0, reg: None })
|
||||||
|
};
|
||||||
|
($var:ident, $pos:expr) => {
|
||||||
|
vars.entry($var.clone()).or_default().push_back(VarUse {
|
||||||
|
pos: $pos.pos + pos,
|
||||||
|
reg: $pos.reg,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for instr in body {
|
||||||
|
match instr {
|
||||||
|
BInstr::Set { dst, src: _ } => push!(dst),
|
||||||
|
BInstr::Call { dst, f, args } => {
|
||||||
|
push!(dst);
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
push!(arg);
|
||||||
|
if i < 8 {
|
||||||
|
regs[i].push_back(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BInstr::Copy { dst, src } => {
|
||||||
|
push!(dst);
|
||||||
|
push!(src);
|
||||||
|
}
|
||||||
|
BInstr::Add { dst, src1, src2 } => {
|
||||||
|
push!(dst);
|
||||||
|
push!(src1);
|
||||||
|
push!(src2);
|
||||||
|
}
|
||||||
|
BInstr::If { cond, then, else_ } => {
|
||||||
|
push!(cond);
|
||||||
|
pos += 1;
|
||||||
|
let (seg_i1, len1) = calc_uses(segments, then);
|
||||||
|
let (seg_i2, len2) = calc_uses(segments, else_);
|
||||||
|
// insert closest usages
|
||||||
|
for (var, poss1) in &segments[seg_i1].var {
|
||||||
|
if let Some(poss2) = segments[seg_i2].var.get(var) {
|
||||||
|
push!(var, poss1[0].min(poss2[0]));
|
||||||
|
} else {
|
||||||
|
push!(var, poss1[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var, poss2) in &segments[seg_i2].var {
|
||||||
|
if !segments[seg_i1].var.contains_key(var) {
|
||||||
|
push!(var, poss2[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += len1.max(len2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
BInstr::Loop(instrs) => {
|
||||||
|
let (seg_i2, len) = calc_uses(segments, instrs);
|
||||||
|
// insert closest usages
|
||||||
|
for (var, poss) in &segments[seg_i2].var {
|
||||||
|
push!(var, poss[0]);
|
||||||
|
}
|
||||||
|
// during register allocation, want to insert the first use of each var
|
||||||
|
// at len + use, because if it jumps back up, that will be the next
|
||||||
|
// usage rather than if the loop exits; pad ensures there is room so
|
||||||
|
// the insertions are always before the rest of the usages (after loop)
|
||||||
|
let pad = (len * 2).saturating_sub(pos);
|
||||||
|
pos += len + pad;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
BInstr::Break(var) => {
|
||||||
|
if let Some(var) = var {
|
||||||
|
push!(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BInstr::Return(var) => {
|
||||||
|
if let Some(var) = var {
|
||||||
|
push!(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BInstr::Asm(asm) => {
|
||||||
|
for &(reg, var) in &asm.args {
|
||||||
|
push!(
|
||||||
|
var,
|
||||||
|
VarUse {
|
||||||
|
pos: 0,
|
||||||
|
reg: Some(reg)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
regs[reg as usize].push_back(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
segments[seg_i].var = vars;
|
||||||
|
segments[seg_i].reg = regs;
|
||||||
|
return (seg_i, pos);
|
||||||
|
}
|
||||||
|
|
||||||
type BInstr = crate::backend::Instr<X86_64>;
|
type BInstr = crate::backend::Instr<X86_64>;
|
||||||
impl<'a> Encoder<'a> {
|
impl<'a> Encoder<'a> {
|
||||||
fn compile_instr(&mut self, instr: &BInstr) -> Result<(), CompilerMsg> {
|
fn compile_instr(&mut self, instr: &BInstr) -> EncodeRes {
|
||||||
match instr {
|
match instr {
|
||||||
BInstr::Asm(asm) => {
|
BInstr::Asm(asm) => self.asm(asm)?,
|
||||||
self.code.extend(asm);
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn asm(&mut self, asm: &Asm) -> EncodeRes {
|
||||||
|
let mut used = HashSet::default();
|
||||||
|
let mut vars = HashSet::default();
|
||||||
|
for &instr in &asm.instrs {
|
||||||
|
if let Some(regw) = instr.regw_set() {
|
||||||
|
used.insert(regw.reg());
|
||||||
|
}
|
||||||
|
for var in instr.vars() {
|
||||||
|
vars.insert(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let overlap = used.intersection(&self.vars.active.values());
|
||||||
|
for var in &vars {
|
||||||
|
if let Some(reg) = self.vars.active.get(var)
|
||||||
|
&& used.contains(reg)
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
for &instr in &asm.instrs {
|
||||||
|
match instr {
|
||||||
|
Instr::Mov { dst, src } => self.code.mov(dst, src)?,
|
||||||
|
Instr::Push(rvmi) => todo!(),
|
||||||
|
Instr::Pop(rvm) => todo!(),
|
||||||
|
Instr::Lea { dst, src } => todo!(),
|
||||||
|
Instr::Int(code) => self.code.int(code),
|
||||||
|
Instr::Syscall => self.code.syscall(),
|
||||||
|
Instr::Call(sym) => self.code.call(sym),
|
||||||
|
Instr::CallAt(sym) => self.code.call_mem(sym),
|
||||||
|
Instr::Ret => self.code.ret(),
|
||||||
|
Instr::Add { dst, src } => todo!(),
|
||||||
|
Instr::Sub { dst, src } => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(program: &'a Program<X86_64>) -> Self {
|
pub fn new(program: &'a Program<X86_64>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
code: Code::default(),
|
code: Code::default(),
|
||||||
sym_tab: SymTable::new(program.sym_count()),
|
sym_tab: SymTable::new(program.sym_count()),
|
||||||
sym_refs: Default::default(),
|
sym_refs: Default::default(),
|
||||||
|
vars: VarMap::default(),
|
||||||
program,
|
program,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct VarMap {
|
||||||
|
active: HashMap<VarId, Reg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VarMap {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
active: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::backend::Symbol;
|
use crate::backend::Symbol;
|
||||||
|
|
||||||
type ERes = Result<(), CompilerMsg>;
|
pub type EncodeRes = Result<(), CompilerMsg>;
|
||||||
|
|
||||||
/// machine code
|
/// machine code
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -11,7 +11,7 @@ pub struct Code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Code {
|
impl Code {
|
||||||
pub fn mov(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
|
pub fn mov(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes {
|
||||||
let src = src.into();
|
let src = src.into();
|
||||||
match dst.kind() {
|
match dst.kind() {
|
||||||
RegMemKind::Reg(mut dst) => match src {
|
RegMemKind::Reg(mut dst) => match src {
|
||||||
@@ -92,7 +92,7 @@ impl Code {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, reg: impl Into<RegMemImm>) -> ERes {
|
pub fn push(&mut self, reg: impl Into<RegMemImm>) -> EncodeRes {
|
||||||
match reg.into() {
|
match reg.into() {
|
||||||
RegMemImm::Reg(reg) => match reg.width() {
|
RegMemImm::Reg(reg) => match reg.width() {
|
||||||
Width::B64 => {
|
Width::B64 => {
|
||||||
@@ -120,7 +120,7 @@ impl Code {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self, reg: Reg) -> ERes {
|
pub fn pop(&mut self, reg: RegW) -> EncodeRes {
|
||||||
match reg.width() {
|
match reg.width() {
|
||||||
Width::B64 | Width::B16 => (),
|
Width::B64 | Width::B16 => (),
|
||||||
_ => return Err("register must be 64 or 16 bit".into()),
|
_ => return Err("register must be 64 or 16 bit".into()),
|
||||||
@@ -133,7 +133,7 @@ impl Code {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lea(&mut self, dst: Reg, sym: Symbol) -> ERes {
|
pub fn lea(&mut self, dst: RegW, sym: Symbol) -> EncodeRes {
|
||||||
self.rex(1, dst, 0, 0)?;
|
self.rex(1, dst, 0, 0)?;
|
||||||
self.bytes.push(0x8d);
|
self.bytes.push(0x8d);
|
||||||
self.modrm(dst, sym);
|
self.modrm(dst, sym);
|
||||||
@@ -162,7 +162,7 @@ impl Code {
|
|||||||
self.bytes.push(0xc3);
|
self.bytes.push(0xc3);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>, ext: u8) -> ERes {
|
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>, ext: u8) -> EncodeRes {
|
||||||
match src.into() {
|
match src.into() {
|
||||||
RegMemImm::Reg(src) => {
|
RegMemImm::Reg(src) => {
|
||||||
if src.width() != dst.width() {
|
if src.width() != dst.width() {
|
||||||
@@ -219,11 +219,11 @@ impl Code {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
|
pub fn add(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes {
|
||||||
self.add_sub(dst, src, 0)
|
self.add_sub(dst, src, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
|
pub fn sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes {
|
||||||
self.add_sub(dst, src, 5)
|
self.add_sub(dst, src, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ impl Code {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> ERes {
|
fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> EncodeRes {
|
||||||
if r.req() && b.req_no() || r.req_no() && b.req() {
|
if r.req() && b.req_no() || r.req_no() && b.req() {
|
||||||
return Err("registers incompatible (REX)".into());
|
return Err("registers incompatible (REX)".into());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,15 @@ pub struct X86_64;
|
|||||||
|
|
||||||
impl Arch for X86_64 {
|
impl Arch for X86_64 {
|
||||||
const NAME: &str = "x86_64";
|
const NAME: &str = "x86_64";
|
||||||
type Asm = Code;
|
type Asm = Asm;
|
||||||
type Addr = u64;
|
type Addr = u64;
|
||||||
|
type CallConv = CallConv;
|
||||||
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg> {
|
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg> {
|
||||||
compile(p)
|
compile(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CallConv {
|
||||||
|
volatile: Vec<Reg>,
|
||||||
|
nonvolatile: Vec<Reg>,
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ pub fn imms() -> impl Iterator<Item = i128> {
|
|||||||
IMMS.iter().cloned()
|
IMMS.iter().cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn regs() -> impl Iterator<Item = Reg> {
|
pub fn regs() -> impl Iterator<Item = RegW> {
|
||||||
Reg::IMPORTANT.iter().cloned()
|
RegW::IMPORTANT.iter().cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mems() -> impl Iterator<Item = Mem> {
|
pub fn mems() -> impl Iterator<Item = Mem> {
|
||||||
gen move {
|
gen move {
|
||||||
for ® in Reg::IMPORTANT {
|
for ® in RegW::IMPORTANT {
|
||||||
for &disp in DISPS {
|
for &disp in DISPS {
|
||||||
for &width in WIDTHS {
|
for &width in WIDTHS {
|
||||||
yield mem(reg, disp, width);
|
yield mem(reg, disp, width);
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ pub trait RegMem: RexBit + RexW + ModRMRM + Copy + MaybeMem {
|
|||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum RegMemKind {
|
pub enum RegMemKind {
|
||||||
Reg(Reg),
|
Reg(RegW),
|
||||||
Mem(Mem),
|
Mem(Mem),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum RegMemImm {
|
pub enum RegMemImm {
|
||||||
Reg(Reg),
|
Reg(RegW),
|
||||||
Imm(Imm),
|
Imm(Imm),
|
||||||
Mem(Mem),
|
Mem(Mem),
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ pub trait MaybeMem {
|
|||||||
fn mem(&self) -> Option<Mem>;
|
fn mem(&self) -> Option<Mem>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegMem for Reg {
|
impl RegMem for RegW {
|
||||||
fn width(&self) -> Width {
|
fn width(&self) -> Width {
|
||||||
self.width()
|
self.width()
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ impl RegMem for Reg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaybeMem for Reg {
|
impl MaybeMem for RegW {
|
||||||
fn mem(&self) -> Option<Mem> {
|
fn mem(&self) -> Option<Mem> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -54,14 +54,14 @@ impl MaybeMem for Mem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fromrot
|
// fromrot
|
||||||
impl From<Reg> for RegMemImm {
|
impl From<RegW> for RegMemImm {
|
||||||
fn from(value: Reg) -> Self {
|
fn from(value: RegW) -> Self {
|
||||||
Self::Reg(value)
|
Self::Reg(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Reg> for RegMemKind {
|
impl From<RegW> for RegMemKind {
|
||||||
fn from(value: Reg) -> Self {
|
fn from(value: RegW) -> Self {
|
||||||
Self::Reg(value)
|
Self::Reg(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ pub enum EffAddr {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModRMRM for Reg {
|
impl ModRMRM for RegW {
|
||||||
fn rm(&self) -> u8 {
|
fn rm(&self) -> u8 {
|
||||||
self.base()
|
self.base()
|
||||||
}
|
}
|
||||||
@@ -171,7 +171,7 @@ impl ModRMReg for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModRMReg for Reg {
|
impl ModRMReg for RegW {
|
||||||
fn val(&self) -> u8 {
|
fn val(&self) -> u8 {
|
||||||
self.base()
|
self.base()
|
||||||
}
|
}
|
||||||
@@ -207,7 +207,7 @@ impl RexBit for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RexBit for Reg {
|
impl RexBit for RegW {
|
||||||
fn rex(&self) -> bool {
|
fn rex(&self) -> bool {
|
||||||
self.gt8()
|
self.gt8()
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ impl RexW for Width {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RexW for Reg {
|
impl RexW for RegW {
|
||||||
fn rexw(&self) -> bool {
|
fn rexw(&self) -> bool {
|
||||||
self.width().rexw()
|
self.width().rexw()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::backend::{Symbol, VarId};
|
||||||
|
|
||||||
|
pub struct Asm {
|
||||||
|
pub args: Vec<(Reg, VarId)>,
|
||||||
|
pub instrs: Vec<Instr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Instr {
|
||||||
|
Mov { dst: Rvm, src: Rvmi },
|
||||||
|
Push(Rvmi),
|
||||||
|
Pop(Rvm),
|
||||||
|
Lea { dst: Rv, src: Symbol },
|
||||||
|
Int(u8),
|
||||||
|
Syscall,
|
||||||
|
Call(Symbol),
|
||||||
|
CallAt(Symbol),
|
||||||
|
Ret,
|
||||||
|
Add { dst: Rvm, src: Rvmi },
|
||||||
|
Sub { dst: Rvm, src: Rvmi },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instr {
|
||||||
|
pub fn regw_set(&self) -> Option<RegW> {
|
||||||
|
match self {
|
||||||
|
Instr::Mov { dst, src: _ } => dst.reg(),
|
||||||
|
Instr::Push(_) => None,
|
||||||
|
Instr::Pop(dst) => dst.reg(),
|
||||||
|
Instr::Lea { dst, src: _ } => dst.reg(),
|
||||||
|
Instr::Int(_) => None,
|
||||||
|
Instr::Syscall => None,
|
||||||
|
Instr::Call(_) => None,
|
||||||
|
Instr::CallAt(_) => None,
|
||||||
|
Instr::Ret => None,
|
||||||
|
Instr::Add { dst, src: _ } => dst.reg(),
|
||||||
|
Instr::Sub { dst, src: _ } => dst.reg(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn vars(&self) -> impl Iterator<Item = VarId> {
|
||||||
|
let mut vars = (None, None);
|
||||||
|
match self {
|
||||||
|
Instr::Mov { dst, src } => vars = (dst.var(), src.var()),
|
||||||
|
Instr::Push(src) => vars.0 = src.var(),
|
||||||
|
Instr::Pop(dst) => vars.0 = dst.var(),
|
||||||
|
Instr::Lea { dst, .. } => vars.0 = dst.var(),
|
||||||
|
Instr::Int(_) => (),
|
||||||
|
Instr::Syscall => (),
|
||||||
|
Instr::Call(_) => (),
|
||||||
|
Instr::CallAt(_) => (),
|
||||||
|
Instr::Ret => (),
|
||||||
|
Instr::Add { dst, src } => vars = (dst.var(), src.var()),
|
||||||
|
Instr::Sub { dst, src } => vars = (dst.var(), src.var()),
|
||||||
|
}
|
||||||
|
vars.0.into_iter().chain(vars.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Rv {
|
||||||
|
Reg(RegW),
|
||||||
|
Var(VarId),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Rvm {
|
||||||
|
Reg(RegW),
|
||||||
|
Var(VarId),
|
||||||
|
Mem(Mem),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Rvmi {
|
||||||
|
Reg(RegW),
|
||||||
|
Var(VarId),
|
||||||
|
Mem(Mem),
|
||||||
|
Imm(Imm),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rv {
|
||||||
|
pub fn reg(&self) -> Option<RegW> {
|
||||||
|
if let &Self::Reg(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn var(&self) -> Option<VarId> {
|
||||||
|
if let &Self::Var(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rvm {
|
||||||
|
pub fn reg(&self) -> Option<RegW> {
|
||||||
|
if let &Self::Reg(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn var(&self) -> Option<VarId> {
|
||||||
|
if let &Self::Var(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rvmi {
|
||||||
|
pub fn reg(&self) -> Option<RegW> {
|
||||||
|
if let &Self::Reg(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn var(&self) -> Option<VarId> {
|
||||||
|
if let &Self::Var(v) = self {
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,12 @@ use super::*;
|
|||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Mem {
|
pub struct Mem {
|
||||||
pub reg: Reg,
|
pub reg: RegW,
|
||||||
pub disp: i32,
|
pub disp: i32,
|
||||||
pub width: Width,
|
pub width: Width,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mem(reg: Reg, disp: i32, width: Width) -> Mem {
|
pub fn mem(reg: RegW, disp: i32, width: Width) -> Mem {
|
||||||
Mem { reg, disp, width }
|
Mem { reg, disp, width }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
mod arg;
|
mod arg;
|
||||||
|
mod asm;
|
||||||
mod imm;
|
mod imm;
|
||||||
mod mem;
|
mod mem;
|
||||||
mod reg;
|
mod reg;
|
||||||
mod width;
|
mod width;
|
||||||
|
|
||||||
pub use arg::*;
|
pub use arg::*;
|
||||||
|
pub use asm::*;
|
||||||
pub use imm::*;
|
pub use imm::*;
|
||||||
pub use mem::*;
|
pub use mem::*;
|
||||||
pub use reg::*;
|
pub use reg::*;
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
use super::Width;
|
use super::Width;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub struct Reg {
|
pub struct RegW {
|
||||||
val: u8,
|
reg: Reg,
|
||||||
high: bool,
|
high: bool,
|
||||||
width: Width,
|
width: Width,
|
||||||
}
|
}
|
||||||
|
|
||||||
def_regs! {
|
pub type Reg = u8;
|
||||||
|
|
||||||
|
def_regs! { RegW;
|
||||||
0b0000 : rax eax ax al,
|
0b0000 : rax eax ax al,
|
||||||
0b0001 : rcx ecx cx cl !_,
|
0b0001 : rcx ecx cx cl !_,
|
||||||
0b0010 : rdx edx dx dl,
|
0b0010 : rdx edx dx dl,
|
||||||
@@ -28,16 +30,20 @@ def_regs! {
|
|||||||
0b1111 : r15 r15d r15w r15b,
|
0b1111 : r15 r15d r15w r15b,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reg {
|
impl RegW {
|
||||||
|
pub fn reg(&self) -> u8 {
|
||||||
|
self.reg
|
||||||
|
}
|
||||||
|
|
||||||
pub fn base(&self) -> u8 {
|
pub fn base(&self) -> u8 {
|
||||||
self.val & 0b111
|
self.reg & 0b111
|
||||||
}
|
}
|
||||||
/// checks if register is not one of the first 8 (0-7)
|
/// checks if register is not one of the first 8 (0-7)
|
||||||
pub fn gt8(&self) -> bool {
|
pub fn gt8(&self) -> bool {
|
||||||
self.val >= 0b1000
|
self.reg >= 0b1000
|
||||||
}
|
}
|
||||||
pub fn gt4(&self) -> bool {
|
pub fn gt4(&self) -> bool {
|
||||||
self.val >= 0b0100
|
self.reg >= 0b0100
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> Width {
|
pub fn width(&self) -> Width {
|
||||||
@@ -65,12 +71,12 @@ impl Reg {
|
|||||||
|| (self.gt4() && self.width == Width::B8 && !self.high)
|
|| (self.gt4() && self.width == Width::B8 && !self.high)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn incompatible(&self, other: &Reg) -> bool {
|
pub fn incompatible(&self, other: &RegW) -> bool {
|
||||||
(self.requires_rex() && other.high) || (self.high && other.requires_rex())
|
(self.requires_rex() && other.high) || (self.high && other.requires_rex())
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn new(val: u8, width: Width, high: bool) -> Self {
|
const fn new(reg: u8, width: Width, high: bool) -> Self {
|
||||||
Self { val, high, width }
|
Self { reg, high, width }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,30 +123,30 @@ macro_rules! filter {
|
|||||||
use filter;
|
use filter;
|
||||||
|
|
||||||
macro_rules! def_regs {
|
macro_rules! def_regs {
|
||||||
($($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $(norex=$B8H:ident)? $(!$imp:tt)?,)*) => {
|
($reg:ident; $($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $(norex=$B8H:ident)? $(!$imp:tt)?,)*) => {
|
||||||
$(
|
$(
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const $B64: Reg = Reg::new($val, Width::B64, false);
|
pub const $B64: $reg = $reg::new($val, Width::B64, false);
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const $B32: Reg = Reg::new($val, Width::B32, false);
|
pub const $B32: $reg = $reg::new($val, Width::B32, false);
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const $B16: Reg = Reg::new($val, Width::B16, false);
|
pub const $B16: $reg = $reg::new($val, Width::B16, false);
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const $B8 : Reg = Reg::new($val, Width::B8 , false);
|
pub const $B8 : $reg = $reg::new($val, Width::B8 , false);
|
||||||
$(
|
$(
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const $B8H: Reg = Reg::new($val, Width::B8, true);
|
pub const $B8H: $reg = $reg::new($val, Width::B8, true);
|
||||||
)?
|
)?
|
||||||
)*
|
)*
|
||||||
|
|
||||||
impl Reg {
|
impl $reg {
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// pub const ALL: &[Reg] = &[
|
// pub const ALL: &[$reg] = &[
|
||||||
// $( $B64, $B32, $B16, $B8, $($B8H,)? )*
|
// $( $B64, $B32, $B16, $B8, $($B8H,)? )*
|
||||||
// ];
|
// ];
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub const IMPORTANT: &[Reg] = &
|
pub const IMPORTANT: &[$reg] = &
|
||||||
filter!(; $($(!$imp)? $B64 $B32 $B16 $B8 $($B8H)?; )* )
|
filter!(; $($(!$imp)? $B64 $B32 $B16 $B8 $($B8H)?; )* )
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -159,7 +165,7 @@ macro_rules! def_regs {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for Reg {
|
impl std::fmt::Display for $reg {
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", match *self {
|
write!(f, "{}", match *self {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ pub enum Width {
|
|||||||
B64 = 3,
|
B64 = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Reg> for Width {
|
impl From<RegW> for Width {
|
||||||
fn from(value: Reg) -> Self {
|
fn from(value: RegW) -> Self {
|
||||||
value.width()
|
value.width()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
ids!(VarId FnId);
|
||||||
|
|
||||||
|
macro_rules! ids {
|
||||||
|
($($name:ident)*) => {
|
||||||
|
$(
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct $name(usize);
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
use ids;
|
||||||
+35
-8
@@ -1,6 +1,8 @@
|
|||||||
mod addr;
|
mod addr;
|
||||||
|
mod id;
|
||||||
mod symbol;
|
mod symbol;
|
||||||
pub use addr::*;
|
pub use addr::*;
|
||||||
|
pub use id::*;
|
||||||
pub use symbol::*;
|
pub use symbol::*;
|
||||||
|
|
||||||
use crate::{arch::Arch, backend::LinkedProgram, io::CompilerMsg};
|
use crate::{arch::Arch, backend::LinkedProgram, io::CompilerMsg};
|
||||||
@@ -10,6 +12,7 @@ pub struct Program<A: Arch> {
|
|||||||
pub funcs: Vec<Func<A>>,
|
pub funcs: Vec<Func<A>>,
|
||||||
pub entry: Option<Symbol>,
|
pub entry: Option<Symbol>,
|
||||||
pub external: Vec<External>,
|
pub external: Vec<External>,
|
||||||
|
pub call_convs: Vec<A::CallConv>,
|
||||||
|
|
||||||
sym_info: Vec<SymInfo>,
|
sym_info: Vec<SymInfo>,
|
||||||
sym_count: usize,
|
sym_count: usize,
|
||||||
@@ -21,8 +24,10 @@ pub struct Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Func<A: Arch> {
|
pub struct Func<A: Arch> {
|
||||||
pub instrs: Vec<Instr<A>>,
|
pub params: Vec<VarId>,
|
||||||
|
pub body: Body<A>,
|
||||||
pub sym: Symbol,
|
pub sym: Symbol,
|
||||||
|
pub conv: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct External {
|
pub struct External {
|
||||||
@@ -35,16 +40,38 @@ pub struct SymInfo {
|
|||||||
pub external: bool,
|
pub external: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type Body<A> = Vec<Instr<A>>;
|
||||||
|
|
||||||
pub enum Instr<A: Arch> {
|
pub enum Instr<A: Arch> {
|
||||||
Set { dst: VarId, src: Vec<u8> },
|
Set {
|
||||||
Call { dst: FnId, args: Vec<VarId> },
|
dst: VarId,
|
||||||
Copy { dst: VarId, src: VarId },
|
src: Vec<u8>,
|
||||||
|
},
|
||||||
|
Call {
|
||||||
|
dst: VarId,
|
||||||
|
f: FnId,
|
||||||
|
args: Vec<VarId>,
|
||||||
|
},
|
||||||
|
Copy {
|
||||||
|
dst: VarId,
|
||||||
|
src: VarId,
|
||||||
|
},
|
||||||
|
Add {
|
||||||
|
dst: VarId,
|
||||||
|
src1: VarId,
|
||||||
|
src2: VarId,
|
||||||
|
},
|
||||||
|
If {
|
||||||
|
cond: VarId,
|
||||||
|
then: Body<A>,
|
||||||
|
else_: Body<A>,
|
||||||
|
},
|
||||||
|
Loop(Body<A>),
|
||||||
|
Return(Option<VarId>),
|
||||||
|
Break(Option<VarId>),
|
||||||
Asm(A::Asm),
|
Asm(A::Asm),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type VarId = usize;
|
|
||||||
pub type FnId = usize;
|
|
||||||
|
|
||||||
impl<A: Arch> Program<A> {
|
impl<A: Arch> Program<A> {
|
||||||
pub fn encode_data(&self, data: &mut Vec<u8>, sym_tab: &mut SymTable<A::Addr>) {
|
pub fn encode_data(&self, data: &mut Vec<u8>, sym_tab: &mut SymTable<A::Addr>) {
|
||||||
for d in &self.ro_data {
|
for d in &self.ro_data {
|
||||||
@@ -70,7 +97,7 @@ impl<A: Arch> Program<A> {
|
|||||||
name: name.into(),
|
name: name.into(),
|
||||||
external: false,
|
external: false,
|
||||||
});
|
});
|
||||||
self.funcs.push(Func { instrs, sym });
|
self.funcs.push(Func { body: instrs, sym });
|
||||||
sym
|
sym
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,17 +68,17 @@ pub fn parse_rmi(ctx: &mut crate::parser::ParseCtx) -> Result<RegMemImm, Compile
|
|||||||
let next = ctx.expect_next()?;
|
let next = ctx.expect_next()?;
|
||||||
let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register or immediate");
|
let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register or immediate");
|
||||||
Ok(match &next {
|
Ok(match &next {
|
||||||
Token::Ident(ident) => RegMemImm::Reg(Reg::parse(ident).ok_or_else(err)?),
|
Token::Ident(ident) => RegMemImm::Reg(RegW::parse(ident).ok_or_else(err)?),
|
||||||
Token::Lit(LitTy::Number(num)) => RegMemImm::Imm(parse_imm(num, ctx.span)?),
|
Token::Lit(LitTy::Number(num)) => RegMemImm::Imm(parse_imm(num, ctx.span)?),
|
||||||
_ => return Err(err()),
|
_ => return Err(err()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_reg(ctx: &mut crate::parser::ParseCtx) -> Result<Reg, CompilerMsg> {
|
pub fn parse_reg(ctx: &mut crate::parser::ParseCtx) -> Result<RegW, CompilerMsg> {
|
||||||
let next = ctx.expect_next()?;
|
let next = ctx.expect_next()?;
|
||||||
let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register");
|
let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register");
|
||||||
let Token::Ident(next) = &next else {
|
let Token::Ident(next) = &next else {
|
||||||
return Err(err());
|
return Err(err());
|
||||||
};
|
};
|
||||||
Reg::parse(next).ok_or_else(err)
|
RegW::parse(next).ok_or_else(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user