circom/ports/wasm/builder.js
ports/wasm/builder.jsRaw
1const streamFromArrayTxt = require("../../src/streamfromarray_txt");
2const streamFromArrayBin = require("../../src/streamfromarray_bin");
3const bigInt = require("big-integer");
4const assert = require("assert");
5const ModuleBuilder = require("wasmbuilder").ModuleBuilder;
6const ModuleBuilderWat = require("wasmbuilder").ModuleBuilderWat;
7const buildRuntime = require("./build_runtime");
8
9
10const errs = require("./errs");
11
12function hexToBytesR(hex) {
13 for (var bytes = [], c = hex.length-2; c >=0; c -= 2)
14 bytes.push(parseInt(hex.substr(c, 2), 16));
15 return bytes;
16}
17
18
19function intToBytes32(v) {
20 return [ v &0xFF, (v>>8) & 0xFF, (v>>16) & 0xFF, (v>>24) & 0xFF ];
21}
22
23
24class CodeBuilderWasm {
25 constructor(fnBuilder) {
26 this.fnBuilder = fnBuilder;
27 this.ops = [];
28 }
29
30 addComment(comment) {
31 this.ops.push({op: "COMMENT", comment});
32 }
33
34 addBlock(block) {
35 this.ops.push({op: "BLOCK", block});
36 }
37
38 calcOffset(dLabel, offsets) {
39 this.ops.push({op: "CALCOFFSETS", dLabel, offsets});
40 }
41
42 assign(dLabel, src, sOffset) {
43 this.ops.push({op: "ASSIGN", dLabel, src, sOffset});
44 }
45
46 getSubComponentOffset(dLabel, component, hash, hashLabel) {
47 this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel});
48 }
49
50 getSubComponentSizes(dLabel, component, hash, hashLabel) {
51 this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel});
52 }
53
54 getSignalOffset(dLabel, component, hash, hashLabel) {
55 this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel});
56 }
57
58 getSignalSizes(dLabel, component, hash, hashLabel) {
59 this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel});
60 }
61
62 setSignal(component, signal, value) {
63 this.ops.push({op: "SETSIGNAL", component, signal, value});
64 }
65
66 getSignal(dLabel, component, signal) {
67 this.ops.push({op: "GETSIGNAL", dLabel, component, signal});
68 }
69
70 copyN(dLabel, offset, src, n) {
71 this.ops.push({op: "COPYN", dLabel, offset, src, n});
72 }
73
74 copyNRet(src, n) {
75 this.ops.push({op: "COPYNRET", src, n});
76 }
77
78 fieldOp(dLabel, fOp, params) {
79 this.ops.push({op: "FOP", dLabel, fOp, params});
80 }
81
82 ret() {
83 this.ops.push({op: "RET"});
84 }
85
86 addLoop(condLabel, body) {
87 this.ops.push({op: "LOOP", condLabel, body});
88 }
89
90 addIf(condLabel, thenCode, elseCode) {
91 this.ops.push({op: "IF", condLabel, thenCode, elseCode});
92 }
93
94 fnCall(fnName, retLabel, params) {
95 this.ops.push({op: "FNCALL", fnName, retLabel, params});
96 }
97
98 checkConstraint(a, b, strErr) {
99 this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr});
100 }
101
102
103 concat(cb) {
104 this.ops.push(...cb.ops);
105 }
106
107 log(val) {
108 this.ops.push({op: "LOG", val});
109 }
110
111 hasCode() {
112 for (let i=0; i<this.ops.length; i++) {
113 if (this.ops[i].op != "COMMENT") return true;
114 }
115 return false;
116 }
117
118
119 _buildOffset(c, offsets) {
120 let rN=0;
121 let S = null;
122 offsets.forEach((o) => {
123 if ((o[0][0] == "V") && (o[1][0]== "V")) {
124 rN += o[0][1]*o[1][1];
125 return;
126 }
127 let f=[];
128 if (o[0][0] == "V") {
129 f = c.i32_const(o[0][1]);
130 } else if (o[0][0] == "RI") {
131 f = c.i32_load(this.fnBuilder._getPtr(c, o[0][1]));
132 } else if (o[0][0] == "R") {
133 f = c.call("Fr_toInt", this.fnBuilder._getPtr(c, o[0][1]));
134 } else {
135 assert(false);
136 }
137 if (o[1][0] == "V") {
138 if (o[1][1]==0) return;
139 if (o[1][1]>1) {
140 f = c.i32_mul(f, c.i32_const(o[1][1]));
141 }
142 } else if (o[1][0] == "RS") {
143 f = c.i32_mul(
144 f,
145 c.i32_load(
146 c.i32_add(
147 c.i32_load(this.fnBuilder._getPtr(c, o[1][1])),
148 c.i32_const(o[1][2]*4)
149 )
150 )
151 );
152 } else {
153 assert(false);
154 }
155 if (S!=null) {
156 S = c.i32_add( S, f);
157 } else {
158 S = f;
159 }
160 });
161 if (rN>0) {
162 if (S!=null) {
163 S = c.i32_add( S, c.i32_const(rN));
164 } else {
165 S = c.i32_const(rN);
166 }
167 }
168 return S;
169 }
170
171 build(c) {
172 const code = [];
173 this.ops.forEach( (o) => {
174 if (o.op == "COMMENT") {
175 code.push(
176 c.comment(o.comment)
177 );
178 // DO nothing
179 } else if (o.op == "BLOCK") {
180 code.push(
181 o.block.build(c)
182 );
183 } else if (o.op == "CALCOFFSETS") {
184 code.push(
185 c.i32_store(
186 this.fnBuilder._getPtr(c, o.dLabel),
187 this._buildOffset(c, o.offsets)
188 )
189 );
190 } else if (o.op == "ASSIGN") {
191 code.push(
192 c.i32_store(
193 this.fnBuilder._getPtr(c, o.dLabel),
194 c.i32_add(
195 this.fnBuilder._deRefFr(c, o.src),
196 c.i32_mul(
197 this.fnBuilder._deRefInt(c, o.sOffset),
198 c.i32_const(this.fnBuilder.builder.sizeFr)
199 )
200 )
201 )
202 );
203 } else if (o.op == "GETSUBCOMPONENTOFFSET") {
204 code.push(
205 c.call(
206 "getSubComponentOffset",
207 this.fnBuilder._getPtr(c, o.dLabel),
208 this.fnBuilder._deRefInt(c, o.component),
209 c.i64_const("0x" + o.hash)
210 )
211 );
212 } else if (o.op == "GETSUBCOMPONENTSIZES") {
213 code.push(
214 c.call(
215 "getSubComponentSizes",
216 this.fnBuilder._getPtr(c, o.dLabel),
217 this.fnBuilder._deRefInt(c, o.component),
218 c.i64_const("0x" + o.hash)
219 )
220 );
221 } else if (o.op == "GETSIGNALOFFSET") {
222 code.push(
223 c.call(
224 "getSignalOffset",
225 this.fnBuilder._getPtr(c, o.dLabel),
226 this.fnBuilder._deRefInt(c, o.component),
227 c.i64_const("0x" + o.hash)
228 )
229 );
230 } else if (o.op == "GETSIGNALSIZES") {
231 code.push(
232 c.call(
233 "getSignalSizes",
234 this.fnBuilder._getPtr(c, o.dLabel),
235 this.fnBuilder._deRefInt(c, o.component),
236 c.i64_const("0x" + o.hash)
237 )
238 );
239 } else if (o.op == "SETSIGNAL") {
240 code.push(
241 c.call(
242 "setSignal",
243 c.getLocal("cIdx"),
244 this.fnBuilder._deRefInt(c, o.component),
245 this.fnBuilder._deRefInt(c, o.signal),
246 this.fnBuilder._deRefFr(c, o.value)
247 )
248 );
249 } else if (o.op == "GETSIGNAL") {
250 code.push(
251 c.call(
252 "getSignal",
253 c.getLocal("cIdx"),
254 this.fnBuilder._getPtr(c, o.dLabel),
255 this.fnBuilder._deRefInt(c, o.component),
256 this.fnBuilder._deRefInt(c, o.signal)
257 )
258 );
259 } else if (o.op == "COPYN") {
260 code.push(
261 c.call(
262 "Fr_copyn",
263 c.i32_add(
264 this.fnBuilder._getPtr(c, o.dLabel),
265 c.i32_mul(
266 this.fnBuilder._deRefInt(c, o.offset),
267 c.i32_const(this.fnBuilder.builder.sizeFr)
268 )
269 ),
270 this.fnBuilder._deRefFr(c, o.src),
271 c.i32_const(o.n)
272 )
273 );
274 } else if (o.op == "COPYNRET") {
275 code.push(
276 c.call(
277 "Fr_copyn",
278 c.getLocal("pRet"),
279 this.fnBuilder._deRefFr(c, o.src),
280 c.i32_const(o.n)
281 )
282 );
283 } else if (o.op == "RET") {
284 code.push(this.fnBuilder._freeStack(c));
285 code.push(c.ret([]));
286 } else if (o.op == "FOP") {
287 let params = [];
288 for (let i=0; i<o.params.length; i++) {
289 params.push( this.fnBuilder._deRefFr(c, o.params[i]));
290 }
291 code.push(
292 c.call(
293 "Fr_" + o.fOp,
294 this.fnBuilder._getPtr(c, o.dLabel),
295 ...params
296 )
297 );
298 } else if (o.op == "LOOP") {
299 code.push(
300 c.block(c.loop(
301 c.br_if(1, c.i32_eqz(c.call("Fr_isTrue", this.fnBuilder._deRefFr(c, ["R", o.condLabel])))),
302 o.body.build(c),
303 c.br(0)
304 ))
305 );
306 } else if (o.op == "IF") {
307 code.push(
308 c.if(
309 c.call("Fr_isTrue", this.fnBuilder._deRefFr(c, ["R", o.condLabel])),
310 o.thenCode.build(c),
311 o.elseCode ? o.elseCode.build(c) : undefined
312 )
313 );
314 } else if (o.op == "FNCALL") {
315 const params = [];
316 for (let i=0; i<o.params.length; i++) {
317 params.push(this.fnBuilder._deRefFr(c, ["R", o.params[i]]));
318 }
319 code.push(
320 c.call(
321 o.fnName,
322 this.fnBuilder._getPtr(c, o.retLabel),
323 ...params
324 )
325 );
326 } else if (o.op == "CHECKCONSTRAINT") {
327 code.push(
328 c.call(
329 "checkConstraint",
330 c.getLocal("cIdx"),
331 this.fnBuilder._deRefFr(c, o.a),
332 this.fnBuilder._deRefFr(c, o.b),
333 c.i32_const(this.fnBuilder.builder.module.allocString(o.strErr))
334 )
335 );
336 } else if (o.op == "LOG") {
337 code.push(
338 c.call(
339 "log",
340 this.fnBuilder._deRefFr(c, o.val)
341 )
342 );
343 }
344 });
345 return code;
346 }
347}
348
349class FunctionBuilderWasm {
350
351 constructor(builder, name, instanceDef, type, params) {
352 this.builder = builder;
353 this.name = name;
354 this.instanceDef = instanceDef;
355 this.type = type; // "COMPONENT" or "FUNCTION"
356 this.definedFrElements = [];
357 this.definedIntElements = [];
358 this.definedSizeElements = [];
359 this.definedPFrElements = [];
360 this.initializedElements = [];
361 this.initializedSignalOffset = [];
362 this.initializedSignalSizes = [];
363
364 this.refs = {};
365
366 this.nFr = 0;
367 this.nInt = 0;
368 this.nSizes = 0;
369 this.nPFr = 0;
370
371 }
372
373 defineFrElements(dLabel, size) {
374 this.refs[dLabel] = {
375 t: "Fr",
376 n: size,
377 idx: this.nFr
378 };
379 this.nFr += size;
380 }
381
382 defineIntElement(dLabel) {
383 this.refs[dLabel] = {
384 t: "I",
385 idx: this.nInt
386 };
387 this.nInt++;
388 }
389
390 defineSizesElement(dLabel) {
391 this.refs[dLabel] = {
392 t: "S",
393 idx: this.nSizes
394 };
395 this.nSizes++;
396 }
397
398 definePFrElement(dLabel) {
399 this.refs[dLabel] = {
400 t: "PFr",
401 idx: this.nPFr
402 };
403 this.nPFr++;
404 }
405
406 initializeFrElement(dLabel, offset, idConstant) {
407 this.initializedElements.push({dLabel, offset, idConstant});
408 }
409
410 initializeSignalOffset(dLabel, component, hash, hashLabel) {
411 this.initializedSignalOffset.push({dLabel, component, hash, hashLabel});
412 }
413
414 initializeSignalSizes(dLabel, component, hash, hashLabel) {
415 this.initializedSignalSizes.push({dLabel, component, hash, hashLabel});
416 }
417
418 setParams(params) {
419 assert(typeof this.params === "undefined");
420 assert(this.type === "FUNCTION");
421 this.params=params;
422 for (let i=0; i<this.params.length; i++) {
423 assert(typeof this.refs[this.params[i]] === "undefined");
424 this.refs[this.params[i]] = {
425 t: "P",
426 l: this.params[i]
427 };
428 }
429 }
430
431 _getPtr(c, label, offset) {
432 offset = offset || 0;
433 assert (typeof this.refs[label] !== "undefined");
434 if (this.refs[label].t == "Fr") {
435 return c.i32_add(
436 c.getLocal("sp"),
437 c.i32_const((this.refs[label].idx + offset) * this.builder.sizeFr)
438 );
439 } else if (this.refs[label].t == "I") {
440 assert(offset == 0);
441 return c.i32_add(
442 c.getLocal("sp"),
443 c.i32_const(
444 this.nFr * this.builder.sizeFr +
445 this.refs[label].idx * 4)
446 );
447 } else if (this.refs[label].t == "S") {
448 assert(offset == 0);
449 return c.i32_add(
450 c.getLocal("sp"),
451 c.i32_const(
452 this.nFr * this.builder.sizeFr +
453 this.nInt * 4 +
454 this.refs[label].idx * 4)
455 );
456 } else if (this.refs[label].t == "PFr") {
457 assert(offset == 0);
458 return c.i32_add(
459 c.getLocal("sp"),
460 c.i32_const(
461 this.nFr * this.builder.sizeFr +
462 this.nInt * 4 +
463 this.nSizes * 4 +
464 this.refs[label].idx * 4)
465 );
466 } else if (this.refs[label].t == "P") {
467 return c.i32_add(
468 c.getLocal(this.refs[label].l),
469 c.i32_const( offset * this.builder.sizeFr )
470 );
471 }
472 }
473
474 _getPtrConstant(c, idConstant) {
475 return c.i32_const( this.builder.pConstants + idConstant * this.builder.sizeFr );
476 }
477
478 _deRefInt(c, ref) {
479 if (ref[0] == "R") {
480 return c.call(
481 "Fr_toInt",
482 this._getPtr(c, ref[1])
483 );
484 } else if (ref[0] == "RI") {
485 return c.i32_load(this._getPtr(c, ref[1]));
486 } else if (ref[0] == "V") {
487 return c.i32_const(ref[1]);
488 } else if (ref[0] == "C") {
489 return c.call(
490 "Fr_toInt",
491 this._getPtrConstant(c, ref[1])
492 );
493 } else if (ref[0] == "CC") {
494 return c.getLocal("cIdx");
495 } else {
496 assert(false);
497 }
498 }
499
500 _deRefFr(c, ref) {
501 if (ref[0] == "R") {
502 if (this.refs[ref[1]].t == "Fr") {
503 return this._getPtr(c, ref[1]);
504 } else if (this.refs[ref[1]].t == "PFr") {
505 return c.i32_load(this._getPtr(c, ref[1]));
506 } else if (this.refs[ref[1]].t == "P") {
507 return c.getLocal(ref[1]);
508 } else {
509 assert(false);
510 }
511 } else if (ref[0] == "C") {
512 return this._getPtrConstant(c, ref[1]);
513 } else {
514 assert(false);
515 }
516 }
517
518 _reserveStack(c) {
519 const code = [];
520 code.push(
521 // Load SP
522 c.setLocal("sp", c.i32_load(c.i32_const(4))),
523
524 // Check we have enough memory
525 c.if(
526 c.i32_gt_u(
527 c.i32_const(this.nStackSize),
528 c.getLocal("sp")
529 ),
530 c.call(
531 "error",
532 c.i32_const(errs.STACK_OUT_OF_MEM.code),
533 c.i32_const(errs.STACK_OUT_OF_MEM.pointer),
534 c.i32_const(0),
535 c.i32_const(0),
536 c.i32_const(0),
537 c.i32_const(0)
538 )
539 ),
540
541 // Reserve space in sp
542 c.setLocal(
543 "sp",
544 c.i32_sub(
545 c.getLocal("sp"),
546 c.i32_const(this.nStackSize)
547 )
548 ),
549
550 // Check if we have enought free memory
551 c.if(
552 c.i32_gt_u(
553 c.i32_load(c.i32_const(0)),
554 c.getLocal("sp")
555 ),
556 c.call(
557 "error",
558 c.i32_const(errs.STACK_TOO_SMALL.code),
559 c.i32_const(errs.STACK_TOO_SMALL.pointer),
560 c.i32_const(0),
561 c.i32_const(0),
562 c.i32_const(0),
563 c.i32_const(0)
564 )
565 ),
566
567 // Save sp
568 c.i32_store(c.i32_const(4), c.getLocal("sp"))
569 );
570 return code;
571 }
572
573 _freeStack(c) {
574 const code = [];
575 code.push(
576 c.i32_store(
577 c.i32_const(4),
578 c.i32_add(
579 c.getLocal("sp"),
580 c.i32_const(this.nStackSize)
581 )
582 )
583 );
584 return code;
585 }
586
587 _buildHeader(c) {
588 const code = [];
589 this.nStackSize =
590 this.nFr * this.builder.sizeFr +
591 this.nInt * 4 +
592 this.nSizes * 4 +
593 this.nPFr * 4;
594
595
596 code.push( this._reserveStack(c));
597
598 this.initializedElements.forEach( (o) => {
599 code.push(
600 c.call(
601 "Fr_copy",
602 this._getPtr(c, o.dLabel, o.offset),
603 this._getPtrConstant(c, o.idConstant)
604 )
605 );
606 });
607 this.initializedSignalOffset.forEach( (o) => {
608 code.push(
609 c.call(
610 "getSignalOffset",
611 this._getPtr(c, o.dLabel, o.offset),
612 this._deRefInt(c, o.component),
613 c.i64_const("0x" + o.hash)
614 )
615 );
616 });
617 this.initializedSignalSizes.forEach( (o) => {
618 code.push(
619 c.call(
620 "getSignalSizes",
621 this._getPtr(c, o.dLabel, o.offset),
622 this._deRefInt(c, o.component),
623 c.i64_const("0x" + o.hash)
624 )
625 );
626 });
627 return code;
628 }
629
630 _buildFooter(c) {
631 return this._freeStack(c);
632 }
633
634 newCodeBuilder() {
635 return new CodeBuilderWasm(this);
636 }
637
638 setBody(body) {
639 this.body = body;
640 }
641
642 build(module) {
643
644
645 const f = module.addFunction(this.name, this.instanceDef);
646
647 if (this.type=="COMPONENT") {
648 f.addParam("cIdx", "i32");
649 } else if (this.type=="FUNCTION") {
650 f.addParam("pRet", "i32");
651 for (let i=0;i<this.params.length;i++ ) {
652 f.addParam(this.params[i], "i32");
653 }
654 } else {
655 assert(false);
656 }
657 f.addLocal("sp", "i32");
658
659 const c = f.getCodeBuilder();
660
661
662 const code = [];
663 if (this.type=="COMPONENT") {
664 code.push(c.call("componentStarted", c.getLocal("cIdx")));
665 }
666
667 code.push(this._buildHeader(c));
668 code.push(this.body.build(c));
669 if (this.type=="COMPONENT") {
670 code.push(c.call("componentFinished", c.getLocal("cIdx")));
671 }
672 code.push(this._buildFooter(c));
673
674 f.addCode(flatArray(code));
675
676 function flatArray(c) {
677 const res=[];
678 for (let i=0; i<c.length; i++) {
679 if (Array.isArray(c[i])) {
680 res.push(...flatArray(c[i]));
681 } else {
682 res.push(c[i]);
683 }
684 }
685 return res;
686 }
687 }
688
689}
690
691class BuilderWasm {
692 constructor() {
693 this.hashMaps={};
694 this.componentEntriesTables={};
695 this.sizes ={};
696 this.constants = [];
697 this.usedConstants = {};
698 this.functions = [];
699 this.components = [];
700
701 this.TYPE_SIGNAL = 1;
702 this.TYPE_COMPONENT = 2;
703
704 this.addConstant(bigInt(0)); // constants[0] = 0;
705 this.addConstant(bigInt(1)); // constants[1] = 1;
706
707 this.offsetComponentNInputSignals = 12;
708 this.sizeofComponent = 20;
709 }
710
711 setHeader(header) {
712 this.header=header;
713 this.n64 = Math.floor((this.header.P.bitLength() - 1) / 64)+1;
714 this.sizeFr = this.n64*8 + 8;
715 }
716
717 // ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
718 addHashMap(name, table) {
719 this.hashMaps[name] = {table};
720 }
721
722 addComponentEntriesTable(name, cet) {
723 this.componentEntriesTables[name] = cet;
724 }
725
726 addSizes(name, table) {
727 this.sizes[name] = {table};
728 }
729
730 addConstant(c) {
731 c = bigInt(c);
732 const cS = c.toString();
733 if (this.usedConstants[cS]) return this.usedConstants[cS];
734 this.constants.push(c);
735 this.usedConstants[cS] = this.constants.length - 1;
736 return this.constants.length - 1;
737 }
738
739 addFunction(fnBuilder) {
740 this.functions.push(fnBuilder);
741 }
742
743 addComponent(component) {
744 this.components.push(component);
745 }
746
747 setMapIsInput(map) {
748 this.mapIsInput = map;
749 }
750
751 setWit2Sig(wit2sig) {
752 this.wit2sig = wit2sig;
753 }
754
755
756 newComponentFunctionBuilder(name, instanceDef) {
757 return new FunctionBuilderWasm(this, name, instanceDef, "COMPONENT");
758 }
759
760 newFunctionBuilder(name, instanceDef, params) {
761 return new FunctionBuilderWasm(this, name, instanceDef, "FUNCTION", params);
762 }
763
764
765 // Body functions
766
767 _buildHeader(module) {
768
769 this.pCircuit = module.alloc(48);
770
771 this.pNSignals = this.pCircuit;
772 this.pNComponents = this.pCircuit + 4;
773 this.pNInputs = this.pCircuit + 8;
774 this.pNOutputs = this.pCircuit + 12;
775 this.pNVars = this.pCircuit + 16;
776 this.ppWit2sig = this.pCircuit + 20;
777 this.ppComponents = this.pCircuit + 24;
778 this.ppMapIsInput = this.pCircuit + 28;
779 this.ppConstants = this.pCircuit + 32;
780 this.ppSignals = this.pCircuit + 36;
781 this.ppInputSignalsToTrigger = this.pCircuit + 40;
782 this.ppSignalsAssigned = this.pCircuit + 44;
783 }
784
785 _buildSizes(module) {
786 for (let sName in this.sizes) {
787 const accSizes = this.sizes[sName];
788
789 const bytes = [];
790
791 for (let i=0; i<accSizes.table.length; i++) {
792 bytes.push(intToBytes32(accSizes.table[i]));
793 }
794
795 const fBytes = [].concat(...bytes);
796 accSizes.pointer = module.alloc(fBytes);
797 }
798 }
799
800
801 _buildHashMaps(module) {
802
803 for (let hmName in this.hashMaps ) {
804 const hm = this.hashMaps[hmName];
805
806 const bytes = [];
807
808 for (let i=0; i<256; i++) {
809 if (hm.table[i]) {
810 bytes.push(hexToBytesR(hm.table[i][0]));
811 bytes.push(intToBytes32(hm.table[i][1]));
812 } else {
813 bytes.push([0,0,0,0,0,0,0,0,0,0,0,0]);
814 }
815 }
816
817 const fBytes = [].concat(...bytes);
818 hm.pointer = module.alloc(fBytes);
819 }
820
821 }
822
823 _buildComponentEntriesTables(module) {
824
825 for (let cetName in this.componentEntriesTables) {
826 const cet = this.componentEntriesTables[cetName];
827
828 const bytes = [];
829
830 for (let j=0; j<cet.length; j++) {
831 const ty = cet[j].type == "S" ? this.TYPE_SIGNAL : this.TYPE_COMPONENT;
832 bytes.push(intToBytes32(cet[j].offset));
833 bytes.push(intToBytes32(this.sizes[cet[j].sizeName].pointer));
834 bytes.push(intToBytes32(ty));
835 }
836
837 const fBytes = [].concat(...bytes);
838 this.componentEntriesTables[cetName].pointer = module.alloc(fBytes);
839 }
840 }
841
842
843 _buildConstants(module) {
844 const self = this;
845 const R = bigInt.one.shiftLeft(this.n64*64);
846
847 const bytes = [];
848 for (let i=0; i<self.constants.length; i++) {
849 bytes.push(Fr2Bytes(self.constants[i]));
850 }
851
852 const fBytes = [].concat(...bytes);
853 this.pConstants = module.alloc(fBytes);
854
855 function Fr2Bytes(n) {
856 if (n.lt(bigInt("80000000", 16)) ) {
857 return shortMontgomeryPositive(n);
858 }
859 if (n.geq(self.header.P.minus(bigInt("80000000", 16))) ) {
860 return shortMontgomeryNegative(n);
861 }
862 return longMontgomery(n);
863
864
865 function shortMontgomeryPositive(a) {
866 return [
867 ...intToBytes32(parseInt(a)),
868 ...intToBytes32(0x40000000),
869 ...long(toMontgomery(a))
870 ];
871 }
872
873
874 function shortMontgomeryNegative(a) {
875 const b = a.minus(self.header.P);
876 return [
877 ...intToBytes32(parseInt(b)),
878 ...intToBytes32(0x40000000),
879 ...long(toMontgomery(a))
880 ];
881 }
882
883 function longMontgomery(a) {
884 return [
885 ...intToBytes32(0),
886 ...intToBytes32(0xC0000000),
887 ...long(toMontgomery(a))
888 ];
889 }
890
891 function long(a) {
892 const bytes = [];
893 let r = bigInt(a);
894 let S = "";
895 let i = 0;
896 while (!r.isZero()) {
897 S = ("0000000000000000" + r.and(bigInt("FFFFFFFFFFFFFFFF", 16)).toString(16)).substr(-16);
898 bytes.push(hexToBytesR(S));
899 i++;
900 r = r.shiftRight(64);
901 }
902 while (i<self.n64) {
903 bytes.push(hexToBytesR("0000000000000000"));
904 i++;
905 }
906
907 const fBytes = [].concat(...bytes);
908 return fBytes;
909 }
910
911 function toMontgomery(a) {
912 return a.times(R).mod(self.header.P);
913 }
914
915 }
916 }
917
918 _buildFunctions(module) {
919 for (let i=0; i<this.functions.length; i++) {
920 const cfb = this.functions[i];
921 cfb.build(module);
922 }
923 }
924
925 _buildComponents(module) {
926 const bytes = [];
927 for (let i=0; i<this.components.length; i++) {
928 const c = this.components[i];
929
930 bytes.push(intToBytes32(this.hashMaps[c.hashMapName].pointer));
931 bytes.push(intToBytes32(this.componentEntriesTables[c.entryTableName].pointer));
932 bytes.push(intToBytes32(i));
933 bytes.push(intToBytes32(c.nInSignals));
934 bytes.push(intToBytes32(c.newThread ? 1 : 0));
935
936 module.addFunctionToTable(c.functionName);
937 }
938
939 const fBytes = [].concat(...bytes);
940 this.pComponents = module.alloc(fBytes);
941 }
942
943 _buildMapIsInput(module) {
944 const bytes = [];
945 for (let i=0; i<this.mapIsInput.length; i++) {
946 bytes.push(intToBytes32(this.mapIsInput[i]));
947 }
948
949 const fBytes = [].concat(...bytes);
950 this.pMapIsInput = module.alloc(fBytes);
951 }
952
953 _buildWit2Sig(module) {
954 const bytes = [];
955 for (let i=0; i<this.wit2sig.length; i++) {
956 bytes.push(intToBytes32(this.wit2sig[i]));
957 }
958
959 const fBytes = [].concat(...bytes);
960 this.pWit2sig = module.alloc(fBytes);
961 }
962
963 _buildCircuitVar(module) {
964 module.addData(this.pNSignals, intToBytes32(this.header.NSignals));
965 module.addData(this.pNComponents, intToBytes32(this.header.NComponents));
966 module.addData(this.pNInputs, intToBytes32(this.header.NInputs));
967 module.addData(this.pNOutputs, intToBytes32(this.header.NOutputs));
968 module.addData(this.pNVars, intToBytes32(this.header.NVars));
969 module.addData(this.ppWit2sig, intToBytes32(this.pWit2sig));
970 module.addData(this.ppComponents, intToBytes32(this.pComponents));
971 module.addData(this.ppMapIsInput, intToBytes32(this.pMapIsInput));
972 module.addData(this.ppConstants, intToBytes32(this.pConstants));
973 module.addData(this.ppSignals, intToBytes32(this.pSignals));
974 module.addData(this.ppInputSignalsToTrigger, intToBytes32(this.pInputSignalsToTrigger));
975 module.addData(this.ppSignalsAssigned, intToBytes32(this.pSignalsAssigned));
976
977 }
978
979 _buildErrors(module) {
980 for (let e in errs) {
981 errs[e].pointer = module.allocString(errs[e].str);
982 }
983 }
984
985
986 build(outType) {
987 let module;
988 if (outType == "wasm") {
989 module=new ModuleBuilder();
990 } else if (outType == "wat") {
991 module=new ModuleBuilderWat();
992 } else {
993 assert(false);
994 }
995 this.module = module;
996
997 // First of all reseve the space for the header so this has a fixed position starting at 8.
998 this._buildHeader(module);
999 this._buildErrors(module);
1000 buildRuntime(module, this);
1001 this._buildSizes(module);
1002 this._buildConstants(module);
1003 this._buildHashMaps(module);
1004 this._buildComponentEntriesTables(module);
1005 this._buildFunctions(module);
1006 this._buildComponents(module);
1007 this._buildMapIsInput(module);
1008 this._buildWit2Sig(module);
1009 this._buildCircuitVar(module);
1010
1011 module.setMemory(2000);
1012 if (outType == "wasm") {
1013 return streamFromArrayBin(module.build());
1014 } else if (outType == "wat") {
1015 return streamFromArrayTxt(module.build());
1016 } else {
1017 assert(false);
1018 }
1019 }
1020}
1021
1022
1023module.exports = BuilderWasm;