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