1 /************************************************************************
2  ************************************************************************
3     FAUST compiler
4     Copyright (C) 2003-2018 GRAME, Centre National de Creation Musicale
5     ---------------------------------------------------------------------
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  ************************************************************************
20  ************************************************************************/
21 
22 /*****************************************************************************
23     HISTORY
24     22/01/05 : corrected bug on bool signals cached in float variables
25 *****************************************************************************/
26 
27 #include <math.h>
28 #include <stdio.h>
29 #include <fstream>
30 #include <iostream>
31 #include <sstream>
32 #include <vector>
33 
34 #include "compatibility.hh"
35 #include "compile.hh"
36 #include "compile_scal.hh"
37 #include "floats.hh"
38 #include "ppsig.hh"
39 #include "prim2.hh"
40 #include "privatise.hh"
41 #include "recursivness.hh"
42 #include "sigConstantPropagation.hh"
43 #include "sigPromotion.hh"
44 #include "sigToGraph.hh"
45 #include "signal2vhdlVisitor.hh"
46 #include "sigprint.hh"
47 #include "sigtype.hh"
48 #include "sigtyperules.hh"
49 #include "simplify.hh"
50 #include "timing.hh"
51 #include "xtended.hh"
52 
53 using namespace std;
54 
signal2klass(Klass * parent,const string & name,Tree sig)55 static Klass* signal2klass(Klass* parent, const string& name, Tree sig)
56 {
57     Type t = getCertifiedSigType(sig);  //, NULLENV);
58     if (t->nature() == kInt) {
59         ScalarCompiler C(new SigIntGenKlass(parent, name));
60         C.compileSingleSignal(sig);
61         return C.getClass();
62 
63     } else {
64         ScalarCompiler C(new SigFloatGenKlass(parent, name));
65         C.compileSingleSignal(sig);
66         return C.getClass();
67     }
68 }
69 
70 /*****************************************************************************
71  getFreshID
72  *****************************************************************************/
73 
74 map<string, int> ScalarCompiler::fIDCounters;
75 
getFreshID(const string & prefix)76 string ScalarCompiler::getFreshID(const string& prefix)
77 {
78     if (fIDCounters.find(prefix) == fIDCounters.end()) {
79         fIDCounters[prefix] = 0;
80     }
81     int n               = fIDCounters[prefix];
82     fIDCounters[prefix] = n + 1;
83     return subst("$0$1", prefix, T(n));
84 }
85 
86 /*****************************************************************************
87  prepare
88  *****************************************************************************/
89 
prepare(Tree LS)90 Tree ScalarCompiler::prepare(Tree LS)
91 {
92     startTiming("ScalarCompiler::prepare");
93     //  startTiming("first simplification");
94     //  LS = simplify(LS);
95     //  endTiming("first simplification");
96     startTiming("deBruijn2Sym");
97     Tree L1 = deBruijn2Sym(LS);  // convert debruijn recursion into symbolic recursion
98     endTiming("deBruijn2Sym");
99 
100     startTiming("L1 typeAnnotation");
101     // Annotate L1 with type information (needed by castAndPromotion(), but don't check causality)
102     typeAnnotation(L1, gGlobal->gLocalCausalityCheck);
103     endTiming("L1 typeAnnotation");
104 
105     startTiming("Cast and Promotion");
106     SignalPromotion SP;
107     // SP.trace(true, "Cast");
108     Tree L1b = SP.mapself(L1);
109     endTiming("Cast and Promotion");
110 
111     startTiming("second simplification");
112     Tree L2 = simplify(L1b);  // Simplify by executing every computable operation
113     endTiming("second simplification");
114 
115     startTiming("Constant propagation");
116     SignalConstantPropagation SK;
117     // SK.trace(true, "ConstProp2");
118     Tree L2b = SK.mapself(L2);
119     endTiming("Constant propagation");
120 
121     startTiming("privatise");
122     Tree L3 = privatise(L2b);  // Un-share tables with multiple writers
123     endTiming("privatise");
124 
125     startTiming("conditionAnnotation");
126     conditionAnnotation(L3);
127     endTiming("conditionAnnotation");
128     // conditionStatistics(L3); // Count condition occurrences
129 
130     // dump normal form
131     if (gGlobal->gDumpNorm) {
132         cout << ppsig(L3) << endl;
133         throw faustexception("Dump normal form finished...\n");
134     }
135 
136     startTiming("recursivnessAnnotation");
137     recursivnessAnnotation(L3);  // Annotate L3 with recursivness information
138     endTiming("recursivnessAnnotation");
139 
140     startTiming("typeAnnotation");
141     typeAnnotation(L3, true);    // Annotate L3 with type information
142     endTiming("typeAnnotation");
143 
144     startTiming("sharingAnalysis");
145     sharingAnalysis(L3);         // Annotate L3 with sharing count
146     endTiming("sharingAnalysis");
147 
148     startTiming("occurrences analysis");
149     delete fOccMarkup;
150     fOccMarkup = new old_OccMarkup(fConditionProperty);
151     fOccMarkup->mark(L3);  // Annotate L3 with occurrences analysis
152     endTiming("occurrences analysis");
153 
154     endTiming("ScalarCompiler::prepare");
155 
156     if (gGlobal->gDrawSignals) {
157         ofstream dotfile(subst("$0-sig.dot", gGlobal->makeDrawPath()).c_str());
158         sigToGraph(L3, dotfile);
159     }
160 
161     // Generate VHDL if --vhdl option is set
162     if (gGlobal->gVHDLSwitch) {
163         Signal2VHDLVisitor V(fOccMarkup);
164         ofstream dotfile(subst("faust.vhd", gGlobal->makeDrawPath()).c_str());
165         V.sigToVHDL(L3, dotfile);
166         V.trace(gGlobal->gVHDLTrace, "VHDL");  // activate with --trace option
167         V.mapself(L3);
168     }
169 
170     return L3;
171 }
172 
prepare2(Tree L0)173 Tree ScalarCompiler::prepare2(Tree L0)
174 {
175     startTiming("ScalarCompiler::prepare2");
176 
177     recursivnessAnnotation(L0);  // Annotate L0 with recursivness information
178     typeAnnotation(L0, true);    // Annotate L0 with type information
179     sharingAnalysis(L0);         // annotate L0 with sharing count
180 
181     if (fOccMarkup != 0) {
182         delete fOccMarkup;
183     }
184     fOccMarkup = new old_OccMarkup();
185     fOccMarkup->mark(L0);  // annotate L0 with occurrences analysis
186 
187     endTiming("ScalarCompiler::prepare2");
188     return L0;
189 }
190 
191 /*****************************************************************************
192  compileMultiSignal
193  *****************************************************************************/
194 
dnf2code(Tree cc)195 string ScalarCompiler::dnf2code(Tree cc)
196 {
197     if (cc == gGlobal->nil) return "";
198     Tree c1 = hd(cc);
199     cc      = tl(cc);
200     if (cc == gGlobal->nil) {
201         return and2code(c1);
202     } else {
203         return subst("($0 || $1)", and2code(c1), dnf2code(cc));
204     }
205 }
206 
and2code(Tree cs)207 string ScalarCompiler::and2code(Tree cs)
208 {
209     if (cs == gGlobal->nil) return "";
210     Tree c1 = hd(cs);
211     cs      = tl(cs);
212     if (cs == gGlobal->nil) {
213         return CS(c1);
214     } else {
215         return subst("($0 && $1)", CS(c1), and2code(cs));
216     }
217 }
218 
cnf2code(Tree cs)219 string ScalarCompiler::cnf2code(Tree cs)
220 {
221     if (cs == gGlobal->nil) return "";
222     Tree c1 = hd(cs);
223     cs      = tl(cs);
224     if (cs == gGlobal->nil) {
225         return or2code(c1);
226     } else {
227         return subst("(($0) && $1)", or2code(c1), cnf2code(cs));
228     }
229 }
230 
or2code(Tree cs)231 string ScalarCompiler::or2code(Tree cs)
232 {
233     if (cs == gGlobal->nil) return "";
234     Tree c1 = hd(cs);
235     cs      = tl(cs);
236     if (cs == gGlobal->nil) {
237         return CS(c1);
238     } else {
239         return subst("($0 || $1)", CS(c1), or2code(cs));
240     }
241 }
242 
243 #if _DNF_
244 #define CND2CODE dnf2code
245 #else
246 #define CND2CODE cnf2code
247 #endif
248 
249 // temporary implementation for test purposes
getConditionCode(Tree sig)250 string ScalarCompiler::getConditionCode(Tree sig)
251 {
252     Tree cc = fConditionProperty[sig];
253     if ((cc != 0) && (cc != gGlobal->nil)) {
254         return CND2CODE(cc);
255     } else {
256         return "";
257     }
258 }
259 
260 /*****************************************************************************
261  CS : compile a signal
262  *****************************************************************************/
263 
264 /**
265  * Test if a signal is already compiled
266  * @param sig the signal expression to compile.
267  * @param name the string representing the compiled expression.
268  * @return true is already compiled
269  */
getCompiledExpression(Tree sig,string & cexp)270 bool ScalarCompiler::getCompiledExpression(Tree sig, string& cexp)
271 {
272     return fCompileProperty.get(sig, cexp);
273 }
274 
275 /**
276  * Set the string of a compiled expression is already compiled
277  * @param sig the signal expression to compile.
278  * @param cexp the string representing the compiled expression.
279  * @return the cexp (for commodity)
280  */
setCompiledExpression(Tree sig,const string & cexp)281 string ScalarCompiler::setCompiledExpression(Tree sig, const string& cexp)
282 {
283     // cerr << "ScalarCompiler::setCompiledExpression : " << cexp << " ==> " << ppsig(sig) << endl;
284     string old;
285     if (fCompileProperty.get(sig, old) && (old != cexp)) {
286         // cerr << "ERROR already a compiled expression attached : " << old << " replaced by " << cexp << endl;
287         // exit(1);
288     }
289     fCompileProperty.set(sig, cexp);
290     return cexp;
291 }
292 
293 /*****************************************************************************
294  vector name property
295  *****************************************************************************/
296 
297 /**
298  * Set the vector name property of a signal, the name of the vector used to
299  * store the previous values of the signal to implement a delay.
300  * @param sig the signal expression.
301  * @param vecname the string representing the vector name.
302  * @return true is already compiled
303  */
setVectorNameProperty(Tree sig,const string & vecname)304 void ScalarCompiler::setVectorNameProperty(Tree sig, const string& vecname)
305 {
306     faustassert(vecname.size() > 0);
307     fVectorProperty.set(sig, vecname);
308 }
309 
310 /**
311  * Get the vector name property of a signal, the name of the vector used to
312  * store the previous values of the signal to implement a delay.
313  * @param sig the signal expression.
314  * @param vecname the string where to store the vector name.
315  * @return true if the signal has this property, false otherwise
316  */
317 
getVectorNameProperty(Tree sig,string & vecname)318 bool ScalarCompiler::getVectorNameProperty(Tree sig, string& vecname)
319 {
320     return fVectorProperty.get(sig, vecname);
321 }
322 
323 /**
324  * Compile a signal
325  * @param sig the signal expression to compile.
326  * @return the C code translation of sig as a string
327  */
CS(Tree sig)328 string ScalarCompiler::CS(Tree sig)
329 {
330     // contextor contextRecursivness;
331     string code;
332 
333     if (!getCompiledExpression(sig, code)) {
334         // not compiled yet
335         /*
336          if (getRecursivness(sig) != contextRecursivness.get()) {
337             contextRecursivness.set(getRecursivness(sig));
338          }
339          */
340         code = generateCode(sig);
341         setCompiledExpression(sig, code);
342     }
343     return code;
344 }
345 
346 /*****************************************************************************
347  compileMultiSignal
348  *****************************************************************************/
349 
compileMultiSignal(Tree L)350 void ScalarCompiler::compileMultiSignal(Tree L)
351 {
352     // contextor recursivness(0);
353     L = prepare(L);  // optimize, share and annotate expression
354 
355     for (int i = 0; i < fClass->inputs(); i++) {
356         fClass->addZone3(subst("$1* input$0 = input[$0];", T(i), xfloat()));
357         if (gGlobal->gInPlace) {
358             CS(sigInput(i));
359         }
360     }
361     for (int i = 0; i < fClass->outputs(); i++) {
362         fClass->addZone3(subst("$1* output$0 = output[$0];", T(i), xfloat()));
363     }
364 
365     for (int i = 0; isList(L); L = tl(L), i++) {
366         Tree sig = hd(L);
367         fClass->addExecCode(
368             Statement("", subst("output$0[i] = $2$1;", T(i), generateCacheCode(sig, CS(sig)), xcast())));
369     }
370 
371     generateMetaData();
372     generateUserInterfaceTree(prepareUserInterfaceTree(fUIRoot), true);
373     generateMacroInterfaceTree("", prepareUserInterfaceTree(fUIRoot));
374     if (fDescription) {
375         fDescription->ui(prepareUserInterfaceTree(fUIRoot));
376     }
377 
378     if (gGlobal->gPrintJSONSwitch) {
379         ofstream xout(subst("$0.json", gGlobal->makeDrawPath()).c_str());
380         xout << fJSON.JSON();
381     }
382 
383     ensureIotaCode();
384 }
385 
386 /*****************************************************************************
387  compileSingleSignal
388  *****************************************************************************/
389 
compileSingleSignal(Tree sig)390 void ScalarCompiler::compileSingleSignal(Tree sig)
391 {
392     // contextor recursivness(0);
393     sig = prepare2(sig);  // optimize and annotate expression
394     fClass->addExecCode(Statement("", subst("output[i] = $0;", CS(sig))));
395     generateUserInterfaceTree(prepareUserInterfaceTree(fUIRoot), true);
396     generateMacroInterfaceTree("", prepareUserInterfaceTree(fUIRoot));
397     if (fDescription) {
398         fDescription->ui(prepareUserInterfaceTree(fUIRoot));
399     }
400 
401     ensureIotaCode();
402 }
403 
404 /*****************************************************************************
405  generateCode : dispatch according to signal
406  *****************************************************************************/
407 /**
408  * Main code generator dispatch.
409  * @param sig the signal expression to compile.
410  * @return the C code translation of sig
411  */
412 
generateCode(Tree sig)413 string ScalarCompiler::generateCode(Tree sig)
414 {
415 #if 0
416 	fprintf(stderr, "CALL generateCode(");
417     printSignal(sig, stderr);
418 	fprintf(stderr, ")\n");
419 #endif
420 
421     int    i;
422     double r;
423     Tree   c, sel, x, y, z, label, id, ff, largs, type, name, file, sf;
424 
425     // printf("compilation of %p : ", sig); print(sig); printf("\n");
426 
427     if (getUserData(sig)) {
428         return generateXtended(sig);
429     } else if (isSigInt(sig, &i)) {
430         return generateNumber(sig, T(i));
431     } else if (isSigReal(sig, &r)) {
432         return generateNumber(sig, T(r));
433     } else if (isSigWaveform(sig)) {
434         return generateWaveform(sig);
435     } else if (isSigInput(sig, &i)) {
436         return generateInput(sig, T(i));
437     } else if (isSigOutput(sig, &i, x)) {
438         return generateOutput(sig, T(i), CS(x));
439     }
440 
441     else if (isSigDelay(sig, x, y)) {
442         return generateDelay(sig, x, y);
443     } else if (isSigPrefix(sig, x, y)) {
444         return generatePrefix(sig, x, y);
445     } else if (isSigIota(sig, x)) {
446         return generateIota(sig, x);
447     }
448 
449     else if (isSigBinOp(sig, &i, x, y)) {
450         return generateBinOp(sig, i, x, y);
451     } else if (isSigFFun(sig, ff, largs)) {
452         return generateFFun(sig, ff, largs);
453     } else if (isSigFConst(sig, type, name, file)) {
454         return generateFConst(sig, tree2str(file), tree2str(name));
455     } else if (isSigFVar(sig, type, name, file)) {
456         return generateFVar(sig, tree2str(file), tree2str(name));
457     }
458 
459     else if (isSigTable(sig, id, x, y)) {
460         return generateTable(sig, x, y);
461     } else if (isSigWRTbl(sig, id, x, y, z)) {
462         return generateWRTbl(sig, x, y, z);
463     } else if (isSigRDTbl(sig, x, y)) {
464         return generateRDTbl(sig, x, y);
465     }
466 
467     else if (isSigSelect2(sig, sel, x, y)) {
468         return generateSelect2(sig, sel, x, y);
469     }
470 
471     else if (isSigGen(sig, x)) {
472         return generateSigGen(sig, x);
473     }
474 
475     else if (isProj(sig, &i, x)) {
476         return generateRecProj(sig, x, i);
477     }
478 
479     else if (isSigIntCast(sig, x)) {
480         return generateIntCast(sig, x);
481     } else if (isSigFloatCast(sig, x)) {
482         return generateFloatCast(sig, x);
483     }
484 
485     else if (isSigButton(sig, label)) {
486         return generateButton(sig, label);
487     } else if (isSigCheckbox(sig, label)) {
488         return generateCheckbox(sig, label);
489     } else if (isSigVSlider(sig, label, c, x, y, z)) {
490         return generateVSlider(sig, label, c, x, y, z);
491     } else if (isSigHSlider(sig, label, c, x, y, z)) {
492         return generateHSlider(sig, label, c, x, y, z);
493     } else if (isSigNumEntry(sig, label, c, x, y, z)) {
494         return generateNumEntry(sig, label, c, x, y, z);
495     }
496 
497     else if (isSigVBargraph(sig, label, x, y, z)) {
498         return generateVBargraph(sig, label, x, y, CS(z));
499     } else if (isSigHBargraph(sig, label, x, y, z)) {
500         return generateHBargraph(sig, label, x, y, CS(z));
501     }
502 
503     else if (isSigSoundfile(sig, label)) {
504         return generateSoundfile(sig, label);
505     } else if (isSigSoundfileLength(sig, sf, x)) {
506         return generateCacheCode(sig, subst("$0cache->fLength[$1]", CS(sf), CS(x)));
507     } else if (isSigSoundfileRate(sig, sf, x)) {
508         return generateCacheCode(sig, subst("$0cache->fSR[$1]", CS(sf), CS(x)));
509     } else if (isSigSoundfileBuffer(sig, sf, x, y, z)) {
510         return generateCacheCode(sig, subst("(($1)$0cache->fBuffers)[$2][$0cache->fOffset[$3]+$4]", CS(sf),
511                                             ifloatptrptr(), CS(x), CS(y), CS(z)));
512     }
513 
514     else if (isSigAttach(sig, x, y)) {
515         CS(y);
516         return generateCacheCode(sig, CS(x));
517     } else if (isSigControl(sig, x, y)) {
518         if (gGlobal->gVectorSwitch) {
519             throw faustexception("ERROR : 'control/enable' can only be used in scalar mode\n");
520         }
521         return generateControl(sig, x, y);
522 
523     } else if (isSigAssertBounds(sig, x, y, z)) {
524         /* no debug option for the moment */
525         return generateCode(z);
526     } else if (isSigLowest(sig, x) || isSigHighest(sig, x)) {
527         throw faustexception("ERROR : annotations should have been deleted in Simplification process\n");
528     }
529 
530     /* we should not have any control at this stage*/
531     else {
532         stringstream error;
533         error << "ERROR when compiling, ScalarCompiler::generateCode unrecognized signal : " << ppsig(sig) << endl;
534         throw faustexception(error.str());
535     }
536     return "error in generated code";
537 }
538 
539 /*****************************************************************************
540  NUMBERS
541  *****************************************************************************/
542 
generateNumber(Tree sig,const string & exp)543 string ScalarCompiler::generateNumber(Tree sig, const string& exp)
544 {
545     string          ctype, vname;
546     old_Occurences* o = fOccMarkup->retrieve(sig);
547 
548     // check for number occuring in delays
549     if (o->getMaxDelay() > 0) {
550         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
551         generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
552     }
553     return exp;
554 }
555 
556 /*****************************************************************************
557  FOREIGN CONSTANTS
558  *****************************************************************************/
559 
generateFConst(Tree sig,const string & file,const string & exp_aux)560 string ScalarCompiler::generateFConst(Tree sig, const string& file, const string& exp_aux)
561 {
562     // Special case for 02/25/19 renaming
563     string exp = (exp_aux == "fSamplingFreq") ? "fSampleRate" : exp_aux;
564 
565     string          ctype, vname;
566     old_Occurences* o = fOccMarkup->retrieve(sig);
567 
568     addIncludeFile(file);
569 
570     if (o->getMaxDelay() > 0) {
571         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
572         generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
573     }
574     return exp;
575 }
576 
577 /*****************************************************************************
578  FOREIGN VARIABLES
579  *****************************************************************************/
580 
generateFVar(Tree sig,const string & file,const string & exp)581 string ScalarCompiler::generateFVar(Tree sig, const string& file, const string& exp)
582 {
583     string ctype, vname;
584 
585     addIncludeFile(file);
586     return generateCacheCode(sig, exp);
587 }
588 
589 /*****************************************************************************
590  INPUTS - OUTPUTS
591  *****************************************************************************/
592 
generateInput(Tree sig,const string & idx)593 string ScalarCompiler::generateInput(Tree sig, const string& idx)
594 {
595     if (gGlobal->gInPlace) {
596         // inputs must be cached for in-place transformations
597         return forceCacheCode(sig, subst("$1input$0[i]", idx, icast()));
598     } else {
599         return generateCacheCode(sig, subst("$1input$0[i]", idx, icast()));
600     }
601 }
602 
generateOutput(Tree sig,const string & idx,const string & arg)603 string ScalarCompiler::generateOutput(Tree sig, const string& idx, const string& arg)
604 {
605     string dst = subst("output$0[i]", idx);
606     fClass->addExecCode(Statement("", subst("$0 = $2$1;", dst, arg, xcast())));
607     return dst;
608 }
609 
610 /*****************************************************************************
611  BINARY OPERATION
612  *****************************************************************************/
613 
generateBinOp(Tree sig,int opcode,Tree arg1,Tree arg2)614 string ScalarCompiler::generateBinOp(Tree sig, int opcode, Tree arg1, Tree arg2)
615 {
616     if (opcode == kDiv) {
617         // special handling for division, we always want a float division
618         Type t1 = getCertifiedSigType(arg1);
619         Type t2 = getCertifiedSigType(arg2);
620 
621         interval j = t2->getInterval();
622 
623         if (j.haszero()) {
624             // potential division by zero
625             // interval    i = t1->getInterval();
626             // std::cerr << "WARNING : potential division by zero (" << i << "/" << j << ") in " << ppsig(sig) <<
627             // std::endl;
628         }
629 
630         if (t1->nature() == kInt && t2->nature() == kInt) {
631             return generateCacheCode(
632                 sig, subst("($3($0) $1 $3($2))", CS(arg1), gBinOpTable[opcode]->fName, CS(arg2), ifloat()));
633         } else if (t1->nature() == kInt && t2->nature() == kReal) {
634             return generateCacheCode(sig,
635                                      subst("($3($0) $1 $2)", CS(arg1), gBinOpTable[opcode]->fName, CS(arg2), ifloat()));
636         } else if (t1->nature() == kReal && t2->nature() == kInt) {
637             return generateCacheCode(sig,
638                                      subst("($0 $1 $3($2))", CS(arg1), gBinOpTable[opcode]->fName, CS(arg2), ifloat()));
639         } else {
640             return generateCacheCode(sig,
641                                      subst("($0 $1 $2)", CS(arg1), gBinOpTable[opcode]->fName, CS(arg2), ifloat()));
642         }
643     } else {
644         return generateCacheCode(sig, subst("($0 $1 $2)", CS(arg1), gBinOpTable[opcode]->fName, CS(arg2)));
645     }
646 }
647 
648 /*****************************************************************************
649  Primitive Operations
650  *****************************************************************************/
651 
generateFFun(Tree sig,Tree ff,Tree largs)652 string ScalarCompiler::generateFFun(Tree sig, Tree ff, Tree largs)
653 {
654     addIncludeFile(ffincfile(ff));  // printf("inc file %s\n", ffincfile(ff));
655     addLibrary(fflibfile(ff));      // printf("lib file %s\n", fflibfile(ff));
656 
657     string code = ffname(ff);
658     code += '(';
659     string sep = "";
660     for (int i = 0; i < ffarity(ff); i++) {
661         code += sep;
662         code += CS(nth(largs, i));
663         sep = ", ";
664     }
665     code += ')';
666     return generateCacheCode(sig, code);
667 }
668 
669 /*****************************************************************************
670  CACHE CODE
671  *****************************************************************************/
672 
getTypedNames(Type t,const string & prefix,string & ctype,string & vname)673 void ScalarCompiler::getTypedNames(Type t, const string& prefix, string& ctype, string& vname)
674 {
675     if (t->nature() == kInt) {
676         ctype = "int";
677         vname = subst("i$0", getFreshID(prefix));
678     } else {
679         ctype = ifloat();
680         vname = subst("f$0", getFreshID(prefix));
681     }
682 }
683 
generateCacheCode(Tree sig,const string & exp)684 string ScalarCompiler::generateCacheCode(Tree sig, const string& exp)
685 {
686     string code;
687 
688     // check reentrance
689     if (getCompiledExpression(sig, code)) {
690         return code;
691     }
692 
693     string          vname, ctype;
694     int             sharing = getSharingCount(sig);
695     old_Occurences* o       = fOccMarkup->retrieve(sig);
696     faustassert(o);
697 
698     // check for expression occuring in delays
699     if (o->getMaxDelay() > 0) {
700         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
701         if (sharing > 1) {
702             return generateDelayVec(sig, generateVariableStore(sig, exp), ctype, vname, o->getMaxDelay());
703         } else {
704             return generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
705         }
706 
707     } else if ((sharing > 1) || (o->hasMultiOccurences())) {
708         return generateVariableStore(sig, exp);
709 
710     } else if (sharing == 1) {
711         return exp;
712 
713     } else {
714         stringstream error;
715         error << "ERROR in sharing count (" << sharing << ") for " << *sig << endl;
716         throw faustexception(error.str());
717     }
718 
719     return "Error in generateCacheCode";
720 }
721 
722 // like generateCacheCode but we force caching like if sharing was always > 1
forceCacheCode(Tree sig,const string & exp)723 string ScalarCompiler::forceCacheCode(Tree sig, const string& exp)
724 {
725     string code;
726 
727     // check reentrance
728     if (getCompiledExpression(sig, code)) {
729         return code;
730     }
731 
732     string          vname, ctype;
733     old_Occurences* o = fOccMarkup->retrieve(sig);
734     faustassert(o);
735 
736     // check for expression occuring in delays
737     if (o->getMaxDelay() > 0) {
738         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
739         return generateDelayVec(sig, generateVariableStore(sig, exp), ctype, vname, o->getMaxDelay());
740     } else {
741         return generateVariableStore(sig, exp);
742     }
743 }
744 
generateVariableStore(Tree sig,const string & exp)745 string ScalarCompiler::generateVariableStore(Tree sig, const string& exp)
746 {
747     string          vname, vname_perm, ctype;
748     Type            t = getCertifiedSigType(sig);
749     old_Occurences* o = fOccMarkup->retrieve(sig);
750     faustassert(o);
751 
752     switch (t->variability()) {
753         case kKonst:
754             getTypedNames(t, "Const", ctype, vname);
755             // The variable is used in compute (kBlock or kSamp), so define is as a field in the DSP struct
756             if (o->getOccurence(kBlock) || o->getOccurence(kSamp)) {
757                 fClass->addDeclCode(subst("$0 \t$1;", ctype, vname));
758                 fClass->addInitCode(subst("$0 = $1;", vname, exp));
759             } else {
760                 // Otherwise it can stay as a local variable
761                 fClass->addInitCode(subst("$0 \t$1 = $2;", ctype, vname, exp));
762             }
763             break;
764 
765         case kBlock:
766             getTypedNames(t, "Slow", ctype, vname);
767             fClass->addFirstPrivateDecl(vname);
768             fClass->addZone2(subst("$0 \t$1 = $2;", ctype, vname, exp));
769             break;
770 
771         case kSamp:
772             getTypedNames(t, "Temp", ctype, vname);
773             if (getConditionCode(sig) == "") {
774                 fClass->addExecCode(Statement("", subst("$0 \t$1 = $2;", ctype, vname, exp)));
775             } else {
776                 getTypedNames(t, "TempPerm", ctype, vname_perm);
777                 // need to be preserved because of new enable and control primitives
778                 fClass->addDeclCode(subst("$0 \t$1;", ctype, vname_perm));
779                 fClass->addInitCode(subst("$0 = 0;", vname_perm));
780                 // copy the object variable to the local one
781                 fClass->addZone2(subst("$0 \t$1 = $2;", ctype, vname, vname_perm));
782                 // execute the code
783                 fClass->addExecCode(Statement(getConditionCode(sig), subst("$0 = $1;", vname, exp)));
784                 // copy the local variable to the object one
785                 fClass->addZone4(subst("$0 = $1;", vname_perm, vname));
786             }
787             break;
788     }
789     return vname;
790 }
791 
792 /*****************************************************************************
793  CASTING
794  *****************************************************************************/
795 
generateIntCast(Tree sig,Tree x)796 string ScalarCompiler::generateIntCast(Tree sig, Tree x)
797 {
798     return generateCacheCode(sig, subst("int($0)", CS(x)));
799 }
800 
generateFloatCast(Tree sig,Tree x)801 string ScalarCompiler::generateFloatCast(Tree sig, Tree x)
802 {
803     return generateCacheCode(sig, subst("$1($0)", CS(x), ifloat()));
804 }
805 
806 /*****************************************************************************
807  user interface elements
808  *****************************************************************************/
809 
generateButton(Tree sig,Tree path)810 string ScalarCompiler::generateButton(Tree sig, Tree path)
811 {
812     string varname = getFreshID("fbutton");
813     fClass->addDeclCode(subst("$1 \t$0;", varname, xfloat()));
814     fClass->addInitUICode(subst("$0 = 0.0;", varname));
815     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
816 
817     // return generateCacheCode(sig, varname);
818     return generateCacheCode(sig, subst("$1($0)", varname, ifloat()));
819 }
820 
generateCheckbox(Tree sig,Tree path)821 string ScalarCompiler::generateCheckbox(Tree sig, Tree path)
822 {
823     string varname = getFreshID("fcheckbox");
824     fClass->addDeclCode(subst("$1 \t$0;", varname, xfloat()));
825     fClass->addInitUICode(subst("$0 = 0.0;", varname));
826     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
827 
828     // return generateCacheCode(sig, varname);
829     return generateCacheCode(sig, subst("$1($0)", varname, ifloat()));
830 }
831 
generateVSlider(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)832 string ScalarCompiler::generateVSlider(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
833 {
834     string varname = getFreshID("fslider");
835     fClass->addDeclCode(subst("$1 \t$0;", varname, xfloat()));
836     fClass->addInitUICode(subst("$0 = $1;", varname, T(tree2float(cur))));
837     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
838 
839     // return generateCacheCode(sig, varname);
840     return generateCacheCode(sig, subst("$1($0)", varname, ifloat()));
841 }
842 
generateHSlider(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)843 string ScalarCompiler::generateHSlider(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
844 {
845     string varname = getFreshID("fslider");
846     fClass->addDeclCode(subst("$1 \t$0;", varname, xfloat()));
847     fClass->addInitUICode(subst("$0 = $1;", varname, T(tree2float(cur))));
848     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
849 
850     // return generateCacheCode(sig, varname);
851     return generateCacheCode(sig, subst("$1($0)", varname, ifloat()));
852 }
853 
generateNumEntry(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)854 string ScalarCompiler::generateNumEntry(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
855 {
856     string varname = getFreshID("fentry");
857     fClass->addDeclCode(subst("$1 \t$0;", varname, xfloat()));
858     fClass->addInitUICode(subst("$0 = $1;", varname, T(tree2float(cur))));
859     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
860 
861     // return generateCacheCode(sig, varname);
862     return generateCacheCode(sig, subst("$1($0)", varname, ifloat()));
863 }
864 
generateVBargraph(Tree sig,Tree path,Tree min,Tree max,const string & exp)865 string ScalarCompiler::generateVBargraph(Tree sig, Tree path, Tree min, Tree max, const string& exp)
866 {
867     string varname = getFreshID("fbargraph");
868     fClass->addDeclCode(subst("$1 \t$0;", varname, xfloat()));
869     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
870 
871     Type t = getCertifiedSigType(sig);
872     switch (t->variability()) {
873         case kKonst:
874             fClass->addInitUICode(subst("$0 = $1;", varname, exp));
875             break;
876 
877         case kBlock:
878             fClass->addZone2(subst("$0 = $1;", varname, exp));
879             break;
880 
881         case kSamp:
882             fClass->addExecCode(Statement(getConditionCode(sig), subst("$0 = $1;", varname, exp)));
883             break;
884     }
885 
886     // return varname;
887     return generateCacheCode(sig, varname);
888 }
889 
generateHBargraph(Tree sig,Tree path,Tree min,Tree max,const string & exp)890 string ScalarCompiler::generateHBargraph(Tree sig, Tree path, Tree min, Tree max, const string& exp)
891 {
892     string varname = getFreshID("fbargraph");
893     fClass->addDeclCode(subst("$1 \t$0;", varname, xfloat()));
894     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
895 
896     Type t = getCertifiedSigType(sig);
897     switch (t->variability()) {
898         case kKonst:
899             fClass->addInitUICode(subst("$0 = $1;", varname, exp));
900             break;
901 
902         case kBlock:
903             fClass->addZone2(subst("$0 = $1;", varname, exp));
904             break;
905 
906         case kSamp:
907             fClass->addExecCode(Statement(getConditionCode(sig), subst("$0 = $1;", varname, exp)));
908             break;
909     }
910 
911     // return varname;
912     return generateCacheCode(sig, varname);
913 }
914 
generateSoundfile(Tree sig,Tree path)915 string ScalarCompiler::generateSoundfile(Tree sig, Tree path)
916 {
917     string varname = getFreshID("fSoundfile");
918 
919     // SL
920     // fClass->addIncludeFile("<atomic>");
921     // fClass->addIncludeFile("\"faust/gui/soundfile.h\"");
922 
923     // SL
924     // fClass->addDeclCode(subst("std::atomic<Soundfile*> \t$0;", varname));
925     fClass->addDeclCode(subst("Soundfile* \t$0;", varname));
926 
927     // fClass->addDeclCode(subst("Soundfile* \t$0cache;", varname));
928     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
929 
930     // SL
931     fClass->addInitUICode(subst("if (uintptr_t($0) == 0) $0 = defaultsound;", varname));
932     fClass->addFirstPrivateDecl(subst("$0cache", varname));
933 
934     // SL
935     // fClass->addZone2(subst("Soundfile* $0cache = $0.exchange(nullptr);", varname));
936     fClass->addZone2(subst("Soundfile* $0cache = $0;", varname));
937     fClass->addZone4(subst("$0 = $0cache;", varname));
938     return varname;
939 }
940 
941 /*****************************************************************************
942  TABLES
943  *****************************************************************************/
944 
945 /*----------------------------------------------------------------------------
946                         sigGen : initial table content
947 ----------------------------------------------------------------------------*/
948 
generateSigGen(Tree sig,Tree content)949 string ScalarCompiler::generateSigGen(Tree sig, Tree content)
950 {
951     string klassname = getFreshID("SIG");
952     string signame   = getFreshID("sig");
953 
954     fClass->addSubKlass(signal2klass(fClass, klassname, content));
955     fClass->addInitCode(subst("$0 $1;", klassname, signame));
956     fInstanceInitProperty.set(content, pair<string, string>(klassname, signame));
957 
958     return signame;
959 }
960 
generateStaticSigGen(Tree sig,Tree content)961 string ScalarCompiler::generateStaticSigGen(Tree sig, Tree content)
962 {
963     string klassname = getFreshID("SIG");
964     string signame   = getFreshID("sig");
965 
966     fClass->addSubKlass(signal2klass(fClass, klassname, content));
967     fClass->addStaticInitCode(subst("$0 $1;", klassname, signame));
968     fStaticInitProperty.set(content, pair<string, string>(klassname, signame));
969 
970     return signame;
971 }
972 
973 /*----------------------------------------------------------------------------
974                         sigTable : table declaration
975 ----------------------------------------------------------------------------*/
976 
generateTable(Tree sig,Tree tsize,Tree content)977 string ScalarCompiler::generateTable(Tree sig, Tree tsize, Tree content)
978 {
979     int size;
980     if (!isSigInt(tsize, &size)) {
981         stringstream error;
982         error << "ERROR in generateTable : " << *tsize << " is not an integer expression " << endl;
983         throw faustexception(error.str());
984     }
985 
986     string generator(CS(content));
987     Tree   g;
988     string cexp;
989     string ctype, vname;
990 
991     // already compiled but check if we need to add declarations
992     faustassert(isSigGen(content, g));
993     pair<string, string> kvnames;
994     if (!fInstanceInitProperty.get(g, kvnames)) {
995         // not declared here, we add a declaration
996         bool b = fStaticInitProperty.get(g, kvnames);
997         faustassert(b);
998         fClass->addInitCode(subst("$0 $1;", kvnames.first, kvnames.second));
999     }
1000 
1001     // definition du nom et du type de la table
1002     // A REVOIR !!!!!!!!!
1003     Type t = getCertifiedSigType(content);  //, tEnv);
1004 
1005     if (t->nature() == kInt) {
1006         vname = getFreshID("itbl");
1007         ctype = "int";
1008     } else {
1009         vname = getFreshID("ftbl");
1010         ctype = ifloat();
1011     }
1012 
1013     // declaration de la table
1014     fClass->addDeclCode(subst("$0 \t$1[$2];", ctype, vname, T(size)));
1015 
1016     // initialisation du generateur de contenu
1017     fClass->addInitCode(subst("$0.init(sample_rate);", generator));
1018     // remplissage de la table
1019     fClass->addInitCode(subst("$0.fill($1,$2);", generator, T(size), vname));
1020 
1021     // on retourne le nom de la table
1022     return vname;
1023 }
1024 
generateStaticTable(Tree sig,Tree tsize,Tree content)1025 string ScalarCompiler::generateStaticTable(Tree sig, Tree tsize, Tree content)
1026 {
1027     int size;
1028     if (!isSigInt(tsize, &size)) {
1029         stringstream error;
1030         error << "ERROR in generateStaticTable : " << *tsize << " is not an integer expression " << endl;
1031         throw faustexception(error.str());
1032     }
1033 
1034     Tree   g;
1035     string cexp;
1036     string ctype, vname;
1037 
1038     faustassert(isSigGen(content, g));
1039 
1040     if (!getCompiledExpression(content, cexp)) {
1041         cexp = setCompiledExpression(content, generateStaticSigGen(content, g));
1042     } else {
1043         // already compiled but check if we need to add declarations
1044         pair<string, string> kvnames;
1045         if (!fStaticInitProperty.get(g, kvnames)) {
1046             // not declared here, we add a declaration
1047             bool b = fInstanceInitProperty.get(g, kvnames);
1048             faustassert(b);
1049             fClass->addStaticInitCode(subst("$0 $1;", kvnames.first, kvnames.second));
1050         }
1051     }
1052 
1053     // definition du nom et du type de la table
1054     // A REVOIR !!!!!!!!!
1055     Type t = getCertifiedSigType(content);  //, tEnv);
1056 
1057     if (t->nature() == kInt) {
1058         vname = getFreshID("itbl");
1059         ctype = "int";
1060     } else {
1061         vname = getFreshID("ftbl");
1062         ctype = ifloat();
1063     }
1064 
1065     // declaration de la table
1066     if (gGlobal->gMemoryManager) {
1067         fClass->addDeclCode(subst("static $0* \t$1;", ctype, vname));
1068         fClass->addStaticFields(subst("$0* \t$1::$2 = 0;", ctype, fClass->getClassName(), vname));
1069         fClass->addStaticInitCode(
1070             subst("$0 = static_cast<$1*>(fManager->allocate(sizeof($1) * $2));", vname, ctype, T(size)));
1071         fClass->addStaticDestroyCode(subst("fManager->destroy($0);", vname));
1072     } else {
1073         fClass->addDeclCode(subst("static $0 \t$1[$2];", ctype, vname, T(size)));
1074         fClass->addStaticFields(subst("$0 \t$1::$2[$3];", ctype, fClass->getClassName(), vname, T(size)));
1075     }
1076 
1077     // initialisation du generateur de contenu
1078     fClass->addStaticInitCode(subst("$0.init(sample_rate);", cexp));
1079     // remplissage de la table
1080     fClass->addStaticInitCode(subst("$0.fill($1,$2);", cexp, T(size), vname));
1081 
1082     // on retourne le nom de la table
1083     return vname;
1084 }
1085 
1086 /*----------------------------------------------------------------------------
1087                         sigWRTable : table assignement
1088 ----------------------------------------------------------------------------*/
1089 
generateWRTbl(Tree sig,Tree tbl,Tree idx,Tree data)1090 string ScalarCompiler::generateWRTbl(Tree sig, Tree tbl, Tree idx, Tree data)
1091 {
1092     string tblName(CS(tbl));
1093 
1094     Type t2 = getCertifiedSigType(idx);
1095     Type t3 = getCertifiedSigType(data);
1096     // TODO : for a bug in type caching, t->variability() is not correct.
1097     // Therefore in the meantime we compute it manually. (YO 2020/03/30)
1098     int var = t2->variability() | t3->variability();
1099     switch (var) {
1100         case kKonst:
1101             fClass->addInitCode(subst("$0[$1] = $2;", tblName, CS(idx), CS(data)));
1102             break;
1103         case kBlock:
1104             fClass->addZone2(subst("$0[$1] = $2;", tblName, CS(idx), CS(data)));
1105             break;
1106         default:
1107             fClass->addExecCode(Statement(getConditionCode(sig), subst("$0[$1] = $2;", tblName, CS(idx), CS(data))));
1108             break;
1109     }
1110 
1111     return tblName;
1112 }
1113 
1114 /*----------------------------------------------------------------------------
1115                         sigRDTable : table access
1116 ----------------------------------------------------------------------------*/
1117 
generateRDTbl(Tree sig,Tree tbl,Tree idx)1118 string ScalarCompiler::generateRDTbl(Tree sig, Tree tbl, Tree idx)
1119 {
1120     // YO le 21/04/05 : La lecture des tables n'était pas mise dans le cache
1121     // et donc le code était dupliqué (dans tester.dsp par exemple)
1122     // return subst("$0[$1]", CS(tEnv, tbl), CS(tEnv, idx));
1123 
1124     // cerr << "generateRDTable " << *sig << endl;
1125     // test the special case of a read only table that can be compiled as a static member
1126     Tree id, size, content;
1127     if (isSigTable(tbl, id, size, content)) {
1128         string tblname;
1129         if (!getCompiledExpression(tbl, tblname)) {
1130             tblname = setCompiledExpression(tbl, generateStaticTable(tbl, size, content));
1131         }
1132         return generateCacheCode(sig, subst("$0[$1]", tblname, CS(idx)));
1133     } else {
1134         return generateCacheCode(sig, subst("$0[$1]", CS(tbl), CS(idx)));
1135     }
1136 }
1137 
1138 /*****************************************************************************
1139                                RECURSIONS
1140 *****************************************************************************/
1141 
1142 /**
1143  * Generate code for a projection of a group of mutually recursive definitions
1144  */
generateRecProj(Tree sig,Tree r,int i)1145 string ScalarCompiler::generateRecProj(Tree sig, Tree r, int i)
1146 {
1147     string vname;
1148     Tree   var, le;
1149 
1150     if (!getVectorNameProperty(sig, vname)) {
1151         faustassert(isRec(r, var, le));
1152         generateRec(r, var, le);
1153         faustassert(getVectorNameProperty(sig, vname));
1154     }
1155     return "[[UNUSED EXP]]";  // make sure the resulting expression is never used in the generated code
1156 }
1157 
1158 /**
1159  * Generate code for a group of mutually recursive definitions
1160  */
generateRec(Tree sig,Tree var,Tree le)1161 void ScalarCompiler::generateRec(Tree sig, Tree var, Tree le)
1162 {
1163     int N = len(le);
1164 
1165     vector<bool>   used(N);
1166     vector<int>    delay(N);
1167     vector<string> vname(N);
1168     vector<string> ctype(N);
1169 
1170     // prepare each element of a recursive definition
1171     for (int i = 0; i < N; i++) {
1172         Tree e = sigProj(i, sig);  // recreate each recursive definition
1173         if (fOccMarkup->retrieve(e)) {
1174             // this projection is used
1175             used[i] = true;
1176             getTypedNames(getCertifiedSigType(e), "Rec", ctype[i], vname[i]);
1177             setVectorNameProperty(e, vname[i]);
1178             delay[i] = fOccMarkup->retrieve(e)->getMaxDelay();
1179         } else {
1180             // this projection is not used therefore
1181             // we should not generate code for it
1182             used[i] = false;
1183         }
1184     }
1185 
1186     // generate delayline for each element of a recursive definition
1187     for (int i = 0; i < N; i++) {
1188         if (used[i]) {
1189             generateDelayLine(ctype[i], vname[i], delay[i], CS(nth(le, i)), getConditionCode(nth(le, i)));
1190         }
1191     }
1192 }
1193 
1194 /*****************************************************************************
1195  PREFIX, DELAY A PREFIX VALUE
1196  *****************************************************************************/
1197 
generateControl(Tree sig,Tree x,Tree y)1198 string ScalarCompiler::generateControl(Tree sig, Tree x, Tree y)
1199 {
1200     CS(y);
1201     return generateCacheCode(x, CS(x));
1202 }
1203 
generatePrefix(Tree sig,Tree x,Tree e)1204 string ScalarCompiler::generatePrefix(Tree sig, Tree x, Tree e)
1205 {
1206     Type te = getCertifiedSigType(sig);  //, tEnv);
1207 
1208     string vperm = getFreshID("M");
1209     string vtemp = getFreshID("T");
1210 
1211     string type = (te->nature() == kInt) ? "int" : ifloat();
1212 
1213     fClass->addDeclCode(subst("$0 \t$1;", type, vperm));
1214     fClass->addInitCode(subst("$0 = $1;", vperm, CS(x)));
1215     fClass->addInitCode(subst("$0 \t$1;", type, vtemp));
1216 
1217     fClass->addExecCode(Statement(getConditionCode(sig), subst("$0 = $1;", vtemp, vperm)));
1218     fClass->addExecCode(Statement(getConditionCode(sig), subst("$0 = $1;", vperm, CS(e))));
1219     return vtemp;
1220 }
1221 
1222 /*****************************************************************************
1223  IOTA(n)
1224  *****************************************************************************/
1225 
isPowerOf2(int n)1226 static bool isPowerOf2(int n)
1227 {
1228     return !(n & (n - 1));
1229 }
1230 
generateIota(Tree sig,Tree n)1231 string ScalarCompiler::generateIota(Tree sig, Tree n)
1232 {
1233     int size;
1234     if (!isSigInt(n, &size)) {
1235         throw faustexception("ERROR in generateIota\n");
1236     }
1237 
1238     string vperm = getFreshID("iota");
1239 
1240     fClass->addDeclCode(subst("int \t$0;", vperm));
1241     fClass->addClearCode(subst("$0 = 0;", vperm));
1242 
1243     if (isPowerOf2(size)) {
1244         fClass->addExecCode(Statement("", subst("$0 = ($0+1)&$1;", vperm, T(size - 1))));
1245     } else {
1246         fClass->addExecCode(Statement("", subst("if (++$0 == $1) $0=0;", vperm, T(size))));
1247     }
1248     return vperm;
1249 }
1250 
1251 /*****************************************************************************
1252  SELECT
1253  *****************************************************************************/
1254 
generateSelect2(Tree sig,Tree sel,Tree s1,Tree s2)1255 string ScalarCompiler::generateSelect2(Tree sig, Tree sel, Tree s1, Tree s2)
1256 {
1257     return generateCacheCode(sig, subst("(($0)?$1:$2)", CS(sel), CS(s2), CS(s1)));
1258 }
1259 
1260 /*****************************************************************************
1261  EXTENDED
1262  *****************************************************************************/
1263 
generateXtended(Tree sig)1264 string ScalarCompiler::generateXtended(Tree sig)
1265 {
1266     xtended*       p = (xtended*)getUserData(sig);
1267     vector<string> args;
1268     vector<Type>   types;
1269 
1270     for (int i = 0; i < sig->arity(); i++) {
1271         args.push_back(CS(sig->branch(i)));
1272         types.push_back(getCertifiedSigType(sig->branch(i)));
1273     }
1274 
1275     if (p->needCache()) {
1276         return generateCacheCode(sig, p->old_generateCode(fClass, args, types));
1277     } else {
1278         return p->old_generateCode(fClass, args, types);
1279     }
1280 }
1281 
1282 /**
1283  * Compute the minimal power of 2 greater than x
1284  */
1285 
pow2limit(int x)1286 int ScalarCompiler::pow2limit(int x)
1287 {
1288     int n = 2;
1289     while (n < x) {
1290         n = 2 * n;
1291     }
1292     return n;
1293 }
1294 
1295 /*****************************************************************************
1296  N-SAMPLE FIXED DELAY : sig = exp@delay
1297 
1298  case 1-sample max delay :
1299  Y(t-0)	Y(t-1)
1300  V[0]	V[1]
1301 
1302  case max delay < gMaxCopyDelay :
1303  Y(t-0)	Y(t-1)	Y(t-2)  ...
1304  V[0]	V[1]	V[2]	...
1305 
1306  case max delay >= gMaxCopyDelay :
1307  Y(t-0)	Y(t-1)	Y(t-2)  ...
1308  V[0]	V[1]	V[2]	...
1309 
1310 
1311  *****************************************************************************/
1312 
1313 /**
1314  * Generate code for accessing a delayed signal. The generated code depend of
1315  * the maximum delay attached to exp.
1316  */
1317 
generateDelay(Tree sig,Tree exp,Tree delay)1318 string ScalarCompiler::generateDelay(Tree sig, Tree exp, Tree delay)
1319 {
1320     // cerr << "ScalarCompiler::generateDelay sig = " << *sig << endl;
1321     // cerr << "ScalarCompiler::generateDelay exp = " << *exp << endl;
1322     // cerr << "ScalarCompiler::generateDelay del = " << *delay << endl;
1323 
1324     string code = CS(exp);  // ensure exp is compiled to have a vector name
1325     int    mxd  = fOccMarkup->retrieve(exp)->getMaxDelay();
1326     string vecname;
1327 
1328     if (!getVectorNameProperty(exp, vecname)) {
1329         if (mxd == 0) {
1330             // cerr << "it is a pure zero delay : " << code << endl;
1331             return code;
1332         } else {
1333             cerr << "No vector name for : " << ppsig(exp) << endl;
1334             faustassert(0);
1335         }
1336     }
1337 
1338     if (mxd == 0) {
1339         // not a real vector name but a scalar name
1340         return vecname;
1341 
1342     } else if (mxd < gGlobal->gMaxCopyDelay) {
1343         int d;
1344         if (isSigInt(delay, &d)) {
1345             return subst("$0[$1]", vecname, CS(delay));
1346         } else {
1347             return generateCacheCode(sig, subst("$0[$1]", vecname, CS(delay)));
1348         }
1349 
1350     } else {
1351         int N = pow2limit(mxd + 1);
1352         return generateCacheCode(sig, subst("$0[(IOTA-$1)&$2]", vecname, CS(delay), T(N - 1)));
1353     }
1354 }
1355 
1356 /**
1357  * Generate code for the delay mecchanism. The generated code depend of the
1358  * maximum delay attached to exp and the "less temporaries" switch
1359  */
1360 
generateDelayVec(Tree sig,const string & exp,const string & ctype,const string & vname,int mxd)1361 string ScalarCompiler::generateDelayVec(Tree sig, const string& exp, const string& ctype, const string& vname, int mxd)
1362 {
1363     string s = generateDelayVecNoTemp(sig, exp, ctype, vname, mxd);
1364     if (getCertifiedSigType(sig)->variability() < kSamp) {
1365         return exp;
1366     } else {
1367         return s;
1368     }
1369 }
1370 
1371 /**
1372  * Generate code for the delay mecchanism without using temporary variables
1373  */
1374 
generateDelayVecNoTemp(Tree sig,const string & exp,const string & ctype,const string & vname,int mxd)1375 string ScalarCompiler::generateDelayVecNoTemp(Tree sig, const string& exp, const string& ctype, const string& vname,
1376                                               int mxd)
1377 {
1378     faustassert(mxd > 0);
1379 
1380     // bool odocc = fOccMarkup->retrieve(sig)->hasOutDelayOccurences();
1381     string ccs = getConditionCode(sig);
1382 
1383     if (mxd < gGlobal->gMaxCopyDelay) {
1384         // short delay : we copy
1385         fClass->addDeclCode(subst("$0 \t$1[$2];", ctype, vname, T(mxd + 1)));
1386         fClass->addClearCode(subst("for (int i=0; i<$1; i++) $0[i] = 0;", vname, T(mxd + 1)));
1387         fClass->addExecCode(Statement(ccs, subst("$0[0] = $1;", vname, exp)));
1388 
1389         // generate post processing copy code to update delay values
1390         if (mxd == 1) {
1391             fClass->addPostCode(Statement(ccs, subst("$0[1] = $0[0];", vname)));
1392         } else if (mxd == 2) {
1393             // fClass->addPostCode(subst("$0[2] = $0[1];", vname));
1394             fClass->addPostCode(Statement(ccs, subst("$0[2] = $0[1]; $0[1] = $0[0];", vname)));
1395         } else {
1396             fClass->addPostCode(Statement(ccs, subst("for (int i=$0; i>0; i--) $1[i] = $1[i-1];", T(mxd), vname)));
1397         }
1398         setVectorNameProperty(sig, vname);
1399         return subst("$0[0]", vname);
1400 
1401     } else {
1402         // generate code for a long delay : we use a ring buffer of size N = 2**x > mxd
1403         int N = pow2limit(mxd + 1);
1404 
1405         // we need an iota index
1406         fMaxIota = 0;
1407 
1408         // declare and init
1409         fClass->addDeclCode(subst("$0 \t$1[$2];", ctype, vname, T(N)));
1410         fClass->addClearCode(subst("for (int i=0; i<$1; i++) $0[i] = 0;", vname, T(N)));
1411 
1412         // execute
1413         fClass->addExecCode(Statement(ccs, subst("$0[IOTA&$1] = $2;", vname, T(N - 1), exp)));
1414         setVectorNameProperty(sig, vname);
1415         return subst("$0[IOTA&$1]", vname, T(N - 1));
1416     }
1417 }
1418 
1419 /**
1420  * Generate code for the delay mecchanism without using temporary variables
1421  */
1422 
generateDelayLine(const string & ctype,const string & vname,int mxd,const string & exp,const string & ccs)1423 void ScalarCompiler::generateDelayLine(const string& ctype, const string& vname, int mxd, const string& exp,
1424                                        const string& ccs)
1425 {
1426     if (mxd == 0) {
1427         // cerr << "MXD==0 :  " << vname << " := " << exp << endl;
1428         // no need for a real vector
1429         if (ccs == "") {
1430             fClass->addExecCode(Statement(ccs, subst("$0 \t$1 = $2;", ctype, vname, exp)));
1431         } else {
1432             fClass->addZone2(subst("$0 \t$1 = 0;", ctype, vname));
1433             fClass->addExecCode(Statement(ccs, subst("\t$0 = $1;", vname, exp)));
1434         }
1435 
1436     } else if (mxd < gGlobal->gMaxCopyDelay) {
1437         // cerr << "small delay : " << vname << "[" << mxd << "]" << endl;
1438 
1439         // short delay : we copy
1440         fClass->addDeclCode(subst("$0 \t$1[$2];", ctype, vname, T(mxd + 1)));
1441         fClass->addClearCode(subst("for (int i=0; i<$1; i++) $0[i] = 0;", vname, T(mxd + 1)));
1442         fClass->addExecCode(Statement(ccs, subst("$0[0] = $1;", vname, exp)));
1443 
1444         // generate post processing copy code to update delay values
1445         if (mxd == 1) {
1446             fClass->addPostCode(Statement(ccs, subst("$0[1] = $0[0];", vname)));
1447         } else if (mxd == 2) {
1448             fClass->addPostCode(Statement(ccs, subst("$0[2] = $0[1]; $0[1] = $0[0];", vname)));
1449         } else {
1450             fClass->addPostCode(Statement(ccs, subst("for (int i=$0; i>0; i--) $1[i] = $1[i-1];", T(mxd), vname)));
1451         }
1452 
1453     } else {
1454         // generate code for a long delay : we use a ring buffer of size N = 2**x > mxd
1455         int N = pow2limit(mxd + 1);
1456 
1457         // we need an iota index
1458         fMaxIota = 0;
1459 
1460         // declare and init
1461         fClass->addDeclCode(subst("$0 \t$1[$2];", ctype, vname, T(N)));
1462         fClass->addClearCode(subst("for (int i=0; i<$1; i++) $0[i] = 0;", vname, T(N)));
1463 
1464         // execute
1465         fClass->addExecCode(Statement(ccs, subst("$0[IOTA&$1] = $2;", vname, T(N - 1), exp)));
1466     }
1467 }
1468 
1469 /**
1470  * Generate code for a unique IOTA variable increased at each sample
1471  * and used to index delay buffers.
1472  */
ensureIotaCode()1473 void ScalarCompiler::ensureIotaCode()
1474 {
1475     if (fMaxIota >= 0) {
1476         fClass->addDeclCode("int \tIOTA;");
1477         fClass->addClearCode(subst("IOTA = $0;", T(fMaxIota)));
1478         fClass->addPostCode(Statement("", "IOTA = IOTA+1;"));
1479     }
1480 }
1481 
1482 /*****************************************************************************
1483  WAVEFORM
1484  *****************************************************************************/
1485 
1486 /**
1487  * Generate code for a waveform. The waveform will be declared as a static field.
1488  * The name of the waveform is returned in vname and its size in size.
1489  */
declareWaveform(Tree sig,string & vname,int & size)1490 void ScalarCompiler::declareWaveform(Tree sig, string& vname, int& size)
1491 {
1492     // computes C type and unique name for the waveform
1493     string ctype;
1494     getTypedNames(getCertifiedSigType(sig), "Wave", ctype, vname);
1495 
1496     size = sig->arity();
1497 
1498     // Converts waveform into a string : "{a,b,c,...}"
1499     stringstream content;
1500 
1501     char sep = '{';
1502     for (int i = 0; i < size; i++) {
1503         content << sep << ppsig(sig->branch(i));
1504         sep = ',';
1505     }
1506     content << '}';
1507 
1508     // Declares the Waveform
1509     fClass->addDeclCode(subst("static $0 \t$1[$2];", ctype, vname, T(size)));
1510     fClass->addDeclCode(subst("int \tidx$0;", vname));
1511     fClass->addInitCode(subst("idx$0 = 0;", vname));
1512     fClass->getTopParentKlass()->addStaticFields(
1513         subst("$0 \t$1::$2[$3] = ", ctype, fClass->getFullClassName(), vname, T(size)) + content.str() + ";");
1514 }
1515 
generateWaveform(Tree sig)1516 string ScalarCompiler::generateWaveform(Tree sig)
1517 {
1518     string vname;
1519     int    size;
1520 
1521     declareWaveform(sig, vname, size);
1522     fClass->addPostCode(Statement(getConditionCode(sig), subst("idx$0 = (idx$0 + 1) % $1;", vname, T(size))));
1523     return generateCacheCode(sig, subst("$0[idx$0]", vname));
1524 }
1525