diff --git a/cli.js b/cli.js index e2d8528..b00bc22 100755 --- a/cli.js +++ b/cli.js @@ -33,6 +33,7 @@ const argv = require("yargs") .usage("circom [input source circuit file] -o [output definition circuit file] -c [output c file]") .alias("o", "output") .alias("c", "csource") + .alias("w", "wasm") .alias("s", "sym") .alias("r", "r1cs") .alias("n", "newThreadTemplates") @@ -69,6 +70,7 @@ if (argv._.length == 0) { const fullFileName = path.resolve(process.cwd(), inputFile); const fileName = path.basename(fullFileName, ".circom"); const cSourceName = typeof(argv.csource) === "string" ? argv.csource : fileName + ".cpp"; +const wasmName = typeof(argv.wasm) === "string" ? argv.wasm : fileName + ".wasm"; const r1csName = typeof(argv.r1cs) === "string" ? argv.r1cs : fileName + ".r1cs"; const symName = typeof(argv.sym) === "string" ? argv.sym : fileName + ".sym"; @@ -78,6 +80,9 @@ options.verbose = argv.verbose || false; if (argv.csource) { options.cSourceWriteStream = fs.createWriteStream(cSourceName); } +if (argv.wasm) { + options.wasmWriteStream = fs.createWriteStream(wasmName); +} if (argv.r1cs) { options.r1csFileName = r1csName; } @@ -90,17 +95,26 @@ if (argv.newThreadTemplates) { compiler(fullFileName, options).then( () => { let cSourceDone = false; + let wasmDone = false; let symDone = false; if (options.cSourceWriteStream) { - options.cSourceWriteStream.end(() => { + options.cSourceWriteStream.on("finish", () => { cSourceDone = true; finishIfDone(); }); } else { cSourceDone = true; } + if (options.wasmWriteStream) { + options.wasmWriteStream.on("finish", () => { + wasmDone = true; + finishIfDone(); + }); + } else { + wasmDone = true; + } if (options.symWriteStream) { - options.symWriteStream.end(() => { + options.symWriteStream.on("finish", () => { symDone = true; finishIfDone(); }); @@ -108,7 +122,7 @@ compiler(fullFileName, options).then( () => { symDone = true; } function finishIfDone() { - if ((cSourceDone)&&(symDone)) { + if ((cSourceDone)&&(symDone)&&(wasmDone)) { setTimeout(() => { process.exit(0); }, 300); diff --git a/src/c_build.js b/src/build.js similarity index 58% rename from src/c_build.js rename to src/build.js index b1a1a42..a4edfe4 100644 --- a/src/c_build.js +++ b/src/build.js @@ -20,67 +20,63 @@ const assert = require("assert"); const bigInt = require("big-integer"); const utils = require("./utils"); -const gen = require("./c_gen").gen; -const createRefs = require("./c_gen").createRefs; -const streamFromMultiArray = require("./stream_from_multiarray"); +const gen = require("./gencode").gen; +const createRefs = require("./gencode").createRefs; -module.exports = buildC; +module.exports = build; -function buildC(ctx) { +function build(ctx) { ctx.definedFunctions = {}; ctx.functionCodes = []; ctx.buildFunction = buildFunction; - ctx.code = ""; ctx.conditionalCodeHeader = ""; ctx.codes_sizes = []; ctx.definedSizes = {}; ctx.addSizes = addSizes; - ctx.constants = []; ctx.constantsMap = {}; ctx.addConstant = addConstant; ctx.addConstant(bigInt.zero); ctx.addConstant(bigInt.one); - const entryTables = buildEntryTables(ctx); + buildHeader(ctx); + buildEntryTables(ctx); ctx.globalNames = ctx.uniqueNames; - const code = buildCode(ctx); - const functions = buildFuncFunctions(ctx); - const compnentsArray = buildComponentsArray(ctx); - - const headder = buildHeader(ctx); - const sizes = buildSizes(ctx); - const constants = buildConstants(ctx); - const mapIsInput = buildMapIsInput(ctx); - const wit2Sig = buildWit2Sig(ctx); - const circuitVar = buildCircuitVar(ctx); - - return streamFromMultiArray([ - headder , "\n" , - sizes , "\n" , - constants , "\n" , - entryTables , "\n" , - functions , "\n" , - code , "\n" , - compnentsArray , "\n" , - mapIsInput , "\n" , - wit2Sig , "\n" , - circuitVar - ]); + buildCode(ctx); + + buildComponentsArray(ctx); + + buildMapIsInput(ctx); + buildWit2Sig(ctx); + } function buildEntryTables(ctx) { const codes_hashMaps = []; const codes_componentEntries = []; - const definedHashTables = {}; + const definedHashMaps = {}; for (let i=0; i ((a>b) ? 1 : -1)); const h = utils.fnvHash(keys.join(",")); - if (definedHashTables[h]) return definedHashTables[h]; - definedHashTables[h] = {}; - definedHashTables[h].htName = ctx.getUniqueName("_ht"+ctx.components[cIdx].template); - definedHashTables[h].htMap = []; + if (definedHashMaps[h]) return definedHashMaps[h]; + definedHashMaps[h] = {}; + definedHashMaps[h].htName = ctx.getUniqueName("_ht"+ctx.components[cIdx].template); + definedHashMaps[h].htMap = []; const t = []; for (let i=0; i0 ? "," : ""; - if (t[i]) { - code += `{0x${t[i][0]}LL, ${t[i][1]}} /* ${keys[t[i][1]]} */`; - } else { - code += "{0,0}"; - } - } - code += "};\n"; + ctx.builder.addHashMap(definedHashMaps[h].htName, t); - codes_hashMaps.push(code); - - return definedHashTables[h]; + return definedHashMaps[h]; } } @@ -155,9 +140,8 @@ function buildCode(ctx) { ctx.scopes = [{}]; ctx.conditionalCode = false; - ctx.code = ""; - ctx.codeHeader = "// Header\n"; - ctx.codeFooter = "// Footer\n"; + ctx.fnBuilder = ctx.builder.newComponentFunctionBuilder(fName, instanceDef); + ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder(); ctx.uniqueNames = Object.assign({},ctx.globalNames); ctx.refs = []; ctx.fileName = ctx.templates[ctx.components[i].template].fileName; @@ -184,20 +168,10 @@ function buildCode(ctx) { gen(ctx, ctx.templates[ctx.components[i].template].block); if (ctx.error) return; - const S = - "/*\n" + - instanceDef + - "\n*/\n" + - `void ${fName}(Circom_CalcWit *ctx, int __cIdx) {\n` + - utils.ident( - ctx.codeHeader + "\n" + - ctx.code + "\n" + - "ctx->finished(__cIdx);\n" + - ctx.codeFooter - ) + - "}\n"; - - fnComponents.push(S); + ctx.fnBuilder.setBody(ctx.codeBuilder); + + ctx.builder.addFunction(ctx.fnBuilder); + fDefined[fName] = true; } ctx.components[i].fnName = fName; @@ -206,172 +180,63 @@ function buildCode(ctx) { return fnComponents; } -function buildFuncFunctions(ctx) { - if (ctx.functionCodes) { - return ["// Functions\n" , - ctx.functionCodes - ]; - } else { - return ""; - } - -} - function buildComponentsArray(ctx) { - - const ccodes = []; - ccodes.push(`Circom_Component _components[${ctx.components.length}] = {\n`); for (let i=0; i< ctx.components.length; i++) { let newThread; if (ctx.newThreadTemplates) { if (ctx.newThreadTemplates.test(ctx.components[i].template)) { - newThread = "true"; + newThread = true; } else { - newThread = "false"; + newThread = false; } } else { - newThread = "false"; + newThread = false; } - ccodes.push(i>0 ? " ," : " "); - ccodes.push(`{${ctx.components[i].htName},${ctx.components[i].etName},${ctx.components[i].fnName}, ${ctx.components[i].nInSignals}, ${newThread}}\n`); + ctx.builder.addComponent({ + hashMapName: ctx.components[i].htName, + entryTableName: ctx.components[i].etName, + functionName: ctx.components[i].fnName, + nInSignals: ctx.components[i].nInSignals, + newThread: newThread + }); } - ccodes.push("};\n"); - - return [ - "// Components\n" , - ccodes , "\n" - ]; } function buildHeader(ctx) { - return "#include \"circom.h\"\n" + - "#include \"calcwit.h\"\n" + - `#define NSignals ${ctx.signals.length}\n` + - `#define NComponents ${ctx.components.length}\n` + - `#define NInputs ${ctx.components[ ctx.getComponentIdx("main") ].nInSignals}\n`+ - `#define NOutputs ${ctx.totals[ ctx.stOUTPUT ]}\n`+ - `#define NVars ${ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + ctx.totals[ctx.stPUBINPUT] + ctx.totals[ctx.stPRVINPUT] + ctx.totals[ctx.stINTERNAL]}\n` + - `#define __P__ "${ctx.field.p.toString()}"\n` + - "\n"; -} - -function buildSizes(ctx) { - return [ - "// Sizes\n" , - ctx.codes_sizes - ]; -} - -function buildConstants(ctx) { - const n64 = Math.floor((ctx.field.p.bitLength() - 1) / 64)+1; - const R = bigInt.one.shiftLeft(n64*64); - - const lines = []; - lines.push("// Constants\n"); - lines.push(`FrElement _constants[${ctx.constants.length}] = {\n`); - for (let i=0; i0 ? "," : " ") + "{" + number2Code(ctx.constants[i]) + "}\n"); - } - lines.push("};\n"); - - return lines; - - function number2Code(n) { - if (n.lt(bigInt("80000000", 16)) ) { - return addShortMontgomeryPositive(n); - } - if (n.geq(ctx.field.p.minus(bigInt("80000000", 16))) ) { - return addShortMontgomeryNegative(n); - } - return addLongMontgomery(n); - - - function addShortMontgomeryPositive(a) { - return `${a.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`; - } - - - function addShortMontgomeryNegative(a) { - const b = a.minus(ctx.field.p); - return `${b.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`; - } - - function addLongMontgomery(a) { - return `0, 0xC0000000, { ${getLongString(toMontgomery(a))} }`; - } - - function getLongString(a) { - let r = bigInt(a); - let S = ""; - let i = 0; - while (!r.isZero()) { - if (S!= "") S = S+","; - S += "0x" + r.and(bigInt("FFFFFFFFFFFFFFFF", 16)).toString(16) + "LL"; - i++; - r = r.shiftRight(64); - } - while (i31) ? "," : " "; - line += toHex(acc); + map.push(acc); acc = 0; - if ( (i+1) % (32*64) == 0) { - arr.push(line); - line = ""; - } } } - if ((i%32) != 0) { - line += (i>31) ? "," : " "; - line += toHex(acc); + map.push(acc); } - if (line != "") { - arr.push(line); - } - - return ["// mapIsArray\n" , - `u32 _mapIsInput[${Math.floor((ctx.signals.length-1) / 32)+1}] = {\n`, - arr, "\n" , - "};\n" - ]; - function toHex(number) { - if (number < 0) number = 0xFFFFFFFF + number + 1; - let S=number.toString(16).toUpperCase(); - while (S.length<8) S = "0" + S; - return "0x"+S; - } + ctx.builder.setMapIsInput(map); } + function buildWit2Sig(ctx) { - const codes = []; const NVars = ctx.totals[ctx.stONE] + ctx.totals[ctx.stOUTPUT] + @@ -388,47 +253,12 @@ function buildWit2Sig(ctx) { arr[outIdx] = i; } } - codes.push("// Signal Table\n"); - codes.push(`int _wit2sig[${NVars}] = {\n`); - let code = ""; - for (let i=0; i0) ? ",": " "; - code += arr[i]; - if ((i>0)&&(i%64 == 0)) { - if (code != "") { - codes.push(code + "\n"); - } else { - codes.push(code); - } - code =""; - } - } - if (code != "") codes.push(code + "\n"); - codes.push("};\n"); - - return codes; -} - - -function buildCircuitVar() { - return "Circom_Circuit _circuit = {\n" + - " NSignals,\n"+ - " NComponents,\n"+ - " NInputs,\n"+ - " NOutputs,\n"+ - " NVars,\n"+ - " _wit2sig,\n"+ - " _components,\n"+ - " _mapIsInput,\n"+ - " _constants,\n" + - " __P__\n" + - "};\n"; + ctx.builder.setWit2Sig(arr); } - function addSizes(_sizes) { const sizes = _sizes || []; let name = "sizes"; @@ -443,6 +273,8 @@ function addSizes(_sizes) { const accSizes = utils.accSizes(sizes); + this.builder.addSizes(labelName, accSizes); + let code = `Circom_Size ${labelName}[${accSizes.length}] = {`; for (let i=0; i0) code += ","; @@ -458,10 +290,9 @@ function addConstant(c) { c = bigInt(c); const s = c.toString(); if (typeof (this.constantsMap[s]) !== "undefined") return this.constantsMap[s]; - const newId = this.constants.length; - this.constants.push(c); - this.constantsMap[s] = newId; - return newId; + const cIdx = this.builder.addConstant(c); + this.constantsMap[s] = cIdx; + return cIdx; } function buildFunction(name, paramValues) { @@ -476,9 +307,9 @@ function buildFunction(name, paramValues) { const oldRefs = ctx.refs; const oldConditionalCode = ctx.conditionalCode; - const oldCode = ctx.code; - const oldCodeHeader = ctx.codeHeader; - const oldCodeFooter = ctx.codeFooter; + const oldCodeBuilder = ctx.codeBuilder; + const oldFnBuilder = ctx.fnBuilder; + const oldUniqueNames = ctx.uniqueNames; const oldFileName = ctx.fileName; const oldFilePath = ctx.oldFilePath; @@ -489,21 +320,20 @@ function buildFunction(name, paramValues) { ctx.scopes = [{}]; ctx.refs = []; ctx.conditionalCode = false; - ctx.code = ""; - ctx.codeHeader = "// Header\n"; - ctx.codeFooter = "// Footer\n"; + ctx.fnBuilder = ctx.builder.newFunctionBuilder(`${name}_${h}`, instanceDef); + ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder(); ctx.uniqueNames = Object.assign({},ctx.globalNames); ctx.returnValue = null; ctx.returnSizes = null; ctx.fileName = ctx.functions[name].fileName; ctx.filePath = ctx.functions[name].filePath; - let paramsStr = ""; + let paramLabels = []; for (let i=0; icircuit->constants + ${c[1]})`; + } else if (c[0] == "CC") { + return "__cIdx"; + } else { + assert(false); + } +} + +class CodeBuilderC { + constructor() { + this.ops = []; + } + + addComment(comment) { + this.ops.push({op: "COMMENT", comment}); + } + + addBlock(block) { + this.ops.push({op: "BLOCK", block}); + } + + calcOffset(dLabel, offsets) { + this.ops.push({op: "CALCOFFSETS", dLabel, offsets}); + } + + assign(dLabel, src, sOffset) { + this.ops.push({op: "ASSIGN", dLabel, src, sOffset}); + } + + getSubComponentOffset(dLabel, component, hash, hashLabel) { + this.ops.push({op: "GETSUBCOMPONENTOFFSET", dLabel, component, hash, hashLabel}); + } + + getSubComponentSizes(dLabel, component, hash, hashLabel) { + this.ops.push({op: "GETSUBCOMPONENTSIZES", dLabel, component, hash, hashLabel}); + } + + getSignalOffset(dLabel, component, hash, hashLabel) { + this.ops.push({op: "GETSIGNALOFFSET", dLabel, component, hash, hashLabel}); + } + + getSignalSizes(dLabel, component, hash, hashLabel) { + this.ops.push({op: "GETSIGNALSIZES", dLabel, component, hash, hashLabel}); + } + + setSignal(component, signal, value) { + this.ops.push({op: "SETSIGNAL", component, signal, value}); + } + + getSignal(dLabel, component, signal) { + this.ops.push({op: "GETSIGNAL", dLabel, component, signal}); + } + + copyN(dLabel, offset, src, n) { + this.ops.push({op: "COPYN", dLabel, offset, src, n}); + } + + copyNRet(src, n) { + this.ops.push({op: "COPYNRET", src, n}); + } + + fieldOp(dLabel, fOp, params) { + this.ops.push({op: "FOP", dLabel, fOp, params}); + } + + ret() { + this.ops.push({op: "RET"}); + } + + addLoop(condLabel, body) { + this.ops.push({op: "LOOP", condLabel, body}); + } + + addIf(condLabel, thenCode, elseCode) { + this.ops.push({op: "IF", condLabel, thenCode, elseCode}); + } + + fnCall(fnName, retLabel, params) { + this.ops.push({op: "FNCALL", fnName, retLabel, params}); + } + + checkConstraint(a, b, strErr) { + this.ops.push({op: "CHECKCONSTRAINT", a, b, strErr}); + } + + + concat(cb) { + this.ops.push(...cb.ops); + } + + hasCode() { + for (let i=0; i { + if ((o[0][0] == "V") && (o[1][0]== "V")) { + rN += o[0][1]*o[1][1]; + return; + } + let f=""; + if (o[0][0] == "V") { + if (o[0][1]==0) return; + f += o[0][1]; + } else if (o[0][0] == "RI") { + if (o[0][1]==0) return; + f += o[0][1]; + } else if (o[0][0] == "R") { + f += `Fr_toInt(${o[0][1]})`; + } else { + assert(false); + } + if (o[1][0] == "V") { + if (o[1][1]==0) return; + if (o[1][1]>1) { + f += "*" + o[1][1]; + } + } else if (o[1][0] == "RS") { + f += `*${o[1][1]}[${o[1][2]}]`; + } else { + assert(false); + } + if (S!="") S+= " + "; + S += f; + }); + if (rN>0) { + S = `${rN} + ${S}`; + } + return S; + } + + build(code) { + this.ops.forEach( (o) => { + if (o.op == "COMMENT") { + code.push(`/* ${o.comment} */`); + } else if (o.op == "BLOCK") { + const codeBlock=[]; + o.block.build(codeBlock); + code.push(utils.ident(codeBlock)); + } else if (o.op == "CALCOFFSETS") { + code.push(`${o.dLabel} = ${this._buildOffset(o.offsets)};`); + } else if (o.op == "ASSIGN") { + const oS = ref2src(o.sOffset); + if (oS != "0") { + code.push(`${o.dLabel} = ${ref2src(o.src)} + ${oS};`); + } else { + code.push(`${o.dLabel} = ${ref2src(o.src)};`); + } + } else if (o.op == "GETSUBCOMPONENTOFFSET") { + code.push(`${o.dLabel} = ctx->getSubComponentOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); + } else if (o.op == "GETSUBCOMPONENTSIZES") { + code.push(`${o.dLabel} = ctx->getSubComponentSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); + } else if (o.op == "GETSIGNALOFFSET") { + code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); + } else if (o.op == "GETSIGNALSIZES") { + code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); + } else if (o.op == "SETSIGNAL") { + code.push(`ctx->setSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${ref2src(o.value)});`); + } else if (o.op == "GETSIGNAL") { + code.push(`ctx->getSignal(__cIdx, ${ref2src(o.component)}, ${ref2src(o.signal)}, ${o.dLabel});`); + } else if (o.op == "COPYN") { + const oS = ref2src(o.offset); + const dLabel = (oS != "0") ? (o.dLabel + "+" + oS) : o.dLabel; + code.push(`Fr_copyn(${dLabel}, ${ref2src(o.src)}, ${o.n});`); + } else if (o.op == "COPYNRET") { + code.push(`Fr_copyn(__retValue, ${ref2src(o.src)}, ${o.n});`); + } else if (o.op == "RET") { + code.push("goto returnFunc;"); + } else if (o.op == "FOP") { + let paramsS = ""; + for (let i=0; i0) paramsS += ", "; + paramsS += ref2src(o.params[i]); + } + code.push(`Fr_${o.fOp}(${o.dLabel}, ${paramsS});`); + } else if (o.op == "LOOP") { + code.push(`while (Fr_isTrue(${o.condLabel})) {`); + const body = []; + o.body.build(body); + code.push(utils.ident(body)); + code.push("}"); + } else if (o.op == "IF") { + code.push(`if (Fr_isTrue(${o.condLabel})) {`); + const thenCode = []; + o.thenCode.build(thenCode); + code.push(utils.ident(thenCode)); + if (o.elseCode) { + code.push("} else {"); + const elseCode = []; + o.elseCode.build(elseCode); + code.push(utils.ident(elseCode)); + } + code.push("}"); + } else if (o.op == "FNCALL") { + code.push(`${o.fnName}(ctx, ${o.retLabel}, ${o.params.join(",")});`); + } else if (o.op == "CHECKCONSTRAINT") { + code.push(`ctx->checkConstraint(__cIdx, ${ref2src(o.a)}, ${ref2src(o.b)}, "${o.strErr}");`); + } + }); + } +} + +class FunctionBuilderC { + + constructor(name, instanceDef, type) { + this.name = name; + this.instanceDef = instanceDef; + this.type = type; // "COMPONENT" or "FUNCTIOM" + this.definedFrElements = []; + this.definedIntElements = []; + this.definedSizeElements = []; + this.definedPFrElements = []; + this.initializedElements = []; + this.initializedSignalOffset = []; + this.initializedSignalSizes = []; + + } + + defineFrElements(dLabel, size) { + this.definedFrElements.push({dLabel, size}); + } + + defineIntElement(dLabel) { + this.definedIntElements.push({dLabel}); + } + + defineSizesElement(dLabel) { + this.definedSizeElements.push({dLabel}); + } + + definePFrElement(dLabel) { + this.definedPFrElements.push({dLabel}); + } + + initializeFrElement(dLabel, offset, idConstant) { + this.initializedElements.push({dLabel, offset, idConstant}); + } + + initializeSignalOffset(dLabel, component, hash, hashLabel) { + this.initializedSignalOffset.push({dLabel, component, hash, hashLabel}); + } + + initializeSignalSizes(dLabel, component, hash, hashLabel) { + this.initializedSignalSizes.push({dLabel, component, hash, hashLabel}); + } + + setParams(params) { + this.params = params; + } + + _buildHeader(code) { + this.definedFrElements.forEach( (o) => { + code.push(`FrElement ${o.dLabel}[${o.size}];`); + }); + this.definedIntElements.forEach( (o) => { + code.push(`int ${o.dLabel};`); + }); + this.definedSizeElements.forEach( (o) => { + code.push(`Circom_Sizes ${o.dLabel};`); + }); + this.definedPFrElements.forEach( (o) => { + code.push(`PFrElement ${o.dLabel};`); + }); + this.initializedElements.forEach( (o) => { + code.push(`Fr_copy(&(${o.dLabel}[${o.offset}]), ctx->circuit->constants +${o.idConstant});`); + }); + this.initializedSignalOffset.forEach( (o) => { + code.push(`${o.dLabel} = ctx->getSignalOffset(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); + }); + this.initializedSignalSizes.forEach( (o) => { + code.push(`${o.dLabel} = ctx->getSignalSizes(${ref2src(o.component)}, 0x${o.hash}LL /* ${o.hashLabel} */);`); + }); + } + + _buildFooter(code) { + } + + newCodeBuilder() { + return new CodeBuilderC(); + } + + setBody(body) { + this.body = body; + } + + build(code) { + code.push( + "/*", + this.instanceDef, + "*/" + ); + + if (this.type=="COMPONENT") { + code.push(`void ${this.name}(Circom_CalcWit *ctx, int __cIdx) {`); + } else if (this.type=="FUNCTION") { + let sParams = ""; + for (let i=0;ifinished(__cIdx);"); + } else if (this.type=="FUNCTION") { + fnCode.push("returnFunc: ;"); + } else { + assert(false); + } + this._buildFooter(fnCode); + + code.push(utils.ident(fnCode)); + code.push("}"); + } + +} + +class BuilderC { + constructor() { + this.hashMaps={}; + this.componentEntriesTables={}; + this.sizes ={}; + this.constants = []; + this.functions = []; + this.components = []; + } + + setHeader(header) { + this.header=header; + } + + // ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements. + addHashMap(name, hm) { + this.hashMaps[name] = hm; + } + + addComponentEntriesTable(name, cet) { + this.componentEntriesTables[name] = cet; + } + + addSizes(name, accSizes) { + this.sizes[name] = accSizes; + } + + addConstant(c) { + this.constants.push(c); + return this.constants.length - 1; + } + + addFunction(fnBuilder) { + this.functions.push(fnBuilder); + } + + addComponent(component) { + this.components.push(component); + } + + setMapIsInput(map) { + this.mapIsInput = map; + } + + setWit2Sig(wit2sig) { + this.wit2sig = wit2sig; + } + + + newComponentFunctionBuilder(name, instanceDef) { + return new FunctionBuilderC(name, instanceDef, "COMPONENT"); + } + + newFunctionBuilder(name, instanceDef) { + return new FunctionBuilderC(name, instanceDef, "FUNCTION"); + } + + + // Body functions + + _buildHeader(code) { + code.push( + "#include \"circom.h\"", + "#include \"calcwit.h\"", + `#define NSignals ${this.header.NSignals}`, + `#define NComponents ${this.header.NComponents}`, + `#define NOutputs ${this.header.NOutputs}`, + `#define NInputs ${this.header.NInputs}`, + `#define NVars ${this.header.NVars}`, + `#define __P__ "${this.header.P.toString()}"`, + "" + ); + } + + _buildHashMaps(code) { + + code.push("// Hash Maps "); + for (let hmName in this.hashMaps ) { + const hm = this.hashMaps[hmName]; + + let c = `Circom_HashEntry ${hmName}[256] = {`; + for (let i=0; i<256; i++) { + c += i>0 ? "," : ""; + if (hm[i]) { + c += `{0x${hm[i][0]}LL, ${hm[i][1]}} /* ${hm[i][2]} */`; + } else { + c += "{0,0}"; + } + } + c += "};"; + code.push(c); + } + + } + + _buildComponentEntriesTables(code) { + code.push("// Component Entry tables"); + for (let cetName in this.componentEntriesTables) { + const cet = this.componentEntriesTables[cetName]; + + code.push(`Circom_ComponentEntry ${cetName}[${cet.length}] = {`); + for (let j=0; j0?",":" "}{${cet[j].offset},${cet[j].sizeName}, ${ty}}`); + } + code.push("};"); + } + } + + _buildSizes(code) { + code.push("// Sizes"); + for (let sName in this.sizes) { + const accSizes = this.sizes[sName]; + + let c = `Circom_Size ${sName}[${accSizes.length}] = {`; + for (let i=0; i0) c += ","; + c += accSizes[i]; + } + c += "};"; + code.push(c); + } + } + + _buildConstants(code) { + const self = this; + const n64 = Math.floor((self.header.P.bitLength() - 1) / 64)+1; + const R = bigInt.one.shiftLeft(n64*64); + + code.push("// Constants"); + code.push(`FrElement _constants[${self.constants.length}] = {`); + for (let i=0; i0 ? "," : " ") + "{" + number2Code(self.constants[i]) + "}"); + } + code.push("};"); + + function number2Code(n) { + if (n.lt(bigInt("80000000", 16)) ) { + return addShortMontgomeryPositive(n); + } + if (n.geq(self.header.P.minus(bigInt("80000000", 16))) ) { + return addShortMontgomeryNegative(n); + } + return addLongMontgomery(n); + + + function addShortMontgomeryPositive(a) { + return `${a.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`; + } + + + function addShortMontgomeryNegative(a) { + const b = a.minus(self.header.P); + return `${b.toString()}, 0x40000000, { ${getLongString(toMontgomery(a))} }`; + } + + function addLongMontgomery(a) { + return `0, 0xC0000000, { ${getLongString(toMontgomery(a))} }`; + } + + function getLongString(a) { + let r = bigInt(a); + let S = ""; + let i = 0; + while (!r.isZero()) { + if (S!= "") S = S+","; + S += "0x" + r.and(bigInt("FFFFFFFFFFFFFFFF", 16)).toString(16) + "LL"; + i++; + r = r.shiftRight(64); + } + while (i0 ? " ," : " "; + code.push(`${sep}{${c.hashMapName}, ${c.entryTableName}, ${c.functionName}, ${c.nInSignals}, ${c.newThread}}`); + } + code.push("};"); + } + + _buildMapIsInput(code) { + code.push("// mapIsInput"); + code.push(`u32 _mapIsInput[${this.mapIsInput.length}] = {`); + let line = ""; + for (let i=0; i0 ? ", " : " "; + line += toHex(this.mapIsInput[i]); + if (((i+1) % 64)==0) { + code.push(" "+line); + line = ""; + } + } + if (line != "") code.push(" "+line); + code.push("};"); + + function toHex(number) { + if (number < 0) number = 0xFFFFFFFF + number + 1; + let S=number.toString(16).toUpperCase(); + while (S.length<8) S = "0" + S; + return "0x"+S; + } + } + + _buildWit2Sig(code) { + code.push("// Witness to Signal Table"); + code.push(`int _wit2sig[${this.wit2sig.length}] = {`); + let line = ""; + for (let i=0; i0 ? "," : " "; + line += this.wit2sig[i]; + if (((i+1) % 64) == 0) { + code.push(" "+line); + line = ""; + } + } + if (line != "") code.push(" "+line); + code.push("};"); + } + + _buildCircuitVar(code) { + + code.push( + "// Circuit Variable", + "Circom_Circuit _circuit = {" , + " NSignals,", + " NComponents,", + " NInputs,", + " NOutputs,", + " NVars,", + " _wit2sig,", + " _components,", + " _mapIsInput,", + " _constants,", + " __P__", + "};" + ); + } + + + build() { + const code=[]; + this._buildHeader(code); + this._buildSizes(code); + this._buildConstants(code); + this._buildHashMaps(code); + this._buildComponentEntriesTables(code); + this._buildFunctions(code); + this._buildComponents(code); + this._buildMapIsInput(code); + this._buildWit2Sig(code); + this._buildCircuitVar(code); + return streamFromMultiArray(code); + } +} + + +module.exports = BuilderC; diff --git a/src/builder_wasm.js b/src/builder_wasm.js new file mode 100644 index 0000000..9c2b2e1 --- /dev/null +++ b/src/builder_wasm.js @@ -0,0 +1,28 @@ +class BuilderWasm { + constructor() { + + } + + setHeader(header) { + this.header=header; + } + + // ht is an array of 256 element that can be undefined or [Hash, Idx, KeyName] elements. + addHashMap(name, ht) { + this.hashTables[name] = ht; + } + + addComponentEntriesTable(name, cet) { + this.componentEntriesTables[name] = cet; + } + + addSizes(name, accSizes) { + this.sizes[name] = accSizes; + } + + build() { + + } +} + +module.exports = BuilderWasm; diff --git a/src/c_gen.js b/src/c_gen.js deleted file mode 100644 index 623d8cc..0000000 --- a/src/c_gen.js +++ /dev/null @@ -1,1220 +0,0 @@ -const bigInt = require("big-integer"); -const utils = require("./utils"); -const assert = require("assert"); -const iterateAST = require("./iterateast"); - -module.exports.gen = gen; -module.exports.newRef = newRef; -module.exports.createRefs = createRefs; - -function newRef(ctx, type, name, value, sizes) { - let label; - if (!name) { - label = ctx.getUniqueName(); - } else { - label = ctx.getUniqueName(name); - } - if (Array.isArray(sizes)) { - // sizes = sizes; - } else if (utils.isDefined(value)) { - sizes = utils.accSizes(utils.extractSizes(value)); - } else { - sizes = [1, 0]; - } - - const refId = ctx.refs.length; - const entry = { - type: type, - used: false, - sizes: sizes, - label: label - }; - if (utils.isDefined(value)) { - entry.value = utils.flatArray(value); - } - - ctx.refs.push(entry); - return refId; -} - -function instantiateRef(ctx, refId, initValue) { - const v = ctx.refs[refId]; - if (v.used) return; - if (!v.sizes) v.sizes = [1,0]; - - if (v.type=="BIGINT") { - ctx.codeHeader += `FrElement ${v.label}[${v.sizes[0]}];\n`; - } else if (v.type=="INT") { - ctx.codeHeader += `int ${v.label};\n`; - } else if (v.type=="SIZES") { - ctx.codeHeader += `Circom_Sizes ${v.label};\n`; - } - v.used = true; - if (utils.isDefined(initValue)) { - if (v.type == "BIGINT") { - for (let i=0; icircuit->constants +${idConstant});\n`; - } - } - } - } -} - -function instantiateConstant(ctx, value) { - const sizes = utils.accSizes(utils.extractSizes(value)); - const flatedValue = utils.flatArray(value); - const label = ctx.getUniqueName("_const"); - ctx.codeHeader += `FrElement ${label}[${sizes[0]}];\n`; - for (let i=0; icircuit->constants +${idConstant});\n`; - } - const refId = ctx.refs.length; - ctx.refs.push({ - type: "BIGINT", - used: true, - sizes: sizes, - label: label - }); - return refId; -} - - -function createRefs(ctx, ast) { - const scopeLabels = []; - iterateAST(ast, (ast, level) => { - while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) { - ctx.scopes.pop(); - scopeLabels.pop(); - } - if (ast.type == "DECLARE") { - if (ctx.scopes[ctx.scopes.length-1][ast.name.name]) return ctx.throwError(ast, `Name already defined: ${ast.name.name}`); - for (let i=ctx.scopes.length-2; i>=0; i--) { - if (ctx.scopes[i][ast.name.name]) ctx.logWarning(ast, `Shadowing variable: ${ast.name.name}`); - } - let entry = {}; - if (ast.declareType == "COMPONENT") { - entry.type= "COMPONENT"; - entry.label = ast.name.name; - } else if ((ast.declareType == "SIGNALIN")|| - (ast.declareType == "SIGNALOUT")|| - (ast.declareType == "SIGNAL")) { - entry.type = "SIGNAL"; - entry.label = ast.name.name; - } else if (ast.declareType == "VARIABLE") { - entry.type = "BIGINT"; - entry.used = false; - entry.sizes = null; - entry.label = ctx.getUniqueName(ast.name.name); - } else { - return ctx.throwError(ast, "Invalid declaration: " + ast.declareType); - } - - const refId = ctx.refs.length; - ctx.refs.push(entry); - ast.refId = refId; - ctx.scopes[ctx.scopes.length-1][ast.name.name] = refId; - } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) { - scopeLabels.push(level); - ctx.scopes.push({}); - } else if (ast.type == "VARIABLE") { - ast.refId = name2ref(ast.name); - } - }); - - function name2ref(n) { - for (let i=ctx.scopes.length-1; i>=0; i--) { - if (typeof ctx.scopes[i][n] !== "undefined") return ctx.scopes[i][n]; - } - return -1; - } -} - -function gen(ctx, ast) { - if ((ast.type == "NUMBER") ) { - return genNumber(ctx, ast); - } else if (ast.type == "VARIABLE") { - return genVariable(ctx, ast); - } else if (ast.type == "PIN") { - return genPin(ctx, ast); - } else if (ast.type == "OP") { - if (ast.op == "=") { - return genAssignement(ctx, ast); - } else if (ast.op == "<--") { - return genAssignement(ctx, ast); - } else if (ast.op == "<==") { - return genSignalAssignConstraint(ctx, ast); - } else if (ast.op == "===") { - return genConstraint(ctx, ast); - } else if (ast.op == "+=") { - return genVarAddAssignement(ctx, ast); - } else if (ast.op == "*=") { - return genVarMulAssignement(ctx, ast); - } else if (ast.op == "+") { - return genOp(ctx, ast, "add", 2); - } else if (ast.op == "-") { - return genOp(ctx, ast, "sub", 2); - } else if (ast.op == "UMINUS") { - return genOp(ctx, ast, "neg", 1); - } else if (ast.op == "*") { - return genOp(ctx, ast, "mul", 2); - } else if (ast.op == "%") { - return genOp(ctx, ast, "mod", 2); - } else if (ast.op == "PLUSPLUSRIGHT") { - return genOpOp(ctx, ast, "add", "RIGHT"); - } else if (ast.op == "PLUSPLUSLEFT") { - return genOpOp(ctx, ast, "add", "LEFT"); - } else if (ast.op == "MINUSMINUSRIGHT") { - return genOpOp(ctx, ast, "sub", "RIGHT"); - } else if (ast.op == "MINUSMINUSLEFT") { - return genOpOp(ctx, ast, "sub", "LEFT"); - } else if (ast.op == "**") { - return genOp(ctx, ast, "pow", 2); - } else if (ast.op == "/") { - return genOp(ctx, ast, "div", 2); - } else if (ast.op == "\\") { - return genOp(ctx, ast, "idiv", 2); - } else if (ast.op == "&") { - return genOp(ctx, ast, "band", 2); - } else if (ast.op == "|") { - return genOp(ctx, ast, "bor", 2); - } else if (ast.op == "^") { - return genOp(ctx, ast, "bxor", 2); - } else if (ast.op == "~") { - return genOp(ctx, ast, "bnot", 1); - } else if (ast.op == "&&") { - return genOp(ctx, ast, "land", 2); - } else if (ast.op == "||") { - return genOp(ctx, ast, "lor", 2); - } else if (ast.op == "!") { - return genOp(ctx, ast, "lnot", 1); - } else if (ast.op == "<<") { - return genOp(ctx, ast, "shl", 2); - } else if (ast.op == ">>") { - return genOp(ctx, ast, "shr", 2); - } else if (ast.op == "<") { - return genOp(ctx, ast, "lt", 2); - } else if (ast.op == ">") { - return genOp(ctx, ast, "gt", 2); - } else if (ast.op == "<=") { - return genOp(ctx, ast, "leq", 2); - } else if (ast.op == ">=") { - return genOp(ctx, ast, "geq", 2); - } else if (ast.op == "==") { - return genOp(ctx, ast, "eq", 2); - } else if (ast.op == "!=") { - return genOp(ctx, ast, "neq", 2); - } else if (ast.op == "?") { - return genTerCon(ctx, ast); - } else { - ctx.throwError(ast, "Invalid operation: " + ast.op); - } - } else if (ast.type == "DECLARE") { - if (ast.declareType == "COMPONENT") { - return genDeclareComponent(ctx, ast); - } else if ((ast.declareType == "SIGNALIN")|| - (ast.declareType == "SIGNALOUT")|| - (ast.declareType == "SIGNAL")) { - return genDeclareSignal(ctx, ast); - } else if (ast.declareType == "VARIABLE") { - return genDeclareVariable(ctx, ast); - } else { - ctx.throwError(ast, "Invalid declaration: " + ast.declareType); - } - } else if (ast.type == "FUNCTIONCALL") { - return genFunctionCall(ctx, ast); - } else if (ast.type == "BLOCK") { - return genBlock(ctx, ast); - } else if (ast.type == "COMPUTE") { - return gen(ctx, ast.body); - } else if (ast.type == "FOR") { - return genLoop(ctx, ast); - } else if (ast.type == "WHILE") { - return genLoop(ctx, ast); - } else if (ast.type == "IF") { - return genIf(ctx, ast); - } else if (ast.type == "RETURN") { - return genReturn(ctx, ast); - } else if (ast.type == "INCLUDE") { - return genInclude(ctx, ast); - } else if (ast.type == "ARRAY") { - return genArray(ctx, ast); - } else { - ctx.throwError(ast, "GEN -> Invalid AST node type: " + ast.type); - } -} - -function genBlock(ctx, ast) { - ctx.scopes.push({}); - const oldCode = ctx.code; - let res = null; - ctx.code = ""; - for (let i=0; i=0 ? p2 : undefined)); - } - return codes.join("\n"); -} - -function genSrcComment(ctx, ast) { - const code = getSource(ctx, ast); - ctx.code += "\n/* "+code+" */\n"; -} - -function genLoopSrcComment(ctx, ast) { - if (ast.type == "FOR") { - const init = getSource(ctx, ast.init); - const condition = getSource(ctx, ast.condition); - const step = getSource(ctx, ast.step); - ctx.code += `\n/* for (${init},${condition},${step}) */\n`; - } else if (ast.type == "WHILE") { - const condition = getSource(ctx, ast.condition); - ctx.code += `\n/* while (${condition}) */\n`; - } else { - assert(false, "Invalid loop type: "+ ast.type); - } -} - -function genIfSrcComment(ctx, ast) { - const condition = getSource(ctx, ast.condition); - ctx.code += `\n/* if (${condition}) */\n`; -} - - -function genDeclareComponent(ctx, ast) { - return ast.refId; -} - -function genDeclareSignal(ctx, ast) { - return ast.refId; -} - -function genDeclareVariable(ctx, ast) { - - - const v = ctx.refs[ast.refId]; - - let sizes; - if (ast.name.selectors.length>0) { - sizes=[]; - for (let i=0; i< ast.name.selectors.length; i++) { - const sizeRef = gen(ctx, ast.name.selectors[i]); - const size = ctx.refs[sizeRef]; - if (size.sizes[0] != 1) return ctx.throwError(ast, "A selector cannot be an array"); - if (size.used) return ctx.throwError(ast, "Variable size variables not allowed"); - sizes.push(size.value[0].toJSNumber()); - } - sizes = utils.accSizes(sizes); - } else { - sizes = [1,0]; - } - - if ((!v.sizes)&&(sizes)) { - v.sizes = sizes; - v.value = new Array(sizes[0]); - } - - if (v.sizes) { - if (!utils.sameSizes(v.sizes, sizes)) return ctx.throwError(ast, "Redefined a var with different sized"); - } - - return ast.refId; -} - -function genNumber(ctx, ast) { - return newRef(ctx, "BIGINT", "_num", bigInt(ast.value)); -} - - - -function genGetOffset(ctx, refOffset, vSizes, sels) { - - let rN = 0; - let rStr = ""; - let offset; - - if (refOffset) { - offset = ctx.refs[refOffset]; - if (offset.used) { - rStr += offset.label; - } else { - rN += offset.value.toJSNumber(); - } - } - - if ((sels)&&(sels.length>0)) { - - let iSizes; - if (Array.isArray(vSizes)) { - iSizes = { - used: false, - sizes: vSizes - }; - } else { - iSizes = ctx.refs[vSizes]; - } - - for (let i=0; i0) { - if (rStr != "") rStr += " + "; - rStr += rN; - rN =0; - } - - if (rStr != "") rStr += " + "; - if (iIdx.used) { - rStr += `Fr_toInt(${iIdx.label})`; - } else { - rStr += iIdx.value[0].toString(); - } - rStr += "*"; - if (iSizes.used) { - rStr += `${iSizes.label}[${i+1}]`; - } else { - rStr += iSizes.sizes[i+1]; - } - } - } - } - - if (rStr == "") { - const o = newRef(ctx, "INT", "_offset", rN); - return o; - } else { - if (rN>0) { - if (rStr != "") rStr += " + "; - rStr += rN; - rN =0; - } - if ((refOffset)&&(rStr == offset.label)) { - return offset.label; - } else { - const resRef = newRef(ctx, "INT", "_offset"); - instantiateRef(ctx, resRef); - const res = ctx.refs[resRef]; - ctx.code += `${res.label} = ${rStr};\n`; - return resRef; - } - } -} - -function genVariable(ctx, ast) { - const v = ctx.refs[ast.refId]; - - const l = ast.selectors ? ast.selectors.length : 0; - - if (v.type == "SIGNAL") { - let vOffset; - if (l>0) { - const vsOffset = genGetSigalOffset(ctx, -1, ast.name); - const vsSizes = genGetSignalSizes(ctx, -1, ast.name); - vOffset = genGetOffset(ctx, vsOffset, vsSizes, ast.selectors ); - } else { - vOffset = genGetSigalOffset(ctx, -1, ast.name); - } - return genGetSignal(ctx, -1, vOffset); - - } else if (v.type == "BIGINT") { - const refOffset = genGetOffset(ctx, 0, v.sizes, ast.selectors ); - const offset = ctx.refs[refOffset]; - if (v.used) { - if (offset.used) { - const refRes = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); - const res = ctx.refs[refRes]; - res.used = true; - ctx.codeHeader += `PFrElement ${res.label};\n`; - ctx.code += `${res.label} = ${v.label} + ${offset.label};\n`; - return refRes; - } else if ((offset.value[0])||(l>0)) { - const refRes = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); - const res = ctx.refs[refRes]; - res.used = true; - ctx.codeHeader += `PFrElement ${res.label};\n`; - ctx.code += `${res.label} = ${v.label} + ${offset.value[0]};\n`; - return refRes; - } else { - return ast.refId; - } - } else { - if (offset.used) { - instantiateRef(ctx, ast.refId, v.value); - const resRef = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); - const res = ctx.refs[resRef]; - res.used = true; - ctx.codeHeader += `PFrElement ${res.label};\n`; - ctx.code += `${res.label} = ${v.label} + ${offset.label};\n`; - return resRef; - } else { - // return newSubRef(ctx, ast.name, ast.selectors); - return newRef(ctx, "BIGINT", "_v", v.value.slice(offset.value[0], offset.value[0] + v.sizes[l]),v.sizes.slice(l)); - } - } - } -} - - -function genPin(ctx, ast) { - let vcIdx; - if (ast.component.selectors.length>0) { - const vcOffset = genGetSubComponentOffset(ctx, -1, ast.component.name); - const vcSizes = genGetSubComponentSizes(ctx, -1, ast.component.name); - vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.component.selectors ); - } else { - vcIdx = genGetSubComponentOffset(ctx, -1, ast.component.name); - } - - let vsIdx; - if (ast.pin.selectors.length>0) { - const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.pin.name); - const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.pin.name); - vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.pin.selectors ); - } else { - vsIdx = genGetSigalOffset(ctx, vcIdx, ast.pin.name); - } - - return genGetSignal(ctx, vcIdx, vsIdx); -} - -function genGetSubComponentOffset(ctx, cIdxRef, label) { - const refOffset = newRef(ctx, "INT", "_compIdx"); - const offset = ctx.refs[refOffset]; - instantiateRef(ctx, refOffset); - - const h = utils.fnvHash(label); - let s; - if (cIdxRef>=0) { - const cIdx = ctx.refs[cIdxRef]; - s = cIdx.label; - } else { - s = "__cIdx"; - } - ctx.code += `${offset.label} = ctx->getSubComponentOffset(${s}, 0x${h}LL /* ${label} */);\n`; - return refOffset; -} - -function genGetSubComponentSizes(ctx, cIdxRef, label) { - const sizesRef = newRef(ctx, "SIZES", "_compSizes"); - const sizes = ctx.refs[sizesRef]; - instantiateRef(ctx, sizesRef); - - const h = utils.fnvHash(label); - let s; - if (cIdxRef>=0) { - const cIdx = ctx.refs[cIdxRef]; - s = cIdx.label; - } else { - s = "__cIdx"; - } - ctx.code += `${sizes.label} = ctx->getSubComponentSizes(${s}, 0x${h}LL /* ${label} */);\n`; - return sizesRef; -} - -function genGetSigalOffset(ctx, cIdxRef, label) { - - let constCIdx = null; - if (cIdxRef>=0) { - const cIdx = ctx.refs[cIdxRef]; - if (utils.isDefined(cIdx.value)) { - constCIdx = cIdx.value.toString()+"_"+label; - } - } else { - constCIdx = "_cIdx_"+label; - } - if (constCIdx && ctx.getSignalOffsetCache[constCIdx]) return ctx.getSignalOffsetCache[constCIdx]; - - const refOffset = newRef(ctx, "INT", "_sigIdx"); - const offset = ctx.refs[refOffset]; - instantiateRef(ctx, refOffset); - - const h = utils.fnvHash(label); - let s; - if (cIdxRef>=0) { - const cIdx = ctx.refs[cIdxRef]; - s = cIdx.label; - } else { - s = "__cIdx"; - } - if (constCIdx) { - ctx.codeHeader += `${offset.label} = ctx->getSignalOffset(${s}, 0x${h}LL /* ${label} */);\n`; - ctx.getSignalOffsetCache[constCIdx] = refOffset; - } else { - ctx.code += `${offset.label} = ctx->getSignalOffset(${s}, 0x${h}LL /* ${label} */);\n`; - } - return refOffset; -} - -function genGetSignalSizes(ctx, cIdxRef, label) { - let constCIdx = null; - if (cIdxRef>=0) { - const cIdx = ctx.refs[cIdxRef]; - if (utils.isDefined(cIdx.value)) { - constCIdx = cIdx.value.toString()+"_"+label; - } - } else { - constCIdx = "_cIdx_"+label; - } - if (constCIdx && ctx.getSignalSizesCache[constCIdx]) return ctx.getSignalSizesCache[constCIdx]; - - - const refSizes = newRef(ctx, "SIZES", "_sigSizes"); - const sizes = ctx.refs[refSizes]; - instantiateRef(ctx, refSizes); - - let s; - if (cIdxRef>=0) { - const cIdx = ctx.refs[cIdxRef]; - s = cIdx.label; - } else { - s = "__cIdx"; - } - const h = utils.fnvHash(label); - if (constCIdx) { - ctx.codeHeader += `${sizes.label} = ctx->getSignalSizes(${s}, 0x${h}LL /* ${label} */);\n`; - ctx.getSignalSizesCache[constCIdx] = refSizes; - } else { - ctx.code += `${sizes.label} = ctx->getSignalSizes(${s}, 0x${h}LL /* ${label} */);\n`; - } - return refSizes; -} - -function genSetSignal(ctx, cIdxRef, sIdxRef, valueRef) { - const v = ctx.refs[valueRef]; - if (!utils.isDefined(v)) { - console.log("BREAK!!!"); - } - if (!v.used) { - instantiateRef(ctx, valueRef, v.value); - } - let s; - if (cIdxRef>=0) { - const cIdx = ctx.refs[cIdxRef]; - s = cIdx.label; - } else { - s = "__cIdx"; - } - const sIdx = ctx.refs[sIdxRef]; - ctx.code += `ctx->setSignal(__cIdx, ${s}, ${sIdx.label}, ${v.label});\n`; - - return valueRef; -} - -function genGetSignal(ctx, cIdxRef, sIdxRef) { - const resRef = newRef(ctx, "BIGINT", "_sigValue"); - const res = ctx.refs[resRef]; - instantiateRef(ctx, resRef); - let s; - if (cIdxRef>=0) { - const cIdx = ctx.refs[cIdxRef]; - s = cIdx.label; - } else { - s = "__cIdx"; - } - const sIdx = ctx.refs[sIdxRef]; - ctx.code += `ctx->getSignal(__cIdx, ${s}, ${sIdx.label}, ${res.label});\n`; - return resRef; -} - -function genPinAssignement(ctx, ast) { - let vcIdx; - if (ast.values[0].component.selectors.length>0) { - const vcOffset = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name); - const vcSizes = genGetSubComponentSizes(ctx, -1, ast.values[0].component.name); - vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors ); - } else { - vcIdx = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name); - } - - let vsIdx; - if (ast.values[0].pin.selectors.length>0) { - const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); - const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name); - vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors ); - } else { - vsIdx = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); - } - - const vVal = gen(ctx, ast.values[1]); - - genSetSignal(ctx, vcIdx, vsIdx, vVal); - - return vVal; -} - -function genSignalAssignmen(ctx, ast, lName, sels, rName) { - let vsIdx; - const signal = ctx.refs[lName]; - if (sels.length>0) { - const vsOffset = genGetSigalOffset(ctx, -1, signal.label); - const vsSizes = genGetSignalSizes(ctx, -1, signal.label); - vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels ); - } else { - vsIdx = genGetSigalOffset(ctx, -1, signal.label); - } - - return genSetSignal(ctx, -1, vsIdx, rName); -} - -function genVarAssignment(ctx, ast, lRef, sels, rRef) { - - const left = ctx.refs[lRef]; - const right = ctx.refs[rRef]; - if (!utils.isDefined(left.sizes)) { - left.sizes = right.sizes; - left.value = new Array(left.sizes[0]); - } - if (!utils.sameSizes(left.sizes.slice(sels.length), right.sizes)) return ctx.throwError(ast, "Sizes do not match"); - - const oRef = genGetOffset(ctx, 0, left.sizes, sels); - const offset = ctx.refs[oRef]; - - let instantiated=false; - if (left.used) { - instantiateRef(ctx, rRef, right.value); - instantiated=true; - } else if (right.used) { - if (sels.length == 0) { - instantiateRef(ctx,lRef); - } else { - instantiateRef(ctx,lRef, left.value); - } - instantiated=true; - } else if (offset.used) { - instantiateRef(ctx, rRef, right.value); - if (sels.length == 0) { - instantiateRef(ctx,lRef); - } else { - instantiateRef(ctx,lRef, left.value); - } - instantiated=true; - } - - if (instantiated) { - if (offset.used) { - ctx.code += `Fr_copyn(${left.label} + ${offset.label}, ${right.label}, ${right.sizes[0]});\n`; - } else { - if (offset.value[0]>0) { - ctx.code += `Fr_copyn(${left.label} + ${offset.value[0]}, ${right.label}, ${right.sizes[0]});\n`; - } else { - ctx.code += `Fr_copyn(${left.label}, ${right.label}, ${right.sizes[0]});\n`; - } - } - } else { - if (offset.value[0]>0) { - for (let i=0; icheckConstraint(__cIdx, ${a.label}, ${b.label}, "${strErr}");`; -} - - -function genArray(ctx, ast) { - let subSizes; - let instantiate = false; - if (ast.values.length == 0) return ctx.throwError(ast, "Arrays with zero elements not allowed"); - const value = []; - const refs = []; - for (let i=0; ilog(${val.label});`; - return vRef; - } - const params = []; - for (let i=0; i { - if (ast.type == "OP") { - if (["=", "+=", "*=", "PLUSPLUSLEFT", "PLUSPLUSRIGHT", "MINUSMINUSLEFT", "MINUSMINUSRIGHT"].indexOf(ast.op) >= 0) { - let refId; - refId = ast.values[0].refId; - const ref = ctx.refs[refId]; - instantiateRef(ctx, refId, ref.value); - } - } - }); - ctx.conditionalCode = 1; - } else { - ctx.conditionalCode ++; - } -} - -function leaveConditionalCode(ctx) { - assert(ctx.conditionalCode, "Leaving conditional code too many times"); - ctx.conditionalCode --; - if (!ctx.conditionalCode) { - delete ctx.conditionalCode; - } -} - -function genLoop(ctx, ast) { - genLoopSrcComment(ctx, ast); - let inLoop = false; - - if (ast.init) { - gen(ctx, ast.init); - if (ctx.error) return; - } - - let end=false; - let condVarRef; - let condVar; - - - const condRef = gen(ctx, ast.condition); - if (ctx.error) return; - - const cond = ctx.refs[condRef]; - if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array"); - - if (cond.used) { - inLoop = true; - enterConditionalCode(ctx, ast); - condVarRef = newRef(ctx, "INT", "_cond"); - condVar = ctx.refs[condVarRef]; - instantiateRef(ctx, condVarRef); - - ctx.code += - `${condVar.label} = Fr_isTrue(${cond.label});\n` + - `while (${condVar.label}) {\n`; - } else { - if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned"); - if (cond.value[0].isZero()) end=true; - } - - - while (!end) { - - const oldCode = ctx.code; - ctx.code = ""; - - if (ast.body.type != "BLOCK") genSrcComment(ctx, ast.body); - gen(ctx, ast.body); - if (ctx.error) return; - - if (ast.step) { - gen(ctx, ast.step); - if (ctx.error) return; - } - - const condRef2 = gen(ctx, ast.condition); - if (ctx.error) return; - - const cond2 = ctx.refs[condRef2]; - - if ((!cond2.used) &&(haveCode(ctx.code))) { - instantiateRef(ctx, condRef2, cond2.value); - } - - if (!inLoop) { - if (cond2.used) { - ctx.code = oldCode + ctx.code; - inLoop = true; - enterConditionalCode(ctx, ast); - condVarRef = newRef(ctx, "INT", "_cond"); - condVar = ctx.refs[condVarRef]; - instantiateRef(ctx, condVarRef); - - ctx.code += - `${condVar.label} = Fr_isTrue(${cond2.label});\n` + - `while (${condVar.label}) {\n`; - } else { - ctx.code = oldCode + ctx.code; - if (cond2.value[0].isZero()) end=true; - } - } else { - ctx.code = - oldCode + - utils.ident( - ctx.code + - `${condVar.label} = Fr_isTrue(${cond2.label});\n`); - end=true; - } - } - if (inLoop) { - ctx.code += "}\n"; - leaveConditionalCode(ctx); - } - ctx.scopes.pop(); - - function haveCode(c) { - return c.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "").trim() != ""; - } -} - -function genIf(ctx, ast) { - genIfSrcComment(ctx, ast); - const condRef = gen(ctx, ast.condition); - if (ctx.error) return; - const cond = ctx.refs[condRef]; - if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array"); - - if (cond.used) { - enterConditionalCode(ctx, ast); - - ctx.code += `if (Fr_isTrue(${cond.label})) {\n`; - - const oldCode = ctx.code; - ctx.code = ""; - - gen(ctx, ast.then); - if (ctx.error) return; - - ctx.code = oldCode + utils.ident(ctx.code); - - if (ast.else) { - ctx.code += "} else {\n"; - const oldCode = ctx.code; - ctx.code = ""; - gen(ctx, ast.else); - if (ctx.error) return; - ctx.code = oldCode + utils.ident(ctx.code); - } - - ctx.code += "}\n"; - leaveConditionalCode(ctx); - - } else { - if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned"); - if (!cond.value[0].isZero()) { - gen(ctx, ast.then); - } else { - if (ast.else) { - gen(ctx, ast.else); - } - } - } -} - - -function genReturn(ctx, ast) { - const vRef = gen(ctx, ast.value); - const v= ctx.refs[vRef]; - if (ctx.returnSizes) { - if (!utils.sameSizes(v.sizes, ctx.returnSizes)) return ctx.throwError(ast, "Diferent return sizes"); - } else { - ctx.returnSizes = v.sizes; - } - if (ctx.conditionalCode) { - instantiateRef(ctx, vRef, v.value); - } - if (v.used) { - ctx.code += `Fr_copyn(__retValue, ${v.label}, ${v.sizes[0]});\n`; - } else { - if (!utils.isDefined(v.value)) return ctx.throwError(ast, "Returning an unknown value"); - if (!utils.isDefined(ctx.returnValue)) { - ctx.returnValue = v.value; - } - } - ctx.code += "goto returnFunc;\n"; - return vRef; -} - - - -function genSignalAssignConstraint(ctx, ast) { - const res = genAssignement(ctx, ast); - // genConstraint(ctx, ast); - return res; - // return genVarAssignement(ctx, ast); -} - -function genVarAddAssignement(ctx, ast) { - return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]}); -} - -function genVarMulAssignement(ctx, ast) { - return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]}); -} - -function genOpOp(ctx, ast, op, lr) { - - if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable"); - - const vRef = ast.values[0].refId; - - const vevalRef = gen(ctx, ast.values[0]); - if (ctx.error) return; - const veval = ctx.refs[vevalRef]; - - if (veval.type != "BIGINT") return ctx.throwError(ast, "incrementing a non variable"); - - const resRef = newRef(ctx, "BIGINT", "_tmp"); - const res = ctx.refs[resRef]; - if (veval.used) { - instantiateRef(ctx, resRef); - ctx.code += `Fr_${op}(${res.label}, ${veval.label}, ctx->circuit->constants + ${ctx.addConstant(bigInt.one)});\n`; - } else { - res.value = [ctx.field[op](veval.value[0], bigInt(1))]; - } - genVarAssignment(ctx, ast, vRef, ast.values[0].selectors, resRef); - if (lr == "RIGHT") { - return vevalRef; - } else if (lr == "LEFT") { - return resRef; - } -} - -function genOp(ctx, ast, op, nOps) { - const vals = []; - const valRefs = []; - - var anyUsed=false; - - for (let i=0; i options.cSourceWriteStream.on("finish", fulfill)); + ctx.builder = new BuilderC(); + build(ctx); + const rdStream = ctx.builder.build(); + rdStream.pipe(options.cSourceWriteStream); + + // await new Promise(fulfill => options.cSourceWriteStream.on("finish", fulfill)); + } + + if (options.wasmWriteStream) { + ctx.builder = new BuilderWasm(); + build(ctx); + const rdStream = ctx.builder.build(); + rdStream.pipe(options.wasmWriteStream); + + // await new Promise(fulfill => options.wasmWriteStream.on("finish", fulfill)); } // const mainCode = gen(ctx,ast); diff --git a/src/genOptCode.js b/src/genOptCode.js deleted file mode 100644 index 62411f4..0000000 --- a/src/genOptCode.js +++ /dev/null @@ -1,70 +0,0 @@ - - -module.exports = genOpt; - - -function genOpt(ctx, ast) { - if (ast.type == "OP") { - if (ast.op == "=") { - return genOptVarAssignement(ctx, ast); - } else { - error(ctx, ast, "GENOPT -> Invalid operation: " + ast.op); - } - } else if (ast.type == "TEMPLATEDEF") { - return genOptTemplateDef(ctx, ast); - } else { - error(ctx, ast, "GENOPT -> Invalid AST node type: " + ast.type); - } -} - -function error(ctx, ast, errStr) { - ctx.error = { - pos: { - first_line: ast.first_line, - first_column: ast.first_column, - last_line: ast.last_line, - last_column: ast.last_column - }, - errStr: errStr, - errFile: ctx.fileName, - ast: ast - }; -} - - -function genOptTemplateDef(ctx, ast) { - if (ctx.templates[ast.name]) { - return error(ctx, ast, "Template name already exists: "+ast.name); - } - ctx.templates[ast.name] = { - type: "TEMPLATE", - params: ast.params, - block: ast.block, - fileName: ctx.fileName, - filePath: ctx.filePath - }; -} - -function genOptVarAssignement(ctx, ast) { - let varName; - if (ast.values[0].type == "DECLARE") { - varName = genOptCode(ctx, ast.values[0]); - if (ctx.error) return; - } else { - varName = ast.values[0]; - } - const varContent = getScope(ctx, varName.name, varName.selectors); - if (ctx.error) return; - - if ((typeof(varContent) != "object")||(varContent == null)) return error(ctx, ast, "Variable not defined"); - - if (varContent.type == "COMPONENT") return genOptInstantiateComponet(ctx, varName, ast.values[1]); - if (varContent.type == "SIGNAL") return error(ctx, ast, "Cannot assig to a signal with `=` use <-- or <== ops"); - - const res = genOpt(ctx, ast.values[1]); - if (ctx.error) return; - - setScope(ctx, varName.name, varName.selectors, res); - - return v; -} diff --git a/src/gencode.js b/src/gencode.js index 0688345..894bef9 100644 --- a/src/gencode.js +++ b/src/gencode.js @@ -1,32 +1,115 @@ -/* - Copyright 2018 0KIMS association. - - This file is part of circom (Zero Knowledge Circuit Compiler). - - circom is a free software: you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +const bigInt = require("big-integer"); +const utils = require("./utils"); +const assert = require("assert"); +const iterateAST = require("./iterateast"); + +module.exports.gen = gen; +module.exports.newRef = newRef; +module.exports.createRefs = createRefs; + +function newRef(ctx, type, name, value, sizes) { + let label; + if (!name) { + label = ctx.getUniqueName(); + } else { + label = ctx.getUniqueName(name); + } + if (Array.isArray(sizes)) { + // sizes = sizes; + } else if (utils.isDefined(value)) { + sizes = utils.accSizes(utils.extractSizes(value)); + } else { + sizes = [1, 0]; + } - circom is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public - License for more details. + const refId = ctx.refs.length; + const entry = { + type: type, + used: false, + sizes: sizes, + label: label + }; + if (utils.isDefined(value)) { + entry.value = utils.flatArray(value); + } - You should have received a copy of the GNU General Public License - along with circom. If not, see . -*/ + ctx.refs.push(entry); + return refId; +} -const bigInt = require("big-integer"); +function instantiateRef(ctx, refId, initValue) { + const v = ctx.refs[refId]; + if (v.used) return; + if (!v.sizes) v.sizes = [1,0]; + + if (v.type=="BIGINT") { + ctx.fnBuilder.defineFrElements(v.label, v.sizes[0]); + } else if (v.type=="INT") { + ctx.fnBuilder.defineIntElement(v.label); + } else if (v.type=="SIZES") { + ctx.fnBuilder.defineSizesElement(v.label); + } + v.used = true; + if (utils.isDefined(initValue)) { + if (v.type == "BIGINT") { + for (let i=0; i { + while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) { + ctx.scopes.pop(); + scopeLabels.pop(); + } + if (ast.type == "DECLARE") { + if (ctx.scopes[ctx.scopes.length-1][ast.name.name]) return ctx.throwError(ast, `Name already defined: ${ast.name.name}`); + for (let i=ctx.scopes.length-2; i>=0; i--) { + if (ctx.scopes[i][ast.name.name]) ctx.logWarning(ast, `Shadowing variable: ${ast.name.name}`); + } + let entry = {}; + if (ast.declareType == "COMPONENT") { + entry.type= "COMPONENT"; + entry.label = ast.name.name; + } else if ((ast.declareType == "SIGNALIN")|| + (ast.declareType == "SIGNALOUT")|| + (ast.declareType == "SIGNAL")) { + entry.type = "SIGNAL"; + entry.label = ast.name.name; + } else if (ast.declareType == "VARIABLE") { + entry.type = "BIGINT"; + entry.used = false; + entry.sizes = null; + entry.label = ctx.getUniqueName(ast.name.name); + } else { + return ctx.throwError(ast, "Invalid declaration: " + ast.declareType); + } + + const refId = ctx.refs.length; + ctx.refs.push(entry); + ast.refId = refId; + ctx.scopes[ctx.scopes.length-1][ast.name.name] = refId; + } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) { + scopeLabels.push(level); + ctx.scopes.push({}); + } else if (ast.type == "VARIABLE") { + ast.refId = name2ref(ast.name); + } + }); -function ident(text) { - let lines = text.split("\n"); - for (let i=0; i=0; i--) { + if (typeof ctx.scopes[i][n] !== "undefined") return ctx.scopes[i][n]; + } + return -1; } - return lines.join("\n"); } function gen(ctx, ast) { @@ -38,67 +121,75 @@ function gen(ctx, ast) { return genPin(ctx, ast); } else if (ast.type == "OP") { if (ast.op == "=") { - return genVarAssignement(ctx, ast); + return genAssignement(ctx, ast); } else if (ast.op == "<--") { - return genVarAssignement(ctx, ast); + return genAssignement(ctx, ast); } else if (ast.op == "<==") { - return genSignalAssignConstrain(ctx, ast); + return genSignalAssignConstraint(ctx, ast); } else if (ast.op == "===") { - return genConstrain(ctx, ast); + return genConstraint(ctx, ast); } else if (ast.op == "+=") { return genVarAddAssignement(ctx, ast); } else if (ast.op == "*=") { return genVarMulAssignement(ctx, ast); } else if (ast.op == "+") { - return genAdd(ctx, ast); + return genOp(ctx, ast, "add", 2); } else if (ast.op == "-") { - return genSub(ctx, ast); + return genOp(ctx, ast, "sub", 2); } else if (ast.op == "UMINUS") { - return genUMinus(ctx, ast); + return genOp(ctx, ast, "neg", 1); } else if (ast.op == "*") { - return genMul(ctx, ast); + return genOp(ctx, ast, "mul", 2); } else if (ast.op == "%") { - return genMod(ctx, ast); + return genOp(ctx, ast, "mod", 2); } else if (ast.op == "PLUSPLUSRIGHT") { - return genPlusPlusRight(ctx, ast); + return genOpOp(ctx, ast, "add", "RIGHT"); } else if (ast.op == "PLUSPLUSLEFT") { - return genPlusPlusLeft(ctx, ast); + return genOpOp(ctx, ast, "add", "LEFT"); } else if (ast.op == "MINUSMINUSRIGHT") { - return genMinusMinusRight(ctx, ast); + return genOpOp(ctx, ast, "sub", "RIGHT"); } else if (ast.op == "MINUSMINUSLEFT") { - return genMinusMinusLeft(ctx, ast); + return genOpOp(ctx, ast, "sub", "LEFT"); } else if (ast.op == "**") { - return genExp(ctx, ast); + return genOp(ctx, ast, "pow", 2); } else if (ast.op == "/") { - return genDiv(ctx, ast); + return genOp(ctx, ast, "div", 2); } else if (ast.op == "\\") { - return genIDiv(ctx, ast); + return genOp(ctx, ast, "idiv", 2); } else if (ast.op == "&") { - return genBAnd(ctx, ast); + return genOp(ctx, ast, "band", 2); + } else if (ast.op == "|") { + return genOp(ctx, ast, "bor", 2); + } else if (ast.op == "^") { + return genOp(ctx, ast, "bxor", 2); + } else if (ast.op == "~") { + return genOp(ctx, ast, "bnot", 1); } else if (ast.op == "&&") { - return genAnd(ctx, ast); + return genOp(ctx, ast, "land", 2); } else if (ast.op == "||") { - return genOr(ctx, ast); + return genOp(ctx, ast, "lor", 2); + } else if (ast.op == "!") { + return genOp(ctx, ast, "lnot", 1); } else if (ast.op == "<<") { - return genShl(ctx, ast); + return genOp(ctx, ast, "shl", 2); } else if (ast.op == ">>") { - return genShr(ctx, ast); + return genOp(ctx, ast, "shr", 2); } else if (ast.op == "<") { - return genLt(ctx, ast); + return genOp(ctx, ast, "lt", 2); } else if (ast.op == ">") { - return genGt(ctx, ast); + return genOp(ctx, ast, "gt", 2); } else if (ast.op == "<=") { - return genLte(ctx, ast); + return genOp(ctx, ast, "leq", 2); } else if (ast.op == ">=") { - return genGte(ctx, ast); + return genOp(ctx, ast, "geq", 2); } else if (ast.op == "==") { - return genEq(ctx, ast); + return genOp(ctx, ast, "eq", 2); } else if (ast.op == "!=") { - return genNeq(ctx, ast); + return genOp(ctx, ast, "neq", 2); } else if (ast.op == "?") { return genTerCon(ctx, ast); } else { - error(ctx, ast, "Invalid operation: " + ast.op); + ctx.throwError(ast, "Invalid operation: " + ast.op); } } else if (ast.type == "DECLARE") { if (ast.declareType == "COMPONENT") { @@ -110,526 +201,1016 @@ function gen(ctx, ast) { } else if (ast.declareType == "VARIABLE") { return genDeclareVariable(ctx, ast); } else { - error(ctx, ast, "Invalid declaration: " + ast.declareType); + ctx.throwError(ast, "Invalid declaration: " + ast.declareType); } } else if (ast.type == "FUNCTIONCALL") { return genFunctionCall(ctx, ast); } else if (ast.type == "BLOCK") { return genBlock(ctx, ast); } else if (ast.type == "COMPUTE") { - return genCompute(ctx, ast); + return gen(ctx, ast.body); } else if (ast.type == "FOR") { - return genFor(ctx, ast); + return genLoop(ctx, ast); } else if (ast.type == "WHILE") { - return genWhile(ctx, ast); + return genLoop(ctx, ast); } else if (ast.type == "IF") { return genIf(ctx, ast); } else if (ast.type == "RETURN") { return genReturn(ctx, ast); - } else if (ast.type == "TEMPLATEDEF") { - return genTemplateDef(ctx, ast); - } else if (ast.type == "FUNCTIONDEF") { - return genFunctionDef(ctx, ast); } else if (ast.type == "INCLUDE") { return genInclude(ctx, ast); } else if (ast.type == "ARRAY") { return genArray(ctx, ast); } else { - error(ctx, ast, "GEN -> Invalid AST node type: " + ast.type); + ctx.throwError(ast, "GEN -> Invalid AST node type: " + ast.type); } } +function genBlock(ctx, ast) { + ctx.scopes.push({}); + let res = null; + for (let i=0; i=0 ? p2 : undefined)); + } + return codes.join("\n"); } +function genSrcComment(ctx, ast) { + const code = getSource(ctx, ast); + ctx.codeBuilder.addComment(code); +} -function getScope(ctx, name) { - for (let i=ctx.scopes.length-1; i>=0; i--) { - if (ctx.scopes[i][name]) return ctx.scopes[i][name]; +function genLoopSrcComment(ctx, ast) { + if (ast.type == "FOR") { + const init = getSource(ctx, ast.init); + const condition = getSource(ctx, ast.condition); + const step = getSource(ctx, ast.step); + ctx.codeBuilder.addComment(`for (${init};${condition};${step})`); + } else if (ast.type == "WHILE") { + const condition = getSource(ctx, ast.condition); + ctx.codeBuilder.addComment(`while (${condition})`); + } else { + assert(false, "Invalid loop type: "+ ast.type); } - return null; } +function genIfSrcComment(ctx, ast) { + const condition = getSource(ctx, ast.condition); + ctx.codeBuilder.addComment(`if (${condition})`); +} -function genFunctionCall(ctx, ast) { - let S = "["; - for (let i=0; i0) S += ","; - S += gen(ctx, ast.params[i]); - } - S+="]"; - return `ctx.callFunction("${ast.name}", ${S})`; +function genDeclareComponent(ctx, ast) { + return ast.refId; } -function genBlock(ctx, ast) { - let body = ""; - for (let i=0; i0) { + sizes=[]; + for (let i=0; i< ast.name.selectors.length; i++) { + const sizeRef = gen(ctx, ast.name.selectors[i]); + const size = ctx.refs[sizeRef]; + if (size.sizes[0] != 1) return ctx.throwError(ast, "A selector cannot be an array"); + if (size.used) return ctx.throwError(ast, "Variable size variables not allowed"); + sizes.push(size.value[0]); + } + sizes = utils.accSizes(sizes); + } else { + sizes = [1,0]; + } - ctx.scope = {}; - for (let i=0; i< ast.params.length; i++) { - ctx.f.addParam(ast.params[i].name, "i32"); - ctx.scope[ast.params[i].name] = { - type: "PARAM", - sels: ast.params[i].sels, - getter: () => { return ctx.c.getLocal(ast.params[i].name); }, - setter: (v) => { return ctx.c.setLocal(ast.params[i].name, v); } - }; + if ((!v.sizes)&&(sizes)) { + v.sizes = sizes; + v.value = new Array(sizes[0]); } - genBlock(ctx, ast.block); + if (v.sizes) { + if (!utils.sameSizes(v.sizes, sizes)) return ctx.throwError(ast, "Redefined a var with different sized"); + } - ctx.scope = null; - ctx.c = null; - ctx.f = null; + return ast.refId; } -function genFunctionDef(ctx, ast) { - let S = "function(ctx) "; +function genNumber(ctx, ast) { + return newRef(ctx, "BIGINT", "_num", bigInt(ast.value)); +} - const newScope = {}; - const params = []; - for (let i=0; i< ast.params.length; i++) { - newScope[ast.params[i]] = { type: "VARIABLE" }; - params.push(ast.params[i]); - } - ctx.scopes.push(newScope); - S += genBlock(ctx, ast.block); - ctx.scopes.pop(); -// const scope = ctx.scopes[0]; // Scope for templates is top - const scope = ctx.scopes[ctx.scopes.length-1]; +function genGetOffset(ctx, refOffset, vSizes, sels) { - scope[ast.name] = { - type: "FUNCTION" - }; + let offsets = []; + let offset; - ctx.functions[ast.name] = S; - ctx.functionParams[ast.name] = params; - return ""; -} + let isConstant = true; + let constant = 0; + let isRef = false; -function genFor(ctx, ast) { - ctx.scopes.push({}); - const init = gen(ctx, ast.init); - if (ctx.error) return; - const condition = gen(ctx, ast.condition); - if (ctx.error) return; - const step = gen(ctx, ast.step); - if (ctx.error) return; - const body = gen(ctx, ast.body); - if (ctx.error) return; - ctx.scopes.pop(); - return `for (${init};bigInt(${condition}).neq(bigInt(0));${step}) { \n${body}\n }\n`; -} + if (refOffset) { + offset = ctx.refs[refOffset]; + if (offset.used) { + offsets.push([["RI", offset.label], ["V", 1]]); + isRef = true; + isConstant = false; + } else { + offsets.push([["V", parseInt(offset.value)], ["V", 1]]); + constant = parseInt(offset.value); + } + } -function genWhile(ctx, ast) { - const condition = gen(ctx, ast.condition); - if (ctx.error) return; - const body = gen(ctx, ast.body); - if (ctx.error) return; - return `while (bigInt(${condition}).neq(bigInt(0))) {\n${body}\n}\n`; -} + if ((sels)&&(sels.length>0)) { -function genCompute(ctx, ast) { - const body = gen(ctx, ast.body); - if (ctx.error) return; - return `{\n${body}\n}\n`; -} + let iSizes; + if (Array.isArray(vSizes)) { + iSizes = { + used: false, + sizes: vSizes + }; + } else { + iSizes = ctx.refs[vSizes]; + } -function genIf(ctx, ast) { - const condition = gen(ctx, ast.condition); - if (ctx.error) return; - const thenBody = gen(ctx, ast.then); - if (ctx.error) return; - if (ast.else) { - const elseBody = gen(ctx, ast.else); - if (ctx.error) return; - return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n} else {\n${elseBody}\n}\n`; - } else { - return `if (bigInt(${condition}).neq(bigInt(0))) {\n${thenBody}\n}\n`; + for (let i=0; i 0) isRef = false; + } else { + isConstant = false; + isRef = false; + } + } } -} + if (isConstant) { + const o = newRef(ctx, "INT", "_offset", constant); + return o; + } + if (isRef) { + return refOffset; + } + const resRef = newRef(ctx, "INT", "_offset"); + instantiateRef(ctx, resRef); -function genReturn(ctx, ast) { - const value = gen(ctx, ast.value); - if (ctx.error) return; - return `return ${value};`; + const res = ctx.refs[resRef]; + + ctx.codeBuilder.calcOffset(res.label, offsets); + return resRef; } -function genDeclareComponent(ctx, ast) { - const scope = ctx.scopes[ctx.scopes.length - 1]; +function genVariable(ctx, ast) { + const v = ctx.refs[ast.refId]; - if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); - if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name); + const l = ast.selectors ? ast.selectors.length : 0; - scope[ast.name.name] = { - type: "COMPONENT" - }; + if (v.type == "SIGNAL") { + let vOffset; + if (l>0) { + const vsOffset = genGetSignalOffset(ctx, -1, ast.name); + const vsSizes = genGetSignalSizes(ctx, -1, ast.name); + vOffset = genGetOffset(ctx, vsOffset, vsSizes, ast.selectors ); + } else { + vOffset = genGetSignalOffset(ctx, -1, ast.name); + } - return ""; + const resRef = newRef(ctx, "BIGINT", "_sigValue"); + const res = ctx.refs[resRef]; + instantiateRef(ctx, resRef); + ctx.codeBuilder.getSignal(res.label, ["CC"], toRefA_Int1(ctx, ast, vOffset)); + return resRef; + + } else if (v.type == "BIGINT") { + const refOffset = genGetOffset(ctx, 0, v.sizes, ast.selectors ); + const offset = ctx.refs[refOffset]; + if (v.used) { + if (offset.used) { + const refRes = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); + const res = ctx.refs[refRes]; + res.used = true; + ctx.fnBuilder.definePFrElement(res.label); + ctx.codeBuilder.assign(res.label, ["R", v.label], ["R", offset.label]); + return refRes; + } else if ((offset.value[0]>0)||(l>0)) { + const refRes = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); + const res = ctx.refs[refRes]; + res.used = true; + ctx.fnBuilder.definePFrElement(res.label); + ctx.codeBuilder.assign(res.label, ["R", v.label], ["V", offset.value[0]]); + return refRes; + } else { + return ast.refId; + } + } else { + if (offset.used) { + instantiateRef(ctx, ast.refId, v.value); + const resRef = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); + const res = ctx.refs[resRef]; + res.used = true; + ctx.fnBuilder.definePFrElement(res.label); + ctx.codeBuilder.assign(res.label, ["R", v.label], ["R", offset.label]); + return resRef; + } else { + // return newSubRef(ctx, ast.name, ast.selectors); + return newRef(ctx, "BIGINT", "_v", v.value.slice(offset.value[0], offset.value[0] + v.sizes[l]),v.sizes.slice(l)); + } + } + } } -function genDeclareSignal(ctx, ast) { - const scope = ctx.scopes[ctx.scopes.length-1]; - if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); - if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name); +function genPin(ctx, ast) { + let vcIdx; + if (ast.component.selectors.length>0) { + const vcOffset = genGetSubComponentOffset(ctx, -1, ast.component.name); + const vcSizes = genGetSubComponentSizes(ctx, -1, ast.component.name); + vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.component.selectors ); + } else { + vcIdx = genGetSubComponentOffset(ctx, -1, ast.component.name); + } - scope[ast.name.name] = { - type: "SIGNAL" - }; + let vsIdx; + if (ast.pin.selectors.length>0) { + const vsOffset = genGetSignalOffset(ctx, vcIdx, ast.pin.name); + const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.pin.name); + vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.pin.selectors ); + } else { + vsIdx = genGetSignalOffset(ctx, vcIdx, ast.pin.name); + } - return ""; + const resRef = newRef(ctx, "BIGINT", "_sigValue"); + const res = ctx.refs[resRef]; + instantiateRef(ctx, resRef); + ctx.codeBuilder.getSignal( + res.label, + toRefA_Int1(ctx, ast.component, vcIdx), + toRefA_Int1(ctx, ast.pin, vsIdx) + ); + return resRef; } -function genDeclareVariable(ctx, ast) { - const scope = ctx.scopes[ctx.scopes.length-1]; +function genGetSubComponentOffset(ctx, cIdxRef, label) { + const refOffset = newRef(ctx, "INT", "_compIdx"); + const offset = ctx.refs[refOffset]; + instantiateRef(ctx, refOffset); + + const h = utils.fnvHash(label); + let c; + if (cIdxRef>=0) { + const cIdx = ctx.refs[cIdxRef]; + if (cIdx.used) { + c = ["R", cIdx.label]; + } else { + c = ["V", cIdx.value[0]]; + } + } else { + c = ["CC"]; + } + ctx.codeBuilder.getSubComponentOffset(offset.label, c, h, label); + return refOffset; +} - if (ast.name.type != "VARIABLE") return error(ctx, ast, "Invalid component name"); - if (getScope(ctx, ast.name.name)) return error(ctx, ast, "Name already exists: "+ast.name.name); +function genGetSubComponentSizes(ctx, cIdxRef, label) { + const sizesRef = newRef(ctx, "SIZES", "_compSizes"); + const sizes = ctx.refs[sizesRef]; + instantiateRef(ctx, sizesRef); + + const h = utils.fnvHash(label); + let c; + if (cIdxRef>=0) { + const cIdx = ctx.refs[cIdxRef]; + if (cIdx.used) { + c = ["R", cIdx.label]; + } else { + c = ["V", cIdx.value[0]]; + } + } else { + c = ["CC"]; + } + ctx.codeBuilder.getSubComponentSizes(sizes.label, c, h, label); + return sizesRef; +} - scope[ast.name.name] = { - type: "VARIABLE" - }; +function genGetSignalOffset(ctx, cIdxRef, label) { - return ""; + let constCIdx = null; + if (cIdxRef>=0) { + const cIdx = ctx.refs[cIdxRef]; + if (!cIdx.used) { + constCIdx = cIdx.value.toString()+"_"+label; + } + } else { + constCIdx = "_cIdx_"+label; + } + if (constCIdx && ctx.getSignalOffsetCache[constCIdx]) return ctx.getSignalOffsetCache[constCIdx]; + + const refOffset = newRef(ctx, "INT", "_" + label + "_sigIdx_"); + const offset = ctx.refs[refOffset]; + instantiateRef(ctx, refOffset); + + const h = utils.fnvHash(label); + let c; + if (cIdxRef>=0) { + const cIdx = ctx.refs[cIdxRef]; + if (cIdx.used) { + c = ["R", cIdx.label]; + } else { + c = ["V", cIdx.value[0]]; + } + } else { + c = ["CC"]; + } + if (constCIdx) { + ctx.fnBuilder.initializeSignalOffset(offset.label, c, h, label); + ctx.getSignalOffsetCache[constCIdx] = refOffset; + } else { + ctx.codeBuilder.getSignalOffset(offset.label, c, h, label); + } + return refOffset; } -function genNumber(ctx, ast) { - return `"${ast.value.toString()}"`; -} +function genGetSignalSizes(ctx, cIdxRef, label) { + let constCIdx = null; + if (cIdxRef>=0) { + const cIdx = ctx.refs[cIdxRef]; + if (utils.isDefined(cIdx.value)) { + constCIdx = cIdx.value.toString()+"_"+label; + } + } else { + constCIdx = "_cIdx_"+label; + } + if (constCIdx && ctx.getSignalSizesCache[constCIdx]) return ctx.getSignalSizesCache[constCIdx]; -function genVariable(ctx, ast) { - const v = getScope(ctx, ast.name); - const sels = []; - for (let i=0; i=0) { + const cIdx = ctx.refs[cIdxRef]; + if (cIdx.used) { + c = ["R", cIdx.label]; + } else { + c = ["V", cIdx.value[0]]; + } + } else { + c = ["CC"]; } - if (v.type == "VARIABLE") { - return `ctx.getVar("${ast.name}",[${sels.join(",")}])`; - } else if (v.type == "SIGNAL") { - return `ctx.getSignal("${ast.name}", [${sels.join(",")}])`; + const h = utils.fnvHash(label); + if (constCIdx) { + ctx.fnBuilder.initializeSignalSizes(sizes.label, c, h, label); + ctx.getSignalSizesCache[constCIdx] = refSizes; } else { - error(ctx, ast, "Invalid Variable type"); + ctx.codeBuilder.getSignalSizes(sizes.label, c, h, label); } + return refSizes; } -function genPin(ctx, ast) { - let componentName = ast.component.name; - let componentSelectors = []; - for (let i=0; i0) { + const vcOffset = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name); + const vcSizes = genGetSubComponentSizes(ctx, -1, ast.values[0].component.name); + vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors ); + } else { + vcIdx = genGetSubComponentOffset(ctx, -1, ast.values[0].component.name); } - componentSelectors = "["+ componentSelectors.join(",") + "]"; - let pinName = ast.pin.name; - let pinSelectors = []; - for (let i=0; i0) { + const vsOffset = genGetSignalOffset(ctx, vcIdx, ast.values[0].pin.name); + const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name); + vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors ); + } else { + vsIdx = genGetSignalOffset(ctx, vcIdx, ast.values[0].pin.name); } - pinSelectors = "["+ pinSelectors.join(",") + "]"; - return `ctx.getPin("${componentName}", ${componentSelectors}, "${pinName}", ${pinSelectors})`; + + const vVal = gen(ctx, ast.values[1]); + + ctx.codeBuilder.setSignal( + toRefA_Int1(ctx, ast.values[0].component, vcIdx), + toRefA_Int1(ctx, ast.values[0].pin, vsIdx), + toRefA_Fr1(ctx, ast.values[1], vVal) + ); + + return vVal; } -function genVarAssignement(ctx, ast) { +function genSignalAssignmen(ctx, ast, lName, sels, rName) { + let vsIdx; + const signal = ctx.refs[lName]; + if (sels.length>0) { + const vsOffset = genGetSignalOffset(ctx, -1, signal.label); + const vsSizes = genGetSignalSizes(ctx, -1, signal.label); + vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels ); + } else { + vsIdx = genGetSignalOffset(ctx, -1, signal.label); + } - const sels = []; - let vName; + ctx.codeBuilder.setSignal( + ["CC"], + toRefA_Int1(ctx, ast.values[0], vsIdx), + toRefA_Fr1(ctx, ast.values[1], rName) + ); +} - if (ctx.error) return; +function genVarAssignment(ctx, ast, lRef, sels, rRef) { + + const left = ctx.refs[lRef]; + const right = ctx.refs[rRef]; + if (!utils.isDefined(left.sizes)) { + left.sizes = right.sizes; + left.value = new Array(left.sizes[0]); + } + if (!utils.sameSizes(left.sizes.slice(sels.length), right.sizes)) return ctx.throwError(ast, "Sizes do not match"); + + const oRef = genGetOffset(ctx, 0, left.sizes, sels); + const offset = ctx.refs[oRef]; + + let instantiated=false; + if (left.used) { + instantiateRef(ctx, rRef, right.value); + instantiated=true; + } else if (right.used) { + if (sels.length == 0) { + instantiateRef(ctx,lRef); + } else { + instantiateRef(ctx,lRef, left.value); + } + instantiated=true; + } else if (offset.used) { + instantiateRef(ctx, rRef, right.value); + if (sels.length == 0) { + instantiateRef(ctx,lRef); + } else { + instantiateRef(ctx,lRef, left.value); + } + instantiated=true; + } - if (ast.values[0].type == "PIN") { - let componentName = ast.values[0].component.name; - let componentSelectors = []; - for (let i=0; i0) { + for (let i=0; ilog(${val.label});`; + return vRef; + } + const params = []; + for (let i=0; i { + if (ast.type == "OP") { + if (["=", "+=", "*=", "PLUSPLUSLEFT", "PLUSPLUSRIGHT", "MINUSMINUSLEFT", "MINUSMINUSRIGHT"].indexOf(ast.op) >= 0) { + let refId; + refId = ast.values[0].refId; + const ref = ctx.refs[refId]; + instantiateRef(ctx, refId, ref.value); + } + } + }); + ctx.conditionalCode = 1; + } else { + ctx.conditionalCode ++; + } } -function genBAnd(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `bigInt(${a}).and(bigInt(${b})).and(__MASK__)`; +function leaveConditionalCode(ctx) { + assert(ctx.conditionalCode, "Leaving conditional code too many times"); + ctx.conditionalCode --; + if (!ctx.conditionalCode) { + delete ctx.conditionalCode; + } } -function genAnd(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `((bigInt(${a}).neq(bigInt(0)) && bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`; -} +function genLoop(ctx, ast) { + genLoopSrcComment(ctx, ast); + let inLoop = false; -function genOr(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `((bigInt(${a}).neq(bigInt(0)) || bigInt(${b}).neq(bigInt(0))) ? bigInt(1) : bigInt(0))`; -} + if (ast.init) { + gen(ctx, ast.init); + if (ctx.error) return; + } -function genShl(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shl(bigInt(${b})).and(__MASK__)`; -} + let end=false; -function genShr(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `bigInt(${b}).greater(bigInt(256)) ? 0 : bigInt(${a}).shr(bigInt(${b})).and(__MASK__)`; -} + let loopCondRef; + let loopCond; + let bodyCode; -function genMod(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); + + const condRef = gen(ctx, ast.condition); if (ctx.error) return; - return `bigInt(${a}).mod(bigInt(${b}))`; + + const cond = ctx.refs[condRef]; + if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array"); + + if (cond.used) { + inLoop = true; + enterConditionalCode(ctx, ast); + + loopCondRef = newRef(ctx, "BIGINT", "_loopCond"); + loopCond = ctx.refs[loopCondRef]; + loopCond.used = true; + ctx.fnBuilder.definePFrElement(loopCond.label); + ctx.codeBuilder.assign(loopCond.label, ["R", cond.label], ["V", 0]); + } else { + if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned"); + if (cond.value[0].isZero()) end=true; + } + + + while (!end) { + + const oldCodeBuilder = ctx.codeBuilder; + ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder(); + + if (ast.body.type != "BLOCK") genSrcComment(ctx, ast.body); + gen(ctx, ast.body); + if (ctx.error) return; + + if (ast.step) { + gen(ctx, ast.step); + if (ctx.error) return; + } + + const condRef2 = gen(ctx, ast.condition); + if (ctx.error) return; + + const cond2 = ctx.refs[condRef2]; + + if ((!cond2.used) &&(ctx.codeBuilder.hasCode())) { + instantiateRef(ctx, condRef2, cond2.value); + } + + if (!inLoop) { + if (cond2.used) { + oldCodeBuilder.concat(ctx.codeBuilder); + ctx.codeBuilder = oldCodeBuilder; + inLoop = true; + enterConditionalCode(ctx, ast); + + loopCondRef = newRef(ctx, "BIGINT", "_loopCond"); + loopCond = ctx.refs[loopCondRef]; + loopCond.used = true; + ctx.fnBuilder.definePFrElement(loopCond.label); + ctx.codeBuilder.assign(loopCond.label, ["R", cond2.label], ["V", 0]); + + } else { + oldCodeBuilder.concat(ctx.codeBuilder); + ctx.codeBuilder = oldCodeBuilder; + if (cond2.value[0].isZero()) end=true; + } + } else { + ctx.codeBuilder.assign(loopCond.label, ["R", cond2.label], ["V", 0]); + bodyCode = ctx.codeBuilder; + ctx.codeBuilder = oldCodeBuilder; + end=true; + } + } + if (inLoop) { + ctx.codeBuilder.addLoop(loopCond.label, bodyCode); + leaveConditionalCode(ctx); + } + ctx.scopes.pop(); } -function genLt(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); +function genIf(ctx, ast) { + genIfSrcComment(ctx, ast); + const condRef = gen(ctx, ast.condition); if (ctx.error) return; - return `bigInt(${a}).lt(bigInt(${b})) ? 1 : 0`; + const cond = ctx.refs[condRef]; + if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array"); + + if (cond.used) { + let thenCode, elseCode; + enterConditionalCode(ctx, ast); + + const oldCodeBuilder = ctx.codeBuilder; + ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder(); + + gen(ctx, ast.then); + if (ctx.error) return; + + thenCode = ctx.codeBuilder; + + if (ast.else) { + ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder(); + gen(ctx, ast.else); + if (ctx.error) return; + elseCode = ctx.codeBuilder; + } + + ctx.codeBuilder = oldCodeBuilder; + ctx.codeBuilder.addIf(cond.label, thenCode, elseCode); + + leaveConditionalCode(ctx); + + } else { + if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned"); + if (!cond.value[0].isZero()) { + gen(ctx, ast.then); + } else { + if (ast.else) { + gen(ctx, ast.else); + } + } + } } -function genGt(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `bigInt(${a}).gt(bigInt(${b})) ? 1 : 0`; + +function genReturn(ctx, ast) { + const vRef = gen(ctx, ast.value); + const v= ctx.refs[vRef]; + if (ctx.returnSizes) { + if (!utils.sameSizes(v.sizes, ctx.returnSizes)) return ctx.throwError(ast, "Diferent return sizes"); + } else { + ctx.returnSizes = v.sizes; + } + if (ctx.conditionalCode) { + instantiateRef(ctx, vRef, v.value); + } + if (v.used) { + ctx.codeBuilder.copyNRet(["R", v.label], v.sizes[0]); + } else { + if (!utils.isDefined(v.value)) return ctx.throwError(ast, "Returning an unknown value"); + if (!utils.isDefined(ctx.returnValue)) { + ctx.returnValue = v.value; + } + } + ctx.codeBuilder.ret(); + return vRef; } -function genLte(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `bigInt(${a}).lesserOrEquals(bigInt(${b})) ? 1 : 0`; + + +function genSignalAssignConstraint(ctx, ast) { + const res = genAssignement(ctx, ast); + // genConstraint(ctx, ast); + return res; + // return genVarAssignement(ctx, ast); } -function genGte(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `bigInt(${a}).greaterOrEquals(bigInt(${b})) ? 1 : 0`; +function genVarAddAssignement(ctx, ast) { + return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]}); } -function genEq(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `(bigInt(${a}).eq(bigInt(${b})) ? 1 : 0)`; +function genVarMulAssignement(ctx, ast) { + return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]}); } -function genNeq(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); +function genOpOp(ctx, ast, op, lr) { + + if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable"); + + const vRef = ast.values[0].refId; + + const vevalRef = gen(ctx, ast.values[0]); if (ctx.error) return; - return `(bigInt(${a}).eq(bigInt(${b})) ? 0 : 1)`; + const veval = ctx.refs[vevalRef]; + + if (veval.type != "BIGINT") return ctx.throwError(ast, "incrementing a non variable"); + + const resRef = newRef(ctx, "BIGINT", "_tmp"); + const res = ctx.refs[resRef]; + if (veval.used) { + instantiateRef(ctx, resRef); + ctx.codeBuilder.fieldOp(res.label, op, [["R", veval.label], ["C", ctx.addConstant(bigInt.one)]]); + } else { + res.value = [ctx.field[op](veval.value[0], bigInt(1))]; + } + genVarAssignment(ctx, ast, vRef, ast.values[0].selectors, resRef); + if (lr == "RIGHT") { + return vevalRef; + } else if (lr == "LEFT") { + return resRef; + } } -function genUMinus(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - return `__P__.sub(bigInt(${a})).mod(__P__)`; +function genOp(ctx, ast, op, nOps) { + const vals = []; + const valRefs = []; + + var anyUsed=false; + + for (let i=0; i0) S += ","; - S += gen(ctx, ast.values[i]); - } - S+="]"; - return S; -} diff --git a/src/stream_from_multiarray.js b/src/stream_from_multiarray.js index c9bc3a1..a0cdf0b 100644 --- a/src/stream_from_multiarray.js +++ b/src/stream_from_multiarray.js @@ -8,11 +8,13 @@ module.exports = function streamFromMultiarray(ma) { rs._read = function() { let res; - do { - res = objFromIdx(ma, curIndex); - curIndex = nextIdx(curIndex); - } while (res===""); - rs.push(res); + res = objFromIdx(ma, curIndex); + curIndex = nextIdx(curIndex); + if (res!=null) { + rs.push(res + "\n"); + } else { + rs.push(null); + } }; diff --git a/src/utils.js b/src/utils.js index a6bf11b..f8748da 100644 --- a/src/utils.js +++ b/src/utils.js @@ -15,11 +15,18 @@ module.exports.isDefined = isDefined; module.exports.accSizes2Str = accSizes2Str; function ident(text) { - let lines = text.split("\n"); - for (let i=0; i { await doTest( "inout.circom", @@ -111,7 +112,6 @@ describe("basic cases", function () { ] ); }); - it("function1", async () => { await doTest( "function1.circom", @@ -266,6 +266,7 @@ describe("basic cases", function () { ] ); }); + it("Conditional Ternary operator", async () => { await doTest( "condternary.circom", @@ -277,6 +278,7 @@ describe("basic cases", function () { ] ); }); + it("Compute block", async () => { await doTest( "compute.circom", @@ -337,4 +339,5 @@ describe("basic cases", function () { ] ); }); + });