use std::num::NonZeroU32;
use inkwell::context::Context;
use inkwell::values::{FloatValue, IntValue, PointerValue};
use sv_lang::{Bit, SVInt};
use crate::error::LoweringError;
pub const WIDE_THRESHOLD: u32 = 65535;
#[derive(Clone, Copy)]
pub struct WideValueRef<'ctx> {
pub base_ptr: PointerValue<'ctx>,
pub limb_count: u32,
}
pub fn wide_limb_count(width: u32) -> u32 {
width.div_ceil(64)
}
pub fn aval_bval_to_bit(aval: bool, bval: bool) -> Bit {
match (aval, bval) {
(false, false) => Bit::Zero,
(true, false) => Bit::One,
(false, true) => Bit::Z,
(true, true) => Bit::X,
}
}
pub fn bit_to_aval_bval(b: Bit) -> (bool, bool) {
match b {
Bit::Zero => (false, false),
Bit::One => (true, false),
Bit::Z => (false, true),
Bit::X => (true, true),
}
}
#[derive(Clone, Copy)]
pub enum AnyValue<'ctx> {
Narrow(PackedValue<'ctx>),
Wide(WideValueRef<'ctx>),
}
impl<'ctx> AnyValue<'ctx> {
pub fn expect_narrow(
self,
what: &str,
sym: &sv_lang::Symbol,
) -> Result<PackedValue<'ctx>, LoweringError> {
match self {
AnyValue::Narrow(p) => Ok(p),
AnyValue::Wide(_) => Err(LoweringError::unsupported_at(what, sym)),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ValueRepr {
FourState,
TwoState,
}
#[derive(Clone, Copy)]
pub struct PackedValue<'ctx> {
pub llvm: IntValue<'ctx>,
pub width: u32,
pub repr: ValueRepr,
pub signed: bool,
}
#[derive(Clone, Copy)]
pub struct RealValue<'ctx> {
pub llvm: FloatValue<'ctx>,
}
pub fn readback_bits(bytes: &[u8], width: u32, repr: ValueRepr) -> Vec<Bit> {
let get = |bit: u32| -> u8 {
let byte = (bit / 8) as usize;
if byte >= bytes.len() {
0
} else {
(bytes[byte] >> (bit % 8)) & 1
}
};
(0..width)
.map(|i| match repr {
ValueRepr::TwoState => {
if get(i) == 1 {
Bit::One
} else {
Bit::Zero
}
}
ValueRepr::FourState => match 2 * get(width + i) + get(i) {
0 => Bit::Zero,
1 => Bit::One,
2 => Bit::X,
_ => Bit::Z,
},
})
.collect()
}
fn int_type(ctx: &Context, w: u32) -> inkwell::types::IntType<'_> {
ctx.custom_width_int_type(NonZeroU32::new(w).expect("zero-width int type"))
.expect("custom_width_int_type")
}
fn words_for_bits(bits: u32) -> usize {
(bits as u64).div_ceil(64) as usize
}
pub(crate) fn packed_words(bits_lsb_first: &[Bit], width: u32, repr: ValueRepr) -> Vec<u64> {
let total_bits = match repr {
ValueRepr::TwoState => width,
ValueRepr::FourState => width * 2,
};
let mut words = vec![0u64; words_for_bits(total_bits)];
let mut set_bit = |bit_index: u32, v: bool| {
if v {
words[(bit_index / 64) as usize] |= 1u64 << (bit_index % 64);
}
};
for (i, b) in bits_lsb_first.iter().enumerate() {
let i = i as u32;
let (vv, uu) = match b {
Bit::Zero => (false, false),
Bit::One => (true, false),
Bit::X => (false, true),
Bit::Z => (true, true),
};
set_bit(i, vv);
if repr == ValueRepr::FourState {
set_bit(width + i, uu);
}
}
words
}
pub fn const_svint<'ctx>(ctx: &'ctx Context, v: &SVInt, repr: ValueRepr) -> PackedValue<'ctx> {
let bits = sv_sim_sim::state::svint_bits(v)
.expect("a constant whose bits are unrecoverable past sv-lang's expansion cap");
const_bits(ctx, &bits, v.is_signed(), repr)
}
pub fn const_bits<'ctx>(
ctx: &'ctx Context,
bits: &[Bit],
signed: bool,
repr: ValueRepr,
) -> PackedValue<'ctx> {
let width = bits.len() as u32;
assert!(
width <= WIDE_THRESHOLD && width > 0,
"narrow path supports 1..=WIDE_THRESHOLD-bit ({WIDE_THRESHOLD}) constants only"
);
let words = packed_words(bits, width, repr);
let t = match repr {
ValueRepr::TwoState => int_type(ctx, width),
ValueRepr::FourState => int_type(ctx, width * 2),
};
PackedValue {
llvm: t.const_int_arbitrary_precision(&words),
width,
repr,
signed,
}
}