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 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 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 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 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 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 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}