adding different-sized integers
This commit is contained in:
parent
b28baa86f7
commit
7cca9a3683
35
hblang/examples/different_types.hb
Normal file
35
hblang/examples/different_types.hb
Normal 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;
|
||||||
|
}
|
|
@ -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,8 +209,25 @@ 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) {
|
||||||
self.encode(instrs::addi64(dest, src, imm.wrapping_neg()));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, func: LabelId) {
|
fn call(&mut self, func: LabelId) {
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 220
|
code size: 209
|
||||||
ret: 1
|
ret: 1
|
||||||
status: Ok(())
|
status: Ok(())
|
3
hblang/tests/codegen_tests_different_types.txt
Normal file
3
hblang/tests/codegen_tests_different_types.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
code size: 336
|
||||||
|
ret: 512
|
||||||
|
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 107
|
code size: 96
|
||||||
ret: 1
|
ret: 1
|
||||||
status: Ok(())
|
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 1114
|
code size: 1081
|
||||||
ret: 0
|
ret: 0
|
||||||
status: Ok(())
|
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 399
|
code size: 388
|
||||||
ret: 33
|
ret: 33
|
||||||
status: Ok(())
|
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 364
|
code size: 353
|
||||||
ret: 55
|
ret: 55
|
||||||
status: Ok(())
|
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 469
|
code size: 458
|
||||||
ret: 55
|
ret: 55
|
||||||
status: Ok(())
|
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 339
|
code size: 331
|
||||||
ret: 0
|
ret: 0
|
||||||
status: Ok(())
|
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
||||||
code size: 596
|
code size: 544
|
||||||
ret: 3
|
ret: 3
|
||||||
status: Ok(())
|
status: Ok(())
|
Loading…
Reference in a new issue