asm output, random fixes

This commit is contained in:
2025-04-07 19:42:40 -04:00
parent f57af3b2b5
commit cb9a366f43
18 changed files with 266 additions and 117 deletions

View File

@@ -5,6 +5,8 @@ struct Test {
fn start() {
println("Helld!");
print_hex(rem(10, 7));
println("");
println("Hello World!!!!!");
thinger();
let x = 3;
@@ -14,7 +16,7 @@ fn start() {
println("before:");
x = 0;
loop {
if not(lt(x, 5)) {
if not(lt(x, 10)) {
break;
};
println("RAAAAA");
@@ -28,7 +30,11 @@ fn start() {
};
arger("a", "b", "c");
let z = sub(test.a, 10);
exit(add(mul(sub(add(5, test.b), 1), 3), z));
// exit(add(mul(sub(add(5, test.b), 1), 3), z));
print("test: 0x");
print_hex(31);
println("");
exit(0);
}
fn structer(test: Test) {
@@ -48,7 +54,7 @@ fn println(msg: slice<8>) {
}
fn print(msg: slice<8>) {
asm (a1 = msg) {
asm (a1 = &msg) {
ld a2, 8, a1
ld a1, 0, a1
li a0, 1
@@ -57,58 +63,89 @@ fn print(msg: slice<8>) {
}
}
fn add(a: 64, b: 64) -> 64 {
let c: 64 = 0;
asm (t0 = a, t1 = b, a0 = c) {
ld t0, 0, t0
ld t1, 0, t1
add t0, t0, t1
sd t0, 0, a0
fn print_hex(x: 64) {
let i = 32;
loop {
i = sub(i, 4);
let c = and(shr(x, i), 15);
if gt(c, 9) {
c = add(c, 7);
};
c
c = add(c, 48);
asm (a1 = &c) {
li a2, 1
li a0, 1
li a7, 64
ecall
};
if lt(i, 1) {
break;
};
}
}
fn add(a: 64, b: 64) -> 64 {
asm (t0 = a, t1 = b, out = t0) {
add t0, t0, t1
}
}
fn mul(a: 64, b: 64) -> 64 {
let c: 64 = 0;
asm (t0 = a, t1 = b, a0 = c) {
ld t0, 0, t0
ld t1, 0, t1
asm (t0 = a, t1 = b, out = t0) {
mul t0, t0, t1
sd t0, 0, a0
};
c
}
}
fn div(a: 64, b: 64) -> 64 {
asm (t0 = a, t1 = b, out = t0) {
div t0, t0, t1
}
}
fn sub(a: 64, b: 64) -> 64 {
let c: 64 = 0;
asm (t0 = a, t1 = b, a0 = c) {
ld t0, 0, t0
ld t1, 0, t1
asm (t0 = a, t1 = b, out = t0) {
sub t0, t0, t1
sd t0, 0, a0
};
c
}
}
fn rem(a: 64, b: 64) -> 64 {
asm (t0 = a, t1 = b, out = t0) {
rem t0, t0, t1
}
}
fn shr(a: 64, b: 64) -> 64 {
asm (t0 = a, t1 = b, out = t0) {
srl t0, t0, t1
}
}
fn shl(a: 64, b: 64) -> 64 {
asm (t0 = a, t1 = b, out = t0) {
sll t0, t0, t1
}
}
fn lt(a: 64, b: 64) -> 64 {
let c: 64 = 0;
asm (t0 = a, t1 = b, a0 = c) {
ld t0, 0, t0
ld t1, 0, t1
asm (t0 = a, t1 = b, out = t0) {
slt t0, t0, t1
sd t0, 0, a0
};
c
}
}
fn gt(a: 64, b: 64) -> 64 {
lt(b, a)
}
fn and(a: 64, b: 64) -> 64 {
asm (t0 = a, t1 = b, out = t0) {
and t0, t0, t1
}
}
fn not(a: 64) -> 64 {
let c: 64 = 0;
asm (t0 = a, a0 = c) {
ld t0, 0, t0
asm (t0 = a, out = t0) {
xori t0, t0, 1
sd t0, 0, a0
};
c
}
}
fn arger(a: slice<8>, b: slice<8>, c: slice<8>) {
@@ -119,7 +156,6 @@ fn arger(a: slice<8>, b: slice<8>, c: slice<8>) {
fn exit(status: 64) {
asm (a0 = status) {
ld a0, 0, a0
li a7, 93
ecall
};

13
data/test2.lang Normal file
View File

@@ -0,0 +1,13 @@
fn start() {
let x = asm(out = t0) {
li t0, 40
};
exit(x);
}
fn exit(status: 64) {
asm (a0 = status) {
li a7, 93
ecall
};
}

View File

@@ -1,7 +1,7 @@
use crate::{
compiler::program::{Addr, Instr, SymTable},
ir::Symbol,
util::LabeledFmt,
util::{Bits32, LabeledFmt},
};
use super::*;
@@ -70,7 +70,11 @@ pub enum LinkerInstruction<R = Reg, S = Symbol> {
}
pub fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction {
opi(op32i::ADD, dest, src, imm)
opi(op32i::ADD, dest, src, imm.to_u())
}
pub fn ori(dest: Reg, src: Reg, imm: Bits32<11, 0>) -> RawInstruction {
opi(op32i::OR, dest, src, imm)
}
impl Instr for LinkerInstruction {
@@ -89,7 +93,7 @@ impl Instr for LinkerInstruction {
src1,
src2,
} => opr(*op, *funct, *dest, *src1, *src2),
Self::OpImm { op, dest, src, imm } => opi(*op, *dest, *src, BitsI32::new(*imm)),
Self::OpImm { op, dest, src, imm } => opi(*op, *dest, *src, BitsI32::new(*imm).to_u()),
Self::OpImmF7 {
op,
funct,
@@ -113,8 +117,17 @@ impl Instr for LinkerInstruction {
Self::La { dest, src } => {
if let Some(addr) = sym_map.get(*src) {
let offset = addr.val() as i32 - pos.val() as i32;
data.extend(auipc(*dest, BitsI32::new(0)).to_le_bytes());
addi(*dest, *dest, BitsI32::new(offset))
let sign = offset.signum();
let mut lower = offset % 0x1000;
let mut upper = offset - lower;
if (((lower >> 11) & 1) == 1) ^ (sign == -1) {
let add = sign << 12;
upper += add;
lower = offset - upper;
}
assert!(upper + (lower << 20 >> 20) == offset);
data.extend(auipc(*dest, BitsI32::new(upper)).to_le_bytes());
addi(*dest, *dest, BitsI32::new(lower))
} else {
data.extend_from_slice(&[0; 2 * 4]);
return Some(*src);

View File

@@ -106,7 +106,10 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
s,
);
}
IRI::Ref { dest, src } => todo!(),
IRI::Ref { dest, src } => {
v.push(LI::addi(t0, sp, stack[src]));
v.push(LI::sd(t0, stack[dest], sp));
}
IRI::LoadAddr { dest, offset, src } => {
v.extend([
LI::La {
@@ -142,9 +145,13 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
}
v.push(LI::Call(*f));
}
IRI::AsmBlock { args, instructions } => {
for (reg, var) in args {
v.push(LI::addi(*reg, sp, stack[var]));
IRI::AsmBlock {
inputs,
outputs,
instructions,
} => {
for (reg, var) in inputs {
v.push(LI::ld(*reg, stack[var], sp));
}
fn r(rr: RegRef) -> Reg {
match rr {
@@ -223,6 +230,9 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram<LI> {
AI::Branch { .. } => todo!(),
}
}
for (reg, var) in outputs {
v.push(LI::sd(*reg, stack[var], sp));
}
}
IRI::Ret { src } => {
let Some(rva) = stack_rva else {

View File

@@ -80,8 +80,8 @@ pub const fn j_type(imm: Bits32<20, 1>, rd: Reg, opcode: u32) -> I {
pub fn opr(op: Funct3, funct: Funct7, dest: Reg, src1: Reg, src2: Reg) -> I {
r_type(funct, src2, src1, op, dest, OP)
}
pub fn opi(op: Funct3, dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction {
i_type(imm.to_u(), src, op, dest, IMM_OP)
pub fn opi(op: Funct3, dest: Reg, src: Reg, imm: Bits32<11, 0>) -> RawInstruction {
i_type(imm, src, op, dest, IMM_OP)
}
pub fn opif7(op: Funct3, funct: Funct7, dest: Reg, src: Reg, imm: BitsI32<4, 0>) -> I {
i_type(

View File

@@ -1,3 +1,5 @@
use crate::compiler::arch::riscv::Reg;
use super::VarID;
#[derive(Clone)]
@@ -11,3 +13,4 @@ pub enum RegRef {
Var(VarID),
Reg(String),
}

View File

@@ -42,7 +42,8 @@ pub enum IRLInstruction {
},
AsmBlock {
instructions: Vec<RV64Instruction>,
args: Vec<(Reg, VarID)>,
inputs: Vec<(Reg, VarID)>,
outputs: Vec<(Reg, VarID)>,
},
Ret {
src: VarID,
@@ -56,3 +57,4 @@ pub enum IRLInstruction {
},
Mark(Symbol),
}

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap;
use crate::ir::{IRUFunction, IRUInstrInst, Size, SymbolSpace};
use crate::ir::{AsmBlockArgType, IRUFunction, IRUInstrInst, Size, SymbolSpace};
use super::{
IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, SymbolSpaceBuilder, Type,
@@ -161,9 +161,21 @@ impl<'a> IRLFunctionBuilder<'a> {
});
}
IRUInstruction::AsmBlock { instructions, args } => {
let mut inputs = Vec::new();
let mut outputs = Vec::new();
for a in args {
match a.ty {
AsmBlockArgType::In => inputs.push((a.reg, a.var.id)),
AsmBlockArgType::Out => {
self.alloc_stack(a.var.id)?;
outputs.push((a.reg, a.var.id));
}
}
}
self.instrs.push(IRLInstruction::AsmBlock {
instructions: instructions.clone(),
args: args.iter().cloned().map(|(r, v)| (r, v.id)).collect(),
inputs,
outputs,
})
}
IRUInstruction::Ret { src } => self.instrs.push(IRLInstruction::Ret { src: src.id }),

View File

@@ -39,11 +39,7 @@ pub struct DataDef {
pub label: String,
}
#[derive(Debug, Clone, Copy)]
pub enum Origin {
Builtin,
File(FileSpan),
}
pub type Origin = FileSpan;
impl FnDef {
pub fn ty(&self) -> Type {

View File

@@ -45,7 +45,7 @@ pub enum IRUInstruction {
},
AsmBlock {
instructions: Vec<RV64Instruction>,
args: Vec<(Reg, VarInst)>,
args: Vec<AsmBlockArg>,
},
Ret {
src: VarInst,
@@ -64,6 +64,19 @@ pub enum IRUInstruction {
Break,
}
#[derive(Debug)]
pub struct AsmBlockArg {
pub var: VarInst,
pub reg: Reg,
pub ty: AsmBlockArgType,
}
#[derive(Debug)]
pub enum AsmBlockArgType {
In,
Out,
}
impl std::fmt::Debug for IRUInstruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
@@ -77,7 +90,9 @@ impl std::fmt::Debug for IRUInstruction {
f: func,
args,
} => write!(f, "{dest:?} <- {func:?}({args:?})")?,
Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}")?,
Self::AsmBlock { args, instructions } => {
write!(f, "asm {args:?} {instructions:#?}")?
}
Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?,
Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}")?,
Self::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}")?,

View File

@@ -1,7 +1,4 @@
use std::{
collections::HashMap,
fmt::Debug,
};
use std::{collections::HashMap, fmt::Debug};
use crate::common::FileSpan;
@@ -98,10 +95,10 @@ impl IRUProgram {
pub fn size_of_var(&self, var: VarID) -> Option<Size> {
self.size_of_type(&self.var_defs[var.0].ty)
}
pub fn temp_var(&mut self, origin: FileSpan, ty: Type) -> VarInst {
pub fn temp_var(&mut self, origin: Origin, ty: Type) -> VarInst {
let v = self.def_var(VarDef {
name: format!("temp{}", self.temp),
origin: super::Origin::File(origin),
origin,
ty,
});
self.temp += 1;

View File

@@ -1,12 +1,19 @@
// TODO: move this into ir, not parser
use super::{IRUInstrInst, IRUInstruction, IRUProgram, Type};
use crate::common::{CompilerMsg, CompilerOutput};
use crate::common::{CompilerMsg, CompilerOutput, FileSpan};
impl IRUProgram {
pub fn validate(&self) -> CompilerOutput {
let mut output = CompilerOutput::new();
for (f, fd) in self.fns.iter().flatten().zip(&self.fn_defs) {
self.validate_fn(&f.instructions, &fd.ret, &mut output, false);
self.validate_fn(
&f.instructions,
fd.origin,
&fd.ret,
&mut output,
true,
false,
);
}
output
}
@@ -14,10 +21,13 @@ impl IRUProgram {
pub fn validate_fn(
&self,
instructions: &[IRUInstrInst],
origin: FileSpan,
ret: &Type,
output: &mut CompilerOutput,
needs_ret: bool,
breakable: bool,
) {
let mut no_ret = true;
for i in instructions {
match &i.i {
IRUInstruction::Mv { dest, src } => {
@@ -25,7 +35,9 @@ impl IRUProgram {
let src = self.get_var(src.id);
output.check_assign(self, &src.ty, &dest.ty, i.span);
}
IRUInstruction::Ref { dest, src } => todo!(),
IRUInstruction::Ref { dest, src } => {
// TODO
}
IRUInstruction::LoadData { dest, src } => {
let dest = self.get_var(dest.id);
let src = self.get_data(*src);
@@ -64,6 +76,7 @@ impl IRUProgram {
IRUInstruction::Ret { src } => {
let srcty = &self.get_var(src.id).ty;
output.check_assign(self, srcty, ret, src.span);
no_ret = false;
}
IRUInstruction::Construct { dest, fields } => {
let dest_def = self.get_var(dest.id);
@@ -120,10 +133,10 @@ impl IRUProgram {
IRUInstruction::If { cond, body } => {
let cond = self.get_var(cond.id);
output.check_assign(self, &cond.ty, &Type::Bits(64), i.span);
self.validate_fn(body, ret, output, breakable);
self.validate_fn(body, origin, ret, output, false, breakable);
}
IRUInstruction::Loop { body } => {
self.validate_fn(body, ret, output, true);
self.validate_fn(body, origin, ret, output, false, true);
}
IRUInstruction::Break => {
if !breakable {
@@ -136,5 +149,14 @@ impl IRUProgram {
}
}
}
if needs_ret && no_ret && *ret != Type::Unit {
output.err(CompilerMsg {
msg: format!(
"Function implicitly returns () at the end, must return {}",
self.type_name(ret)
),
spans: vec![origin],
});
}
}
}

View File

@@ -1,9 +1,14 @@
use crate::{
compiler::arch::riscv::Reg,
ir::{arch::riscv64::RV64Instruction, IRUInstruction, VarInst},
ir::{
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, IRUInstruction, Type, VarInst,
},
parser::PAsmBlockArg,
};
use super::{FnLowerCtx, FnLowerable, PAsmBlock, PAsmBlockArg, PInstruction};
use super::{FnLowerCtx, FnLowerable, PAsmBlock, PInstruction, PUAsmBlockArg};
type PLAsmBlockArg = PAsmBlockArg<Reg, VarInst>;
impl FnLowerable for PInstruction {
type Output = RV64Instruction;
@@ -14,9 +19,35 @@ impl FnLowerable for PInstruction {
}
impl FnLowerable for PAsmBlock {
type Output = ();
type Output = VarInst;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
let mut args = Vec::new();
let mut output = None;
for a in &self.args {
if let Some(a) = a.lower(ctx) {
match a {
PAsmBlockArg::In { reg, var } => args.push(AsmBlockArg {
reg,
var,
ty: AsmBlockArgType::In,
}),
PAsmBlockArg::Out { reg } => {
if output.is_some() {
ctx.err("cannot evaluate to more than one register".to_string());
continue;
}
let var = ctx.temp(Type::Bits(64));
args.push(AsmBlockArg {
var,
reg,
ty: AsmBlockArgType::Out,
});
output = Some(var)
}
}
}
}
let block = IRUInstruction::AsmBlock {
instructions: {
let mut v = Vec::new();
@@ -27,27 +58,25 @@ impl FnLowerable for PAsmBlock {
}
v
},
args: {
let mut v = Vec::new();
for a in &self.args {
if let Some(a) = a.lower(ctx) {
v.push(a);
}
}
v
},
args,
};
ctx.push(block);
Some(())
output
}
}
impl FnLowerable for PAsmBlockArg {
type Output = (Reg, VarInst);
impl FnLowerable for PUAsmBlockArg {
type Output = PLAsmBlockArg;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
let var = ctx.get_var(&self.var)?;
let reg = Reg::from_ident(&self.reg, ctx)?;
Some((reg, var))
Some(match self {
PAsmBlockArg::In { reg, var } => PLAsmBlockArg::In {
reg: Reg::from_ident(reg, ctx)?,
var: var.as_ref()?.lower(ctx)?,
},
PAsmBlockArg::Out { reg } => PLAsmBlockArg::Out {
reg: Reg::from_ident(reg, ctx)?,
},
})
}
}

View File

@@ -3,11 +3,7 @@ use crate::ir::{IRUProgram, Origin, Type, VarDef};
use super::{CompilerMsg, CompilerOutput, FileSpan, Node, PType, PVarDef};
impl Node<PVarDef> {
pub fn lower(
&self,
program: &mut IRUProgram,
output: &mut CompilerOutput,
) -> Option<VarDef> {
pub fn lower(&self, program: &mut IRUProgram, output: &mut CompilerOutput) -> Option<VarDef> {
let s = self.as_ref()?;
let name = s.name.as_ref()?.to_string();
let ty = match &s.ty {
@@ -17,7 +13,7 @@ impl Node<PVarDef> {
Some(VarDef {
name,
ty,
origin: Origin::File(self.span),
origin: self.span,
})
}
}

View File

@@ -1,6 +1,6 @@
use super::{func::FnLowerCtx, FnLowerable, PExpr, UnaryOp};
use crate::{
ir::{DataDef, IRUInstruction, Origin, Type, VarInst},
ir::{DataDef, IRUInstruction, Type, VarInst},
parser::PInfixOp,
};
@@ -15,7 +15,7 @@ impl FnLowerable for PExpr {
let src = ctx.program.def_data(
DataDef {
ty: Type::Bits(8).arr(data.len() as u32),
origin: Origin::File(l.span),
origin: l.span,
label: format!("string \"{}\"", s.replace("\n", "\\n")),
},
data,
@@ -29,7 +29,7 @@ impl FnLowerable for PExpr {
let src = ctx.program.def_data(
DataDef {
ty,
origin: Origin::File(l.span),
origin: l.span,
label: format!("char '{c}'"),
},
c.to_string().as_bytes().to_vec(),
@@ -44,7 +44,7 @@ impl FnLowerable for PExpr {
let src = ctx.program.def_data(
DataDef {
ty,
origin: Origin::File(l.span),
origin: l.span,
label: format!("num {n:?}"),
},
n.whole.parse::<i64>().unwrap().to_le_bytes().to_vec(),
@@ -131,10 +131,7 @@ impl FnLowerable for PExpr {
}
}
PExpr::Block(b) => b.lower(ctx)?,
PExpr::AsmBlock(b) => {
b.lower(ctx);
return None;
}
PExpr::AsmBlock(b) => b.lower(ctx)?,
PExpr::Call(e, args) => {
let fe = e.lower(ctx)?;
let mut nargs = Vec::new();

View File

@@ -31,7 +31,7 @@ impl PFunction {
.map(|a| {
a.lower(map, output).unwrap_or(VarDef {
name: "{error}".to_string(),
origin: Origin::File(a.span),
origin: a.span,
ty: Type::Error,
})
})
@@ -42,7 +42,7 @@ impl PFunction {
};
Some(map.def_fn(FnDef {
name: name.to_string(),
origin: Origin::File(self.header.span),
origin: self.header.span,
args,
ret,
}))

View File

@@ -81,7 +81,7 @@ impl PStruct {
};
p.def_type(StructDef {
name: self.name.as_ref()?.to_string(),
origin: Origin::File(span),
origin: span,
size: offset,
fields,
});

View File

@@ -1,17 +1,19 @@
use super::{
util::parse_list, PIdent, Node, Parsable, ParseResult, PInstruction, ParserCtx, Symbol,
util::parse_list, Node, PExpr, PIdent, PInstruction, Parsable, ParseResult, ParserCtx, Symbol,
};
pub struct PAsmBlock {
pub instructions: Vec<Node<PInstruction>>,
pub args: Vec<Node<PAsmBlockArg>>,
pub args: Vec<Node<PUAsmBlockArg>>,
}
pub struct PAsmBlockArg {
pub reg: Node<PIdent>,
pub var: Node<PIdent>,
pub enum PAsmBlockArg<R, V> {
In { reg: R, var: V },
Out { reg: R },
}
pub type PUAsmBlockArg = PAsmBlockArg<Node<PIdent>, Node<PExpr>>;
impl Parsable for PAsmBlock {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let args = if ctx.expect_peek()?.is_symbol(Symbol::OpenParen) {
@@ -34,12 +36,18 @@ impl Parsable for PAsmBlock {
}
}
impl Parsable for PAsmBlockArg {
impl Parsable for PUAsmBlockArg {
fn parse(ctx: &mut ParserCtx) -> ParseResult<Self> {
let reg = ctx.parse::<PIdent>()?;
ParseResult::Ok(if reg.inner.as_ref().is_some_and(|s| s.0 == "out") {
ctx.expect_sym(Symbol::Equals)?;
let reg = ctx.parse()?;
Self::Out { reg }
} else {
ctx.expect_sym(Symbol::Equals)?;
let var = ctx.parse()?;
ParseResult::Ok(Self { reg, var })
Self::In { reg, var }
})
}
}