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