2 *****************************************************************************
4 Implementation of interfaces for the TinyRAM ALU control-flow gadgets.
6 See alu_control_flow.hpp .
8 *****************************************************************************
9 * @author This file is part of libsnark, developed by SCIPR Lab
10 * and contributors (see AUTHORS).
11 * @copyright MIT license (see LICENSE file)
12 *****************************************************************************/
14 #ifndef ALU_CONTROL_FLOW_TCC_
15 #define ALU_CONTROL_FLOW_TCC_
17 #include <libff/common/profiling.hpp>
23 template<typename FieldT>
24 void ALU_jmp_gadget<FieldT>::generate_r1cs_constraints()
26 this->pb.add_r1cs_constraint(
27 r1cs_constraint<FieldT>({ONE}, {this->argval2.packed}, {this->result}),
28 FMT(this->annotation_prefix, " jmp_result"));
31 template<typename FieldT> void ALU_jmp_gadget<FieldT>::generate_r1cs_witness()
33 this->pb.val(this->result) = this->pb.val(this->argval2.packed);
36 template<typename FieldT> void test_ALU_jmp_gadget()
38 libff::print_time("starting jmp test");
40 tinyram_architecture_params ap(16, 16);
42 P.instructions = generate_tinyram_prelude(ap);
43 tinyram_protoboard<FieldT> pb(ap, P.size(), 0, 10);
45 word_variable_gadget<FieldT> pc(pb, "pc"), argval2(pb, "argval2");
46 pb_variable<FieldT> flag, result;
48 pc.generate_r1cs_constraints(true);
49 argval2.generate_r1cs_constraints(true);
50 flag.allocate(pb, "flag");
51 result.allocate(pb, "result");
53 ALU_jmp_gadget<FieldT> jmp(pb, pc, argval2, flag, result, "jmp");
54 jmp.generate_r1cs_constraints();
56 pb.val(argval2.packed) = FieldT(123);
57 argval2.generate_r1cs_witness_from_packed();
59 jmp.generate_r1cs_witness();
61 assert(pb.val(result) == FieldT(123));
62 assert(pb.is_satisfied());
63 libff::print_time("positive jmp test successful");
65 pb.val(result) = FieldT(1);
66 assert(!pb.is_satisfied());
67 libff::print_time("negative jmp test successful");
71 template<typename FieldT>
72 void ALU_cjmp_gadget<FieldT>::generate_r1cs_constraints()
75 flag1 * argval2 + (1-flag1) * (pc1 + 1) = cjmp_result
76 flag1 * (argval2 - pc1 - 1) = cjmp_result - pc1 - 1
78 Note that instruction fetch semantics require program counter to
79 be aligned to the double word by rounding down, and pc_addr in
80 the outer reduction is expressed as a double word address. To
81 achieve this we just discard the first ap.subaddr_len() bits of
82 the byte address of the PC.
84 this->pb.add_r1cs_constraint(
85 r1cs_constraint<FieldT>(
87 pb_packing_sum<FieldT>(pb_variable_array<FieldT>(
88 this->argval2.bits.begin() + this->pb.ap.subaddr_len(),
89 this->argval2.bits.end())) -
91 this->result - this->pc.packed - 1),
92 FMT(this->annotation_prefix, " cjmp_result"));
95 template<typename FieldT> void ALU_cjmp_gadget<FieldT>::generate_r1cs_witness()
97 this->pb.val(this->result) =
98 ((this->pb.val(this->flag) == FieldT::one())
100 this->pb.val(this->argval2.packed).as_ulong() >>
101 this->pb.ap.subaddr_len())
102 : this->pb.val(this->pc.packed) + FieldT::one());
105 template<typename FieldT> void test_ALU_cjmp_gadget()
108 libff::print_time("starting cjmp test");
110 tinyram_architecture_params ap(16, 16);
112 P.instructions = generate_tinyram_prelude(ap);
113 tinyram_protoboard<FieldT> pb(ap, P.size(), 0, 10);
115 word_variable_gadget<FieldT> pc(pb, "pc"), argval2(pb, "argval2");
116 pb_variable<FieldT> flag, result;
118 pc.generate_r1cs_constraints(true);
119 argval2.generate_r1cs_constraints(true);
120 flag.allocate(pb, "flag");
121 result.allocate(pb, "result");
123 ALU_cjmp_gadget<FieldT> cjmp(pb, pc, argval2, flag, result, "cjmp");
124 cjmp.generate_r1cs_constraints();
126 pb.val(argval2.packed) = FieldT(123);
127 argval2.generate_r1cs_witness_from_packed();
128 pb.val(pc.packed) = FieldT(456);
129 pc.generate_r1cs_witness_from_packed();
131 pb.val(flag) = FieldT(1);
132 cjmp.generate_r1cs_witness();
134 assert(pb.val(result) == FieldT(123));
135 assert(pb.is_satisfied());
136 libff::print_time("positive cjmp test successful");
138 pb.val(flag) = FieldT(0);
139 assert(!pb.is_satisfied());
140 libff::print_time("negative cjmp test successful");
142 pb.val(flag) = FieldT(0);
143 cjmp.generate_r1cs_witness();
145 assert(pb.val(result) == FieldT(456 + 2 * ap.w / 8));
146 assert(pb.is_satisfied());
147 libff::print_time("positive cjmp test successful");
149 pb.val(flag) = FieldT(1);
150 assert(!pb.is_satisfied());
151 libff::print_time("negative cjmp test successful");
155 template<typename FieldT>
156 void ALU_cnjmp_gadget<FieldT>::generate_r1cs_constraints()
159 flag1 * (pc1 + inc) + (1-flag1) * argval2 = cnjmp_result
160 flag1 * (pc1 + inc - argval2) = cnjmp_result - argval2
162 Note that instruction fetch semantics require program counter to
163 be aligned to the double word by rounding down, and pc_addr in
164 the outer reduction is expressed as a double word address. To
165 achieve this we just discard the first ap.subaddr_len() bits of
166 the byte address of the PC.
168 this->pb.add_r1cs_constraint(
169 r1cs_constraint<FieldT>(
171 this->pc.packed + 1 -
172 pb_packing_sum<FieldT>(pb_variable_array<FieldT>(
173 this->argval2.bits.begin() + this->pb.ap.subaddr_len(),
174 this->argval2.bits.end())),
176 pb_packing_sum<FieldT>(pb_variable_array<FieldT>(
177 this->argval2.bits.begin() + this->pb.ap.subaddr_len(),
178 this->argval2.bits.end()))),
179 FMT(this->annotation_prefix, " cnjmp_result"));
182 template<typename FieldT> void ALU_cnjmp_gadget<FieldT>::generate_r1cs_witness()
184 this->pb.val(this->result) =
185 ((this->pb.val(this->flag) == FieldT::one())
186 ? this->pb.val(this->pc.packed) + FieldT::one()
188 this->pb.val(this->argval2.packed).as_ulong() >>
189 this->pb.ap.subaddr_len()));
192 template<typename FieldT> void test_ALU_cnjmp_gadget()
195 libff::print_time("starting cnjmp test");
197 tinyram_architecture_params ap(16, 16);
199 P.instructions = generate_tinyram_prelude(ap);
200 tinyram_protoboard<FieldT> pb(ap, P.size(), 0, 10);
202 word_variable_gadget<FieldT> pc(pb, "pc"), argval2(pb, "argval2");
203 pb_variable<FieldT> flag, result;
205 pc.generate_r1cs_constraints(true);
206 argval2.generate_r1cs_constraints(true);
207 flag.allocate(pb, "flag");
208 result.allocate(pb, "result");
210 ALU_cnjmp_gadget<FieldT> cnjmp(pb, pc, argval2, flag, result, "cjmp");
211 cnjmp.generate_r1cs_constraints();
213 pb.val(argval2.packed) = FieldT(123);
214 argval2.generate_r1cs_witness_from_packed();
215 pb.val(pc.packed) = FieldT(456);
216 pc.generate_r1cs_witness_from_packed();
218 pb.val(flag) = FieldT(0);
219 cnjmp.generate_r1cs_witness();
221 assert(pb.val(result) == FieldT(123));
222 assert(pb.is_satisfied());
223 libff::print_time("positive cnjmp test successful");
225 pb.val(flag) = FieldT(1);
226 assert(!pb.is_satisfied());
227 libff::print_time("negative cnjmp test successful");
229 pb.val(flag) = FieldT(1);
230 cnjmp.generate_r1cs_witness();
232 assert(pb.val(result) == FieldT(456 + (2 * pb.ap.w / 8)));
233 assert(pb.is_satisfied());
234 libff::print_time("positive cnjmp test successful");
236 pb.val(flag) = FieldT(0);
237 assert(!pb.is_satisfied());
238 libff::print_time("negative cnjmp test successful");
241 } // namespace libsnark
243 #endif // ALU_CONTROL_FLOW_TCC_