use inkwell::AddressSpace;
use inkwell::attributes::{Attribute, AttributeLoc};
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::module::{Linkage, Module};
use inkwell::types::{AnyType, IntType, StructType};
use inkwell::values::{BasicValue, FunctionValue, IntValue, PointerValue};
use vj_abi::accessor::{
SV_GET_MEM_WORD_SYMBOL, SV_GET_SCALAR_SYMBOL, SV_GET_WIDE_SYMBOL, SV_SET_MEM_WORD_SYMBOL,
SV_SET_SCALAR_SYMBOL, SV_SET_WIDE_SYMBOL,
};
use crate::expr::LowerCtx;
pub struct AccessorFns<'ctx> {
get_scalar: FunctionValue<'ctx>,
set_scalar: FunctionValue<'ctx>,
get_wide: FunctionValue<'ctx>,
set_wide: FunctionValue<'ctx>,
get_mem_word: FunctionValue<'ctx>,
set_mem_word: FunctionValue<'ctx>,
}
pub fn accessor_fns_of<'ctx>(module: &Module<'ctx>) -> Option<AccessorFns<'ctx>> {
Some(AccessorFns {
get_scalar: module.get_function(SV_GET_SCALAR_SYMBOL)?,
set_scalar: module.get_function(SV_SET_SCALAR_SYMBOL)?,
get_wide: module.get_function(SV_GET_WIDE_SYMBOL)?,
set_wide: module.get_function(SV_SET_WIDE_SYMBOL)?,
get_mem_word: module.get_function(SV_GET_MEM_WORD_SYMBOL)?,
set_mem_word: module.get_function(SV_SET_MEM_WORD_SYMBOL)?,
})
}
impl<'ctx> AccessorFns<'ctx> {
pub fn get_mem_word(&self) -> FunctionValue<'ctx> {
self.get_mem_word
}
pub fn set_mem_word(&self) -> FunctionValue<'ctx> {
self.set_mem_word
}
}
fn int_type(ctx: &Context, w: u32) -> IntType<'_> {
ctx.custom_width_int_type(std::num::NonZeroU32::new(w).expect("zero-width int type"))
.expect("custom_width_int_type")
}
fn alwaysinline_attr(ctx: &Context) -> Attribute {
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert_ne!(
kind_id, 0,
"\"alwaysinline\" must be a recognized LLVM enum attribute name"
);
ctx.create_enum_attribute(kind_id, 0)
}
pub fn declare_accessor_fns<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) -> AccessorFns<'ctx> {
let ptr_ty = ctx.ptr_type(AddressSpace::default());
let i32_ty = ctx.i32_type();
let i64_ty = ctx.i64_type();
let i128_ty = ctx.i128_type();
let void_ty = ctx.void_type();
let wide_view_ty = wide_view_llvm_type(ctx);
let attr = alwaysinline_attr(ctx);
let get_scalar_ty = i128_ty.fn_type(&[ptr_ty.into(), i32_ty.into()], false);
let get_scalar =
module.add_function(SV_GET_SCALAR_SYMBOL, get_scalar_ty, Some(Linkage::External));
get_scalar.add_attribute(AttributeLoc::Function, attr);
let set_scalar_ty = void_ty.fn_type(&[ptr_ty.into(), i32_ty.into(), i128_ty.into()], false);
let set_scalar =
module.add_function(SV_SET_SCALAR_SYMBOL, set_scalar_ty, Some(Linkage::External));
set_scalar.add_attribute(AttributeLoc::Function, attr);
let get_wide_ty = void_ty.fn_type(&[ptr_ty.into(), ptr_ty.into(), i32_ty.into()], false);
let get_wide = module.add_function(SV_GET_WIDE_SYMBOL, get_wide_ty, Some(Linkage::External));
get_wide.add_attribute(AttributeLoc::Function, attr);
let sret_kind_id = Attribute::get_named_enum_kind_id("sret");
get_wide.add_attribute(
AttributeLoc::Param(0),
ctx.create_type_attribute(sret_kind_id, wide_view_ty.as_any_type_enum()),
);
let set_wide_ty = void_ty.fn_type(&[ptr_ty.into(), i32_ty.into(), ptr_ty.into()], false);
let set_wide = module.add_function(SV_SET_WIDE_SYMBOL, set_wide_ty, Some(Linkage::External));
set_wide.add_attribute(AttributeLoc::Function, attr);
let byval_kind_id = Attribute::get_named_enum_kind_id("byval");
set_wide.add_attribute(
AttributeLoc::Param(2),
ctx.create_type_attribute(byval_kind_id, wide_view_ty.as_any_type_enum()),
);
let get_mem_word_ty = void_ty.fn_type(
&[ptr_ty.into(), ptr_ty.into(), i32_ty.into(), i64_ty.into()],
false,
);
let get_mem_word = module.add_function(
SV_GET_MEM_WORD_SYMBOL,
get_mem_word_ty,
Some(Linkage::External),
);
get_mem_word.add_attribute(AttributeLoc::Function, attr);
get_mem_word.add_attribute(
AttributeLoc::Param(0),
ctx.create_type_attribute(sret_kind_id, wide_view_ty.as_any_type_enum()),
);
let set_mem_word_ty = void_ty.fn_type(
&[ptr_ty.into(), i32_ty.into(), i64_ty.into(), ptr_ty.into()],
false,
);
let set_mem_word = module.add_function(
SV_SET_MEM_WORD_SYMBOL,
set_mem_word_ty,
Some(Linkage::External),
);
set_mem_word.add_attribute(AttributeLoc::Function, attr);
set_mem_word.add_attribute(
AttributeLoc::Param(3),
ctx.create_type_attribute(byval_kind_id, wide_view_ty.as_any_type_enum()),
);
AccessorFns {
get_scalar,
set_scalar,
get_wide,
set_wide,
get_mem_word,
set_mem_word,
}
}
fn wide_view_llvm_type(ctx: &Context) -> StructType<'_> {
let ptr_ty = ctx.ptr_type(AddressSpace::default());
let i32_ty = ctx.i32_type();
ctx.struct_type(&[ptr_ty.into(), ptr_ty.into(), i32_ty.into()], false)
}
fn resize<'ctx>(
b: &Builder<'ctx>,
v: IntValue<'ctx>,
src_bits: u32,
dst_ty: IntType<'ctx>,
dst_bits: u32,
name: &str,
) -> IntValue<'ctx> {
if dst_bits <= src_bits {
b.build_int_truncate_or_bit_cast(v, dst_ty, name).unwrap()
} else {
b.build_int_z_extend_or_bit_cast(v, dst_ty, name).unwrap()
}
}
fn low_bits_mask(width: u32) -> u64 {
if width >= 64 {
u64::MAX
} else {
(1u64 << width) - 1
}
}
pub fn build_get_scalar<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
width: u32,
) -> IntValue<'ctx> {
debug_assert!(
(1..=64).contains(&width),
"sv_get_scalar is SlotClass::Inline-only: width must be 1..=64, got {width}"
);
let raw = build_get_scalar_raw(cx, fns, handle, slot);
build_fixed_to_packed(cx, raw, width)
}
pub const SV_RT_INLINE_PTRS_SYMBOL: &str = "sv_rt_inline_ptrs";
thread_local! {
static DIRECT_INLINE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}
pub(crate) struct DirectInlineGuard;
impl DirectInlineGuard {
pub(crate) fn enter(on: bool) -> Self {
DIRECT_INLINE.with(|d| d.set(on));
DirectInlineGuard
}
}
impl Drop for DirectInlineGuard {
fn drop(&mut self) {
DIRECT_INLINE.with(|d| d.set(false));
}
}
pub(crate) fn direct_inline_enabled() -> bool {
DIRECT_INLINE.with(std::cell::Cell::get)
}
pub fn build_inline_ptr_table<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
handle: PointerValue<'ctx>,
) -> PointerValue<'ctx> {
let ptr_ty = cx.ctx.ptr_type(AddressSpace::default());
let f = cx
.module
.get_function(SV_RT_INLINE_PTRS_SYMBOL)
.unwrap_or_else(|| {
cx.module.add_function(
SV_RT_INLINE_PTRS_SYMBOL,
ptr_ty.fn_type(&[ptr_ty.into()], false),
None,
)
});
cx.b.build_call(f, &[handle.into()], "inline_ptrs")
.unwrap()
.try_as_basic_value()
.unwrap_basic()
.into_pointer_value()
}
pub fn build_get_scalar_raw_direct<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
table: PointerValue<'ctx>,
slot: u32,
) -> IntValue<'ctx> {
let ctx = cx.ctx;
let b = &cx.b;
let ptr_ty = ctx.ptr_type(AddressSpace::default());
let i64_ty = ctx.i64_type();
let entry = unsafe {
b.build_gep(
ptr_ty,
table,
&[i64_ty.const_int(u64::from(slot), false)],
"inline_ptr_ent",
)
}
.unwrap();
let cell = b
.build_load(ptr_ty, entry, "inline_ptr")
.unwrap()
.into_pointer_value();
let is_null = b.build_is_null(cell, "inline_ptr_null").unwrap();
let func = b.get_insert_block().unwrap().get_parent().unwrap();
let fast_bb = ctx.append_basic_block(func, "get_direct");
let slow_bb = ctx.append_basic_block(func, "get_accessor");
let join_bb = ctx.append_basic_block(func, "get_join");
b.build_conditional_branch(is_null, slow_bb, fast_bb)
.unwrap();
b.position_at_end(fast_bb);
let load = b
.build_load(ctx.i128_type(), cell, "inline_direct")
.unwrap();
load.as_instruction_value()
.unwrap()
.set_alignment(16)
.unwrap();
let direct = load.into_int_value();
b.build_unconditional_branch(join_bb).unwrap();
b.position_at_end(slow_bb);
let slow = build_get_scalar_raw(cx, fns, handle, slot);
b.build_unconditional_branch(join_bb).unwrap();
b.position_at_end(join_bb);
let phi = b.build_phi(ctx.i128_type(), "inline_val").unwrap();
phi.add_incoming(&[(&direct, fast_bb), (&slow, slow_bb)]);
phi.as_basic_value().into_int_value()
}
pub(crate) struct InlineReads<'ctx> {
direct: bool,
table: Option<PointerValue<'ctx>>,
}
impl<'ctx> InlineReads<'ctx> {
pub(crate) fn new(inline_reads: usize) -> Self {
InlineReads {
direct: direct_inline_enabled() && inline_reads >= 2,
table: None,
}
}
pub(crate) fn get(
&mut self,
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
width: u32,
) -> IntValue<'ctx> {
if !self.direct {
return build_get_scalar(cx, fns, handle, slot, width);
}
let table = *self
.table
.get_or_insert_with(|| build_inline_ptr_table(cx, handle));
let raw = build_get_scalar_raw_direct(cx, fns, handle, table, slot);
build_fixed_to_packed(cx, raw, width)
}
}
pub fn build_get_scalar_raw<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
) -> IntValue<'ctx> {
let slot_const = cx.ctx.i32_type().const_int(u64::from(slot), false);
cx.b.build_call(
fns.get_scalar,
&[handle.into(), slot_const.into()],
"sv_get_scalar_call",
)
.unwrap()
.try_as_basic_value()
.unwrap_basic()
.into_int_value()
}
pub fn build_fixed_to_packed<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
raw: IntValue<'ctx>,
width: u32,
) -> IntValue<'ctx> {
let ctx = cx.ctx;
let b = &cx.b;
let i64_ty = ctx.i64_type();
let i128_ty = ctx.i128_type();
let sixty_four = i128_ty.const_int(64, false);
let val64 = b.build_int_truncate(raw, i64_ty, "acc_get_val64").unwrap();
let raw_hi = b
.build_right_shift(raw, sixty_four, false, "acc_get_raw_hi")
.unwrap();
let unk64 = b
.build_int_truncate(raw_hi, i64_ty, "acc_get_unk64")
.unwrap();
let mask = i64_ty.const_int(low_bits_mask(width), false);
let val_masked = b.build_and(val64, mask, "acc_get_val_masked").unwrap();
let unk_masked = b.build_and(unk64, mask, "acc_get_unk_masked").unwrap();
let packed_bits = width.checked_mul(2).expect("width*2 overflow");
let packed_ty = int_type(ctx, packed_bits);
let val_ext = resize(b, val_masked, 64, packed_ty, packed_bits, "acc_get_val_ext");
let unk_ext = resize(b, unk_masked, 64, packed_ty, packed_bits, "acc_get_unk_ext");
let width_const = packed_ty.const_int(u64::from(width), false);
let unk_shifted = b
.build_left_shift(unk_ext, width_const, "acc_get_unk_shifted")
.unwrap();
b.build_or(val_ext, unk_shifted, "acc_get_packed").unwrap()
}
pub fn reposition_width_tight_to_fixed<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
value: IntValue<'ctx>,
width: u32,
) -> IntValue<'ctx> {
debug_assert!(
(1..=64).contains(&width),
"the accessor ABI is SlotClass::Inline-only: width must be 1..=64, got {width}"
);
let ctx = cx.ctx;
let b = &cx.b;
let packed_bits = width.checked_mul(2).expect("width*2 overflow");
let packed_ty = int_type(ctx, packed_bits);
let mask = packed_ty.const_int(low_bits_mask(width), false);
let val_part = b.build_and(value, mask, "acc_set_val_part").unwrap();
let width_const = packed_ty.const_int(u64::from(width), false);
let shifted = b
.build_right_shift(value, width_const, false, "acc_set_shift")
.unwrap();
let unk_part = b.build_and(shifted, mask, "acc_set_unk_part").unwrap();
let i64_ty = ctx.i64_type();
let val64 = resize(b, val_part, packed_bits, i64_ty, 64, "acc_set_val64");
let unk64 = resize(b, unk_part, packed_bits, i64_ty, 64, "acc_set_unk64");
let i128_ty = ctx.i128_type();
let val128 = b
.build_int_z_extend(val64, i128_ty, "acc_set_val128")
.unwrap();
let unk128 = b
.build_int_z_extend(unk64, i128_ty, "acc_set_unk128")
.unwrap();
let sixty_four = i128_ty.const_int(64, false);
let unk_shifted = b
.build_left_shift(unk128, sixty_four, "acc_set_unk_shifted")
.unwrap();
b.build_or(val128, unk_shifted, "acc_set_fixed").unwrap()
}
pub fn build_set_scalar<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
value: IntValue<'ctx>,
width: u32,
) {
let fixed = reposition_width_tight_to_fixed(cx, value, width);
let slot_const = cx.ctx.i32_type().const_int(u64::from(slot), false);
cx.b.build_call(
fns.set_scalar,
&[handle.into(), slot_const.into(), fixed.into()],
"sv_set_scalar_call",
)
.unwrap();
}
#[derive(Clone, Copy)]
pub enum Slot<'ctx> {
Const(u32),
Dyn(IntValue<'ctx>),
}
impl From<u32> for Slot<'_> {
fn from(slot: u32) -> Self {
Slot::Const(slot)
}
}
impl<'ctx> Slot<'ctx> {
fn value(self, ctx: &'ctx inkwell::context::Context) -> IntValue<'ctx> {
match self {
Slot::Const(s) => ctx.i32_type().const_int(u64::from(s), false),
Slot::Dyn(v) => v,
}
}
}
fn call_get_view<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
get_fn: FunctionValue<'ctx>,
handle: PointerValue<'ctx>,
slot: impl Into<Slot<'ctx>>,
row_index: Option<IntValue<'ctx>>,
) -> (PointerValue<'ctx>, PointerValue<'ctx>) {
let ctx = cx.ctx;
let b = &cx.b;
let wide_view_ty = wide_view_llvm_type(ctx);
let sret_alloca = crate::ops::shared_entry_alloca(ctx, b, wide_view_ty, "wideview_sret");
let slot_const = slot.into().value(ctx);
match row_index {
Some(idx) => b
.build_call(
get_fn,
&[
sret_alloca.into(),
handle.into(),
slot_const.into(),
idx.into(),
],
"wideview_get_call",
)
.unwrap(),
None => b
.build_call(
get_fn,
&[sret_alloca.into(), handle.into(), slot_const.into()],
"wideview_get_call",
)
.unwrap(),
};
let ptr_ty = ctx.ptr_type(AddressSpace::default());
let aval_field = b
.build_struct_gep(wide_view_ty, sret_alloca, 0, "wideview_aval_field")
.unwrap();
let bval_field = b
.build_struct_gep(wide_view_ty, sret_alloca, 1, "wideview_bval_field")
.unwrap();
let aval_ptr = b
.build_load(ptr_ty, aval_field, "wideview_aval_ptr")
.unwrap()
.into_pointer_value();
let bval_ptr = b
.build_load(ptr_ty, bval_field, "wideview_bval_ptr")
.unwrap()
.into_pointer_value();
(aval_ptr, bval_ptr)
}
fn build_wideview_to_packed<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
get_fn: FunctionValue<'ctx>,
handle: PointerValue<'ctx>,
slot: impl Into<Slot<'ctx>>,
row_index: Option<IntValue<'ctx>>,
width: u32,
) -> IntValue<'ctx> {
let ctx = cx.ctx;
let b = &cx.b;
let n_words = width.div_ceil(64);
let (aval_ptr, bval_ptr) = call_get_view(cx, get_fn, handle, slot, row_index);
let i64_ty = ctx.i64_type();
let val_ty = int_type(ctx, width);
if width > crate::ops::WIDE_SELECT_BITS {
return gather_planes_via_memory(cx, aval_ptr, bval_ptr, width);
}
let mut val_acc = val_ty.const_zero();
let mut unk_acc = val_ty.const_zero();
for i in 0..n_words {
let idx_const = i64_ty.const_int(u64::from(i), false);
let aval_word_ptr = unsafe {
b.build_gep(i64_ty, aval_ptr, &[idx_const], "wideview_aval_word_ptr")
.unwrap()
};
let bval_word_ptr = unsafe {
b.build_gep(i64_ty, bval_ptr, &[idx_const], "wideview_bval_word_ptr")
.unwrap()
};
let aval_word = b
.build_load(i64_ty, aval_word_ptr, "wideview_aval_word")
.unwrap()
.into_int_value();
let bval_word = b
.build_load(i64_ty, bval_word_ptr, "wideview_bval_word")
.unwrap()
.into_int_value();
let val_word = b
.build_xor(aval_word, bval_word, "wideview_val_word")
.unwrap();
let aval_ext = resize(b, val_word, 64, val_ty, width, "wideview_aval_ext");
let bval_ext = resize(b, bval_word, 64, val_ty, width, "wideview_bval_ext");
let shift = val_ty.const_int(u64::from(i) * 64, false);
let aval_shifted = b
.build_left_shift(aval_ext, shift, "wideview_aval_shifted")
.unwrap();
let bval_shifted = b
.build_left_shift(bval_ext, shift, "wideview_bval_shifted")
.unwrap();
val_acc = b
.build_or(val_acc, aval_shifted, "wideview_val_acc")
.unwrap();
unk_acc = b
.build_or(unk_acc, bval_shifted, "wideview_unk_acc")
.unwrap();
}
let packed_bits = width.checked_mul(2).expect("width*2 overflow");
let packed_ty = int_type(ctx, packed_bits);
let val_wide = resize(
b,
val_acc,
width,
packed_ty,
packed_bits,
"wideview_val_wide",
);
let unk_wide = resize(
b,
unk_acc,
width,
packed_ty,
packed_bits,
"wideview_unk_wide",
);
let width_const = packed_ty.const_int(u64::from(width), false);
let unk_shifted = b
.build_left_shift(unk_wide, width_const, "wideview_unk_shifted")
.unwrap();
b.build_or(val_wide, unk_shifted, "wideview_packed")
.unwrap()
}
fn set_align(inst: inkwell::values::InstructionValue<'_>, align: u32) {
inst.set_alignment(align).expect("power-of-two alignment");
}
fn gather_planes_via_memory<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
aval_ptr: PointerValue<'ctx>,
bval_ptr: PointerValue<'ctx>,
width: u32,
) -> IntValue<'ctx> {
const _: () = assert!(cfg!(target_endian = "little"));
let ctx = cx.ctx;
let b = &cx.b;
let n_words = width.div_ceil(64);
let i64_ty = ctx.i64_type();
let i32_ty = ctx.i32_type();
let val_ty = int_type(ctx, width);
let arr_ty = i64_ty.array_type(n_words);
let fused = width.is_multiple_of(64);
let (val_buf, unk_buf) = if fused {
let buf = crate::ops::shared_entry_alloca(
ctx,
b,
i64_ty.array_type(2 * n_words),
&format!("wideview_packed_buf_{n_words}"),
);
let hi = unsafe {
b.build_in_bounds_gep(
i64_ty,
buf,
&[i64_ty.const_int(u64::from(n_words), false)],
"wideview_unk_half",
)
.unwrap()
};
(buf, hi)
} else {
(
crate::ops::shared_entry_alloca(ctx, b, arr_ty, &format!("wideview_val_buf_{n_words}")),
crate::ops::shared_entry_alloca(ctx, b, arr_ty, &format!("wideview_unk_buf_{n_words}")),
)
};
for i in 0..n_words {
let idx_const = i64_ty.const_int(u64::from(i), false);
let idx0 = i32_ty.const_zero();
let idxn = i32_ty.const_int(u64::from(i), false);
let (aval_word_ptr, bval_word_ptr) = unsafe {
(
b.build_gep(i64_ty, aval_ptr, &[idx_const], "wideview_aval_word_ptr")
.unwrap(),
b.build_gep(i64_ty, bval_ptr, &[idx_const], "wideview_bval_word_ptr")
.unwrap(),
)
};
let (val_slot, unk_slot) = unsafe {
(
b.build_gep(arr_ty, val_buf, &[idx0, idxn], "wideview_val_slot")
.unwrap(),
if fused {
b.build_gep(i64_ty, unk_buf, &[idx_const], "wideview_unk_slot")
.unwrap()
} else {
b.build_gep(arr_ty, unk_buf, &[idx0, idxn], "wideview_unk_slot")
.unwrap()
},
)
};
let aval_word = b
.build_load(i64_ty, aval_word_ptr, "wideview_aval_word")
.unwrap()
.into_int_value();
let bval_word = b
.build_load(i64_ty, bval_word_ptr, "wideview_bval_word")
.unwrap()
.into_int_value();
let val_word = b
.build_xor(aval_word, bval_word, "wideview_val_word")
.unwrap();
b.build_store(val_slot, val_word).unwrap();
b.build_store(unk_slot, bval_word).unwrap();
}
if fused {
let v = b
.build_load(int_type(ctx, width * 2), val_buf, "wideview_packed")
.unwrap();
set_align(
inkwell::values::BasicValue::as_instruction_value(&v)
.expect("a load is an instruction"),
8,
);
return v.into_int_value();
}
let load_plane = |buf: PointerValue<'ctx>, name: &str| {
let v = b.build_load(val_ty, buf, name).unwrap();
set_align(
inkwell::values::BasicValue::as_instruction_value(&v)
.expect("a load is an instruction"),
8,
);
v.into_int_value()
};
let val_acc = load_plane(val_buf, "wideview_val_acc");
let unk_acc = load_plane(unk_buf, "wideview_unk_acc");
let packed_bits = width.checked_mul(2).expect("width*2 overflow");
let packed_ty = int_type(ctx, packed_bits);
let val_wide = resize(
b,
val_acc,
width,
packed_ty,
packed_bits,
"wideview_val_wide",
);
let unk_wide = resize(
b,
unk_acc,
width,
packed_ty,
packed_bits,
"wideview_unk_wide",
);
let width_const = packed_ty.const_int(u64::from(width), false);
let unk_shifted = b
.build_left_shift(unk_wide, width_const, "wideview_unk_shifted")
.unwrap();
b.build_or(val_wide, unk_shifted, "wideview_packed")
.unwrap()
}
fn scatter_planes_via_memory<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
val_part: IntValue<'ctx>,
unk_part: IntValue<'ctx>,
width: u32,
aval_arr: PointerValue<'ctx>,
bval_arr: PointerValue<'ctx>,
) {
const _: () = assert!(cfg!(target_endian = "little"));
let b = &cx.b;
let n_words = width.div_ceil(64);
let padded_bits = n_words * 64;
let padded_ty = int_type(cx.ctx, padded_bits);
let aval = b
.build_xor(val_part, unk_part, "wideview_set_aval")
.unwrap();
for (plane, arr, name) in [
(aval, aval_arr, "wideview_set_aval_pad"),
(unk_part, bval_arr, "wideview_set_bval_pad"),
] {
let ext = resize(b, plane, width, padded_ty, padded_bits, name);
set_align(b.build_store(arr, ext).unwrap(), 8);
}
}
fn build_packed_to_wideview<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
set_fn: FunctionValue<'ctx>,
handle: PointerValue<'ctx>,
slot: impl Into<Slot<'ctx>>,
row_index: Option<IntValue<'ctx>>,
value: IntValue<'ctx>,
width: u32,
) {
let ctx = cx.ctx;
let b = &cx.b;
let n_words = width.div_ceil(64);
let i64_ty = ctx.i64_type();
let i32_ty = ctx.i32_type();
let val_ty = int_type(ctx, width);
let packed_bits = width.checked_mul(2).expect("width*2 overflow");
let packed_ty = int_type(ctx, packed_bits);
let width_const = packed_ty.const_int(u64::from(width), false);
let low_mask = b
.build_right_shift(
packed_ty.const_all_ones(),
width_const,
false,
"wideview_set_low_mask",
)
.unwrap();
let val_part_wide = b
.build_and(value, low_mask, "wideview_set_val_part_wide")
.unwrap();
let shifted = b
.build_right_shift(value, width_const, false, "wideview_set_shift")
.unwrap();
let unk_part_wide = b
.build_and(shifted, low_mask, "wideview_set_unk_part_wide")
.unwrap();
let val_part = resize(
b,
val_part_wide,
packed_bits,
val_ty,
width,
"wideview_set_val_part",
);
let unk_part = resize(
b,
unk_part_wide,
packed_bits,
val_ty,
width,
"wideview_set_unk_part",
);
let arr_ty = i64_ty.array_type(n_words);
let aval_alloca = crate::ops::shared_entry_alloca(
ctx,
b,
arr_ty,
&format!("wideview_set_aval_arr_{n_words}"),
);
let bval_alloca = crate::ops::shared_entry_alloca(
ctx,
b,
arr_ty,
&format!("wideview_set_bval_arr_{n_words}"),
);
let idx0 = i32_ty.const_zero();
let loop_words = if width > crate::ops::WIDE_SELECT_BITS {
scatter_planes_via_memory(cx, val_part, unk_part, width, aval_alloca, bval_alloca);
0
} else {
n_words
};
for i in 0..loop_words {
let shift = val_ty.const_int(u64::from(i) * 64, false);
let val_word_wide = b
.build_right_shift(val_part, shift, false, "wideview_set_val_word_wide")
.unwrap();
let unk_word_wide = b
.build_right_shift(unk_part, shift, false, "wideview_set_unk_word_wide")
.unwrap();
let val_word = resize(b, val_word_wide, width, i64_ty, 64, "wideview_set_val_word");
let unk_word = resize(b, unk_word_wide, width, i64_ty, 64, "wideview_set_unk_word");
let idxn = i32_ty.const_int(u64::from(i), false);
let aval_word_ptr = unsafe {
b.build_gep(
arr_ty,
aval_alloca,
&[idx0, idxn],
"wideview_set_aval_word_ptr",
)
.unwrap()
};
let bval_word_ptr = unsafe {
b.build_gep(
arr_ty,
bval_alloca,
&[idx0, idxn],
"wideview_set_bval_word_ptr",
)
.unwrap()
};
let aval_word = b
.build_xor(val_word, unk_word, "wideview_set_aval_word")
.unwrap();
b.build_store(aval_word_ptr, aval_word).unwrap();
b.build_store(bval_word_ptr, unk_word).unwrap();
}
call_set_view(
cx,
set_fn,
handle,
slot,
row_index,
aval_alloca,
bval_alloca,
n_words,
);
}
#[allow(clippy::too_many_arguments)]
fn call_set_view<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
set_fn: FunctionValue<'ctx>,
handle: PointerValue<'ctx>,
slot: impl Into<Slot<'ctx>>,
row_index: Option<IntValue<'ctx>>,
aval_alloca: PointerValue<'ctx>,
bval_alloca: PointerValue<'ctx>,
n_words: u32,
) {
let ctx = cx.ctx;
let b = &cx.b;
let i32_ty = ctx.i32_type();
let wide_view_ty = wide_view_llvm_type(ctx);
let view_alloca = crate::ops::shared_entry_alloca(ctx, b, wide_view_ty, "wideview_set_view");
let aval_field = b
.build_struct_gep(wide_view_ty, view_alloca, 0, "wideview_set_aval_field")
.unwrap();
let bval_field = b
.build_struct_gep(wide_view_ty, view_alloca, 1, "wideview_set_bval_field")
.unwrap();
let wc_field = b
.build_struct_gep(wide_view_ty, view_alloca, 2, "wideview_set_wc_field")
.unwrap();
b.build_store(aval_field, aval_alloca).unwrap();
b.build_store(bval_field, bval_alloca).unwrap();
b.build_store(wc_field, i32_ty.const_int(u64::from(n_words), false))
.unwrap();
let slot_const = slot.into().value(ctx);
match row_index {
Some(idx) => b
.build_call(
set_fn,
&[
handle.into(),
slot_const.into(),
idx.into(),
view_alloca.into(),
],
"wideview_set_call",
)
.unwrap(),
None => b
.build_call(
set_fn,
&[handle.into(), slot_const.into(), view_alloca.into()],
"wideview_set_call",
)
.unwrap(),
};
}
pub fn build_get_wide<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
width: u32,
) -> IntValue<'ctx> {
debug_assert!(
width > 64,
"build_get_wide is for SlotClass::Wide (width > 64); use build_get_scalar for width <= 64, got {width}"
);
build_wideview_to_packed(cx, fns.get_wide, handle, slot, None, width)
}
pub fn build_set_wide<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
value: IntValue<'ctx>,
width: u32,
) {
debug_assert!(
width > 64,
"build_set_wide is for SlotClass::Wide (width > 64); use build_set_scalar for width <= 64, got {width}"
);
build_packed_to_wideview(cx, fns.set_wide, handle, slot, None, value, width);
}
pub fn build_get_huge<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
width: u32,
dst: PointerValue<'ctx>,
) {
get_huge_view(cx, fns.get_wide, handle, slot, None, width, dst);
}
pub fn build_get_mem_huge<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
row_index: IntValue<'ctx>,
width: u32,
dst: PointerValue<'ctx>,
) {
get_huge_view(
cx,
fns.get_mem_word(),
handle,
slot,
Some(row_index),
width,
dst,
);
}
fn get_huge_view<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
get_fn: FunctionValue<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
row_index: Option<IntValue<'ctx>>,
width: u32,
dst: PointerValue<'ctx>,
) {
let (aval_ptr, bval_ptr) = call_get_view(cx, get_fn, handle, slot, row_index);
crate::huge::call_wide(
cx,
vj_runtime::wide_rt::WideOp::FromPlanes,
dst,
aval_ptr,
bval_ptr,
[u64::from(width), 0, 0, 0],
);
}
pub fn build_set_huge<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
width: u32,
src: PointerValue<'ctx>,
) {
set_huge_view(cx, fns.set_wide, handle, slot, None, width, src);
}
pub fn build_set_mem_huge<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
row_index: IntValue<'ctx>,
width: u32,
src: PointerValue<'ctx>,
) {
set_huge_view(
cx,
fns.set_mem_word(),
handle,
slot,
Some(row_index),
width,
src,
);
}
fn set_huge_view<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
set_fn: FunctionValue<'ctx>,
handle: PointerValue<'ctx>,
slot: u32,
row_index: Option<IntValue<'ctx>>,
width: u32,
src: PointerValue<'ctx>,
) {
let ctx = cx.ctx;
let n_words = width.div_ceil(64);
let arr_ty = ctx.i64_type().array_type(n_words);
let aval = crate::ops::shared_entry_alloca(
ctx,
&cx.b,
arr_ty,
&format!("wideview_set_aval_arr_{n_words}"),
);
let bval = crate::ops::shared_entry_alloca(
ctx,
&cx.b,
arr_ty,
&format!("wideview_set_bval_arr_{n_words}"),
);
crate::huge::call_wide(
cx,
vj_runtime::wide_rt::WideOp::ToPlanes,
aval,
src,
bval,
[u64::from(width), 0, 0, 0],
);
call_set_view(cx, set_fn, handle, slot, row_index, aval, bval, n_words);
}
pub fn build_get_mem_word<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: impl Into<Slot<'ctx>>,
row_index: IntValue<'ctx>,
elem_width: u32,
) -> IntValue<'ctx> {
debug_assert_eq!(
row_index.get_type().get_bit_width(),
64,
"row_index must be the accessor ABI's own i64 (u64) type"
);
build_wideview_to_packed(
cx,
fns.get_mem_word(),
handle,
slot,
Some(row_index),
elem_width,
)
}
pub fn build_set_mem_word<'ctx>(
cx: &LowerCtx<'ctx, '_, '_>,
fns: &AccessorFns<'ctx>,
handle: PointerValue<'ctx>,
slot: impl Into<Slot<'ctx>>,
row_index: IntValue<'ctx>,
value: IntValue<'ctx>,
elem_width: u32,
) {
debug_assert_eq!(
row_index.get_type().get_bit_width(),
64,
"row_index must be the accessor ABI's own i64 (u64) type"
);
build_packed_to_wideview(
cx,
fns.set_mem_word(),
handle,
slot,
Some(row_index),
value,
elem_width,
);
}