ableos/hblang/src/codegen.rs

1508 lines
48 KiB
Rust
Raw Normal View History

2024-05-12 13:10:50 -05:00
use std::ops::Range;
2024-05-13 06:36:29 -05:00
use crate::ident::{self, Ident};
2024-05-12 04:52:58 -05:00
2024-05-10 14:33:42 -05:00
use {
2024-05-10 15:54:12 -05:00
crate::{
2024-05-11 15:22:08 -05:00
instrs, lexer, log,
2024-05-12 04:52:58 -05:00
parser::{self},
2024-05-10 15:54:12 -05:00
},
2024-05-10 14:33:42 -05:00
std::rc::Rc,
};
2024-05-09 11:16:01 -05:00
2024-05-12 04:52:58 -05:00
use {lexer::TokenKind as T, parser::Expr as E};
2024-05-10 14:33:42 -05:00
type LabelId = u32;
type Reg = u8;
type MaskElem = u64;
2024-05-11 15:22:08 -05:00
type Type = u32;
2024-05-13 06:36:29 -05:00
#[derive(Debug, PartialEq, Eq)]
2024-05-12 04:52:58 -05:00
struct LinReg(Reg);
#[cfg(debug_assertions)]
impl Drop for LinReg {
fn drop(&mut self) {
if !std::thread::panicking() {
panic!("reg leaked");
}
}
}
enum CowReg {
Lin(LinReg),
Reg(Reg),
}
impl CowReg {
fn get(&self) -> Reg {
match *self {
Self::Lin(LinReg(reg)) => reg,
Self::Reg(reg) => reg,
}
}
}
#[derive(Default)]
enum Ctx {
#[default]
None,
Inferred(Type),
Dest(Value),
}
impl Ctx {
fn ty(&self) -> Option<Type> {
Some(match self {
Self::Inferred(ty) => *ty,
Self::Dest(Value { ty, .. }) => *ty,
_ => return None,
})
}
}
2024-05-12 04:52:58 -05:00
pub mod bt {
2024-05-11 15:22:08 -05:00
use super::*;
macro_rules! builtin_type {
($($name:ident;)*) => {$(
2024-05-13 06:36:29 -05:00
pub const $name: Type = ${index(0)};
2024-05-11 15:22:08 -05:00
)*};
}
builtin_type! {
2024-05-12 04:52:58 -05:00
VOID;
NEVER;
2024-05-12 15:40:28 -05:00
I8;
2024-05-13 06:36:29 -05:00
I16;
I32;
INT;
2024-05-12 15:40:28 -05:00
U8;
2024-05-13 06:36:29 -05:00
U16;
U32;
UINT;
2024-05-11 15:22:08 -05:00
BOOL;
}
2024-05-13 06:36:29 -05:00
pub fn is_signed(ty: Type) -> bool {
ty >= I8 && ty <= INT
}
pub fn is_unsigned(ty: Type) -> bool {
ty >= U8 && ty <= UINT
}
pub fn strip_pointer(ty: Type) -> Type {
match TypeKind::from_ty(ty) {
TypeKind::Pointer(_) => UINT,
_ => ty,
}
}
2024-05-13 07:23:19 -05:00
pub fn is_pointer(ty: Type) -> bool {
matches!(TypeKind::from_ty(ty), TypeKind::Pointer(_))
}
2024-05-13 06:36:29 -05:00
pub fn try_upcast(a: Type, b: Type) -> Option<Type> {
Some(match (strip_pointer(a.min(b)), strip_pointer(a.max(b))) {
_ if a == b => a,
_ if is_signed(a) && is_signed(b) || is_unsigned(a) && is_unsigned(b) => a.max(b),
_ if is_unsigned(b) && is_signed(a) && b - U8 < a - I8 => a,
_ => return None,
})
}
2024-05-11 15:22:08 -05:00
}
2024-05-12 04:52:58 -05:00
#[derive(Debug)]
2024-05-11 15:22:08 -05:00
enum TypeKind {
Builtin(Type),
Struct(Type),
2024-05-12 04:52:58 -05:00
Pointer(Type),
2024-05-11 15:22:08 -05:00
}
impl TypeKind {
2024-05-13 06:36:29 -05:00
const FLAG_BITS: u32 = 2;
const FLAG_OFFSET: u32 = std::mem::size_of::<Type>() as u32 * 8 - Self::FLAG_BITS;
const INDEX_MASK: u32 = (1 << (32 - Self::FLAG_BITS)) - 1;
fn from_ty(ty: Type) -> Self {
let (flag, index) = (ty >> Self::FLAG_OFFSET, ty & Self::INDEX_MASK);
2024-05-12 04:52:58 -05:00
match flag {
2024-05-13 06:36:29 -05:00
0 => Self::Builtin(index),
2024-05-12 04:52:58 -05:00
1 => Self::Pointer(index),
2 => Self::Struct(index),
_ => unreachable!(),
2024-05-11 15:22:08 -05:00
}
}
2024-05-12 04:52:58 -05:00
const fn encode(self) -> Type {
let (index, flag) = match self {
2024-05-13 06:36:29 -05:00
Self::Builtin(index) => (index, 0),
2024-05-12 04:52:58 -05:00
Self::Pointer(index) => (index, 1),
Self::Struct(index) => (index, 2),
};
2024-05-13 06:36:29 -05:00
(flag << Self::FLAG_OFFSET) | index
2024-05-12 04:52:58 -05:00
}
2024-05-11 15:22:08 -05:00
}
const STACK_PTR: Reg = 254;
const ZERO: Reg = 0;
const RET_ADDR: Reg = 31;
const ELEM_WIDTH: usize = std::mem::size_of::<MaskElem>() * 8;
2024-05-10 14:33:42 -05:00
struct Frame {
label: LabelId,
prev_relocs: usize,
offset: u32,
}
struct Reloc {
2024-05-11 09:04:13 -05:00
id: LabelId,
2024-05-10 14:33:42 -05:00
offset: u32,
2024-05-11 09:04:13 -05:00
instr_offset: u16,
size: u16,
2024-05-10 14:33:42 -05:00
}
struct StackReloc {
offset: u32,
size: u16,
}
2024-05-10 14:33:42 -05:00
#[derive(Default)]
pub struct Func {
code: Vec<u8>,
relocs: Vec<Reloc>,
}
impl Func {
pub fn extend(&mut self, bytes: &[u8]) {
self.code.extend_from_slice(bytes);
}
2024-05-11 09:04:13 -05:00
pub fn offset(&mut self, id: LabelId, instr_offset: u16, size: u16) {
2024-05-10 14:33:42 -05:00
self.relocs.push(Reloc {
id,
2024-05-11 09:04:13 -05:00
offset: self.code.len() as u32,
instr_offset,
2024-05-10 14:33:42 -05:00
size,
});
}
fn encode(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) {
let name = instrs::NAMES[instr[0] as usize];
2024-05-11 15:22:08 -05:00
log::dbg!(
"{:08x}: {}: {}",
self.code.len(),
name,
instr
.iter()
.take(len)
.skip(1)
.map(|b| format!("{:02x}", b))
.collect::<String>()
);
self.code.extend_from_slice(&instr[..len]);
}
2024-05-10 14:33:42 -05:00
fn push(&mut self, value: Reg, size: usize) {
self.subi64(STACK_PTR, STACK_PTR, size as _);
self.encode(instrs::st(value, STACK_PTR, 0, size as _));
2024-05-10 14:33:42 -05:00
}
fn pop(&mut self, value: Reg, size: usize) {
self.encode(instrs::ld(value, STACK_PTR, 0, size as _));
self.encode(instrs::addi64(STACK_PTR, STACK_PTR, size as _));
}
2024-05-13 06:36:29 -05:00
fn short_cut_bin_op(&mut self, dest: Reg, src: Reg, imm: u64) -> bool {
if imm == 0 {
if dest != src {
self.encode(instrs::cp(dest, src));
}
}
imm != 0
}
fn subi64(&mut self, dest: Reg, src: Reg, imm: u64) {
2024-05-13 06:36:29 -05:00
if self.short_cut_bin_op(dest, src, imm) {
self.encode(instrs::addi64(dest, src, imm.wrapping_neg()));
}
}
fn addi64(&mut self, dest: Reg, src: Reg, imm: u64) {
if self.short_cut_bin_op(dest, src, imm) {
self.encode(instrs::addi64(dest, src, imm));
}
2024-05-10 14:33:42 -05:00
}
fn call(&mut self, func: LabelId) {
self.offset(func, 3, 4);
self.encode(instrs::jal(RET_ADDR, ZERO, 0));
2024-05-10 14:33:42 -05:00
}
fn ret(&mut self) {
self.encode(instrs::jala(ZERO, RET_ADDR, 0));
2024-05-10 14:33:42 -05:00
}
fn prelude(&mut self, entry: LabelId) {
self.call(entry);
self.encode(instrs::tx());
2024-05-10 15:54:12 -05:00
}
2024-05-11 09:04:13 -05:00
fn relocate(&mut self, labels: &[FnLabel], shift: i64) {
2024-05-10 14:33:42 -05:00
for reloc in self.relocs.drain(..) {
let label = &labels[reloc.id as usize];
let offset = if reloc.size == 8 {
reloc.offset as i64
} else {
label.offset as i64 - reloc.offset as i64
} + shift;
2024-05-11 15:22:08 -05:00
log::dbg!(
2024-05-12 04:52:58 -05:00
label.name,
2024-05-11 09:04:13 -05:00
offset,
reloc.size,
reloc.instr_offset,
reloc.offset,
shift,
label.offset
);
let dest = &mut self.code[reloc.offset as usize + reloc.instr_offset as usize..]
[..reloc.size as usize];
2024-05-10 14:33:42 -05:00
match reloc.size {
2 => dest.copy_from_slice(&(offset as i16).to_le_bytes()),
4 => dest.copy_from_slice(&(offset as i32).to_le_bytes()),
8 => dest.copy_from_slice(&(offset as i64).to_le_bytes()),
_ => unreachable!(),
};
}
}
}
#[derive(Default)]
pub struct RegAlloc {
2024-05-12 15:40:28 -05:00
free: Vec<Reg>,
max_used: Reg,
}
impl RegAlloc {
2024-05-11 09:04:13 -05:00
fn init_callee(&mut self) {
2024-05-10 14:33:42 -05:00
self.free.clear();
2024-05-12 15:40:28 -05:00
self.free.extend((32..=253).rev());
self.max_used = RET_ADDR;
}
2024-05-12 04:52:58 -05:00
fn allocate(&mut self) -> LinReg {
let reg = self.free.pop().expect("TODO: we need to spill");
2024-05-12 15:40:28 -05:00
self.max_used = self.max_used.max(reg);
2024-05-12 04:52:58 -05:00
LinReg(reg)
}
2024-05-12 04:52:58 -05:00
fn free(&mut self, reg: LinReg) {
self.free.push(reg.0);
std::mem::forget(reg);
}
2024-05-12 13:10:50 -05:00
fn free_cow(&mut self, reg: CowReg) {
match reg {
CowReg::Lin(reg) => self.free(reg),
CowReg::Reg(_) => {}
}
}
2024-05-12 15:40:28 -05:00
fn pushed_size(&self) -> usize {
(self.max_used as usize - RET_ADDR as usize + 1) * 8
2024-05-12 13:10:50 -05:00
}
}
2024-05-09 11:16:01 -05:00
2024-05-12 16:45:28 -05:00
#[derive(Clone)]
2024-05-11 09:04:13 -05:00
struct FnLabel {
2024-05-10 14:33:42 -05:00
offset: u32,
2024-05-12 04:52:58 -05:00
name: Ident,
2024-05-12 16:45:28 -05:00
args: Rc<[Type]>,
ret: Type,
2024-05-10 14:33:42 -05:00
}
2024-05-12 04:52:58 -05:00
struct Variable {
id: Ident,
value: Value,
}
2024-05-11 09:04:13 -05:00
struct RetReloc {
offset: u32,
instr_offset: u16,
size: u16,
}
2024-05-11 11:16:27 -05:00
struct Loop {
offset: u32,
relocs: Vec<RetReloc>,
}
2024-05-11 15:22:08 -05:00
struct Struct {
2024-05-12 15:40:28 -05:00
name: Rc<str>,
2024-05-12 05:21:40 -05:00
id: Ident,
2024-05-12 06:13:36 -05:00
fields: Rc<[(Rc<str>, Type)]>,
2024-05-11 15:22:08 -05:00
}
2024-05-12 15:40:28 -05:00
struct TypeDisplay<'a> {
codegen: &'a Codegen<'a>,
ty: Type,
}
impl<'a> TypeDisplay<'a> {
fn new(codegen: &'a Codegen<'a>, ty: Type) -> Self {
Self { codegen, ty }
}
fn rety(&self, ty: Type) -> Self {
Self::new(self.codegen, ty)
}
}
impl<'a> std::fmt::Display for TypeDisplay<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use TypeKind as TK;
let str = match TK::from_ty(self.ty) {
TK::Builtin(bt::VOID) => "void",
TK::Builtin(bt::NEVER) => "never",
TK::Builtin(bt::INT) => "int",
TK::Builtin(bt::I32) => "i32",
TK::Builtin(bt::I16) => "i16",
TK::Builtin(bt::I8) => "i8",
TK::Builtin(bt::UINT) => "uint",
TK::Builtin(bt::U32) => "u32",
TK::Builtin(bt::U16) => "u16",
TK::Builtin(bt::U8) => "u8",
TK::Builtin(bt::BOOL) => "bool",
TK::Builtin(_) => unreachable!(),
TK::Pointer(ty) => {
return write!(f, "*{}", self.rety(self.codegen.pointers[ty as usize]))
}
TK::Struct(idx) => return write!(f, "{}", self.codegen.records[idx as usize].name),
};
f.write_str(str)
}
}
2024-05-12 17:02:32 -05:00
#[derive(Default)]
2024-05-09 16:41:59 -05:00
pub struct Codegen<'a> {
2024-05-12 17:02:32 -05:00
path: &'a str,
2024-05-12 16:19:45 -05:00
input: &'a [u8],
2024-05-12 13:10:50 -05:00
ret: Type,
gpa: RegAlloc,
code: Func,
temp: Func,
labels: Vec<FnLabel>,
stack_size: u64,
2024-05-12 13:10:50 -05:00
vars: Vec<Variable>,
2024-05-11 09:04:13 -05:00
ret_relocs: Vec<RetReloc>,
2024-05-12 13:10:50 -05:00
loops: Vec<Loop>,
records: Vec<Struct>,
pointers: Vec<Type>,
main: Option<LabelId>,
2024-05-09 11:16:01 -05:00
}
2024-05-09 16:41:59 -05:00
impl<'a> Codegen<'a> {
2024-05-12 17:02:32 -05:00
pub fn file(&mut self, path: &'a str, input: &'a [u8], exprs: &'a [parser::Expr<'a>]) {
self.path = path;
2024-05-12 16:19:45 -05:00
self.input = input;
2024-05-12 16:45:28 -05:00
for expr in exprs {
match expr {
E::BinOp {
left: E::Ident { id, .. },
op: T::Decl,
right: E::Closure { args, ret, .. },
} => {
let args = args.iter().map(|arg| self.ty(&arg.ty)).collect::<Rc<[_]>>();
let ret = self.ty(ret);
self.declare_fn_label(*id, args, ret);
}
E::BinOp {
left: E::Ident { id, name, .. },
op: T::Decl,
right: E::Struct { .. },
} => {
self.records.push(Struct {
id: *id,
name: (*name).into(),
fields: Rc::from([]),
});
}
_ => self.report(expr.pos(), "expected declaration"),
}
}
2024-05-09 16:41:59 -05:00
for expr in exprs {
2024-05-12 16:19:45 -05:00
let E::BinOp {
2024-05-12 16:45:28 -05:00
left: E::Ident { id, name, .. },
2024-05-12 16:19:45 -05:00
op: T::Decl,
2024-05-12 16:45:28 -05:00
right,
2024-05-12 16:19:45 -05:00
} = expr
else {
self.report(expr.pos(), format_args!("expected declaration"));
};
2024-05-12 16:45:28 -05:00
match right {
E::Struct { fields, .. } => {
let fields = fields
.iter()
.map(|&(name, ty)| (name.into(), self.ty(&ty)))
.collect();
self.records
.iter_mut()
.find(|r| r.id == *id)
.unwrap()
.fields = fields;
}
E::Closure { body, args, .. } => {
log::dbg!("fn: {}", name);
let frame = self.define_fn_label(*id);
if *name == "main" {
self.main = Some(frame.label);
}
let fn_label = self.labels[frame.label as usize].clone();
log::dbg!("fn-args");
2024-05-13 02:38:33 -05:00
let mut parama = 3..12;
2024-05-12 16:45:28 -05:00
for (arg, &ty) in args.iter().zip(fn_label.args.iter()) {
let loc = self.load_arg(ty, &mut parama);
self.vars.push(Variable {
id: arg.id,
value: Value { ty, loc },
});
}
self.gpa.init_callee();
self.ret = fn_label.ret;
log::dbg!("fn-body");
if self.expr(body).is_some() {
2024-05-12 16:45:28 -05:00
self.report(body.pos(), "expected all paths in the fucntion to return");
}
self.vars.clear();
log::dbg!("fn-prelude, stack: {:x}", self.stack_size);
log::dbg!("fn-relocs");
self.write_fn_prelude(frame);
log::dbg!("fn-ret");
self.reloc_rets();
self.ret();
self.stack_size = 0;
}
_ => unreachable!(),
}
2024-05-09 11:16:01 -05:00
}
}
2024-05-12 15:40:28 -05:00
fn align_of(&self, ty: Type) -> u64 {
use TypeKind as TK;
match TypeKind::from_ty(ty) {
TK::Struct(t) => self.records[t as usize]
.fields
.iter()
.map(|&(_, ty)| self.align_of(ty))
.max()
.unwrap(),
_ => self.size_of(ty).max(1),
}
}
2024-05-12 04:52:58 -05:00
fn size_of(&self, ty: Type) -> u64 {
2024-05-12 15:40:28 -05:00
use TypeKind as TK;
match TK::from_ty(ty) {
TK::Pointer(_) => 8,
TK::Builtin(bt::VOID) => 0,
TK::Builtin(bt::NEVER) => unreachable!(),
2024-05-13 06:36:29 -05:00
TK::Builtin(bt::INT | bt::UINT) => 8,
2024-05-12 15:40:28 -05:00
TK::Builtin(bt::I32 | bt::U32) => 4,
TK::Builtin(bt::I16 | bt::U16) => 2,
TK::Builtin(bt::I8 | bt::U8 | bt::BOOL) => 1,
TK::Builtin(e) => unreachable!("{:?}", e),
TK::Struct(ty) => {
log::dbg!("size_of: {:?}", ty);
let mut offset = 0;
let record = &self.records[ty as usize];
for &(_, ty) in record.fields.iter() {
let align = self.align_of(ty);
offset = (offset + align - 1) & !(align - 1);
offset += self.size_of(ty);
}
offset
}
2024-05-12 04:52:58 -05:00
}
}
2024-05-12 15:40:28 -05:00
fn display_ty(&self, ty: Type) -> TypeDisplay {
TypeDisplay::new(self, ty)
}
2024-05-12 16:19:45 -05:00
fn offset_of(&self, pos: parser::Pos, ty: Type, field: &str) -> (u64, Type) {
2024-05-12 15:40:28 -05:00
let TypeKind::Struct(idx) = TypeKind::from_ty(ty) else {
2024-05-12 16:19:45 -05:00
self.report(
pos,
format_args!("expected struct, got {}", self.display_ty(ty)),
);
2024-05-12 15:40:28 -05:00
};
let record = &self.records[idx as usize];
let mut offset = 0;
for (name, ty) in record.fields.iter() {
if name.as_ref() == field {
return (offset, *ty);
}
let align = self.align_of(*ty);
offset = (offset + align - 1) & !(align - 1);
offset += self.size_of(*ty);
}
2024-05-12 16:19:45 -05:00
self.report(pos, format_args!("field not found: {}", field));
2024-05-12 15:40:28 -05:00
}
2024-05-13 06:36:29 -05:00
fn loc_to_reg(&mut self, loc: Loc, size: u64) -> LinReg {
match loc {
2024-05-12 04:52:58 -05:00
Loc::RegRef(rr) => {
let reg = self.gpa.allocate();
self.code.encode(instrs::cp(reg.0, rr));
reg
}
Loc::Return => todo!(),
Loc::Reg(reg) => reg,
2024-05-12 15:40:28 -05:00
Loc::Deref(dreg, offset) => {
2024-05-12 04:52:58 -05:00
let reg = self.gpa.allocate();
2024-05-13 06:36:29 -05:00
self.code
.encode(instrs::ld(reg.0, dreg.0, offset, size as _));
2024-05-12 04:52:58 -05:00
self.gpa.free(dreg);
reg
}
2024-05-12 15:40:28 -05:00
Loc::DerefRef(dreg, offset) => {
2024-05-12 13:10:50 -05:00
let reg = self.gpa.allocate();
2024-05-13 06:36:29 -05:00
self.code.encode(instrs::ld(reg.0, dreg, offset, size as _));
2024-05-12 13:10:50 -05:00
reg
}
Loc::Imm(imm) => {
let reg = self.gpa.allocate();
2024-05-12 04:52:58 -05:00
self.code.encode(instrs::li64(reg.0, imm));
reg
}
2024-05-12 06:13:36 -05:00
Loc::Stack(offset) | Loc::StackRef(offset) => {
let reg = self.gpa.allocate();
2024-05-13 06:36:29 -05:00
self.load_stack(reg.0, offset, size as _);
reg
}
}
}
2024-05-13 06:36:29 -05:00
fn alloc_stack(&mut self, size: u64) -> u64 {
let offset = self.stack_size;
2024-05-12 13:10:50 -05:00
log::dbg!("alloc_stack: {} {}", offset, size);
2024-05-13 06:36:29 -05:00
self.stack_size += size;
offset
}
fn store_stack(&mut self, reg: Reg, offset: u64, size: u16) {
self.code.encode(instrs::st(reg, STACK_PTR, offset, size));
}
fn load_stack(&mut self, reg: Reg, offset: u64, size: u16) {
self.code.encode(instrs::ld(reg, STACK_PTR, offset, size));
}
2024-05-11 09:04:13 -05:00
fn reloc_rets(&mut self) {
let len = self.code.code.len() as i32;
for reloc in self.ret_relocs.drain(..) {
let dest = &mut self.code.code[reloc.offset as usize + reloc.instr_offset as usize..]
[..reloc.size as usize];
debug_assert!(dest.iter().all(|&b| b == 0));
let offset = len - reloc.offset as i32;
dest.copy_from_slice(&offset.to_ne_bytes());
}
}
2024-05-12 04:52:58 -05:00
fn ty(&mut self, expr: &parser::Expr<'a>) -> Type {
match *expr {
2024-05-13 06:36:29 -05:00
E::Ident { id, .. } if ident::is_null(id) => id,
2024-05-12 15:40:28 -05:00
E::UnOp {
op: T::Star, val, ..
} => {
let ty = self.ty(val);
self.alloc_pointer(ty)
}
E::Ident { id, name, .. } => {
2024-05-12 16:19:45 -05:00
let Some(index) = self.records.iter().position(|r| r.id == id) else {
self.report(expr.pos(), format_args!("unknown type: {}", name))
};
2024-05-12 06:13:36 -05:00
TypeKind::Struct(index as Type).encode()
}
2024-05-12 04:52:58 -05:00
expr => unimplemented!("type: {:#?}", expr),
}
}
fn expr(&mut self, expr: &'a parser::Expr<'a>) -> Option<Value> {
self.expr_ctx(expr, Ctx::default())
}
fn expr_ctx(&mut self, expr: &'a parser::Expr<'a>, ctx: Ctx) -> Option<Value> {
let value = match *expr {
2024-05-12 13:10:50 -05:00
E::Bool { value, .. } => Some(Value {
ty: bt::BOOL,
loc: Loc::Imm(value as u64),
}),
2024-05-12 06:13:36 -05:00
E::Ctor { ty, fields } => {
let ty = self.ty(&ty);
2024-05-12 13:10:50 -05:00
let size = self.size_of(ty);
let loc = match ctx {
Ctx::Dest(dest) => dest.loc,
2024-05-13 06:36:29 -05:00
_ => Loc::Stack(self.alloc_stack(size)),
};
for (name, field) in fields {
let (offset, ty) = self.offset_of(expr.pos(), ty, name);
let loc = loc.offset_ref(offset);
self.expr_ctx(field, Ctx::Dest(Value { ty, loc }))?;
2024-05-12 06:13:36 -05:00
}
return Some(Value { ty, loc });
2024-05-12 06:13:36 -05:00
}
E::Field { target, field } => {
let mut tal = self.expr(target)?;
2024-05-12 16:19:45 -05:00
if let TypeKind::Pointer(ty) = TypeKind::from_ty(tal.ty) {
tal.ty = self.pointers[ty as usize];
tal.loc = match tal.loc {
2024-05-12 15:40:28 -05:00
Loc::Reg(r) => Loc::Deref(r, 0),
Loc::RegRef(r) => Loc::DerefRef(r, 0),
Loc::StackRef(stack) | Loc::Stack(stack) => {
let reg = self.gpa.allocate();
self.load_stack(reg.0, stack, 8);
Loc::Deref(reg, 0)
}
2024-05-12 16:19:45 -05:00
l => todo!("cant get field of {:?}", l),
2024-05-12 15:40:28 -05:00
};
}
2024-05-12 16:19:45 -05:00
let (offset, ty) = self.offset_of(target.pos(), tal.ty, field);
let loc = match tal.loc {
2024-05-12 15:40:28 -05:00
Loc::Deref(r, off) => Loc::Deref(r, off + offset),
Loc::DerefRef(r, off) => Loc::DerefRef(r, off + offset),
2024-05-12 13:10:50 -05:00
Loc::Stack(stack) => Loc::Stack(stack + offset),
Loc::StackRef(stack) => Loc::StackRef(stack + offset),
2024-05-12 15:40:28 -05:00
l => todo!("cant get field of {:?}", l),
2024-05-12 06:13:36 -05:00
};
2024-05-12 15:40:28 -05:00
Some(Value { ty, loc })
2024-05-12 06:13:36 -05:00
}
2024-05-12 04:52:58 -05:00
E::UnOp {
2024-05-12 16:19:45 -05:00
op: T::Amp,
val,
pos,
2024-05-12 04:52:58 -05:00
} => {
let val = self.expr(val)?;
let loc = match val.loc {
2024-05-12 06:13:36 -05:00
Loc::StackRef(off) => {
2024-05-12 04:52:58 -05:00
let reg = self.gpa.allocate();
2024-05-13 06:36:29 -05:00
self.code.addi64(reg.0, STACK_PTR, off);
Loc::Reg(reg)
}
Loc::Deref(r, off) => {
2024-05-13 06:36:29 -05:00
self.code.addi64(r.0, r.0, off);
Loc::Reg(r)
}
Loc::DerefRef(r, off) => {
let reg = self.gpa.allocate();
2024-05-13 06:36:29 -05:00
self.code.addi64(reg.0, r, off);
Loc::Reg(reg)
2024-05-12 04:52:58 -05:00
}
2024-05-12 16:19:45 -05:00
l => self.report(
pos,
format_args!("cant take pointer of {} ({:?})", self.display_ty(val.ty), l),
),
};
Some(Value {
ty: self.alloc_pointer(val.ty),
loc,
})
2024-05-12 04:52:58 -05:00
}
E::UnOp {
2024-05-12 16:19:45 -05:00
op: T::Star,
val,
pos,
2024-05-12 04:52:58 -05:00
} => {
let val = self.expr(val)?;
2024-05-13 06:36:29 -05:00
let reg = self.loc_to_reg(val.loc, self.size_of(val.ty));
2024-05-12 04:52:58 -05:00
match TypeKind::from_ty(val.ty) {
TypeKind::Pointer(ty) => Some(Value {
ty: self.pointers[ty as usize],
2024-05-12 15:40:28 -05:00
loc: Loc::Deref(reg, 0),
2024-05-12 04:52:58 -05:00
}),
2024-05-12 16:19:45 -05:00
_ => self.report(
pos,
format_args!("expected pointer, got {}", self.display_ty(val.ty)),
),
2024-05-12 04:52:58 -05:00
}
}
2024-05-11 15:22:08 -05:00
E::BinOp {
2024-05-12 04:52:58 -05:00
left: E::Ident { id, .. },
2024-05-11 15:22:08 -05:00
op: T::Decl,
right,
} => {
let val = self.expr(right)?;
2024-05-12 06:13:36 -05:00
let loc = self.make_loc_owned(val.loc, val.ty);
let loc = self.ensure_spilled(loc);
2024-05-12 04:52:58 -05:00
self.vars.push(Variable {
id: *id,
2024-05-12 06:13:36 -05:00
value: Value { ty: val.ty, loc },
2024-05-12 04:52:58 -05:00
});
Some(Value::VOID)
2024-05-11 15:22:08 -05:00
}
2024-05-11 09:04:13 -05:00
E::Call {
2024-05-12 04:52:58 -05:00
func: E::Ident { id, .. },
2024-05-11 09:04:13 -05:00
args,
} => {
2024-05-12 16:45:28 -05:00
let func = self.get_label(*id);
let fn_label = self.labels[func as usize].clone();
2024-05-13 02:38:33 -05:00
let mut parama = 3..12;
2024-05-12 16:45:28 -05:00
for (earg, &ty) in args.iter().zip(fn_label.args.iter()) {
let arg = self.expr_ctx(earg, Ctx::Inferred(ty))?;
2024-05-13 06:36:29 -05:00
_ = self.assert_ty(earg.pos(), ty, arg.ty);
2024-05-12 13:10:50 -05:00
self.pass_arg(arg, &mut parama);
2024-05-11 09:04:13 -05:00
}
2024-05-13 02:38:33 -05:00
let size = self.size_of(fn_label.ret);
let loc = match size {
0 => Loc::Imm(0),
2024-05-13 06:36:29 -05:00
..=8 => Loc::RegRef(1),
..=16 => match ctx {
Ctx::Dest(dest) => dest.loc,
2024-05-13 06:36:29 -05:00
_ => Loc::Stack(self.alloc_stack(size)),
},
2024-05-13 06:36:29 -05:00
..=u64::MAX => {
let val = match ctx {
Ctx::Dest(dest) => dest.loc,
2024-05-13 06:36:29 -05:00
_ => Loc::Stack(self.alloc_stack(size)),
};
let (ptr, off) = val.ref_to_ptr(size);
self.code.encode(instrs::cp(1, ptr));
2024-05-13 06:36:29 -05:00
self.code.addi64(1, ptr, off);
val
2024-05-13 02:38:33 -05:00
}
};
2024-05-11 09:04:13 -05:00
self.code.call(func);
2024-05-13 02:38:33 -05:00
match size {
0 => {}
2024-05-13 06:36:29 -05:00
..=8 => {}
..=16 => {
2024-05-13 02:38:33 -05:00
if let Loc::Stack(stack) = loc {
self.store_stack(1, stack, 16);
2024-05-13 06:36:29 -05:00
} else {
unreachable!()
2024-05-13 02:38:33 -05:00
}
}
2024-05-13 06:36:29 -05:00
..=u64::MAX => {}
2024-05-13 02:38:33 -05:00
}
return Some(Value {
2024-05-13 02:38:33 -05:00
ty: fn_label.ret,
loc,
});
2024-05-11 09:04:13 -05:00
}
2024-05-12 04:52:58 -05:00
E::Ident { name, id, .. } => {
2024-05-12 16:19:45 -05:00
let Some(var) = self.vars.iter().find(|v| v.id == id) else {
self.report(expr.pos(), format_args!("unknown variable: {}", name))
};
Some(Value {
2024-05-12 04:52:58 -05:00
ty: var.value.ty,
loc: var.value.loc.take_ref(),
})
}
2024-05-12 16:19:45 -05:00
E::Return { val, pos } => {
2024-05-10 14:33:42 -05:00
if let Some(val) = val {
let val = self.expr_ctx(val, Ctx::Inferred(self.ret))?;
2024-05-13 06:36:29 -05:00
let ty = self.assert_ty(pos, self.ret, val.ty);
let val = self.ensure_sign_extended(val, ty);
self.assign(self.ret, Loc::Return, val.loc);
2024-05-10 14:33:42 -05:00
}
2024-05-11 09:04:13 -05:00
self.ret_relocs.push(RetReloc {
offset: self.code.code.len() as u32,
instr_offset: 1,
size: 4,
});
self.code.encode(instrs::jmp(0));
2024-05-10 14:33:42 -05:00
None
2024-05-09 11:16:01 -05:00
}
2024-05-11 15:22:08 -05:00
E::Block { stmts, .. } => {
2024-05-09 16:41:59 -05:00
for stmt in stmts {
if let Loc::Reg(reg) = self.expr(stmt)?.loc {
2024-05-12 04:52:58 -05:00
self.gpa.free(reg);
}
2024-05-09 11:16:01 -05:00
}
Some(Value::VOID)
2024-05-09 11:16:01 -05:00
}
2024-05-11 15:22:08 -05:00
E::Number { value, .. } => Some(Value {
ty: ctx.ty().unwrap_or(bt::INT),
2024-05-10 14:33:42 -05:00
loc: Loc::Imm(value),
}),
2024-05-11 15:22:08 -05:00
E::If {
cond, then, else_, ..
} => 'b: {
2024-05-12 04:52:58 -05:00
log::dbg!("if-cond");
let cond = self.expr_ctx(cond, Ctx::Inferred(bt::BOOL))?;
2024-05-13 06:36:29 -05:00
let reg = self.loc_to_reg(cond.loc, 1);
let jump_offset = self.code.code.len() as u32;
2024-05-12 04:52:58 -05:00
self.code.encode(instrs::jeq(reg.0, 0, 0));
self.gpa.free(reg);
2024-05-12 04:52:58 -05:00
log::dbg!("if-then");
let then_unreachable = self.expr(then).is_none();
let mut else_unreachable = false;
2024-05-11 11:16:27 -05:00
let mut jump = self.code.code.len() as i16 - jump_offset as i16;
2024-05-11 11:16:27 -05:00
if let Some(else_) = else_ {
2024-05-12 04:52:58 -05:00
log::dbg!("if-else");
2024-05-11 11:16:27 -05:00
let else_jump_offset = self.code.code.len() as u32;
if !then_unreachable {
self.code.encode(instrs::jmp(0));
jump = self.code.code.len() as i16 - jump_offset as i16;
}
2024-05-11 11:16:27 -05:00
else_unreachable = self.expr(else_).is_none();
2024-05-11 11:16:27 -05:00
if !then_unreachable {
let jump = self.code.code.len() as i32 - else_jump_offset as i32;
log::dbg!("if-else-jump: {}", jump);
self.code.code[else_jump_offset as usize + 1..][..4]
.copy_from_slice(&jump.to_ne_bytes());
}
2024-05-11 11:16:27 -05:00
}
2024-05-12 04:52:58 -05:00
log::dbg!("if-then-jump: {}", jump);
self.code.code[jump_offset as usize + 3..][..2]
.copy_from_slice(&jump.to_ne_bytes());
if then_unreachable && else_unreachable {
break 'b None;
}
Some(Value::VOID)
}
E::Loop { body, .. } => 'a: {
2024-05-12 04:52:58 -05:00
log::dbg!("loop");
2024-05-11 11:16:27 -05:00
let loop_start = self.code.code.len() as u32;
self.loops.push(Loop {
offset: loop_start,
relocs: Default::default(),
});
let body_unreachable = self.expr(body).is_none();
2024-05-11 11:16:27 -05:00
2024-05-12 04:52:58 -05:00
log::dbg!("loop-end");
if !body_unreachable {
let loop_end = self.code.code.len();
self.code
.encode(instrs::jmp(loop_start as i32 - loop_end as i32));
}
2024-05-11 11:16:27 -05:00
let loop_end = self.code.code.len() as u32;
let loop_ = self.loops.pop().unwrap();
let is_unreachable = loop_.relocs.is_empty();
2024-05-11 11:16:27 -05:00
for reloc in loop_.relocs {
let dest = &mut self.code.code
[reloc.offset as usize + reloc.instr_offset as usize..]
[..reloc.size as usize];
let offset = loop_end as i32 - reloc.offset as i32;
dest.copy_from_slice(&offset.to_ne_bytes());
}
if is_unreachable {
log::dbg!("infinite loop");
break 'a None;
}
Some(Value::VOID)
2024-05-11 11:16:27 -05:00
}
2024-05-11 15:22:08 -05:00
E::Break { .. } => {
2024-05-11 11:16:27 -05:00
let loop_ = self.loops.last_mut().unwrap();
let offset = self.code.code.len() as u32;
self.code.encode(instrs::jmp(0));
loop_.relocs.push(RetReloc {
offset,
instr_offset: 1,
size: 4,
});
None
}
2024-05-11 15:22:08 -05:00
E::Continue { .. } => {
2024-05-11 11:16:27 -05:00
let loop_ = self.loops.last().unwrap();
let offset = self.code.code.len() as u32;
self.code
.encode(instrs::jmp(loop_.offset as i32 - offset as i32));
None
}
2024-05-10 15:54:12 -05:00
E::BinOp { left, op, right } => {
2024-05-13 06:36:29 -05:00
use instrs as i;
2024-05-12 04:52:58 -05:00
if op == T::Assign {
let left = self.expr(left)?;
2024-05-13 06:36:29 -05:00
self.expr_ctx(right, Ctx::Dest(left))?;
return Some(Value::VOID);
2024-05-12 04:52:58 -05:00
}
2024-05-10 15:54:12 -05:00
let left = self.expr(left)?;
2024-05-13 06:36:29 -05:00
let lsize = self.size_of(left.ty);
let lhs = self.loc_to_reg(left.loc, lsize);
let right = self.expr_ctx(right, Ctx::Inferred(left.ty))?;
2024-05-13 06:36:29 -05:00
let rsize = self.size_of(right.ty);
let rhs = self.loc_to_reg(right.loc, rsize);
let ty = self.assert_ty(expr.pos(), left.ty, right.ty);
let size = self.size_of(ty);
let signed = bt::is_signed(ty);
2024-05-13 07:23:19 -05:00
let min_size = lsize.min(rsize);
if bt::is_signed(ty) && min_size < size {
let operand = if lsize < rsize { lhs.0 } else { rhs.0 };
let op = [i::sxt8, i::sxt16, i::sxt32][min_size.ilog2() as usize];
self.code.encode(op(operand, operand));
}
if bt::is_pointer(left.ty) ^ bt::is_pointer(right.ty) {
let (offset, ty) = if bt::is_pointer(left.ty) {
(lhs.0, left.ty)
} else {
(rhs.0, right.ty)
};
let TypeKind::Pointer(ty) = TypeKind::from_ty(ty) else {
unreachable!()
};
let size = self.size_of(self.pointers[ty as usize]);
self.code.encode(i::mul64(offset, offset, size as _));
}
2024-05-13 06:36:29 -05:00
let ops = match op {
T::Plus => [i::add8, i::add16, i::add32, i::add64],
T::Minus => [i::sub8, i::sub16, i::sub32, i::sub64],
T::Star => [i::mul8, i::mul16, i::mul32, i::mul64],
T::FSlash if signed => [
|a, b, c| i::dirs8(a, ZERO, b, c),
|a, b, c| i::dirs16(a, ZERO, b, c),
|a, b, c| i::dirs32(a, ZERO, b, c),
|a, b, c| i::dirs64(a, ZERO, b, c),
],
T::FSlash => [
|a, b, c| i::diru8(a, ZERO, b, c),
|a, b, c| i::diru16(a, ZERO, b, c),
|a, b, c| i::diru32(a, ZERO, b, c),
|a, b, c| i::diru64(a, ZERO, b, c),
],
2024-05-13 07:23:19 -05:00
T::Le | T::Ge | T::Ne => {
let against = match op {
T::Le => 1,
T::Ne => 0,
T::Ge => (-1i64) as _,
_ => unreachable!(),
};
2024-05-13 06:36:29 -05:00
let op = if signed { i::cmps } else { i::cmpu };
self.code.encode(op(lhs.0, lhs.0, rhs.0));
self.gpa.free(rhs);
2024-05-13 06:36:29 -05:00
self.code.encode(instrs::cmpui(lhs.0, lhs.0, against));
return Some(Value {
2024-05-12 04:52:58 -05:00
ty: bt::BOOL,
loc: Loc::Reg(lhs),
});
}
2024-05-13 06:36:29 -05:00
T::Eq | T::Lt | T::Gt => {
let against = match op {
T::Eq => 0,
T::Lt => 1,
T::Gt => (-1i64) as _,
_ => unreachable!(),
};
let op = if signed { i::cmps } else { i::cmpu };
self.code.encode(op(lhs.0, lhs.0, rhs.0));
2024-05-11 11:16:27 -05:00
self.gpa.free(rhs);
2024-05-13 06:36:29 -05:00
self.code.encode(instrs::cmpui(lhs.0, lhs.0, against));
2024-05-12 04:52:58 -05:00
self.code.encode(instrs::not(lhs.0, lhs.0));
2024-05-11 11:16:27 -05:00
return Some(Value {
2024-05-12 04:52:58 -05:00
ty: bt::BOOL,
2024-05-11 11:16:27 -05:00
loc: Loc::Reg(lhs),
});
}
2024-05-10 15:54:12 -05:00
_ => unimplemented!("{:#?}", op),
};
2024-05-13 07:23:19 -05:00
self.code
.encode(ops[size.ilog2() as usize](lhs.0, lhs.0, rhs.0));
2024-05-10 15:54:12 -05:00
self.gpa.free(rhs);
Some(Value {
2024-05-13 06:36:29 -05:00
ty,
2024-05-10 15:54:12 -05:00
loc: Loc::Reg(lhs),
})
}
ast => unimplemented!("{:#?}", ast),
}?;
if let Ctx::Dest(dest) = ctx {
self.assign(dest.ty, dest.loc, value.loc);
Some(Value::VOID)
} else {
Some(value)
2024-05-09 11:16:01 -05:00
}
}
2024-05-13 06:36:29 -05:00
fn ensure_sign_extended(&mut self, val: Value, ty: Type) -> Value {
let size = self.size_of(ty);
let lsize = self.size_of(val.ty);
if lsize < size {
let reg = self.loc_to_reg(val.loc, lsize);
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32][lsize.ilog2() as usize];
self.code.encode(op(reg.0, reg.0));
Value {
ty,
loc: Loc::Reg(reg),
}
} else {
val
}
}
fn assign(&mut self, ty: Type, right: Loc, left: Loc) -> Option<Value> {
2024-05-13 06:36:29 -05:00
if left == right {
return Some(Value::VOID);
}
let size = self.size_of(ty);
2024-05-12 13:10:50 -05:00
match size {
2024-05-13 06:36:29 -05:00
0 => {}
..=8 => {
let lhs = self.loc_to_reg(left, size);
match right {
2024-05-13 06:36:29 -05:00
Loc::RegRef(reg) => self.code.encode(instrs::cp(reg, lhs.0)),
Loc::Return => self.code.encode(instrs::cp(1, lhs.0)),
2024-05-12 15:40:28 -05:00
Loc::Deref(reg, off) => {
2024-05-13 06:36:29 -05:00
self.code.encode(instrs::st(lhs.0, reg.0, off, size as _));
2024-05-12 13:10:50 -05:00
self.gpa.free(reg);
}
2024-05-13 06:36:29 -05:00
Loc::DerefRef(reg, off) => {
self.code.encode(instrs::st(lhs.0, reg, off, size as _));
}
2024-05-12 13:10:50 -05:00
Loc::StackRef(offset) | Loc::Stack(offset) => {
2024-05-13 06:36:29 -05:00
self.store_stack(lhs.0, offset, size as _)
2024-05-12 13:10:50 -05:00
}
l => unimplemented!("{:?}", l),
}
self.gpa.free(lhs);
}
2024-05-13 06:36:29 -05:00
..=16 if matches!(right, Loc::Return) => {
let (lhs, loff) = left.to_ptr(size);
self.code.encode(instrs::st(1, lhs.get(), loff, 16));
self.gpa.free_cow(lhs);
}
2024-05-13 06:36:29 -05:00
..=u64::MAX => {
let (rhs, roff) = right.to_ptr(size);
let (lhs, loff) = left.to_ptr(size);
let (rhs, lhs) = (self.to_owned(rhs), self.to_owned(lhs));
2024-05-13 02:55:09 -05:00
2024-05-13 06:36:29 -05:00
self.code.addi64(rhs.0, rhs.0, roff);
self.code.addi64(lhs.0, lhs.0, loff);
2024-05-13 02:55:09 -05:00
self.code
.encode(instrs::bmc(lhs.0, rhs.0, size.try_into().unwrap()));
2024-05-12 13:10:50 -05:00
self.gpa.free(rhs);
self.gpa.free(lhs);
2024-05-12 04:52:58 -05:00
}
}
2024-05-12 13:10:50 -05:00
Some(Value::VOID)
}
fn to_owned(&mut self, loc: CowReg) -> LinReg {
2024-05-12 13:10:50 -05:00
match loc {
CowReg::Lin(reg) => reg,
CowReg::Reg(reg) => {
let new = self.gpa.allocate();
self.code.encode(instrs::cp(new.0, reg));
new
2024-05-12 13:10:50 -05:00
}
}
}
2024-05-12 16:45:28 -05:00
fn declare_fn_label(&mut self, name: Ident, args: Rc<[Type]>, ret: Type) -> LabelId {
self.labels.push(FnLabel {
offset: 0,
name,
args,
ret,
});
self.labels.len() as u32 - 1
2024-05-09 11:16:01 -05:00
}
2024-05-12 16:45:28 -05:00
fn define_fn_label(&mut self, name: Ident) -> Frame {
2024-05-10 14:33:42 -05:00
let offset = self.code.code.len() as u32;
2024-05-12 16:45:28 -05:00
let label = self.get_label(name);
self.labels[label as usize].offset = offset;
2024-05-10 14:33:42 -05:00
Frame {
label,
prev_relocs: self.code.relocs.len(),
offset,
}
2024-05-09 11:16:01 -05:00
}
2024-05-12 04:52:58 -05:00
fn get_label(&self, name: Ident) -> LabelId {
self.labels.iter().position(|l| l.name == name).unwrap() as _
2024-05-09 11:16:01 -05:00
}
2024-05-10 14:33:42 -05:00
fn write_fn_prelude(&mut self, frame: Frame) {
2024-05-12 15:40:28 -05:00
self.temp.push(RET_ADDR, self.gpa.pushed_size());
2024-05-12 04:52:58 -05:00
self.temp.subi64(STACK_PTR, STACK_PTR, self.stack_size);
2024-05-10 14:33:42 -05:00
for reloc in &mut self.code.relocs[frame.prev_relocs..] {
reloc.offset += self.temp.code.len() as u32;
}
2024-05-11 09:04:13 -05:00
for reloc in &mut self.ret_relocs {
reloc.offset += self.temp.code.len() as u32;
}
2024-05-10 14:33:42 -05:00
self.code.code.splice(
frame.offset as usize..frame.offset as usize,
self.temp.code.drain(..),
);
}
2024-05-10 14:33:42 -05:00
fn ret(&mut self) {
2024-05-11 09:04:13 -05:00
self.code
2024-05-12 04:52:58 -05:00
.encode(instrs::addi64(STACK_PTR, STACK_PTR, self.stack_size));
2024-05-12 15:40:28 -05:00
self.code.pop(RET_ADDR, self.gpa.pushed_size());
2024-05-10 14:33:42 -05:00
self.code.ret();
}
pub fn dump(mut self, out: &mut impl std::io::Write) -> std::io::Result<()> {
2024-05-12 04:52:58 -05:00
self.temp.prelude(self.main.unwrap());
2024-05-10 14:33:42 -05:00
self.temp
.relocate(&self.labels, self.temp.code.len() as i64);
self.code.relocate(&self.labels, 0);
out.write_all(&self.temp.code)?;
out.write_all(&self.code.code)
}
2024-05-12 04:52:58 -05:00
fn alloc_pointer(&mut self, ty: Type) -> Type {
let ty = self
.pointers
.iter()
.position(|&p| p == ty)
.unwrap_or_else(|| {
self.pointers.push(ty);
self.pointers.len() - 1
});
TypeKind::Pointer(ty as Type).encode()
}
2024-05-12 06:13:36 -05:00
fn make_loc_owned(&mut self, loc: Loc, ty: Type) -> Loc {
match loc {
Loc::RegRef(rreg) => {
let reg = self.gpa.allocate();
self.code.encode(instrs::cp(reg.0, rreg));
Loc::Reg(reg)
}
Loc::Imm(imm) => {
let reg = self.gpa.allocate();
self.code.encode(instrs::li64(reg.0, imm));
Loc::Reg(reg)
}
2024-05-12 13:10:50 -05:00
Loc::StackRef(off) => {
2024-05-12 06:13:36 -05:00
let size = self.size_of(ty);
2024-05-13 06:36:29 -05:00
let stack = self.alloc_stack(size);
self.assign(ty, Loc::Stack(stack), Loc::StackRef(off));
2024-05-12 06:13:36 -05:00
Loc::Stack(stack)
}
l => l,
}
}
2024-05-12 13:10:50 -05:00
fn pass_arg(&mut self, value: Value, parama: &mut Range<u8>) {
let size = self.size_of(value.ty);
let p = parama.next().unwrap() as Reg;
if size > 16 {
let (Loc::Stack(stack) | Loc::StackRef(stack)) = value.loc else {
2024-05-12 16:19:45 -05:00
todo!("expected stack location, got {:?}", value.loc);
2024-05-12 13:10:50 -05:00
};
2024-05-13 06:36:29 -05:00
self.code.addi64(p, STACK_PTR, stack);
2024-05-13 02:38:33 -05:00
return;
2024-05-12 13:10:50 -05:00
}
match value.loc {
Loc::Reg(reg) => {
self.code.encode(instrs::cp(p, reg.0));
self.gpa.free(reg);
}
Loc::RegRef(reg) => {
self.code.encode(instrs::cp(p, reg));
}
Loc::Return => todo!(),
2024-05-12 15:40:28 -05:00
Loc::Deref(reg, off) => {
self.code.encode(instrs::ld(p, reg.0, off, size as _));
2024-05-12 13:10:50 -05:00
self.gpa.free(reg);
}
2024-05-12 15:40:28 -05:00
Loc::DerefRef(reg, off) => {
self.code.encode(instrs::ld(p, reg, off, size as _));
2024-05-12 13:10:50 -05:00
}
Loc::Imm(imm) => {
self.code.encode(instrs::li64(p, imm));
}
Loc::Stack(stack) | Loc::StackRef(stack) => {
self.load_stack(p, stack, size as _);
self.load_stack(parama.next().unwrap(), stack + 8, size as _);
}
}
}
fn load_arg(&mut self, ty: Type, parama: &mut Range<u8>) -> Loc {
let size = self.size_of(ty);
match size {
2024-05-13 06:36:29 -05:00
0 => Loc::Imm(0),
..=8 => {
let stack = self.alloc_stack(size as _);
self.store_stack(parama.next().unwrap(), stack, size as _);
2024-05-12 13:10:50 -05:00
Loc::Stack(stack)
}
2024-05-13 06:36:29 -05:00
..=16 => {
let stack = self.alloc_stack(size);
self.store_stack(parama.next().unwrap(), stack, size as _);
parama.next().unwrap();
2024-05-12 13:10:50 -05:00
Loc::Stack(stack)
}
2024-05-13 06:36:29 -05:00
..=u64::MAX => {
2024-05-12 13:10:50 -05:00
let ptr = parama.next().unwrap();
2024-05-13 06:36:29 -05:00
let stack = self.alloc_stack(size);
self.assign(ty, Loc::StackRef(stack), Loc::DerefRef(ptr, 0));
2024-05-12 13:10:50 -05:00
Loc::Stack(stack)
}
}
}
2024-05-12 06:13:36 -05:00
fn ensure_spilled(&mut self, loc: Loc) -> Loc {
match loc {
Loc::Reg(reg) => {
let stack = self.alloc_stack(8);
self.store_stack(reg.0, stack, 8);
self.gpa.free(reg);
Loc::Stack(stack)
}
l => l,
}
}
2024-05-12 16:19:45 -05:00
2024-05-13 06:36:29 -05:00
#[must_use]
fn assert_ty(&self, pos: parser::Pos, ty: Type, expected: Type) -> Type {
if let Some(res) = bt::try_upcast(ty, expected) {
res
} else {
2024-05-12 16:45:28 -05:00
let ty = self.display_ty(ty);
let expected = self.display_ty(expected);
self.report(pos, format_args!("expected {ty}, got {expected}"));
}
}
2024-05-12 16:19:45 -05:00
fn report(&self, pos: parser::Pos, msg: impl std::fmt::Display) -> ! {
let (line, col) = lexer::line_col(self.input, pos);
2024-05-12 17:02:32 -05:00
println!("{}:{}:{}: {}", self.path, line, col, msg);
2024-05-12 16:19:45 -05:00
unreachable!();
}
}
2024-05-12 04:52:58 -05:00
pub struct Value {
ty: Type,
2024-05-10 14:33:42 -05:00
loc: Loc,
}
impl Value {
const VOID: Self = Self {
ty: bt::VOID,
loc: Loc::Imm(0),
};
}
2024-05-13 06:36:29 -05:00
#[derive(Debug, PartialEq, Eq)]
2024-05-12 04:52:58 -05:00
enum Loc {
Reg(LinReg),
RegRef(Reg),
Return,
2024-05-12 15:40:28 -05:00
Deref(LinReg, u64),
DerefRef(Reg, u64),
2024-05-10 14:33:42 -05:00
Imm(u64),
Stack(u64),
2024-05-12 06:13:36 -05:00
StackRef(u64),
}
2024-05-12 04:52:58 -05:00
impl Loc {
fn take_ref(&self) -> Loc {
match self {
Self::Reg(reg) => Self::RegRef(reg.0),
2024-05-12 06:13:36 -05:00
Self::Stack(off) => Self::StackRef(*off),
un => unreachable!("{:?}", un),
2024-05-12 04:52:58 -05:00
}
}
fn to_ptr(self, size: u64) -> (CowReg, u64) {
match self {
Loc::Return if size > 16 => (CowReg::Reg(1), 0),
Loc::Deref(reg, off) => (CowReg::Lin(reg), off),
Loc::DerefRef(reg, off) => (CowReg::Reg(reg), off),
Loc::Stack(offset) | Loc::StackRef(offset) => (CowReg::Reg(STACK_PTR), offset),
l => panic!("expected stack location, got {:?}", l),
}
}
fn ref_to_ptr(&self, size: u64) -> (Reg, u64) {
match *self {
Loc::Return if size > 16 => (1, 0),
Loc::Deref(LinReg(reg), off) => (reg, off),
Loc::DerefRef(reg, off) => (reg, off),
Loc::Stack(offset) | Loc::StackRef(offset) => (STACK_PTR, offset),
ref l => panic!("expected stack location, got {:?}", l),
}
}
fn offset_ref(&self, offset: u64) -> Loc {
match *self {
Self::Deref(LinReg(r), off) => Self::DerefRef(r, off + offset),
Self::DerefRef(r, off) => Self::DerefRef(r, off + offset),
Self::Stack(off) => Self::Stack(off + offset),
Self::StackRef(off) => Self::StackRef(off + offset),
ref un => unreachable!("{:?}", un),
}
}
2024-05-12 04:52:58 -05:00
}
2024-05-09 16:41:59 -05:00
#[cfg(test)]
mod tests {
2024-05-11 15:22:08 -05:00
use crate::{instrs, log};
2024-05-11 09:04:13 -05:00
struct TestMem;
impl hbvm::mem::Memory for TestMem {
#[inline]
unsafe fn load(
&mut self,
addr: hbvm::mem::Address,
target: *mut u8,
count: usize,
) -> Result<(), hbvm::mem::LoadError> {
2024-05-11 15:22:08 -05:00
log::dbg!(
2024-05-11 09:04:13 -05:00
"read: {:x} {} {:?}",
addr.get(),
count,
2024-05-12 13:10:50 -05:00
core::slice::from_raw_parts(addr.get() as *const u8, count)
2024-05-11 09:04:13 -05:00
.iter()
.rev()
.skip_while(|&&b| b == 0)
.map(|&b| format!("{:02x}", b))
.collect::<String>()
);
unsafe { core::ptr::copy(addr.get() as *const u8, target, count) }
Ok(())
}
#[inline]
unsafe fn store(
&mut self,
addr: hbvm::mem::Address,
source: *const u8,
count: usize,
) -> Result<(), hbvm::mem::StoreError> {
2024-05-12 13:10:50 -05:00
log::dbg!(
"write: {:x} {} {:?}",
addr.get(),
count,
core::slice::from_raw_parts(source, count)
.iter()
.rev()
.skip_while(|&&b| b == 0)
.map(|&b| format!("{:02x}", b))
.collect::<String>()
);
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
Ok(())
}
#[inline]
unsafe fn prog_read<T: Copy>(&mut self, addr: hbvm::mem::Address) -> T {
2024-05-11 15:22:08 -05:00
log::dbg!(
2024-05-11 09:04:13 -05:00
"read-typed: {:x} {} {:?}",
addr.get(),
std::any::type_name::<T>(),
if core::mem::size_of::<T>() == 1 {
instrs::NAMES[std::ptr::read(addr.get() as *const u8) as usize].to_string()
} else {
core::slice::from_raw_parts(addr.get() as *const u8, core::mem::size_of::<T>())
.iter()
.map(|&b| format!("{:02x}", b))
.collect::<String>()
}
);
unsafe { core::ptr::read(addr.get() as *const T) }
}
}
2024-05-09 16:41:59 -05:00
fn generate(input: &'static str, output: &mut String) {
2024-05-12 17:02:32 -05:00
let path = "test";
let arena = crate::parser::Arena::default();
2024-05-13 02:38:33 -05:00
let mut parser = super::parser::Parser::new(&arena);
let exprs = parser.file(input, path);
2024-05-12 17:02:32 -05:00
let mut codegen = super::Codegen::default();
codegen.file(path, input.as_bytes(), &exprs);
2024-05-10 14:33:42 -05:00
let mut out = Vec::new();
codegen.dump(&mut out).unwrap();
2024-05-10 14:33:42 -05:00
use std::fmt::Write;
2024-05-12 04:52:58 -05:00
let mut stack = [0_u64; 128];
2024-05-10 14:33:42 -05:00
let mut vm = unsafe {
hbvm::Vm::<TestMem, 0>::new(TestMem, hbvm::mem::Address::new(out.as_ptr() as u64))
};
vm.write_reg(
super::STACK_PTR,
unsafe { stack.as_mut_ptr().add(stack.len()) } as u64,
);
2024-05-10 14:33:42 -05:00
let stat = loop {
match vm.run() {
Ok(hbvm::VmRunOk::End) => break Ok(()),
Ok(ev) => writeln!(output, "ev: {:?}", ev).unwrap(),
Err(e) => break Err(e),
}
};
2024-05-12 15:40:28 -05:00
writeln!(output, "code size: {}", out.len()).unwrap();
2024-05-11 09:04:13 -05:00
writeln!(output, "ret: {:?}", vm.read_reg(1).0).unwrap();
2024-05-10 14:33:42 -05:00
writeln!(output, "status: {:?}", stat).unwrap();
2024-05-09 11:16:01 -05:00
}
2024-05-09 16:41:59 -05:00
crate::run_tests! { generate:
example => include_str!("../examples/main_fn.hb");
2024-05-10 15:54:12 -05:00
arithmetic => include_str!("../examples/arithmetic.hb");
variables => include_str!("../examples/variables.hb");
2024-05-11 09:04:13 -05:00
functions => include_str!("../examples/functions.hb");
if_statements => include_str!("../examples/if_statement.hb");
2024-05-11 11:16:27 -05:00
loops => include_str!("../examples/loops.hb");
2024-05-12 04:52:58 -05:00
fb_driver => include_str!("../examples/fb_driver.hb");
pointers => include_str!("../examples/pointers.hb");
2024-05-12 05:16:40 -05:00
structs => include_str!("../examples/structs.hb");
2024-05-13 06:36:29 -05:00
different_types => include_str!("../examples/different_types.hb");
2024-05-09 11:16:01 -05:00
}
}