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 ctx.codeBuilder.copyN(left.label, ["R", offset.label], ["R", right.label], right.sizes[0]);
727 } else {
728 ctx.codeBuilder.copyN(left.label, ["V", offset.value[0]], ["R", right.label], right.sizes[0]);
729 }
730 } else {
731 if (offset.value[0]>0) {
732 for (let i=0; i<right.sizes[0]; i++) left.value[offset.value[0] + i] = right.value[i];
733 } else {
734 for (let i=0; i<right.sizes[0]; i++) left.value[i] = right.value[i];
735 }
736 }
737}
738
739function genAssignement(ctx, ast) {
740
741 let lRef;
742 let sels;
743
744 if (ctx.error) return;
745
746 if (ast.values[0].type == "PIN") return genPinAssignement(ctx, ast);
747
748 if (ast.values[0].type == "DECLARE") {
749 lRef = gen(ctx, ast.values[0]);
750 if (ctx.error) return;
751 sels = [];
752 } else {
753 lRef = ast.values[0].refId;
754 sels = ast.values[0].selectors;
755 }
756
757
758 const left = ctx.refs[lRef];
759 if (!left) return ctx.throwError(ast, "Variable does not exists: "+ast.values[0].name);
760
761 // Component instantiation is already done.
762 if (left.type == "COMPONENT") return;
763
764 const rRef = gen(ctx, ast.values[1]);
765 if (ctx.error) return;
766
767 if (left.type == "SIGNAL") return genSignalAssignmen(ctx, ast, lRef, sels, rRef);
768
769 if (left.type == "BIGINT") return genVarAssignment(ctx, ast, lRef, sels, rRef);
770
771 return ctx.throwError(ast, "Assigning to invalid");
772}
773
774function toRefA_Fr1(ctx, ast, aRef) {
775 const a = ctx.refs[aRef];
776 if (a.sizes[0] != 1) return ctx.throwError(ast, "Expected only one element");
777 if (a.used) {
778 if (a.type == "BIGINT") {
779 return ["R", a.label];
780 } else {
781 assert(false);
782 }
783 } else {
784 return ["C", ctx.addConstant(a.value[0])];
785 }
786}
787
788function toRefA_Int1(ctx, ast, aRef) {
789 if (aRef <0) return ["CC"];
790 const a = ctx.refs[aRef];
791 if (a.sizes[0] != 1) return ctx.throwError(ast, "Expected only one element");
792 if (a.used) {
793 if (a.type == "INT") {
794 return ["RI", a.label];
795 } else if (a.type == "BIGINT") {
796 return ["R", a.label];
797 } else {
798 assert(false);
799 }
800 } else {
801 return ["V", a.value[0]];
802 }
803}
804
805
806function genConstraint(ctx, ast) {
807 const aRef = gen(ctx, ast.values[0]);
808 if (ctx.error) return;
809 const bRef = gen(ctx, ast.values[1]);
810 if (ctx.error) return;
811 const strErr = ast.fileName + ":" + ast.first_line + ":" + ast.first_column;
812
813 ctx.codeBuilder.checkConstraint(toRefA_Fr1(ctx, ast.values[0], aRef), toRefA_Fr1(ctx, ast.values[1], bRef), strErr);
814}
815
816
817function genArray(ctx, ast) {
818 let subSizes;
819 let instantiate = false;
820 if (ast.values.length == 0) return ctx.throwError(ast, "Arrays with zero elements not allowed");
821 const value = [];
822 const refs = [];
823 for (let i=0; i<ast.values.length; i++) {
824 const eRef = gen(ctx, ast.values[i]);
825 const e = ctx.refs[eRef];
826
827 if (i==0) {
828 subSizes = e.sizes;
829 } else {
830 if (!utils.sameSizes(subSizes, e.sizes)) return ctx.throwError(ast, "Heteroeneus array not allowed");
831 }
832 if (e.used) {
833 instantiate = true;
834 }
835 if (!instantiate) {
836 value.push(...e.value);
837 }
838 refs.push(eRef);
839 }
840
841 const newSize = [subSizes[0]*ast.values.length , ...subSizes];
842
843 if (instantiate) {
844 const rRef = newRef(ctx, "BIGINT", "_arr", null, newSize);
845 instantiateRef(ctx, rRef);
846 for (let i=0; i<ast.values.length; i++) {
847 const v = ctx.refs[refs[i]];
848 const r = ctx.refs[rRef];
849 ctx.codeBuilder.copyN(r.label, ["V", i*subSizes[0]], ["R", v.label], subSizes[0]);
850 }
851 return rRef;
852 } else {
853 const rRef = newRef(ctx, "BIGINT", "_arr", value, newSize);
854 return rRef;
855 }
856
857}
858
859
860function genFunctionCall(ctx, ast) {
861 if (ast.name == "log") {
862 const vRef = gen(ctx, ast.params[0]);
863 ctx.codeBuilder.log(toRefA_Fr1(ctx, ast.params[0], vRef));
864 return vRef;
865 }
866 const params = [];
867 for (let i=0; i<ast.params.length; i++) {
868 const pRef = gen(ctx, ast.params[i]);
869 params.push(ctx.refs[pRef]);
870 }
871
872 const fn = ctx.buildFunction(ast.name, params);
873 if (ctx.error) return;
874
875 if (fn.type == "VARVAL_CONSTSIZE") {
876 const resRef = newRef(ctx, "BIGINT", `_ret${ast.name}`, null, fn.returnSizes);
877 const res = ctx.refs[resRef];
878 instantiateRef(ctx, resRef);
879
880 const paramLabels = [];
881 for (let i=0; i<params.length; i++) {
882 if (params[i].used) paramLabels.push(params[i].label);
883 }
884
885 ctx.codeBuilder.fnCall(fn.fnName, res.label, paramLabels);
886
887 return resRef;
888 } else {
889 const res = newRef(ctx, "BIGINT", "_retVal", fn.returnValue, fn.returnSizes);
890 return res;
891 }
892}
893
894function enterConditionalCode(ctx, ast) {
895 if (!ctx.conditionalCode) {
896 iterateAST(ast, (ast) => {
897 if (ast.type == "OP") {
898 if (["=", "+=", "*=", "PLUSPLUSLEFT", "PLUSPLUSRIGHT", "MINUSMINUSLEFT", "MINUSMINUSRIGHT"].indexOf(ast.op) >= 0) {
899 let refId;
900 refId = ast.values[0].refId;
901 const ref = ctx.refs[refId];
902 instantiateRef(ctx, refId, ref.value);
903 }
904 }
905 });
906 ctx.conditionalCode = 1;
907 } else {
908 ctx.conditionalCode ++;
909 }
910}
911
912function leaveConditionalCode(ctx) {
913 assert(ctx.conditionalCode, "Leaving conditional code too many times");
914 ctx.conditionalCode --;
915 if (!ctx.conditionalCode) {
916 delete ctx.conditionalCode;
917 }
918}
919
920function genLoop(ctx, ast) {
921 genLoopSrcComment(ctx, ast);
922 let inLoop = false;
923
924 if (ast.init) {
925 gen(ctx, ast.init);
926 if (ctx.error) return;
927 }
928
929 let end=false;
930
931 let loopCondRef;
932 let loopCond;
933 let bodyCode;
934
935
936 const condRef = gen(ctx, ast.condition);
937 if (ctx.error) return;
938
939 const cond = ctx.refs[condRef];
940 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
941
942 if (cond.used) {
943 inLoop = true;
944 enterConditionalCode(ctx, ast);
945
946 loopCondRef = newRef(ctx, "BIGINT", "_loopCond");
947 loopCond = ctx.refs[loopCondRef];
948 loopCond.used = true;
949 ctx.fnBuilder.definePFrElement(loopCond.label);
950 ctx.codeBuilder.assign(loopCond.label, ["R", cond.label], ["V", 0]);
951 } else {
952 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
953 if (ctx.F.isZero(cond.value[0])) end=true;
954 }
955
956
957 while (!end) {
958
959 const oldCodeBuilder = ctx.codeBuilder;
960 ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
961
962 if (ast.body.type != "BLOCK") genSrcComment(ctx, ast.body);
963 gen(ctx, ast.body);
964 if (ctx.error) return;
965
966 if (ast.step) {
967 gen(ctx, ast.step);
968 if (ctx.error) return;
969 }
970
971 const condRef2 = gen(ctx, ast.condition);
972 if (ctx.error) return;
973
974 const cond2 = ctx.refs[condRef2];
975
976 if ((!cond2.used) &&(ctx.codeBuilder.hasCode())) {
977 instantiateRef(ctx, condRef2, cond2.value);
978 }
979
980 if (!inLoop) {
981 if (cond2.used) {
982 oldCodeBuilder.concat(ctx.codeBuilder);
983 ctx.codeBuilder = oldCodeBuilder;
984 inLoop = true;
985 enterConditionalCode(ctx, ast);
986
987 loopCondRef = newRef(ctx, "BIGINT", "_loopCond");
988 loopCond = ctx.refs[loopCondRef];
989 loopCond.used = true;
990 ctx.fnBuilder.definePFrElement(loopCond.label);
991 ctx.codeBuilder.assign(loopCond.label, ["R", cond2.label], ["V", 0]);
992
993 } else {
994 oldCodeBuilder.concat(ctx.codeBuilder);
995 ctx.codeBuilder = oldCodeBuilder;
996 if (ctx.F.isZero(cond2.value[0])) end=true;
997 }
998 } else {
999 ctx.codeBuilder.assign(loopCond.label, ["R", cond2.label], ["V", 0]);
1000 bodyCode = ctx.codeBuilder;
1001 ctx.codeBuilder = oldCodeBuilder;
1002 end=true;
1003 }
1004 }
1005 if (inLoop) {
1006 ctx.codeBuilder.addLoop(loopCond.label, bodyCode);
1007 leaveConditionalCode(ctx);
1008 }
1009 ctx.scopes.pop();
1010}
1011
1012function genIf(ctx, ast) {
1013 genIfSrcComment(ctx, ast);
1014 const condRef = gen(ctx, ast.condition);
1015 if (ctx.error) return;
1016 const cond = ctx.refs[condRef];
1017 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
1018
1019 if (cond.used) {
1020 let thenCode, elseCode;
1021 enterConditionalCode(ctx, ast);
1022
1023 const oldCodeBuilder = ctx.codeBuilder;
1024 ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
1025
1026 gen(ctx, ast.then);
1027 if (ctx.error) return;
1028
1029 thenCode = ctx.codeBuilder;
1030
1031 if (ast.else) {
1032 ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
1033 gen(ctx, ast.else);
1034 if (ctx.error) return;
1035 elseCode = ctx.codeBuilder;
1036 }
1037
1038 ctx.codeBuilder = oldCodeBuilder;
1039 ctx.codeBuilder.addIf(cond.label, thenCode, elseCode);
1040
1041 leaveConditionalCode(ctx);
1042
1043 } else {
1044 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
1045 if (!ctx.F.isZero(cond.value[0])) {
1046 gen(ctx, ast.then);
1047 } else {
1048 if (ast.else) {
1049 gen(ctx, ast.else);
1050 }
1051 }
1052 }
1053}
1054
1055
1056function genReturn(ctx, ast) {
1057 const vRef = gen(ctx, ast.value);
1058 const v= ctx.refs[vRef];
1059 if (ctx.returnSizes) {
1060 if (!utils.sameSizes(v.sizes, ctx.returnSizes)) return ctx.throwError(ast, "Diferent return sizes");
1061 } else {
1062 ctx.returnSizes = v.sizes;
1063 }
1064 if (ctx.conditionalCode) {
1065 instantiateRef(ctx, vRef, v.value);
1066 }
1067 if (v.used) {
1068 ctx.codeBuilder.copyNRet(["R", v.label], v.sizes[0]);
1069 } else {
1070 if (!utils.isDefined(v.value)) return ctx.throwError(ast, "Returning an unknown value");
1071 if (!utils.isDefined(ctx.returnValue)) {
1072 ctx.returnValue = v.value;
1073 }
1074 }
1075 ctx.codeBuilder.ret();
1076 return vRef;
1077}
1078
1079
1080
1081function genSignalAssignConstraint(ctx, ast) {
1082 const res = genAssignement(ctx, ast);
1083 // genConstraint(ctx, ast);
1084 return res;
1085 // return genVarAssignement(ctx, ast);
1086}
1087
1088function genVarAddAssignement(ctx, ast) {
1089 return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]});
1090}
1091
1092function genVarMulAssignement(ctx, ast) {
1093 return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]});
1094}
1095
1096function genOpOp(ctx, ast, op, lr) {
1097
1098 if (ast.values[0].type != "VARIABLE") return ctx.throwError(ast, "incrementing a non variable");
1099
1100 const vRef = ast.values[0].refId;
1101
1102 const vevalRef = gen(ctx, ast.values[0]);
1103 if (ctx.error) return;
1104 const veval = ctx.refs[vevalRef];
1105
1106 if (veval.type != "BIGINT") return ctx.throwError(ast, "incrementing a non variable");
1107
1108 const resRef = newRef(ctx, "BIGINT", "_tmp");
1109 const res = ctx.refs[resRef];
1110 if (veval.used) {
1111 instantiateRef(ctx, resRef);
1112 ctx.codeBuilder.fieldOp(res.label, op, [["R", veval.label], ["C", ctx.addConstant(ctx.F.one)]]);
1113 } else {
1114 res.value = [ctx.F[op](veval.value[0], ctx.F.one)];
1115 }
1116 genVarAssignment(ctx, ast, vRef, ast.values[0].selectors, resRef);
1117 if (lr == "RIGHT") {
1118 return vevalRef;
1119 } else if (lr == "LEFT") {
1120 return resRef;
1121 }
1122}
1123
1124function genOp(ctx, ast, op, nOps, adjustBool) {
1125 const vals = [];
1126 const valRefs = [];
1127
1128 var anyUsed=false;
1129
1130 for (let i=0; i<nOps; i++) {
1131 const ref = gen(ctx, ast.values[i]);
1132 if (ctx.error) return;
1133 let v = ctx.refs[ref];
1134 valRefs.push(ref);
1135 vals.push(v);
1136
1137 if (!utils.sameSizes(v.sizes, [1,0])) return ctx.throwError(ast, "Operation cannot be done on an array");
1138 if ( (!v.used)
1139 &&( (!utils.isDefined(v.value))
1140 ||(!utils.isDefined(v.value[0]))))
1141 return ctx.throwError(ast, "Using a not assigned varialble: ");
1142
1143 if (v.used) anyUsed=true;
1144 }
1145
1146 let rRef;
1147 if (anyUsed) {
1148 const params = [];
1149 for (let i=0; i<nOps; i++) {
1150 params.push(toRefA_Fr1(ctx, ast.values[i], valRefs[i]));
1151 }
1152
1153 rRef = newRef(ctx, "BIGINT", "_tmp");
1154 instantiateRef(ctx, rRef);
1155 const r = ctx.refs[rRef];
1156 ctx.codeBuilder.fieldOp(r.label, op, params);
1157 } else {
1158 const params = [];
1159 for (let i=0; i<nOps; i++) {
1160 params.push(ctx.F.e(vals[i].value[0]));
1161 }
1162
1163 rRef = newRef(ctx, "BIGINT", "_tmp", adjustBool ? (ctx.F[op](...params)?ctx.F.one:ctx.F.zero) : ctx.F[op](...params));
1164 }
1165 return rRef;
1166}
1167
1168
1169function genTerCon(ctx, ast) {
1170 const condRef = gen(ctx, ast.values[0]);
1171 if (ctx.error) return;
1172 const cond = ctx.refs[condRef];
1173 if (!utils.sameSizes(cond.sizes, [1,0])) return ctx.throwError(ast.condition, "Operation cannot be done on an array");
1174
1175 if (cond.used) {
1176 let thenCode, elseCode;
1177 enterConditionalCode(ctx, ast);
1178
1179 const rLabel = ctx.getUniqueName("_ter");
1180
1181 ctx.fnBuilder.definePFrElement(rLabel);
1182
1183 const oldCodeBuilder = ctx.codeBuilder;
1184 ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
1185
1186 const thenRef = gen(ctx, ast.values[1]);
1187 if (ctx.error) return;
1188 const then = ctx.refs[thenRef];
1189
1190 let t;
1191 if (then.used) {
1192 t = ["R", then.label];
1193 } else {
1194 if (then.sizes[0] == 1) {
1195 t = ["C", ctx.addConstant(then.value[0])];
1196 } else {
1197 instantiateRef(ctx, thenRef, then.value);
1198 t = ["R", then.label];
1199 }
1200 }
1201
1202 ctx.codeBuilder.assign(rLabel, t, ["V", 0]);
1203
1204 thenCode = ctx.codeBuilder;
1205 ctx.codeBuilder = ctx.fnBuilder.newCodeBuilder();
1206
1207 const elseRef = gen(ctx, ast.values[2]);
1208 if (ctx.error) return;
1209 const els = ctx.refs[elseRef];
1210
1211 let e;
1212 if (els.used) {
1213 e = ["R", els.label];
1214 } else {
1215 if (els.sizes[0] == 1) {
1216 e = ["C", ctx.addConstant(els.value[0])];
1217 } else {
1218 instantiateRef(ctx, elseRef, els.value);
1219 e = ["R", els.label];
1220 }
1221 }
1222
1223 ctx.codeBuilder.assign(rLabel, e, ["V", 0]);
1224 elseCode = ctx.codeBuilder;
1225
1226 ctx.codeBuilder = oldCodeBuilder;
1227
1228 ctx.codeBuilder.addIf(cond.label, thenCode, elseCode);
1229
1230
1231 if (!utils.sameSizes(then.sizes, els.sizes)) return ctx.throwError(ast, "Ternary op must return the same sizes");
1232
1233 const refId = ctx.refs.length;
1234 ctx.refs.push({
1235 type: "BIGINT",
1236 sizes: then.sizes,
1237 used: true,
1238 label: rLabel
1239 });
1240
1241 return refId;
1242
1243 } else {
1244 if (!utils.isDefined(cond.value)) return ctx.throwError(ast, "condition value not assigned");
1245 if (!ctx.F.isZero(cond.value[0])) {
1246 return gen(ctx, ast.values[1]);
1247 } else {
1248 return gen(ctx, ast.values[2]);
1249 }
1250 }
1251}
1252
1253function genInclude(ctx, ast) {
1254 return ast.block ? gen(ctx, ast.block) : "";
1255}
1256
1257