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 <fstream>
23 
24 #include "Text.hh"
25 #include "dag_instructions_compiler.hh"
26 #include "exception.hh"
27 #include "ppsig.hh"
28 #include "prim2.hh"
29 #include "privatise.hh"
30 #include "recursivness.hh"
31 #include "sigtyperules.hh"
32 #include "simplify.hh"
33 #include "timing.hh"
34 #include "xtended.hh"
35 
DAGInstructionsCompiler(CodeContainer * container)36 DAGInstructionsCompiler::DAGInstructionsCompiler(CodeContainer* container) : InstructionsCompiler(container)
37 {
38 }
39 
compileMultiSignal(Tree L)40 void DAGInstructionsCompiler::compileMultiSignal(Tree L)
41 {
42     // Has to be done *after* gMachinePtrSize is set by the actual backend
43     gGlobal->initTypeSizeMap();
44 
45     L = prepare(L);  // Optimize, share and annotate expression
46 
47     // "input" and "inputs" used as a name convention
48     if (!gGlobal->gOpenCLSwitch && !gGlobal->gCUDASwitch) {  // HACK
49 
50         Typed* type = InstBuilder::genArrayTyped(InstBuilder::genFloatMacroTyped(), 0);
51 
52         for (int index = 0; index < fContainer->inputs(); index++) {
53             // 'name1' variable must be shared between 'compute' and computeThread' methods,
54             // so it is moved in the DSP struct
55             if (gGlobal->gSchedulerSwitch) {
56                 string name1 = subst("fInput$0_ptr", T(index));
57                 pushDeclare(InstBuilder::genDecStructVar(name1, type));
58                 pushComputeBlockMethod(InstBuilder::genStoreStructVar(
59                     name1, InstBuilder::genLoadArrayFunArgsVar("inputs", InstBuilder::genInt32NumInst(index))));
60             } else {
61                 string name1 = subst("input$0_ptr", T(index));
62                 pushComputeBlockMethod(InstBuilder::genDecStackVar(
63                     name1, type, InstBuilder::genLoadArrayFunArgsVar("inputs", InstBuilder::genInt32NumInst(index))));
64             }
65         }
66 
67         // "output" and "outputs" used as a name convention
68         for (int index = 0; index < fContainer->outputs(); index++) {
69             // 'name1' variable must be shared between 'compute' and computeThread' methods,
70             // so it is moved in the DSP struct
71             if (gGlobal->gSchedulerSwitch) {
72                 string name1 = subst("fOutput$0_ptr", T(index));
73                 pushDeclare(InstBuilder::genDecStructVar(name1, type));
74                 pushComputeBlockMethod(InstBuilder::genStoreStructVar(
75                     name1, InstBuilder::genLoadArrayFunArgsVar("outputs", InstBuilder::genInt32NumInst(index))));
76             } else {
77                 string name1 = subst("output$0_ptr", T(index));
78                 pushComputeBlockMethod(InstBuilder::genDecStackVar(
79                     name1, type, InstBuilder::genLoadArrayFunArgsVar("outputs", InstBuilder::genInt32NumInst(index))));
80             }
81         }
82     }
83 
84     if (gGlobal->gOpenCLSwitch || gGlobal->gCUDASwitch) {  // HACK
85 
86         for (int index = 0; isList(L); L = tl(L), index++) {
87             Tree   sig  = hd(L);
88             string name = subst("output$0", T(index));
89 
90             fContainer->openLoop("i");
91 
92             // Cast to external float
93             ValueInst* res = InstBuilder::genCastFloatMacroInst(CS(sig));
94             pushComputeDSPMethod(InstBuilder::genStoreArrayFunArgsVar(
95                 name, getCurrentLoopIndex() + InstBuilder::genLoadLoopVar("vindex"), res));
96 
97             fContainer->closeLoop(sig);
98         }
99 
100     } else {
101         for (int index = 0; isList(L); L = tl(L), index++) {
102             Tree   sig  = hd(L);
103             string name = subst("output$0", T(index));
104 
105             fContainer->openLoop("i");
106 
107             // Cast to external float
108             ValueInst* res = InstBuilder::genCastFloatMacroInst(CS(sig));
109 
110             if (gGlobal->gComputeMix) {
111                 ValueInst* res1 = InstBuilder::genAdd(res, InstBuilder::genLoadArrayStackVar(name, getCurrentLoopIndex()));
112                 pushComputeDSPMethod(InstBuilder::genStoreArrayStackVar(name, getCurrentLoopIndex(), res1));
113             } else {
114                 pushComputeDSPMethod(InstBuilder::genStoreArrayStackVar(name, getCurrentLoopIndex(), res));
115             }
116 
117             fContainer->closeLoop(sig);
118         }
119     }
120 
121     generateUserInterfaceTree(prepareUserInterfaceTree(fUIRoot), true);
122     generateMacroInterfaceTree("", prepareUserInterfaceTree(fUIRoot));
123     if (fDescription) {
124         fDescription->ui(prepareUserInterfaceTree(fUIRoot));
125     }
126 
127     // Apply FIR to FIR transformations
128     fContainer->processFIR();
129 
130     // Generate JSON
131     if (gGlobal->gPrintJSONSwitch) {
132         if (gGlobal->gFloatSize == 1) {
133             fContainer->generateJSONFile<float>();
134         } else {
135             fContainer->generateJSONFile<double>();
136         }
137     }
138 }
139 
140 /**
141  * Compile a signal
142  * @param sig the signal expression to compile.
143  * @return the code translation of sig as ValueInst*
144  */
CS(Tree sig)145 ValueInst* DAGInstructionsCompiler::CS(Tree sig)
146 {
147     ValueInst* code;
148 
149     // cerr << "ENTER VectorCompiler::CS : "<< ppsig(sig) << endl;
150     if (!getCompiledExpression(sig, code)) {
151         code = generateCode(sig);
152         // cerr << "CS : " << code << " for " << ppsig(sig) << endl;
153         setCompiledExpression(sig, code);
154     } else {
155         // we require an already compiled expression
156         // therefore we must update the dependencies of
157         // the current loop
158         int       i;
159         Tree      x, d, r;
160         CodeLoop* ls;
161         CodeLoop* tl = fContainer->getCurLoop();
162 
163         if (fContainer->getLoopProperty(sig, ls)) {
164             // sig has a loop property
165             // cerr << "CASE SH : fBackwardLoopDependencies.insert : " << tl << " --depend(A)son--> " << ls << endl;
166             tl->addBackwardDependency(ls);
167 
168         } else if (isSigDelay(sig, x, d) && fContainer->getLoopProperty(x, ls)) {
169             // cerr << "CASE DL : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
170             tl->addBackwardDependency(ls);
171 
172         } else if (isSigDelay(sig, x, d) && isProj(x, &i, r) && fContainer->getLoopProperty(r, ls)) {
173             // cerr << "CASE DR : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
174             tl->addBackwardDependency(ls);
175         }
176         if (isProj(sig, &i, r) && fContainer->getLoopProperty(r, ls)) {
177             // cerr << "CASE R* : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
178             tl->addBackwardDependency(ls);
179 
180         } else {
181             if (isProj(sig, &i, r)) {
182                 // cerr << "SYMBOL RECURSIF EN COURS ??? " << *r << endl;
183             } else if (getCertifiedSigType(sig)->variability() < kSamp) {
184                 // cerr << "SLOW EXPRESSION " << endl;
185             } else {
186                 // cerr << "Expression absorbée" << *sig << endl;
187             }
188         }
189     }
190     return code;
191 }
192 
generateCode(Tree sig)193 ValueInst* DAGInstructionsCompiler::generateCode(Tree sig)
194 {
195     generateCodeRecursions(sig);
196     return generateCodeNonRec(sig);
197 }
198 
generateCodeRecursions(Tree sig)199 void DAGInstructionsCompiler::generateCodeRecursions(Tree sig)
200 {
201     Tree       id, body;
202     ValueInst* code;
203     // cerr << "DAGInstructionsCompiler::generateCodeRecursions( " << ppsig(sig) << " )" << endl;
204     if (getCompiledExpression(sig, code)) {
205         // cerr << "** ALREADY VISITED : " << code << " ===> " << ppsig(sig) << endl;
206         return;
207     } else if (isRec(sig, id, body)) {
208         // cerr << "we have a recursive expression non compiled yet : " << ppsig(sig) << endl;
209         setCompiledExpression(sig, InstBuilder::genNullValueInst());
210         fContainer->openLoop(sig, "i");
211         generateRec(sig, id, body);
212         fContainer->closeLoop(sig);
213     } else {
214         // we go down the expression
215         vector<Tree> subsigs;
216         int          n = getSubSignals(sig, subsigs, false);
217         for (int i = 0; i < n; i++) {
218             generateCodeRecursions(subsigs[i]);
219         }
220     }
221 }
222 
generateCodeNonRec(Tree sig)223 ValueInst* DAGInstructionsCompiler::generateCodeNonRec(Tree sig)
224 {
225     ValueInst* code;
226     if (getCompiledExpression(sig, code)) {
227         // already visited
228         return code;
229     } else {
230         // cerr << "DAGInstructionsCompiler::generateCodeNonRec( " << ppsig(sig) << " )" << endl;
231         code = generateLoopCode(sig);
232         setCompiledExpression(sig, code);
233         return code;
234     }
235 }
236 
237 /**
238  * Compile a signal
239  * @param sig the signal expression to compile.
240  * @return the code translation of sig as ValueInst*
241  */
generateLoopCode(Tree sig)242 ValueInst* DAGInstructionsCompiler::generateLoopCode(Tree sig)
243 {
244     int       i;
245     Tree      x;
246     CodeLoop* l;
247     CodeLoop* l2;
248 
249     l = fContainer->getCurLoop();
250     faustassert(l);
251     // cerr << "VectorCompiler::OLDgenerateCode " << ppsig(sig) << endl;
252     if (needSeparateLoop(sig)) {
253         // we need a separate loop unless it's an old recursion
254         if (isProj(sig, &i, x)) {
255             // projection of a recursive group x
256             if (l->hasRecDependencyIn(singleton(x))) {
257                 // x is already in the loop stack
258                 return InstructionsCompiler::generateCode(sig);
259             } else if (fContainer->getLoopProperty(x, l2)) {
260                 ValueInst* c = InstructionsCompiler::generateCode(sig);
261                 // cerr << "SPECIAL CASE TO PREVENT VECTOR BUG " << ppsig(sig, true) << endl;
262                 return c;
263             } else {
264                 // x must be defined
265                 fContainer->openLoop(sig, "i");
266                 ValueInst* c = InstructionsCompiler::generateCode(sig);
267                 fContainer->closeLoop(sig);
268                 return c;
269             }
270         } else {
271             fContainer->openLoop("i");
272             ValueInst* c = InstructionsCompiler::generateCode(sig);
273             fContainer->closeLoop(sig);
274             return c;
275         }
276     } else {
277         return InstructionsCompiler::generateCode(sig);
278     }
279 }
280 
generateCacheCode(Tree sig,ValueInst * exp)281 ValueInst* DAGInstructionsCompiler::generateCacheCode(Tree sig, ValueInst* exp)
282 {
283     string         vname;
284     Typed::VarType ctype;
285     int            sharing = getSharingCount(sig);
286     ::Type         t       = getCertifiedSigType(sig);
287     old_Occurences*    o   = fOccMarkup->retrieve(sig);
288     int            d       = o->getMaxDelay();
289 
290     if (t->variability() < kSamp) {
291         if (d == 0) {
292             // non-sample, not delayed : same as scalar cache
293             return InstructionsCompiler::generateCacheCode(sig, exp);
294 
295         } else {
296             // it is a non-sample expressions but used delayed
297             // we need a delay line
298             getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
299             Address::AccessType var_access;
300 
301             if ((sharing > 1) && !verySimple(sig)) {
302                 // first cache this expression because it
303                 // it is shared and complex
304                 ValueInst* cachedexp = generateVariableStore(sig, exp);
305                 generateDelayLine(cachedexp, ctype, vname, d, var_access, nullptr);
306                 setVectorNameProperty(sig, vname);
307                 return cachedexp;
308             } else {
309                 // no need to cache this expression because
310                 // it is either not shared or very simple
311                 generateDelayLine(exp, ctype, vname, d, var_access, nullptr);
312                 setVectorNameProperty(sig, vname);
313                 return exp;
314             }
315         }
316     } else {
317         // sample-rate signal
318         if (d > 0) {
319             // used delayed : we need a delay line
320             getTypedNames(getCertifiedSigType(sig), "Yec", ctype, vname);
321             Address::AccessType var_access;
322             generateDelayLine(exp, ctype, vname, d, var_access, nullptr);
323             setVectorNameProperty(sig, vname);
324 
325             if (verySimple(sig)) {
326                 return exp;
327             } else {
328                 if (d < gGlobal->gMaxCopyDelay) {
329                     // return subst("$0[i]", vname);
330                     return InstBuilder::genLoadArrayVar(vname, var_access, getCurrentLoopIndex());
331                 } else {
332                     // we use a ring buffer
333                     string vname_idx = vname + "_idx";
334                     int    mask      = pow2limit(d + gGlobal->gVecSize) - 1;
335                     // return subst("$0[($0_idx+i) & $1]", vname, mask);
336                     FIRIndex index1 = (getCurrentLoopIndex() + InstBuilder::genLoadStructVar(vname_idx)) & mask;
337                     return InstBuilder::genLoadArrayStructVar(vname, index1);
338                 }
339             }
340         } else {
341             // not delayed
342             Tree x, y;
343             if (sharing > 1 && isSigDelay(sig, x, y) && verySimple(y)) {
344                 // cerr << "SPECIAL CASE NO CACHE NEEDED : " << ppsig(sig) << endl;
345                 return exp;
346             } else if (sharing > 1 && !verySimple(sig)) {
347                 // shared and not simple : we need a vector
348                 // cerr << "Zec : " << ppsig(sig) << endl;
349                 getTypedNames(getCertifiedSigType(sig), "Zec", ctype, vname);
350                 Address::AccessType var_access;
351                 generateDelayLine(exp, ctype, vname, d, var_access, nullptr);
352                 setVectorNameProperty(sig, vname);
353                 // return subst("$0[i]", vname);
354                 return InstBuilder::genLoadArrayVar(vname, var_access, getCurrentLoopIndex());
355             } else {
356                 // not shared or simple : no cache needed
357                 return exp;
358             }
359         }
360     }
361 }
362 
363 // Code generation
364 
365 /**
366  * Test if a signal need to be compiled in a separate loop.
367  * @param sig the signal expression to test.
368  * @return true if a separate loop is needed
369  */
needSeparateLoop(Tree sig)370 bool DAGInstructionsCompiler::needSeparateLoop(Tree sig)
371 {
372     old_Occurences* o = fOccMarkup->retrieve(sig);
373     ::Type      t = getCertifiedSigType(sig);
374     int         c = getSharingCount(sig);
375     bool        b;
376 
377     int  i;
378     Tree x, y;
379 
380     if (o->getMaxDelay() > 0) {
381         b = true;
382     } else if (verySimple(sig) || t->variability() < kSamp) {
383         b = false;  // non sample computation never require a loop
384     } else if (isSigDelay(sig, x, y)) {
385         b = false;
386     } else if (isProj(sig, &i, x)) {
387         b = true;
388     } else if (c > 1) {
389         b = true;
390     } else {
391         // sample expressions that are not recursive, not delayed
392         // and not shared, doesn't require a separate loop.
393         b = false;
394     }
395     return b;
396 }
397 
generateVariableStore(Tree sig,ValueInst * exp)398 ValueInst* DAGInstructionsCompiler::generateVariableStore(Tree sig, ValueInst* exp)
399 {
400     ::Type t = getCertifiedSigType(sig);
401 
402     if (t->variability() == kSamp) {
403         string         vname;
404         Typed::VarType ctype;
405         getTypedNames(t, "Vector", ctype, vname);
406         Address::AccessType var_access;
407         generateVectorLoop(ctype, vname, exp, var_access);
408         return InstBuilder::genLoadArrayVar(vname, var_access, getCurrentLoopIndex());
409     } else {
410         return InstructionsCompiler::generateVariableStore(sig, exp);
411     }
412 }
413 
generateInput(Tree sig,int idx)414 ValueInst* DAGInstructionsCompiler::generateInput(Tree sig, int idx)
415 {
416     if (gGlobal->gOpenCLSwitch || gGlobal->gCUDASwitch) {  // HACK
417         // "input" use as a name convention
418         string     name = subst("input$0", T(idx));
419         ValueInst* res =
420             InstBuilder::genLoadArrayFunArgsVar(name, getCurrentLoopIndex() + InstBuilder::genLoadLoopVar("vindex"));
421         // Cast to internal float
422         res = InstBuilder::genCastFloatInst(res);
423         return generateCacheCode(sig, res);
424 
425     } else {
426         // "fInput" use as a name convention
427         string     name = subst("input$0", T(idx));
428         ValueInst* res  = InstBuilder::genLoadArrayStackVar(name, getCurrentLoopIndex());
429         // Cast to internal float
430         res = InstBuilder::genCastFloatInst(res);
431         return generateCacheCode(sig, res);
432     }
433 }
434 
generateDelay(Tree sig,Tree exp,Tree delay)435 ValueInst* DAGInstructionsCompiler::generateDelay(Tree sig, Tree exp, Tree delay)
436 {
437     string     vname;
438     ValueInst* code = CS(exp);  // ensure exp is compiled to have a vector name
439     int        d, mxd = fOccMarkup->retrieve(exp)->getMaxDelay();
440 
441     if (!getVectorNameProperty(exp, vname)) {
442         if (mxd == 0) {
443             // cerr << "it is a pure zero delay : " << code << endl;
444             return code;
445         } else {
446             stringstream error;
447             error << "ERROR : no vector name for : " << ppsig(exp) << endl;
448             throw faustexception(error.str());
449         }
450     }
451 
452     if (mxd == 0) {
453         // not a real vector name but a scalar name
454         // return subst("$0[i]", vname);
455         return InstBuilder::genLoadArrayStackVar(vname, getCurrentLoopIndex());
456 
457     } else if (mxd < gGlobal->gMaxCopyDelay) {
458         if (isSigInt(delay, &d)) {
459             if (d == 0) {
460                 // return subst("$0[i]", vname);
461                 return InstBuilder::genLoadArrayStackVar(vname, getCurrentLoopIndex());
462             } else {
463                 // return subst("$0[i-$1]", vname, T(d));
464                 FIRIndex index = getCurrentLoopIndex() - d;
465                 return generateCacheCode(sig, InstBuilder::genLoadArrayStackVar(vname, index));
466             }
467         } else {
468             // return subst("$0[i-$1]", vname, CS(delay));
469             FIRIndex index = getCurrentLoopIndex() - CS(delay);
470             return generateCacheCode(sig, InstBuilder::genLoadArrayStackVar(vname, index));
471         }
472     } else {
473         // long delay : we use a ring buffer of size 2^x
474         int    N         = pow2limit(mxd + gGlobal->gVecSize);
475         string vname_idx = vname + "_idx";
476 
477         if (isSigInt(delay, &d)) {
478             if (d == 0) {
479                 // return subst("$0[($0_idx+i)&$1]", vname, T(N-1));
480                 FIRIndex index1 = (getCurrentLoopIndex() + InstBuilder::genLoadStructVar(vname_idx)) & (N - 1);
481                 return generateCacheCode(sig, InstBuilder::genLoadArrayStructVar(vname, index1));
482             } else {
483                 // return subst("$0[($0_idx+i-$2)&$1]", vname, T(N-1), T(d));
484                 FIRIndex index1 = getCurrentLoopIndex() + InstBuilder::genLoadStructVar(vname_idx);
485                 FIRIndex index2 = index1 - d;
486                 FIRIndex index3 = index2 & (N - 1);
487                 return generateCacheCode(sig, InstBuilder::genLoadArrayStructVar(vname, index3));
488             }
489         } else {
490             // return subst("$0[($0_idx+i-$2)&$1]", vname, T(N-1), CS(delay));
491             FIRIndex index1 = getCurrentLoopIndex() + InstBuilder::genLoadStructVar(vname_idx);
492             FIRIndex index2 = index1 - CS(delay);
493             FIRIndex index3 = index2 & (N - 1);
494             return generateCacheCode(sig, InstBuilder::genLoadArrayStructVar(vname, index3));
495         }
496     }
497 }
498 
generateDelayVec(Tree sig,ValueInst * exp,Typed::VarType ctype,const string & vname,int mxd)499 ValueInst* DAGInstructionsCompiler::generateDelayVec(Tree sig, ValueInst* exp, Typed::VarType ctype,
500                                                      const string& vname, int mxd)
501 {
502     // it is a non-sample but used delayed
503     // we need a delay line
504 
505     setVectorNameProperty(sig, vname);
506     Address::AccessType var_access;
507     generateDelayLine(exp, ctype, vname, mxd, var_access, nullptr);
508 
509     if (verySimple(sig)) {
510         return exp;
511     } else {
512         return InstBuilder::genLoadArrayVar(vname, var_access, getCurrentLoopIndex());
513     }
514 }
515 
generateDelayLine(ValueInst * exp,Typed::VarType ctype,const string & vname,int mxd,Address::AccessType & var_access,ValueInst * unused)516 ValueInst* DAGInstructionsCompiler::generateDelayLine(ValueInst* exp, Typed::VarType ctype, const string& vname,
517                                                       int mxd, Address::AccessType& var_access, ValueInst* unused)
518 {
519     if (mxd == 0) {
520         generateVectorLoop(ctype, vname, exp, var_access);
521     } else {
522         generateDlineLoop(ctype, vname, mxd, exp, var_access);
523     }
524 
525     return exp;
526 }
527 
generateVectorLoop(Typed::VarType ctype,const string & vname,ValueInst * exp,Address::AccessType & var_access)528 void DAGInstructionsCompiler::generateVectorLoop(Typed::VarType ctype, const string& vname, ValueInst* exp,
529                                                  Address::AccessType& var_access)
530 {
531     // "$0 $1[$2];"
532     DeclareVarInst* table_inst = InstBuilder::genDecStackVar(
533         vname, InstBuilder::genArrayTyped(InstBuilder::genBasicTyped(ctype), gGlobal->gVecSize));
534     pushComputeBlockMethod(table_inst);
535 
536     // -- compute the new samples
537     // $0[i] = $1;"
538     pushComputeDSPMethod(InstBuilder::genStoreArrayStackVar(vname, getCurrentLoopIndex(), exp));
539 
540     // Set desired variable access
541     var_access = Address::kStack;
542 }
543 
generateDlineLoop(Typed::VarType ctype,const string & vname,int delay,ValueInst * exp,Address::AccessType & var_access)544 void DAGInstructionsCompiler::generateDlineLoop(Typed::VarType ctype, const string& vname, int delay, ValueInst* exp,
545                                                 Address::AccessType& var_access)
546 {
547     BasicTyped* typed = InstBuilder::genBasicTyped(ctype);
548 
549     if (delay < gGlobal->gMaxCopyDelay) {
550         // Implementation of a copy based delayline
551         // create names for temporary and permanent storage
552         string buf  = subst("$0_tmp", vname);
553         string pmem = subst("$0_perm", vname);
554 
555         // constraints delay size to be multiple of 4
556         delay = (delay + 3) & -4;
557 
558         // allocate permanent storage for delayed samples
559         pushClearMethod(generateInitArray(pmem, ctype, delay));
560 
561         // compute method
562 
563         // -- declare a buffer and a "shifted" vector
564         DeclareVarInst* table_inst1 =
565             InstBuilder::genDecStackVar(buf, InstBuilder::genArrayTyped(typed, gGlobal->gVecSize + delay));
566         pushComputeBlockMethod(table_inst1);
567 
568         ValueInst* address_value = InstBuilder::genLoadArrayStackVarAddress(buf, InstBuilder::genInt32NumInst(delay));
569         DeclareVarInst* table_inst2 =
570             InstBuilder::genDecStackVar(vname, InstBuilder::genArrayTyped(typed, 0), address_value);
571         pushComputeBlockMethod(table_inst2);
572 
573         // -- copy the stored samples to the delay line
574         pushPreComputeDSPMethod(generateCopyArray(buf, pmem, delay));
575 
576         // -- compute the new samples
577         pushComputeDSPMethod(InstBuilder::genStoreArrayStackVar(vname, getCurrentLoopIndex(), exp));
578 
579         // -- copy back to stored samples
580         pushPostComputeDSPMethod(generateCopyBackArray(pmem, buf, delay));
581 
582         // Set desired variable access
583         var_access = Address::kStack;
584 
585     } else {
586         // Implementation of a ring-buffer delayline, the size should be large enough and aligned on a power of two
587         delay = pow2limit(delay + gGlobal->gVecSize);
588 
589         // create names for temporary and permanent storage
590         string idx      = subst("$0_idx", vname);
591         string idx_save = subst("$0_idx_save", vname);
592 
593         // allocate permanent storage for delayed samples
594         pushClearMethod(generateInitArray(vname, ctype, delay));
595         pushDeclare(InstBuilder::genDecStructVar(idx, InstBuilder::genInt32Typed()));
596         pushDeclare(InstBuilder::genDecStructVar(idx_save, InstBuilder::genInt32Typed()));
597 
598         // init permanent memory
599         pushClearMethod(InstBuilder::genStoreStructVar(idx, InstBuilder::genInt32NumInst(0)));
600         pushClearMethod(InstBuilder::genStoreStructVar(idx_save, InstBuilder::genInt32NumInst(0)));
601 
602         // -- update index
603         FIRIndex index1 = FIRIndex(InstBuilder::genLoadStructVar(idx)) + InstBuilder::genLoadStructVar(idx_save);
604         FIRIndex index2 = index1 & (delay - 1);
605 
606         pushPreComputeDSPMethod(InstBuilder::genStoreStructVar(idx, index2));
607 
608         // -- compute the new samples
609         FIRIndex index3 = getCurrentLoopIndex() + InstBuilder::genLoadStructVar(idx);
610         FIRIndex index4 = index3 & (delay - 1);
611 
612         pushComputeDSPMethod(InstBuilder::genStoreArrayStructVar(vname, index4, exp));
613 
614         // -- save index
615         pushPostComputeDSPMethod(InstBuilder::genStoreStructVar(idx_save, InstBuilder::genLoadLoopVar("vsize")));
616 
617         // Set desired variable access
618         var_access = Address::kStruct;
619     }
620 }
621 
generateCopyBackArray(const string & vname_to,const string & vname_from,int size)622 StatementInst* DAGInstructionsCompiler::generateCopyBackArray(const string& vname_to, const string& vname_from,
623                                                               int size)
624 {
625     string index = gGlobal->getFreshID("j");
626 
627     // Generates init table loop
628     DeclareVarInst* loop_decl =
629         InstBuilder::genDecLoopVar(index, InstBuilder::genInt32Typed(), InstBuilder::genInt32NumInst(0));
630     ValueInst*    loop_end       = InstBuilder::genLessThan(loop_decl->load(), InstBuilder::genInt32NumInst(size));
631     StoreVarInst* loop_increment = loop_decl->store(InstBuilder::genAdd(loop_decl->load(), 1));
632 
633     ForLoopInst* loop = InstBuilder::genForLoopInst(loop_decl, loop_end, loop_increment);
634 
635     FIRIndex   load_index = FIRIndex(InstBuilder::genLoadLoopVar("vsize")) + loop_decl->load();
636     ValueInst* load_value = InstBuilder::genLoadArrayStackVar(vname_from, load_index);
637 
638     loop->pushFrontInst(InstBuilder::genStoreArrayStructVar(vname_to, loop_decl->load(), load_value));
639     return loop;
640 }
641 
generateWaveform(Tree sig)642 ValueInst* DAGInstructionsCompiler::generateWaveform(Tree sig)
643 {
644     string vname;
645     int    size;
646 
647     declareWaveform(sig, vname, size);
648 
649     string   idx    = subst("$0_idx", vname);
650     FIRIndex index1 = (FIRIndex(InstBuilder::genLoadStructVar(idx)) + InstBuilder::genLoadLoopVar("vsize")) % size;
651     pushPostComputeDSPMethod(InstBuilder::genStoreStructVar(idx, index1));
652     FIRIndex index2 = (FIRIndex(InstBuilder::genLoadStructVar(idx)) + getCurrentLoopIndex()) % size;
653     return generateCacheCode(sig, InstBuilder::genLoadArrayStaticStructVar(vname, index2));
654 }
655