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