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    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                    "err",
 532                    c.i32_const(errs.STACK_OUT_OF_MEM.code),
 533                    c.i32_const(errs.STACK_OUT_OF_MEM.pointer)
 534                )
 535            ),
 536
 537            // Reserve space in sp
 538            c.setLocal(
 539                "sp",
 540                c.i32_sub(
 541                    c.getLocal("sp"),
 542                    c.i32_const(this.nStackSize)
 543                )
 544            ),
 545
 546            // Check if we have enought free memory
 547            c.if(
 548                c.i32_gt_u(
 549                    c.i32_load(c.i32_const(0)),
 550                    c.getLocal("sp")
 551                ),
 552                c.call(
 553                    "err",
 554                    c.i32_const(errs.STACK_TOO_SMALL.code),
 555                    c.i32_const(errs.STACK_TOO_SMALL.pointer)
 556                )
 557            ),
 558
 559            // Save sp
 560            c.i32_store(c.i32_const(4), c.getLocal("sp"))
 561        );
 562        return code;
 563    }
 564
 565    _freeStack(c) {
 566        const code = [];
 567        code.push(
 568            c.i32_store(
 569                c.i32_const(4),
 570                c.i32_add(
 571                    c.getLocal("sp"),
 572                    c.i32_const(this.nStackSize)
 573                )
 574            )
 575        );
 576        return code;
 577    }
 578
 579    _buildHeader(c) {
 580        const code = [];
 581        this.nStackSize =
 582            this.nFr * this.builder.sizeFr +
 583            this.nInt * 4 +
 584            this.nSizes * 4 +
 585            this.nPFr * 4;
 586
 587
 588        code.push( this._reserveStack(c));
 589
 590        this.initializedElements.forEach( (o) => {
 591            code.push(
 592                c.call(
 593                    "Fr_copy",
 594                    this._getPtr(c, o.dLabel, o.offset),
 595                    this._getPtrConstant(c, o.idConstant)
 596                )
 597            );
 598        });
 599        this.initializedSignalOffset.forEach( (o) => {
 600            code.push(
 601                c.call(
 602                    "getSignalOffset",
 603                    this._getPtr(c, o.dLabel, o.offset),
 604                    this._deRefInt(c, o.component),
 605                    c.i64_const("0x" + o.hash)
 606                )
 607            );
 608        });
 609        this.initializedSignalSizes.forEach( (o) => {
 610            code.push(
 611                c.call(
 612                    "getSignalSizes",
 613                    this._getPtr(c, o.dLabel, o.offset),
 614                    this._deRefInt(c, o.component),
 615                    c.i64_const("0x" + o.hash)
 616                )
 617            );
 618        });
 619        return code;
 620    }
 621
 622    _buildFooter(c) {
 623        return this._freeStack(c);
 624    }
 625
 626    newCodeBuilder() {
 627        return new CodeBuilderWasm(this);
 628    }
 629
 630    setBody(body) {
 631        this.body = body;
 632    }
 633
 634    build(module) {
 635
 636
 637        const f = module.addFunction(this.name, this.instanceDef);
 638
 639        if (this.type=="COMPONENT") {
 640            f.addParam("cIdx", "i32");
 641        } else if (this.type=="FUNCTION") {
 642            f.addParam("pRet", "i32");
 643            for (let i=0;i<this.params.length;i++ ) {
 644                f.addParam(this.params[i], "i32");
 645            }
 646        } else {
 647            assert(false);
 648        }
 649        f.addLocal("sp", "i32");
 650
 651        const c = f.getCodeBuilder();
 652
 653        const code = [];
 654        code.push(this._buildHeader(c));
 655        code.push(this.body.build(c));
 656        if (this.type=="COMPONENT") {
 657            code.push(c.call("componentFinished", c.getLocal("cIdx")));
 658        }
 659        code.push(this._buildFooter(c));
 660
 661        f.addCode(flatArray(code));
 662
 663        function flatArray(c) {
 664            const res=[];
 665            for (let i=0; i<c.length; i++) {
 666                if (Array.isArray(c[i])) {
 667                    res.push(...flatArray(c[i]));
 668                } else {
 669                    res.push(c[i]);
 670                }
 671            }
 672            return res;
 673        }
 674    }
 675
 676}
 677
 678class BuilderWasm {
 679    constructor(sanityCheck) {
 680        this.hashMaps={};
 681        this.componentEntriesTables={};
 682        this.sizes ={};
 683        this.constants = [];
 684        this.usedConstants = {};
 685        this.functions = [];
 686        this.components = [];
 687        this.sanityCheck = sanityCheck;
 688
 689        this.TYPE_SIGNAL = 1;
 690        this.TYPE_COMPONENT = 2;
 691
 692        this.addConstant(bigInt(0));  // constants[0] = 0;
 693        this.addConstant(bigInt(1));  // constants[1] = 1;
 694
 695        this.offsetComponentNInputSignals = 12;
 696        this.sizeofComponent = 20;
 697    }
 698
 699    setHeader(header) {
 700        this.header=header;
 701        this.n64 = Math.floor((this.header.P.bitLength() - 1) / 64)+1;
 702        this.sizeFr = this.n64*8 + 8;
 703    }
 704
 705    // ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements.
 706    addHashMap(name, table) {
 707        this.hashMaps[name] = {table};
 708    }
 709
 710    addComponentEntriesTable(name, cet) {
 711        this.componentEntriesTables[name] = cet;
 712    }
 713
 714    addSizes(name, table) {
 715        this.sizes[name] = {table};
 716    }
 717
 718    addConstant(c) {
 719        c = bigInt(c);
 720        const cS = c.toString();
 721        if (this.usedConstants[cS]) return this.usedConstants[cS];
 722        this.constants.push(c);
 723        this.usedConstants[cS] = this.constants.length - 1;
 724        return this.constants.length - 1;
 725    }
 726
 727    addFunction(fnBuilder) {
 728        this.functions.push(fnBuilder);
 729    }
 730
 731    addComponent(component) {
 732        this.components.push(component);
 733    }
 734
 735    setMapIsInput(map) {
 736        this.mapIsInput = map;
 737    }
 738
 739    setWit2Sig(wit2sig) {
 740        this.wit2sig = wit2sig;
 741    }
 742
 743
 744    newComponentFunctionBuilder(name, instanceDef) {
 745        return new FunctionBuilderWasm(this, name, instanceDef, "COMPONENT");
 746    }
 747
 748    newFunctionBuilder(name, instanceDef, params) {
 749        return new FunctionBuilderWasm(this, name, instanceDef, "FUNCTION", params);
 750    }
 751
 752
 753    // Body functions
 754
 755    _buildHeader(module) {
 756
 757        this.pCircuit = module.alloc(48);
 758
 759        this.pNSignals = this.pCircuit;
 760        this.pNComponents = this.pCircuit + 4;
 761        this.pNInputs = this.pCircuit + 8;
 762        this.pNOutputs = this.pCircuit + 12;
 763        this.pNVars = this.pCircuit + 16;
 764        this.ppWit2sig = this.pCircuit + 20;
 765        this.ppComponents = this.pCircuit + 24;
 766        this.ppMapIsInput = this.pCircuit + 28;
 767        this.ppConstants = this.pCircuit + 32;
 768        this.ppSignals = this.pCircuit + 36;
 769        this.ppInputSignalsToTrigger = this.pCircuit + 40;
 770        this.ppSignalsAssigned = this.pCircuit + 44;
 771    }
 772
 773    _buildSizes(module) {
 774        for (let sName in this.sizes) {
 775            const accSizes = this.sizes[sName];
 776
 777            const bytes = [];
 778
 779            for (let i=0; i<accSizes.table.length; i++) {
 780                bytes.push(intToBytes32(accSizes.table[i]));
 781            }
 782
 783            const fBytes = [].concat(...bytes);
 784            accSizes.pointer = module.alloc(fBytes);
 785        }
 786    }
 787
 788
 789    _buildHashMaps(module) {
 790
 791        for (let hmName in this.hashMaps ) {
 792            const hm = this.hashMaps[hmName];
 793
 794            const bytes = [];
 795
 796            for (let i=0; i<256; i++) {
 797                if (hm.table[i]) {
 798                    bytes.push(hexToBytesR(hm.table[i][0]));
 799                    bytes.push(intToBytes32(hm.table[i][1]));
 800                } else {
 801                    bytes.push([0,0,0,0,0,0,0,0,0,0,0,0]);
 802                }
 803            }
 804
 805            const fBytes = [].concat(...bytes);
 806            hm.pointer = module.alloc(fBytes);
 807        }
 808
 809    }
 810
 811    _buildComponentEntriesTables(module) {
 812
 813        for (let cetName in this.componentEntriesTables) {
 814            const cet = this.componentEntriesTables[cetName];
 815
 816            const bytes = [];
 817
 818            for (let j=0; j<cet.length; j++) {
 819                const ty = cet[j].type == "S" ? this.TYPE_SIGNAL : this.TYPE_COMPONENT;
 820                bytes.push(intToBytes32(cet[j].offset));
 821                bytes.push(intToBytes32(this.sizes[cet[j].sizeName].pointer));
 822                bytes.push(intToBytes32(ty));
 823            }
 824
 825            const fBytes = [].concat(...bytes);
 826            this.componentEntriesTables[cetName].pointer = module.alloc(fBytes);
 827        }
 828    }
 829
 830
 831    _buildConstants(module) {
 832        const self = this;
 833        const R = bigInt.one.shiftLeft(this.n64*64);
 834
 835        const bytes = [];
 836        for (let i=0; i<self.constants.length; i++) {
 837            bytes.push(Fr2Bytes(self.constants[i]));
 838        }
 839
 840        const fBytes = [].concat(...bytes);
 841        this.pConstants = module.alloc(fBytes);
 842
 843        function Fr2Bytes(n) {
 844            if (n.lt(bigInt("80000000", 16)) ) {
 845                return shortMontgomeryPositive(n);
 846            }
 847            if (n.geq(self.header.P.minus(bigInt("80000000", 16))) ) {
 848                return shortMontgomeryNegative(n);
 849            }
 850            return longMontgomery(n);
 851
 852
 853            function shortMontgomeryPositive(a) {
 854                return [
 855                    ...intToBytes32(parseInt(a)),
 856                    ...intToBytes32(0x40000000),
 857                    ...long(toMontgomery(a))
 858                ];
 859            }
 860
 861
 862            function shortMontgomeryNegative(a) {
 863                const b = a.minus(self.header.P);
 864                return [
 865                    ...intToBytes32(parseInt(b)),
 866                    ...intToBytes32(0x40000000),
 867                    ...long(toMontgomery(a))
 868                ];
 869            }
 870
 871            function longMontgomery(a) {
 872                return [
 873                    ...intToBytes32(0),
 874                    ...intToBytes32(0xC0000000),
 875                    ...long(toMontgomery(a))
 876                ];
 877            }
 878
 879            function long(a) {
 880                const bytes = [];
 881                let r = bigInt(a);
 882                let S = "";
 883                let i = 0;
 884                while (!r.isZero()) {
 885                    S = ("0000000000000000" + r.and(bigInt("FFFFFFFFFFFFFFFF", 16)).toString(16)).substr(-16);
 886                    bytes.push(hexToBytesR(S));
 887                    i++;
 888                    r = r.shiftRight(64);
 889                }
 890                while (i<self.n64) {
 891                    bytes.push(hexToBytesR("0000000000000000"));
 892                    i++;
 893                }
 894
 895                const fBytes = [].concat(...bytes);
 896                return fBytes;
 897            }
 898
 899            function toMontgomery(a) {
 900                return a.times(R).mod(self.header.P);
 901            }
 902
 903        }
 904    }
 905
 906    _buildFunctions(module) {
 907        for (let i=0; i<this.functions.length; i++) {
 908            const cfb = this.functions[i];
 909            cfb.build(module);
 910        }
 911    }
 912
 913    _buildComponents(module) {
 914        const bytes = [];
 915        for (let i=0; i<this.components.length; i++) {
 916            const c = this.components[i];
 917
 918            bytes.push(intToBytes32(this.hashMaps[c.hashMapName].pointer));
 919            bytes.push(intToBytes32(this.componentEntriesTables[c.entryTableName].pointer));
 920            bytes.push(intToBytes32(i));
 921            bytes.push(intToBytes32(c.nInSignals));
 922            bytes.push(intToBytes32(c.newThread ? 1 : 0));
 923
 924            module.addFunctionToTable(c.functionName);
 925        }
 926
 927        const fBytes = [].concat(...bytes);
 928        this.pComponents = module.alloc(fBytes);
 929    }
 930
 931    _buildMapIsInput(module) {
 932        const bytes = [];
 933        for (let i=0; i<this.mapIsInput.length; i++) {
 934            bytes.push(intToBytes32(this.mapIsInput[i]));
 935        }
 936
 937        const fBytes = [].concat(...bytes);
 938        this.pMapIsInput = module.alloc(fBytes);
 939    }
 940
 941    _buildWit2Sig(module) {
 942        const bytes = [];
 943        for (let i=0; i<this.wit2sig.length; i++) {
 944            bytes.push(intToBytes32(this.wit2sig[i]));
 945        }
 946
 947        const fBytes = [].concat(...bytes);
 948        this.pWit2sig = module.alloc(fBytes);
 949    }
 950
 951    _buildCircuitVar(module) {
 952        module.addData(this.pNSignals, intToBytes32(this.header.NSignals));
 953        module.addData(this.pNComponents, intToBytes32(this.header.NComponents));
 954        module.addData(this.pNInputs, intToBytes32(this.header.NInputs));
 955        module.addData(this.pNOutputs, intToBytes32(this.header.NOutputs));
 956        module.addData(this.pNVars, intToBytes32(this.header.NVars));
 957        module.addData(this.ppWit2sig, intToBytes32(this.pWit2sig));
 958        module.addData(this.ppComponents, intToBytes32(this.pComponents));
 959        module.addData(this.ppMapIsInput, intToBytes32(this.pMapIsInput));
 960        module.addData(this.ppConstants, intToBytes32(this.pConstants));
 961        module.addData(this.ppSignals, intToBytes32(this.pSignals));
 962        module.addData(this.ppInputSignalsToTrigger, intToBytes32(this.pInputSignalsToTrigger));
 963        module.addData(this.ppSignalsAssigned, intToBytes32(this.pSignalsAssigned));
 964
 965    }
 966
 967    _buildErrors(module) {
 968        for (let e in errs) {
 969            errs[e].pointer = module.allocString(errs[e].str);
 970        }
 971    }
 972
 973
 974    build(outType) {
 975        let module;
 976        if (outType == "wasm") {
 977            module=new ModuleBuilder();
 978        } else if (outType == "wat") {
 979            module=new ModuleBuilderWat();
 980        } else {
 981            assert(false);
 982        }
 983        this.module = module;
 984
 985        // First of all reseve the space for the header so this has a fixed position starting at 8.
 986        this._buildHeader(module);
 987        this._buildErrors(module);
 988        buildRuntime(module, this);
 989        this._buildSizes(module);
 990        this._buildConstants(module);
 991        this._buildHashMaps(module);
 992        this._buildComponentEntriesTables(module);
 993        this._buildFunctions(module);
 994        this._buildComponents(module);
 995        this._buildMapIsInput(module);
 996        this._buildWit2Sig(module);
 997        this._buildCircuitVar(module);
 998
 999        module.setMemory(2000);
1000        if (outType == "wasm") {
1001            return streamFromArrayBin(module.build());
1002        } else if (outType == "wat") {
1003            return streamFromArrayTxt(module.build());
1004        } else {
1005            assert(false);
1006        }
1007    }
1008}
1009
1010
1011module.exports = BuilderWasm;