1use crate::value::{
2 LocalValueId,
3 insn::bits::{mask_for_size, signed_value},
4};
5
6use super::mnemonic::{Args, MnemonicKind};
7use smallvec::smallvec;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
10pub struct Zext {
11 pub src: LocalValueId,
12 pub size: usize,
13}
14
15impl Zext {
16 pub fn eval(value: u128, dst_size: usize) -> u128 {
18 value & mask_for_size(dst_size)
19 }
20}
21
22impl MnemonicKind for Zext {
23 fn opcode(&self) -> &'static str {
24 "zext"
25 }
26
27 fn args(&self) -> Args {
28 smallvec![self.src]
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
33pub struct Sext {
34 pub src: LocalValueId,
35 pub size: usize,
36}
37
38impl Sext {
39 pub fn eval(value: u128, src_size: usize, dst_size: usize) -> u128 {
41 if src_size == 0 {
42 return 0;
43 }
44 signed_value(value & mask_for_size(src_size), src_size) as u128 & mask_for_size(dst_size)
45 }
46}
47
48impl MnemonicKind for Sext {
49 fn opcode(&self) -> &'static str {
50 "sext"
51 }
52
53 fn args(&self) -> Args {
54 smallvec![self.src]
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
59pub struct Range {
60 pub src: LocalValueId,
61 pub start: usize,
62 pub size: usize,
63}
64
65impl Range {
66 pub fn eval(value: u128, start: usize, size: usize) -> u128 {
68 use super::bits::mask_for_size;
69 let shift = start.saturating_mul(8);
70 if shift >= u128::BITS as usize {
71 0
72 } else {
73 (value >> shift) & mask_for_size(size)
74 }
75 }
76}
77
78impl MnemonicKind for Range {
79 fn opcode(&self) -> &'static str {
80 "range"
81 }
82
83 fn args(&self) -> Args {
84 smallvec![self.src]
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
89pub struct IntToFloat {
90 pub src: LocalValueId,
91 pub size: usize,
92}
93
94impl MnemonicKind for IntToFloat {
95 fn opcode(&self) -> &'static str {
96 "int_to_float"
97 }
98
99 fn args(&self) -> Args {
100 smallvec![self.src]
101 }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
105pub struct FloatToFloat {
106 pub src: LocalValueId,
107 pub size: usize,
108}
109
110impl MnemonicKind for FloatToFloat {
111 fn opcode(&self) -> &'static str {
112 "float_to_float"
113 }
114
115 fn args(&self) -> Args {
116 smallvec![self.src]
117 }
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
121pub struct FloatToInt {
122 pub src: LocalValueId,
123 pub size: usize,
124}
125
126impl MnemonicKind for FloatToInt {
127 fn opcode(&self) -> &'static str {
128 "float_to_int"
129 }
130
131 fn args(&self) -> Args {
132 smallvec![self.src]
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use wazabin_qcode_macro::qcode;
139
140 use crate::context::Context;
141 use crate::value::insn::{Instruction, Mnemonic};
142
143 use super::*;
144
145 #[test]
146 fn test_zext_display() {
147 let mut ctx = Context::new();
148
149 qcode!(
150 ctx,
151 "
152 <block>
153 local i32 V0;
154 %v0 = load(V0:4, V0);
155 i64 %v = zext(i64, i32 %v0);
156 goto <0x1001>;
157 "
158 );
159
160 let v = Instruction::from_id(&ctx, v);
161
162 if !matches!(v.mnemonic(), Mnemonic::Zext(Zext { size: 8, .. })) {
163 panic!("expected zext instruction");
164 }
165
166 assert_eq!(v.as_statement().to_string(), "i64 %v = zext(i64, i32 %v0);");
167 }
168
169 #[test]
170 fn test_sext_display() {
171 let mut ctx = Context::new();
172
173 qcode!(
174 ctx,
175 "
176 <block>
177 local i32 V0;
178 %v0 = load(V0:4, V0);
179 i64 %v = sext(i64, i32 %v0);
180 goto <0x1001>;
181 "
182 );
183
184 let v = Instruction::from_id(&ctx, v);
185
186 if !matches!(v.mnemonic(), Mnemonic::Sext(Sext { size: 8, .. })) {
187 panic!("expected sext instruction");
188 }
189
190 assert_eq!(v.as_statement().to_string(), "i64 %v = sext(i64, i32 %v0);");
191 }
192
193 #[test]
194 fn test_int2float_display() {
195 let mut ctx = Context::new();
196
197 qcode!(
198 ctx,
199 "
200 <block>
201 local i32 V0;
202 %v0 = load(V0:4, V0);
203 i64 %v = int2float(f32, i32 %v0);
204 goto <0x1001>;
205 "
206 );
207
208 let v = Instruction::from_id(&ctx, v);
209
210 if !matches!(
211 v.mnemonic(),
212 Mnemonic::IntToFloat(IntToFloat { size: 4, .. })
213 ) {
214 panic!("expected int2float instruction");
215 }
216
217 assert_eq!(
218 v.as_statement().to_string(),
219 "i32 %v = int2float(f32, i32 %v0);"
220 );
221 }
222
223 #[test]
224 fn test_float2float_display() {
225 let mut ctx = Context::new();
226
227 qcode!(
228 ctx,
229 "
230 <block>
231 local i32 V0;
232 %v0 = load(V0:4, V0);
233 i64 %v = float2float(f64, i32 %v0);
234 goto <0x1001>;
235 "
236 );
237
238 let v = Instruction::from_id(&ctx, v);
239
240 if !matches!(
241 v.mnemonic(),
242 Mnemonic::FloatToFloat(FloatToFloat { size: 8, .. })
243 ) {
244 panic!("expected float2float instruction");
245 }
246
247 assert_eq!(
248 v.as_statement().to_string(),
249 "i64 %v = float2float(f64, i32 %v0);"
250 );
251 }
252
253 #[test]
254 fn test_trunc_display() {
255 let mut ctx = Context::new();
256
257 qcode!(
258 ctx,
259 "
260 <block>
261 local i32 V0;
262 %v0 = load(V0:4, V0);
263 i16 %v = trunc(i16, i32 %v0);
264 goto <0x1001>;
265 "
266 );
267
268 let v = Instruction::from_id(&ctx, v);
269
270 if !matches!(
271 v.mnemonic(),
272 Mnemonic::FloatToInt(FloatToInt { size: 2, .. })
273 ) {
274 panic!("expected float2int instruction");
275 }
276
277 assert_eq!(
278 v.as_statement().to_string(),
279 "i16 %v = trunc(i16, i32 %v0);"
280 );
281 }
282
283 #[test]
284 fn test_zext_eval() {
285 assert_eq!(Zext::eval(0xFF, 4), 0xFF);
287 assert_eq!(Zext::eval(0xDEAD_BEEF_FFFF_FFFF, 4), 0xFFFF_FFFF);
288 assert_eq!(Zext::eval(0, 8), 0);
289 }
290
291 #[test]
292 fn test_sext_eval() {
293 assert_eq!(Sext::eval(0x7F, 1, 4), 0x7F);
295 assert_eq!(Sext::eval(0xFF, 1, 4), 0xFFFF_FFFF);
297 assert_eq!(Sext::eval(0x80, 1, 4), 0xFFFF_FF80);
298 assert_eq!(Sext::eval(0, 1, 4), 0);
300 }
301
302 #[test]
303 fn test_range_eval() {
304 assert_eq!(Range::eval(0xDEAD_BEEF, 1, 2), 0xADBE);
307 assert_eq!(Range::eval(0xDEAD_BEEF, 0, 1), 0xEF);
309 assert_eq!(Range::eval(0xFF, 16, 1), 0);
311 }
312}