adding different-sized integers

This commit is contained in:
mlokr 2024-05-13 13:36:29 +02:00
parent b28baa86f7
commit 7cca9a3683
22 changed files with 267 additions and 110 deletions

View file

@ -0,0 +1,35 @@
Color := struct {
r: u8,
g: u8,
b: u8,
a: u8,
}
Point := struct {
x: u32,
y: u32,
}
Pixel := struct {
color: Color,
point: Point,
}
main := fn(): int {
pixel := Pixel.{
color: Color.{
r: 255,
g: 0,
b: 0,
a: 255,
},
point: Point.{
x: 0,
y: 2,
},
};
return pixel.point.x + pixel.point.y + pixel.color.r
+ pixel.color.g + pixel.color.b + pixel.color.a;
}

View file

@ -1,6 +1,6 @@
use std::ops::Range; use std::ops::Range;
use crate::ident::Ident; use crate::ident::{self, Ident};
use { use {
crate::{ crate::{
@ -17,7 +17,7 @@ type Reg = u8;
type MaskElem = u64; type MaskElem = u64;
type Type = u32; type Type = u32;
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
struct LinReg(Reg); struct LinReg(Reg);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -63,31 +63,49 @@ impl Ctx {
pub mod bt { pub mod bt {
use super::*; use super::*;
const fn builtin_type(id: u32) -> Type {
Type::MAX - id
}
macro_rules! builtin_type { macro_rules! builtin_type {
($($name:ident;)*) => {$( ($($name:ident;)*) => {$(
pub const $name: Type = ${index(0)} << 2; pub const $name: Type = ${index(0)};
)*}; )*};
} }
builtin_type! { builtin_type! {
VOID; VOID;
NEVER; NEVER;
INT;
I64;
I32;
I16;
I8; I8;
UINT; I16;
U64; I32;
U32; INT;
U16;
U8; U8;
U16;
U32;
UINT;
BOOL; BOOL;
} }
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,
}
}
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,
})
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -98,10 +116,14 @@ enum TypeKind {
} }
impl TypeKind { impl TypeKind {
const fn from_ty(ty: Type) -> Self { const FLAG_BITS: u32 = 2;
let (flag, index) = (ty & 0b11, ty >> 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);
match flag { match flag {
0 => Self::Builtin(ty), 0 => Self::Builtin(index),
1 => Self::Pointer(index), 1 => Self::Pointer(index),
2 => Self::Struct(index), 2 => Self::Struct(index),
_ => unreachable!(), _ => unreachable!(),
@ -110,11 +132,11 @@ impl TypeKind {
const fn encode(self) -> Type { const fn encode(self) -> Type {
let (index, flag) = match self { let (index, flag) = match self {
Self::Builtin(index) => return index, Self::Builtin(index) => (index, 0),
Self::Pointer(index) => (index, 1), Self::Pointer(index) => (index, 1),
Self::Struct(index) => (index, 2), Self::Struct(index) => (index, 2),
}; };
index << 2 | flag (flag << Self::FLAG_OFFSET) | index
} }
} }
@ -187,9 +209,26 @@ impl Func {
self.encode(instrs::addi64(STACK_PTR, STACK_PTR, size as _)); self.encode(instrs::addi64(STACK_PTR, STACK_PTR, size as _));
} }
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) { fn subi64(&mut self, dest: Reg, src: Reg, imm: u64) {
if self.short_cut_bin_op(dest, src, imm) {
self.encode(instrs::addi64(dest, src, imm.wrapping_neg())); 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));
}
}
fn call(&mut self, func: LabelId) { fn call(&mut self, func: LabelId) {
self.offset(func, 3, 4); self.offset(func, 3, 4);
@ -324,12 +363,10 @@ impl<'a> std::fmt::Display for TypeDisplay<'a> {
TK::Builtin(bt::VOID) => "void", TK::Builtin(bt::VOID) => "void",
TK::Builtin(bt::NEVER) => "never", TK::Builtin(bt::NEVER) => "never",
TK::Builtin(bt::INT) => "int", TK::Builtin(bt::INT) => "int",
TK::Builtin(bt::I64) => "i64",
TK::Builtin(bt::I32) => "i32", TK::Builtin(bt::I32) => "i32",
TK::Builtin(bt::I16) => "i16", TK::Builtin(bt::I16) => "i16",
TK::Builtin(bt::I8) => "i8", TK::Builtin(bt::I8) => "i8",
TK::Builtin(bt::UINT) => "uint", TK::Builtin(bt::UINT) => "uint",
TK::Builtin(bt::U64) => "u64",
TK::Builtin(bt::U32) => "u32", TK::Builtin(bt::U32) => "u32",
TK::Builtin(bt::U16) => "u16", TK::Builtin(bt::U16) => "u16",
TK::Builtin(bt::U8) => "u8", TK::Builtin(bt::U8) => "u8",
@ -478,7 +515,7 @@ impl<'a> Codegen<'a> {
TK::Pointer(_) => 8, TK::Pointer(_) => 8,
TK::Builtin(bt::VOID) => 0, TK::Builtin(bt::VOID) => 0,
TK::Builtin(bt::NEVER) => unreachable!(), TK::Builtin(bt::NEVER) => unreachable!(),
TK::Builtin(bt::INT | bt::I64 | bt::UINT | bt::U64) => 8, TK::Builtin(bt::INT | bt::UINT) => 8,
TK::Builtin(bt::I32 | bt::U32) => 4, TK::Builtin(bt::I32 | bt::U32) => 4,
TK::Builtin(bt::I16 | bt::U16) => 2, TK::Builtin(bt::I16 | bt::U16) => 2,
TK::Builtin(bt::I8 | bt::U8 | bt::BOOL) => 1, TK::Builtin(bt::I8 | bt::U8 | bt::BOOL) => 1,
@ -521,7 +558,7 @@ impl<'a> Codegen<'a> {
self.report(pos, format_args!("field not found: {}", field)); self.report(pos, format_args!("field not found: {}", field));
} }
fn loc_to_reg(&mut self, loc: Loc) -> LinReg { fn loc_to_reg(&mut self, loc: Loc, size: u64) -> LinReg {
match loc { match loc {
Loc::RegRef(rr) => { Loc::RegRef(rr) => {
let reg = self.gpa.allocate(); let reg = self.gpa.allocate();
@ -532,13 +569,14 @@ impl<'a> Codegen<'a> {
Loc::Reg(reg) => reg, Loc::Reg(reg) => reg,
Loc::Deref(dreg, offset) => { Loc::Deref(dreg, offset) => {
let reg = self.gpa.allocate(); let reg = self.gpa.allocate();
self.code.encode(instrs::ld(reg.0, dreg.0, offset, 8)); self.code
.encode(instrs::ld(reg.0, dreg.0, offset, size as _));
self.gpa.free(dreg); self.gpa.free(dreg);
reg reg
} }
Loc::DerefRef(dreg, offset) => { Loc::DerefRef(dreg, offset) => {
let reg = self.gpa.allocate(); let reg = self.gpa.allocate();
self.code.encode(instrs::ld(reg.0, dreg, offset, 8)); self.code.encode(instrs::ld(reg.0, dreg, offset, size as _));
reg reg
} }
Loc::Imm(imm) => { Loc::Imm(imm) => {
@ -548,16 +586,16 @@ impl<'a> Codegen<'a> {
} }
Loc::Stack(offset) | Loc::StackRef(offset) => { Loc::Stack(offset) | Loc::StackRef(offset) => {
let reg = self.gpa.allocate(); let reg = self.gpa.allocate();
self.load_stack(reg.0, offset, 8); self.load_stack(reg.0, offset, size as _);
reg reg
} }
} }
} }
fn alloc_stack(&mut self, size: u32) -> u64 { fn alloc_stack(&mut self, size: u64) -> u64 {
let offset = self.stack_size; let offset = self.stack_size;
log::dbg!("alloc_stack: {} {}", offset, size); log::dbg!("alloc_stack: {} {}", offset, size);
self.stack_size += size as u64; self.stack_size += size;
offset offset
} }
@ -582,10 +620,7 @@ impl<'a> Codegen<'a> {
fn ty(&mut self, expr: &parser::Expr<'a>) -> Type { fn ty(&mut self, expr: &parser::Expr<'a>) -> Type {
match *expr { match *expr {
E::Ident { name: "int", .. } => bt::INT, E::Ident { id, .. } if ident::is_null(id) => id,
E::Ident { name: "bool", .. } => bt::BOOL,
E::Ident { name: "void", .. } => bt::VOID,
E::Ident { name: "never", .. } => bt::NEVER,
E::UnOp { E::UnOp {
op: T::Star, val, .. op: T::Star, val, ..
} => { } => {
@ -618,7 +653,7 @@ impl<'a> Codegen<'a> {
let loc = match ctx { let loc = match ctx {
Ctx::Dest(dest) => dest.loc, Ctx::Dest(dest) => dest.loc,
_ => Loc::Stack(self.alloc_stack(size as u32)), _ => Loc::Stack(self.alloc_stack(size)),
}; };
for (name, field) in fields { for (name, field) in fields {
@ -663,16 +698,16 @@ impl<'a> Codegen<'a> {
let loc = match val.loc { let loc = match val.loc {
Loc::StackRef(off) => { Loc::StackRef(off) => {
let reg = self.gpa.allocate(); let reg = self.gpa.allocate();
self.code.encode(instrs::addi64(reg.0, STACK_PTR, off)); self.code.addi64(reg.0, STACK_PTR, off);
Loc::Reg(reg) Loc::Reg(reg)
} }
Loc::Deref(r, off) => { Loc::Deref(r, off) => {
self.code.encode(instrs::addi64(r.0, r.0, off)); self.code.addi64(r.0, r.0, off);
Loc::Reg(r) Loc::Reg(r)
} }
Loc::DerefRef(r, off) => { Loc::DerefRef(r, off) => {
let reg = self.gpa.allocate(); let reg = self.gpa.allocate();
self.code.encode(instrs::addi64(reg.0, r, off)); self.code.addi64(reg.0, r, off);
Loc::Reg(reg) Loc::Reg(reg)
} }
l => self.report( l => self.report(
@ -691,7 +726,7 @@ impl<'a> Codegen<'a> {
pos, pos,
} => { } => {
let val = self.expr(val)?; let val = self.expr(val)?;
let reg = self.loc_to_reg(val.loc); let reg = self.loc_to_reg(val.loc, self.size_of(val.ty));
match TypeKind::from_ty(val.ty) { match TypeKind::from_ty(val.ty) {
TypeKind::Pointer(ty) => Some(Value { TypeKind::Pointer(ty) => Some(Value {
ty: self.pointers[ty as usize], ty: self.pointers[ty as usize],
@ -726,43 +761,43 @@ impl<'a> Codegen<'a> {
let mut parama = 3..12; let mut parama = 3..12;
for (earg, &ty) in args.iter().zip(fn_label.args.iter()) { for (earg, &ty) in args.iter().zip(fn_label.args.iter()) {
let arg = self.expr_ctx(earg, Ctx::Inferred(ty))?; let arg = self.expr_ctx(earg, Ctx::Inferred(ty))?;
self.assert_ty(earg.pos(), ty, arg.ty); _ = self.assert_ty(earg.pos(), ty, arg.ty);
self.pass_arg(arg, &mut parama); self.pass_arg(arg, &mut parama);
} }
let size = self.size_of(fn_label.ret); let size = self.size_of(fn_label.ret);
let loc = match size { let loc = match size {
0 => Loc::Imm(0), 0 => Loc::Imm(0),
8 => Loc::RegRef(1), ..=8 => Loc::RegRef(1),
16 => match ctx { ..=16 => match ctx {
Ctx::Dest(dest) => dest.loc, Ctx::Dest(dest) => dest.loc,
_ => Loc::Stack(self.alloc_stack(size as u32)), _ => Loc::Stack(self.alloc_stack(size)),
}, },
24..=u64::MAX => { ..=u64::MAX => {
let val = match ctx { let val = match ctx {
Ctx::Dest(dest) => dest.loc, Ctx::Dest(dest) => dest.loc,
_ => Loc::Stack(self.alloc_stack(size as u32)), _ => Loc::Stack(self.alloc_stack(size)),
}; };
let (ptr, off) = val.ref_to_ptr(size); let (ptr, off) = val.ref_to_ptr(size);
self.code.encode(instrs::cp(1, ptr)); self.code.encode(instrs::cp(1, ptr));
self.code.encode(instrs::addi64(1, ptr, off)); self.code.addi64(1, ptr, off);
val val
} }
s => todo!("call return size: {}", s),
}; };
self.code.call(func); self.code.call(func);
match size { match size {
0 => {} 0 => {}
8 => {} ..=8 => {}
16 => { ..=16 => {
if let Loc::Stack(stack) = loc { if let Loc::Stack(stack) = loc {
self.store_stack(1, stack, 16); self.store_stack(1, stack, 16);
} else {
unreachable!()
} }
} }
24..=u64::MAX => {} ..=u64::MAX => {}
s => todo!("call return size: {}", s),
} }
return Some(Value { return Some(Value {
@ -782,7 +817,8 @@ impl<'a> Codegen<'a> {
E::Return { val, pos } => { E::Return { val, pos } => {
if let Some(val) = val { if let Some(val) = val {
let val = self.expr_ctx(val, Ctx::Inferred(self.ret))?; let val = self.expr_ctx(val, Ctx::Inferred(self.ret))?;
self.assert_ty(pos, self.ret, val.ty); 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); self.assign(self.ret, Loc::Return, val.loc);
} }
self.ret_relocs.push(RetReloc { self.ret_relocs.push(RetReloc {
@ -810,7 +846,7 @@ impl<'a> Codegen<'a> {
} => 'b: { } => 'b: {
log::dbg!("if-cond"); log::dbg!("if-cond");
let cond = self.expr_ctx(cond, Ctx::Inferred(bt::BOOL))?; let cond = self.expr_ctx(cond, Ctx::Inferred(bt::BOOL))?;
let reg = self.loc_to_reg(cond.loc); let reg = self.loc_to_reg(cond.loc, 1);
let jump_offset = self.code.code.len() as u32; let jump_offset = self.code.code.len() as u32;
self.code.encode(instrs::jeq(reg.0, 0, 0)); self.code.encode(instrs::jeq(reg.0, 0, 0));
self.gpa.free(reg); self.gpa.free(reg);
@ -903,49 +939,86 @@ impl<'a> Codegen<'a> {
None None
} }
E::BinOp { left, op, right } => { E::BinOp { left, op, right } => {
use instrs as i;
if op == T::Assign { if op == T::Assign {
let left = self.expr(left)?; let left = self.expr(left)?;
let right = self.expr_ctx(right, Ctx::Inferred(left.ty))?; self.expr_ctx(right, Ctx::Dest(left))?;
return self.assign(left.ty, left.loc, right.loc); return Some(Value::VOID);
} }
let left = self.expr(left)?; let left = self.expr(left)?;
let lhs = self.loc_to_reg(left.loc); 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))?; let right = self.expr_ctx(right, Ctx::Inferred(left.ty))?;
let rhs = self.loc_to_reg(right.loc); let rsize = self.size_of(right.ty);
let rhs = self.loc_to_reg(right.loc, rsize);
let op = match op { let ty = self.assert_ty(expr.pos(), left.ty, right.ty);
T::Plus => instrs::add64,
T::Minus => instrs::sub64, let size = self.size_of(ty);
T::Star => instrs::mul64,
T::Le => { let signed = bt::is_signed(ty);
self.code.encode(instrs::cmpu(lhs.0, lhs.0, rhs.0)); let index = size.ilog2() as usize;
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),
],
T::Le | T::Ge => {
let against = if op == T::Le { 1 } else { (-1i64) as _ };
let op = if signed { i::cmps } else { i::cmpu };
self.code.encode(op(lhs.0, lhs.0, rhs.0));
self.gpa.free(rhs); self.gpa.free(rhs);
self.code.encode(instrs::cmpui(lhs.0, lhs.0, 1)); self.code.encode(instrs::cmpui(lhs.0, lhs.0, against));
return Some(Value { return Some(Value {
ty: bt::BOOL, ty: bt::BOOL,
loc: Loc::Reg(lhs), loc: Loc::Reg(lhs),
}); });
} }
T::Eq => { T::Eq | T::Lt | T::Gt => {
self.code.encode(instrs::cmpu(lhs.0, lhs.0, rhs.0)); 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));
self.gpa.free(rhs); self.gpa.free(rhs);
self.code.encode(instrs::cmpui(lhs.0, lhs.0, 0)); self.code.encode(instrs::cmpui(lhs.0, lhs.0, against));
self.code.encode(instrs::not(lhs.0, lhs.0)); self.code.encode(instrs::not(lhs.0, lhs.0));
return Some(Value { return Some(Value {
ty: bt::BOOL, ty: bt::BOOL,
loc: Loc::Reg(lhs), loc: Loc::Reg(lhs),
}); });
} }
T::FSlash => |reg0, reg1, reg2| instrs::diru64(reg0, ZERO, reg1, reg2),
_ => unimplemented!("{:#?}", op), _ => unimplemented!("{:#?}", op),
}; };
self.code.encode(op(lhs.0, lhs.0, rhs.0)); self.code.encode(ops[index](lhs.0, lhs.0, rhs.0));
self.gpa.free(rhs); self.gpa.free(rhs);
let min_size = lsize.min(rsize);
if bt::is_signed(ty) && min_size < size {
let op = [i::sxt8, i::sxt16, i::sxt32][min_size.ilog2() as usize];
self.code.encode(op(lhs.0, lhs.0));
}
Some(Value { Some(Value {
ty: left.ty, ty,
loc: Loc::Reg(lhs), loc: Loc::Reg(lhs),
}) })
} }
@ -960,45 +1033,68 @@ impl<'a> Codegen<'a> {
} }
} }
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> { fn assign(&mut self, ty: Type, right: Loc, left: Loc) -> Option<Value> {
if left == right {
return Some(Value::VOID);
}
let size = self.size_of(ty); let size = self.size_of(ty);
match size { match size {
8 => { 0 => {}
let lhs = self.loc_to_reg(left); ..=8 => {
let lhs = self.loc_to_reg(left, size);
match right { match right {
Loc::RegRef(reg) => self.code.encode(instrs::cp(reg, lhs.0)),
Loc::Return => self.code.encode(instrs::cp(1, lhs.0)),
Loc::Deref(reg, off) => { Loc::Deref(reg, off) => {
self.code.encode(instrs::st(lhs.0, reg.0, off, 8)); self.code.encode(instrs::st(lhs.0, reg.0, off, size as _));
self.gpa.free(reg); self.gpa.free(reg);
} }
Loc::RegRef(reg) => self.code.encode(instrs::cp(reg, lhs.0)), Loc::DerefRef(reg, off) => {
Loc::StackRef(offset) | Loc::Stack(offset) => { self.code.encode(instrs::st(lhs.0, reg, off, size as _));
self.store_stack(lhs.0, offset, 8) }
Loc::StackRef(offset) | Loc::Stack(offset) => {
self.store_stack(lhs.0, offset, size as _)
} }
Loc::Return => self.code.encode(instrs::cp(1, lhs.0)),
l => unimplemented!("{:?}", l), l => unimplemented!("{:?}", l),
} }
self.gpa.free(lhs); self.gpa.free(lhs);
} }
16 if matches!(right, Loc::Return) => { ..=16 if matches!(right, Loc::Return) => {
let (lhs, loff) = left.to_ptr(size); let (lhs, loff) = left.to_ptr(size);
self.code.encode(instrs::st(1, lhs.get(), loff, 16)); self.code.encode(instrs::st(1, lhs.get(), loff, 16));
self.gpa.free_cow(lhs); self.gpa.free_cow(lhs);
} }
16..=u64::MAX => { ..=u64::MAX => {
let (rhs, roff) = right.to_ptr(size); let (rhs, roff) = right.to_ptr(size);
let (lhs, loff) = left.to_ptr(size); let (lhs, loff) = left.to_ptr(size);
let (rhs, lhs) = (self.to_owned(rhs), self.to_owned(lhs)); let (rhs, lhs) = (self.to_owned(rhs), self.to_owned(lhs));
self.code.encode(instrs::addi64(rhs.0, rhs.0, roff)); self.code.addi64(rhs.0, rhs.0, roff);
self.code.encode(instrs::addi64(lhs.0, lhs.0, loff)); self.code.addi64(lhs.0, lhs.0, loff);
self.code self.code
.encode(instrs::bmc(lhs.0, rhs.0, size.try_into().unwrap())); .encode(instrs::bmc(lhs.0, rhs.0, size.try_into().unwrap()));
self.gpa.free(rhs); self.gpa.free(rhs);
self.gpa.free(lhs); self.gpa.free(lhs);
} }
s => unimplemented!("size: {}", s),
} }
Some(Value::VOID) Some(Value::VOID)
@ -1102,7 +1198,7 @@ impl<'a> Codegen<'a> {
} }
Loc::StackRef(off) => { Loc::StackRef(off) => {
let size = self.size_of(ty); let size = self.size_of(ty);
let stack = self.alloc_stack(size as u32); let stack = self.alloc_stack(size);
self.assign(ty, Loc::Stack(stack), Loc::StackRef(off)); self.assign(ty, Loc::Stack(stack), Loc::StackRef(off));
Loc::Stack(stack) Loc::Stack(stack)
} }
@ -1118,7 +1214,7 @@ impl<'a> Codegen<'a> {
let (Loc::Stack(stack) | Loc::StackRef(stack)) = value.loc else { let (Loc::Stack(stack) | Loc::StackRef(stack)) = value.loc else {
todo!("expected stack location, got {:?}", value.loc); todo!("expected stack location, got {:?}", value.loc);
}; };
self.code.encode(instrs::addi64(p, STACK_PTR, stack)); self.code.addi64(p, STACK_PTR, stack);
return; return;
} }
@ -1151,24 +1247,24 @@ impl<'a> Codegen<'a> {
fn load_arg(&mut self, ty: Type, parama: &mut Range<u8>) -> Loc { fn load_arg(&mut self, ty: Type, parama: &mut Range<u8>) -> Loc {
let size = self.size_of(ty); let size = self.size_of(ty);
match size { match size {
8 => { 0 => Loc::Imm(0),
let stack = self.alloc_stack(8); ..=8 => {
self.store_stack(parama.next().unwrap(), stack, 8); let stack = self.alloc_stack(size as _);
self.store_stack(parama.next().unwrap(), stack, size as _);
Loc::Stack(stack) Loc::Stack(stack)
} }
16 => { ..=16 => {
let stack = self.alloc_stack(16); let stack = self.alloc_stack(size);
self.store_stack(parama.next().unwrap(), stack, 8); self.store_stack(parama.next().unwrap(), stack, size as _);
self.store_stack(parama.next().unwrap(), stack + 8, 8); parama.next().unwrap();
Loc::Stack(stack) Loc::Stack(stack)
} }
24..=u64::MAX => { ..=u64::MAX => {
let ptr = parama.next().unwrap(); let ptr = parama.next().unwrap();
let stack = self.alloc_stack(size as u32); let stack = self.alloc_stack(size);
self.assign(ty, Loc::StackRef(stack), Loc::DerefRef(ptr, 0)); self.assign(ty, Loc::StackRef(stack), Loc::DerefRef(ptr, 0));
Loc::Stack(stack) Loc::Stack(stack)
} }
blah => todo!("{blah:?}"),
} }
} }
@ -1184,8 +1280,11 @@ impl<'a> Codegen<'a> {
} }
} }
fn assert_ty(&self, pos: parser::Pos, ty: Type, expected: Type) { #[must_use]
if ty != expected { fn assert_ty(&self, pos: parser::Pos, ty: Type, expected: Type) -> Type {
if let Some(res) = bt::try_upcast(ty, expected) {
res
} else {
let ty = self.display_ty(ty); let ty = self.display_ty(ty);
let expected = self.display_ty(expected); let expected = self.display_ty(expected);
self.report(pos, format_args!("expected {ty}, got {expected}")); self.report(pos, format_args!("expected {ty}, got {expected}"));
@ -1211,7 +1310,7 @@ impl Value {
}; };
} }
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
enum Loc { enum Loc {
Reg(LinReg), Reg(LinReg),
RegRef(Reg), RegRef(Reg),
@ -1378,5 +1477,6 @@ mod tests {
fb_driver => include_str!("../examples/fb_driver.hb"); fb_driver => include_str!("../examples/fb_driver.hb");
pointers => include_str!("../examples/pointers.hb"); pointers => include_str!("../examples/pointers.hb");
structs => include_str!("../examples/structs.hb"); structs => include_str!("../examples/structs.hb");
different_types => include_str!("../examples/different_types.hb");
} }
} }

View file

@ -6,13 +6,17 @@ pub fn len(ident: Ident) -> u32 {
ident & ((1 << LEN_BITS) - 1) ident & ((1 << LEN_BITS) - 1)
} }
pub fn is_null(ident: Ident) -> bool {
(ident >> LEN_BITS) == 0
}
pub fn pos(ident: Ident) -> u32 { pub fn pos(ident: Ident) -> u32 {
ident >> LEN_BITS (ident >> LEN_BITS).saturating_sub(1)
} }
pub fn new(pos: u32, len: u32) -> Ident { pub fn new(pos: u32, len: u32) -> Ident {
debug_assert!(len < (1 << LEN_BITS)); debug_assert!(len < (1 << LEN_BITS));
(pos << LEN_BITS) | len ((pos + 1) << LEN_BITS) | len
} }
pub fn range(ident: Ident) -> std::ops::Range<usize> { pub fn range(ident: Ident) -> std::ops::Range<usize> {

View file

@ -109,6 +109,9 @@ gen_token_kind! {
Assign = "=", Assign = "=",
#[prec = 21] #[prec = 21]
Le = "<=", Le = "<=",
Ge = ">=",
Lt = "<",
Gt = ">",
Eq = "==", Eq = "==",
#[prec = 22] #[prec = 22]
Amp = "&", Amp = "&",
@ -216,6 +219,9 @@ impl<'a> Iterator for Lexer<'a> {
b'=' if self.advance_if(b'=') => T::Eq, b'=' if self.advance_if(b'=') => T::Eq,
b'=' => T::Assign, b'=' => T::Assign,
b'<' if self.advance_if(b'=') => T::Le, b'<' if self.advance_if(b'=') => T::Le,
b'<' => T::Lt,
b'>' if self.advance_if(b'=') => T::Ge,
b'>' => T::Gt,
b'+' => T::Plus, b'+' => T::Plus,
b'-' => T::Minus, b'-' => T::Minus,
b'*' => T::Star, b'*' => T::Star,

View file

@ -80,7 +80,7 @@ impl<'a, 'b> Parser<'a, 'b> {
break; break;
}; };
if prec < min_prec { if prec <= min_prec {
break; break;
} }
@ -100,7 +100,13 @@ impl<'a, 'b> Parser<'a, 'b> {
fn try_resolve_builtin(name: &str) -> Option<Ident> { fn try_resolve_builtin(name: &str) -> Option<Ident> {
// FIXME: we actually do this the second time in the codegen // FIXME: we actually do this the second time in the codegen
Some(match name { Some(match name {
"int" => bt::INT, "int" | "i64" => bt::INT,
"i8" => bt::I8,
"i16" => bt::I16,
"i32" => bt::I32,
"u8" => bt::U8,
"u16" => bt::U16,
"uint" | "u32" => bt::U32,
"bool" => bt::BOOL, "bool" => bt::BOOL,
"void" => bt::VOID, "void" => bt::VOID,
"never" => bt::NEVER, "never" => bt::NEVER,

View file

@ -12,7 +12,10 @@ pub fn run_test(name: &'static str, input: &'static str, test: fn(&'static str,
test(input, &mut output); test(input, &mut output);
let mut root = PathBuf::from(std::env::var("PT_TEST_ROOT").unwrap_or("tests".to_string())); let mut root = PathBuf::from(std::env::var("PT_TEST_ROOT").unwrap_or("tests".to_string()));
root.push(name.replace("::", "_")); root.push(
name.replace("::", "_")
.replace(concat!(env!("CARGO_PKG_NAME"), "_"), ""),
);
root.set_extension("txt"); root.set_extension("txt");
let expected = std::fs::read_to_string(&root).unwrap_or_default(); let expected = std::fs::read_to_string(&root).unwrap_or_default();

View file

@ -1,3 +1,3 @@
code size: 220 code size: 209
ret: 1 ret: 1
status: Ok(()) status: Ok(())

View file

@ -0,0 +1,3 @@
code size: 336
ret: 512
status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 107 code size: 96
ret: 1 ret: 1
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 1114 code size: 1081
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 399 code size: 388
ret: 33 ret: 33
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 364 code size: 353
ret: 55 ret: 55
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 469 code size: 458
ret: 55 ret: 55
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 339 code size: 331
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,3 @@
code size: 596 code size: 544
ret: 3 ret: 3
status: Ok(()) status: Ok(())