1use alloc::collections::BTreeSet;
24
25use aluvm::isa::{ExecStep, Instruction};
26use aluvm::regs::Status;
27use aluvm::{Core, CoreExt, Site, SiteId, Supercore};
28
29use super::{FieldInstr, Instr, ISA_GFA128};
30use crate::{fe256, GfaCore, RegE};
31
32impl<Id: SiteId> Instruction<Id> for FieldInstr {
33 const ISA_EXT: &'static [&'static str] = &[ISA_GFA128];
34 type Core = GfaCore;
35 type Context<'ctx> = ();
36
37 fn is_goto_target(&self) -> bool { false }
38
39 fn local_goto_pos(&mut self) -> Option<&mut u16> { None }
40
41 fn remote_goto_pos(&mut self) -> Option<&mut Site<Id>> { None }
42
43 fn src_regs(&self) -> BTreeSet<RegE> {
44 match *self {
45 FieldInstr::Clr { dst: _ }
46 | FieldInstr::PutD { dst: _, data: _ }
47 | FieldInstr::PutZ { dst: _ }
48 | FieldInstr::PutV { dst: _, val: _ } => none!(),
49
50 FieldInstr::Eq { src1, src2 } => bset![src1, src2],
51
52 FieldInstr::Test { src }
53 | FieldInstr::Fits { src, bits: _ }
54 | FieldInstr::Mov { dst: _, src }
55 | FieldInstr::Neg { dst: _, src } => bset![src],
56
57 FieldInstr::Add { dst_src, src } | FieldInstr::Mul { dst_src, src } => bset![src, dst_src],
58 }
59 }
60
61 fn dst_regs(&self) -> BTreeSet<RegE> {
62 match *self {
63 FieldInstr::Clr { dst }
64 | FieldInstr::PutD { dst, data: _ }
65 | FieldInstr::PutZ { dst }
66 | FieldInstr::PutV { dst, val: _ }
67 | FieldInstr::Mov { dst, src: _ } => bset![dst],
68
69 FieldInstr::Eq { src1: _, src2: _ }
70 | FieldInstr::Test { src: _ }
71 | FieldInstr::Fits { src: _, bits: _ } => none!(),
72
73 FieldInstr::Neg { dst, src: _ }
74 | FieldInstr::Add { dst_src: dst, src: _ }
75 | FieldInstr::Mul { dst_src: dst, src: _ } => bset![dst],
76 }
77 }
78
79 fn op_data_bytes(&self) -> u16 {
80 match self {
81 FieldInstr::PutV { dst: _, val: _ } | FieldInstr::Fits { src: _, bits: _ } => 1,
82
83 FieldInstr::Test { src: _ }
84 | FieldInstr::Clr { dst: _ }
85 | FieldInstr::PutD { dst: _, data: _ }
86 | FieldInstr::PutZ { dst: _ }
87 | FieldInstr::Mov { dst: _, src: _ }
88 | FieldInstr::Eq { src1: _, src2: _ }
89 | FieldInstr::Neg { dst: _, src: _ }
90 | FieldInstr::Add { dst_src: _, src: _ }
91 | FieldInstr::Mul { dst_src: _, src: _ } => 0,
92 }
93 }
94
95 fn ext_data_bytes(&self) -> u16 {
96 match self {
97 FieldInstr::PutD { dst: _, data: _ } => 32,
98
99 FieldInstr::Test { src: _ }
100 | FieldInstr::Clr { dst: _ }
101 | FieldInstr::PutZ { dst: _ }
102 | FieldInstr::PutV { dst: _, val: _ }
103 | FieldInstr::Fits { src: _, bits: _ }
104 | FieldInstr::Mov { dst: _, src: _ }
105 | FieldInstr::Eq { src1: _, src2: _ }
106 | FieldInstr::Neg { dst: _, src: _ }
107 | FieldInstr::Add { dst_src: _, src: _ }
108 | FieldInstr::Mul { dst_src: _, src: _ } => 0,
109 }
110 }
111
112 fn complexity(&self) -> u64 {
113 let base = Instruction::<Id>::base_complexity(self);
114 match self {
115 FieldInstr::Test { src: _ }
116 | FieldInstr::Clr { dst: _ }
117 | FieldInstr::PutZ { dst: _ }
118 | FieldInstr::PutV { dst: _, val: _ }
119 | FieldInstr::PutD { dst: _, data: _ }
120 | FieldInstr::Mov { dst: _, src: _ }
121 | FieldInstr::Eq { src1: _, src2: _ } => base,
122
123 FieldInstr::Fits { src: _, bits: _ }
124 | FieldInstr::Neg { dst: _, src: _ }
125 | FieldInstr::Add { dst_src: _, src: _ }
126 | FieldInstr::Mul { dst_src: _, src: _ } => {
127 base * 2
129 }
130 }
131 }
132
133 fn exec(&self, _: Site<Id>, core: &mut Core<Id, GfaCore>, _: &Self::Context<'_>) -> ExecStep<Site<Id>> {
134 let res = match *self {
135 FieldInstr::Test { src } => {
136 let res = core.cx.test(src);
137 core.set_co(res);
138 Status::Ok
139 }
140 FieldInstr::Clr { dst } => {
141 core.cx.clr(dst);
142 Status::Ok
143 }
144 FieldInstr::PutD { dst, data } => {
145 core.cx.set(dst, data);
146 Status::Ok
147 }
148 FieldInstr::PutZ { dst } => {
149 core.cx.set(dst, fe256::ZERO);
150 Status::Ok
151 }
152 FieldInstr::PutV { dst, val } => {
153 let val = val.to_fe256().unwrap_or_else(|| core.cx.fq().into());
154 core.cx.set(dst, val);
155 Status::Ok
156 }
157 FieldInstr::Mov { dst, src } => {
158 core.cx.mov(dst, src);
159 Status::Ok
160 }
161 FieldInstr::Eq { src1, src2 } => {
162 let res = core.cx.eqv(src1, src2);
163 core.set_co(res);
164 Status::Ok
165 }
166
167 FieldInstr::Fits { src, bits } => match core.cx.fits(src, bits) {
168 None => Status::Fail,
169 Some(true) => {
170 core.set_co(Status::Ok);
171 Status::Ok
172 }
173 Some(false) => {
174 core.set_co(Status::Fail);
175 Status::Ok
176 }
177 },
178 FieldInstr::Neg { dst, src } => core.cx.neg_mod(dst, src),
179 FieldInstr::Add { dst_src, src } => core.cx.add_mod(dst_src, src),
180 FieldInstr::Mul { dst_src, src } => core.cx.mul_mod(dst_src, src),
181 };
182 if res == Status::Ok {
183 ExecStep::Next
184 } else {
185 ExecStep::Fail
186 }
187 }
188}
189
190impl<Id: SiteId> Instruction<Id> for Instr<Id> {
191 const ISA_EXT: &'static [&'static str] = &[ISA_GFA128];
192 type Core = GfaCore;
193 type Context<'ctx> = ();
194
195 fn is_goto_target(&self) -> bool {
196 match self {
197 Instr::Ctrl(ctrl) => ctrl.is_goto_target(),
198 Instr::Gfa(instr) => Instruction::<Id>::is_goto_target(instr),
199 Instr::Reserved(reserved) => Instruction::<Id>::is_goto_target(reserved),
200 }
201 }
202
203 fn local_goto_pos(&mut self) -> Option<&mut u16> {
204 match self {
205 Instr::Ctrl(ctrl) => ctrl.local_goto_pos(),
206 Instr::Gfa(instr) => Instruction::<Id>::local_goto_pos(instr),
207 Instr::Reserved(reserved) => Instruction::<Id>::local_goto_pos(reserved),
208 }
209 }
210
211 fn remote_goto_pos(&mut self) -> Option<&mut Site<Id>> {
212 match self {
213 Instr::Ctrl(ctrl) => ctrl.remote_goto_pos(),
214 Instr::Gfa(instr) => Instruction::<Id>::remote_goto_pos(instr),
215 Instr::Reserved(reserved) => Instruction::<Id>::remote_goto_pos(reserved),
216 }
217 }
218
219 fn src_regs(&self) -> BTreeSet<<Self::Core as CoreExt>::Reg> {
220 match self {
221 Instr::Ctrl(_) => none!(),
222 Instr::Gfa(instr) => Instruction::<Id>::src_regs(instr),
223 Instr::Reserved(_) => none!(),
224 }
225 }
226
227 fn dst_regs(&self) -> BTreeSet<<Self::Core as CoreExt>::Reg> {
228 match self {
229 Instr::Ctrl(_) => none!(),
230 Instr::Gfa(instr) => Instruction::<Id>::dst_regs(instr),
231 Instr::Reserved(_) => none!(),
232 }
233 }
234
235 fn op_data_bytes(&self) -> u16 {
236 match self {
237 Instr::Ctrl(instr) => instr.op_data_bytes(),
238 Instr::Gfa(instr) => Instruction::<Id>::op_data_bytes(instr),
239 Instr::Reserved(_) => none!(),
240 }
241 }
242
243 fn ext_data_bytes(&self) -> u16 {
244 match self {
245 Instr::Ctrl(instr) => instr.ext_data_bytes(),
246 Instr::Gfa(instr) => Instruction::<Id>::ext_data_bytes(instr),
247 Instr::Reserved(_) => none!(),
248 }
249 }
250
251 fn exec(&self, site: Site<Id>, core: &mut Core<Id, Self::Core>, context: &Self::Context<'_>) -> ExecStep<Site<Id>> {
252 match self {
253 Instr::Ctrl(instr) => {
254 let mut subcore = core.subcore();
255 let step = instr.exec(site, &mut subcore, context);
256 core.merge_subcore(subcore);
257 step
258 }
259 Instr::Gfa(instr) => {
260 let step = instr.exec(site, core, context);
261 step
262 }
263 Instr::Reserved(instr) => {
264 let mut subcore = core.subcore();
265 let step = instr.exec(site, &mut subcore, context);
266 core.merge_subcore(subcore);
267 step
268 }
269 }
270 }
271}
272
273#[cfg(test)]
274mod test {
275 use aluvm::{CoreConfig, Lib, LibId, LibSite, Vm};
276 use amplify::num::u256;
277
278 use super::*;
279 use crate::zk_aluasm;
280
281 const CONFIG: CoreConfig = CoreConfig {
282 halt: true,
283 complexity_lim: None,
284 };
285
286 #[test]
287 fn putd() {
288 const VAL: u256 = u256::from_inner([73864950, 463656, 3456556, 23456657]);
289 let code = zk_aluasm! {
290 mov EA, :VAL;
291 };
292 let lib = Lib::assemble(&code).unwrap();
293 let lib_id = lib.lib_id();
294
295 let mut vm = Vm::<Instr<LibId>>::with(CONFIG, default!());
296 let resolver = |id: LibId| {
297 assert_eq!(id, lib_id);
298 Some(&lib)
299 };
300 let res = vm.exec(LibSite::new(lib_id, 0), &(), resolver).is_ok();
301 assert!(res);
302
303 assert_eq!(vm.core.cx.get(RegE::EA), Some(fe256::from(VAL)));
304 }
305}