use crate::value::LocalValueId;
use super::mnemonic::{Args, MnemonicKind};
use smallvec::smallvec;
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct IsFloatNaN {
pub src: LocalValueId,
}
impl MnemonicKind for IsFloatNaN {
fn opcode(&self) -> &'static str {
"is_float_nan"
}
fn args(&self) -> Args {
smallvec![self.src]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct LzCount {
pub src: LocalValueId,
}
impl LzCount {
pub fn eval(value: u128, size: usize) -> u128 {
use super::bits::mask_for_size;
let bits = size.saturating_mul(8);
let masked = value & mask_for_size(size);
let count = if bits == 0 {
0u64
} else if bits >= u128::BITS as usize {
masked.leading_zeros() as u64
} else {
(masked << (u128::BITS as usize - bits)).leading_zeros() as u64
};
u128::from(count.min(bits as u64))
}
}
impl MnemonicKind for LzCount {
fn opcode(&self) -> &'static str {
"lz_count"
}
fn args(&self) -> Args {
smallvec![self.src]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct PopCount {
pub src: LocalValueId,
}
impl PopCount {
pub fn eval(value: u128, size: usize) -> u128 {
use super::bits::mask_for_size;
u128::from((value & mask_for_size(size)).count_ones())
}
}
impl MnemonicKind for PopCount {
fn opcode(&self) -> &'static str {
"pop_count"
}
fn args(&self) -> Args {
smallvec![self.src]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct Carry {
pub lhs: LocalValueId,
pub rhs: LocalValueId,
}
impl Carry {
pub fn eval(lhs: u128, rhs: u128, size: usize) -> u128 {
use super::bits::mask_for_size;
let mask = mask_for_size(size);
let a = lhs & mask;
let b = rhs & mask;
let carry = if size >= 16 {
a.overflowing_add(b).1
} else {
a + b > mask
};
u128::from(carry)
}
}
impl MnemonicKind for Carry {
fn opcode(&self) -> &'static str {
"carry"
}
fn args(&self) -> Args {
smallvec![self.lhs, self.rhs]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct SCarry {
pub lhs: LocalValueId,
pub rhs: LocalValueId,
}
impl SCarry {
pub fn eval(lhs: u128, rhs: u128, size: usize) -> u128 {
use super::bits::mask_for_size;
let bits = size.saturating_mul(8);
let mask = mask_for_size(size);
let a = lhs & mask;
let b = rhs & mask;
let result = a.wrapping_add(b) & mask;
let sign_bit = 1u128 << (bits.saturating_sub(1));
let overflow = (a ^ result) & (b ^ result) & sign_bit != 0;
u128::from(overflow)
}
}
impl MnemonicKind for SCarry {
fn opcode(&self) -> &'static str {
"scarry"
}
fn args(&self) -> Args {
smallvec![self.lhs, self.rhs]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct SBorrow {
pub lhs: LocalValueId,
pub rhs: LocalValueId,
}
impl SBorrow {
pub fn eval(lhs: u128, rhs: u128, size: usize) -> u128 {
use super::bits::mask_for_size;
let bits = size.saturating_mul(8);
let mask = mask_for_size(size);
let a = lhs & mask;
let b = rhs & mask;
let result = a.wrapping_sub(b) & mask;
let sign_bit = 1u128 << (bits.saturating_sub(1));
let overflow = (a ^ b) & (a ^ result) & sign_bit != 0;
u128::from(overflow)
}
}
impl MnemonicKind for SBorrow {
fn opcode(&self) -> &'static str {
"sborrow"
}
fn args(&self) -> Args {
smallvec![self.lhs, self.rhs]
}
}
#[cfg(test)]
mod tests {
use wazabin_qcode_macro::qcode;
use crate::context::Context;
use crate::value::insn::{Instruction, Mnemonic};
use super::*;
#[test]
fn test_nan_display() {
let mut ctx = Context::new();
qcode!(
ctx,
"
<block>
local i32 V0;
%v0 = load(V0:4, V0);
%v = nan(%v0);
goto <0x1001>;
"
);
let v = Instruction::from_id(&ctx, v);
assert!(matches!(
v.mnemonic(),
Mnemonic::IsFloatNaN(IsFloatNaN { .. })
));
assert_eq!(v.size(), 1);
assert_eq!(v.as_statement().to_string(), "i8 %v = nan(i32 %v0);");
}
#[test]
fn test_popcount_display() {
let mut ctx = Context::new();
qcode!(
ctx,
"
<block>
local i32 V0;
%v0 = load(V0:4, V0);
%v = popcount(%v0);
goto <0x1001>;
"
);
let v = Instruction::from_id(&ctx, v);
assert!(matches!(v.mnemonic(), Mnemonic::PopCount(PopCount { .. })));
assert_eq!(v.size(), 1);
assert_eq!(v.as_statement().to_string(), "i8 %v = popcount(i32 %v0);");
}
#[test]
fn test_lzcount_display() {
let mut ctx = Context::new();
qcode!(
ctx,
"
<block>
local i32 V0;
%v0 = load(V0:4, V0);
%v = lzcount(%v0);
goto <0x1001>;
"
);
let v = Instruction::from_id(&ctx, v);
assert!(matches!(v.mnemonic(), Mnemonic::LzCount(LzCount { .. })));
assert_eq!(v.size(), 1);
assert_eq!(v.as_statement().to_string(), "i8 %v = lzcount(i32 %v0);");
}
#[test]
fn test_carry_display() {
let mut ctx = Context::new();
qcode!(
ctx,
"
<block>
local i32 V0;
local i32 V1;
%v0 = load(V0:4, V0);
%v1 = load(V1:4, V1);
%v = carry(%v0, %v1);
goto <0x1001>;
"
);
let v = Instruction::from_id(&ctx, v);
assert!(matches!(v.mnemonic(), Mnemonic::Carry(Carry { .. })));
assert_eq!(v.size(), 1);
assert_eq!(
v.as_statement().to_string(),
"i8 %v = carry(i32 %v0, i32 %v1);"
);
}
#[test]
fn test_scarry_display() {
let mut ctx = Context::new();
qcode!(
ctx,
"
<block>
local i32 V0;
local i32 V1;
%v0 = load(V0:4, V0);
%v1 = load(V1:4, V1);
%v = scarry(%v0, %v1);
goto <0x1001>;
"
);
let v = Instruction::from_id(&ctx, v);
assert!(matches!(v.mnemonic(), Mnemonic::SCarry(SCarry { .. })));
assert_eq!(v.size(), 1);
assert_eq!(
v.as_statement().to_string(),
"i8 %v = scarry(i32 %v0, i32 %v1);"
);
}
#[test]
fn test_sborrow_display() {
let mut ctx = Context::new();
qcode!(
ctx,
"
<block>
local i32 V0;
local i32 V1;
%v0 = load(V0:4, V0);
%v1 = load(V1:4, V1);
%v = sborrow(%v0, %v1);
goto <0x1001>;
"
);
let v = Instruction::from_id(&ctx, v);
assert!(matches!(v.mnemonic(), Mnemonic::SBorrow(SBorrow { .. })));
assert_eq!(v.size(), 1);
assert_eq!(
v.as_statement().to_string(),
"i8 %v = sborrow(i32 %v0, i32 %v1);"
);
}
}