use inkwell::values::IntValue;
use sv_lang::kinds::ExpressionKind;
use vj_sim::symid::SymbolUid;
use crate::error::LoweringError;
use crate::expr::{LowerCtx, lower_expr};
use crate::ops::OpBuilder;
use crate::value::PackedValue;
#[derive(Clone, Copy)]
struct Pla {
and: bool,
invert: bool,
plane: bool,
}
fn parse_name(name: &str) -> Option<Pla> {
let mut parts = name.strip_prefix('$')?.split('$');
let kind = parts.next()?;
let logic = parts.next()?;
let format = parts.next()?;
if parts.next().is_some() || !matches!(kind, "sync" | "async") {
return None;
}
let (and, invert) = match logic {
"and" => (true, false),
"nand" => (true, true),
"or" => (false, false),
"nor" => (false, true),
_ => return None,
};
let plane = match format {
"array" => false,
"plane" => true,
_ => return None,
};
Some(Pla { and, invert, plane })
}
pub fn is_pla_task(name: &str) -> bool {
parse_name(name).is_some()
}
pub fn is_async_pla_task(name: &str) -> bool {
name.starts_with("$async$") && is_pla_task(name)
}
pub(crate) fn lower_pla_call<'ctx, 'd>(
cx: &LowerCtx<'ctx, 'd, '_>,
name: &str,
e: sv_lang::Expression<'d>,
) -> Result<(), LoweringError> {
let pla = parse_name(name).ok_or_else(|| {
LoweringError::unsupported_at(&format!("{name} is not a PLA task"), &cx.fn_sym)
})?;
let ops = e.operands();
if ops.len() != 3 {
return Err(LoweringError::unsupported_at(
&format!(
"{name} takes (memory, input_terms, output_terms); got {} arguments",
ops.len()
),
&cx.fn_sym,
));
}
let unwrap = |x: sv_lang::Expression<'d>| {
if x.kind() == ExpressionKind::Assignment {
x.left().unwrap_or(x)
} else {
x
}
};
let mem_expr = unwrap(ops[0]);
let mem_sym = if mem_expr.kind() == ExpressionKind::ArbitrarySymbol {
mem_expr.arbitrary_symbol()
} else if vj_sim::symid::is_value_name(mem_expr.kind()) {
mem_expr.referenced_symbol()
} else {
None
};
let mem = mem_sym
.and_then(|s| cx.mem_signals.get(&s.uid()).copied())
.ok_or_else(|| {
LoweringError::unsupported_at(
&format!(
"{name}: the first argument must name a fixed-size unpacked array \
(the personality memory) of this module (got a {:?} expression)",
mem_expr.kind()
),
&cx.fn_sym,
)
})?;
let input = lower_expr(cx, ops[1])?.into_packed(
&format!("{name}: the input terms must be an integral value"),
&cx.fn_sym,
)?;
let out_lhs = unwrap(ops[2]);
let out_width = crate::select::lvalue_width(cx, out_lhs)?;
if mem.elem_width != input.width {
return Err(LoweringError::unsupported_at(
&format!(
"{name}: the personality memory is {} bits wide but there are {} input terms \
(LRM 20.16.3: as wide as the number of input terms)",
mem.elem_width, input.width
),
&cx.fn_sym,
));
}
if mem.num_rows != out_width {
return Err(LoweringError::unsupported_at(
&format!(
"{name}: the personality memory has {} words but there are {out_width} output \
terms (LRM 20.16.3: as deep as the number of output terms)",
mem.num_rows
),
&cx.fn_sym,
));
}
let acc = cx.mem_accessor.ok_or_else(|| {
LoweringError::unsupported_at(
&format!("{name}: memory read with no accessor context"),
&cx.fn_sym,
)
})?;
let op = OpBuilder {
ctx: cx.ctx,
b: &cx.b,
};
let n = input.width;
let i64_ty = cx.ctx.i64_type();
let two = |v: IntValue<'ctx>| op.pack(v, op.i_n(n).const_zero(), n, cx.repr, false);
let mut result: Option<PackedValue<'ctx>> = None;
for r in 0..mem.num_rows {
let raw = crate::accessor::build_get_mem_word(
cx,
acc.fns,
acc.handle,
mem.slot,
i64_ty.const_int(u64::from(r), false),
n,
);
let row = PackedValue {
llvm: raw,
width: n,
repr: cx.repr,
signed: false,
};
let (v, u) = (op.lane_val(row), op.lane_unk(row));
let nv = op.not(v);
let nu = op.not(u);
let m1 = op.and(v, nu);
let m0 = op.and(nv, nu);
let mx = op.and(nv, u);
let a = input;
let term = match (pla.and, pla.plane) {
(true, true) => {
let t1 = op.or4(a, two(op.not(m1)));
let t0 = op.or4(op.not4(a), two(op.not(m0)));
op.and4(t1, t0)
}
(true, false) => op.or4(a, two(op.not(m1))),
(false, true) => {
let t1 = op.and4(a, two(m1));
let t0 = op.and4(op.not4(a), two(m0));
op.or4(t1, t0)
}
(false, false) => op.and4(a, two(m1)),
};
let term = if pla.plane {
op.xor4(term, op.pack(mx, mx, n, cx.repr, false))
} else {
term
};
let bit = if pla.and {
op.redand4(term)
} else {
op.redor4(term)
};
let bit = if pla.invert { op.not4(bit) } else { bit };
result = Some(match result {
None => bit,
Some(acc_v) => concat(&op, acc_v, bit, cx.repr),
});
}
let value = result.ok_or_else(|| {
LoweringError::unsupported_at(&format!("{name}: an empty personality memory"), &cx.fn_sym)
})?;
crate::select::lower_assign_to(cx, out_lhs, value)
}
fn concat<'ctx>(
op: &OpBuilder<'_, 'ctx>,
hi: PackedValue<'ctx>,
lo: PackedValue<'ctx>,
repr: crate::value::ValueRepr,
) -> PackedValue<'ctx> {
let w = hi.width + lo.width;
let ext = |x: IntValue<'ctx>| op.b.build_int_z_extend(x, op.i_n(w), "pla_ext").unwrap();
let sh = |x: IntValue<'ctx>| {
op.b.build_left_shift(x, op.i_n(w).const_int(u64::from(lo.width), false), "pla_sh")
.unwrap()
};
let val = op.or(sh(ext(op.lane_val(hi))), ext(op.lane_val(lo)));
let unk = op.or(sh(ext(op.lane_unk(hi))), ext(op.lane_unk(lo)));
op.pack(val, unk, w, repr, false)
}