adding inline functions
This commit is contained in:
parent
654005eea2
commit
c61efc3933
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
373
lang/src/son.rs
373
lang/src/son.rs
|
@ -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]);
|
||||||
|
|
Loading…
Reference in a new issue