use cranelift::{codegen::ir::MemFlagsData as MemFlags, prelude::*};
use cranelift_frontend::FunctionBuilder;
use super::core::promote_to_physical;
use super::core::{
MEM_SHIFT_THRESHOLD, TransValue, alloc_stack_slot, cast_type, get_chunk_as_i64, get_cl_type,
};
use super::{SIRTranslator, TranslationState, wide_ops};
use crate::{BinaryOp, RegisterId, SIRValue, UnaryOp};
impl SIRTranslator {
pub(super) fn translate_imm_inst(
&self,
state: &mut TranslationState,
dst: &RegisterId,
val: &SIRValue,
) {
let width = state.register_map[dst].width();
let num_chunks = if width <= 64 { 1 } else { width.div_ceil(64) };
let mut cl_chunks = Vec::with_capacity(num_chunks);
let digits = val.payload.to_u64_digits();
if width <= 64 {
let ty = get_cl_type(width);
let raw = digits.first().copied().unwrap_or(0);
cl_chunks.push(state.builder.ins().iconst(ty, raw as i64));
} else {
for i in 0..num_chunks {
let d = digits.get(i).copied().unwrap_or(0);
cl_chunks.push(state.builder.ins().iconst(types::I64, d as i64));
}
}
if self.options.four_state {
let mut cl_masks = Vec::with_capacity(num_chunks);
let mask_digits = val.mask.to_u64_digits();
if width <= 64 {
let ty = get_cl_type(width);
let raw_m = mask_digits.first().copied().unwrap_or(0);
cl_masks.push(state.builder.ins().iconst(ty, raw_m as i64));
} else {
for i in 0..num_chunks {
let d = mask_digits.get(i).copied().unwrap_or(0);
cl_masks.push(state.builder.ins().iconst(types::I64, d as i64));
}
}
state.regs.insert(
*dst,
TransValue::FourState {
values: cl_chunks,
masks: cl_masks,
},
);
} else {
state.regs.insert(*dst, TransValue::TwoState(cl_chunks));
}
}
pub(super) fn try_fold_const_sir_concat(
&self,
state: &mut TranslationState,
dst: &RegisterId,
args: &[RegisterId],
) -> Option<TransValue> {
if self.options.four_state {
return None; }
let mut const_vals: Vec<(u64, usize)> = Vec::with_capacity(args.len());
for arg in args {
let val = state.imm_constants.get(arg)?;
let width = state.register_map[arg].width();
if width > 64 {
return None; }
const_vals.push((*val, width));
}
let dst_width = state.register_map[dst].width();
let num_chunks = dst_width.div_ceil(64).max(1);
let mut combined = vec![0u64; num_chunks];
let mut bit_offset = 0usize;
for &(val, width) in const_vals.iter().rev() {
let mask = if width >= 64 {
u64::MAX
} else {
(1u64 << width) - 1
};
let masked_val = val & mask;
let chunk_idx = bit_offset / 64;
let bit_in_chunk = bit_offset % 64;
if chunk_idx < num_chunks {
combined[chunk_idx] |= masked_val << bit_in_chunk;
if bit_in_chunk + width > 64 && chunk_idx + 1 < num_chunks {
combined[chunk_idx + 1] |= masked_val >> (64 - bit_in_chunk);
}
}
bit_offset += width;
}
let cl_chunks: Vec<Value> = combined
.iter()
.enumerate()
.map(|(i, &v)| {
if num_chunks == 1 {
let ty = get_cl_type(dst_width);
state.builder.ins().iconst(ty, v as i64)
} else {
let remaining = dst_width - i * 64;
let _ty = if remaining >= 64 {
types::I64
} else {
get_cl_type(remaining)
};
state.builder.ins().iconst(types::I64, v as i64)
}
})
.collect();
Some(TransValue::TwoState(cl_chunks))
}
pub(super) fn translate_concat_inst(
&self,
state: &mut TranslationState,
dst: &RegisterId,
args: &[RegisterId],
) {
let dst_width = state.register_map[dst].width();
let num_chunks = if dst_width <= 64 {
1
} else {
dst_width.div_ceil(64)
};
let mut dst_chunks_v = vec![state.builder.ins().iconst(types::I64, 0); num_chunks];
let mut dst_chunks_m = vec![state.builder.ins().iconst(types::I64, 0); num_chunks];
let mut current_bit_offset: usize = 0;
for arg_reg in args.iter().rev() {
let arg_width = state.register_map[arg_reg].width();
let arg_chunks_v = state.regs[arg_reg].load_value_chunks(state.builder);
let arg_chunks_m: Vec<Value> = if self.options.four_state {
state.regs[arg_reg]
.load_mask_chunks(state.builder)
.unwrap_or_else(|| {
let ty = get_cl_type(arg_width);
vec![state.builder.ins().iconst(ty, 0)]
})
} else {
vec![]
};
let arg_num_chunks = if arg_width <= 64 {
1
} else {
arg_width.div_ceil(64)
};
for i in 0..arg_num_chunks {
let arg_chunk_bits = arg_width.saturating_sub(i * 64).min(64);
let raw_chunk_v = get_chunk_as_i64(state.builder, &arg_chunks_v, i);
let raw_chunk_m = if self.options.four_state {
get_chunk_as_i64(state.builder, &arg_chunks_m, i)
} else {
state.builder.ins().iconst(types::I64, 0)
};
let (chunk_val_v, chunk_val_m) = if arg_chunk_bits < 64 {
let mask_value = (1u64 << arg_chunk_bits) - 1;
let mask = state.builder.ins().iconst(types::I64, mask_value as i64);
(
state.builder.ins().band(raw_chunk_v, mask),
state.builder.ins().band(raw_chunk_m, mask),
)
} else {
(raw_chunk_v, raw_chunk_m)
};
let abs_bit_offset = current_bit_offset + i * 64;
let dst_chunk_idx = abs_bit_offset / 64;
let bit_shift = abs_bit_offset % 64;
if dst_chunk_idx < num_chunks {
let shifted_v = state
.builder
.ins()
.ishl_imm_s(chunk_val_v, bit_shift as i64);
dst_chunks_v[dst_chunk_idx] = state
.builder
.ins()
.bor(dst_chunks_v[dst_chunk_idx], shifted_v);
if self.options.four_state {
let shifted_m = state
.builder
.ins()
.ishl_imm_s(chunk_val_m, bit_shift as i64);
dst_chunks_m[dst_chunk_idx] = state
.builder
.ins()
.bor(dst_chunks_m[dst_chunk_idx], shifted_m);
}
}
if bit_shift > 0
&& (dst_chunk_idx + 1) < num_chunks
&& (bit_shift + arg_chunk_bits > 64)
{
let shift_down = 64 - bit_shift;
let shifted_down_v = state
.builder
.ins()
.ushr_imm_s(chunk_val_v, shift_down as i64);
dst_chunks_v[dst_chunk_idx + 1] = state
.builder
.ins()
.bor(dst_chunks_v[dst_chunk_idx + 1], shifted_down_v);
if self.options.four_state {
let shifted_down_m = state
.builder
.ins()
.ushr_imm_s(chunk_val_m, shift_down as i64);
dst_chunks_m[dst_chunk_idx + 1] = state
.builder
.ins()
.bor(dst_chunks_m[dst_chunk_idx + 1], shifted_down_m);
}
}
}
current_bit_offset += arg_width;
}
if dst_width <= 64 {
let ty = get_cl_type(dst_width);
let val_v = cast_type(state.builder, dst_chunks_v[0], ty);
if self.options.four_state {
let val_m = cast_type(state.builder, dst_chunks_m[0], ty);
state.regs.insert(
*dst,
TransValue::FourState {
values: vec![val_v],
masks: vec![val_m],
},
);
} else {
state.regs.insert(*dst, TransValue::TwoState(vec![val_v]));
}
} else if self.options.four_state {
state.regs.insert(
*dst,
TransValue::FourState {
values: dst_chunks_v,
masks: dst_chunks_m,
},
);
} else {
state.regs.insert(*dst, TransValue::TwoState(dst_chunks_v));
}
}
pub(super) fn translate_binary_inst(
&self,
state: &mut TranslationState,
dst: &RegisterId,
lhs: &RegisterId,
op: &BinaryOp,
rhs: &RegisterId,
) {
let l_width = state.register_map[lhs].width();
let r_width = state.register_map[rhs].width();
let d_width = state.register_map[dst].width();
let common_logical_width = l_width.max(r_width).max(d_width);
if common_logical_width <= 64 {
let common_ty = get_cl_type(common_logical_width);
let signed_divrem = matches!(op, BinaryOp::DivS | BinaryOp::RemS);
let any_divrem = matches!(
op,
BinaryOp::DivU | BinaryOp::DivS | BinaryOp::RemU | BinaryOp::RemS
);
let signed_comparison = matches!(
op,
BinaryOp::LtS | BinaryOp::LeS | BinaryOp::GtS | BinaryOp::GeS
);
let any_comparison = matches!(
op,
BinaryOp::Eq
| BinaryOp::Ne
| BinaryOp::EqCase
| BinaryOp::NeCase
| BinaryOp::EqWildcard
| BinaryOp::NeWildcard
| BinaryOp::LtU
| BinaryOp::LtS
| BinaryOp::LeU
| BinaryOp::LeS
| BinaryOp::GtU
| BinaryOp::GtS
| BinaryOp::GeU
| BinaryOp::GeS
);
let l_is_signed = if any_divrem {
signed_divrem
} else if any_comparison {
signed_comparison
} else if matches!(op, BinaryOp::Shr) {
false
} else {
state.register_map[lhs].is_signed() || matches!(op, BinaryOp::Sar)
};
let r_is_signed = signed_divrem || signed_comparison;
let l_val = state.regs[lhs].first_value(state.builder);
let r_val = state.regs[rhs].first_value(state.builder);
let l = promote_to_physical(state, l_val, l_width, l_is_signed, common_ty);
let r = promote_to_physical(state, r_val, r_width, r_is_signed, common_ty);
let build_icmp = |builder: &mut FunctionBuilder, cc: IntCC| {
let b1_res = builder.ins().icmp(cc, l, r);
let zero = builder.ins().iconst(common_ty, 0);
let one = builder.ins().iconst(common_ty, 1);
builder.ins().select(b1_res, one, zero)
};
let mut res_v = match op {
BinaryOp::Add => state.builder.ins().iadd(l, r),
BinaryOp::Sub => state.builder.ins().isub(l, r),
BinaryOp::Mul => state.builder.ins().imul(l, r),
BinaryOp::DivU => {
let zero = state.builder.ins().iconst(common_ty, 0);
let one = state.builder.ins().iconst(common_ty, 1);
let is_zero = state.builder.ins().icmp(IntCC::Equal, r, zero);
let safe_r = state.builder.ins().select(is_zero, one, r);
let div_result = state.builder.ins().udiv(l, safe_r);
state.builder.ins().select(is_zero, zero, div_result)
}
BinaryOp::RemU => {
let zero = state.builder.ins().iconst(common_ty, 0);
let one = state.builder.ins().iconst(common_ty, 1);
let is_zero = state.builder.ins().icmp(IntCC::Equal, r, zero);
let safe_r = state.builder.ins().select(is_zero, one, r);
let rem_result = state.builder.ins().urem(l, safe_r);
state.builder.ins().select(is_zero, zero, rem_result)
}
BinaryOp::DivS | BinaryOp::RemS => {
let zero = state.builder.ins().iconst(common_ty, 0);
let one = state.builder.ins().iconst(common_ty, 1);
let neg_one = state.builder.ins().iconst(common_ty, -1);
let physical_bits = common_ty.bits() as usize;
let min_payload = 1u64 << (physical_bits - 1);
let min = state.builder.ins().iconst(common_ty, min_payload as i64);
let is_zero = state.builder.ins().icmp(IntCC::Equal, r, zero);
let is_min = state.builder.ins().icmp(IntCC::Equal, l, min);
let is_neg_one = state.builder.ins().icmp(IntCC::Equal, r, neg_one);
let is_overflow = state.builder.ins().band(is_min, is_neg_one);
let unsafe_divisor = state.builder.ins().bor(is_zero, is_overflow);
let safe_r = state.builder.ins().select(unsafe_divisor, one, r);
let raw = if matches!(op, BinaryOp::DivS) {
state.builder.ins().sdiv(l, safe_r)
} else {
state.builder.ins().srem(l, safe_r)
};
state.builder.ins().select(is_zero, zero, raw)
}
BinaryOp::And => state.builder.ins().band(l, r),
BinaryOp::Or => state.builder.ins().bor(l, r),
BinaryOp::Xor => state.builder.ins().bxor(l, r),
BinaryOp::Shr => {
let shifted = state.builder.ins().ushr(l, r);
apply_d_width_mask(state, shifted, common_ty, d_width)
}
BinaryOp::Shl => {
let shifted = state.builder.ins().ishl(l, r);
apply_d_width_mask(state, shifted, common_ty, d_width)
}
BinaryOp::Sar => {
let raw_shifted = state.builder.ins().sshr(l, r);
apply_d_width_mask_arith(state, raw_shifted, common_ty, d_width)
}
BinaryOp::Eq => build_icmp(state.builder, IntCC::Equal),
BinaryOp::Ne => build_icmp(state.builder, IntCC::NotEqual),
BinaryOp::EqCase => build_icmp(state.builder, IntCC::Equal),
BinaryOp::NeCase => build_icmp(state.builder, IntCC::NotEqual),
BinaryOp::LtS => build_icmp(state.builder, IntCC::SignedLessThan),
BinaryOp::LtU => build_icmp(state.builder, IntCC::UnsignedLessThan),
BinaryOp::GtS => build_icmp(state.builder, IntCC::SignedGreaterThan),
BinaryOp::GtU => build_icmp(state.builder, IntCC::UnsignedGreaterThan),
BinaryOp::LeS => build_icmp(state.builder, IntCC::SignedLessThanOrEqual),
BinaryOp::LeU => build_icmp(state.builder, IntCC::UnsignedLessThanOrEqual),
BinaryOp::GeS => build_icmp(state.builder, IntCC::SignedGreaterThanOrEqual),
BinaryOp::GeU => build_icmp(state.builder, IntCC::UnsignedGreaterThanOrEqual),
BinaryOp::LogicAnd => {
let zero = state.builder.ins().iconst(common_ty, 0);
let l_bool = state.builder.ins().icmp(IntCC::NotEqual, l, zero);
let r_bool = state.builder.ins().icmp(IntCC::NotEqual, r, zero);
let res_bool = state.builder.ins().band(l_bool, r_bool);
let one = state.builder.ins().iconst(common_ty, 1);
state.builder.ins().select(res_bool, one, zero)
}
BinaryOp::LogicOr => {
let zero = state.builder.ins().iconst(common_ty, 0);
let l_bool = state.builder.ins().icmp(IntCC::NotEqual, l, zero);
let r_bool = state.builder.ins().icmp(IntCC::NotEqual, r, zero);
let res_bool = state.builder.ins().bor(l_bool, r_bool);
let one = state.builder.ins().iconst(common_ty, 1);
state.builder.ins().select(res_bool, one, zero)
}
BinaryOp::EqWildcard | BinaryOp::NeWildcard => {
build_icmp(
state.builder,
if matches!(op, BinaryOp::EqWildcard) {
IntCC::Equal
} else {
IntCC::NotEqual
},
)
}
};
let dst_ty = get_cl_type(d_width);
if self.options.four_state {
let l_m_val = state.regs[lhs]
.first_mask(state.builder)
.unwrap_or_else(|| state.builder.ins().iconst(common_ty, 0));
let r_m_val = state.regs[rhs]
.first_mask(state.builder)
.unwrap_or_else(|| state.builder.ins().iconst(common_ty, 0));
let l_m = promote_to_physical(state, l_m_val, l_width, l_is_signed, common_ty);
let r_m = promote_to_physical(state, r_m_val, r_width, r_is_signed, common_ty);
let res_m = match op {
BinaryOp::And => {
let m1 = state.builder.ins().band(l_m, r_m);
let m2 = state.builder.ins().band(l_m, r);
let m3 = state.builder.ins().band(r_m, l);
let m_tmp = state.builder.ins().bor(m1, m2);
state.builder.ins().bor(m_tmp, m3)
}
BinaryOp::Or => {
let m1 = state.builder.ins().band(l_m, r_m);
let not_b_v = state.builder.ins().bnot(r);
let m2 = state.builder.ins().band(l_m, not_b_v);
let not_a_v = state.builder.ins().bnot(l);
let m3 = state.builder.ins().band(r_m, not_a_v);
let m_tmp = state.builder.ins().bor(m1, m2);
state.builder.ins().bor(m_tmp, m3)
}
BinaryOp::Xor => state.builder.ins().bor(l_m, r_m),
BinaryOp::Shr => {
let zero = state.builder.ins().iconst(common_ty, 0);
let shift_amt_has_x = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let shifted_m = state.builder.ins().ushr(l_m, r);
let all_ones = state.builder.ins().iconst(common_ty, -1);
let m = state
.builder
.ins()
.select(shift_amt_has_x, all_ones, shifted_m);
apply_d_width_mask(state, m, common_ty, d_width)
}
BinaryOp::Shl => {
let zero = state.builder.ins().iconst(common_ty, 0);
let shift_amt_has_x = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let shifted_m = state.builder.ins().ishl(l_m, r);
let all_ones = state.builder.ins().iconst(common_ty, -1);
let m = state
.builder
.ins()
.select(shift_amt_has_x, all_ones, shifted_m);
apply_d_width_mask(state, m, common_ty, d_width)
}
BinaryOp::Sar => {
let zero = state.builder.ins().iconst(common_ty, 0);
let shift_amt_has_x = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let shifted_m = state.builder.ins().sshr(l_m, r);
let all_ones = state.builder.ins().iconst(common_ty, -1);
let m = state
.builder
.ins()
.select(shift_amt_has_x, all_ones, shifted_m);
apply_d_width_mask_arith(state, m, common_ty, d_width)
}
BinaryOp::EqCase | BinaryOp::NeCase => {
let value_diff = state.builder.ins().bxor(l, r);
let mask_diff = state.builder.ins().bxor(l_m, r_m);
let diff = state.builder.ins().bor(value_diff, mask_diff);
let zero = state.builder.ins().iconst(common_ty, 0);
let matches = state.builder.ins().icmp(
if matches!(op, BinaryOp::EqCase) {
IntCC::Equal
} else {
IntCC::NotEqual
},
diff,
zero,
);
let one = state.builder.ins().iconst(common_ty, 1);
res_v = state.builder.ins().select(matches, one, zero);
zero
}
BinaryOp::EqWildcard | BinaryOp::NeWildcard => {
let zero = state.builder.ins().iconst(common_ty, 0);
let compare_mask = state.builder.ins().bnot(r_m);
let l_definite = state.builder.ins().bnot(l_m);
let definite_compare = state.builder.ins().band(compare_mask, l_definite);
let l_xor_r = state.builder.ins().bxor(l, r);
let mismatch_bits = state.builder.ins().band(l_xor_r, definite_compare);
let has_definite_mismatch =
state
.builder
.ins()
.icmp(IntCC::NotEqual, mismatch_bits, zero);
let x_at_compared = state.builder.ins().band(l_m, compare_mask);
let has_x_at_compared =
state
.builder
.ins()
.icmp(IntCC::NotEqual, x_at_compared, zero);
let all_ones = state.builder.ins().iconst(common_ty, -1);
let x_mask = state
.builder
.ins()
.select(has_x_at_compared, all_ones, zero);
let mask = state
.builder
.ins()
.select(has_definite_mismatch, zero, x_mask);
let l_eff = state.builder.ins().band(l, definite_compare);
let r_eff = state.builder.ins().band(r, definite_compare);
let cmp_result = state.builder.ins().icmp(
if matches!(op, BinaryOp::EqWildcard) {
IntCC::Equal
} else {
IntCC::NotEqual
},
l_eff,
r_eff,
);
let one = state.builder.ins().iconst(common_ty, 1);
res_v = state.builder.ins().select(cmp_result, one, zero);
mask
}
BinaryOp::LogicAnd => {
let zero = state.builder.ins().iconst(common_ty, 0);
let l_val_or_mask = state.builder.ins().bor(l, l_m);
let r_val_or_mask = state.builder.ins().bor(r, r_m);
let l_def_false =
state.builder.ins().icmp(IntCC::Equal, l_val_or_mask, zero);
let r_def_false =
state.builder.ins().icmp(IntCC::Equal, r_val_or_mask, zero);
let either_def_false = state.builder.ins().bor(l_def_false, r_def_false);
let any_x_l = state.builder.ins().icmp(IntCC::NotEqual, l_m, zero);
let any_x_r = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let any_x = state.builder.ins().bor(any_x_l, any_x_r);
let all_ones = state.builder.ins().iconst(common_ty, -1);
let conservative = state.builder.ins().select(any_x, all_ones, zero);
state
.builder
.ins()
.select(either_def_false, zero, conservative)
}
BinaryOp::LogicOr => {
let zero = state.builder.ins().iconst(common_ty, 0);
let not_l_m = state.builder.ins().bnot(l_m);
let l_definite_v = state.builder.ins().band(l, not_l_m);
let not_r_m = state.builder.ins().bnot(r_m);
let r_definite_v = state.builder.ins().band(r, not_r_m);
let l_def_true =
state
.builder
.ins()
.icmp(IntCC::NotEqual, l_definite_v, zero);
let r_def_true =
state
.builder
.ins()
.icmp(IntCC::NotEqual, r_definite_v, zero);
let either_def_true = state.builder.ins().bor(l_def_true, r_def_true);
let any_x_l = state.builder.ins().icmp(IntCC::NotEqual, l_m, zero);
let any_x_r = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let any_x = state.builder.ins().bor(any_x_l, any_x_r);
let all_ones = state.builder.ins().iconst(common_ty, -1);
let conservative = state.builder.ins().select(any_x, all_ones, zero);
state
.builder
.ins()
.select(either_def_true, zero, conservative)
}
_ => {
let zero = state.builder.ins().iconst(common_ty, 0);
let any_x_l = state.builder.ins().icmp(IntCC::NotEqual, l_m, zero);
let any_x_r = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let any_x = state.builder.ins().bor(any_x_l, any_x_r);
let all_ones = state.builder.ins().iconst(common_ty, -1);
state.builder.ins().select(any_x, all_ones, zero)
}
};
let final_res_v = promote_to_physical(state, res_v, d_width, false, dst_ty);
let final_res_m = promote_to_physical(state, res_m, d_width, false, dst_ty);
let normalized_v = state.builder.ins().bor(final_res_v, final_res_m);
state.regs.insert(
*dst,
TransValue::FourState {
values: vec![normalized_v],
masks: vec![final_res_m],
},
);
} else {
let final_res = promote_to_physical(state, res_v, d_width, false, dst_ty);
state
.regs
.insert(*dst, TransValue::TwoState(vec![final_res]));
}
} else {
let num_chunks = common_logical_width.div_ceil(64);
let final_num_chunks = d_width.div_ceil(64);
let is_shift = matches!(op, BinaryOp::Shl | BinaryOp::Shr | BinaryOp::Sar);
if is_shift && num_chunks >= MEM_SHIFT_THRESHOLD {
let src_chunks = state.regs[lhs].load_value_chunks(state.builder);
let (_, l_addr) = alloc_stack_slot(state.builder, num_chunks);
for (i, &v) in src_chunks.iter().enumerate() {
state
.builder
.ins()
.store(MemFlags::new(), v, l_addr, (i * 8) as i32);
}
let zero_pad = state.builder.ins().iconst(types::I64, 0);
for i in src_chunks.len()..num_chunks {
state
.builder
.ins()
.store(MemFlags::new(), zero_pad, l_addr, (i * 8) as i32);
}
let r_chunks = state.regs[rhs].load_value_chunks(state.builder);
let (_, dst_addr) = alloc_stack_slot(state.builder, num_chunks);
if matches!(op, BinaryOp::Sar) {
wide_ops::emit_wide_sar_mem(
state.builder,
l_addr,
&r_chunks,
dst_addr,
num_chunks,
l_width,
);
} else {
wide_ops::emit_wide_shift_mem(
state.builder,
op,
l_addr,
&r_chunks,
dst_addr,
num_chunks,
);
}
let mask_addr = if self.options.four_state {
let l_masks: Vec<Value> = state.regs[lhs]
.load_mask_chunks(state.builder)
.unwrap_or_else(|| {
(0..num_chunks)
.map(|_| state.builder.ins().iconst(types::I64, 0))
.collect()
});
let r_masks: Vec<Value> = state.regs[rhs]
.load_mask_chunks(state.builder)
.unwrap_or_else(|| {
(0..num_chunks)
.map(|_| state.builder.ins().iconst(types::I64, 0))
.collect()
});
let mut r_any_x = state.builder.ins().iconst(types::I64, 0);
for m in &r_masks {
let m_i64 = cast_type(state.builder, *m, types::I64);
r_any_x = state.builder.ins().bor(r_any_x, m_i64);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let shift_has_x = state.builder.ins().icmp(IntCC::NotEqual, r_any_x, zero);
let (_, l_mask_addr) = alloc_stack_slot(state.builder, num_chunks);
for (i, &m) in l_masks.iter().enumerate() {
state
.builder
.ins()
.store(MemFlags::new(), m, l_mask_addr, (i * 8) as i32);
}
let zero_mask_pad = state.builder.ins().iconst(types::I64, 0);
for i in l_masks.len()..num_chunks {
state.builder.ins().store(
MemFlags::new(),
zero_mask_pad,
l_mask_addr,
(i * 8) as i32,
);
}
let (_, mask_dst_addr) = alloc_stack_slot(state.builder, num_chunks);
if matches!(op, BinaryOp::Sar) {
wide_ops::emit_wide_sar_mem(
state.builder,
l_mask_addr,
&r_chunks,
mask_dst_addr,
num_chunks,
l_width,
);
} else {
wide_ops::emit_wide_shift_mem(
state.builder,
op,
l_mask_addr,
&r_chunks,
mask_dst_addr,
num_chunks,
);
}
let all_ones = state.builder.ins().iconst(types::I64, -1i64);
for i in 0..num_chunks {
let m = state.builder.ins().load(
types::I64,
MemFlags::new(),
mask_dst_addr,
(i * 8) as i32,
);
let selected = state.builder.ins().select(shift_has_x, all_ones, m);
state.builder.ins().store(
MemFlags::new(),
selected,
mask_dst_addr,
(i * 8) as i32,
);
}
Some(mask_dst_addr)
} else {
None
};
if final_num_chunks >= MEM_SHIFT_THRESHOLD {
state.regs.insert(
*dst,
TransValue::MemBacked {
addr: dst_addr,
num_chunks: final_num_chunks,
mask_addr,
},
);
} else {
let res_chunks: Vec<Value> = (0..final_num_chunks)
.map(|i| {
state.builder.ins().load(
types::I64,
MemFlags::new(),
dst_addr,
(i * 8) as i32,
)
})
.collect();
if self.options.four_state {
let res_masks: Vec<Value> = if let Some(ma) = mask_addr {
(0..final_num_chunks)
.map(|i| {
state.builder.ins().load(
types::I64,
MemFlags::new(),
ma,
(i * 8) as i32,
)
})
.collect()
} else {
(0..final_num_chunks)
.map(|_| state.builder.ins().iconst(types::I64, 0))
.collect()
};
let normalized: Vec<_> = res_chunks
.iter()
.zip(res_masks.iter())
.map(|(&v, &m)| state.builder.ins().bor(v, m))
.collect();
state.regs.insert(
*dst,
TransValue::FourState {
values: normalized,
masks: res_masks,
},
);
} else {
state.regs.insert(*dst, TransValue::TwoState(res_chunks));
}
}
} else {
let l_chunks = state.regs[lhs].load_value_chunks(state.builder);
let r_chunks = state.regs[rhs].load_value_chunks(state.builder);
let mut res_chunks = if matches!(op, BinaryOp::LogicAnd | BinaryOp::LogicOr) {
wide_ops::emit_wide_logic_andor(
state.builder,
op,
&l_chunks,
&r_chunks,
num_chunks,
)
} else {
wide_ops::emit_wide_binary(
state.builder,
op,
&l_chunks,
&r_chunks,
num_chunks,
l_width,
r_width,
common_logical_width,
)
};
res_chunks.truncate(final_num_chunks);
while res_chunks.len() < final_num_chunks {
res_chunks.push(state.builder.ins().iconst(types::I64, 0));
}
if self.options.four_state {
let l_masks: Vec<Value> = state.regs[lhs]
.load_mask_chunks(state.builder)
.unwrap_or_else(|| {
(0..num_chunks)
.map(|_| state.builder.ins().iconst(types::I64, 0))
.collect()
});
let r_masks: Vec<Value> = state.regs[rhs]
.load_mask_chunks(state.builder)
.unwrap_or_else(|| {
(0..num_chunks)
.map(|_| state.builder.ins().iconst(types::I64, 0))
.collect()
});
let mut res_masks = match op {
BinaryOp::And => (0..num_chunks)
.map(|i| {
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let lv = get_chunk_as_i64(state.builder, &l_chunks, i);
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let m1 = state.builder.ins().band(lm, rm);
let m2 = state.builder.ins().band(lm, rv);
let m3 = state.builder.ins().band(rm, lv);
let mt = state.builder.ins().bor(m1, m2);
state.builder.ins().bor(mt, m3)
})
.collect(),
BinaryOp::Or => (0..num_chunks)
.map(|i| {
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let lv = get_chunk_as_i64(state.builder, &l_chunks, i);
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let m1 = state.builder.ins().band(lm, rm);
let m2 = state.builder.ins().band_not(lm, rv);
let m3 = state.builder.ins().band_not(rm, lv);
let mt = state.builder.ins().bor(m1, m2);
state.builder.ins().bor(mt, m3)
})
.collect(),
BinaryOp::Xor => (0..num_chunks)
.map(|i| {
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
state.builder.ins().bor(lm, rm)
})
.collect(),
BinaryOp::Shr | BinaryOp::Shl | BinaryOp::Sar => {
let mut r_any_x = state.builder.ins().iconst(types::I64, 0);
for m in &r_masks {
let m_i64 = cast_type(state.builder, *m, types::I64);
r_any_x = state.builder.ins().bor(r_any_x, m_i64);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let shift_has_x =
state.builder.ins().icmp(IntCC::NotEqual, r_any_x, zero);
let shifted_masks = if matches!(op, BinaryOp::Sar) {
wide_ops::emit_wide_sar(
state.builder,
&l_masks,
&r_chunks,
num_chunks,
l_width,
)
} else {
wide_ops::emit_wide_shift(
state.builder,
op,
&l_masks,
&r_chunks,
num_chunks,
)
};
let all_ones = state.builder.ins().iconst(types::I64, -1i64);
shifted_masks
.into_iter()
.map(|m| state.builder.ins().select(shift_has_x, all_ones, m))
.collect()
}
BinaryOp::EqWildcard | BinaryOp::NeWildcard => {
let mut accumulated_mismatch =
state.builder.ins().iconst(types::I64, 0);
let mut accumulated_x = state.builder.ins().iconst(types::I64, 0);
let effective_l: Vec<Value> = (0..num_chunks)
.map(|i| {
let lv = get_chunk_as_i64(state.builder, &l_chunks, i);
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let compare_mask = state.builder.ins().bnot(rm);
let l_definite = state.builder.ins().bnot(lm);
let definite_compare =
state.builder.ins().band(compare_mask, l_definite);
state.builder.ins().band(lv, definite_compare)
})
.collect();
let effective_r: Vec<Value> = (0..num_chunks)
.map(|i| {
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let compare_mask = state.builder.ins().bnot(rm);
let l_definite = state.builder.ins().bnot(lm);
let definite_compare =
state.builder.ins().band(compare_mask, l_definite);
state.builder.ins().band(rv, definite_compare)
})
.collect();
for i in 0..num_chunks {
let lv = get_chunk_as_i64(state.builder, &l_chunks, i);
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let compare_mask = state.builder.ins().bnot(rm);
let l_definite = state.builder.ins().bnot(lm);
let definite_compare =
state.builder.ins().band(compare_mask, l_definite);
let xor_bits = state.builder.ins().bxor(lv, rv);
let mismatch = state.builder.ins().band(xor_bits, definite_compare);
accumulated_mismatch =
state.builder.ins().bor(accumulated_mismatch, mismatch);
let x_at = state.builder.ins().band(lm, compare_mask);
accumulated_x = state.builder.ins().bor(accumulated_x, x_at);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let has_mismatch = state.builder.ins().icmp(
IntCC::NotEqual,
accumulated_mismatch,
zero,
);
let has_x =
state
.builder
.ins()
.icmp(IntCC::NotEqual, accumulated_x, zero);
let all_ones = state.builder.ins().iconst(types::I64, -1i64);
let x_mask = state.builder.ins().select(has_x, all_ones, zero);
let mask_val = state.builder.ins().select(has_mismatch, zero, x_mask);
let cmp_op = if matches!(op, BinaryOp::EqWildcard) {
&BinaryOp::Eq
} else {
&BinaryOp::Ne
};
let mut new_res = wide_ops::emit_wide_unsigned_cmp(
state.builder,
cmp_op,
&effective_l,
&effective_r,
num_chunks,
);
new_res.truncate(final_num_chunks);
while new_res.len() < final_num_chunks {
new_res.push(state.builder.ins().iconst(types::I64, 0));
}
res_chunks = new_res;
vec![mask_val; final_num_chunks]
}
BinaryOp::EqCase | BinaryOp::NeCase => {
let mut accumulated_diff = state.builder.ins().iconst(types::I64, 0);
for i in 0..num_chunks {
let lv = get_chunk_as_i64(state.builder, &l_chunks, i);
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let value_diff = state.builder.ins().bxor(lv, rv);
let mask_diff = state.builder.ins().bxor(lm, rm);
let chunk_diff = state.builder.ins().bor(value_diff, mask_diff);
accumulated_diff =
state.builder.ins().bor(accumulated_diff, chunk_diff);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let matches = state.builder.ins().icmp(
if matches!(op, BinaryOp::EqCase) {
IntCC::Equal
} else {
IntCC::NotEqual
},
accumulated_diff,
zero,
);
let one = state.builder.ins().iconst(types::I64, 1);
let result = state.builder.ins().select(matches, one, zero);
res_chunks.clear();
res_chunks.push(result);
while res_chunks.len() < final_num_chunks {
res_chunks.push(state.builder.ins().iconst(types::I64, 0));
}
(0..final_num_chunks)
.map(|_| state.builder.ins().iconst(types::I64, 0))
.collect()
}
BinaryOp::LogicAnd | BinaryOp::LogicOr => {
let mut l_val_or = state.builder.ins().iconst(types::I64, 0);
let mut r_val_or = state.builder.ins().iconst(types::I64, 0);
let mut l_mask_or = state.builder.ins().iconst(types::I64, 0);
let mut r_mask_or = state.builder.ins().iconst(types::I64, 0);
for i in 0..num_chunks {
let lv = get_chunk_as_i64(state.builder, &l_chunks, i);
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
l_val_or = state.builder.ins().bor(l_val_or, lv);
r_val_or = state.builder.ins().bor(r_val_or, rv);
l_mask_or = state.builder.ins().bor(l_mask_or, lm);
r_mask_or = state.builder.ins().bor(r_mask_or, rm);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let any_x_all = state.builder.ins().bor(l_mask_or, r_mask_or);
let has_x = state.builder.ins().icmp(IntCC::NotEqual, any_x_all, zero);
let all_ones = state.builder.ins().iconst(types::I64, -1i64);
let conservative = state.builder.ins().select(has_x, all_ones, zero);
let dominant = if matches!(op, BinaryOp::LogicAnd) {
let l_vm = state.builder.ins().bor(l_val_or, l_mask_or);
let r_vm = state.builder.ins().bor(r_val_or, r_mask_or);
let l_def_false =
state.builder.ins().icmp(IntCC::Equal, l_vm, zero);
let r_def_false =
state.builder.ins().icmp(IntCC::Equal, r_vm, zero);
state.builder.ins().bor(l_def_false, r_def_false)
} else {
let mut l_definite_or = state.builder.ins().iconst(types::I64, 0);
let mut r_definite_or = state.builder.ins().iconst(types::I64, 0);
for i in 0..num_chunks {
let lv = get_chunk_as_i64(state.builder, &l_chunks, i);
let lm = get_chunk_as_i64(state.builder, &l_masks, i);
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let not_lm = state.builder.ins().bnot(lm);
let l_def = state.builder.ins().band(lv, not_lm);
let not_rm = state.builder.ins().bnot(rm);
let r_def = state.builder.ins().band(rv, not_rm);
l_definite_or = state.builder.ins().bor(l_definite_or, l_def);
r_definite_or = state.builder.ins().bor(r_definite_or, r_def);
}
let l_def_true =
state
.builder
.ins()
.icmp(IntCC::NotEqual, l_definite_or, zero);
let r_def_true =
state
.builder
.ins()
.icmp(IntCC::NotEqual, r_definite_or, zero);
state.builder.ins().bor(l_def_true, r_def_true)
};
let mask_val = state.builder.ins().select(dominant, zero, conservative);
vec![mask_val; final_num_chunks]
}
_ => {
let mut any_x = state.builder.ins().iconst(types::I64, 0);
for m in l_masks.iter().chain(r_masks.iter()) {
let m_i64 = cast_type(state.builder, *m, types::I64);
any_x = state.builder.ins().bor(any_x, m_i64);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let has_x = state.builder.ins().icmp(IntCC::NotEqual, any_x, zero);
let all_ones = state.builder.ins().iconst(types::I64, -1i64);
let mask_val = state.builder.ins().select(has_x, all_ones, zero);
vec![mask_val; final_num_chunks]
}
};
res_masks.truncate(final_num_chunks);
while res_masks.len() < final_num_chunks {
res_masks.push(state.builder.ins().iconst(types::I64, 0));
}
let last_chunk_bits = d_width % 64;
if last_chunk_bits != 0 && !res_masks.is_empty() {
let width_mask_val = ((1u64 << last_chunk_bits) - 1) as i64;
let width_mask = state.builder.ins().iconst(types::I64, width_mask_val);
let last_idx = res_masks.len() - 1;
res_masks[last_idx] =
state.builder.ins().band(res_masks[last_idx], width_mask);
}
let normalized: Vec<_> = res_chunks
.iter()
.zip(res_masks.iter())
.map(|(&v, &m)| state.builder.ins().bor(v, m))
.collect();
state.regs.insert(
*dst,
TransValue::FourState {
values: normalized,
masks: res_masks,
},
);
} else {
state.regs.insert(*dst, TransValue::TwoState(res_chunks));
}
}
}
}
pub(super) fn translate_unary_inst(
&self,
state: &mut TranslationState,
dst: &RegisterId,
op: &UnaryOp,
rhs: &RegisterId,
) {
if matches!(
op,
UnaryOp::PopCount | UnaryOp::CountLeadingZeros | UnaryOp::CountTrailingZeros
) {
self.translate_count_unary_inst(state, dst, op, rhs);
return;
}
let r_width = state.register_map[rhs].width();
let d_width = state.register_map[dst].width();
let common_logical_width = r_width.max(d_width);
if common_logical_width <= 64 {
let r_val = state.regs[rhs].first_value(state.builder);
let common_ty = get_cl_type(common_logical_width);
let r_is_signed = state.register_map[rhs].is_signed() || matches!(op, UnaryOp::Minus);
let r = promote_to_physical(state, r_val, r_width, r_is_signed, common_ty);
let res_v = match op {
UnaryOp::Minus => state.builder.ins().ineg(r),
UnaryOp::Ident | UnaryOp::ToTwoState => r,
UnaryOp::BitNot => {
let notted = state.builder.ins().bnot(r);
let phys_bits = common_ty.bits() as usize;
if d_width < phys_bits {
let mask = ((1u64 << d_width) - 1) as i64;
state.builder.ins().band_imm_s(notted, mask)
} else {
notted
}
}
UnaryOp::LogicNot => {
let zero = state.builder.ins().iconst(common_ty, 0);
let is_zero = state.builder.ins().icmp(IntCC::Equal, r, zero);
let one_val = state.builder.ins().iconst(common_ty, 1);
state.builder.ins().select(is_zero, one_val, zero)
}
UnaryOp::Or => {
let zero = state.builder.ins().iconst(common_ty, 0);
let is_not_zero = state.builder.ins().icmp(IntCC::NotEqual, r, zero);
let one_val = state.builder.ins().iconst(common_ty, 1);
state.builder.ins().select(is_not_zero, one_val, zero)
}
UnaryOp::Xor => {
let popcnt = state.builder.ins().popcnt(r);
state.builder.ins().band_imm_s(popcnt, 1)
}
UnaryOp::And => {
let mask = if r_width >= 128 {
!0u128
} else {
(1u128 << r_width) - 1
};
let full_val = state.builder.ins().iconst(common_ty, mask as i64);
let is_all_ones = state.builder.ins().icmp(IntCC::Equal, r, full_val);
let zero = state.builder.ins().iconst(common_ty, 0);
let one_val = state.builder.ins().iconst(common_ty, 1);
state.builder.ins().select(is_all_ones, one_val, zero)
}
UnaryOp::PopCount | UnaryOp::CountLeadingZeros | UnaryOp::CountTrailingZeros => {
unreachable!()
}
};
let dst_ty = get_cl_type(d_width);
if self.options.four_state {
let r_m_val = state.regs[rhs]
.first_mask(state.builder)
.unwrap_or_else(|| state.builder.ins().iconst(common_ty, 0));
let r_m = promote_to_physical(state, r_m_val, r_width, r_is_signed, common_ty);
let res_m = match op {
UnaryOp::Ident | UnaryOp::BitNot => r_m,
UnaryOp::ToTwoState => state.builder.ins().iconst(common_ty, 0),
UnaryOp::LogicNot | UnaryOp::Or => {
let not_m = state.builder.ins().bnot(r_m);
let definite_ones = state.builder.ins().band(r, not_m);
let zero = state.builder.ins().iconst(common_ty, 0);
let has_definite_one =
state
.builder
.ins()
.icmp(IntCC::NotEqual, definite_ones, zero);
let has_any_x = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let all_ones = state.builder.ins().iconst(common_ty, -1);
let x_mask = state.builder.ins().select(has_any_x, all_ones, zero);
state.builder.ins().select(has_definite_one, zero, x_mask)
}
UnaryOp::And => {
let not_m = state.builder.ins().bnot(r_m);
let not_v = state.builder.ins().bnot(r);
let definite_zeros = state.builder.ins().band(not_v, not_m);
let width_mask_val = if r_width >= 64 {
-1i64
} else {
((1u64 << r_width) - 1) as i64
};
let width_mask = state.builder.ins().iconst(common_ty, width_mask_val);
let definite_zeros_masked =
state.builder.ins().band(definite_zeros, width_mask);
let zero = state.builder.ins().iconst(common_ty, 0);
let has_definite_zero =
state
.builder
.ins()
.icmp(IntCC::NotEqual, definite_zeros_masked, zero);
let has_any_x = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let all_ones = state.builder.ins().iconst(common_ty, -1);
let x_mask = state.builder.ins().select(has_any_x, all_ones, zero);
state.builder.ins().select(has_definite_zero, zero, x_mask)
}
UnaryOp::Minus
| UnaryOp::Xor
| UnaryOp::PopCount
| UnaryOp::CountLeadingZeros
| UnaryOp::CountTrailingZeros => {
let zero = state.builder.ins().iconst(common_ty, 0);
let any_x = state.builder.ins().icmp(IntCC::NotEqual, r_m, zero);
let all_ones = state.builder.ins().iconst(common_ty, -1);
state.builder.ins().select(any_x, all_ones, zero)
}
};
let final_res_v = promote_to_physical(state, res_v, d_width, false, dst_ty);
let final_res_m = promote_to_physical(state, res_m, d_width, false, dst_ty);
let normalized_v = match op {
UnaryOp::Ident => final_res_v,
UnaryOp::ToTwoState => {
let final_src_m = if common_ty.bits() > dst_ty.bits() {
state.builder.ins().ireduce(dst_ty, r_m)
} else {
r_m
};
let defined = state.builder.ins().bnot(final_src_m);
state.builder.ins().band(final_res_v, defined)
}
_ => state.builder.ins().bor(final_res_v, final_res_m),
};
state.regs.insert(
*dst,
TransValue::FourState {
values: vec![normalized_v],
masks: vec![final_res_m],
},
);
} else {
let final_res = promote_to_physical(state, res_v, d_width, false, dst_ty);
state
.regs
.insert(*dst, TransValue::TwoState(vec![final_res]));
}
} else {
let num_chunks = common_logical_width.div_ceil(64);
let r_chunks = state.regs[rhs].load_value_chunks(state.builder);
let mut res_chunks = wide_ops::emit_wide_unary(
state.builder,
op,
&r_chunks,
num_chunks,
common_logical_width,
);
let final_num_chunks = d_width.div_ceil(64);
res_chunks.truncate(final_num_chunks);
while res_chunks.len() < final_num_chunks {
res_chunks.push(state.builder.ins().iconst(types::I64, 0));
}
if self.options.four_state {
let r_masks: Vec<Value> = state.regs[rhs]
.load_mask_chunks(state.builder)
.unwrap_or_else(|| {
(0..num_chunks)
.map(|_| state.builder.ins().iconst(types::I64, 0))
.collect()
});
let mut res_masks = match op {
UnaryOp::Ident | UnaryOp::BitNot => {
let mut masks = r_masks.clone();
masks.truncate(final_num_chunks);
while masks.len() < final_num_chunks {
masks.push(state.builder.ins().iconst(types::I64, 0));
}
masks
}
UnaryOp::ToTwoState => (0..final_num_chunks)
.map(|_| state.builder.ins().iconst(types::I64, 0))
.collect(),
UnaryOp::LogicNot | UnaryOp::Or => {
let mut accumulated_definite_ones =
state.builder.ins().iconst(types::I64, 0);
let mut accumulated_mask = state.builder.ins().iconst(types::I64, 0);
for i in 0..num_chunks {
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let not_m = state.builder.ins().bnot(rm);
let definite_ones = state.builder.ins().band(rv, not_m);
accumulated_definite_ones = state
.builder
.ins()
.bor(accumulated_definite_ones, definite_ones);
accumulated_mask = state.builder.ins().bor(accumulated_mask, rm);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let has_definite_one = state.builder.ins().icmp(
IntCC::NotEqual,
accumulated_definite_ones,
zero,
);
let has_any_x =
state
.builder
.ins()
.icmp(IntCC::NotEqual, accumulated_mask, zero);
let all_ones = state.builder.ins().iconst(types::I64, -1i64);
let x_mask = state.builder.ins().select(has_any_x, all_ones, zero);
let mask_val = state.builder.ins().select(has_definite_one, zero, x_mask);
vec![mask_val; final_num_chunks]
}
UnaryOp::And => {
let mut accumulated_definite_zeros =
state.builder.ins().iconst(types::I64, 0);
let mut accumulated_mask = state.builder.ins().iconst(types::I64, 0);
for i in 0..num_chunks {
let rv = get_chunk_as_i64(state.builder, &r_chunks, i);
let rm = get_chunk_as_i64(state.builder, &r_masks, i);
let not_m = state.builder.ins().bnot(rm);
let not_v = state.builder.ins().bnot(rv);
let mut definite_zeros = state.builder.ins().band(not_v, not_m);
if i == num_chunks - 1 {
let remaining = common_logical_width - i * 64;
if remaining < 64 {
let last_chunk_mask = ((1u64 << remaining) - 1) as i64;
let mask_val =
state.builder.ins().iconst(types::I64, last_chunk_mask);
definite_zeros =
state.builder.ins().band(definite_zeros, mask_val);
}
}
accumulated_definite_zeros = state
.builder
.ins()
.bor(accumulated_definite_zeros, definite_zeros);
accumulated_mask = state.builder.ins().bor(accumulated_mask, rm);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let has_definite_zero = state.builder.ins().icmp(
IntCC::NotEqual,
accumulated_definite_zeros,
zero,
);
let has_any_x =
state
.builder
.ins()
.icmp(IntCC::NotEqual, accumulated_mask, zero);
let all_ones = state.builder.ins().iconst(types::I64, -1i64);
let x_mask = state.builder.ins().select(has_any_x, all_ones, zero);
let mask_val = state.builder.ins().select(has_definite_zero, zero, x_mask);
vec![mask_val; final_num_chunks]
}
_ => {
let mut any_x = state.builder.ins().iconst(types::I64, 0);
for m in &r_masks {
any_x = state.builder.ins().bor(any_x, *m);
}
let zero = state.builder.ins().iconst(types::I64, 0);
let has_x = state.builder.ins().icmp(IntCC::NotEqual, any_x, zero);
let all_ones = state.builder.ins().iconst(types::I64, -1i64);
let mask_val = state.builder.ins().select(has_x, all_ones, zero);
vec![mask_val; final_num_chunks]
}
};
res_masks.truncate(final_num_chunks);
while res_masks.len() < final_num_chunks {
res_masks.push(state.builder.ins().iconst(types::I64, 0));
}
let last_chunk_bits = d_width % 64;
if last_chunk_bits != 0 && !res_masks.is_empty() {
let width_mask_val = ((1u64 << last_chunk_bits) - 1) as i64;
let width_mask = state.builder.ins().iconst(types::I64, width_mask_val);
let last_idx = res_masks.len() - 1;
res_masks[last_idx] = state.builder.ins().band(res_masks[last_idx], width_mask);
}
let normalized: Vec<_> = match op {
UnaryOp::Ident => res_chunks,
UnaryOp::ToTwoState => res_chunks
.iter()
.enumerate()
.map(|(index, &value)| {
let source_mask = get_chunk_as_i64(state.builder, &r_masks, index);
let defined = state.builder.ins().bnot(source_mask);
state.builder.ins().band(value, defined)
})
.collect(),
_ => res_chunks
.iter()
.zip(res_masks.iter())
.map(|(&v, &m)| state.builder.ins().bor(v, m))
.collect(),
};
state.regs.insert(
*dst,
TransValue::FourState {
values: normalized,
masks: res_masks,
},
);
} else {
state.regs.insert(*dst, TransValue::TwoState(res_chunks));
}
}
}
pub(super) fn translate_mux_inst(
&self,
state: &mut TranslationState,
dst: &RegisterId,
cond: &RegisterId,
then_val: &RegisterId,
else_val: &RegisterId,
) {
let d_width = state.register_map[dst].width();
let cond_width = state.register_map[cond].width();
let zero = state.builder.ins().iconst(types::I64, 0);
let cond_chunks = state.regs[cond].load_value_chunks(state.builder);
let cond_masks = state.regs[cond]
.load_mask_chunks(state.builder)
.unwrap_or_default();
let cond_n_chunks = cond_width.div_ceil(64).max(1);
let mut known_one_bits = zero;
let mut unknown_bits = zero;
for index in 0..cond_n_chunks {
let mut value = get_chunk_as_i64(state.builder, &cond_chunks, index);
let mut mask = get_chunk_as_i64(state.builder, &cond_masks, index);
if index + 1 == cond_n_chunks && !cond_width.is_multiple_of(64) {
let logical_mask = ((1u64 << (cond_width % 64)) - 1) as i64;
value = state.builder.ins().band_imm_s(value, logical_mask);
mask = state.builder.ins().band_imm_s(mask, logical_mask);
}
let defined = state.builder.ins().bnot(mask);
let known_ones = state.builder.ins().band(value, defined);
known_one_bits = state.builder.ins().bor(known_one_bits, known_ones);
unknown_bits = state.builder.ins().bor(unknown_bits, mask);
}
let cond_is_true = state
.builder
.ins()
.icmp(IntCC::NotEqual, known_one_bits, zero);
let cond_has_unknown = state
.builder
.ins()
.icmp(IntCC::NotEqual, unknown_bits, zero);
let cond_is_not_true = state.builder.ins().icmp(IntCC::Equal, known_one_bits, zero);
let cond_is_unknown = state.builder.ins().band(cond_has_unknown, cond_is_not_true);
if d_width <= 64 {
let ty = get_cl_type(d_width);
let tv_raw = state.regs[then_val].first_value(state.builder);
let ev_raw = state.regs[else_val].first_value(state.builder);
let tv = cast_type(state.builder, tv_raw, ty);
let ev = cast_type(state.builder, ev_raw, ty);
let selected_value = state.builder.ins().select(cond_is_true, tv, ev);
if self.options.four_state {
let then_mc = state.regs[then_val]
.load_mask_chunks(state.builder)
.unwrap_or_else(|| vec![state.builder.ins().iconst(ty, 0)]);
let else_mc = state.regs[else_val]
.load_mask_chunks(state.builder)
.unwrap_or_else(|| vec![state.builder.ins().iconst(ty, 0)]);
let tm = cast_type(state.builder, then_mc[0], ty);
let em = cast_type(state.builder, else_mc[0], ty);
let selected_mask = state.builder.ins().select(cond_is_true, tm, em);
let value_diff = state.builder.ins().bxor(tv, ev);
let mask_diff = state.builder.ins().bxor(tm, em);
let diff = state.builder.ins().bor(value_diff, mask_diff);
let unknown_value = state.builder.ins().bor(tv, diff);
let unknown_mask = state.builder.ins().bor(tm, diff);
let result =
state
.builder
.ins()
.select(cond_is_unknown, unknown_value, selected_value);
let result_mask =
state
.builder
.ins()
.select(cond_is_unknown, unknown_mask, selected_mask);
let result = apply_d_width_mask(state, result, ty, d_width);
let result_mask = apply_d_width_mask(state, result_mask, ty, d_width);
state.regs.insert(
*dst,
TransValue::FourState {
values: vec![result],
masks: vec![result_mask],
},
);
} else {
let result = apply_d_width_mask(state, selected_value, ty, d_width);
state.regs.insert(*dst, TransValue::TwoState(vec![result]));
}
} else {
let n_chunks = d_width.div_ceil(64);
let tv_chunks = state.regs[then_val].load_value_chunks(state.builder);
let ev_chunks = state.regs[else_val].load_value_chunks(state.builder);
let tm_chunks = state.regs[then_val]
.load_mask_chunks(state.builder)
.unwrap_or_default();
let em_chunks = state.regs[else_val]
.load_mask_chunks(state.builder)
.unwrap_or_default();
let mut res_chunks = Vec::with_capacity(n_chunks);
let mut mask_chunks = Vec::with_capacity(n_chunks);
for i in 0..n_chunks {
let tv = get_chunk_as_i64(state.builder, &tv_chunks, i);
let ev = get_chunk_as_i64(state.builder, &ev_chunks, i);
let selected_value = state.builder.ins().select(cond_is_true, tv, ev);
let mut result = selected_value;
if self.options.four_state {
let tm = get_chunk_as_i64(state.builder, &tm_chunks, i);
let em = get_chunk_as_i64(state.builder, &em_chunks, i);
let selected_mask = state.builder.ins().select(cond_is_true, tm, em);
let value_diff = state.builder.ins().bxor(tv, ev);
let mask_diff = state.builder.ins().bxor(tm, em);
let diff = state.builder.ins().bor(value_diff, mask_diff);
let unknown_value = state.builder.ins().bor(tv, diff);
let unknown_mask = state.builder.ins().bor(tm, diff);
result =
state
.builder
.ins()
.select(cond_is_unknown, unknown_value, selected_value);
let result_mask =
state
.builder
.ins()
.select(cond_is_unknown, unknown_mask, selected_mask);
mask_chunks.push(result_mask);
}
res_chunks.push(result);
}
if !d_width.is_multiple_of(64) {
let logical_mask = ((1u64 << (d_width % 64)) - 1) as i64;
let last = n_chunks - 1;
res_chunks[last] = state
.builder
.ins()
.band_imm_s(res_chunks[last], logical_mask);
if self.options.four_state {
mask_chunks[last] = state
.builder
.ins()
.band_imm_s(mask_chunks[last], logical_mask);
}
}
if self.options.four_state {
state.regs.insert(
*dst,
TransValue::FourState {
values: res_chunks,
masks: mask_chunks,
},
);
} else {
state.regs.insert(*dst, TransValue::TwoState(res_chunks));
}
}
}
fn translate_count_unary_inst(
&self,
state: &mut TranslationState,
dst: &RegisterId,
op: &UnaryOp,
rhs: &RegisterId,
) {
let r_width = state.register_map[rhs].width();
let d_width = state.register_map[dst].width();
let r_chunks = state.regs[rhs].load_value_chunks(state.builder);
let count = wide_ops::emit_wide_bit_count(state.builder, op, &r_chunks, r_width);
let final_num_chunks = d_width.div_ceil(64);
let zero = state.builder.ins().iconst(types::I64, 0);
let mut values = if d_width <= 64 {
let dst_ty = get_cl_type(d_width);
let value = cast_type(state.builder, count, dst_ty);
vec![apply_d_width_mask(state, value, dst_ty, d_width)]
} else {
std::iter::once(count)
.chain(std::iter::repeat_n(
zero,
final_num_chunks.saturating_sub(1),
))
.collect()
};
if !self.options.four_state {
state.regs.insert(*dst, TransValue::TwoState(values));
return;
}
let r_masks = state.regs[rhs]
.load_mask_chunks(state.builder)
.unwrap_or_default();
let source_chunks = r_width.div_ceil(64);
let mut any_x = zero;
for index in 0..source_chunks {
let mut mask = get_chunk_as_i64(state.builder, &r_masks, index);
if index + 1 == source_chunks && !r_width.is_multiple_of(64) {
let valid_bits = r_width % 64;
mask = state
.builder
.ins()
.band_imm_s(mask, ((1u64 << valid_bits) - 1) as i64);
}
any_x = state.builder.ins().bor(any_x, mask);
}
let has_x = state.builder.ins().icmp_imm_s(IntCC::NotEqual, any_x, 0);
let masks = if d_width <= 64 {
let dst_ty = get_cl_type(d_width);
let zero = state.builder.ins().iconst(dst_ty, 0);
let logical_mask = if d_width == dst_ty.bits() as usize {
state.builder.ins().iconst(dst_ty, -1)
} else {
state
.builder
.ins()
.iconst(dst_ty, ((1u64 << d_width) - 1) as i64)
};
vec![state.builder.ins().select(has_x, logical_mask, zero)]
} else {
(0..final_num_chunks)
.map(|index| {
let logical_mask =
if index + 1 == final_num_chunks && !d_width.is_multiple_of(64) {
((1u64 << (d_width % 64)) - 1) as i64
} else {
-1
};
let logical_mask = state.builder.ins().iconst(types::I64, logical_mask);
state.builder.ins().select(has_x, logical_mask, zero)
})
.collect::<Vec<_>>()
};
for (value, mask) in values.iter_mut().zip(&masks) {
*value = state.builder.ins().bor(*value, *mask);
}
state
.regs
.insert(*dst, TransValue::FourState { values, masks });
}
}
pub(super) fn apply_d_width_mask(
state: &mut TranslationState,
val: Value,
ty: Type,
d_width: usize,
) -> Value {
if d_width < ty.bits() as usize {
let mask_val = (1u64 << d_width).wrapping_sub(1);
let mask = state.builder.ins().iconst(ty, mask_val as i64);
state.builder.ins().band(val, mask)
} else {
val
}
}
fn apply_d_width_mask_arith(
state: &mut TranslationState,
val: Value,
ty: Type,
d_width: usize,
) -> Value {
let phys_bits = ty.bits() as i64;
if d_width < (phys_bits as usize) {
let shift_back_amt = phys_bits - (d_width as i64);
let tmp = state.builder.ins().ishl_imm_s(val, shift_back_amt);
state.builder.ins().sshr_imm_s(tmp, shift_back_amt)
} else {
val
}
}