diff --git a/hblang/README.md b/hblang/README.md index 7252607..0d7bdee 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -239,7 +239,7 @@ main := fn(): int { align_of_Type_in_bytes := @alignof(foo.Type) hardcoded_pointer := @as(^u8, @bitcast(10)) ecall_that_returns_int := @eca(int, 1, foo.Type.(10, 20), 5, 6) - return 0 + return @inline(foo.foo) } // in module: foo.hb @@ -248,6 +248,8 @@ Type := struct { brah: int, blah: int, } + +foo := fn(): int return 0 ``` - `@use()`: imports a module based of string, the string is passed to a loader that can be customized, default loader uses following syntax: @@ -257,7 +259,8 @@ Type := struct { - `@intcast()`: needs to be used when conversion of `@TypeOf()` would loose precision (widening of integers is implicit) - `@sizeof(), @alignof()`: I think explaining this would insult your intelligence - `@bitcast()`: tell compiler to assume `@TypeOf()` is whatever is inferred, so long as size and alignment did not change -- `@eca(, ...)`: invoke `eca` instruction, where `` is the type this will return and `...` are arguments passed to the call +- `@eca(, ...)`: invoke `eca` instruction, where `` is the type this will return and `...` are arguments passed to the call +- `@inline(, ...)`: equivalent to `(...)` but function is guaranteed to inline, compiler will otherwise never inline #### c_strings ```hb @@ -324,6 +327,17 @@ pass := fn(arr: ^[int; 3]): int { } ``` +#### inline +```hb +main := fn(): int { + return @inline(foo, 1, 2, 3) - 6 +} + +foo := fn(a: int, b: int, c: int): int { + return a + b + c +} +``` + ### Incomplete Examples #### comptime_pointers diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index a2f0112..583599a 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -8,8 +8,7 @@ use { parser::{self, find_symbol, idfl, CtorField, Expr, ExprRef, FileId, Pos}, HashMap, }, - core::panic, - std::{ops::Range, rc::Rc}, + std::{ops::Range, rc::Rc, usize}, }; type Offset = u32; @@ -635,6 +634,13 @@ impl Loc { Self::Rt { stack: Some(stack), reg: reg::STACK_PTR.into(), derefed: true, offset: 0 } } + fn get_reg(&self) -> reg::Id { + match self { + Self::Rt { reg, .. } => reg.as_ref(), + _ => unreachable!(), + } + } + fn reg(reg: impl Into) -> Self { let reg = reg.into(); assert!(reg.get() != 0); @@ -779,6 +785,15 @@ impl ItemCtx { debug_assert!(reloc.offset < len); } + if let Some(last_ret) = self.ret_relocs.last() + && last_ret.offset as usize == output.code.len() - 5 + { + output.code.truncate(output.code.len() - 5); + self.ret_relocs.pop(); + } + + let len = output.code.len() as Offset; + self.stack.finalize_leaked(); for rel in self.stack_relocs.drain(..) { rel.apply_stack_offset(&mut output.code[base as usize..], &self.stack) @@ -1046,6 +1061,24 @@ pub struct Snapshot { strings: usize, } +impl Snapshot { + fn _sub(&mut self, other: &Self) { + self.code -= other.code; + self.string_data -= other.string_data; + self.funcs -= other.funcs; + self.globals -= other.globals; + self.strings -= other.strings; + } + + fn _add(&mut self, other: &Self) { + self.code += other.code; + self.string_data += other.string_data; + self.funcs += other.funcs; + self.globals += other.globals; + self.strings += other.strings; + } +} + #[derive(Default)] struct Output { code: Vec, @@ -1348,6 +1381,7 @@ impl Codegen { pub fn generate(&mut self) { self.output.emit_entry_prelude(); self.find_or_declare(0, 0, Err("main"), ""); + self.make_func_reachable(0); self.complete_call_graph_low(); self.link(); } @@ -1474,6 +1508,53 @@ impl Codegen { let val = self.ty(val); Some(Value::ty(self.tys.make_ptr(val))) } + E::Directive { name: "inline", args: [func_ast, args @ ..], .. } => { + let ty::Kind::Func(mut func) = self.ty(func_ast).expand() else { + self.report(func_ast.pos(), "first argument of inline needs to be a function"); + }; + + let fuc = self.tys.funcs[func as usize]; + let fast = self.files[fuc.file as usize].clone(); + let E::BinOp { right: &E::Closure { args: cargs, body, .. }, .. } = + fuc.expr.get(&fast).unwrap() + else { + unreachable!(); + }; + + let scope = self.ci.vars.len(); + let sig = self.compute_signature(&mut func, func_ast.pos(), args)?; + + if scope == self.ci.vars.len() { + for ((arg, ty), carg) in + args.iter().zip(sig.args.view(&self.tys.args).to_owned()).zip(cargs) + { + let loc = self.expr_ctx(arg, Ctx::default().with_ty(ty))?.loc; + self.ci.vars.push(Variable { id: carg.id, value: Value { ty, loc } }); + } + } + + let ret_reloc_base = self.ci.ret_relocs.len(); + + let loc = self.alloc_ret(sig.ret, ctx, false); + let prev_ret_reg = std::mem::replace(&mut self.ci.ret_reg, loc.get_reg()); + self.expr(body); + self.ci.ret_reg = prev_ret_reg; + + //if let Some(last_ret) = self.ci.ret_relocs.last() + // && last_ret.offset as usize + self.ci.snap.code == self.output.code.len() - 5 + //{ + // self.output.code.truncate(self.output.code.len() - 5); + // self.ci.ret_relocs.pop(); + //} + let len = self.output.code.len() as u32; + for mut rel in self.ci.ret_relocs.drain(ret_reloc_base..) { + rel.offset += self.ci.snap.code as u32; + rel.shifted = true; + rel.apply_jump(&mut self.output.code, len); + } + + return Some(Value { ty: sig.ret, loc }); + } E::Directive { name: "TypeOf", args: [expr], .. } => { let snap = self.output.snap(); let value = self.expr(expr).unwrap(); @@ -1495,7 +1576,7 @@ impl Codegen { self.ci.free_loc(value); } - let loc = self.alloc_ret(ty, ctx); + let loc = self.alloc_ret(ty, ctx, false); self.output.emit(eca()); @@ -1797,72 +1878,27 @@ impl Codegen { self.report(fast.pos(), "can't call this, maybe in the future"); }; + // TODO: this will be usefull but not now + let scope = self.ci.vars.len(); + //let mut snap = self.output.snap(); + //snap.sub(&self.ci.snap); + //let prev_stack_rel = self.ci.stack_relocs.len(); + //let prev_ret_rel = self.ci.ret_relocs.len(); + let sig = self.compute_signature(&mut func, expr.pos(), args)?; + //self.ci.ret_relocs.truncate(prev_ret_rel); + //self.ci.stack_relocs.truncate(prev_stack_rel); + //snap.add(&self.ci.snap); + //self.output.trunc(&snap); + self.ci.vars.truncate(scope); + let fuc = self.tys.funcs[func as usize]; let ast = self.files[fuc.file as usize].clone(); - let E::BinOp { right: &E::Closure { args: cargs, ret, .. }, .. } = + let E::BinOp { right: &E::Closure { args: cargs, .. }, .. } = fuc.expr.get(&ast).unwrap() else { unreachable!(); }; - let sig = if let Some(sig) = fuc.sig { - sig - } else { - let scope = self.ci.vars.len(); - let arg_base = self.tys.args.len(); - - for (arg, carg) in args.iter().zip(cargs) { - let ty = self.ty(&carg.ty); - log::dbg!("arg: {}", self.ty_display(ty)); - self.tys.args.push(ty); - let sym = parser::find_symbol(&ast.symbols, carg.id); - let loc = if sym.flags & idfl::COMPTIME == 0 { - // FIXME: could fuck us - Loc::default() - } else { - debug_assert_eq!( - ty, - ty::TYPE.into(), - "TODO: we dont support anything except type generics" - ); - let arg = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; - self.tys.args.push(arg.loc.to_ty().unwrap()); - arg.loc - }; - - self.ci.vars.push(Variable { id: carg.id, value: Value { ty, loc } }); - } - - let args = self.pack_args(expr.pos(), arg_base); - let ret = self.ty(ret); - self.ci.vars.truncate(scope); - - let sym = SymKey { - file: !args.repr(), - ident: ty::Kind::Func(func).compress().repr(), - }; - let ct = || { - let func_id = self.tys.funcs.len(); - self.tys.funcs.push(Func { - file: fuc.file, - offset: task::id(func_id), - sig: Some(Sig { args, ret }), - expr: fuc.expr, - }); - - self.tasks.push(Some(FTask { - // FIXME: this will fuck us - file: fuc.file, - id: func_id as _, - })); - - ty::Kind::Func(func_id as _).compress() - }; - func = self.tys.syms.entry(sym).or_insert_with(ct).expand().inner(); - - Sig { args, ret } - }; - let mut parama = self.tys.parama(sig.ret); let mut values = Vec::with_capacity(args.len()); let mut sig_args = sig.args.range(); @@ -1888,7 +1924,7 @@ impl Codegen { log::dbg!("call ctx: {ctx:?}"); - let loc = self.alloc_ret(sig.ret, ctx); + let loc = self.alloc_ret(sig.ret, ctx, false); if should_momize { self.output.write_trap(trap::Trap::MomizedCall(trap::MomizedCall { func })); @@ -1897,6 +1933,7 @@ impl Codegen { let reloc = Reloc::new(self.local_offset(), 3, 4); self.output.funcs.push((func, reloc)); self.output.emit(jal(RET_ADDR, ZERO, 0)); + self.make_func_reachable(func); if should_momize { self.output.emit(tx()); @@ -2177,6 +2214,63 @@ impl Codegen { }) } + fn compute_signature(&mut self, func: &mut ty::Func, pos: Pos, args: &[Expr]) -> Option { + let fuc = self.tys.funcs[*func as usize]; + let fast = self.files[fuc.file as usize].clone(); + let Expr::BinOp { right: &Expr::Closure { args: cargs, ret, .. }, .. } = + fuc.expr.get(&fast).unwrap() + else { + unreachable!(); + }; + + Some(if let Some(sig) = fuc.sig { + sig + } else { + let arg_base = self.tys.args.len(); + + for (arg, carg) in args.iter().zip(cargs) { + let ty = self.ty(&carg.ty); + log::dbg!("arg: {}", self.ty_display(ty)); + self.tys.args.push(ty); + let sym = parser::find_symbol(&fast.symbols, carg.id); + let loc = if sym.flags & idfl::COMPTIME == 0 { + // FIXME: could fuck us + Loc::default() + } else { + debug_assert_eq!( + ty, + ty::TYPE.into(), + "TODO: we dont support anything except type generics" + ); + let arg = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; + self.tys.args.push(arg.loc.to_ty().unwrap()); + arg.loc + }; + + self.ci.vars.push(Variable { id: carg.id, value: Value { ty, loc } }); + } + + let args = self.pack_args(pos, arg_base); + let ret = self.ty(ret); + + let sym = SymKey { file: !args.repr(), ident: ty::Kind::Func(*func).compress().repr() }; + let ct = || { + let func_id = self.tys.funcs.len(); + self.tys.funcs.push(Func { + file: fuc.file, + offset: u32::MAX, + sig: Some(Sig { args, ret }), + expr: fuc.expr, + }); + + ty::Kind::Func(func_id as _).compress() + }; + *func = self.tys.syms.entry(sym).or_insert_with(ct).expand().inner(); + + Sig { args, ret } + }) + } + fn has_ct(&self, expr: &Expr) -> bool { expr.has_ct(&self.cfile().symbols) } @@ -2625,7 +2719,7 @@ impl Codegen { Value { ty: ret.into(), loc: Loc::reg(1) } } - fn alloc_ret(&mut self, ret: ty::Id, ctx: Ctx) -> Loc { + fn alloc_ret(&mut self, ret: ty::Id, ctx: Ctx, custom_ret_reg: bool) -> Loc { let size = self.tys.size_of(ret); if size == 0 { debug_assert!(ctx.loc.is_none(), "{}", self.ty_display(ret)); @@ -2638,9 +2732,10 @@ impl Codegen { match size { 0 => Loc::default(), + 1..=8 if custom_ret_reg => Loc::reg(self.ci.regs.allocate()), 1..=8 => Loc::reg(1), 9..=16 => Loc::stack(self.ci.stack.allocate(size)), - _ => { + 17.. => { let loc = ctx.loc.unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(size))); let Loc::Rt { reg, stack, offset, .. } = &loc else { todo!("old man with the beard looks at the sky scared"); @@ -2955,10 +3050,8 @@ impl Codegen { op: TokenKind::Decl, right: &Expr::Closure { pos, args, ret, .. }, } => { - let mut push_task = false; let func = Func { file, - offset: task::id(self.tasks.len()), sig: 'b: { let arg_base = self.tys.args.len(); for arg in args { @@ -2971,8 +3064,6 @@ impl Codegen { self.tys.args.push(ty); } - push_task = true; - let args = self.pack_args(pos, arg_base); log::dbg!("eval ret"); let ret = self.ty(ret); @@ -2984,12 +3075,10 @@ impl Codegen { debug_assert!(refr.get(&f).is_some()); refr }, + offset: u32::MAX, }; let id = self.tys.funcs.len() as _; - if push_task { - self.tasks.push(Some(FTask { file, id })); - } self.tys.funcs.push(func); ty::Kind::Func(id) @@ -3023,6 +3112,14 @@ impl Codegen { sym } + fn make_func_reachable(&mut self, func: ty::Func) { + let fuc = &mut self.tys.funcs[func as usize]; + if fuc.offset == u32::MAX { + fuc.offset = task::id(self.tasks.len() as _); + self.tasks.push(Some(FTask { file: fuc.file, id: func })); + } + } + fn generate_global(&mut self, expr: &Expr) -> Global { self.output.emit_prelude(); @@ -3364,5 +3461,6 @@ mod tests { comptime_min_reg_leak => README; // structs_in_registers => README; comptime_function_from_another_file => README; + inline => README; } } diff --git a/hblang/tests/codegen_tests_arithmetic.txt b/hblang/tests/codegen_tests_arithmetic.txt index 8773e5c..572eddf 100644 --- a/hblang/tests/codegen_tests_arithmetic.txt +++ b/hblang/tests/codegen_tests_arithmetic.txt @@ -1,3 +1,3 @@ -code size: 193 +code size: 188 ret: 1 status: Ok(()) diff --git a/hblang/tests/codegen_tests_arrays.txt b/hblang/tests/codegen_tests_arrays.txt index b165b4e..3246da5 100644 --- a/hblang/tests/codegen_tests_arrays.txt +++ b/hblang/tests/codegen_tests_arrays.txt @@ -1,3 +1,3 @@ -code size: 418 +code size: 408 ret: 7 status: Ok(()) diff --git a/hblang/tests/codegen_tests_c_strings.txt b/hblang/tests/codegen_tests_c_strings.txt index ecce9ff..f66f396 100644 --- a/hblang/tests/codegen_tests_c_strings.txt +++ b/hblang/tests/codegen_tests_c_strings.txt @@ -1,3 +1,3 @@ -code size: 313 +code size: 303 ret: 16 status: Ok(()) diff --git a/hblang/tests/codegen_tests_comments.txt b/hblang/tests/codegen_tests_comments.txt index 5bd7713..08da43b 100644 --- a/hblang/tests/codegen_tests_comments.txt +++ b/hblang/tests/codegen_tests_comments.txt @@ -1,3 +1,3 @@ -code size: 153 +code size: 143 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_comptime_function_from_another_file.txt b/hblang/tests/codegen_tests_comptime_function_from_another_file.txt index 18abd15..59c666a 100644 --- a/hblang/tests/codegen_tests_comptime_function_from_another_file.txt +++ b/hblang/tests/codegen_tests_comptime_function_from_another_file.txt @@ -1,3 +1,3 @@ -code size: 255 +code size: 245 ret: 50 status: Ok(()) diff --git a/hblang/tests/codegen_tests_comptime_min_reg_leak.txt b/hblang/tests/codegen_tests_comptime_min_reg_leak.txt index 11b95db..c3bac2f 100644 --- a/hblang/tests/codegen_tests_comptime_min_reg_leak.txt +++ b/hblang/tests/codegen_tests_comptime_min_reg_leak.txt @@ -1,3 +1,3 @@ -code size: 231 +code size: 221 ret: 50 status: Ok(()) diff --git a/hblang/tests/codegen_tests_different_types.txt b/hblang/tests/codegen_tests_different_types.txt index dea1d1d..a28d285 100644 --- a/hblang/tests/codegen_tests_different_types.txt +++ b/hblang/tests/codegen_tests_different_types.txt @@ -1,3 +1,3 @@ -code size: 536 +code size: 531 ret: 512 status: Ok(()) diff --git a/hblang/tests/codegen_tests_fb_driver.txt b/hblang/tests/codegen_tests_fb_driver.txt index 07dad9f..b20e820 100644 --- a/hblang/tests/codegen_tests_fb_driver.txt +++ b/hblang/tests/codegen_tests_fb_driver.txt @@ -1,3 +1,3 @@ -code size: 525 +code size: 505 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_functions.txt b/hblang/tests/codegen_tests_functions.txt index c8c04fe..db32607 100644 --- a/hblang/tests/codegen_tests_functions.txt +++ b/hblang/tests/codegen_tests_functions.txt @@ -1,3 +1,3 @@ -code size: 281 +code size: 266 ret: 33 status: Ok(()) diff --git a/hblang/tests/codegen_tests_generic_functions.txt b/hblang/tests/codegen_tests_generic_functions.txt index 7b0ab0e..4186302 100644 --- a/hblang/tests/codegen_tests_generic_functions.txt +++ b/hblang/tests/codegen_tests_generic_functions.txt @@ -1,3 +1,3 @@ -code size: 296 +code size: 281 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_generic_types.txt b/hblang/tests/codegen_tests_generic_types.txt index 6ec333d..3ac54b1 100644 --- a/hblang/tests/codegen_tests_generic_types.txt +++ b/hblang/tests/codegen_tests_generic_types.txt @@ -1,3 +1,3 @@ -code size: 1504 +code size: 1469 ret: 69 status: Ok(()) diff --git a/hblang/tests/codegen_tests_global_variables.txt b/hblang/tests/codegen_tests_global_variables.txt index d613fdb..9d6a68f 100644 --- a/hblang/tests/codegen_tests_global_variables.txt +++ b/hblang/tests/codegen_tests_global_variables.txt @@ -1,3 +1,3 @@ -code size: 277 +code size: 267 ret: 50 status: Ok(()) diff --git a/hblang/tests/codegen_tests_hex_octal_binary_literals.txt b/hblang/tests/codegen_tests_hex_octal_binary_literals.txt index e3aee3a..9e3f9ab 100644 --- a/hblang/tests/codegen_tests_hex_octal_binary_literals.txt +++ b/hblang/tests/codegen_tests_hex_octal_binary_literals.txt @@ -1,3 +1,3 @@ -code size: 210 +code size: 205 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_if_statements.txt b/hblang/tests/codegen_tests_if_statements.txt index 2ae138d..6d9419a 100644 --- a/hblang/tests/codegen_tests_if_statements.txt +++ b/hblang/tests/codegen_tests_if_statements.txt @@ -1,3 +1,3 @@ -code size: 262 +code size: 252 ret: 55 status: Ok(()) diff --git a/hblang/tests/codegen_tests_inline.txt b/hblang/tests/codegen_tests_inline.txt new file mode 100644 index 0000000..4e788d6 --- /dev/null +++ b/hblang/tests/codegen_tests_inline.txt @@ -0,0 +1,3 @@ +code size: 124 +ret: 0 +status: Ok(()) diff --git a/hblang/tests/codegen_tests_loops.txt b/hblang/tests/codegen_tests_loops.txt index 5f06c4b..9ce8c1b 100644 --- a/hblang/tests/codegen_tests_loops.txt +++ b/hblang/tests/codegen_tests_loops.txt @@ -1,3 +1,3 @@ -code size: 268 +code size: 258 ret: 55 status: Ok(()) diff --git a/hblang/tests/codegen_tests_pointers.txt b/hblang/tests/codegen_tests_pointers.txt index de42326..def5877 100644 --- a/hblang/tests/codegen_tests_pointers.txt +++ b/hblang/tests/codegen_tests_pointers.txt @@ -1,3 +1,3 @@ -code size: 330 +code size: 315 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_sort_something_viredly.txt b/hblang/tests/codegen_tests_sort_something_viredly.txt index 2f8b41a..7c615dd 100644 --- a/hblang/tests/codegen_tests_sort_something_viredly.txt +++ b/hblang/tests/codegen_tests_sort_something_viredly.txt @@ -1,3 +1,3 @@ -code size: 357 +code size: 87 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_struct_operators.txt b/hblang/tests/codegen_tests_struct_operators.txt index 71ebefb..7f383d1 100644 --- a/hblang/tests/codegen_tests_struct_operators.txt +++ b/hblang/tests/codegen_tests_struct_operators.txt @@ -1,3 +1,3 @@ -code size: 786 +code size: 781 ret: 10 status: Ok(()) diff --git a/hblang/tests/codegen_tests_struct_patterns.txt b/hblang/tests/codegen_tests_struct_patterns.txt index 05768a8..6f3db46 100644 --- a/hblang/tests/codegen_tests_struct_patterns.txt +++ b/hblang/tests/codegen_tests_struct_patterns.txt @@ -1,3 +1,3 @@ -code size: 533 +code size: 518 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_struct_return_from_module_function.txt b/hblang/tests/codegen_tests_struct_return_from_module_function.txt index de42326..c08808c 100644 --- a/hblang/tests/codegen_tests_struct_return_from_module_function.txt +++ b/hblang/tests/codegen_tests_struct_return_from_module_function.txt @@ -1,3 +1,3 @@ -code size: 330 +code size: 320 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_structs.txt b/hblang/tests/codegen_tests_structs.txt index 93f83d6..c179894 100644 --- a/hblang/tests/codegen_tests_structs.txt +++ b/hblang/tests/codegen_tests_structs.txt @@ -1,3 +1,3 @@ -code size: 460 +code size: 445 ret: 3 status: Ok(()) diff --git a/hblang/tests/codegen_tests_variables.txt b/hblang/tests/codegen_tests_variables.txt index deb087b..27ddd88 100644 --- a/hblang/tests/codegen_tests_variables.txt +++ b/hblang/tests/codegen_tests_variables.txt @@ -1,3 +1,3 @@ -code size: 116 +code size: 111 ret: 0 status: Ok(())