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