1use core::ops::RangeInclusive;
24
25use aluvm::isa::{Bytecode, BytecodeRead, BytecodeWrite, CodeEofError, CtrlInstr, ReservedInstr};
26use aluvm::SiteId;
27use amplify::num::{u2, u256, u3, u4};
28
29use super::{Bits, ConstVal, FieldInstr, Instr};
30use crate::{fe256, RegE};
31
32#[allow(missing_docs, clippy::identity_op)]
33impl FieldInstr {
34 pub const START: u8 = 64;
36 pub const END: u8 = Self::MUL;
38
39 pub const SET: u8 = Self::START + 0;
40 pub const TEST: u8 = Self::START + 0;
41 pub const CLR: u8 = Self::START + 0;
42 pub const PUTD: u8 = Self::START + 0;
43 pub const PUTZ: u8 = Self::START + 0;
44 pub const PUTV: u8 = Self::START + 0;
45 pub const FITS: u8 = Self::START + 0;
46
47 pub const MOV: u8 = Self::START + 1;
48 pub const EQ: u8 = Self::START + 2;
49 pub const NEG: u8 = Self::START + 3;
50 pub const ADD: u8 = Self::START + 4;
51 pub const MUL: u8 = Self::START + 5;
52}
53
54const SUB_TEST: u8 = 0b_0000;
55const SUB_CLR: u8 = 0b_0001;
56const SUB_PUTD: u8 = 0b_0010;
57const SUB_PUTZ: u8 = 0b_0011;
58const MASK_PUTV: u8 = 0b_1100;
59const TEST_PUTV: u8 = 0b_0100;
60const MASK_FITS: u8 = 0b_1000;
61const TEST_FITS: u8 = 0b_1000;
62
63impl<Id: SiteId> Bytecode<Id> for FieldInstr {
64 fn op_range() -> RangeInclusive<u8> { Self::START..=Self::END }
65
66 fn opcode_byte(&self) -> u8 {
67 match *self {
68 FieldInstr::Test { .. }
69 | FieldInstr::Clr { .. }
70 | FieldInstr::PutD { .. }
71 | FieldInstr::PutZ { .. }
72 | FieldInstr::PutV { .. }
73 | FieldInstr::Fits { .. } => Self::SET,
74 FieldInstr::Mov { .. } => Self::MOV,
75 FieldInstr::Eq { .. } => Self::EQ,
76 FieldInstr::Neg { .. } => Self::NEG,
77 FieldInstr::Add { .. } => Self::ADD,
78 FieldInstr::Mul { .. } => Self::MUL,
79 }
80 }
81
82 fn code_byte_len(&self) -> u16 {
83 let arg_len = match *self {
84 FieldInstr::Test { src: _ } => 1,
85 FieldInstr::Clr { dst: _ } => 1,
86 FieldInstr::PutD { dst: _, data: _ } => 3,
87 FieldInstr::PutZ { dst: _ } => 1,
88 FieldInstr::PutV { dst: _, val: _ } => 1,
89 FieldInstr::Fits { src: _, bits: _ } => 1,
90 FieldInstr::Mov { dst: _, src: _ } => 1,
91 FieldInstr::Eq { src1: _, src2: _ } => 1,
92 FieldInstr::Neg { dst: _, src: _ } => 1,
93 FieldInstr::Add { dst_src: _, src: _ } => 1,
94 FieldInstr::Mul { dst_src: _, src: _ } => 1,
95 };
96 arg_len + 1
97 }
98
99 fn external_ref(&self) -> Option<Id> { None }
100
101 fn encode_operands<W>(&self, writer: &mut W) -> Result<(), W::Error>
102 where W: BytecodeWrite<Id> {
103 match *self {
104 FieldInstr::Test { src } => {
105 writer.write_4bits(u4::with(SUB_TEST))?;
106 writer.write_4bits(src.to_u4())?;
107 }
108 FieldInstr::Clr { dst } => {
109 writer.write_4bits(u4::with(SUB_CLR))?;
110 writer.write_4bits(dst.to_u4())?;
111 }
112 FieldInstr::PutD { dst, data } => {
113 writer.write_4bits(u4::with(SUB_PUTD))?;
114 writer.write_4bits(dst.to_u4())?;
115 writer.write_fixed(data.to_u256().to_le_bytes())?;
116 }
117 FieldInstr::PutZ { dst } => {
118 writer.write_4bits(u4::with(SUB_PUTZ))?;
119 writer.write_4bits(dst.to_u4())?;
120 }
121 FieldInstr::PutV { dst, val } => {
122 let half = u4::with(TEST_PUTV | val.to_u2().to_u8());
123 writer.write_4bits(half)?;
124 writer.write_4bits(dst.to_u4())?;
125 }
126 FieldInstr::Fits { src, bits } => {
127 let half = u4::with(TEST_FITS | bits.to_u3().to_u8());
128 writer.write_4bits(half)?;
129 writer.write_4bits(src.to_u4())?;
130 }
131 FieldInstr::Mov { dst, src } => {
132 writer.write_4bits(dst.to_u4())?;
133 writer.write_4bits(src.to_u4())?;
134 }
135 FieldInstr::Eq { src1, src2 } => {
136 writer.write_4bits(src1.to_u4())?;
137 writer.write_4bits(src2.to_u4())?;
138 }
139 FieldInstr::Neg { dst, src } => {
140 writer.write_4bits(dst.to_u4())?;
141 writer.write_4bits(src.to_u4())?;
142 }
143 FieldInstr::Add { dst_src, src } => {
144 writer.write_4bits(dst_src.to_u4())?;
145 writer.write_4bits(src.to_u4())?;
146 }
147 FieldInstr::Mul { dst_src, src } => {
148 writer.write_4bits(dst_src.to_u4())?;
149 writer.write_4bits(src.to_u4())?;
150 }
151 }
152 Ok(())
153 }
154
155 fn decode_operands<R>(reader: &mut R, opcode: u8) -> Result<Self, CodeEofError>
156 where
157 Self: Sized,
158 R: BytecodeRead<Id>,
159 {
160 Ok(match opcode {
161 Self::SET => {
162 let sub = reader.read_4bits()?.to_u8();
163 match sub {
164 SUB_TEST => {
165 let src = RegE::from(reader.read_4bits()?);
166 FieldInstr::Test { src }
167 }
168 SUB_CLR => {
169 let dst = RegE::from(reader.read_4bits()?);
170 FieldInstr::Clr { dst }
171 }
172 SUB_PUTD => {
173 let dst = RegE::from(reader.read_4bits()?);
174 let data = reader.read_fixed(|d: [u8; 32]| fe256::from(u256::from_le_bytes(d)))?;
175 FieldInstr::PutD { dst, data }
176 }
177 SUB_PUTZ => {
178 let dst = RegE::from(reader.read_4bits()?);
179 FieldInstr::PutZ { dst }
180 }
181 x if x & MASK_PUTV == TEST_PUTV => {
182 let val = ConstVal::from(u2::with(sub & !MASK_PUTV));
183 let dst = RegE::from(reader.read_4bits()?);
184 FieldInstr::PutV { dst, val }
185 }
186 x if x & MASK_FITS == TEST_FITS => {
187 let bits = Bits::from(u3::with(sub & !MASK_FITS));
188 let src = RegE::from(reader.read_4bits()?);
189 FieldInstr::Fits { src, bits }
190 }
191 _ => unreachable!(),
192 }
193 }
194 Self::MOV => {
195 let dst = RegE::from(reader.read_4bits()?);
196 let src = RegE::from(reader.read_4bits()?);
197 FieldInstr::Mov { dst, src }
198 }
199 Self::EQ => {
200 let src1 = RegE::from(reader.read_4bits()?);
201 let src2 = RegE::from(reader.read_4bits()?);
202 FieldInstr::Eq { src1, src2 }
203 }
204 Self::NEG => {
205 let dst = RegE::from(reader.read_4bits()?);
206 let src = RegE::from(reader.read_4bits()?);
207 FieldInstr::Neg { dst, src }
208 }
209 Self::ADD => {
210 let dst_src = RegE::from(reader.read_4bits()?);
211 let src = RegE::from(reader.read_4bits()?);
212 FieldInstr::Add { dst_src, src }
213 }
214 Self::MUL => {
215 let dst_src = RegE::from(reader.read_4bits()?);
216 let src = RegE::from(reader.read_4bits()?);
217 FieldInstr::Mul { dst_src, src }
218 }
219 _ => unreachable!(),
220 })
221 }
222}
223
224impl<Id: SiteId> Bytecode<Id> for Instr<Id> {
225 fn op_range() -> RangeInclusive<u8> { 0..=0xFF }
226
227 fn opcode_byte(&self) -> u8 {
228 match self {
229 Instr::Ctrl(instr) => instr.opcode_byte(),
230 Instr::Gfa(instr) => Bytecode::<Id>::opcode_byte(instr),
231 Instr::Reserved(instr) => Bytecode::<Id>::opcode_byte(instr),
232 }
233 }
234
235 fn code_byte_len(&self) -> u16 {
236 match self {
237 Instr::Ctrl(instr) => instr.code_byte_len(),
238 Instr::Gfa(instr) => Bytecode::<Id>::code_byte_len(instr),
239 Instr::Reserved(instr) => Bytecode::<Id>::code_byte_len(instr),
240 }
241 }
242
243 fn external_ref(&self) -> Option<Id> {
244 match self {
245 Instr::Ctrl(instr) => instr.external_ref(),
246 Instr::Gfa(instr) => Bytecode::<Id>::external_ref(instr),
247 Instr::Reserved(instr) => Bytecode::<Id>::external_ref(instr),
248 }
249 }
250
251 fn encode_operands<W>(&self, writer: &mut W) -> Result<(), W::Error>
252 where W: BytecodeWrite<Id> {
253 match self {
254 Instr::Ctrl(instr) => instr.encode_operands(writer),
255 Instr::Gfa(instr) => instr.encode_operands(writer),
256 Instr::Reserved(instr) => instr.encode_operands(writer),
257 }
258 }
259
260 fn decode_operands<R>(reader: &mut R, opcode: u8) -> Result<Self, CodeEofError>
261 where
262 Self: Sized,
263 R: BytecodeRead<Id>,
264 {
265 match opcode {
266 op if CtrlInstr::<Id>::op_range().contains(&op) => {
267 CtrlInstr::<Id>::decode_operands(reader, op).map(Self::Ctrl)
268 }
269 op if <FieldInstr as Bytecode<Id>>::op_range().contains(&op) => {
270 FieldInstr::decode_operands(reader, op).map(Self::Gfa)
271 }
272 _ => ReservedInstr::decode_operands(reader, opcode).map(Self::Reserved),
273 }
274 }
275}
276
277#[cfg(test)]
278mod test {
279 #![cfg_attr(coverage_nightly, coverage(off))]
280 use core::str::FromStr;
281
282 use aluvm::{LibId, LibsSeg, Marshaller};
283 use amplify::confinement::SmallBlob;
284
285 use super::*;
286 use crate::RegE;
287
288 const LIB_ID: &str = "5iMb1eHJ-bN5BOe6-9RvBjYL-jF1ELjj-VV7c8Bm-WvFen1Q";
289
290 fn roundtrip(instr: impl Into<Instr<LibId>>, bytecode: impl AsRef<[u8]>, dataseg: Option<&[u8]>) -> SmallBlob {
291 let instr = instr.into();
292 let mut libs = LibsSeg::new();
293 libs.push(LibId::from_str(LIB_ID).unwrap()).unwrap();
294 let mut marshaller = Marshaller::new(&libs);
295 instr.encode_instr(&mut marshaller).unwrap();
296 let (code, data) = marshaller.finish();
297 assert_eq!(code.as_slice(), bytecode.as_ref());
298 if let Some(d) = dataseg {
299 assert_eq!(data.as_slice(), d);
300 } else {
301 assert!(data.is_empty());
302 }
303 let mut marshaller = Marshaller::with(code, data, &libs);
304 let decoded = Instr::<LibId>::decode_instr(&mut marshaller).unwrap();
305 assert_eq!(decoded, instr);
306 marshaller.into_code_data().1
307 }
308
309 #[test]
310 fn test() {
311 for reg in RegE::ALL {
312 let instr = Instr::<LibId>::Gfa(FieldInstr::Test { src: reg });
313 let opcode = FieldInstr::SET;
314 let sub = reg.to_u4().to_u8() << 4 | SUB_TEST;
315
316 roundtrip(instr, [opcode, sub], None);
317
318 assert_eq!(instr.code_byte_len(), 2);
319 assert_eq!(instr.opcode_byte(), FieldInstr::TEST);
320 assert_eq!(instr.external_ref(), None);
321 }
322 }
323
324 #[test]
325 fn clr() {
326 for reg in RegE::ALL {
327 let instr = Instr::<LibId>::Gfa(FieldInstr::Clr { dst: reg });
328 let opcode = FieldInstr::SET;
329 let sub = reg.to_u4().to_u8() << 4 | SUB_CLR;
330
331 roundtrip(instr, [opcode, sub], None);
332
333 assert_eq!(instr.code_byte_len(), 2);
334 assert_eq!(instr.opcode_byte(), FieldInstr::CLR);
335 assert_eq!(instr.external_ref(), None);
336 }
337 }
338
339 #[test]
340 fn putd() {
341 for reg in RegE::ALL {
342 let val = u256::from(0xdeadcafe1badbeef_u64);
343 let data = val.to_le_bytes();
344
345 let instr = Instr::<LibId>::Gfa(FieldInstr::PutD {
346 dst: reg,
347 data: fe256::from(val),
348 });
349 let opcode = FieldInstr::SET;
350 let sub = reg.to_u4().to_u8() << 4 | SUB_PUTD;
351
352 roundtrip(instr, [opcode, sub, 0, 0], Some(&data[..]));
353
354 assert_eq!(instr.code_byte_len(), 4);
355 assert_eq!(instr.opcode_byte(), FieldInstr::PUTD);
356 assert_eq!(instr.external_ref(), None);
357 }
358 }
359
360 #[test]
361 fn putz() {
362 for reg in RegE::ALL {
363 let instr = Instr::<LibId>::Gfa(FieldInstr::PutZ { dst: reg });
364 let opcode = FieldInstr::SET;
365 let sub = reg.to_u4().to_u8() << 4 | SUB_PUTZ;
366
367 roundtrip(instr, [opcode, sub], None);
368
369 assert_eq!(instr.code_byte_len(), 2);
370 assert_eq!(instr.opcode_byte(), FieldInstr::PUTZ);
371 assert_eq!(instr.external_ref(), None);
372 }
373 }
374
375 #[test]
376 fn putv() {
377 for reg in RegE::ALL {
378 for val_u8 in 0..4 {
379 let val = ConstVal::from(u2::with(val_u8));
380 let instr = Instr::<LibId>::Gfa(FieldInstr::PutV { dst: reg, val });
381 let opcode = FieldInstr::SET;
382 let sub = reg.to_u4().to_u8() << 4 | TEST_PUTV | val.to_u2().to_u8();
383
384 roundtrip(instr, [opcode, sub], None);
385
386 assert_eq!(instr.code_byte_len(), 2);
387 assert_eq!(instr.opcode_byte(), FieldInstr::PUTV);
388 assert_eq!(instr.external_ref(), None);
389 }
390 }
391 }
392
393 #[test]
394 fn fits() {
395 for reg in RegE::ALL {
396 for bits_u8 in 0..8 {
397 let bits = Bits::from(u3::with(bits_u8));
398 let instr = Instr::<LibId>::Gfa(FieldInstr::Fits { src: reg, bits });
399 let opcode = FieldInstr::SET;
400 let sub = reg.to_u4().to_u8() << 4 | TEST_FITS | bits.to_u3().to_u8();
401
402 roundtrip(instr, [opcode, sub], None);
403
404 assert_eq!(instr.code_byte_len(), 2);
405 assert_eq!(instr.opcode_byte(), FieldInstr::FITS);
406 assert_eq!(instr.external_ref(), None);
407 }
408 }
409 }
410
411 #[test]
412 fn mov() {
413 for reg1 in RegE::ALL {
414 for reg2 in RegE::ALL {
415 let instr = Instr::<LibId>::Gfa(FieldInstr::Mov { dst: reg1, src: reg2 });
416 let opcode = FieldInstr::MOV;
417 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
418
419 roundtrip(instr, [opcode, regs], None);
420
421 assert_eq!(instr.code_byte_len(), 2);
422 assert_eq!(instr.opcode_byte(), FieldInstr::MOV);
423 assert_eq!(instr.external_ref(), None);
424 }
425 }
426 }
427
428 #[test]
429 fn eq() {
430 for reg1 in RegE::ALL {
431 for reg2 in RegE::ALL {
432 let instr = Instr::<LibId>::Gfa(FieldInstr::Eq { src1: reg1, src2: reg2 });
433 let opcode = FieldInstr::EQ;
434 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
435
436 roundtrip(instr, [opcode, regs], None);
437
438 assert_eq!(instr.code_byte_len(), 2);
439 assert_eq!(instr.opcode_byte(), FieldInstr::EQ);
440 assert_eq!(instr.external_ref(), None);
441 }
442 }
443 }
444
445 #[test]
446 fn neq() {
447 for reg1 in RegE::ALL {
448 for reg2 in RegE::ALL {
449 let instr = Instr::<LibId>::Gfa(FieldInstr::Neg { dst: reg1, src: reg2 });
450 let opcode = FieldInstr::NEG;
451 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
452
453 roundtrip(instr, [opcode, regs], None);
454
455 assert_eq!(instr.code_byte_len(), 2);
456 assert_eq!(instr.opcode_byte(), FieldInstr::NEG);
457 assert_eq!(instr.external_ref(), None);
458 }
459 }
460 }
461
462 #[test]
463 fn add() {
464 for reg1 in RegE::ALL {
465 for reg2 in RegE::ALL {
466 let instr = Instr::<LibId>::Gfa(FieldInstr::Add {
467 dst_src: reg1,
468 src: reg2,
469 });
470 let opcode = FieldInstr::ADD;
471 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
472
473 roundtrip(instr, [opcode, regs], None);
474
475 assert_eq!(instr.code_byte_len(), 2);
476 assert_eq!(instr.opcode_byte(), FieldInstr::ADD);
477 assert_eq!(instr.external_ref(), None);
478 }
479 }
480 }
481
482 #[test]
483 fn mul() {
484 for reg1 in RegE::ALL {
485 for reg2 in RegE::ALL {
486 let instr = Instr::<LibId>::Gfa(FieldInstr::Mul {
487 dst_src: reg1,
488 src: reg2,
489 });
490 let opcode = FieldInstr::MUL;
491 let regs = reg2.to_u4().to_u8() << 4 | reg1.to_u4().to_u8();
492
493 roundtrip(instr, [opcode, regs], None);
494
495 assert_eq!(instr.code_byte_len(), 2);
496 assert_eq!(instr.opcode_byte(), FieldInstr::MUL);
497 assert_eq!(instr.external_ref(), None);
498 }
499 }
500 }
501
502 #[test]
503 fn reserved() {
504 let instr = Instr::<LibId>::Reserved(default!());
505 roundtrip(instr, [0xFF], None);
506
507 assert_eq!(instr.code_byte_len(), 1);
508 assert_eq!(instr.opcode_byte(), 0xFF);
509 assert_eq!(instr.external_ref(), None);
510 }
511}