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