arnaucube
arnaucube/circomPublic Self-hosted repository
circom/src/gencode.js
src/gencode.jsRaw
   1const utils = require("./utils");
   2const assert = require("assert");
   3const iterateAST = require("./iterateast");
   4const Scalar = require("ffjavascript").Scalar;
   5
   6module.exports.gen =  gen;
   7module.exports.newRef = newRef;
   8module.exports.createRefs = createRefs;
   9
  10function newRef(ctx, type, name, value, sizes) {
  11    let label;
  12    if (!name) {
  13        label = ctx.getUniqueName();
  14    } else {
  15        label = ctx.getUniqueName(name);
  16    }
  17    if (Array.isArray(sizes)) {
  18        // sizes = sizes;
  19    } else if (utils.isDefined(value)) {
  20        sizes = utils.accSizes(utils.extractSizes(value));
  21    } else {
  22        sizes = [1, 0];
  23    }
  24
  25    const refId = ctx.refs.length;
  26    const entry = {
  27        type: type,
  28        used: false,
  29        sizes: sizes,
  30        label: label
  31    };
  32    if (utils.isDefined(value)) {
  33        entry.value = utils.flatArray(value);
  34    }
  35
  36    ctx.refs.push(entry);
  37    return refId;
  38}
  39
  40function instantiateRef(ctx, refId, initValue) {
  41    const v = ctx.refs[refId];
  42    if (v.used) return;
  43    if (!v.sizes) v.sizes = [1,0];
  44
  45    if (v.type=="BIGINT") {
  46        ctx.fnBuilder.defineFrElements(v.label, v.sizes[0]);
  47    } else if (v.type=="INT") {
  48        ctx.fnBuilder.defineIntElement(v.label);
  49    } else if (v.type=="SIZES") {
  50        ctx.fnBuilder.defineSizesElement(v.label);
  51    }
  52    v.used = true;
  53    if (utils.isDefined(initValue)) {
  54        if (v.type == "BIGINT") {
  55            for (let i=0; i<initValue.length; i++) {
  56                if (utils.isDefined(initValue[i])) {
  57                    const idConstant = ctx.addConstant(initValue[i]);
  58                    ctx.fnBuilder.initializeFrElement(v.label, i, idConstant);
  59                }
  60            }
  61        }
  62    }
  63}
  64
  65function createRefs(ctx, ast) {
  66    const scopeLabels = [];
  67    iterateAST(ast, (ast, level) => {
  68        while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
  69            ctx.scopes.pop();
  70            scopeLabels.pop();
  71        }
  72        if (ast.type == "DECLARE") {
  73            if (ctx.scopes[ctx.scopes.length-1][ast.name.name]) return ctx.throwError(ast, `Name already defined: ${ast.name.name}`);
  74            for (let i=ctx.scopes.length-2; i>=0; i--) {
  75                if (ctx.scopes[i][ast.name.name]) ctx.logWarning(ast, `Shadowing variable: ${ast.name.name}`);
  76            }
  77            let entry = {};
  78            if (ast.declareType == "COMPONENT") {
  79                entry.type= "COMPONENT";
  80                entry.label = ast.name.name;
  81            } else if ((ast.declareType == "SIGNALIN")||
  82                       (ast.declareType == "SIGNALOUT")||
  83                       (ast.declareType == "SIGNAL")) {
  84                entry.type = "SIGNAL";
  85                entry.label = ast.name.name;
  86            } else if (ast.declareType == "VARIABLE") {
  87                entry.type = "BIGINT";
  88                entry.used = false;
  89                entry.sizes = null;
  90                entry.label = ctx.getUniqueName(ast.name.name);
  91            } else {
  92                return ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
  93            }
  94
  95            const refId = ctx.refs.length;
  96            ctx.refs.push(entry);
  97            ast.refId = refId;
  98            ctx.scopes[ctx.scopes.length-1][ast.name.name] = refId;
  99        } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) {
 100            scopeLabels.push(level);
 101            ctx.scopes.push({});
 102        } else if (ast.type == "VARIABLE") {
 103            ast.refId = name2ref(ast.name);
 104        }
 105    });
 106
 107    function name2ref(n) {
 108        for (let i=ctx.scopes.length-1; i>=0; i--) {
 109            if (typeof ctx.scopes[i][n] !== "undefined") return ctx.scopes[i][n];
 110        }
 111        return -1;
 112    }
 113}
 114
 115function gen(ctx, ast) {
 116    if ((ast.type == "NUMBER") ) {
 117        return genNumber(ctx, ast);
 118    } else if (ast.type == "VARIABLE") {
 119        return genVariable(ctx, ast);
 120    } else if (ast.type == "PIN") {
 121        return genPin(ctx, ast);
 122    } else if (ast.type == "OP") {
 123        if (ast.op == "=") {
 124            return genAssignement(ctx, ast);
 125        } else if (ast.op == "<--") {
 126            return genAssignement(ctx, ast);
 127        } else if (ast.op == "<==") {
 128            return genSignalAssignConstraint(ctx, ast);
 129        } else if (ast.op == "===") {
 130            return genConstraint(ctx, ast);
 131        } else if (ast.op == "+=") {
 132            return genVarAddAssignement(ctx, ast);
 133        } else if (ast.op == "*=") {
 134            return genVarMulAssignement(ctx, ast);
 135        } else if (ast.op == "+") {
 136            return genOp(ctx, ast, "add", 2);
 137        } else if (ast.op == "-") {
 138            return genOp(ctx, ast, "sub", 2);
 139        } else if (ast.op == "UMINUS") {
 140            return genOp(ctx, ast, "neg", 1);
 141        } else if (ast.op == "*") {
 142            return genOp(ctx, ast, "mul", 2);
 143        } else if (ast.op == "%") {
 144            return genOp(ctx, ast, "mod", 2);
 145        } else if (ast.op == "PLUSPLUSRIGHT") {
 146            return genOpOp(ctx, ast, "add", "RIGHT");
 147        } else if (ast.op == "PLUSPLUSLEFT") {
 148            return genOpOp(ctx, ast, "add", "LEFT");
 149        } else if (ast.op == "MINUSMINUSRIGHT") {
 150            return genOpOp(ctx, ast, "sub", "RIGHT");
 151        } else if (ast.op == "MINUSMINUSLEFT") {
 152            return genOpOp(ctx, ast, "sub", "LEFT");
 153        } else if (ast.op == "**") {
 154            return genOp(ctx, ast, "pow", 2);
 155        } else if (ast.op == "/") {
 156            return genOp(ctx, ast, "div", 2);
 157        } else if (ast.op == "\\") {
 158            return genOp(ctx, ast, "idiv", 2);
 159        } else if (ast.op == "&") {
 160            return genOp(ctx, ast, "band", 2);
 161        } else if (ast.op == "|") {
 162            return genOp(ctx, ast, "bor", 2);
 163        } else if (ast.op == "^") {
 164            return genOp(ctx, ast, "bxor", 2);
 165        } else if (ast.op == "~") {
 166            return genOp(ctx, ast, "bnot", 1);
 167        } else if (ast.op == "&&") {
 168            return genOp(ctx, ast, "land", 2);
 169        } else if (ast.op == "||") {
 170            return genOp(ctx, ast, "lor", 2);
 171        } else if (ast.op == "!") {
 172            return genOp(ctx, ast, "lnot", 1);
 173        } else if (ast.op == "<<") {
 174            return genOp(ctx, ast, "shl", 2);
 175        } else if (ast.op == ">>") {
 176            return genOp(ctx, ast, "shr", 2);
 177        } else if (ast.op == "<") {
 178            return genOp(ctx, ast, "lt", 2, true);
 179        } else if (ast.op == ">") {
 180            return genOp(ctx, ast, "gt", 2, true);
 181        } else if (ast.op == "<=") {
 182            return genOp(ctx, ast, "leq", 2, true);
 183        } else if (ast.op == ">=") {
 184            return genOp(ctx, ast, "geq", 2, true);
 185        } else if (ast.op == "==") {
 186            return genOp(ctx, ast, "eq", 2, true);
 187        } else if (ast.op == "!=") {
 188            return genOp(ctx, ast, "neq", 2, true);
 189        } else if (ast.op == "?") {
 190            return genTerCon(ctx, ast);
 191        } else {
 192            ctx.throwError(ast, "Invalid operation: " + ast.op);
 193        }
 194    } else if (ast.type == "DECLARE") {
 195        if (ast.declareType == "COMPONENT") {
 196            return genDeclareComponent(ctx, ast);
 197        } else if ((ast.declareType == "SIGNALIN")||
 198                   (ast.declareType == "SIGNALOUT")||
 199                   (ast.declareType == "SIGNAL")) {
 200            return genDeclareSignal(ctx, ast);
 201        } else if (ast.declareType == "VARIABLE") {
 202            return genDeclareVariable(ctx, ast);
 203        } else {
 204            ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
 205        }
 206    } else if (ast.type == "FUNCTIONCALL") {
 207        return genFunctionCall(ctx, ast);
 208    } else if (ast.type == "BLOCK") {
 209        return genBlock(ctx, ast);
 210    } else if (ast.type == "COMPUTE") {
 211        return gen(ctx, ast.body);
 212    } else if (ast.type == "FOR") {
 213        return genLoop(ctx, ast);
 214    } else if (ast.type == "WHILE") {
 215        return genLoop(ctx, ast);
 216    } else if (ast.type == "IF") {
 217        return genIf(ctx, ast);
 218    } else if (ast.type == "RETURN") {
 219        return genReturn(ctx, ast);
 220    } else if (ast.type == "INCLUDE") {
 221        return genInclude(ctx, ast);
 222    } else if (ast.type == "ARRAY") {
 223        return genArray(ctx, ast);
 224    } else {
 225        ctx.throwError(ast, "GEN -> Invalid AST node type: " + ast.type);
 226    }
 227}
 228
 229function genBlock(ctx, ast) {
 230    ctx.scopes.push({});
 231    let res = null;
 232    for (let i=0; i<ast.statements.length; i++) {
 233        if (["BLOCK", "COMPUTE", "FOR", "WHILE", "IF"].indexOf(ast.statements[i].type)<0) {
 234            genSrcComment(ctx, ast.statements[i]);
 235        }
 236        res = gen(ctx, ast.statements[i]);
 237        if (ctx.error) return;
 238    }
 239    ctx.scopes.pop();
 240    return res;
 241}
 242
 243function getSource(ctx, ast) {
 244    let codes = [];
 245    const fl= ast.first_line-1;
 246    const ll = ast.last_line-1;
 247    const fc = ast.first_column;
 248    const lc = ast.last_column;
 249    for (let i=fl; i<=ll; i++) {
 250        const p1 = i==fl ? fc : 0;
 251        const p2 = i==ll ? lc : -1;
 252        codes.push(ctx.includedFiles[ctx.fileName][i].slice(p1, p2>=0 ? p2 : undefined));
 253    }
 254    return codes.join("\n");
 255}
 256
 257function genSrcComment(ctx, ast) {
 258    const code = getSource(ctx, ast);
 259    ctx.codeBuilder.addComment(code);
 260}
 261
 262function genLoopSrcComment(ctx, ast) {
 263    if (ast.type == "FOR") {
 264        const init = getSource(ctx, ast.init);
 265        const condition = getSource(ctx, ast.condition);
 266        const step = getSource(ctx, ast.step);
 267        ctx.codeBuilder.addComment(`for (${init};${condition};${step})`);
 268    } else if (ast.type == "WHILE") {
 269        const condition = getSource(ctx, ast.condition);
 270        ctx.codeBuilder.addComment(`while (${condition})`);
 271    } else {
 272        assert(false, "Invalid loop type: "+ ast.type);
 273    }
 274}
 275
 276function genIfSrcComment(ctx, ast) {
 277    const condition = getSource(ctx, ast.condition);
 278    ctx.codeBuilder.addComment(`if (${condition})`);
 279}
 280
 281
 282function genDeclareComponent(ctx, ast) {
 283    return ast.refId;
 284}
 285
 286function genDeclareSignal(ctx, ast) {
 287    return ast.refId;
 288}
 289
 290function genDeclareVariable(ctx, ast) {
 291
 292
 293    const v = ctx.refs[ast.refId];
 294
 295    let sizes;
 296    if (ast.name.selectors.length>0) {
 297        sizes=[];
 298        for (let i=0; i< ast.name.selectors.length; i++) {
 299            const sizeRef = gen(ctx, ast.name.selectors[i]);
 300            const size = ctx.refs[sizeRef];
 301            if (size.sizes[0] != 1) return ctx.throwError(ast, "A selector cannot be an array");
 302            if (size.used) return ctx.throwError(ast, "Variable size variables not allowed");
 303            sizes.push(Scalar.toNumber(size.value[0]));
 304        }
 305        sizes = utils.accSizes(sizes);
 306    } else {
 307        sizes = [1,0];
 308    }
 309
 310    if ((!v.sizes)&&(sizes)) {
 311        v.sizes = sizes;
 312        v.value = new Array(sizes[0]);
 313    }
 314
 315    if (v.sizes) {
 316        if (!utils.sameSizes(v.sizes, sizes)) return ctx.throwError(ast, "Redefined a var with different sized");
 317    }
 318
 319    return ast.refId;
 320}
 321
 322function genNumber(ctx, ast) {
 323    return newRef(ctx, "BIGINT", "_num", ctx.F.e(ast.value));
 324}
 325
 326
 327
 328function genGetOffset(ctx, refOffset, vSizes, sels) {
 329
 330    let offsets = [];
 331    let offset;
 332
 333    let isConstant = true;
 334    let constant = 0;
 335    let isRef = false;
 336
 337    if (refOffset) {
 338        offset = ctx.refs[refOffset];
 339        if (offset.used) {
 340            offsets.push([["RI", offset.label], ["V", 1]]);
 341            isRef = true;
 342            isConstant = false;
 343        } else {
 344            offsets.push([["V", parseInt(offset.value)], ["V", 1]]);
 345            constant =  parseInt(offset.value);
 346        }
 347    }
 348
 349    if ((sels)&&(sels.length>0)) {
 350
 351        let iSizes;
 352        if (Array.isArray(vSizes)) {
 353            iSizes = {
 354                used: false,
 355                sizes: vSizes
 356            };
 357        } else {
 358            iSizes = ctx.refs[vSizes];
 359        }
 360
 361        for (let i=0; i<sels.length; i++) {
 362            let idx, sze;
 363            const idxRef = gen(ctx, sels[i]);
 364            const iIdx = ctx.refs[idxRef];
 365            if (iIdx.used) {
 366                idx = ["R", iIdx.label];
 367            } else {
 368                idx = ["V", parseInt(iIdx.value[0])];
 369            }
 370            if (iSizes.used) {
 371                sze = ["RS", iSizes.label, i+1 ];
 372            } else {
 373                sze = ["V", iSizes.sizes[i+1] ];
 374            }
 375            offsets.push([idx, sze]);
 376            if ((idx[0] == "V")&&(sze[0] == "V")&&(isConstant)) {
 377                constant += idx[1]*sze[1];
 378                if (idx[1]*sze[1] > 0) isRef = false;
 379            } else {
 380                isConstant = false;
 381                isRef = false;
 382            }
 383        }
 384    }
 385
 386    if (isConstant) {
 387        const o = newRef(ctx, "INT", "_offset", constant);
 388        return o;
 389    }
 390    if (isRef) {
 391        return refOffset;
 392    }
 393    const resRef = newRef(ctx, "INT", "_offset");
 394    instantiateRef(ctx, resRef);
 395
 396    const res = ctx.refs[resRef];
 397
 398    ctx.codeBuilder.calcOffset(res.label, offsets);
 399    return resRef;
 400}
 401
 402function genVariable(ctx, ast) {
 403    const v = ctx.refs[ast.refId];
 404
 405    const l = ast.selectors ? ast.selectors.length : 0;
 406
 407    if (v.type == "SIGNAL") {
 408        let vOffset;
 409        if (l>0) {
 410            const vsOffset = genGetSignalOffset(ctx, -1, ast.name);
 411            const vsSizes = genGetSignalSizes(ctx, -1, ast.name);
 412            vOffset = genGetOffset(ctx, vsOffset, vsSizes, ast.selectors );
 413        } else {
 414            vOffset = genGetSignalOffset(ctx, -1, ast.name);
 415        }
 416
 417        const resRef = newRef(ctx, "BIGINT", "_sigValue");
 418        const res = ctx.refs[resRef];
 419        instantiateRef(ctx, resRef);
 420        ctx.codeBuilder.getSignal(res.label, ["CC"], toRefA_Int1(ctx, ast, vOffset));
 421        return resRef;
 422
 423    } else if (v.type == "BIGINT") {
 424        const refOffset = genGetOffset(ctx, 0, v.sizes, ast.selectors );
 425        const offset = ctx.refs[refOffset];
 426        let ot;
 427        if (offset.type == "BIGINT") {
 428            ot = "R";
 429        } else if (offset.type == "INT") {
 430            ot= "RI";
 431        } else {
 432            assert(false);
 433        }
 434        if (v.used) {
 435            if (offset.used) {
 436                const refRes = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l));
 437                const res = ctx.refs[refRes];
 438                res.used = true;
 439                ctx.fnBuilder.definePFrElement(res.label);
 440                ctx.codeBuilder.assign(res.label, ["R", v.label], [ot, offset.label]);
 441                return refRes;
 442            } else if ((offset.value[0]>0)||(l>0)) {
 443                const refRes = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l));
 444                const res = ctx.refs[refRes];
 445                res.used = true;
 446                ctx.fnBuilder.definePFrElement(res.label);
 447                ctx.codeBuilder.assign(res.label, ["R", v.label], ["V", offset.value[0]]);
 448                return refRes;
 449            } else {
 450                return ast.refId;
 451            }
 452        } else {
 453            if (offset.used) {
 454                instantiateRef(ctx, ast.refId, v.value);
 455                const resRef = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l));
 456                const res = ctx.refs[resRef];
 457                res.used = true;
 458                ctx.fnBuilder.definePFrElement(res.label);
 459                ctx.codeBuilder.assign(res.label, ["R", v.label], [ot, offset.label]);
 460                return resRef;
 461            } else {
 462                // return newSubRef(ctx, ast.name, ast.selectors);
 463                return newRef(ctx, "BIGINT", "_v", v.value.slice(offset.value[0], offset.value[0] + v.sizes[l]),v.sizes.slice(l));
 464            }
 465        }
 466    }
 467}
 468
 469
 470function genPin(ctx, ast) {
 471    let vcIdx;
 472    if (ast.component.selectors.length>0) {
 473        const vcOffset = genGetSubComponentOffset(ctx, -1, ast.component.name);
 474        const vcSizes = genGetSubComponentSizes(ctx, -1, ast.component.name);
 475        vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.component.selectors );
 476    } else {
 477        vcIdx = genGetSubComponentOffset(ctx, -1, ast.component.name);
 478    }
 479
 480    let vsIdx;
 481    if (ast.pin.selectors.length>0) {
 482        const vsOffset = genGetSignalOffset(ctx, vcIdx, ast.pin.name);
 483        const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.pin.name);
 484        vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.pin.selectors );
 485    } else {
 486        vsIdx = genGetSignalOffset(ctx, vcIdx, ast.pin.name);
 487    }
 488
 489    const resRef = newRef(ctx, "BIGINT", "_sigValue");
 490    const res = ctx.refs[resRef];
 491    instantiateRef(ctx, resRef);
 492    ctx.codeBuilder.getSignal(
 493        res.label,
 494        toRefA_Int1(ctx, ast.component, vcIdx),
 495        toRefA_Int1(ctx, ast.pin, vsIdx)
 496    );
 497    return resRef;
 498}
 499
 500function genGetSubComponentOffset(ctx, cIdxRef, label) {
 501    const refOffset = newRef(ctx, "INT", "_compIdx");
 502    const offset = ctx.refs[refOffset];
 503    instantiateRef(ctx, refOffset);
 504
 505    const h = utils.fnvHash(label);
 506    let c;
 507    if (cIdxRef>=0) {
 508        const cIdx = ctx.refs[cIdxRef];
 509        if (cIdx.used) {
 510            if (cIdx.type == "BIGINT") {
 511                c = ["R", cIdx.label];
 512            } else if (cIdx.type == "INT") {
 513                c = ["RI", cIdx.label];
 514            } else {
 515                assert(false);
 516            }
 517        } else {
 518            c = ["V", cIdx.value[0]];
 519        }
 520    } else {
 521        c = ["CC"];
 522    }
 523    ctx.codeBuilder.getSubComponentOffset(offset.label, c, h, label);
 524    return refOffset;
 525}
 526
 527function genGetSubComponentSizes(ctx, cIdxRef, label) {
 528    const sizesRef = newRef(ctx, "SIZES", "_compSizes");
 529    const sizes = ctx.refs[sizesRef];
 530    instantiateRef(ctx, sizesRef);
 531
 532    const h = utils.fnvHash(label);
 533    let c;
 534    if (cIdxRef>=0) {
 535        const cIdx = ctx.refs[cIdxRef];
 536        if (cIdx.used) {
 537            if (cIdx.type == "BIGINT") {
 538                c = ["R", cIdx.label];
 539            } else if (cIdx.type == "INT") {
 540                c = ["RI", cIdx.label];
 541            } else {
 542                assert(false);
 543            }
 544        } else {
 545            c = ["V", cIdx.value[0]];
 546        }
 547    } else {
 548        c = ["CC"];
 549    }
 550    ctx.codeBuilder.getSubComponentSizes(sizes.label, c, h, label);
 551    return sizesRef;
 552}
 553
 554function genGetSignalOffset(ctx, cIdxRef, label) {
 555
 556    let constCIdx = null;
 557    if (cIdxRef>=0) {
 558        const cIdx = ctx.refs[cIdxRef];
 559        if (!cIdx.used) {
 560            constCIdx = cIdx.value.toString()+"_"+label;
 561        }
 562    } else {
 563        constCIdx = "_cIdx_"+label;
 564    }
 565    if (constCIdx && ctx.getSignalOffsetCache[constCIdx]) return ctx.getSignalOffsetCache[constCIdx];
 566
 567    const refOffset = newRef(ctx, "INT", "_" + label + "_sigIdx_");
 568    const offset = ctx.refs[refOffset];
 569    instantiateRef(ctx, refOffset);
 570
 571    const h = utils.fnvHash(label);
 572    let c;
 573    if (cIdxRef>=0) {
 574        const cIdx = ctx.refs[cIdxRef];
 575        if (cIdx.used) {
 576            if (cIdx.type == "BIGINT") {
 577                c = ["R", cIdx.label];
 578            } else if (cIdx.type == "INT") {
 579                c = ["RI", cIdx.label];
 580            } else {
 581                assert(false);
 582            }
 583        } else {
 584            c = ["V", cIdx.value[0]];
 585        }
 586    } else {
 587        c = ["CC"];
 588    }
 589    if (constCIdx) {
 590        ctx.fnBuilder.initializeSignalOffset(offset.label, c, h, label);
 591        ctx.getSignalOffsetCache[constCIdx] = refOffset;
 592    } else {
 593        ctx.codeBuilder.getSignalOffset(offset.label, c, h, label);
 594    }
 595    return refOffset;
 596}
 597
 598function genGetSignalSizes(ctx, cIdxRef, label) {
 599    let constCIdx = null;
 600    if (cIdxRef>=0) {
 601        const cIdx = ctx.refs[cIdxRef];
 602        if (utils.isDefined(cIdx.value)) {
 603            constCIdx = cIdx.value.toString()+"_"+label;
 604        }
 605    } else {
 606        constCIdx = "_cIdx_"+label;
 607    }
 608    if (constCIdx && ctx.getSignalSizesCache[constCIdx]) return ctx.getSignalSizesCache[constCIdx];
 609
 610
 611    const refSizes = newRef(ctx, "SIZES", "_sigSizes_"+label);
 612    const sizes = ctx.refs[refSizes];
 613    instantiateRef(ctx, refSizes);
 614
 615    let c;
 616    if (cIdxRef>=0) {
 617        const cIdx = ctx.refs[cIdxRef];
 618        if (cIdx.used) {
 619            if (cIdx.type == "BIGINT") {
 620                c = ["R", cIdx.label];
 621            } else if (cIdx.type == "INT") {
 622                c = ["RI", cIdx.label];
 623            } else {
 624                assert(false);
 625            }
 626        } else {
 627            c = ["V", cIdx.value[0]];
 628        }
 629    } else {
 630        c = ["CC"];
 631    }
 632    const h = utils.fnvHash(label);
 633    if (constCIdx) {
 634        ctx.fnBuilder.initializeSignalSizes(sizes.label, c, h, label);
 635        ctx.getSignalSizesCache[constCIdx] = refSizes;
 636    } else {
 637        ctx.codeBuilder.getSignalSizes(sizes.label, c, h, label);
 638    }
 639    return refSizes;
 640}
 641
 642function genPinAssignement(ctx, ast) {
 643    let vcIdx;
 644    if (ast.values[0].component.selectors.length>0) {
 645        const vcOffset = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name);
 646        const vcSizes = genGetSubComponentSizes(ctx, -1, ast.values[0].component.name);
 647        vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors );
 648    } else {
 649        vcIdx = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name);
 650    }
 651
 652    let vsIdx;
 653    if (ast.values[0].pin.selectors.length>0) {
 654        const vsOffset = genGetSignalOffset(ctx, vcIdx, ast.values[0].pin.name);
 655        const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name);
 656        vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors );
 657    } else {
 658        vsIdx = genGetSignalOffset(ctx, vcIdx, ast.values[0].pin.name);
 659    }
 660
 661    const vVal = gen(ctx, ast.values[1]);
 662
 663    ctx.codeBuilder.setSignal(
 664        toRefA_Int1(ctx, ast.values[0].component, vcIdx),
 665        toRefA_Int1(ctx, ast.values[0].pin, vsIdx),
 666        toRefA_Fr1(ctx, ast.values[1], vVal)
 667    );
 668
 669    return vVal;
 670}
 671
 672function genSignalAssignmen(ctx, ast, lName, sels, rName) {
 673    let vsIdx;
 674    const signal = ctx.refs[lName];
 675    if (sels.length>0) {
 676        const vsOffset = genGetSignalOffset(ctx, -1, signal.label);
 677        const vsSizes = genGetSignalSizes(ctx, -1, signal.label);
 678        vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels );
 679    } else {
 680        vsIdx = genGetSignalOffset(ctx, -1, signal.label);
 681    }
 682
 683    ctx.codeBuilder.setSignal(
 684        ["CC"],
 685        toRefA_Int1(ctx, ast.values[0], vsIdx),
 686        toRefA_Fr1(ctx, ast.values[1], rName)
 687    );
 688}
 689
 690function genVarAssignment(ctx, ast, lRef, sels, rRef) {
 691
 692    const left = ctx.refs[lRef];
 693    const right = ctx.refs[rRef];
 694    if (!utils.isDefined(left.sizes)) {
 695        left.sizes = right.sizes;
 696        left.value = new Array(left.sizes[0]);
 697    }
 698    if (!utils.sameSizes(left.sizes.slice(sels.length), right.sizes)) return ctx.throwError(ast, "Sizes do not match");
 699
 700    const oRef = genGetOffset(ctx, 0, left.sizes, sels);
 701    const offset = ctx.refs[oRef];
 702
 703    let instantiated=false;
 704    if (left.used) {
 705        instantiateRef(ctx, rRef, right.value);
 706        instantiated=true;
 707    } else if (right.used) {
 708        if (sels.length == 0) {
 709            instantiateRef(ctx,lRef);
 710        } else {
 711            instantiateRef(ctx,lRef, left.value);
 712        }
 713        instantiated=true;
 714    } else if (offset.used) {
 715        instantiateRef(ctx, rRef, right.value);
 716        if (sels.length == 0) {
 717            instantiateRef(ctx,lRef);
 718        } else {
 719            instantiateRef(ctx,lRef, left.value);
 720        }
 721        instantiated=true;
 722    }
 723
 724    if (instantiated) {
 725        if (offset.used) {
 726            let ot;
 727            if (offset.type == "BIGINT") {
 728                ot = "R";
 729            } else if (offset.type == "INT") {
 730                ot= "RI";
 731            } else {
 732                assert(false);
 733            }
 734            ctx.codeBuilder.copyN(left.label, [ot, offset.label], ["R", right.label], right.sizes[0]);
 735        } else {
 736            ctx.codeBuilder.copyN(left.label, ["V", offset.value[0]], ["R", right.label], right.sizes[0]);
 737        }
 738    } else {
 739        if (offset.value[0]>0) {
 740            for (let i=0; i<right.sizes[0]; i++) left.value[offset.value[0] + i] = right.value[i];
 741        } else {
 742            for (let i=0; i<right.sizes[0]; i++) left.value[i] = right.value[i];
 743        }
 744    }
 745}
 746
 747function genAssignement(ctx, ast) {
 748
 749    let lRef;
 750    let sels;
 751
 752    if (ctx.error) return;
 753
 754    if (ast.values[0].type == "PIN") return genPinAssignement(ctx, ast);
 755
 756    if (ast.values[0].type == "DECLARE") {
 757        lRef = gen(ctx, ast.values[0]);
 758        if (ctx.error) return;
 759        sels = [];
 760    } else {
 761        lRef = ast.values[0].refId;
 762        sels = ast.values[0].selectors;
 763    }
 764
 765
 766    const left = ctx.refs[lRef];
 767    if (!left) return ctx.throwError(ast, "Variable does not exists: "+ast.values[0].name);
 768
 769    // Component instantiation is already done.
 770    if (left.type == "COMPONENT") return;
 771
 772    const rRef = gen(ctx, ast.values[1]);
 773    if  (ctx.error) return;
 774
 775    if (left.type == "SIGNAL") return genSignalAssignmen(ctx, ast, lRef, sels, rRef);
 776
 777    if (left.type == "BIGINT") return genVarAssignment(ctx, ast, lRef, sels, rRef);
 778
 779    return ctx.throwError(ast, "Assigning to invalid");
 780}
 781
 782function toRefA_Fr1(ctx, ast, aRef) {
 783    const a = ctx.refs[aRef];
 784    if (a.sizes[0] != 1) return ctx.throwError(ast, "Expected only one element");
 785    if (a.used) {
 786        if (a.type == "BIGINT") {
 787            return ["R", a.label];
 788        } else {
 789            assert(false);
 790        }
 791    } else {
 792        return ["C", ctx.addConstant(a.value[0])];
 793    }
 794}
 795
 796function toRefA_Int1(ctx, ast, aRef) {
 797    if (aRef <0) return ["CC"];
 798    const a = ctx.refs[aRef];
 799    if (a.sizes[0] != 1) return ctx.throwError(ast, "Expected only one element");
 800    if (a.used) {
 801        if (a.type == "INT") {
 802            return ["RI", a.label];
 803        } else if (a.type == "BIGINT") {
 804            return ["R", a.label];
 805        } else {
 806            assert(false);
 807        }
 808    } else {
 809        return ["V", a.value[0]];
 810    }
 811}
 812
 813
 814function genConstraint(ctx, ast) {
 815    const aRef = gen(ctx, ast.values[0]);
 816    if (ctx.error) return;
 817    const bRef = gen(ctx, ast.values[1]);
 818    if (ctx.error) return;
 819    const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column;
 820
 821    ctx.codeBuilder.checkConstraint(toRefA_Fr1(ctx, ast.values[0], aRef), toRefA_Fr1(ctx, ast.values[1], bRef), strErr);
 822}
 823
 824
 825function genArray(ctx, ast) {
 826    let subSizes;
 827    let instantiate = false;
 828    if (ast.values.length == 0) return ctx.throwError(ast, "Arrays with zero elements not allowed");
 829    const value = [];
 830    const refs = [];
 831    for (let i=0; i<ast.values.length; i++) {
 832        const eRef = gen(ctx, ast.values[i]);
 833        const e = ctx.refs[eRef];
 834
 835        if (i==0) {
 836            subSizes = e.sizes;
 837        } else {
 838            if (!utils.sameSizes(subSizes, e.sizes)) return ctx.throwError(ast, "Heteroeneus array not allowed");
 839        }
 840        if (e.used) {
 841            instantiate = true;
 842        }
 843        if (!instantiate) {
 844            value.push(...e.value);
 845        }
 846        refs.push(eRef);
 847    }
 848
 849    const newSize = [subSizes[0]*ast.values.length , ...subSizes];
 850
 851    if (instantiate) {
 852        const rRef = newRef(ctx, "BIGINT", "_arr", null, newSize);
 853        instantiateRef(ctx, rRef);
 854        for (let i=0; i<ast.values.length; i++) {
 855            const v = ctx.refs[refs[i]];
 856            const r = ctx.refs[rRef];
 857            ctx.codeBuilder.copyN(r.label, ["V", i*subSizes[0]], ["R", v.label], subSizes[0]);
 858        }
 859        return rRef;
 860    } else {
 861        const rRef = newRef(ctx, "BIGINT", "_arr", value, newSize);
 862        return rRef;
 863    }
 864
 865}
 866
 867
 868function genFunctionCall(ctx, ast) {
 869    if (ast.name == "log") {
 870        const vRef = gen(ctx, ast.params[0]);
 871        ctx.codeBuilder.log(toRefA_Fr1(ctx, ast.params[0], vRef));
 872        return vRef;
 873    }
 874    if (ast.name == "assert") {
 875        const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column;
 876        const vRef = gen(ctx, ast.params[0]);
 877        ctx.codeBuilder.checkAssert(toRefA_Fr1(ctx, ast.params[0], vRef), strErr);
 878        return vRef;
 879    }
 880    const params = [];
 881    for (let i=0; i<ast.params.length; i++) {
 882        const pRef = gen(ctx, ast.params[i]);
 883        params.push(ctx.refs[pRef]);
 884    }
 885
 886    const fn = ctx.buildFunction(ast.name, params);
 887    if (ctx.error) return;
 888
 889    if (fn.type == "VARVAL_CONSTSIZE") {
 890        const resRef = newRef(ctx, "BIGINT", `_ret${ast.name}`, null, fn.returnSizes);
 891        const res = ctx.refs[resRef];
 892        instantiateRef(ctx, resRef);
 893
 894        const paramLabels = [];
 895        for (let i=0; i<params.length; i++) {
 896            if (params[i].used) paramLabels.push(params[i].label);
 897        }
 898
 899        ctx.codeBuilder.fnCall(fn.fnName, res.label, paramLabels);
 900
 901        return resRef;
 902    } else {
 903        const res = newRef(ctx, "BIGINT", "_retVal", fn.returnValue, fn.returnSizes);
 904        return res;
 905    }
 906}
 907
 908function enterConditionalCode(ctx, ast) {
 909    if (!ctx.conditionalCode) {
 910        iterateAST(ast, (ast) => {
 911            if (ast.type == "OP") {
 912                if (["=", "+=", "*=", "PLUSPLUSLEFT", "PLUSPLUSRIGHT", "MINUSMINUSLEFT", "MINUSMINUSRIGHT"].indexOf(ast.op) >= 0) {
 913                    let refId;
 914                    refId = ast.values[0].refId;
 915                    const ref = ctx.refs[refId];
 916                    instantiateRef(ctx, refId, ref.value);
 917                }
 918            }
 919        });
 920        ctx.conditionalCode = 1;
 921    } else {
 922        ctx.conditionalCode ++;
 923    }
 924}
 925
 926function leaveConditionalCode(ctx) {
 927    assert(ctx.conditionalCode, "Leaving conditional code too many times");
 928    ctx.conditionalCode --;
 929    if (!ctx.conditionalCode) {
 930        delete ctx.conditionalCode;
 931    }
 932}
 933
 934function genLoop(ctx, ast) {
 935    genLoopSrcComment(ctx, ast);
 936    let inLoop = false;
 937
 938    if (ast.init) {
 939        gen(ctx, ast.init);
 940        if (ctx.error) return;
 941    }
 942
 943    let end=false;
 944
 945    let loopCondRef;
 946    let loopCond;
 947    let bodyCode;
 948
 949
 950    const condRef = gen(ctx, ast.condition);
 951    if (ctx.error) return;
 952
 953    const cond = ctx.refs[condRef];
 954    if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
 955
 956    if (cond.used) {
 957        inLoop = true;
 958        enterConditionalCode(ctx, ast);
 959
 960        loopCondRef = newRef(ctx, "BIGINT", "_loopCond");
 961        loopCond = ctx.refs[loopCondRef];
 962        loopCond.used = true;
 963        ctx.fnBuilder.definePFrElement(loopCond.label);
 964        ctx.codeBuilder.assign(loopCond.label, ["R", cond.label], ["V", 0]);
 965    } else {
 966        if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
 967        if (ctx.F.isZero(cond.value[0])) end=true;
 968    }
 969
 970
 971    while (!end) {
 972
 973        const oldCodeBuilder = ctx.codeBuilder;
 974        ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
 975
 976        if (ast.body.type != "BLOCK") genSrcComment(ctx, ast.body);
 977        gen(ctx, ast.body);
 978        if (ctx.error) return;
 979
 980        if (ast.step) {
 981            gen(ctx, ast.step);
 982            if (ctx.error) return;
 983        }
 984
 985        const condRef2 = gen(ctx, ast.condition);
 986        if (ctx.error) return;
 987
 988        const cond2 = ctx.refs[condRef2];
 989
 990        if ((!cond2.used) &&(ctx.codeBuilder.hasCode())) {
 991            instantiateRef(ctx, condRef2, cond2.value);
 992        }
 993
 994        if (!inLoop) {
 995            if (cond2.used) {
 996                oldCodeBuilder.concat(ctx.codeBuilder);
 997                ctx.codeBuilder = oldCodeBuilder;
 998                inLoop = true;
 999                enterConditionalCode(ctx, ast);
1000
1001                loopCondRef = newRef(ctx, "BIGINT", "_loopCond");
1002                loopCond = ctx.refs[loopCondRef];
1003                loopCond.used = true;
1004                ctx.fnBuilder.definePFrElement(loopCond.label);
1005                ctx.codeBuilder.assign(loopCond.label, ["R", cond2.label], ["V", 0]);
1006
1007            } else {
1008                oldCodeBuilder.concat(ctx.codeBuilder);
1009                ctx.codeBuilder = oldCodeBuilder;
1010                if (ctx.F.isZero(cond2.value[0])) end=true;
1011            }
1012        } else {
1013            ctx.codeBuilder.assign(loopCond.label, ["R", cond2.label], ["V", 0]);
1014            bodyCode = ctx.codeBuilder;
1015            ctx.codeBuilder = oldCodeBuilder;
1016            end=true;
1017        }
1018    }
1019    if (inLoop) {
1020        ctx.codeBuilder.addLoop(loopCond.label, bodyCode);
1021        leaveConditionalCode(ctx);
1022    }
1023    ctx.scopes.pop();
1024}
1025
1026function genIf(ctx, ast) {
1027    genIfSrcComment(ctx, ast);
1028    const condRef = gen(ctx, ast.condition);
1029    if (ctx.error) return;
1030    const cond = ctx.refs[condRef];
1031    if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
1032
1033    if (cond.used) {
1034        let thenCode, elseCode;
1035        enterConditionalCode(ctx, ast);
1036
1037        const oldCodeBuilder = ctx.codeBuilder;
1038        ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
1039
1040        gen(ctx, ast.then);
1041        if (ctx.error) return;
1042
1043        thenCode = ctx.codeBuilder;
1044
1045        if (ast.else) {
1046            ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
1047            gen(ctx, ast.else);
1048            if (ctx.error) return;
1049            elseCode = ctx.codeBuilder;
1050        }
1051
1052        ctx.codeBuilder = oldCodeBuilder;
1053        ctx.codeBuilder.addIf(cond.label, thenCode, elseCode);
1054
1055        leaveConditionalCode(ctx);
1056
1057    } else {
1058        if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
1059        if (!ctx.F.isZero(cond.value[0])) {
1060            gen(ctx, ast.then);
1061        } else {
1062            if (ast.else) {
1063                gen(ctx, ast.else);
1064            }
1065        }
1066    }
1067}
1068
1069
1070function genReturn(ctx, ast) {
1071    const vRef = gen(ctx, ast.value);
1072    const v= ctx.refs[vRef];
1073    if (ctx.returnSizes) {
1074        if (!utils.sameSizes(v.sizes, ctx.returnSizes)) return ctx.throwError(ast, "Diferent return sizes");
1075    } else {
1076        ctx.returnSizes = v.sizes;
1077    }
1078    if (ctx.conditionalCode) {
1079        instantiateRef(ctx, vRef, v.value);
1080    }
1081    if (v.used) {
1082        ctx.codeBuilder.copyNRet(["R", v.label], v.sizes[0]);
1083    } else {
1084        if (!utils.isDefined(v.value)) return ctx.throwError(ast, "Returning an unknown value");
1085        if (!utils.isDefined(ctx.returnValue))  {
1086            ctx.returnValue = v.value;
1087        }
1088    }
1089    ctx.codeBuilder.ret();
1090    return vRef;
1091}
1092
1093
1094
1095function genSignalAssignConstraint(ctx, ast) {
1096    const res = genAssignement(ctx, ast);
1097    //    genConstraint(ctx, ast);
1098    return res;
1099    //    return genVarAssignement(ctx, ast);
1100}
1101
1102function genVarAddAssignement(ctx, ast) {
1103    return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]});
1104}
1105
1106function genVarMulAssignement(ctx, ast) {
1107    return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]});
1108}
1109
1110function genOpOp(ctx, ast, op, lr) {
1111
1112    if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
1113
1114    const vRef = ast.values[0].refId;
1115
1116    const vevalRef = gen(ctx, ast.values[0]);
1117    if (ctx.error) return;
1118    const veval = ctx.refs[vevalRef];
1119
1120    if (veval.type != "BIGINT") return ctx.throwError(ast, "incrementing a non variable");
1121
1122    const resRef = newRef(ctx, "BIGINT", "_tmp");
1123    const res = ctx.refs[resRef];
1124    if (veval.used) {
1125        instantiateRef(ctx, resRef);
1126        ctx.codeBuilder.fieldOp(res.label, op, [["R", veval.label], ["C", ctx.addConstant(ctx.F.one)]]);
1127    } else {
1128        res.value = [ctx.F[op](veval.value[0], ctx.F.one)];
1129    }
1130    genVarAssignment(ctx, ast, vRef, ast.values[0].selectors, resRef);
1131    if (lr == "RIGHT") {
1132        return vevalRef;
1133    } else if (lr == "LEFT") {
1134        return resRef;
1135    }
1136}
1137
1138function genOp(ctx, ast, op, nOps, adjustBool) {
1139    const vals = [];
1140    const valRefs = [];
1141
1142    var anyUsed=false;
1143
1144    for (let i=0; i<nOps; i++) {
1145        const  ref = gen(ctx, ast.values[i]);
1146        if (ctx.error) return;
1147        let v = ctx.refs[ref];
1148        valRefs.push(ref);
1149        vals.push(v);
1150
1151        if (!utils.sameSizes(v.sizes, [1,0])) return ctx.throwError(ast, "Operation cannot be done on an array");
1152        if (  (!v.used)
1153            &&(  (!utils.isDefined(v.value))
1154               ||(!utils.isDefined(v.value[0]))))
1155            return ctx.throwError(ast, "Using a not assigned varialble: ");
1156
1157        if (v.used) anyUsed=true;
1158    }
1159
1160    let rRef;
1161    if (anyUsed) {
1162        const params = [];
1163        for (let i=0; i<nOps; i++) {
1164            params.push(toRefA_Fr1(ctx, ast.values[i], valRefs[i]));
1165        }
1166
1167        rRef = newRef(ctx, "BIGINT", "_tmp");
1168        instantiateRef(ctx, rRef);
1169        const r = ctx.refs[rRef];
1170        ctx.codeBuilder.fieldOp(r.label, op, params);
1171    } else {
1172        const params = [];
1173        for (let i=0; i<nOps; i++) {
1174            params.push(ctx.F.e(vals[i].value[0]));
1175        }
1176
1177        rRef = newRef(ctx, "BIGINT", "_tmp", adjustBool ? (ctx.F[op](...params)?ctx.F.one:ctx.F.zero)  : ctx.F[op](...params));
1178    }
1179    return rRef;
1180}
1181
1182
1183function genTerCon(ctx, ast) {
1184    const condRef = gen(ctx, ast.values[0]);
1185    if (ctx.error) return;
1186    const cond = ctx.refs[condRef];
1187    if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
1188
1189    if (cond.used) {
1190        let thenCode, elseCode;
1191        enterConditionalCode(ctx, ast);
1192
1193        const rLabel = ctx.getUniqueName("_ter");
1194
1195        ctx.fnBuilder.definePFrElement(rLabel);
1196
1197        const oldCodeBuilder = ctx.codeBuilder;
1198        ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
1199
1200        const thenRef = gen(ctx, ast.values[1]);
1201        if (ctx.error) return;
1202        const then = ctx.refs[thenRef];
1203
1204        let t;
1205        if (then.used) {
1206            t = ["R", then.label];
1207        } else {
1208            if (then.sizes[0] == 1) {
1209                t = ["C", ctx.addConstant(then.value[0])];
1210            } else {
1211                instantiateRef(ctx, thenRef, then.value);
1212                t = ["R", then.label];
1213            }
1214        }
1215
1216        ctx.codeBuilder.assign(rLabel, t, ["V", 0]);
1217
1218        thenCode = ctx.codeBuilder;
1219        ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
1220
1221        const elseRef = gen(ctx, ast.values[2]);
1222        if (ctx.error) return;
1223        const els = ctx.refs[elseRef];
1224
1225        let e;
1226        if (els.used) {
1227            e = ["R", els.label];
1228        } else {
1229            if (els.sizes[0] == 1) {
1230                e = ["C", ctx.addConstant(els.value[0])];
1231            } else {
1232                instantiateRef(ctx, elseRef, els.value);
1233                e = ["R", els.label];
1234            }
1235        }
1236
1237        ctx.codeBuilder.assign(rLabel,  e, ["V", 0]);
1238        elseCode = ctx.codeBuilder;
1239
1240        ctx.codeBuilder = oldCodeBuilder;
1241
1242        ctx.codeBuilder.addIf(cond.label, thenCode, elseCode);
1243
1244
1245        if (!utils.sameSizes(then.sizes, els.sizes)) return ctx.throwError(ast, "Ternary op must return the same sizes");
1246
1247        const refId = ctx.refs.length;
1248        ctx.refs.push({
1249            type: "BIGINT",
1250            sizes: then.sizes,
1251            used: true,
1252            label: rLabel
1253        });
1254
1255        return refId;
1256
1257    } else {
1258        if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
1259        if (!ctx.F.isZero(cond.value[0])) {
1260            return gen(ctx, ast.values[1]);
1261        } else {
1262            return gen(ctx, ast.values[2]);
1263        }
1264    }
1265}
1266
1267function genInclude(ctx, ast) {
1268    return ast.block ? gen(ctx, ast.block) : "";
1269}
1270
1271