1 /************************************************************************
2 ************************************************************************
4 Copyright (C) 2003-2004 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.
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.
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 ************************************************************************/
24 #include "compile_vect.hh"
30 void VectorCompiler::compileMultiSignal (Tree L
)
32 //contextor recursivness(0);
33 L
= prepare(L
); // optimize, share and annotate expression
35 for (int i
= 0; i
< fClass
->inputs(); i
++) {
36 fClass
->addZone3(subst("$1* input$0 = &input[$0][index];", T(i
), xfloat()));
38 for (int i
= 0; i
< fClass
->outputs(); i
++) {
39 fClass
->addZone3(subst("$1* output$0 = &output[$0][index];", T(i
), xfloat()));
42 fClass
->addSharedDecl("fullcount");
43 fClass
->addSharedDecl("input");
44 fClass
->addSharedDecl("output");
46 for (int i
= 0; isList(L
); L
= tl(L
), i
++) {
48 fClass
->openLoop("count");
49 fClass
->addExecCode(subst("output$0[i] = $2$1;", T(i
), CS(sig
), xcast()));
50 fClass
->closeLoop(sig
);
53 generateUserInterfaceTree(prepareUserInterfaceTree(fUIRoot
));
54 generateMacroInterfaceTree("", prepareUserInterfaceTree(fUIRoot
));
56 fDescription
->ui(prepareUserInterfaceTree(fUIRoot
));
63 * @param sig the signal expression to compile.
64 * @return the C code translation of sig as a string
66 string
VectorCompiler::CS (Tree sig
)
69 //cerr << "ENTER VectorCompiler::CS : "<< ppsig(sig) << endl;
70 if (!getCompiledExpression(sig
, code
)) {
71 code
= generateCode(sig
);
72 //cerr << "CS : " << code << " for " << ppsig(sig) << endl;
73 setCompiledExpression(sig
, code
);
75 // we require an already compiled expression
76 // therefore we must update the dependencies of
81 Loop
* tl
= fClass
->topLoop();
83 if (fClass
->getLoopProperty(sig
,ls
)) {
84 // sig has a loop property
85 //cerr << "CASE SH : fBackwardLoopDependencies.insert : " << tl << " --depend(A)son--> " << ls << endl;
86 tl
->fBackwardLoopDependencies
.insert(ls
);
88 } else if (isSigFixDelay(sig
, x
, d
) && fClass
->getLoopProperty(x
,ls
)) {
89 //cerr << "CASE DL : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
90 tl
->fBackwardLoopDependencies
.insert(ls
);
92 } else if (isSigFixDelay(sig
, x
, d
) && isProj(x
, &i
, r
) && fClass
->getLoopProperty(r
,ls
)) {
93 //cerr << "CASE DR : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
94 tl
->fBackwardLoopDependencies
.insert(ls
);
96 } else if (isProj(sig
, &i
, r
) && fClass
->getLoopProperty(r
,ls
)) {
97 //cerr << "CASE R* : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
98 tl
->fBackwardLoopDependencies
.insert(ls
);
101 if (isProj(sig
, &i
, r
)) {
102 //cerr << "SYMBOL RECURSIF EN COURS ??? " << *r << endl;
103 } else if (getCertifiedSigType(sig
)->variability()<kSamp
) {
104 //cerr << "SLOW EXPRESSION " << endl;
106 //cerr << "Expression absorbée" << *sig << endl;
111 //cerr << "EXIT VectorCompiler::CS : "<< ppsig(sig) << "---code---> " << code << endl;
115 string
VectorCompiler::generateCode (Tree sig
)
117 generateCodeRecursions(sig
);
118 return generateCodeNonRec(sig
);
121 void VectorCompiler::generateCodeRecursions (Tree sig
)
125 //cerr << "VectorCompiler::generateCodeRecursions( " << ppsig(sig) << " )" << endl;
126 if (getCompiledExpression(sig
, code
)) {
127 //cerr << "** ALREADY VISITED : " << code << " ===> " << ppsig(sig) << endl;
129 } else if( isRec(sig
, id
, body
) ) {
130 //cerr << "we have a recursive expression non compiled yet : " << ppsig(sig) << endl;
131 setCompiledExpression(sig
, "[RecursionVisited]");
132 fClass
->openLoop(sig
, "count");
133 generateRec(sig
, id
, body
);
134 fClass
->closeLoop(sig
);
136 // we go down the expression
137 vector
<Tree
> subsigs
;
138 int n
= getSubSignals(sig
, subsigs
, false);
139 for (int i
=0; i
<n
; i
++) { generateCodeRecursions(subsigs
[i
]); }
143 string
VectorCompiler::generateCodeNonRec (Tree sig
)
146 if (getCompiledExpression(sig
, code
)) {
150 //cerr << "VectorCompiler::generateCodeNonRec( " << ppsig(sig) << " )" << endl;
151 code
= generateLoopCode(sig
);
152 setCompiledExpression(sig
, code
);
159 * @param sig the signal expression to compile.
160 * @return the C code translation of sig as a string
162 string
VectorCompiler::generateLoopCode (Tree sig
)
168 l
= fClass
->topLoop();
170 //cerr << "VectorCompiler::OLDgenerateCode " << ppsig(sig) << endl;
171 if (needSeparateLoop(sig
)) {
172 // we need a separate loop unless it's an old recursion
173 if (isProj(sig
, &i
, x
)) {
174 // projection of a recursive group x
175 if (l
->hasRecDependencyIn(singleton(x
))) {
176 // x is already in the loop stack
177 return ScalarCompiler::generateCode(sig
);
180 fClass
->openLoop(x
, "count");
181 string c
= ScalarCompiler::generateCode(sig
);
182 fClass
->closeLoop(sig
);
186 fClass
->openLoop("count");
187 string c
= ScalarCompiler::generateCode(sig
);
188 fClass
->closeLoop(sig
);
192 return ScalarCompiler::generateCode(sig
);
198 * Generate cache code for a signal if needed
199 * @param sig the signal expression.
200 * @param exp the corresponding C code.
201 * @return the cached C code
203 string
VectorCompiler::generateCacheCode(Tree sig
, const string
& exp
)
206 int sharing
= getSharingCount(sig
);
207 Type t
= getCertifiedSigType(sig
);
208 Occurences
* o
= fOccMarkup
.retrieve(sig
);
209 int d
= o
->getMaxDelay();
211 if (t
->variability() < kSamp
) {
213 // non-sample, not delayed : same as scalar cache
214 return ScalarCompiler::generateCacheCode(sig
,exp
);
217 // it is a non-sample expressions but used delayed
218 // we need a delay line
219 getTypedNames(getCertifiedSigType(sig
), "Vec", ctype
, vname
);
220 if ((sharing
> 1) && !verySimple(sig
)) {
221 // first cache this expression because it
222 // it is shared and complex
223 string cachedexp
= generateVariableStore(sig
, exp
);
224 generateDelayLine(ctype
, vname
, d
, cachedexp
);
225 setVectorNameProperty(sig
, vname
);
228 // no need to cache this expression because
229 // it is either not shared or very simple
230 generateDelayLine(ctype
, vname
, d
, exp
);
231 setVectorNameProperty(sig
, vname
);
236 // sample-rate signal
238 // used delayed : we need a delay line
239 getTypedNames(getCertifiedSigType(sig
), "Yec", ctype
, vname
);
240 generateDelayLine(ctype
, vname
, d
, exp
);
241 setVectorNameProperty(sig
, vname
);
243 if (verySimple(sig
)) {
246 if (d
< gMaxCopyDelay
) {
247 return subst("$0[i]", vname
);
249 // we use a ring buffer
250 string mask
= T(pow2limit(d
+ gVecSize
)-1);
251 return subst("$0[($0_idx+i) & $1]", vname
, mask
);
256 if ( sharing
> 1 && ! verySimple(sig
) ) {
257 // shared and not simple : we need a vector
258 // cerr << "ZEC : " << ppsig(sig) << endl;
259 getTypedNames(getCertifiedSigType(sig
), "Zec", ctype
, vname
);
260 generateDelayLine(ctype
, vname
, d
, exp
);
261 setVectorNameProperty(sig
, vname
);
262 return subst("$0[i]", vname
);
264 // not shared or simple : no cache needed
272 * Test if a signal need to be compiled in a separate loop.
273 * @param sig the signal expression to test.
274 * @return true if a separate loop is needed
276 bool VectorCompiler::needSeparateLoop(Tree sig
)
278 Occurences
* o
= fOccMarkup
.retrieve(sig
);
279 Type t
= getCertifiedSigType(sig
);
280 int c
= getSharingCount(sig
);
287 if (o
->getMaxDelay()>0) {
288 //cerr << "DLY "; // delayed expressions require a separate loop
290 } else if (verySimple(sig
) || t
->variability()<kSamp
) {
291 b
= false; // non sample computation never require a loop
292 } else if (isSigFixDelay(sig
, x
, y
)) {
294 } else if (isProj(sig
, &i
,x
)) {
295 //cerr << "REC "; // recursive expressions require a separate loop
298 //cerr << "SHA(" << c << ") "; // expressions used several times required a separate loop
301 // sample expressions that are not recursive, not delayed
302 // and not shared, doesn't require a separate loop.
306 cerr << "Separate Loop for " << ppsig(sig) << endl;
308 cerr << "Same Loop for " << ppsig(sig) << endl;
313 void VectorCompiler::generateDelayLine(const string
& ctype
, const string
& vname
, int mxd
, const string
& exp
)
316 vectorLoop(ctype
, vname
, exp
);
318 dlineLoop(ctype
, vname
, mxd
, exp
);
322 string
VectorCompiler::generateVariableStore(Tree sig
, const string
& exp
)
324 Type t
= getCertifiedSigType(sig
);
326 if (getCertifiedSigType(sig
)->variability() == kSamp
) {
328 getTypedNames(t
, "Vector", ctype
, vname
);
329 vectorLoop(ctype
, vname
, exp
);
330 return subst("$0[i]", vname
);
332 return ScalarCompiler::generateVariableStore(sig
, exp
);
338 * Generate code for accessing a delayed signal. The generated code depend of
339 * the maximum delay attached to exp and the gLessTempSwitch.
342 string
VectorCompiler::generateFixDelay (Tree sig
, Tree exp
, Tree delay
)
347 //cerr << "VectorCompiler::generateFixDelay " << ppsig(sig) << endl;
349 CS(exp
); // ensure exp is compiled to have a vector name
351 mxd
= fOccMarkup
.retrieve(exp
)->getMaxDelay();
353 if (! getVectorNameProperty(exp
, vecname
)) {
354 cerr
<< "ERROR no vector name for " << ppsig(exp
) << endl
;
359 // not a real vector name but a scalar name
360 return subst("$0[i]", vecname
);
362 } else if (mxd
< gMaxCopyDelay
){
363 if (isSigInt(delay
, &d
)) {
365 return subst("$0[i]", vecname
);
367 return subst("$0[i-$1]", vecname
, T(d
));
370 return subst("$0[i-$1]", vecname
, CS(delay
));
375 // long delay : we use a ring buffer of size 2^x
376 int N
= pow2limit( mxd
+gVecSize
);
378 if (isSigInt(delay
, &d
)) {
380 return subst("$0[($0_idx+i)&$1]", vecname
, T(N
-1));
382 return subst("$0[($0_idx+i-$2)&$1]", vecname
, T(N
-1), T(d
));
385 return subst("$0[($0_idx+i-$2)&$1]", vecname
, T(N
-1), CS(delay
));
392 * Generate code for the delay mecchanism. The generated code depend of the
393 * maximum delay attached to exp and the "less temporaries" switch
396 string
VectorCompiler::generateDelayVec(Tree sig
, const string
& exp
, const string
& ctype
, const string
& vname
, int mxd
)
398 // it is a non-sample but used delayed
399 // we need a delay line
400 generateDelayLine(ctype
, vname
, mxd
, exp
);
401 setVectorNameProperty(sig
, vname
);
402 if (verySimple(sig
)) {
405 return subst("$0[i]", vname
);
410 static int pow2limit(int x
)
413 while (n
< x
) { n
= 2*n
; }
419 * Generate the code for a (short) delay line
420 * @param k the c++ class where the delay line will be placed.
421 * @param l the loop where the code will be placed.
422 * @param tname the name of the C++ type (float or int)
423 * @param dlname the name of the delay line (vector) to be used.
424 * @param delay the maximum delay
425 * @param cexp the content of the signal as a C++ expression
427 void VectorCompiler::vectorLoop (const string
& tname
, const string
& vecname
, const string
& cexp
)
429 // -- declare the vector
430 fClass
->addSharedDecl(vecname
);
432 // -- variables moved as class fields...
433 fClass
->addZone1(subst("$0 \t$1[$2];", tname
, vecname
, T(gVecSize
)));
435 // -- compute the new samples
436 fClass
->addExecCode(subst("$0[i] = $1;", vecname
, cexp
));
441 * Generate the code for a (short) delay line
442 * @param k the c++ class where the delay line will be placed.
443 * @param l the loop where the code will be placed.
444 * @param tname the name of the C++ type (float or int)
445 * @param dlname the name of the delay line (vector) to be used.
446 * @param delay the maximum delay
447 * @param cexp the content of the signal as a C++ expression
449 void VectorCompiler::dlineLoop (const string
& tname
, const string
& dlname
, int delay
, const string
& cexp
)
451 if (delay
< gMaxCopyDelay
) {
453 // Implementation of a copy based delayline
455 // create names for temporary and permanent storage
456 string buf
= subst("$0_tmp", dlname
);
457 string pmem
= subst("$0_perm", dlname
);
459 // constraints delay size to be multiple of 4
460 delay
= (delay
+3)&-4;
462 // allocate permanent storage for delayed samples
463 string dsize
= T(delay
);
464 fClass
->addDeclCode(subst("$0 \t$1[$2];", tname
, pmem
, dsize
));
466 // init permanent memory
467 fClass
->addInitCode(subst("for (int i=0; i<$1; i++) $0[i]=0;", pmem
, dsize
));
471 // -- declare a buffer and a "shifted" vector
472 fClass
->addSharedDecl(buf
);
474 // -- variables moved as class fields...
475 fClass
->addZone1(subst("$0 \t$1[$2+$3];", tname
, buf
, T(gVecSize
), dsize
));
477 fClass
->addFirstPrivateDecl(dlname
);
478 fClass
->addZone2(subst("$0* \t$1 = &$2[$3];", tname
, dlname
, buf
, dsize
));
480 // -- copy the stored samples to the delay line
481 fClass
->addPreCode(subst("for (int i=0; i<$2; i++) $0[i]=$1[i];", buf
, pmem
, dsize
));
483 // -- compute the new samples
484 fClass
->addExecCode(subst("$0[i] = $1;", dlname
, cexp
));
486 // -- copy back to stored samples
487 fClass
->addPostCode(subst("for (int i=0; i<$2; i++) $0[i]=$1[count+i];", pmem
, buf
, dsize
));
491 // Implementation of a ring-buffer delayline
493 // the size should be large enough and aligned on a power of two
494 delay
= pow2limit(delay
+ gVecSize
);
495 string dsize
= T(delay
);
496 string mask
= T(delay
-1);
498 // create names for temporary and permanent storage
499 string idx
= subst("$0_idx", dlname
);
500 string idx_save
= subst("$0_idx_save", dlname
);
502 // allocate permanent storage for delayed samples
503 fClass
->addDeclCode(subst("$0 \t$1[$2];", tname
, dlname
, dsize
));
504 fClass
->addDeclCode(subst("int \t$0;", idx
));
505 fClass
->addDeclCode(subst("int \t$0;", idx_save
));
507 // init permanent memory
508 fClass
->addInitCode(subst("for (int i=0; i<$1; i++) $0[i]=0;", dlname
, dsize
));
509 fClass
->addInitCode(subst("$0 = 0;", idx
));
510 fClass
->addInitCode(subst("$0 = 0;", idx_save
));
513 fClass
->addPreCode(subst("$0 = ($0+$1)&$2;", idx
, idx_save
, mask
));
515 // -- compute the new samples
516 fClass
->addExecCode(subst("$0[($2+i)&$3] = $1;", dlname
, cexp
, idx
, mask
));
519 fClass
->addPostCode(subst("$0 = count;", idx_save
));