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