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