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