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