adding inline functions

This commit is contained in:
Jakub Doka 2024-11-10 19:35:48 +01:00
parent 654005eea2
commit c61efc3933
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
3 changed files with 193 additions and 214 deletions

View file

@ -446,9 +446,12 @@ pass := fn(arr: ^[uint; 3]): uint {
#### inline #### inline
```hb ```hb
main := fn(): uint { main := fn(): uint {
return @inline(foo, 1, 2, 3) - 6 return @inline(foo, 1, 2, 3) - bar(3)
} }
// only for functions with no control flow (if, loop)
$bar := fn(a: uint): uint return a * 2
gb := 0 gb := 0
foo := fn(a: uint, b: uint, c: uint): uint { foo := fn(a: uint, b: uint, c: uint): uint {

View file

@ -794,6 +794,7 @@ struct Func {
base: Option<ty::Func>, base: Option<ty::Func>,
expr: ExprRef, expr: ExprRef,
sig: Option<Sig>, sig: Option<Sig>,
is_inline: bool,
comp_state: [CompState; 2], comp_state: [CompState; 2],
} }
@ -1052,20 +1053,28 @@ trait TypeParser {
if let Some(&ty) = tys.syms.get(SymKey::Decl(file, name), &tys.ins) { if let Some(&ty) = tys.syms.get(SymKey::Decl(file, name), &tys.ins) {
ty ty
} else { } else {
let ty = left let (is_ct, ty) = left
.find_pattern_path(name, right, |right, is_ct| { .find_pattern_path(name, right, |right, is_ct| {
if is_ct { (
self.tys() is_ct,
.ins if is_ct && !matches!(right, Expr::Closure { .. }) {
.consts self.tys()
.push(Const { ast: ExprRef::new(expr), name, file }) .ins
.into() .consts
} else { .push(Const { ast: ExprRef::new(expr), name, file })
self.parse_ty(file, right, Some(name), files) .into()
} } else {
self.parse_ty(file, right, Some(name), files)
},
)
}) })
.unwrap_or_else(|_| unreachable!()); .unwrap_or_else(|_| unreachable!());
let tys = self.tys(); let tys = self.tys();
if let ty::Kind::Func(f) = ty.expand()
&& is_ct
{
tys.ins.funcs[f].is_inline = true;
}
tys.syms.insert(SymKey::Decl(file, name), ty, &tys.ins); tys.syms.insert(SymKey::Decl(file, name), ty, &tys.ins);
ty ty
} }

View file

@ -3192,210 +3192,9 @@ impl<'a> Codegen<'a> {
alt_value.or(Some(Value::new(self.ci.ctrl.get()).ty(ty))) alt_value.or(Some(Value::new(self.ci.ctrl.get()).ty(ty)))
} }
Expr::Call { func, args, .. } => { Expr::Call { func, args, .. } => self.gen_call(func, args, false),
let ty = self.ty(func);
let ty::Kind::Func(mut fu) = ty.expand() else {
self.report(
func.pos(),
fa!("compiler cant (yet) call '{}'", self.ty_display(ty)),
);
return Value::NEVER;
};
let Some(sig) = self.compute_signature(&mut fu, func.pos(), args) else {
return Value::NEVER;
};
self.make_func_reachable(fu);
let fuc = &self.tys.ins.funcs[fu];
let ast = &self.files[fuc.file.index()];
let &Expr::Closure { args: cargs, .. } = fuc.expr.get(ast) else { unreachable!() };
if args.len() != cargs.len() {
self.report(
func.pos(),
fa!(
"expected {} function argumenr{}, got {}",
cargs.len(),
if cargs.len() == 1 { "" } else { "s" },
args.len()
),
);
}
let mut inps = Vc::from([NEVER]);
let mut tys = sig.args.args();
let mut cargs = cargs.iter();
let mut args = args.iter();
let mut clobbered_aliases = BitSet::default();
while let Some(ty) = tys.next(self.tys) {
let carg = cargs.next().unwrap();
let Some(arg) = args.next() else { break };
let Arg::Value(ty) = ty else { continue };
let mut value = self.raw_expr_ctx(arg, Ctx::default().with_ty(ty))?;
self.strip_var(&mut value);
debug_assert_ne!(self.ci.nodes[value.id].kind, Kind::Stre);
self.assert_ty(arg.pos(), &mut value, ty, fa!("argument {}", carg.name));
self.strip_ptr(&mut value);
self.add_clobbers(value, &mut clobbered_aliases);
self.ci.nodes.lock(value.id);
inps.push(value.id);
}
for &n in inps.iter().skip(1) {
self.ci.nodes.unlock(n);
}
self.append_clobbers(&mut inps, &mut clobbered_aliases);
let alt_value = match sig.ret.loc(self.tys) {
Loc::Reg => None,
Loc::Stack => {
let stck = self.new_stack(func.pos(), sig.ret);
clobbered_aliases.set(self.ci.nodes.aclass_index(stck).0 as _);
inps.push(stck);
Some(Value::ptr(stck).ty(sig.ret))
}
};
inps[0] = self.ci.ctrl.get();
self.ci.ctrl.set(
self.ci.nodes.new_node_nop(
sig.ret,
Kind::Call { func: fu, args: sig.args },
inps,
),
&mut self.ci.nodes,
);
self.add_clobber_stores(clobbered_aliases);
alt_value.or(Some(Value::new(self.ci.ctrl.get()).ty(sig.ret)))
}
Expr::Directive { name: "inline", args: [func, args @ ..], .. } => { Expr::Directive { name: "inline", args: [func, args @ ..], .. } => {
let ty = self.ty(func); self.gen_call(func, args, true)
let ty::Kind::Func(mut fu) = ty.expand() else {
self.report(
func.pos(),
fa!(
"first argument to @inline should be a function,
but here its '{}'",
self.ty_display(ty)
),
);
return Value::NEVER;
};
let Some(sig) = self.compute_signature(&mut fu, func.pos(), args) else {
return Value::NEVER;
};
let Func { expr, file, .. } = self.tys.ins.funcs[fu];
let ast = &self.files[file.index()];
let &Expr::Closure { args: cargs, body, .. } = expr.get(ast) else {
unreachable!()
};
if args.len() != cargs.len() {
self.report(
func.pos(),
fa!(
"expected {} inline function argumenr{}, got {}",
cargs.len(),
if cargs.len() == 1 { "" } else { "s" },
args.len()
),
);
}
let mut tys = sig.args.args();
let mut args = args.iter();
let mut cargs = cargs.iter();
let var_base = self.ci.scope.vars.len();
let aclass_base = self.ci.scope.aclasses.len();
while let Some(aty) = tys.next(self.tys) {
let carg = cargs.next().unwrap();
let Some(arg) = args.next() else { break };
match aty {
Arg::Type(id) => {
self.ci.scope.vars.push(Variable::new(
carg.id,
id,
false,
NEVER,
&mut self.ci.nodes,
));
}
Arg::Value(ty) => {
let mut value = self.raw_expr_ctx(arg, Ctx::default().with_ty(ty))?;
self.strip_var(&mut value);
debug_assert_ne!(self.ci.nodes[value.id].kind, Kind::Stre);
debug_assert_ne!(value.id, 0);
self.assert_ty(
arg.pos(),
&mut value,
ty,
fa!("argument {}", carg.name),
);
self.ci.scope.vars.push(Variable::new(
carg.id,
ty,
value.ptr,
value.id,
&mut self.ci.nodes,
));
}
}
}
let prev_var_base = mem::replace(&mut self.ci.inline_var_base, var_base);
let prev_aclass_base = mem::replace(&mut self.ci.inline_aclass_base, aclass_base);
let prev_ret = self.ci.ret.replace(sig.ret);
let prev_inline_ret = self.ci.inline_ret.take();
let prev_file = mem::replace(&mut self.ci.file, file);
self.ci.inline_depth += 1;
if self.expr(body).is_some() {
if sig.ret == ty::Id::VOID {
self.expr(&Expr::Return { pos: body.pos(), val: None });
} else {
self.report(
body.pos(),
"expected all paths in the fucntion to return \
or the return type to be 'void'",
);
}
}
self.ci.ret = prev_ret;
self.ci.file = prev_file;
self.ci.inline_depth -= 1;
self.ci.inline_var_base = prev_var_base;
self.ci.inline_aclass_base = prev_aclass_base;
for var in self.ci.scope.vars.drain(var_base..) {
var.remove(&mut self.ci.nodes);
}
for var in self.ci.scope.aclasses.drain(aclass_base..) {
var.remove(&mut self.ci.nodes);
}
mem::replace(&mut self.ci.inline_ret, prev_inline_ret).map(|(v, ctrl, scope)| {
self.ci.nodes.unlock(v.id);
self.ci.scope.clear(&mut self.ci.nodes);
self.ci.scope = scope;
self.ci.scope.vars.drain(var_base..).for_each(|v| v.remove(&mut self.ci.nodes));
self.ci
.scope
.aclasses
.drain(aclass_base..)
.for_each(|v| v.remove(&mut self.ci.nodes));
mem::replace(&mut self.ci.ctrl, ctrl).remove(&mut self.ci.nodes);
v
})
} }
Expr::Tupl { pos, ty, fields, .. } => { Expr::Tupl { pos, ty, fields, .. } => {
ctx.ty = ty ctx.ty = ty
@ -3881,6 +3680,174 @@ impl<'a> Codegen<'a> {
} }
} }
fn gen_call(&mut self, func: &Expr, args: &[Expr], inline: bool) -> Option<Value> {
let ty = self.ty(func);
let ty::Kind::Func(mut fu) = ty.expand() else {
self.report(func.pos(), fa!("compiler cant (yet) call '{}'", self.ty_display(ty)));
return Value::NEVER;
};
let Some(sig) = self.compute_signature(&mut fu, func.pos(), args) else {
return Value::NEVER;
};
self.make_func_reachable(fu);
let Func { expr, file, is_inline, .. } = self.tys.ins.funcs[fu];
let ast = &self.files[file.index()];
let &Expr::Closure { args: cargs, body, .. } = expr.get(ast) else { unreachable!() };
if args.len() != cargs.len() {
self.report(
func.pos(),
fa!(
"expected {} function argumenr{}, got {}",
cargs.len(),
if cargs.len() == 1 { "" } else { "s" },
args.len()
),
);
}
if inline && is_inline {
self.report(
func.pos(),
"function is declared as inline so this @inline directive only reduces readability",
);
}
if is_inline || inline {
let mut tys = sig.args.args();
let mut args = args.iter();
let mut cargs = cargs.iter();
let var_base = self.ci.scope.vars.len();
let aclass_base = self.ci.scope.aclasses.len();
while let Some(aty) = tys.next(self.tys) {
let carg = cargs.next().unwrap();
let Some(arg) = args.next() else { break };
match aty {
Arg::Type(id) => {
self.ci.scope.vars.push(Variable::new(
carg.id,
id,
false,
NEVER,
&mut self.ci.nodes,
));
}
Arg::Value(ty) => {
let mut value = self.raw_expr_ctx(arg, Ctx::default().with_ty(ty))?;
self.strip_var(&mut value);
debug_assert_ne!(self.ci.nodes[value.id].kind, Kind::Stre);
debug_assert_ne!(value.id, 0);
self.assert_ty(arg.pos(), &mut value, ty, fa!("argument {}", carg.name));
self.ci.scope.vars.push(Variable::new(
carg.id,
ty,
value.ptr,
value.id,
&mut self.ci.nodes,
));
}
}
}
let prev_var_base = mem::replace(&mut self.ci.inline_var_base, var_base);
let prev_aclass_base = mem::replace(&mut self.ci.inline_aclass_base, aclass_base);
let prev_inline_ret = self.ci.inline_ret.take();
self.ci.inline_depth += 1;
let prev_ret = self.ci.ret.replace(sig.ret);
let prev_file = mem::replace(&mut self.ci.file, file);
let prev_ctrl = self.ci.ctrl.get();
if self.expr(body).is_some() {
if sig.ret == ty::Id::VOID {
self.expr(&Expr::Return { pos: body.pos(), val: None });
} else {
self.report(
body.pos(),
"expected all paths in the fucntion to return \
or the return type to be 'void'",
);
}
}
self.ci.ret = prev_ret;
self.ci.file = prev_file;
self.ci.inline_depth -= 1;
self.ci.inline_var_base = prev_var_base;
self.ci.inline_aclass_base = prev_aclass_base;
for var in self.ci.scope.vars.drain(var_base..) {
var.remove(&mut self.ci.nodes);
}
for var in self.ci.scope.aclasses.drain(aclass_base..) {
var.remove(&mut self.ci.nodes);
}
let (v, ctrl, scope) = mem::replace(&mut self.ci.inline_ret, prev_inline_ret)?;
if is_inline && ctrl.get() != prev_ctrl {
self.report(body.pos(), "function is makred inline but it contains controlflow");
}
self.ci.nodes.unlock(v.id);
self.ci.scope.clear(&mut self.ci.nodes);
self.ci.scope = scope;
self.ci.scope.vars.drain(var_base..).for_each(|v| v.remove(&mut self.ci.nodes));
self.ci.scope.aclasses.drain(aclass_base..).for_each(|v| v.remove(&mut self.ci.nodes));
mem::replace(&mut self.ci.ctrl, ctrl).remove(&mut self.ci.nodes);
Some(v)
} else {
let mut inps = Vc::from([NEVER]);
let mut tys = sig.args.args();
let mut cargs = cargs.iter();
let mut args = args.iter();
let mut clobbered_aliases = BitSet::default();
while let Some(ty) = tys.next(self.tys) {
let carg = cargs.next().unwrap();
let Some(arg) = args.next() else { break };
let Arg::Value(ty) = ty else { continue };
let mut value = self.raw_expr_ctx(arg, Ctx::default().with_ty(ty))?;
self.strip_var(&mut value);
debug_assert_ne!(self.ci.nodes[value.id].kind, Kind::Stre);
self.assert_ty(arg.pos(), &mut value, ty, fa!("argument {}", carg.name));
self.strip_ptr(&mut value);
self.add_clobbers(value, &mut clobbered_aliases);
self.ci.nodes.lock(value.id);
inps.push(value.id);
}
for &n in inps.iter().skip(1) {
self.ci.nodes.unlock(n);
}
self.append_clobbers(&mut inps, &mut clobbered_aliases);
let alt_value = match sig.ret.loc(self.tys) {
Loc::Reg => None,
Loc::Stack => {
let stck = self.new_stack(func.pos(), sig.ret);
clobbered_aliases.set(self.ci.nodes.aclass_index(stck).0 as _);
inps.push(stck);
Some(Value::ptr(stck).ty(sig.ret))
}
};
inps[0] = self.ci.ctrl.get();
self.ci.ctrl.set(
self.ci.nodes.new_node_nop(sig.ret, Kind::Call { func: fu, args: sig.args }, inps),
&mut self.ci.nodes,
);
self.add_clobber_stores(clobbered_aliases);
alt_value.or(Some(Value::new(self.ci.ctrl.get()).ty(sig.ret)))
}
}
fn gen_global(&mut self, global: ty::Global) -> Option<Value> { fn gen_global(&mut self, global: ty::Global) -> Option<Value> {
let gl = &self.tys.ins.globals[global]; let gl = &self.tys.ins.globals[global];
let value = self.ci.nodes.new_node_nop(gl.ty, Kind::Global { global }, [VOID]); let value = self.ci.nodes.new_node_nop(gl.ty, Kind::Global { global }, [VOID]);