Skip to main content

qcode/value/insn/
flags.rs

1use crate::value::LocalValueId;
2
3use super::mnemonic::{Args, MnemonicKind};
4use smallvec::smallvec;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
7pub struct IsFloatNaN {
8    pub src: LocalValueId,
9}
10
11impl MnemonicKind for IsFloatNaN {
12    fn opcode(&self) -> &'static str {
13        "is_float_nan"
14    }
15
16    fn args(&self) -> Args {
17        smallvec![self.src]
18    }
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
22pub struct LzCount {
23    pub src: LocalValueId,
24}
25
26impl LzCount {
27    /// Counts the leading zero bits of `value` interpreted as a `size`-byte integer.
28    pub fn eval(value: u128, size: usize) -> u128 {
29        use super::bits::mask_for_size;
30        let bits = size.saturating_mul(8);
31        let masked = value & mask_for_size(size);
32        let count = if bits == 0 {
33            0u64
34        } else if bits >= u128::BITS as usize {
35            masked.leading_zeros() as u64
36        } else {
37            (masked << (u128::BITS as usize - bits)).leading_zeros() as u64
38        };
39        // A zero value has no set bit inside the window, so `leading_zeros` runs
40        // past the operand and reports the full u128 width. Clamp to the operand
41        // bit-width (a no-op for any non-zero value).
42        u128::from(count.min(bits as u64))
43    }
44}
45
46impl MnemonicKind for LzCount {
47    fn opcode(&self) -> &'static str {
48        "lz_count"
49    }
50
51    fn args(&self) -> Args {
52        smallvec![self.src]
53    }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
57pub struct PopCount {
58    pub src: LocalValueId,
59}
60
61impl PopCount {
62    /// Counts the number of set bits in `value` interpreted as a `size`-byte integer.
63    pub fn eval(value: u128, size: usize) -> u128 {
64        use super::bits::mask_for_size;
65        u128::from((value & mask_for_size(size)).count_ones())
66    }
67}
68
69impl MnemonicKind for PopCount {
70    fn opcode(&self) -> &'static str {
71        "pop_count"
72    }
73
74    fn args(&self) -> Args {
75        smallvec![self.src]
76    }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
80pub struct Carry {
81    pub lhs: LocalValueId,
82    pub rhs: LocalValueId,
83}
84
85impl Carry {
86    /// Returns `1` if the unsigned addition of `lhs` and `rhs` carries out of `size` bytes.
87    pub fn eval(lhs: u128, rhs: u128, size: usize) -> u128 {
88        use super::bits::mask_for_size;
89        let mask = mask_for_size(size);
90        let a = lhs & mask;
91        let b = rhs & mask;
92        let carry = if size >= 16 {
93            a.overflowing_add(b).1
94        } else {
95            a + b > mask
96        };
97        u128::from(carry)
98    }
99}
100
101impl MnemonicKind for Carry {
102    fn opcode(&self) -> &'static str {
103        "carry"
104    }
105
106    fn args(&self) -> Args {
107        smallvec![self.lhs, self.rhs]
108    }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
112pub struct SCarry {
113    pub lhs: LocalValueId,
114    pub rhs: LocalValueId,
115}
116
117impl SCarry {
118    /// Returns `1` if the signed addition of `lhs` and `rhs` overflows for `size`-byte integers.
119    pub fn eval(lhs: u128, rhs: u128, size: usize) -> u128 {
120        use super::bits::mask_for_size;
121        let bits = size.saturating_mul(8);
122        let mask = mask_for_size(size);
123        let a = lhs & mask;
124        let b = rhs & mask;
125        let result = a.wrapping_add(b) & mask;
126        let sign_bit = 1u128 << (bits.saturating_sub(1));
127        let overflow = (a ^ result) & (b ^ result) & sign_bit != 0;
128        u128::from(overflow)
129    }
130}
131
132impl MnemonicKind for SCarry {
133    fn opcode(&self) -> &'static str {
134        "scarry"
135    }
136
137    fn args(&self) -> Args {
138        smallvec![self.lhs, self.rhs]
139    }
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
143pub struct SBorrow {
144    pub lhs: LocalValueId,
145    pub rhs: LocalValueId,
146}
147
148impl SBorrow {
149    /// Returns `1` if the signed subtraction `lhs - rhs` overflows for `size`-byte integers.
150    pub fn eval(lhs: u128, rhs: u128, size: usize) -> u128 {
151        use super::bits::mask_for_size;
152        let bits = size.saturating_mul(8);
153        let mask = mask_for_size(size);
154        let a = lhs & mask;
155        let b = rhs & mask;
156        let result = a.wrapping_sub(b) & mask;
157        let sign_bit = 1u128 << (bits.saturating_sub(1));
158        let overflow = (a ^ b) & (a ^ result) & sign_bit != 0;
159        u128::from(overflow)
160    }
161}
162
163impl MnemonicKind for SBorrow {
164    fn opcode(&self) -> &'static str {
165        "sborrow"
166    }
167
168    fn args(&self) -> Args {
169        smallvec![self.lhs, self.rhs]
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use wazabin_qcode_macro::qcode;
176
177    use crate::context::Context;
178    use crate::value::insn::{Instruction, Mnemonic};
179
180    use super::*;
181
182    #[test]
183    fn test_nan_display() {
184        let mut ctx = Context::new();
185
186        qcode!(
187            ctx,
188            "
189            <block>
190                local i32 V0;
191                %v0 = load(V0:4, V0);
192                %v = nan(%v0);
193                goto <0x1001>;
194            "
195        );
196
197        let v = Instruction::from_id(&ctx, v);
198
199        assert!(matches!(
200            v.mnemonic(),
201            Mnemonic::IsFloatNaN(IsFloatNaN { .. })
202        ));
203        assert_eq!(v.size(), 1);
204        assert_eq!(v.as_statement().to_string(), "i8 %v = nan(i32 %v0);");
205    }
206
207    #[test]
208    fn test_popcount_display() {
209        let mut ctx = Context::new();
210
211        qcode!(
212            ctx,
213            "
214            <block>
215                local i32 V0;
216                %v0 = load(V0:4, V0);
217                %v = popcount(%v0);
218                goto <0x1001>;
219            "
220        );
221
222        let v = Instruction::from_id(&ctx, v);
223
224        assert!(matches!(v.mnemonic(), Mnemonic::PopCount(PopCount { .. })));
225        assert_eq!(v.size(), 1);
226        assert_eq!(v.as_statement().to_string(), "i8 %v = popcount(i32 %v0);");
227    }
228
229    #[test]
230    fn test_lzcount_display() {
231        let mut ctx = Context::new();
232
233        qcode!(
234            ctx,
235            "
236            <block>
237                local i32 V0;
238                %v0 = load(V0:4, V0);
239                %v = lzcount(%v0);
240                goto <0x1001>;
241            "
242        );
243
244        let v = Instruction::from_id(&ctx, v);
245
246        assert!(matches!(v.mnemonic(), Mnemonic::LzCount(LzCount { .. })));
247        assert_eq!(v.size(), 1);
248        assert_eq!(v.as_statement().to_string(), "i8 %v = lzcount(i32 %v0);");
249    }
250
251    #[test]
252    fn test_carry_display() {
253        let mut ctx = Context::new();
254
255        qcode!(
256            ctx,
257            "
258            <block>
259                local i32 V0;
260                local i32 V1;
261                %v0 = load(V0:4, V0);
262                %v1 = load(V1:4, V1);
263                %v = carry(%v0, %v1);
264                goto <0x1001>;
265            "
266        );
267
268        let v = Instruction::from_id(&ctx, v);
269
270        assert!(matches!(v.mnemonic(), Mnemonic::Carry(Carry { .. })));
271        assert_eq!(v.size(), 1);
272        assert_eq!(
273            v.as_statement().to_string(),
274            "i8 %v = carry(i32 %v0, i32 %v1);"
275        );
276    }
277
278    #[test]
279    fn test_scarry_display() {
280        let mut ctx = Context::new();
281
282        qcode!(
283            ctx,
284            "
285            <block>
286                local i32 V0;
287                local i32 V1;
288                %v0 = load(V0:4, V0);
289                %v1 = load(V1:4, V1);
290                %v = scarry(%v0, %v1);
291                goto <0x1001>;
292            "
293        );
294
295        let v = Instruction::from_id(&ctx, v);
296
297        assert!(matches!(v.mnemonic(), Mnemonic::SCarry(SCarry { .. })));
298        assert_eq!(v.size(), 1);
299        assert_eq!(
300            v.as_statement().to_string(),
301            "i8 %v = scarry(i32 %v0, i32 %v1);"
302        );
303    }
304
305    #[test]
306    fn test_sborrow_display() {
307        let mut ctx = Context::new();
308
309        qcode!(
310            ctx,
311            "
312            <block>
313                local i32 V0;
314                local i32 V1;
315                %v0 = load(V0:4, V0);
316                %v1 = load(V1:4, V1);
317                %v = sborrow(%v0, %v1);
318                goto <0x1001>;
319            "
320        );
321
322        let v = Instruction::from_id(&ctx, v);
323
324        assert!(matches!(v.mnemonic(), Mnemonic::SBorrow(SBorrow { .. })));
325        assert_eq!(v.size(), 1);
326        assert_eq!(
327            v.as_statement().to_string(),
328            "i8 %v = sborrow(i32 %v0, i32 %v1);"
329        );
330    }
331}