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 #include <string>
23 
24 #include "Text.hh"
25 #include "ensure.hh"
26 #include "exception.hh"
27 #include "fir_to_fir.hh"
28 #include "floats.hh"
29 #include "global.hh"
30 #include "instructions.hh"
31 #include "instructions_compiler.hh"
32 #include "instructions_compiler1.hh"
33 #include "ppsig.hh"
34 #include "prim2.hh"
35 #include "privatise.hh"
36 #include "recursivness.hh"
37 #include "sigConstantPropagation.hh"
38 #include "sigPromotion.hh"
39 #include "sigToGraph.hh"
40 #include "signal2vhdlVisitor.hh"
41 #include "signal2Elementary.hh"
42 #include "sigprint.hh"
43 #include "sigtyperules.hh"
44 #include "simplify.hh"
45 #include "timing.hh"
46 #include "xtended.hh"
47 
48 using namespace std;
49 
50 ostream* Printable::fOut = &cout;
51 
genBasicFIRTyped(int sig_type)52 static inline BasicTyped* genBasicFIRTyped(int sig_type)
53 {
54     return InstBuilder::genBasicTyped((sig_type == kInt) ? Typed::kInt32 : itfloat());
55 }
56 
InstructionsCompiler(CodeContainer * container)57 InstructionsCompiler::InstructionsCompiler(CodeContainer* container)
58     : fContainer(container),
59       fSharingKey(nullptr),
60       fOccMarkup(nullptr),
61       fUIRoot(uiFolder(cons(tree(0), tree("")))),
62       fDescription(0),
63       fHasIota(false)
64 {
65 }
66 
67 // Taken from sharing.cpp
68 
getSharingCount(Tree sig)69 int InstructionsCompiler::getSharingCount(Tree sig)
70 {
71     Tree c;
72     if (getProperty(sig, fSharingKey, c)) {
73         return c->node().getInt();
74     } else {
75         return 0;
76     }
77 }
78 
setSharingCount(Tree sig,int count)79 void InstructionsCompiler::setSharingCount(Tree sig, int count)
80 {
81     setProperty(sig, fSharingKey, tree(count));
82 }
83 
sharingAnalysis(Tree t)84 void InstructionsCompiler::sharingAnalysis(Tree t)
85 {
86     fSharingKey = shprkey(t);
87     if (isList(t)) {
88         while (isList(t)) {
89             sharingAnnotation(kSamp, hd(t));
90             t = tl(t);
91         }
92     } else {
93         sharingAnnotation(kSamp, t);
94     }
95 }
96 
sharingAnnotation(int vctxt,Tree sig)97 void InstructionsCompiler::sharingAnnotation(int vctxt, Tree sig)
98 {
99     // cerr << "START sharing annotation of " << *sig << endl;
100     int count = getSharingCount(sig);
101 
102     if (count > 0) {
103         // it is not our first visit
104         setSharingCount(sig, count + 1);
105 
106     } else {
107         // it is our first visit,
108         int v = getCertifiedSigType(sig)->variability();
109 
110         // check "time sharing" cases
111         if (v < vctxt) {
112             setSharingCount(sig, 2);  // time sharing occurence : slower expression in faster context
113         } else {
114             setSharingCount(sig, 1);  // regular occurence
115         }
116 
117         // Annotate the sub signals
118         vector<Tree> subsig;
119         int          n = getSubSignals(sig, subsig);
120         if (n > 0 && !isSigGen(sig)) {
121             for (int i = 0; i < n; i++) sharingAnnotation(v, subsig[i]);
122         }
123     }
124     // cerr << "END sharing annotation of " << *sig << endl;
125 }
126 
127 //------------------------------------------------------------------------------
128 // Condition annotation due to enabled expressions
129 //------------------------------------------------------------------------------
130 #if 0
131 void InstructionsCompiler::conditionStatistics(Tree l)
132 {
133     for (const auto& p : fConditionProperty) {
134         fConditionStatistics[p.second]++;
135     }
136     std::cout << "\nConditions statistics" << std::endl;
137     for (const auto& p : fConditionStatistics) {
138         std::cout << ppsig(p.first) << ":" << p.second << std::endl;
139     }
140 }
141 #endif
142 
conditionStatistics(Tree l)143 void InstructionsCompiler::conditionStatistics(Tree l)
144 {
145     map<Tree, int> fConditionStatistics;  // used with the new X,Y:enable --> sigEnable(X*Y,Y != 0) primitive
146     for (const auto& p : fConditionProperty) {
147         for (Tree lc = p.second; !isNil(lc); lc = tl(lc)) {
148             fConditionStatistics[hd(lc)]++;
149         }
150     }
151     std::cout << "\nConditions statistics" << std::endl;
152     for (const auto& p : fConditionStatistics) {
153         std::cout << ppsig(p.first) << ":" << p.second << std::endl;
154     }
155 }
156 
conditionAnnotation(Tree l)157 void InstructionsCompiler::conditionAnnotation(Tree l)
158 {
159     while (isList(l)) {
160         conditionAnnotation(hd(l), gGlobal->nil);
161         l = tl(l);
162     }
163 }
164 
165 #if _DNF_
166 
167 #define _OR_ dnfOr
168 #define _AND_ dnfAnd
169 #define _CND_ dnfCond
170 
171 #else
172 
173 #define _OR_ cnfOr
174 #define _AND_ cnfAnd
175 #define _CND_ cnfCond
176 
177 #endif
178 
conditionAnnotation(Tree t,Tree nc)179 void InstructionsCompiler::conditionAnnotation(Tree t, Tree nc)
180 {
181     // Check if we need to annotate the tree with new conditions
182     auto p = fConditionProperty.find(t);
183     if (p != fConditionProperty.end()) {
184         Tree cc = p->second;
185         Tree xc = _OR_(cc, nc);
186         if (cc == xc) {
187             // Tree t already correctly annotated, nothing to change
188             return;
189         } else {
190             // we need to re-annotate the tree with a new condition
191             nc        = xc;
192             p->second = nc;
193         }
194     } else {
195         // first visit
196         fConditionProperty[t] = nc;
197     }
198 
199     // Annotate the subtrees with the new condition nc
200     // which is either the nc passed as argument or nc <- (cc v nc)
201     Tree x, y;
202     if (isSigControl(t, x, y)) {
203         // specific annotation case for sigControl
204         conditionAnnotation(y, nc);
205         conditionAnnotation(x, _AND_(nc, _CND_(y)));
206     } else {
207         // general annotation case
208         // Annotate the sub signals with nc
209         vector<Tree> subsig;
210         int          n = getSubSignals(t, subsig);
211         if (n > 0 && !isSigGen(t)) {
212             for (int i = 0; i < n; i++) conditionAnnotation(subsig[i], nc);
213         }
214     }
215 }
216 
217 /*****************************************************************************
218  prepare
219  *****************************************************************************/
220 
prepare(Tree LS)221 Tree InstructionsCompiler::prepare(Tree LS)
222 {
223     startTiming("prepare");
224 
225     startTiming("deBruijn2Sym");
226     Tree L1 = deBruijn2Sym(LS);  // Convert deBruijn recursion into symbolic recursion
227     endTiming("deBruijn2Sym");
228 
229     startTiming("L1 typeAnnotation");
230     // Annotate L1 with type information (needed by castAndPromotion(), but don't check causality)
231     typeAnnotation(L1, gGlobal->gLocalCausalityCheck);
232     endTiming("L1 typeAnnotation");
233 
234     startTiming("Cast and Promotion");
235     SignalPromotion SP;
236     // SP.trace(true, "Cast");
237     Tree L2 = SP.mapself(L1);
238     endTiming("Cast and Promotion");
239 
240     startTiming("second simplification");
241     Tree L3 = simplify(L2);  // Simplify by executing every computable operation
242     endTiming("second simplification");
243 
244     startTiming("Constant propagation");
245     SignalConstantPropagation SK;
246     // SK.trace(true, "ConstProp2");
247     Tree L4 = SK.mapself(L3);
248     endTiming("Constant propagation");
249 
250     startTiming("privatise");
251     Tree L5 = privatise(L4);  // Un-share tables with multiple writers
252     endTiming("privatise");
253 
254     startTiming("conditionAnnotation");
255     conditionAnnotation(L5);
256     endTiming("conditionAnnotation");
257 
258     // dump normal form
259     if (gGlobal->gDumpNorm) {
260         cout << ppsig(L5) << endl;
261         throw faustexception("Dump normal form finished...\n");
262     }
263 
264     startTiming("recursivnessAnnotation");
265     recursivnessAnnotation(L5);  // Annotate L5 with recursivness information
266     endTiming("recursivnessAnnotation");
267 
268     startTiming("L5 typeAnnotation");
269     typeAnnotation(L5, true);    // Annotate L5 with type information and check causality
270     endTiming("L5 typeAnnotation");
271 
272     startTiming("sharingAnalysis");
273     sharingAnalysis(L5);         // Annotate L5 with sharing count
274     endTiming("sharingAnalysis");
275 
276     startTiming("occurrences analysis");
277     delete fOccMarkup;
278     fOccMarkup = new old_OccMarkup(fConditionProperty);
279     fOccMarkup->mark(L5);        // Annotate L5 with occurrences analysis
280     endTiming("occurrences analysis");
281     // annotationStatistics();
282     endTiming("prepare");
283 
284     if (gGlobal->gDrawSignals) {
285         ofstream dotfile(subst("$0-sig.dot", gGlobal->makeDrawPath()).c_str());
286         sigToGraph(L5, dotfile);
287     }
288 
289     // Generate VHDL if -vhdl option is set
290     if (gGlobal->gVHDLSwitch) {
291         Signal2VHDLVisitor V(fOccMarkup);
292         ofstream vhdl_file(subst("faust.vhd", gGlobal->makeDrawPath()).c_str());
293         V.sigToVHDL(L5, vhdl_file);
294         V.trace(gGlobal->gVHDLTrace, "VHDL");  // activate with --trace option
295         V.mapself(L5);
296     }
297 
298     // Experimental : generate Elementary code if -elm option is set
299     if (gGlobal->gElementarySwitch) {
300         Signal2Elementary V;
301         ofstream js_file(subst("$0-el.js", gGlobal->makeDrawPath()).c_str());
302         V.sig2Elementary(L5, js_file);
303         V.mapself(L5);
304     }
305 
306     return L5;
307 }
308 
prepare2(Tree L0)309 Tree InstructionsCompiler::prepare2(Tree L0)
310 {
311     startTiming("prepare2");
312 
313     recursivnessAnnotation(L0);  // Annotate L0 with recursivness information
314     typeAnnotation(L0, true);    // Annotate L0 with type information
315     sharingAnalysis(L0);         // Annotate L0 with sharing count
316 
317     delete fOccMarkup;
318     fOccMarkup = new old_OccMarkup();
319     fOccMarkup->mark(L0);        // Annotate L0 with occurrences analysis
320 
321     endTiming("prepare2");
322     return L0;
323 }
324 
325 /*****************************************************************************
326  CS : compile a signal
327  *****************************************************************************/
328 
329 /**
330  * Test if a signal is already compiled
331  * @param sig the signal expression to compile.
332  * @param name the string representing the compiled expression.
333  * @return true is already compiled
334  */
getCompiledExpression(Tree sig,InstType & cexp)335 bool InstructionsCompiler::getCompiledExpression(Tree sig, InstType& cexp)
336 {
337     return fCompileProperty.get(sig, cexp);
338 }
339 
340 /**
341  * Set the string of a compiled expression is already compiled
342  * @param sig the signal expression to compile.
343  * @param cexp the string representing the compiled expression.
344  * @return the cexp (for commodity)
345  */
setCompiledExpression(Tree sig,const InstType & cexp)346 InstType InstructionsCompiler::setCompiledExpression(Tree sig, const InstType& cexp)
347 {
348     InstType old;
349     if (fCompileProperty.get(sig, old) && (old != cexp)) {
350         // stringstream error;
351         // error << "ERROR already a compiled expression attached : " << old << " replaced by " << cexp << endl;
352         // throw faustexception(error.str());
353     }
354 
355     fCompileProperty.set(sig, cexp);
356     return cexp;
357 }
358 
359 /*****************************************************************************
360  vector name property
361  *****************************************************************************/
362 
363 /**
364  * Set the vector name property of a signal, the name of the vector used to
365  * store the previous values of the signal to implement a delay.
366  * @param sig the signal expression.
367  * @param vname the string representing the vector name.
368  * @return true is already compiled
369  */
setVectorNameProperty(Tree sig,const string & vname)370 void InstructionsCompiler::setVectorNameProperty(Tree sig, const string& vname)
371 {
372     faustassert(vname.size() > 0);
373     fVectorProperty.set(sig, vname);
374 }
375 
376 /**
377  * Get the vector name property of a signal, the name of the vector used to
378  * store the previous values of the signal to implement a delay.
379  * @param sig the signal expression.
380  * @param vname the string where to store the vector name.
381  * @return true if the signal has this property, false otherwise
382  */
383 
getVectorNameProperty(Tree sig,string & vname)384 bool InstructionsCompiler::getVectorNameProperty(Tree sig, string& vname)
385 {
386     return fVectorProperty.get(sig, vname);
387 }
388 
setTableNameProperty(Tree sig,const string & name)389 void InstructionsCompiler::setTableNameProperty(Tree sig, const string& name)
390 {
391     faustassert(name.size() > 0);
392     fTableProperty.set(sig, name);
393 }
394 
getTableNameProperty(Tree sig,string & name)395 bool InstructionsCompiler::getTableNameProperty(Tree sig, string& name)
396 {
397     return fTableProperty.get(sig, name);
398 }
399 
CS(Tree sig)400 ValueInst* InstructionsCompiler::CS(Tree sig)
401 {
402     ValueInst* code;
403 
404     if (!getCompiledExpression(sig, code)) {
405         code = generateCode(sig);
406         setCompiledExpression(sig, code);
407     }
408 
409     return code;
410 }
411 
signal2Container(const string & name,Tree sig)412 CodeContainer* InstructionsCompiler::signal2Container(const string& name, Tree sig)
413 {
414     ::Type t = getCertifiedSigType(sig);
415 
416     CodeContainer* container = fContainer->createScalarContainer(name, t->nature());
417 
418     if (gGlobal->gOutputLang == "rust" || gGlobal->gOutputLang == "julia") {
419         InstructionsCompiler1 C(container);
420         C.compileSingleSignal(sig);
421     } else {
422         InstructionsCompiler C(container);
423         C.compileSingleSignal(sig);
424     }
425     return container;
426 }
427 
428 /*****************************************************************************
429  compileMultiSignal
430  *****************************************************************************/
431 
dnf2code(Tree cc)432 ValueInst* InstructionsCompiler::dnf2code(Tree cc)
433 {
434     if (cc == gGlobal->nil) return InstBuilder::genNullValueInst();
435     Tree c1 = hd(cc);
436     cc      = tl(cc);
437     if (cc == gGlobal->nil) {
438         return and2code(c1);
439     } else {
440         return InstBuilder::genOr(and2code(c1), dnf2code(cc));
441     }
442 }
443 
and2code(Tree cs)444 ValueInst* InstructionsCompiler::and2code(Tree cs)
445 {
446     if (cs == gGlobal->nil) return InstBuilder::genNullValueInst();
447     Tree c1 = hd(cs);
448     cs      = tl(cs);
449     if (cs == gGlobal->nil) {
450         return CS(c1);
451     } else {
452         return InstBuilder::genAnd(CS(c1), and2code(cs));
453     }
454 }
455 
cnf2code(Tree cs)456 ValueInst* InstructionsCompiler::cnf2code(Tree cs)
457 {
458     if (cs == gGlobal->nil) return InstBuilder::genNullValueInst();
459     Tree c1 = hd(cs);
460     cs      = tl(cs);
461     if (cs == gGlobal->nil) {
462         return or2code(c1);
463     } else {
464         return InstBuilder::genAnd(or2code(c1), cnf2code(cs));
465     }
466 }
467 
or2code(Tree cs)468 ValueInst* InstructionsCompiler::or2code(Tree cs)
469 {
470     if (cs == gGlobal->nil) return InstBuilder::genNullValueInst();
471     Tree c1 = hd(cs);
472     cs      = tl(cs);
473     if (cs == gGlobal->nil) {
474         return CS(c1);
475     } else {
476         return InstBuilder::genOr(CS(c1), or2code(cs));
477     }
478 }
479 
480 #if _DNF_
481 #define CND2CODE dnf2code
482 #else
483 #define CND2CODE cnf2code
484 #endif
485 
486 // Temporary implementation for test purposes
getConditionCode(Tree sig)487 ValueInst* InstructionsCompiler::getConditionCode(Tree sig)
488 {
489     Tree cc = fConditionProperty[sig];
490     if ((cc != 0) && (cc != gGlobal->nil)) {
491         return CND2CODE(cc);
492     } else {
493         return InstBuilder::genNullValueInst();
494     }
495 }
496 
compileMultiSignal(Tree L)497 void InstructionsCompiler::compileMultiSignal(Tree L)
498 {
499     // Has to be done *after* gMachinePtrSize is set by the actual backend
500     gGlobal->initTypeSizeMap();
501 
502     L = prepare(L);  // Optimize, share and annotate expression
503 
504     startTiming("compileMultiSignal");
505 
506 #ifdef LLVM_DEBUG
507     // Add function declaration
508     pushGlobalDeclare(InstBuilder::genFunction1("printInt32", Typed::kVoid, "val", Typed::kInt32));
509     pushGlobalDeclare(InstBuilder::genFunction1("printFloat", Typed::kVoid, "val", Typed::kFloat));
510     pushGlobalDeclare(InstBuilder::genFunction1("printDouble", Typed::kVoid, "val", Typed::kDouble));
511     pushGlobalDeclare(InstBuilder::genFunction1("printPtr", Typed::kVoid, "val", Typed::kVoid_ptr));
512 #endif
513 
514     Typed* type = InstBuilder::genFloatMacroTyped();
515 
516     if (!gGlobal->gOpenCLSwitch && !gGlobal->gCUDASwitch) {  // HACK
517 
518         // Input declarations
519         if (gGlobal->gOutputLang == "rust" || gGlobal->gOutputLang == "julia") {
520             // special handling for Rust and Julia backends
521             pushComputeBlockMethod(InstBuilder::genDeclareBufferIterators("input", "inputs", fContainer->inputs(), false));
522         } else {
523             // "input" and "inputs" used as a name convention
524             if (gGlobal->gOneSampleControl) {
525                 for (int index = 0; index < fContainer->inputs(); index++) {
526                     string name = subst("input$0", T(index));
527                     pushDeclare(InstBuilder::genDecStructVar(name, InstBuilder::genArrayTyped(type, 0)));
528                     if (gGlobal->gInPlace) {
529                         CS(sigInput(index));
530                     }
531                 }
532             } else if (gGlobal->gOneSample >= 0) {
533             // Nothing...
534             } else {
535                 for (int index = 0; index < fContainer->inputs(); index++) {
536                     string name = subst("input$0", T(index));
537                     pushComputeBlockMethod(InstBuilder::genDecStackVar(
538                         name, InstBuilder::genArrayTyped(type, 0),
539                         InstBuilder::genLoadArrayFunArgsVar("inputs", InstBuilder::genInt32NumInst(index))));
540                     if (gGlobal->gInPlace) {
541                         CS(sigInput(index));
542                     }
543                 }
544             }
545         }
546 
547         // Output declarations
548         if (gGlobal->gOutputLang == "rust" || gGlobal->gOutputLang == "julia") {
549             // special handling for Rust and Julia backends
550             pushComputeBlockMethod(InstBuilder::genDeclareBufferIterators("output", "outputs", fContainer->outputs(), true));
551         } else {
552             // "output" and "outputs" used as a name convention
553             if (gGlobal->gOneSampleControl) {
554                 for (int index = 0; index < fContainer->outputs(); index++) {
555                     string name = subst("output$0", T(index));
556                     pushDeclare(InstBuilder::genDecStructVar(name, InstBuilder::genArrayTyped(type, 0)));
557                 }
558             } else if (gGlobal->gOneSample >= 0) {
559             // Nothing...
560             } else {
561                 for (int index = 0; index < fContainer->outputs(); index++) {
562                     string name = subst("output$0", T(index));
563                     pushComputeBlockMethod(InstBuilder::genDecStackVar(
564                         name, InstBuilder::genArrayTyped(type, 0),
565                         InstBuilder::genLoadArrayFunArgsVar("outputs", InstBuilder::genInt32NumInst(index))));
566                 }
567             }
568         }
569     }
570 
571     for (int index = 0; isList(L); L = tl(L), index++) {
572         Tree sig = hd(L);
573 
574         // Cast to external float
575         ValueInst* res = InstBuilder::genCastFloatMacroInst(CS(sig));
576 
577         // HACK for Rust backend
578         string name;
579         if (gGlobal->gOutputLang == "rust") {
580             name = subst("*output$0", T(index));
581             pushComputeDSPMethod(InstBuilder::genStoreStackVar(name, res));
582         } else if (gGlobal->gOneSampleControl) {
583             name = subst("output$0", T(index));
584             if (gGlobal->gComputeMix) {
585                 ValueInst* res1 = InstBuilder::genAdd(res, InstBuilder::genLoadStackVar(name));
586                 pushComputeDSPMethod(InstBuilder::genStoreStackVar(name, res1));
587             } else {
588                 pushComputeDSPMethod(InstBuilder::genStoreStackVar(name, res));
589             }
590         } else if (gGlobal->gOneSample >= 0) {
591             name = "outputs";
592             if (gGlobal->gComputeMix) {
593                 ValueInst* res1 = InstBuilder::genAdd(res, InstBuilder::genLoadArrayStackVar(name, InstBuilder::genInt32NumInst(index)));
594                 pushComputeDSPMethod(InstBuilder::genStoreArrayStackVar(name, InstBuilder::genInt32NumInst(index), res1));
595             } else {
596                 pushComputeDSPMethod(InstBuilder::genStoreArrayStackVar(name, InstBuilder::genInt32NumInst(index), res));
597             }
598         } else {
599             name = subst("output$0", T(index));
600             if (gGlobal->gComputeMix) {
601                 ValueInst* res1 = InstBuilder::genAdd(res, InstBuilder::genLoadArrayStackVar(name, getCurrentLoopIndex()));
602                 pushComputeDSPMethod(InstBuilder::genStoreArrayStackVar(name, getCurrentLoopIndex(), res1));
603             } else {
604                 pushComputeDSPMethod(InstBuilder::genStoreArrayStackVar(name, getCurrentLoopIndex(), res));
605             }
606         }
607     }
608 
609     Tree ui = InstructionsCompiler::prepareUserInterfaceTree(fUIRoot);
610     generateUserInterfaceTree(ui, true);
611     generateMacroInterfaceTree("", ui);
612     if (fDescription) {
613         fDescription->ui(ui);
614     }
615 
616     // Apply FIR to FIR transformations
617     fContainer->processFIR();
618 
619     // Generate JSON (which checks for non duplicated path)
620     if (gGlobal->gPrintJSONSwitch) {
621         if (gGlobal->gFloatSize == 1) {
622             fContainer->generateJSONFile<float>();
623         } else {
624             fContainer->generateJSONFile<double>();
625         }
626     } else {
627         // Checks for non duplicated path
628         JSONInstVisitor<float> path_checker;
629         fContainer->generateUserInterface(&path_checker);
630     }
631 
632     endTiming("compileMultiSignal");
633 }
634 
635 /*****************************************************************************
636  compileSingleSignal
637  *****************************************************************************/
638 
compileSingleSignal(Tree sig)639 void InstructionsCompiler::compileSingleSignal(Tree sig)
640 {
641     sig         = prepare2(sig);  // Optimize and annotate expression
642     string name = fContainer->getTableName();
643 
644     pushComputeDSPMethod(InstBuilder::genStoreArrayFunArgsVar(name, getCurrentLoopIndex(), CS(sig)));
645 
646     Tree ui = InstructionsCompiler::prepareUserInterfaceTree(fUIRoot);
647     generateUserInterfaceTree(ui);
648     generateMacroInterfaceTree("", ui);
649     if (fDescription) {
650         fDescription->ui(ui);
651     }
652 }
653 
654 /*****************************************************************************
655  generateCode : dispatch according to signal
656  *****************************************************************************/
657 /**
658  * Main code generator dispatch.
659  * @param sig the signal expression to compile.
660  * @return the FIR code translation of sig
661  */
662 
generateCode(Tree sig)663 ValueInst* InstructionsCompiler::generateCode(Tree sig)
664 {
665 #if 0
666     fprintf(stderr, "CALL generateCode(");
667     printSignal(sig, stderr);
668     fprintf(stderr, ")\n");
669 #endif
670 
671     ValueInst* code;
672     if (getCompiledExpression(sig, code)) {
673         return code;
674     }
675 
676     int    i;
677     double r;
678     Tree   c, sel, x, y, z, label, id, ff, largs, type, name, file, sf;
679 
680     // printf("compilation of %p : ", sig); print(sig); printf("\n");
681 
682     if (getUserData(sig)) {
683         return generateXtended(sig);
684     } else if (isSigInt(sig, &i)) {
685         return generateIntNumber(sig, i);
686     } else if (isSigReal(sig, &r)) {
687         return generateRealNumber(sig, r);
688     } else if (isSigWaveform(sig)) {
689         return generateWaveform(sig);
690     } else if (isSigInput(sig, &i)) {
691         return generateInput(sig, i);
692     }
693 
694     else if (isSigDelay(sig, x, y)) {
695         return generateDelay(sig, x, y);
696     } else if (isSigPrefix(sig, x, y)) {
697         return generatePrefix(sig, x, y);
698     } else if (isSigIota(sig, x)) {
699         return generateIota(sig, x);
700     }
701 
702     else if (isSigBinOp(sig, &i, x, y)) {
703         return generateBinOp(sig, i, x, y);
704     } else if (isSigFFun(sig, ff, largs)) {
705         return generateFFun(sig, ff, largs);
706     } else if (isSigFConst(sig, type, name, file)) {
707         return generateFConst(sig, type, tree2str(file), tree2str(name));
708     } else if (isSigFVar(sig, type, name, file)) {
709         return generateFVar(sig, type, tree2str(file), tree2str(name));
710     }
711 
712     else if (isSigTable(sig, id, x, y)) {
713         return generateTable(sig, x, y);
714     } else if (isSigWRTbl(sig, id, x, y, z)) {
715         return generateWRTbl(sig, x, y, z);
716     } else if (isSigRDTbl(sig, x, y)) {
717         return generateRDTbl(sig, x, y);
718     }
719 
720     else if (isSigSelect2(sig, sel, x, y)) {
721         return generateSelect2(sig, sel, x, y);
722     }
723 
724     else if (isSigGen(sig, x)) {
725         return generateSigGen(sig, x);
726     }
727 
728     else if (isProj(sig, &i, x)) {
729         return generateRecProj(sig, x, i);
730     }
731 
732     else if (isSigIntCast(sig, x)) {
733         return generateIntCast(sig, x);
734     } else if (isSigFloatCast(sig, x)) {
735         return generateFloatCast(sig, x);
736     }
737 
738     else if (isSigButton(sig, label)) {
739         return generateButton(sig, label);
740     } else if (isSigCheckbox(sig, label)) {
741         return generateCheckbox(sig, label);
742     } else if (isSigVSlider(sig, label, c, x, y, z)) {
743         return generateVSlider(sig, label, c, x, y, z);
744     } else if (isSigHSlider(sig, label, c, x, y, z)) {
745         return generateHSlider(sig, label, c, x, y, z);
746     } else if (isSigNumEntry(sig, label, c, x, y, z)) {
747         return generateNumEntry(sig, label, c, x, y, z);
748     }
749 
750     else if (isSigVBargraph(sig, label, x, y, z)) {
751         return generateVBargraph(sig, label, x, y, CS(z));
752     } else if (isSigHBargraph(sig, label, x, y, z)) {
753         return generateHBargraph(sig, label, x, y, CS(z));
754     }
755 
756     else if (isSigSoundfile(sig, label)) {
757         return generateSoundfile(sig, label);
758     } else if (isSigSoundfileLength(sig, sf, x)) {
759         return generateCacheCode(sig, generateSoundfileLength(sig, CS(sf), CS(x)));
760     } else if (isSigSoundfileRate(sig, sf, x)) {
761         return generateCacheCode(sig, generateSoundfileRate(sig, CS(sf), CS(x)));
762     } else if (isSigSoundfileBuffer(sig, sf, x, y, z)) {
763         return generateCacheCode(sig, generateSoundfileBuffer(sig, CS(sf), CS(x), CS(y), CS(z)));
764     }
765 
766     else if (isSigAttach(sig, x, y)) {
767         CS(y);
768         return generateCacheCode(sig, CS(x));
769 
770     } else if (isSigControl(sig, x, y)) {
771         if (gGlobal->gVectorSwitch) {
772             throw faustexception("ERROR : 'control/enable' can only be used in scalar mode\n");
773         }
774         return generateControl(sig, x, y);
775 
776     } else if (isSigAssertBounds(sig, x, y, z)) {
777         /* no debug option for the moment */
778         return generateCode(z);
779     } else if (isSigLowest(sig, x) || isSigHighest(sig, x)) {
780         throw faustexception("ERROR : annotations should have been deleted in Simplification process\n");
781     } else {
782         stringstream error;
783         error << "ERROR when compiling, unrecognized signal : " << ppsig(sig) << endl;
784         throw faustexception(error.str());
785     }
786     return InstBuilder::genNullValueInst();
787 }
788 
789 /*****************************************************************************
790  NUMBERS
791  *****************************************************************************/
792 
generateIntNumber(Tree sig,int num)793 ValueInst* InstructionsCompiler::generateIntNumber(Tree sig, int num)
794 {
795     old_Occurences* o = fOccMarkup->retrieve(sig);
796 
797     // Check for number occuring in delays
798     if (o->getMaxDelay() > 0) {
799         Typed::VarType ctype;
800         string         vname;
801         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
802         generateDelayVec(sig, InstBuilder::genInt32NumInst(num), ctype, vname, o->getMaxDelay());
803     }
804 
805     // No cache for numbers
806     return InstBuilder::genInt32NumInst(num);
807 }
808 
generateRealNumber(Tree sig,double num)809 ValueInst* InstructionsCompiler::generateRealNumber(Tree sig, double num)
810 {
811     Typed::VarType ctype = itfloat();
812     old_Occurences*    o = fOccMarkup->retrieve(sig);
813 
814     // Check for number occuring in delays
815     if (o->getMaxDelay() > 0) {
816         string vname;
817         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
818         generateDelayVec(sig, InstBuilder::genRealNumInst(ctype, num), ctype, vname, o->getMaxDelay());
819     }
820 
821     // No cache for numbers
822     return InstBuilder::genRealNumInst(ctype, num);
823 }
824 
825 /*****************************************************************************
826  FOREIGN CONSTANTS
827  *****************************************************************************/
828 
generateFConst(Tree sig,Tree type,const string & file,const string & name_aux)829 ValueInst* InstructionsCompiler::generateFConst(Tree sig, Tree type, const string& file, const string& name_aux)
830 {
831     fContainer->addIncludeFile(file);
832 
833     // Special case for 02/25/19 renaming
834     string name = (name_aux == "fSamplingFreq") ? "fSampleRate" : name_aux;
835 
836     // Check access (handling "fSampleRate" as a special case)
837     if (name != "fSampleRate" && !gGlobal->gAllowForeignConstant) {
838         stringstream error;
839         error << "ERROR : accessing foreign constant '" << name << "'"
840         << " is not allowed in this compilation mode!" << endl;
841         throw faustexception(error.str());
842     }
843 
844     // Keep SR generation state
845     if (name == "fSampleRate") {
846         fContainer->setGeneratedSR();
847     }
848 
849     // Check for number occuring in delays
850     Typed::VarType ctype;
851     string         vname;
852     old_Occurences*    o = fOccMarkup->retrieve(sig);
853 
854     if (o->getMaxDelay() > 0) {
855         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
856         generateDelayVec(
857             sig, (name == "fSampleRate") ? InstBuilder::genLoadStructVar(name) : InstBuilder::genLoadGlobalVar(name),
858             ctype, vname, o->getMaxDelay());
859     }
860 
861     // Special case for 'fSampleRate' parameter of the class
862     int sig_type = getCertifiedSigType(sig)->nature();
863     if (name == "fSampleRate") {
864         pushDeclare(InstBuilder::genDecStructVar(name, genBasicFIRTyped(sig_type)));
865         return InstBuilder::genLoadStructVar(name);
866     } else {
867         pushExtGlobalDeclare(InstBuilder::genDecGlobalVar(name, genBasicFIRTyped(sig_type)));
868         return InstBuilder::genLoadGlobalVar(name);
869     }
870 }
871 
872 /*****************************************************************************
873  FOREIGN VARIABLES
874  *****************************************************************************/
875 
generateFVar(Tree sig,Tree type,const string & file,const string & name)876 ValueInst* InstructionsCompiler::generateFVar(Tree sig, Tree type, const string& file, const string& name)
877 {
878     // Check access (handling 'fFullCount' as a special case)
879     if ((name != fFullCount && !gGlobal->gAllowForeignVar)
880         || (name == fFullCount && (gGlobal->gOneSample >= 0 || gGlobal->gOneSampleControl))) {
881         stringstream error;
882         error << "ERROR : accessing foreign variable '" << name << "'"
883         << " is not allowed in this compilation mode!" << endl;
884         throw faustexception(error.str());
885     }
886 
887     fContainer->addIncludeFile(file);
888 
889     // Special case for 'count' parameter of the 'compute' method
890     if (name == fFullCount) {
891         return generateCacheCode(sig, InstBuilder::genLoadFunArgsVar(name));
892     } else {
893         int sig_type = getCertifiedSigType(sig)->nature();
894         pushExtGlobalDeclare(InstBuilder::genDecGlobalVar(name, genBasicFIRTyped(sig_type)));
895         return generateCacheCode(sig, InstBuilder::genLoadGlobalVar(name));
896     }
897 }
898 
899 /*****************************************************************************
900  INPUTS - OUTPUTS
901  *****************************************************************************/
902 
generateInput(Tree sig,int idx)903 ValueInst* InstructionsCompiler::generateInput(Tree sig, int idx)
904 {
905     // Cast to internal float
906     ValueInst* res;
907     // HACK for Rust backend
908     if (gGlobal->gOutputLang == "rust") {
909         res = InstBuilder::genLoadStackVar(subst("*input$0", T(idx)));
910     } else if (gGlobal->gOneSampleControl) {
911         res = InstBuilder::genLoadStructVar(subst("input$0", T(idx)));
912     } else if (gGlobal->gOneSample >= 0) {
913         res = InstBuilder::genLoadArrayStackVar("inputs", InstBuilder::genInt32NumInst(idx));
914     } else {
915         res = InstBuilder::genLoadArrayStackVar(subst("input$0", T(idx)), getCurrentLoopIndex());
916     }
917 
918     // Cast to internal float
919     res = InstBuilder::genCastFloatInst(res);
920 
921     if (gGlobal->gInPlace) {
922         // inputs must be cached for in-place transformations
923         return forceCacheCode(sig, res);
924     } else {
925         return generateCacheCode(sig, res);
926     }
927 }
928 
929 /*****************************************************************************
930  BINARY OPERATION
931  *****************************************************************************/
932 
generateBinOp(Tree sig,int opcode,Tree a1,Tree a2)933 ValueInst* InstructionsCompiler::generateBinOp(Tree sig, int opcode, Tree a1, Tree a2)
934 {
935     int t1 = getCertifiedSigType(a1)->nature();
936     int t2 = getCertifiedSigType(a2)->nature();
937     int t3 = getCertifiedSigType(sig)->nature();
938 
939     ValueInst* res;
940     ValueInst* v1 = CS(a1);
941     ValueInst* v2 = CS(a2);
942 
943     // Logical and shift operations work on kInt32, so cast both operands here
944     if (isLogicalOpcode(opcode) || isShiftOpcode(opcode)) {
945         res = InstBuilder::genBinopInst(opcode, promote2int(t1, v1), promote2int(t2, v2));
946         res = cast2real(t3, res);
947         // Boolean operations work on kInt32 or kReal, result is kInt32
948     } else if (isBoolOpcode(opcode)) {
949         if ((t1 == kReal) || (t2 == kReal)) {
950             res = InstBuilder::genBinopInst(opcode, promote2real(t1, v1), promote2real(t2, v2));
951         } else {
952             res = InstBuilder::genBinopInst(opcode, v1, v2);
953         }
954         // HACK for Rust backend
955         if (gGlobal->gOutputLang == "rust") {
956             res = InstBuilder::genCastInt32Inst(res);
957         }
958         // One of a1 or a2 is kReal, operation is done on kReal
959     } else if ((t1 == kReal) || (t2 == kReal)) {
960         res = cast2int(t3, InstBuilder::genBinopInst(opcode, promote2real(t1, v1), promote2real(t2, v2)));
961         // Otherwise kInt32 operation
962     } else if (opcode == kDiv) {
963         // special handling for division, we always want a float division
964         res = cast2int(t3, InstBuilder::genBinopInst(opcode, promote2real(t1, v1), promote2real(t2, v2)));
965     } else {
966         res = cast2real(t3, InstBuilder::genBinopInst(opcode, v1, v2));
967     }
968 
969     return generateCacheCode(sig, res);
970 }
971 
972 /*****************************************************************************
973  Primitive Operations
974  *****************************************************************************/
975 
generateFFun(Tree sig,Tree ff,Tree largs)976 ValueInst* InstructionsCompiler::generateFFun(Tree sig, Tree ff, Tree largs)
977 {
978     fContainer->addIncludeFile(ffincfile(ff));
979     fContainer->addLibrary(fflibfile(ff));
980     string funname = ffname(ff);
981 
982     if (gGlobal->gAllowForeignFunction || gGlobal->hasMathForeignFunction(funname)) {
983 
984         list<ValueInst*>  args_value;
985         list<NamedTyped*> args_types;
986 
987         for (int i = 0; i < ffarity(ff); i++) {
988             Tree parameter = nth(largs, i);
989             // Reversed...
990             int         sig_argtype = ffargtype(ff, (ffarity(ff) - 1) - i);
991             BasicTyped* argtype     = genBasicFIRTyped(sig_argtype);
992             args_types.push_back(InstBuilder::genNamedTyped("dummy" + to_string(i), argtype));
993             args_value.push_back(InstBuilder::genCastInst(CS(parameter), argtype));
994         }
995 
996         // Add function declaration
997         FunTyped* fun_type = InstBuilder::genFunTyped(args_types, genBasicFIRTyped(ffrestype(ff)));
998         pushExtGlobalDeclare(InstBuilder::genDeclareFunInst(funname, fun_type));
999 
1000         return generateCacheCode(sig, InstBuilder::genCastInst(InstBuilder::genFunCallInst(funname, args_value),
1001                                                                genBasicFIRTyped(ffrestype(ff))));
1002     } else {
1003         stringstream error;
1004         error << "ERROR : calling foreign function '" << funname << "'"
1005               << " is not allowed in this compilation mode!" << endl;
1006         throw faustexception(error.str());
1007     }
1008 }
1009 
1010 /*****************************************************************************
1011  CACHE CODE
1012  *****************************************************************************/
1013 
getTypedNames(::Type t,const string & prefix,Typed::VarType & ctype,string & vname)1014 void InstructionsCompiler::getTypedNames(::Type t, const string& prefix, Typed::VarType& ctype, string& vname)
1015 {
1016     if (t->nature() == kInt) {
1017         ctype = Typed::kInt32;
1018         vname = subst("i$0", gGlobal->getFreshID(prefix));
1019     } else {
1020         ctype = itfloat();
1021         vname = subst("f$0", gGlobal->getFreshID(prefix));
1022     }
1023 }
1024 
generateCacheCode(Tree sig,ValueInst * exp)1025 ValueInst* InstructionsCompiler::generateCacheCode(Tree sig, ValueInst* exp)
1026 {
1027     ValueInst* code;
1028 
1029     // Check reentrance
1030     if (getCompiledExpression(sig, code)) {
1031         return code;
1032     }
1033 
1034     string         vname;
1035     Typed::VarType ctype;
1036     int            sharing = getSharingCount(sig);
1037     old_Occurences* o      = fOccMarkup->retrieve(sig);
1038     faustassert(o);
1039 
1040     // Check for expression occuring in delays
1041     if (o->getMaxDelay() > 0) {
1042         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
1043         if (sharing > 1) {
1044             return generateDelayVec(sig, generateVariableStore(sig, exp), ctype, vname, o->getMaxDelay());
1045         } else {
1046             return generateDelayVec(sig, exp, ctype, vname, o->getMaxDelay());
1047         }
1048 
1049     } else if (sharing > 1 || (o->hasMultiOccurences())) {
1050         return generateVariableStore(sig, exp);
1051 
1052     } else if (sharing == 1) {
1053         return exp;
1054 
1055     } else {
1056         stringstream error;
1057         error << "ERROR in sharing count (" << sharing << ") for " << *sig << endl;
1058         throw faustexception(error.str());
1059     }
1060 }
1061 
1062 // Like generateCacheCode but we force caching like if sharing was always > 1
forceCacheCode(Tree sig,ValueInst * exp)1063 ValueInst* InstructionsCompiler::forceCacheCode(Tree sig, ValueInst* exp)
1064 {
1065     ValueInst* code;
1066 
1067     // check reentrance
1068     if (getCompiledExpression(sig, code)) {
1069         return code;
1070     }
1071 
1072     string         vname;
1073     Typed::VarType ctype;
1074     old_Occurences*    o = fOccMarkup->retrieve(sig);
1075     faustassert(o);
1076 
1077     // check for expression occuring in delays
1078     if (o->getMaxDelay() > 0) {
1079         getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
1080         return generateDelayVec(sig, generateVariableStore(sig, exp), ctype, vname, o->getMaxDelay());
1081     } else {
1082         return generateVariableStore(sig, exp);
1083     }
1084 }
1085 
generateVariableStore(Tree sig,ValueInst * exp)1086 ValueInst* InstructionsCompiler::generateVariableStore(Tree sig, ValueInst* exp)
1087 {
1088     string         vname, vname_perm;
1089     Typed::VarType ctype;
1090     ::Type         t = getCertifiedSigType(sig);
1091     old_Occurences*    o = fOccMarkup->retrieve(sig);
1092     faustassert(o);
1093 
1094     switch (t->variability()) {
1095         case kKonst:
1096             getTypedNames(t, "Const", ctype, vname);
1097             // The variable is used in compute (kBlock or kSamp), so define is as a field in the DSP struct
1098             if (o->getOccurence(kBlock) || o->getOccurence(kSamp)) {
1099                 pushDeclare(InstBuilder::genDecStructVar(vname, InstBuilder::genBasicTyped(ctype)));
1100                 pushInitMethod(InstBuilder::genStoreStructVar(vname, exp));
1101                 return InstBuilder::genLoadStructVar(vname);
1102             } else {
1103                 // Otherwise it can stay as a local variable
1104                 pushInitMethod(InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(ctype), exp));
1105                 return InstBuilder::genLoadStackVar(vname);
1106             }
1107 
1108         case kBlock:
1109             if (gGlobal->gOneSample >= 0 || gGlobal->gOneSampleControl) {
1110                 if (t->nature() == kInt) {
1111                     pushComputeBlockMethod(InstBuilder::genStoreArrayStackVar(
1112                         "iControl", InstBuilder::genInt32NumInst(fContainer->fInt32ControlNum), exp));
1113                     ValueInst* res = InstBuilder::genLoadArrayStackVar(
1114                         "iControl", InstBuilder::genInt32NumInst(fContainer->fInt32ControlNum));
1115                     fContainer->fInt32ControlNum++;
1116                     return res;
1117                 } else {
1118                     pushComputeBlockMethod(InstBuilder::genStoreArrayStackVar(
1119                         "fControl", InstBuilder::genInt32NumInst(fContainer->fRealControlNum), exp));
1120                     ValueInst* res = InstBuilder::genLoadArrayStackVar(
1121                         "fControl", InstBuilder::genInt32NumInst(fContainer->fRealControlNum));
1122                     fContainer->fRealControlNum++;
1123                     return res;
1124                 }
1125             } else {
1126                 getTypedNames(t, "Slow", ctype, vname);
1127                 pushComputeBlockMethod(InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(ctype), exp));
1128                 return InstBuilder::genLoadStackVar(vname);
1129             }
1130 
1131         case kSamp:
1132             getTypedNames(t, "Temp", ctype, vname);
1133 
1134             // Only generated for the DSP loop
1135             if (gGlobal->gHasTeeLocal) {
1136 
1137                 if (dynamic_cast<NullValueInst*>(getConditionCode(sig))) {
1138                     pushComputeDSPMethod(InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(ctype)));
1139                 } else {
1140                     getTypedNames(t, "TempPerm", ctype, vname_perm);
1141                     pushDeclare(InstBuilder::genDecStructVar(vname_perm, InstBuilder::genBasicTyped(ctype)));
1142                     pushClearMethod(InstBuilder::genStoreStructVar(vname_perm, InstBuilder::genTypedZero(ctype)));
1143 
1144                     pushComputeBlockMethod(InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(ctype), InstBuilder::genLoadStructVar(vname_perm)));
1145                     pushPostComputeBlockMethod(InstBuilder::genStoreStructVar(vname_perm, InstBuilder::genLoadStackVar(vname)));
1146                 }
1147 
1148                 return InstBuilder::genTeeVar(vname, exp);
1149             } else {
1150 
1151                 if (dynamic_cast<NullValueInst*>(getConditionCode(sig))) {
1152                     pushComputeDSPMethod(InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(ctype), exp));
1153                     return InstBuilder::genLoadStackVar(vname);
1154                 } else {
1155                     getTypedNames(t, "TempPerm", ctype, vname_perm);
1156                     pushDeclare(InstBuilder::genDecStructVar(vname_perm, InstBuilder::genBasicTyped(ctype)));
1157                     pushClearMethod(InstBuilder::genStoreStructVar(vname_perm, InstBuilder::genTypedZero(ctype)));
1158 
1159                     if (gGlobal->gOneSample >= 0 || gGlobal->gOneSampleControl) {
1160                         pushComputeDSPMethod(InstBuilder::genControlInst(getConditionCode(sig), InstBuilder::genStoreStructVar(vname_perm, exp)));
1161                         return InstBuilder::genLoadStructVar(vname_perm);
1162                     } else {
1163                         pushComputeBlockMethod(InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(ctype), InstBuilder::genLoadStructVar(vname_perm)));
1164                         pushComputeDSPMethod(InstBuilder::genControlInst(getConditionCode(sig), InstBuilder::genStoreStackVar(vname, exp)));
1165                         pushPostComputeBlockMethod(InstBuilder::genStoreStructVar(vname_perm, InstBuilder::genLoadStackVar(vname)));
1166                         return InstBuilder::genLoadStackVar(vname);
1167                     }
1168                 }
1169             }
1170 
1171         default:
1172             return InstBuilder::genNullValueInst();
1173     }
1174 }
1175 
1176 /*****************************************************************************
1177  CASTING
1178  *****************************************************************************/
1179 
1180 // Generate cast only when really necessary...
generateIntCast(Tree sig,Tree x)1181 ValueInst* InstructionsCompiler::generateIntCast(Tree sig, Tree x)
1182 {
1183     return generateCacheCode(sig,
1184                              (getCertifiedSigType(x)->nature() != kInt) ? InstBuilder::genCastInt32Inst(CS(x)) : CS(x));
1185 }
1186 
1187 // Generate cast only when really necessary...
generateFloatCast(Tree sig,Tree x)1188 ValueInst* InstructionsCompiler::generateFloatCast(Tree sig, Tree x)
1189 {
1190     return generateCacheCode(
1191         sig, (getCertifiedSigType(x)->nature() != kReal) ? InstBuilder::genCastFloatInst(CS(x)) : CS(x));
1192 }
1193 
1194 /*****************************************************************************
1195  user interface elements
1196  *****************************************************************************/
1197 
generateButtonAux(Tree sig,Tree path,const string & name)1198 ValueInst* InstructionsCompiler::generateButtonAux(Tree sig, Tree path, const string& name)
1199 {
1200     string varname = gGlobal->getFreshID(name);
1201     Typed* type    = InstBuilder::genFloatMacroTyped();
1202 
1203     pushDeclare(InstBuilder::genDecStructVar(varname, type));
1204     pushResetUIInstructions(
1205         InstBuilder::genStoreStructVar(varname, InstBuilder::genRealNumInst(Typed::kFloatMacro, 0)));
1206     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
1207 
1208     // Cast to internal float
1209     return generateCacheCode(sig, InstBuilder::genCastFloatInst(InstBuilder::genLoadStructVar(varname)));
1210 }
1211 
generateButton(Tree sig,Tree path)1212 ValueInst* InstructionsCompiler::generateButton(Tree sig, Tree path)
1213 {
1214     return generateButtonAux(sig, path, "fButton");
1215 }
1216 
generateCheckbox(Tree sig,Tree path)1217 ValueInst* InstructionsCompiler::generateCheckbox(Tree sig, Tree path)
1218 {
1219     return generateButtonAux(sig, path, "fCheckbox");
1220 }
1221 
generateSliderAux(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step,const string & name)1222 ValueInst* InstructionsCompiler::generateSliderAux(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step,
1223                                                    const string& name)
1224 {
1225     string varname = gGlobal->getFreshID(name);
1226     Typed* type    = InstBuilder::genFloatMacroTyped();
1227 
1228     pushDeclare(InstBuilder::genDecStructVar(varname, type));
1229     pushResetUIInstructions(
1230         InstBuilder::genStoreStructVar(varname, InstBuilder::genRealNumInst(Typed::kFloatMacro, tree2float(cur))));
1231     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
1232 
1233     // Cast to internal float
1234     return generateCacheCode(sig, InstBuilder::genCastFloatInst(InstBuilder::genLoadStructVar(varname)));
1235 }
1236 
generateVSlider(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)1237 ValueInst* InstructionsCompiler::generateVSlider(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
1238 {
1239     return generateSliderAux(sig, path, cur, min, max, step, "fVslider");
1240 }
generateHSlider(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)1241 ValueInst* InstructionsCompiler::generateHSlider(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
1242 {
1243     return generateSliderAux(sig, path, cur, min, max, step, "fHslider");
1244 }
1245 
generateNumEntry(Tree sig,Tree path,Tree cur,Tree min,Tree max,Tree step)1246 ValueInst* InstructionsCompiler::generateNumEntry(Tree sig, Tree path, Tree cur, Tree min, Tree max, Tree step)
1247 {
1248     return generateSliderAux(sig, path, cur, min, max, step, "fEntry");
1249 }
1250 
generateBargraphAux(Tree sig,Tree path,Tree min,Tree max,ValueInst * exp,const string & name)1251 ValueInst* InstructionsCompiler::generateBargraphAux(Tree sig, Tree path, Tree min, Tree max, ValueInst* exp,
1252                                                      const string& name)
1253 {
1254     string varname = gGlobal->getFreshID(name);
1255     pushDeclare(InstBuilder::genDecStructVar(varname, InstBuilder::genFloatMacroTyped()));
1256     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
1257 
1258     ::Type t = getCertifiedSigType(sig);
1259 
1260     // Cast to external float
1261     StoreVarInst* res = InstBuilder::genStoreStructVar(varname, InstBuilder::genCastFloatMacroInst(exp));
1262 
1263     switch (t->variability()) {
1264         case kKonst:
1265             pushResetUIInstructions(res);
1266             break;
1267 
1268         case kBlock:
1269             pushComputeBlockMethod(res);
1270             break;
1271 
1272         case kSamp:
1273             pushComputeDSPMethod(InstBuilder::genControlInst(getConditionCode(sig), res));
1274             break;
1275     }
1276 
1277     return generateCacheCode(sig, (t->nature() == kInt)
1278                                       ? InstBuilder::genCastInt32Inst(InstBuilder::genLoadStructVar(varname))
1279                                       : InstBuilder::genLoadStructVar(varname));
1280 }
1281 
generateVBargraph(Tree sig,Tree path,Tree min,Tree max,ValueInst * exp)1282 ValueInst* InstructionsCompiler::generateVBargraph(Tree sig, Tree path, Tree min, Tree max, ValueInst* exp)
1283 {
1284     return generateBargraphAux(sig, path, min, max, exp, "fVbargraph");
1285 }
1286 
generateHBargraph(Tree sig,Tree path,Tree min,Tree max,ValueInst * exp)1287 ValueInst* InstructionsCompiler::generateHBargraph(Tree sig, Tree path, Tree min, Tree max, ValueInst* exp)
1288 {
1289     return generateBargraphAux(sig, path, min, max, exp, "fHbargraph");
1290 }
1291 
generateSoundfile(Tree sig,Tree path)1292 ValueInst* InstructionsCompiler::generateSoundfile(Tree sig, Tree path)
1293 {
1294     string varname = gGlobal->getFreshID("fSoundfile");
1295     string SFcache = varname + "ca";
1296 
1297     addUIWidget(reverse(tl(path)), uiWidget(hd(path), tree(varname), sig));
1298 
1299     pushDeclare(InstBuilder::genDecStructVar(varname, InstBuilder::genBasicTyped(Typed::kSound_ptr)));
1300 
1301     if (gGlobal->gUseDefaultSound) {
1302         BlockInst* block = InstBuilder::genBlockInst();
1303         block->pushBackInst(InstBuilder::genStoreStructVar(varname, InstBuilder::genLoadGlobalVar("defaultsound")));
1304 
1305         pushResetUIInstructions(InstBuilder::genIfInst(
1306             InstBuilder::genEqual(InstBuilder::genCastInst(InstBuilder::genLoadStructVar(varname),
1307                                                            InstBuilder::genBasicTyped(Typed::kUint_ptr)),
1308                                   InstBuilder::genTypedZero(Typed::kSound_ptr)),
1309             block, InstBuilder::genBlockInst()));
1310     }
1311 
1312     if (gGlobal->gOneSample >= 0) {
1313         pushDeclare(InstBuilder::genDecStructVar(SFcache, InstBuilder::genBasicTyped(Typed::kSound_ptr)));
1314         pushComputeBlockMethod(InstBuilder::genStoreStructVar(SFcache, InstBuilder::genLoadStructVar(varname)));
1315         pushPostComputeBlockMethod(InstBuilder::genStoreStructVar(varname, InstBuilder::genLoadStructVar(SFcache)));
1316     } else {
1317         pushComputeBlockMethod(InstBuilder::genDecStackVar(SFcache, InstBuilder::genBasicTyped(Typed::kSound_ptr),
1318                                                            InstBuilder::genLoadStructVar(varname)));
1319         pushPostComputeBlockMethod(InstBuilder::genStoreStructVar(varname, InstBuilder::genLoadStackVar(SFcache)));
1320     }
1321 
1322     return InstBuilder::genLoadStructVar(varname);
1323 }
1324 
generateSoundfileLength(Tree sig,ValueInst * sf,ValueInst * x)1325 ValueInst* InstructionsCompiler::generateSoundfileLength(Tree sig, ValueInst* sf, ValueInst* x)
1326 {
1327     LoadVarInst* load = dynamic_cast<LoadVarInst*>(sf);
1328     faustassert(load);
1329 
1330     Typed* type = InstBuilder::genBasicTyped(Typed::kInt32_ptr);
1331 
1332     string SFcache        = load->fAddress->getName() + "ca";
1333     string SFcache_length = gGlobal->getFreshID(SFcache + "_le");
1334 
1335     if (gGlobal->gOneSample >= 0) {
1336 
1337         // Struct access using an index that will be converted as a field name
1338         ValueInst* v1 = InstBuilder::genLoadStructPtrVar(SFcache, Address::kStruct, InstBuilder::genInt32NumInst(1));
1339 
1340         pushDeclare(InstBuilder::genDecStructVar(SFcache_length, type));
1341         pushComputeBlockMethod(InstBuilder::genStoreStructVar(SFcache_length, v1));
1342         return InstBuilder::genLoadArrayStructVar(SFcache_length, x);
1343     } else {
1344 
1345         // Struct access using an index that will be converted as a field name
1346         ValueInst* v1 = InstBuilder::genLoadStructPtrVar(SFcache, Address::kStack, InstBuilder::genInt32NumInst(1));
1347 
1348         pushComputeBlockMethod(InstBuilder::genDecStackVar(SFcache_length, type, v1));
1349         return InstBuilder::genLoadArrayStackVar(SFcache_length, x);
1350     }
1351 }
1352 
generateSoundfileRate(Tree sig,ValueInst * sf,ValueInst * x)1353 ValueInst* InstructionsCompiler::generateSoundfileRate(Tree sig, ValueInst* sf, ValueInst* x)
1354 {
1355     LoadVarInst* load = dynamic_cast<LoadVarInst*>(sf);
1356     faustassert(load);
1357 
1358     Typed* type = InstBuilder::genBasicTyped(Typed::kInt32_ptr);
1359 
1360     string SFcache      = load->fAddress->getName() + "ca";
1361     string SFcache_rate = gGlobal->getFreshID(SFcache + "_ra");
1362 
1363     if (gGlobal->gOneSample >= 0) {
1364 
1365         // Struct access using an index that will be converted as a field name
1366         ValueInst* v1 = InstBuilder::genLoadStructPtrVar(SFcache, Address::kStruct, InstBuilder::genInt32NumInst(2));
1367 
1368         pushDeclare(InstBuilder::genDecStructVar(SFcache_rate, type));
1369         pushComputeBlockMethod(InstBuilder::genStoreStructVar(SFcache_rate, v1));
1370         return InstBuilder::genLoadArrayStructVar(SFcache_rate, x);
1371     } else {
1372 
1373         // Struct access using an index that will be converted as a field name
1374         ValueInst* v1 = InstBuilder::genLoadStructPtrVar(SFcache, Address::kStack, InstBuilder::genInt32NumInst(2));
1375 
1376         pushComputeBlockMethod(InstBuilder::genDecStackVar(SFcache_rate, type, v1));
1377         return InstBuilder::genLoadArrayStackVar(SFcache_rate, x);
1378     }
1379 }
1380 
generateSoundfileBuffer(Tree sig,ValueInst * sf,ValueInst * x,ValueInst * y,ValueInst * z)1381 ValueInst* InstructionsCompiler::generateSoundfileBuffer(Tree sig, ValueInst* sf, ValueInst* x, ValueInst* y,
1382                                                          ValueInst* z)
1383 {
1384     LoadVarInst* load = dynamic_cast<LoadVarInst*>(sf);
1385     faustassert(load);
1386 
1387     Typed* type1 = InstBuilder::genBasicTyped(itfloatptrptr());
1388     Typed* type2 = InstBuilder::genBasicTyped(itfloat());
1389     Typed* type3 = InstBuilder::genBasicTyped(Typed::kInt32_ptr);
1390 
1391     string SFcache             = load->fAddress->getName() + "ca";
1392     string SFcache_buffer      = gGlobal->getFreshID(SFcache + "_bu");
1393     string SFcache_buffer_chan = gGlobal->getFreshID(SFcache + "_bu_ch");
1394     string SFcache_offset      = gGlobal->getFreshID(SFcache + "_of");
1395 
1396     if (gGlobal->gOneSample >= 0) {
1397 
1398         // Struct access using an index that will be converted as a field name
1399         ValueInst* v1 = InstBuilder::genLoadStructPtrVar(SFcache, Address::kStruct, InstBuilder::genInt32NumInst(3));
1400 
1401         pushDeclare(InstBuilder::genDecStructVar(SFcache_offset, type3));
1402         pushComputeBlockMethod(InstBuilder::genStoreStructVar(SFcache_offset, v1));
1403 
1404         // Struct access using an index that will be converted as a field name
1405         LoadVarInst* load1 =
1406             InstBuilder::genLoadStructPtrVar(SFcache, Address::kStruct, InstBuilder::genInt32NumInst(0));
1407 
1408         pushDeclare(InstBuilder::genDecStructVar(SFcache_buffer, type1));
1409         pushComputeBlockMethod(InstBuilder::genStoreStructVar(SFcache_buffer,InstBuilder::genCastInst(load1, type1)));
1410 
1411         pushDeclare(InstBuilder::genDecStructVar(SFcache_buffer_chan, InstBuilder::genArrayTyped(type2, 0)));
1412         pushComputeBlockMethod(InstBuilder::genStoreStructVar(
1413             SFcache_buffer_chan, InstBuilder::genLoadStructPtrVar(SFcache_buffer, Address::kStruct, x)));
1414 
1415         return InstBuilder::genLoadStructPtrVar(
1416             SFcache_buffer_chan, Address::kStruct,
1417             InstBuilder::genAdd(InstBuilder::genLoadArrayStructVar(SFcache_offset, y), z));
1418     } else {
1419 
1420         // Struct access using an index that will be converted as a field name
1421         ValueInst* v1 = InstBuilder::genLoadStructPtrVar(SFcache, Address::kStack, InstBuilder::genInt32NumInst(3));
1422 
1423         pushComputeBlockMethod(InstBuilder::genDecStackVar(SFcache_offset, type3, v1));
1424 
1425         // Struct access using an index that will be converted as a field name
1426         LoadVarInst* load1 =
1427             InstBuilder::genLoadStructPtrVar(SFcache, Address::kStack, InstBuilder::genInt32NumInst(0));
1428 
1429         pushComputeBlockMethod(InstBuilder::genDecStackVar(SFcache_buffer, type1, InstBuilder::genCastInst(load1, type1)));
1430         pushComputeBlockMethod(
1431             InstBuilder::genDecStackVar(SFcache_buffer_chan, InstBuilder::genArrayTyped(type2, 0),
1432                                         InstBuilder::genLoadStructPtrVar(SFcache_buffer, Address::kStack, x)));
1433         return InstBuilder::genLoadStructPtrVar(
1434             SFcache_buffer_chan, Address::kStack,
1435             InstBuilder::genAdd(InstBuilder::genLoadArrayStackVar(SFcache_offset, y), z));
1436     }
1437 }
1438 
1439 /*****************************************************************************
1440  TABLES
1441  *****************************************************************************/
1442 
1443 /*----------------------------------------------------------------------------
1444  sigTable : table declaration
1445  ----------------------------------------------------------------------------*/
1446 
generateTable(Tree sig,Tree tsize,Tree content)1447 ValueInst* InstructionsCompiler::generateTable(Tree sig, Tree tsize, Tree content)
1448 {
1449     int size;
1450     if (!isSigInt(tsize, &size)) {
1451         stringstream error;
1452         error << "ERROR in generateTable : " << *tsize << " is not an integer expression " << endl;
1453         throw faustexception(error.str());
1454     }
1455 
1456     ValueInst*     generator = CS(content);
1457     Typed::VarType ctype;
1458     Tree           g;
1459     string         vname;
1460 
1461     // already compiled but check if we need to add declarations
1462     faustassert(isSigGen(content, g));
1463     pair<string, string> kvnames;
1464     if (!fInstanceInitProperty.get(g, kvnames)) {
1465         // not declared here, we add a declaration
1466         bool b = fStaticInitProperty.get(g, kvnames);
1467         faustassert(b);
1468         list<ValueInst*> args;
1469         if (gGlobal->gMemoryManager) {
1470             args.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1471         }
1472         ValueInst* obj = InstBuilder::genFunCallInst("new" + kvnames.first, args);
1473         pushInitMethod(InstBuilder::genDecStackVar(
1474             kvnames.second, InstBuilder::genNamedTyped(kvnames.first, InstBuilder::genBasicTyped(Typed::kObj_ptr)),
1475             obj));
1476 
1477         // HACK for Rust and Julia backends
1478         if (gGlobal->gOutputLang != "rust" && gGlobal->gOutputLang != "julia") {
1479             // Delete object
1480             list<ValueInst*> args3;
1481             if (gGlobal->gMemoryManager) {
1482                 args3.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1483             }
1484             args3.push_back(generator);
1485             pushPostInitMethod(InstBuilder::genVoidFunCallInst("delete" + kvnames.first, args3));
1486         }
1487     }
1488 
1489     // Define table name and type
1490     getTypedNames(getCertifiedSigType(content), "tbl", ctype, vname);
1491 
1492     // Table declaration
1493     pushDeclare(
1494         InstBuilder::genDecStructVar(vname, InstBuilder::genArrayTyped(InstBuilder::genBasicTyped(ctype), size)));
1495 
1496     string tablename;
1497     getTableNameProperty(content, tablename);
1498 
1499     // Init content generator
1500     list<ValueInst*> args1;
1501     args1.push_back(generator);
1502     args1.push_back(InstBuilder::genLoadFunArgsVar("sample_rate"));
1503     pushInitMethod(InstBuilder::genVoidFunCallInst("instanceInit" + tablename, args1, true));
1504 
1505     // Fill the table
1506     list<ValueInst*> args2;
1507     args2.push_back(generator);
1508     args2.push_back(InstBuilder::genInt32NumInst(size));
1509     // HACK for Rust backend
1510     args2.push_back(InstBuilder::genLoadMutRefStructVar(vname));
1511     pushInitMethod(InstBuilder::genVoidFunCallInst("fill" + tablename, args2, true));
1512 
1513     // Return table access
1514     return InstBuilder::genLoadStructVar(vname);
1515 }
1516 
generateStaticTable(Tree sig,Tree tsize,Tree content)1517 ValueInst* InstructionsCompiler::generateStaticTable(Tree sig, Tree tsize, Tree content)
1518 {
1519     int size;
1520     if (!isSigInt(tsize, &size)) {
1521         stringstream error;
1522         error << "ERROR in generateStaticTable : " << *tsize << " is not an integer expression " << endl;
1523         throw faustexception(error.str());
1524     }
1525 
1526     Tree           g;
1527     ValueInst*     cexp;
1528     Typed::VarType ctype;
1529     string         vname;
1530 
1531     ensure(isSigGen(content, g));
1532 
1533     if (!getCompiledExpression(content, cexp)) {
1534         cexp = setCompiledExpression(content, generateStaticSigGen(content, g));
1535     } else {
1536         // already compiled but check if we need to add declarations
1537         pair<string, string> kvnames;
1538         if (!fStaticInitProperty.get(g, kvnames)) {
1539             // not declared here, we add a declaration
1540             bool b = fInstanceInitProperty.get(g, kvnames);
1541             faustassert(b);
1542             list<ValueInst*> args;
1543             if (gGlobal->gMemoryManager) {
1544                 args.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1545             }
1546             ValueInst* obj = InstBuilder::genFunCallInst("new" + kvnames.first, args);
1547             pushInitMethod(InstBuilder::genDecStackVar(
1548                 kvnames.second, InstBuilder::genNamedTyped(kvnames.first, InstBuilder::genBasicTyped(Typed::kObj_ptr)),
1549                 obj));
1550 
1551             // HACK for Rust and Julia backends
1552             if (gGlobal->gOutputLang != "rust" && gGlobal->gOutputLang != "julia") {
1553                 // Delete object
1554                 list<ValueInst*> args3;
1555                 if (gGlobal->gMemoryManager) {
1556                     args3.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1557                 }
1558                 args3.push_back(cexp);
1559                 pushPostInitMethod(InstBuilder::genVoidFunCallInst("delete" + kvnames.first, args3));
1560             }
1561         }
1562     }
1563 
1564     // Define table name and type
1565     getTypedNames(getCertifiedSigType(content), "tbl", ctype, vname);
1566 
1567     string tablename;
1568     getTableNameProperty(content, tablename);
1569     vname += tablename;
1570 
1571     // Table declaration
1572     if (gGlobal->gMemoryManager) {
1573         pushGlobalDeclare(InstBuilder::genDecStaticStructVar(
1574             vname, InstBuilder::genArrayTyped(InstBuilder::genBasicTyped(ctype), 0), InstBuilder::genInt32NumInst(0)));
1575     } else {
1576         pushGlobalDeclare(InstBuilder::genDecStaticStructVar(
1577             vname, InstBuilder::genArrayTyped(InstBuilder::genBasicTyped(ctype), size)));
1578     }
1579 
1580     // Init content generator
1581     list<ValueInst*> args1;
1582     args1.push_back(cexp);
1583     args1.push_back(InstBuilder::genLoadFunArgsVar("sample_rate"));
1584     pushStaticInitMethod(InstBuilder::genVoidFunCallInst("instanceInit" + tablename, args1, true));
1585 
1586     if (gGlobal->gMemoryManager) {
1587         list<ValueInst*> alloc_args;
1588         alloc_args.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1589         alloc_args.push_back(InstBuilder::genInt32NumInst(size * gGlobal->gTypeSizeMap[ctype]));
1590         pushStaticInitMethod(InstBuilder::genStoreStaticStructVar(
1591             vname, InstBuilder::genCastInst(InstBuilder::genFunCallInst("allocate", alloc_args, true),
1592                                             InstBuilder::genArrayTyped(InstBuilder::genBasicTyped(ctype), 0))));
1593 
1594         list<ValueInst*> destroy_args;
1595         destroy_args.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1596         destroy_args.push_back(InstBuilder::genLoadStaticStructVar(vname));
1597         pushStaticDestroyMethod(InstBuilder::genVoidFunCallInst("destroy", destroy_args, true));
1598     }
1599 
1600     // Fill the table
1601     list<ValueInst*> args2;
1602     args2.push_back(cexp);
1603     args2.push_back(InstBuilder::genInt32NumInst(size));
1604     // HACK for Rust backend
1605     args2.push_back(InstBuilder::genLoadStaticMutRefStructVar(vname));
1606     pushStaticInitMethod(InstBuilder::genVoidFunCallInst("fill" + tablename, args2, true));
1607 
1608     // Return table access
1609     return InstBuilder::genLoadStaticStructVar(vname);
1610 }
1611 
1612 /*----------------------------------------------------------------------------
1613  sigWRTable : table assignment
1614  ----------------------------------------------------------------------------*/
1615 
generateWRTbl(Tree sig,Tree tbl,Tree idx,Tree data)1616 ValueInst* InstructionsCompiler::generateWRTbl(Tree sig, Tree tbl, Tree idx, Tree data)
1617 {
1618     ValueInst*   tblname    = CS(tbl);
1619     LoadVarInst* load_value = dynamic_cast<LoadVarInst*>(tblname);
1620     faustassert(load_value);
1621 
1622     Tree id, size, content;
1623     if (isSigTable(tbl, id, size, content)) {
1624         // Check write access
1625         if (gGlobal->gCheckTable != "") {
1626             // Check if index is inside the table range (to rework with a low, high, impose interval model)
1627             interval idx_i = getCertifiedSigType(idx)->getInterval();
1628             if (idx_i.lo < 0 || idx_i.hi >= tree2int(size)) {
1629                 stringstream error;
1630                 if (gGlobal->gCheckTable == "cat") {
1631                     error << "WARNING : WRTbl write index [" << idx_i.lo << ":" <<idx_i.hi
1632                           << "] is outside of table range (" << tree2int(size) << ") in "
1633                           << *sig << endl;
1634                     cerr << error.str();
1635                 } else {
1636                     error << "ERROR : WRTbl write index [" << idx_i.lo << ":" <<idx_i.hi
1637                           << "] is outside of table range (" << tree2int(size) << ") in "
1638                           << *sig << endl;
1639                     throw faustexception(error.str());
1640                 }
1641             }
1642         }
1643     }
1644 
1645     // Check types and possibly cast written value
1646     int table_type = getCertifiedSigType(tbl)->nature();
1647     int data_type  = getCertifiedSigType(data)->nature();
1648     ValueInst* cdata = (table_type != data_type) ? InstBuilder::genCastInst(CS(data), genBasicFIRTyped(table_type)) : CS(data);
1649     string vname = load_value->fAddress->getName();
1650 
1651     Type t2 = getCertifiedSigType(idx);
1652     Type t3 = getCertifiedSigType(data);
1653     // TODO : for a bug in type caching, t->variability() is not correct.
1654     // Therefore in the meantime we compute it manually. (YO 2020/03/30)
1655     int var = t2->variability() | t3->variability();
1656     switch (var) {
1657         case kKonst:
1658             pushInitMethod(InstBuilder::genStoreArrayStructVar(vname, CS(idx), cdata));
1659             break;
1660         case kBlock:
1661             pushComputeBlockMethod(InstBuilder::genStoreArrayStructVar(vname, CS(idx), cdata));
1662             break;
1663         default:
1664             pushComputeDSPMethod(InstBuilder::genControlInst(getConditionCode(sig), InstBuilder::genStoreArrayStructVar(vname, CS(idx), cdata)));
1665             break;
1666     }
1667 
1668     // Return table access
1669     return InstBuilder::genLoadStructVar(vname);
1670 }
1671 
1672 /*----------------------------------------------------------------------------
1673  sigRDTable : table access
1674  ----------------------------------------------------------------------------*/
1675 
generateRDTbl(Tree sig,Tree tbl,Tree idx)1676 ValueInst* InstructionsCompiler::generateRDTbl(Tree sig, Tree tbl, Tree idx)
1677 {
1678     // Test the special case of a read only table that can be compiled as a static member
1679     Tree                id, size, content;
1680     ValueInst*          tblname;
1681     Address::AccessType access;
1682 
1683     if (isSigTable(tbl, id, size, content)) {
1684         // Check read access
1685         if (gGlobal->gCheckTable != "") {
1686             // Check if index is inside the table range (to rework with a low, high, impose interval model)
1687             interval idx_i = getCertifiedSigType(idx)->getInterval();
1688             if (idx_i.lo < 0 || (idx_i.hi >= tree2int(size))) {
1689                 stringstream error;
1690 
1691                 if (gGlobal->gCheckTable == "cat") {
1692                     error << "WARNING : RDTbl read index [" << idx_i.lo << ":" <<idx_i.hi
1693                           << "] is outside of table range (" << tree2int(size) << ") in "
1694                           << *sig << endl;
1695                     cerr << error.str();
1696                 } else {
1697                     error << "ERROR : RDTbl read index [" << idx_i.lo << ":" <<idx_i.hi
1698                           << "] is outside of table range (" << tree2int(size) << ") in "
1699                           << *sig << endl;
1700                     throw faustexception(error.str());
1701                 }
1702             }
1703         }
1704         access = Address::kStaticStruct;
1705         if (!getCompiledExpression(tbl, tblname)) {
1706             tblname = setCompiledExpression(tbl, generateStaticTable(tbl, size, content));
1707         }
1708     } else {
1709         access  = Address::kStruct;
1710         tblname = CS(tbl);
1711     }
1712 
1713     LoadVarInst* load_value1 = dynamic_cast<LoadVarInst*>(tblname);
1714     faustassert(load_value1);
1715 
1716     LoadVarInst* load_value2 = InstBuilder::genLoadArrayVar(load_value1->fAddress->getName(), access, CS(idx));
1717     return generateCacheCode(sig, load_value2);
1718 }
1719 
generateSigGen(Tree sig,Tree content)1720 ValueInst* InstructionsCompiler::generateSigGen(Tree sig, Tree content)
1721 {
1722     string cname   = gGlobal->getFreshID(fContainer->getClassName() + "SIG");
1723     string signame = gGlobal->getFreshID("sig");
1724 
1725     CodeContainer* subcontainer = signal2Container(cname, content);
1726     fContainer->addSubContainer(subcontainer);
1727 
1728     // We must allocate an object of type "cname"
1729     list<ValueInst*> args;
1730     if (gGlobal->gMemoryManager) {
1731         args.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1732     }
1733     ValueInst* obj = InstBuilder::genFunCallInst("new" + cname, args);
1734     pushInitMethod(InstBuilder::genDecStackVar(
1735         signame, InstBuilder::genNamedTyped(cname, InstBuilder::genBasicTyped(Typed::kObj_ptr)), obj));
1736 
1737     // HACK for Rust an Julia backends
1738     if (gGlobal->gOutputLang != "rust" && gGlobal->gOutputLang != "julia") {
1739         // Delete object
1740         list<ValueInst*> args3;
1741         args3.push_back(InstBuilder::genLoadStackVar(signame));
1742         if (gGlobal->gMemoryManager) {
1743             args3.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1744         }
1745         pushPostInitMethod(InstBuilder::genVoidFunCallInst("delete" + cname, args3));
1746     }
1747 
1748     setTableNameProperty(sig, cname);
1749     fInstanceInitProperty.set(content, pair<string, string>(cname, signame));
1750 
1751     return InstBuilder::genLoadStackVar(signame);
1752 }
1753 
generateStaticSigGen(Tree sig,Tree content)1754 ValueInst* InstructionsCompiler::generateStaticSigGen(Tree sig, Tree content)
1755 {
1756     string cname   = gGlobal->getFreshID(fContainer->getClassName() + "SIG");
1757     string signame = gGlobal->getFreshID("sig");
1758 
1759     CodeContainer* subcontainer = signal2Container(cname, content);
1760     fContainer->addSubContainer(subcontainer);
1761 
1762     // We must allocate an object of type "cname"
1763     list<ValueInst*> args;
1764     if (gGlobal->gMemoryManager) {
1765         args.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1766     }
1767     ValueInst* obj = InstBuilder::genFunCallInst("new" + cname, args);
1768     pushStaticInitMethod(InstBuilder::genDecStackVar(
1769         signame, InstBuilder::genNamedTyped(cname, InstBuilder::genBasicTyped(Typed::kObj_ptr)), obj));
1770 
1771     // HACK for Rust and Julia backends
1772     if (gGlobal->gOutputLang != "rust" && gGlobal->gOutputLang != "julia") {
1773         // Delete object
1774         list<ValueInst*> args3;
1775         args3.push_back(InstBuilder::genLoadStackVar(signame));
1776         if (gGlobal->gMemoryManager) {
1777             args3.push_back(InstBuilder::genLoadStaticStructVar("fManager"));
1778         }
1779         pushPostStaticInitMethod(InstBuilder::genVoidFunCallInst("delete" + cname, args3));
1780     }
1781 
1782     setTableNameProperty(sig, cname);
1783     fStaticInitProperty.set(content, pair<string, string>(cname, signame));
1784 
1785     return InstBuilder::genLoadStackVar(signame);
1786 }
1787 
1788 /*****************************************************************************
1789  RECURSIONS
1790  *****************************************************************************/
1791 
1792 /**
1793  * Generate code for a projection of a group of mutually recursive definitions
1794  */
generateRecProj(Tree sig,Tree r,int i)1795 ValueInst* InstructionsCompiler::generateRecProj(Tree sig, Tree r, int i)
1796 {
1797     string     vname;
1798     Tree       var, le;
1799     ValueInst* res;
1800 
1801     if (!getVectorNameProperty(sig, vname)) {
1802         ensure(isRec(r, var, le));
1803         res = generateRec(r, var, le, i);
1804         ensure(getVectorNameProperty(sig, vname));
1805     } else {
1806         res = InstBuilder::genNullValueInst();  // Result not used
1807     }
1808     return res;
1809 }
1810 
1811 /**
1812  * Generate code for a group of mutually recursive definitions
1813  */
generateRec(Tree sig,Tree var,Tree le,int index)1814 ValueInst* InstructionsCompiler::generateRec(Tree sig, Tree var, Tree le, int index)
1815 {
1816     int N = len(le);
1817 
1818     ValueInst*             res = nullptr;
1819     vector<bool>           used(N);
1820     vector<int>            delay(N);
1821     vector<string>         vname(N);
1822     vector<Typed::VarType> ctype(N);
1823 
1824     // Prepare each element of a recursive definition
1825     for (int i = 0; i < N; i++) {
1826         Tree e = sigProj(i, sig);  // recreate each recursive definition
1827         if (fOccMarkup->retrieve(e)) {
1828             // This projection is used
1829             used[i] = true;
1830             getTypedNames(getCertifiedSigType(e), "Rec", ctype[i], vname[i]);
1831             setVectorNameProperty(e, vname[i]);
1832             delay[i] = fOccMarkup->retrieve(e)->getMaxDelay();
1833         } else {
1834             // This projection is not used therefore
1835             // we should not generate code for it
1836             used[i] = false;
1837         }
1838     }
1839 
1840     // Generate delayline for each element of a recursive definition
1841     for (int i = 0; i < N; i++) {
1842         if (used[i]) {
1843             Address::AccessType var_access;
1844             ValueInst* ccs = getConditionCode(nth(le, i));
1845             if (index == i) {
1846                 res = generateDelayLine(CS(nth(le, i)), ctype[i], vname[i], delay[i], var_access, ccs);
1847             } else {
1848                 generateDelayLine(CS(nth(le, i)), ctype[i], vname[i], delay[i], var_access, ccs);
1849             }
1850         }
1851     }
1852 
1853     return res;
1854 }
1855 
1856 /*****************************************************************************
1857  PREFIX, DELAY A PREFIX VALUE
1858  *****************************************************************************/
1859 
generateControl(Tree sig,Tree x,Tree y)1860 ValueInst* InstructionsCompiler::generateControl(Tree sig, Tree x, Tree y)
1861 {
1862     CS(y);
1863     return generateCacheCode(x, CS(x));
1864 }
1865 
generatePrefix(Tree sig,Tree x,Tree e)1866 ValueInst* InstructionsCompiler::generatePrefix(Tree sig, Tree x, Tree e)
1867 {
1868     string         vperm = gGlobal->getFreshID("M");
1869     string         vtemp = gGlobal->getFreshID("T");
1870     Typed::VarType type  = ctType(getCertifiedSigType(sig));
1871 
1872     // Table declaration
1873     pushDeclare(InstBuilder::genDecStructVar(vperm, InstBuilder::genBasicTyped(type)));
1874 
1875     // Init
1876     pushInitMethod(InstBuilder::genStoreStructVar(vperm, CS(x)));
1877 
1878     // Exec
1879     pushComputeDSPMethod(
1880         InstBuilder::genDecStructVar(vtemp, InstBuilder::genBasicTyped(type), InstBuilder::genLoadStructVar(vperm)));
1881     pushComputeDSPMethod(InstBuilder::genStoreStructVar(vperm, CS(e)));
1882 
1883     return InstBuilder::genLoadStackVar(vtemp);
1884 }
1885 
1886 /*****************************************************************************
1887  IOTA(n)
1888  *****************************************************************************/
1889 
generateIota(Tree sig,Tree arg)1890 ValueInst* InstructionsCompiler::generateIota(Tree sig, Tree arg)
1891 {
1892     // Result not used
1893     return InstBuilder::genNullValueInst();
1894 }
1895 
1896 /**
1897  * Generate code for a unique IOTA variable increased at each sample
1898  * and used to index ring buffers.
1899  */
ensureIotaCode()1900 void InstructionsCompiler::ensureIotaCode()
1901 {
1902      if (!fHasIota) {
1903         fHasIota = true;
1904         pushDeclare(InstBuilder::genDecStructVar("IOTA", InstBuilder::genInt32Typed()));
1905         pushClearMethod(InstBuilder::genStoreStructVar("IOTA", InstBuilder::genInt32NumInst(0)));
1906 
1907         FIRIndex value = FIRIndex(InstBuilder::genLoadStructVar("IOTA")) + 1;
1908         pushPostComputeDSPMethod(InstBuilder::genStoreStructVar("IOTA", value));
1909     }
1910 }
1911 
1912 /*****************************************************************************
1913  SELECT
1914  *****************************************************************************/
1915 
generateSelect2(Tree sig,Tree sel,Tree s1,Tree s2)1916 ValueInst* InstructionsCompiler::generateSelect2(Tree sig, Tree sel, Tree s1, Tree s2)
1917 {
1918     ValueInst* cond = CS(sel);
1919     ValueInst* v1   = CS(s1);
1920     ValueInst* v2   = CS(s2);
1921 
1922     ::Type ct1 = getCertifiedSigType(s1);
1923     ::Type ct2 = getCertifiedSigType(s2);
1924     int t1 = ct1->nature();
1925     int t2 = ct2->nature();
1926 
1927     // Type promotion
1928     if ((t1 == kReal) || (t2 == kReal)) {
1929         v1 = promote2real(t1, v1);
1930         v2 = promote2real(t2, v2);
1931     }
1932 
1933     string v_then, v_else;
1934     Typed::VarType t_then, t_else;
1935     getTypedNames(ct1, "Then", t_then, v_then);
1936     getTypedNames(ct2, "Else", t_else, v_else);
1937 
1938     // Create local variables to force proper execution of both branches of 'select2'
1939     switch (getCertifiedSigType(sig)->variability()) {
1940 
1941         case kBlock:
1942             // Local variable is only created if needed
1943             // that is if the expression is not already a 'simple value', constant or variable
1944             if (!v1->isSimpleValue()) {
1945                 pushComputeBlockMethod(InstBuilder::genDecStackVar(v_then, InstBuilder::genBasicTyped(t_then), v1));
1946                 v1 = InstBuilder::genLoadStackVar(v_then);
1947             }
1948             if (!v2->isSimpleValue()) {
1949                 pushComputeBlockMethod(InstBuilder::genDecStackVar(v_else, InstBuilder::genBasicTyped(t_else), v2));
1950                 v2 = InstBuilder::genLoadStackVar(v_else);
1951             }
1952             break;
1953 
1954         case kSamp:
1955             // Local variable is only created if needed
1956             // that is if the expression is not already a 'simple value', constant or variable
1957             if (!v1->isSimpleValue()) {
1958                 pushComputeDSPMethod(InstBuilder::genDecStackVar(v_then, InstBuilder::genBasicTyped(t_then), v1));
1959                 v1 = InstBuilder::genLoadStackVar(v_then);
1960             }
1961             if (!v2->isSimpleValue()) {
1962                 pushComputeDSPMethod(InstBuilder::genDecStackVar(v_else, InstBuilder::genBasicTyped(t_else), v2));
1963                 v2 = InstBuilder::genLoadStackVar(v_else);
1964             }
1965             break;
1966     }
1967 
1968     return generateCacheCode(sig, InstBuilder::genSelect2Inst(cond, v2, v1));
1969 }
1970 
1971 /*****************************************************************************
1972  EXTENDED
1973  *****************************************************************************/
1974 
generateXtended(Tree sig)1975 ValueInst* InstructionsCompiler::generateXtended(Tree sig)
1976 {
1977     xtended*         p = (xtended*)getUserData(sig);
1978     list<ValueInst*> args;
1979     vector< ::Type>  arg_types;
1980 
1981     for (int i = 0; i < sig->arity(); i++) {
1982         args.push_back(CS(sig->branch(i)));
1983         arg_types.push_back(getCertifiedSigType(sig->branch(i)));
1984     }
1985 
1986     if (p->needCache()) {
1987         return generateCacheCode(sig, p->generateCode(fContainer, args, getCertifiedSigType(sig), arg_types));
1988     } else {
1989         return p->generateCode(fContainer, args, getCertifiedSigType(sig), arg_types);
1990     }
1991 }
1992 
1993 /*****************************************************************************
1994  N-SAMPLE FIXED DELAY : sig = exp@delay
1995 
1996  case 1-sample max delay :
1997  Y(t-0)	Y(t-1)
1998 
1999  V[0]	V[1]
2000 
2001  case max delay < gMaxCopyDelay :
2002  Y(t-0)	Y(t-1)	Y(t-2)  ...
2003  V[0]	V[1]	V[2]	...
2004 
2005  case max delay >= gMaxCopyDelay :
2006  Y(t-0)	Y(t-1)	Y(t-2)  ...
2007  V[0]	V[1]	V[2]	...
2008 
2009  *****************************************************************************/
2010 
2011 /**
2012  * Generate code for accessing a delayed signal. The generated code depend of
2013  * the maximum delay attached to exp.
2014  */
generateDelay(Tree sig,Tree exp,Tree delay)2015 ValueInst* InstructionsCompiler::generateDelay(Tree sig, Tree exp, Tree delay)
2016 {
2017     ValueInst* code = CS(exp);  // Ensure exp is compiled to have a vector name
2018     int        mxd  = fOccMarkup->retrieve(exp)->getMaxDelay();
2019     string     vname;
2020 
2021     if (!getVectorNameProperty(exp, vname)) {
2022         if (mxd == 0) {
2023             // cerr << "it is a pure zero delay : " << code << endl;
2024             return code;
2025         } else {
2026             stringstream error;
2027             error << "ERROR : no vector name for : " << ppsig(exp) << endl;
2028             throw faustexception(error.str());
2029         }
2030     }
2031 
2032     if (mxd == 0) {
2033 
2034         // not a real vector name but a scalar name
2035         return InstBuilder::genLoadStackVar(vname);
2036 
2037     } else if (mxd < gGlobal->gMaxCopyDelay) {
2038         int d;
2039         if (isSigInt(delay, &d)) {
2040             return InstBuilder::genLoadArrayStructVar(vname, CS(delay));
2041         } else {
2042             return generateCacheCode(sig, InstBuilder::genLoadArrayStructVar(vname, CS(delay)));
2043         }
2044     } else {
2045 
2046         int N = pow2limit(mxd + 1);
2047         if (N <= gGlobal->gMaskDelayLineThreshold) {
2048             FIRIndex value2 = (FIRIndex(InstBuilder::genLoadStructVar("IOTA")) - CS(delay)) & FIRIndex(N - 1);
2049             return generateCacheCode(sig, InstBuilder::genLoadArrayStructVar(vname, value2));
2050         } else {
2051             string ridx_name = gGlobal->getFreshID(vname + "_ridx_tmp");
2052 
2053             // int ridx = widx - delay;
2054             FIRIndex widx1 = FIRIndex(InstBuilder::genLoadStructVar(vname + "_widx"));
2055             pushComputeDSPMethod(InstBuilder::genDecStackVar(ridx_name, InstBuilder::genBasicTyped(Typed::kInt32), widx1 - CS(delay)));
2056 
2057             // dline[((ridx < 0) ? ridx + delay : ridx)];
2058             FIRIndex ridx1 = FIRIndex(InstBuilder::genLoadStackVar(ridx_name));
2059             FIRIndex ridx2 = FIRIndex(InstBuilder::genSelect2Inst(ridx1 < 0, ridx1 + FIRIndex(mxd + 1), ridx1));
2060             return generateCacheCode(sig, InstBuilder::genLoadArrayStructVar(vname, ridx2));
2061         }
2062     }
2063 }
2064 
2065 /**
2066  * Generate code for the delay mechanism. The generated code depends of the
2067  * maximum delay attached to exp and the "less temporaries" switch.
2068  */
generateDelayVec(Tree sig,ValueInst * exp,Typed::VarType ctype,const string & vname,int mxd)2069 ValueInst* InstructionsCompiler::generateDelayVec(Tree sig, ValueInst* exp, Typed::VarType ctype, const string& vname,
2070                                                   int mxd)
2071 {
2072     setVectorNameProperty(sig, vname);
2073     Address::AccessType var_access;
2074     return generateDelayLine(exp, ctype, vname, mxd, var_access, getConditionCode(sig));
2075 }
2076 
generateInitArray(const string & vname,Typed::VarType ctype,int delay)2077 StatementInst* InstructionsCompiler::generateInitArray(const string& vname, Typed::VarType ctype, int delay)
2078 {
2079     ValueInst*  init  = InstBuilder::genTypedZero(ctype);
2080     BasicTyped* typed = InstBuilder::genBasicTyped(ctype);
2081     string      index = gGlobal->getFreshID("l");
2082 
2083     // Generates table declaration
2084     pushDeclare(InstBuilder::genDecStructVar(vname, InstBuilder::genArrayTyped(typed, delay)));
2085 
2086     // Generates init table loop
2087     DeclareVarInst* loop_decl =
2088         InstBuilder::genDecLoopVar(index, InstBuilder::genInt32Typed(), InstBuilder::genInt32NumInst(0));
2089     ValueInst*    loop_end = InstBuilder::genLessThan(loop_decl->load(), InstBuilder::genInt32NumInst(delay));
2090     StoreVarInst* loop_inc = loop_decl->store(InstBuilder::genAdd(loop_decl->load(), 1));
2091 
2092     ForLoopInst* loop = InstBuilder::genForLoopInst(loop_decl, loop_end, loop_inc);
2093 
2094     loop->pushFrontInst(InstBuilder::genStoreArrayStructVar(vname, loop_decl->load(), init));
2095     return loop;
2096 }
2097 
generateShiftArray(const string & vname,int delay)2098 StatementInst* InstructionsCompiler::generateShiftArray(const string& vname, int delay)
2099 {
2100     string index = gGlobal->getFreshID("j");
2101 
2102     // Generates init table loop
2103     DeclareVarInst* loop_decl =
2104         InstBuilder::genDecLoopVar(index, InstBuilder::genInt32Typed(), InstBuilder::genInt32NumInst(delay));
2105     ValueInst*    loop_end = InstBuilder::genGreaterThan(loop_decl->load(), InstBuilder::genInt32NumInst(0));
2106     StoreVarInst* loop_inc = loop_decl->store(InstBuilder::genSub(loop_decl->load(), InstBuilder::genInt32NumInst(1)));
2107 
2108     ForLoopInst* loop        = InstBuilder::genForLoopInst(loop_decl, loop_end, loop_inc);
2109     ValueInst*   load_value2 = InstBuilder::genSub(loop_decl->load(), InstBuilder::genInt32NumInst(1));
2110     ValueInst*   load_value3 = InstBuilder::genLoadArrayStructVar(vname, load_value2);
2111 
2112     loop->pushFrontInst(InstBuilder::genStoreArrayStructVar(vname, loop_decl->load(), load_value3));
2113     return loop;
2114 }
2115 
generateCopyArray(const string & vname,int index_from,int index_to)2116 StatementInst* InstructionsCompiler::generateCopyArray(const string& vname, int index_from, int index_to)
2117 {
2118     ValueInst* inst1 = InstBuilder::genLoadArrayStructVar(vname, InstBuilder::genInt32NumInst(index_from));
2119     return InstBuilder::genStoreArrayStructVar(vname, InstBuilder::genInt32NumInst(index_to), inst1);
2120 }
2121 
generateCopyArray(const string & vname_to,const string & vname_from,int size)2122 StatementInst* InstructionsCompiler::generateCopyArray(const string& vname_to, const string& vname_from, int size)
2123 {
2124     string index = gGlobal->getFreshID("j");
2125 
2126     // Generates init table loop
2127     DeclareVarInst* loop_decl =
2128         InstBuilder::genDecLoopVar(index, InstBuilder::genInt32Typed(), InstBuilder::genInt32NumInst(0));
2129     ValueInst*    loop_end = InstBuilder::genLessThan(loop_decl->load(), InstBuilder::genInt32NumInst(size));
2130     StoreVarInst* loop_inc = loop_decl->store(InstBuilder::genAdd(loop_decl->load(), 1));
2131 
2132     ForLoopInst* loop       = InstBuilder::genForLoopInst(loop_decl, loop_end, loop_inc);
2133     ValueInst*   load_value = InstBuilder::genLoadArrayStructVar(vname_from, loop_decl->load());
2134 
2135     loop->pushFrontInst(InstBuilder::genStoreArrayStackVar(vname_to, loop_decl->load(), load_value));
2136     return loop;
2137 }
2138 
generateDelayLine(ValueInst * exp,Typed::VarType ctype,const string & vname,int mxd,Address::AccessType & var_access,ValueInst * ccs)2139 ValueInst* InstructionsCompiler::generateDelayLine(ValueInst* exp, Typed::VarType ctype, const string& vname, int mxd,
2140                                                    Address::AccessType& var_access, ValueInst* ccs)
2141 {
2142     if (mxd == 0) {
2143 
2144         // Generate scalar use
2145         if (dynamic_cast<NullValueInst*>(ccs)) {
2146             pushComputeDSPMethod(InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(ctype), exp));
2147         } else {
2148             pushPreComputeDSPMethod(InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(ctype), InstBuilder::genTypedZero(ctype)));
2149             pushComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genStoreStackVar(vname, exp)));
2150         }
2151 
2152     } else if (mxd < gGlobal->gMaxCopyDelay) {
2153 
2154         // Generates table init
2155         pushClearMethod(generateInitArray(vname, ctype, mxd + 1));
2156 
2157         // Generate table use
2158         pushComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genStoreArrayStructVar(vname, InstBuilder::genInt32NumInst(0), exp)));
2159 
2160         // Generates post processing copy code to update delay values
2161         if (mxd == 1) {
2162             pushPostComputeDSPMethod(InstBuilder::genControlInst(ccs, generateCopyArray(vname, 0, 1)));
2163         } else if (mxd == 2) {
2164             pushPostComputeDSPMethod(InstBuilder::genControlInst(ccs, generateCopyArray(vname, 1, 2)));
2165             pushPostComputeDSPMethod(InstBuilder::genControlInst(ccs, generateCopyArray(vname, 0, 1)));
2166         } else {
2167             pushPostComputeDSPMethod(InstBuilder::genControlInst(ccs, generateShiftArray(vname, mxd)));
2168         }
2169 
2170     } else {
2171 
2172         int N = pow2limit(mxd + 1);
2173         if (N <= gGlobal->gMaskDelayLineThreshold) {
2174 
2175             ensureIotaCode();
2176 
2177             // Generates table init
2178             pushClearMethod(generateInitArray(vname, ctype, N));
2179 
2180             // Generate table use
2181             if (gGlobal->gComputeIOTA) {  // Ensure IOTA base fixed delays are computed once
2182                 if (fIOTATable.find(N) == fIOTATable.end()) {
2183                     string   iota_name = subst("i$0", gGlobal->getFreshID("IOTA_temp"));
2184                     FIRIndex value2 = FIRIndex(InstBuilder::genLoadStructVar("IOTA")) & FIRIndex(N - 1);
2185 
2186                     pushPreComputeDSPMethod(InstBuilder::genDecStackVar(iota_name, InstBuilder::genInt32Typed(), InstBuilder::genInt32NumInst(0)));
2187                     pushComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genStoreStackVar(iota_name, value2)));
2188 
2189                     fIOTATable[N] = iota_name;
2190                 }
2191 
2192                 pushComputeDSPMethod(InstBuilder::genControlInst(ccs,
2193                     InstBuilder::genStoreArrayStructVar(vname, InstBuilder::genLoadStackVar(fIOTATable[N]), exp)));
2194 
2195             } else {
2196                 FIRIndex value2 = FIRIndex(InstBuilder::genLoadStructVar("IOTA")) & FIRIndex(N - 1);
2197                 pushComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genStoreArrayStructVar(vname, value2, exp)));
2198             }
2199         } else {
2200 
2201             // 'select' based delay
2202             string widx_tmp_name = vname + "_widx_tmp";
2203             string widx_name = vname + "_widx";
2204 
2205             // Generates table write index
2206             pushDeclare(InstBuilder::genDecStructVar(widx_name, InstBuilder::genInt32Typed()));
2207             pushInitMethod(InstBuilder::genStoreStructVar(widx_name, InstBuilder::genInt32NumInst(0)));
2208 
2209             // Generates table init
2210             pushClearMethod(generateInitArray(vname, ctype, mxd + 1));
2211 
2212             // int w = widx;
2213             pushComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genDecStackVar(widx_tmp_name, InstBuilder::genBasicTyped(Typed::kInt32), InstBuilder::genLoadStructVar(widx_name))));
2214 
2215             // dline[w] = v;
2216             pushComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genStoreArrayStructVar(vname, InstBuilder::genLoadStackVar(widx_tmp_name), exp)));
2217 
2218             // w = w + 1;
2219             FIRIndex widx_tmp1 = FIRIndex(InstBuilder::genLoadStackVar(widx_tmp_name));
2220             pushPostComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genStoreStackVar(widx_tmp_name, widx_tmp1 + 1)));
2221 
2222             // w = ((w == delay) ? 0 : w);
2223             FIRIndex widx_tmp2 = FIRIndex(InstBuilder::genLoadStackVar(widx_tmp_name));
2224             pushPostComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genStoreStackVar(widx_tmp_name,
2225                                                                    InstBuilder::genSelect2Inst(widx_tmp2 == FIRIndex(mxd + 1),
2226                                                                                                FIRIndex(0),
2227                                                                                                widx_tmp2))));
2228             // *widx = w
2229             pushPostComputeDSPMethod(InstBuilder::genControlInst(ccs, InstBuilder::genStoreStructVar(widx_name, InstBuilder::genLoadStackVar(widx_tmp_name))));
2230         }
2231     }
2232 
2233     return exp;
2234 }
2235 
2236 /*****************************************************************************
2237  WAVEFORM
2238  *****************************************************************************/
2239 
2240 /**
2241  * Generate code for a waveform. The waveform will be declared as a static field.
2242  * The name of the waveform is returned in vname and its size in size.
2243  */
declareWaveform(Tree sig,string & vname,int & size)2244 void InstructionsCompiler::declareWaveform(Tree sig, string& vname, int& size)
2245 {
2246     // computes C type and unique name for the waveform
2247 
2248     Typed::VarType ctype;
2249     getTypedNames(getCertifiedSigType(sig), fContainer->getClassName() + "Wave", ctype, vname);
2250     size = sig->arity();
2251 
2252     // Declares the Waveform
2253     Typed*     type      = InstBuilder::genArrayTyped(InstBuilder::genBasicTyped(ctype), size);
2254     ValueInst* num_array = InstBuilder::genArrayNumInst(ctype, size);
2255 
2256     double r;
2257     int    i;
2258 
2259     if (ctype == Typed::kInt32) {
2260         Int32ArrayNumInst* int_array = dynamic_cast<Int32ArrayNumInst*>(num_array);
2261         faustassert(int_array);
2262         for (int k = 0; k < size; k++) {
2263             if (isSigInt(sig->branch(k), &i)) {
2264                 int_array->setValue(k, i);
2265             } else if (isSigReal(sig->branch(k), &r)) {
2266                 int_array->setValue(k, int(r));
2267             }
2268         }
2269     } else if (ctype == Typed::kFloat) {
2270         FloatArrayNumInst* float_array = dynamic_cast<FloatArrayNumInst*>(num_array);
2271         faustassert(float_array);
2272         for (int k = 0; k < size; k++) {
2273             if (isSigInt(sig->branch(k), &i)) {
2274                 float_array->setValue(k, float(i));
2275             } else if (isSigReal(sig->branch(k), &r)) {
2276                 float_array->setValue(k, float(r));
2277             }
2278         }
2279     } else if (ctype == Typed::kDouble) {
2280         DoubleArrayNumInst* double_array = dynamic_cast<DoubleArrayNumInst*>(num_array);
2281         faustassert(double_array);
2282         for (int k = 0; k < size; k++) {
2283             if (isSigInt(sig->branch(k), &i)) {
2284                 double_array->setValue(k, double(i));
2285             } else if (isSigReal(sig->branch(k), &r)) {
2286                 double_array->setValue(k, r);
2287             }
2288         }
2289     } else {
2290         faustassert(false);
2291     }
2292 
2293     if (gGlobal->gWaveformInDSP) {
2294         // waveform are allocated in the DSP and not as global data
2295         pushStaticInitMethod(InstBuilder::genDecStaticStructVar(vname, type, num_array));
2296     } else {
2297         pushGlobalDeclare(InstBuilder::genDecConstStaticStructVar(vname, type, num_array));
2298     }
2299 
2300     string idx = subst("$0_idx", vname);
2301     pushDeclare(InstBuilder::genDecStructVar(idx, InstBuilder::genInt32Typed()));
2302     pushInitMethod(InstBuilder::genStoreStructVar(idx, InstBuilder::genInt32NumInst(0)));
2303 }
2304 
generateWaveform(Tree sig)2305 ValueInst* InstructionsCompiler::generateWaveform(Tree sig)
2306 {
2307     string vname;
2308     int    size;
2309 
2310     declareWaveform(sig, vname, size);
2311 
2312     string   idx   = subst("$0_idx", vname);
2313     FIRIndex index = (FIRIndex(1) + InstBuilder::genLoadStructVar(idx)) % InstBuilder::genInt32NumInst(size);
2314     pushPostComputeDSPMethod(InstBuilder::genControlInst(getConditionCode(sig), InstBuilder::genStoreStructVar(idx, index)));
2315     return generateCacheCode(sig, InstBuilder::genLoadArrayStaticStructVar(vname, InstBuilder::genLoadStructVar(idx)));
2316 }
2317 
2318 /**
2319  * Add a widget with a certain path to the user interface tree
2320  */
addUIWidget(Tree path,Tree widget)2321 void InstructionsCompiler::addUIWidget(Tree path, Tree widget)
2322 {
2323     fUIRoot = putSubFolder(fUIRoot, path, widget);
2324 }
2325 
2326 /**
2327  * Remove fake root folder if not needed (that is if the UI
2328  * is completely enclosed in one folder)
2329  */
prepareUserInterfaceTree(Tree t)2330 Tree InstructionsCompiler::prepareUserInterfaceTree(Tree t)
2331 {
2332     Tree root, elems;
2333     if (isUiFolder(t, root, elems) && isList(elems) && isNil(tl(elems))) {
2334         Tree folder = right(hd(elems));
2335         return (isUiFolder(folder)) ? folder : t;
2336     }
2337     return t;
2338 }
2339 
2340 //================================= BUILD USER INTERFACE METHOD =================================
2341 
2342 /**
2343  * Generate buildUserInterface corresponding to user interface element t
2344  */
generateUserInterfaceTree(Tree t,bool root)2345 void InstructionsCompiler::generateUserInterfaceTree(Tree t, bool root)
2346 {
2347     Tree label, elements, varname, sig;
2348 
2349     if (isUiFolder(t, label, elements)) {
2350         OpenboxInst::BoxType orient = static_cast<OpenboxInst::BoxType>(tree2int(left(label)));
2351         // Empty labels will be renamed with a 0xABCD (address) that is ignored and not displayed by UI architectures
2352         string str = tree2str(right(label));
2353 
2354         // extract metadata from group label str resulting in a simplifiedLabel
2355         // and metadata declarations for fictive zone at address 0
2356         string                    simplifiedLabel;
2357         map<string, set<string> > metadata;
2358         extractMetadata(str, simplifiedLabel, metadata);
2359 
2360         // add metadata if any
2361         for (const auto& i : metadata) {
2362             const string&      key    = i.first;
2363             const set<string>& values = i.second;
2364             for (const auto& j : values) {
2365                 pushUserInterfaceMethod(InstBuilder::genAddMetaDeclareInst("0", rmWhiteSpaces(key), rmWhiteSpaces(j)));
2366             }
2367         }
2368         // At rool level and if label is empty, use the name kept in "metadata" (either the one coded in 'declare name
2369         // "XXX";' line, or the filename)
2370         string group = (root && (simplifiedLabel == ""))
2371                            ? unquote(tree2str(*(gGlobal->gMetaDataSet[tree("name")].begin())))
2372                            : checkNullLabel(t, simplifiedLabel);
2373 
2374         pushUserInterfaceMethod(InstBuilder::genOpenboxInst(group, orient));
2375         generateUserInterfaceElements(elements);
2376         pushUserInterfaceMethod(InstBuilder::genCloseboxInst());
2377     } else if (isUiWidget(t, label, varname, sig)) {
2378         generateWidgetCode(label, varname, sig);
2379     } else {
2380         throw faustexception("ERROR in user interface generation\n");
2381     }
2382 }
2383 
2384 /**
2385  * Iterate generateUserInterfaceTree on a list of user interface elements
2386  */
generateUserInterfaceElements(Tree elements)2387 void InstructionsCompiler::generateUserInterfaceElements(Tree elements)
2388 {
2389     while (!isNil(elements)) {
2390         generateUserInterfaceTree(right(hd(elements)));
2391         elements = tl(elements);
2392     }
2393 }
2394 
2395 /**
2396  * Generate buildUserInterface C++ lines of code corresponding
2397  * to user interface widget t
2398  */
generateWidgetCode(Tree fulllabel,Tree varname,Tree sig)2399 void InstructionsCompiler::generateWidgetCode(Tree fulllabel, Tree varname, Tree sig)
2400 {
2401     Tree                      path, c, x, y, z;
2402     map<string, set<string> > metadata;
2403     string                    label, url;
2404 
2405     extractMetadata(tree2str(fulllabel), label, metadata);
2406 
2407     // Extract "url" metadata to be given as parameter to 'addSoundfile' function
2408     if (isSigSoundfile(sig, path)) {
2409         for (const auto& i : metadata) {
2410             const string&      key    = i.first;
2411             const set<string>& values = i.second;
2412             for (const auto& j : values) {
2413                 if (key == "url") {
2414                     url = prepareURL(j);
2415                 }
2416             }
2417         }
2418     } else {
2419         // Add metadata if any
2420         for (const auto& i : metadata) {
2421             const string&      key    = i.first;
2422             const set<string>& values = i.second;
2423             for (const auto& j : values) {
2424                 pushUserInterfaceMethod(InstBuilder::genAddMetaDeclareInst(tree2str(varname), rmWhiteSpaces(key), rmWhiteSpaces(j)));
2425             }
2426         }
2427     }
2428 
2429     if (isSigButton(sig, path)) {
2430         fContainer->incUIActiveCount();
2431         pushUserInterfaceMethod(InstBuilder::genAddButtonInst(checkNullLabel(varname, label), tree2str(varname)));
2432 
2433     } else if (isSigCheckbox(sig, path)) {
2434         fContainer->incUIActiveCount();
2435         pushUserInterfaceMethod(InstBuilder::genAddCheckbuttonInst(checkNullLabel(varname, label), tree2str(varname)));
2436 
2437     } else if (isSigVSlider(sig, path, c, x, y, z)) {
2438         fContainer->incUIActiveCount();
2439         pushUserInterfaceMethod(InstBuilder::genAddVerticalSliderInst(checkNullLabel(varname, label), tree2str(varname),
2440                                                                       tree2float(c), tree2float(x), tree2float(y),
2441                                                                       tree2float(z)));
2442 
2443     } else if (isSigHSlider(sig, path, c, x, y, z)) {
2444         fContainer->incUIActiveCount();
2445         pushUserInterfaceMethod(InstBuilder::genAddHorizontalSliderInst(checkNullLabel(varname, label),
2446                                                                         tree2str(varname), tree2float(c), tree2float(x),
2447                                                                         tree2float(y), tree2float(z)));
2448 
2449     } else if (isSigNumEntry(sig, path, c, x, y, z)) {
2450         fContainer->incUIActiveCount();
2451         pushUserInterfaceMethod(InstBuilder::genAddNumEntryInst(checkNullLabel(varname, label), tree2str(varname),
2452                                                                 tree2float(c), tree2float(x), tree2float(y),
2453                                                                 tree2float(z)));
2454 
2455     } else if (isSigVBargraph(sig, path, x, y, z)) {
2456         fContainer->incUIPassiveCount();
2457         pushUserInterfaceMethod(InstBuilder::genAddVerticalBargraphInst(
2458             checkNullLabel(varname, label, true), tree2str(varname), tree2float(x), tree2float(y)));
2459 
2460     } else if (isSigHBargraph(sig, path, x, y, z)) {
2461         fContainer->incUIPassiveCount();
2462         pushUserInterfaceMethod(InstBuilder::genAddHorizontalBargraphInst(
2463             checkNullLabel(varname, label, true), tree2str(varname), tree2float(x), tree2float(y)));
2464 
2465     } else if (isSigSoundfile(sig, path)) {
2466         fContainer->incUIActiveCount();
2467         pushUserInterfaceMethod(InstBuilder::genAddSoundfileInst(
2468             checkNullLabel(varname, label, true), ((url == "") ? prepareURL(label) : url), tree2str(varname)));
2469 
2470     } else {
2471         throw faustexception("ERROR in generating widget code\n");
2472     }
2473 }
2474 
2475 //==================================== USER INTERFACE MACROS ==================================
2476 
2477 /**
2478  * Generate user interface macros corresponding
2479  * to user interface element t
2480  */
generateMacroInterfaceTree(const string & pathname,Tree t)2481 void InstructionsCompiler::generateMacroInterfaceTree(const string& pathname, Tree t)
2482 {
2483     Tree label, elements, varname, sig;
2484 
2485     if (isUiFolder(t, label, elements)) {
2486         string pathname2 = pathname;
2487         string str       = tree2str(right(label));
2488         if (str.length() > 0) pathname2 += str + "/";
2489         generateMacroInterfaceElements(pathname2, elements);
2490     } else if (isUiWidget(t, label, varname, sig)) {
2491         generateWidgetMacro(pathname, label, varname, sig);
2492     } else {
2493         throw faustexception("ERROR in user interface macro generation\n");
2494     }
2495 }
2496 
2497 /**
2498  * Iterate generateMacroInterfaceTree on a list of user interface elements
2499  */
generateMacroInterfaceElements(const string & pathname,Tree elements)2500 void InstructionsCompiler::generateMacroInterfaceElements(const string& pathname, Tree elements)
2501 {
2502     while (!isNil(elements)) {
2503         generateMacroInterfaceTree(pathname, right(hd(elements)));
2504         elements = tl(elements);
2505     }
2506 }
2507 
2508 /**
2509  * Generate user interface macros corresponding
2510  * to a user interface widget
2511  */
generateWidgetMacro(const string & pathname,Tree fulllabel,Tree varname,Tree sig)2512 void InstructionsCompiler::generateWidgetMacro(const string& pathname, Tree fulllabel, Tree varname, Tree sig)
2513 {
2514     Tree                      path, c, x, y, z;
2515     string                    label;
2516     map<string, set<string>>  metadata;
2517 
2518     extractMetadata(tree2str(fulllabel), label, metadata);
2519     string pathlabel = pathname + label;
2520     string rawlabel = label;
2521     std::replace(rawlabel.begin(), rawlabel.end(), ' ', '_');
2522 
2523     if (isSigButton(sig, path)) {
2524         fContainer->addUIMacro(subst("FAUST_ADDBUTTON(\"$0\", $1);", pathlabel, tree2str(varname)));
2525         fContainer->addUIMacroActives(subst("p(BUTTON, $0, \"$1\", $2, $3, $4, $5, $6) \\", rawlabel, pathlabel, tree2str(varname), T(0.), T(0.), T(1.0), T(1.0)));
2526 
2527     } else if (isSigCheckbox(sig, path)) {
2528         fContainer->addUIMacro(subst("FAUST_ADDCHECKBOX(\"$0\", $1);", pathlabel, tree2str(varname)));
2529         fContainer->addUIMacroActives(subst("p(CHECKBOX, $0, \"$1\", $2, $3, $4, $5, $6) \\", rawlabel, pathlabel, tree2str(varname), T(0.), T(0.), T(1.0), T(1.0)));
2530 
2531     } else if (isSigVSlider(sig, path, c, x, y, z)) {
2532         fContainer->addUIMacro(subst("FAUST_ADDVERTICALSLIDER(\"$0\", $1, $2, $3, $4, $5);", pathlabel,
2533                                      tree2str(varname), T(tree2float(c)), T(tree2float(x)), T(tree2float(y)),
2534                                      T(tree2float(z))));
2535         fContainer->addUIMacroActives(subst("p(VERTICALSLIDER, $0, \"$1\", $2, $3, $4, $5, $6) \\", rawlabel, pathlabel,
2536                                             tree2str(varname), T(tree2float(c)), T(tree2float(x)), T(tree2float(y)),
2537                                             T(tree2float(z))));
2538 
2539     } else if (isSigHSlider(sig, path, c, x, y, z)) {
2540         fContainer->addUIMacro(subst("FAUST_ADDHORIZONTALSLIDER(\"$0\", $1, $2, $3, $4, $5);", pathlabel,
2541                                      tree2str(varname), T(tree2float(c)), T(tree2float(x)), T(tree2float(y)),
2542                                      T(tree2float(z))));
2543         fContainer->addUIMacroActives(subst("p(HORIZONTALSLIDER, $0, \"$1\", $2, $3, $4, $5, $6) \\", rawlabel, pathlabel,
2544                                             tree2str(varname), T(tree2float(c)), T(tree2float(x)), T(tree2float(y)),
2545                                             T(tree2float(z))));
2546 
2547     } else if (isSigNumEntry(sig, path, c, x, y, z)) {
2548         fContainer->addUIMacro(subst("FAUST_ADDNUMENTRY(\"$0\", $1, $2, $3, $4, $5);", pathlabel, tree2str(varname),
2549                                      T(tree2float(c)), T(tree2float(x)), T(tree2float(y)), T(tree2float(z))));
2550         fContainer->addUIMacroActives(subst("p(NUMENTRY, $0, \"$1\", $2, $3, $4, $5, $6) \\", rawlabel, pathlabel,
2551                                             tree2str(varname), T(tree2float(c)), T(tree2float(x)), T(tree2float(y)),
2552                                             T(tree2float(z))));
2553 
2554     } else if (isSigVBargraph(sig, path, x, y, z)) {
2555         fContainer->addUIMacro(subst("FAUST_ADDVERTICALBARGRAPH(\"$0\", $1, $2, $3);", pathlabel, tree2str(varname),
2556                                      T(tree2float(x)), T(tree2float(y))));
2557         fContainer->addUIMacroPassives(subst("p(VERTICALBARGRAPH, $0, \"$1\", $2, 0.0, $3, $4, 0.0) \\", rawlabel, pathlabel,
2558                                             tree2str(varname), T(tree2float(x)), T(tree2float(y))));
2559 
2560     } else if (isSigHBargraph(sig, path, x, y, z)) {
2561         fContainer->addUIMacro(subst("FAUST_ADDHORIZONTALBARGRAPH(\"$0\", $1, $2, $3);", pathlabel, tree2str(varname),
2562                                      T(tree2float(x)), T(tree2float(y))));
2563         fContainer->addUIMacroPassives(subst("p(HORIZONTALBARGRAPH, $0, \"$1\", $2, 0.0, $3, $4, 0.0) \\", rawlabel, pathlabel,
2564                                             tree2str(varname), T(tree2float(x)), T(tree2float(y))));
2565 
2566     } else if (isSigSoundfile(sig, path)) {
2567         fContainer->addUIMacro(subst("FAUST_ADDSOUNDFILE(\"$0\", $1);", pathlabel, tree2str(varname)));
2568 
2569     } else {
2570         throw faustexception("ERROR in generating widget code\n");
2571     }
2572 }
2573