use super::body::{After, ClauseCtx};
use super::lower::{self, LGoal, LGoalKind};
use super::term_emit::collect_vars;
use super::{CodeGen, GoalTarget};
use plg_frontend::CgClause;
use plg_shared::term::VarId;
use plg_shared::{AtomId, Span, Term};
use std::collections::HashMap;
use std::fmt::Write;
impl CodeGen<'_> {
pub fn emit_clause(
&mut self,
functor: AtomId,
arity: u32,
j: usize,
clause: &CgClause,
) -> Result<(), String> {
let base = format!("plg_p{functor}_{arity}_c{j}");
let goals = lower::lower_body(&clause.body, self.interner)?;
let mut var_list: Vec<VarId> = Vec::new();
collect_vars(&clause.head, &mut var_list);
for g in &goals {
lower::collect_goal_vars(g, &mut var_list);
}
let scratch = lower::count_scratch(&goals);
self.reset_temps();
let mut b = String::new();
let mut vars: HashMap<VarId, String> = HashMap::new();
let head_args: &[Term] = match &clause.head {
Term::Compound { args, .. } => args,
_ => &[], };
let mut to_unify: Vec<(String, &Term)> = Vec::new();
for (i, pat) in head_args.iter().enumerate() {
let arg = self.fresh();
writeln!(
b,
" {arg} = call i64 @plg_rt_frame_get(ptr %m, i64 %f, i32 {i})"
)
.unwrap();
match pat {
Term::Var(v) if !vars.contains_key(v) => {
vars.insert(*v, arg);
}
_ => to_unify.push((arg, pat)),
}
}
for v in &var_list {
if !vars.contains_key(v) {
let t = self.fresh();
writeln!(b, " {t} = call i64 @plg_rt_new_var(ptr %m)").unwrap();
vars.insert(*v, t);
}
}
for (arg, pat) in to_unify {
let w = self.emit_term(&mut b, pat, &vars)?;
let u = self.fresh();
writeln!(
b,
" {u} = call i32 @plg_rt_unify(ptr %m, i64 {arg}, i64 {w})"
)
.unwrap();
self.emit_branch_on(&mut b, &u);
}
if goals.is_empty() {
let kf = self.fresh();
writeln!(
b,
" {kf} = call i64 @plg_rt_frame_get(ptr %m, i64 %f, i32 {arity})"
)
.unwrap();
let ke = self.fresh();
writeln!(
b,
" {ke} = call i64 @plg_rt_frame_get(ptr %m, i64 %f, i32 {})",
arity + 1
)
.unwrap();
let kp = self.fresh();
writeln!(b, " {kp} = inttoptr i64 {kf} to ptr").unwrap();
let r = self.fresh();
writeln!(b, " {r} = musttail call i32 {kp}(ptr %m, i64 {ke})").unwrap();
writeln!(b, " ret i32 {r}").unwrap();
self.write_fn(&base, "%f", &b);
return Ok(());
}
let mut ctx = ClauseCtx::new(base.clone(), var_list.clone(), String::new());
let bf = self.fresh();
writeln!(
b,
" {bf} = call i64 @plg_rt_frame_alloc(ptr %m, i32 {})",
ctx.frame_size(scratch)
)
.unwrap();
for (slot, src) in [(0u32, arity), (1, arity + 1), (2, arity + 2)] {
let t = self.fresh();
writeln!(
b,
" {t} = call i64 @plg_rt_frame_get(ptr %m, i64 %f, i32 {src})"
)
.unwrap();
writeln!(
b,
" call void @plg_rt_frame_set(ptr %m, i64 {bf}, i32 {slot}, i64 {t})"
)
.unwrap();
}
for (i, v) in var_list.iter().enumerate() {
let w = &vars[v];
writeln!(
b,
" call void @plg_rt_frame_set(ptr %m, i64 {bf}, i32 {}, i64 {w})",
3 + i
)
.unwrap();
}
ctx.bf = bf;
self.compile_seq(&mut b, &goals, &After::CallerK, &mut ctx, &vars, 2)?;
writeln!(
self.out,
"; clause {j} of {}/{arity}",
self.interner.resolve(functor)
)
.unwrap();
self.write_fn(&base, "%f", &b);
self.emit_aux_fns(&mut ctx)?;
Ok(())
}
fn write_fn(&mut self, sym: &str, env_name: &str, body: &str) {
writeln!(
self.out,
"define internal i32 @{sym}(ptr %m, i64 {env_name}) {{"
)
.unwrap();
writeln!(self.out, "entry:").unwrap();
self.out.push_str(body);
writeln!(self.out, "fail:").unwrap();
writeln!(self.out, " ret i32 0").unwrap();
writeln!(self.out, "}}").unwrap();
}
pub fn emit_branch_on(&mut self, b: &mut String, result: &str) {
let l = self.fresh_label();
let c = self.fresh();
writeln!(b, " {c} = icmp ne i32 {result}, 0").unwrap();
writeln!(b, " br i1 {c}, label %{l}, label %fail").unwrap();
writeln!(b, "{l}:").unwrap();
}
pub fn emit_inline_builtin(
&mut self,
b: &mut String,
g: &LGoal,
vars: &HashMap<VarId, String>,
) -> Result<(), String> {
let span = g.span;
let r = match &g.node {
LGoalKind::Unify(x, y) => {
let (wx, wy) = (self.emit_term(b, x, vars)?, self.emit_term(b, y, vars)?);
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_unify(ptr %m, i64 {wx}, i64 {wy})"
)
.unwrap();
r
}
LGoalKind::NotUnify(x, y) => {
let (wx, wy) = (self.emit_term(b, x, vars)?, self.emit_term(b, y, vars)?);
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_b_neq(ptr %m, i64 {wx}, i64 {wy})"
)
.unwrap();
r
}
LGoalKind::TermCmp(op, x, y) => {
let (wx, wy) = (self.emit_term(b, x, vars)?, self.emit_term(b, y, vars)?);
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_b_term_cmp(ptr %m, i32 {op}, i64 {wx}, i64 {wy})"
)
.unwrap();
r
}
LGoalKind::Compare(o, x, y) => {
let wo = self.emit_term(b, o, vars)?;
let (wx, wy) = (self.emit_term(b, x, vars)?, self.emit_term(b, y, vars)?);
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_b_compare(ptr %m, i64 {wo}, i64 {wx}, i64 {wy})"
)
.unwrap();
r
}
LGoalKind::Is(x, e) => {
let wx = self.emit_term(b, x, vars)?;
let we = self.emit_term(b, e, vars)?;
let site = self.site_id(span);
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_b_is(ptr %m, i64 {wx}, i64 {we}, i32 {site})"
)
.unwrap();
r
}
LGoalKind::ArithCmp(op, x, y) => {
let (wx, wy) = (self.emit_term(b, x, vars)?, self.emit_term(b, y, vars)?);
let site = self.site_id(span);
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_b_arith_cmp(ptr %m, i32 {op}, i64 {wx}, i64 {wy}, i32 {site})"
)
.unwrap();
r
}
LGoalKind::RtDet { sym, args, raises } => {
let mut words = Vec::with_capacity(args.len());
for a in args {
words.push(self.emit_term(b, a, vars)?);
}
let mut arglist: Vec<String> = words.iter().map(|w| format!(", i64 {w}")).collect();
if *raises {
let site = self.site_id(span);
arglist.push(format!(", i32 {site}"));
}
let r = self.fresh();
writeln!(b, " {r} = call i32 @{sym}(ptr %m{})", arglist.join("")).unwrap();
r
}
_ => unreachable!("not an inline builtin"),
};
self.emit_branch_on(b, &r);
Ok(())
}
pub fn emit_call_tail(
&mut self,
b: &mut String,
functor: AtomId,
args: &[Term],
vars: &HashMap<VarId, String>,
span: Span,
) -> Result<(), String> {
let arity = args.len() as u32;
if arity as usize > crate::MAX_GOAL_ARITY {
return Err(format!(
"goal arity {arity} exceeds the supported maximum of {}",
crate::MAX_GOAL_ARITY
));
}
let name = self.interner.resolve(functor).to_string();
match (name.as_str(), arity) {
("throw", 1) => {
let w = self.emit_term(b, &args[0], vars)?;
let r = self.fresh();
writeln!(b, " {r} = call i32 @plg_rt_b_throw_1(ptr %m, i64 {w})").unwrap();
writeln!(b, " ret i32 {r}").unwrap();
return Ok(());
}
("catch", 3) => {
let g = self.emit_term(b, &args[0], vars)?;
let c = self.emit_term(b, &args[1], vars)?;
let rec = self.emit_term(b, &args[2], vars)?;
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_b_catch_3(ptr %m, i64 {g}, i64 {c}, i64 {rec})"
)
.unwrap();
writeln!(b, " ret i32 {r}").unwrap();
return Ok(());
}
("findall", 3) => {
let t = self.emit_term(b, &args[0], vars)?;
let g = self.emit_term(b, &args[1], vars)?;
let bag = self.emit_term(b, &args[2], vars)?;
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_b_findall_3(ptr %m, i64 {t}, i64 {g}, i64 {bag})"
)
.unwrap();
writeln!(b, " ret i32 {r}").unwrap();
return Ok(());
}
("call", n) if n >= 1 => {
let goal = Term::Compound {
functor,
args: args.to_vec(),
};
let g = self.emit_term(b, &goal, vars)?;
let r = self.fresh();
writeln!(b, " {r} = call i32 @plg_rt_metacall(ptr %m, i64 {g})").unwrap();
writeln!(b, " ret i32 {r}").unwrap();
return Ok(());
}
("between", 3) => {
let mut words = Vec::with_capacity(args.len());
for a in args {
words.push(self.emit_term(b, a, vars)?);
}
for (i, w) in words.iter().enumerate() {
writeln!(b, " call void @plg_rt_areg_set(ptr %m, i32 {i}, i64 {w})").unwrap();
}
let r = self.fresh();
writeln!(
b,
" {r} = musttail call i32 @plg_rt_pred_between_3(ptr %m, i64 0)"
)
.unwrap();
writeln!(b, " ret i32 {r}").unwrap();
return Ok(());
}
_ => {}
}
match self.how_to_call(functor, arity) {
GoalTarget::Undefined => {
let site = self.site_id(span);
let r = self.fresh();
writeln!(
b,
" {r} = call i32 @plg_rt_existence_error(ptr %m, i32 {functor}, i32 {arity}, i32 {site})"
)
.unwrap();
writeln!(b, " ret i32 {r}").unwrap();
}
target => {
let mut words = Vec::with_capacity(args.len());
for a in args {
words.push(self.emit_term(b, a, vars)?);
}
for (i, w) in words.iter().enumerate() {
writeln!(b, " call void @plg_rt_areg_set(ptr %m, i32 {i}, i64 {w})").unwrap();
}
let callee = if target == GoalTarget::Defined {
self.pred_symbol(functor, arity)
} else {
"plg_rt_pred_fail".to_string()
};
let r = self.fresh();
writeln!(b, " {r} = musttail call i32 @{callee}(ptr %m, i64 0)").unwrap();
writeln!(b, " ret i32 {r}").unwrap();
}
}
Ok(())
}
}