arnaucube
arnaucube/circomPublic Self-hosted repository
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;