New directory tree, with preprocessor/ inside interpretor/.
[Faustine.git] / interpretor / preprocessor / faust-0.9.47mr3 / compiler / generator / compile_vect.cpp
diff --git a/interpretor/preprocessor/faust-0.9.47mr3/compiler/generator/compile_vect.cpp b/interpretor/preprocessor/faust-0.9.47mr3/compiler/generator/compile_vect.cpp
new file mode 100644 (file)
index 0000000..ad5b04f
--- /dev/null
@@ -0,0 +1,522 @@
+/************************************************************************
+ ************************************************************************
+    FAUST compiler
+       Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
+    ---------------------------------------------------------------------
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ ************************************************************************
+ ************************************************************************/
+
+
+
+#include "compile_vect.hh"
+#include "floats.hh"
+#include "ppsig.hh"
+
+extern int gVecSize;
+
+void VectorCompiler::compileMultiSignal (Tree L)
+{
+    //contextor recursivness(0);
+    L = prepare(L);     // optimize, share and annotate expression
+
+    for (int i = 0; i < fClass->inputs(); i++) {
+        fClass->addZone3(subst("$1* input$0 = &input[$0][index];", T(i), xfloat()));
+    }
+    for (int i = 0; i < fClass->outputs(); i++) {
+        fClass->addZone3(subst("$1* output$0 = &output[$0][index];", T(i), xfloat()));
+    }
+
+    fClass->addSharedDecl("fullcount");
+    fClass->addSharedDecl("input");
+    fClass->addSharedDecl("output");
+
+    for (int i = 0; isList(L); L = tl(L), i++) {
+        Tree sig = hd(L);
+        fClass->openLoop("count");
+        fClass->addExecCode(subst("output$0[i] = $2$1;", T(i), CS(sig), xcast()));
+        fClass->closeLoop(sig);
+    }
+
+    generateUserInterfaceTree(prepareUserInterfaceTree(fUIRoot));
+       generateMacroInterfaceTree("", prepareUserInterfaceTree(fUIRoot));
+    if (fDescription) {
+        fDescription->ui(prepareUserInterfaceTree(fUIRoot));
+    }
+}
+
+
+/**
+ * Compile a signal
+ * @param sig the signal expression to compile.
+ * @return the C code translation of sig as a string
+ */
+string  VectorCompiler::CS (Tree sig)
+{
+    string      code;
+    //cerr << "ENTER VectorCompiler::CS : "<< ppsig(sig) << endl;
+    if (!getCompiledExpression(sig, code)) {
+        code = generateCode(sig);
+               //cerr << "CS : " << code << " for " << ppsig(sig) << endl;
+        setCompiledExpression(sig, code);
+    } else {
+        // we require an already compiled expression
+        // therefore we must update the dependencies of
+        // the current loop
+        int     i;
+        Tree   x, d, r;
+        Loop*   ls;
+        Loop*   tl = fClass->topLoop();
+
+        if (fClass->getLoopProperty(sig,ls)) {
+            // sig has a loop property
+            //cerr << "CASE SH : fBackwardLoopDependencies.insert : " << tl << " --depend(A)son--> " << ls << endl;
+                       tl->fBackwardLoopDependencies.insert(ls);
+
+        } else if (isSigFixDelay(sig, x, d) && fClass->getLoopProperty(x,ls)) {
+            //cerr << "CASE DL : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
+            tl->fBackwardLoopDependencies.insert(ls);
+
+        } else if (isSigFixDelay(sig, x, d) && isProj(x, &i, r) && fClass->getLoopProperty(r,ls)) {
+            //cerr << "CASE DR : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
+            tl->fBackwardLoopDependencies.insert(ls);
+
+        } else if (isProj(sig, &i, r) && fClass->getLoopProperty(r,ls)) {
+            //cerr << "CASE R* : fBackwardLoopDependencies.insert : " << tl << " --depend(B)son--> " << ls << endl;
+            tl->fBackwardLoopDependencies.insert(ls);
+
+        } else {
+            if (isProj(sig, &i, r)) {
+                //cerr << "SYMBOL RECURSIF EN COURS ??? " << *r << endl;
+            } else if (getCertifiedSigType(sig)->variability()<kSamp) {
+                //cerr << "SLOW EXPRESSION " << endl;
+            } else {
+                //cerr << "Expression absorbĂ©e" << *sig << endl;
+            }
+
+        }
+    }
+    //cerr << "EXIT VectorCompiler::CS : "<< ppsig(sig) << "---code---> " << code << endl;
+       return code;
+}
+
+string VectorCompiler::generateCode (Tree sig)
+{
+    generateCodeRecursions(sig);
+    return generateCodeNonRec(sig);
+}
+
+void VectorCompiler::generateCodeRecursions (Tree sig)
+{
+    Tree    id, body;
+    string  code;
+    //cerr << "VectorCompiler::generateCodeRecursions( " << ppsig(sig) << " )" << endl;
+    if (getCompiledExpression(sig, code)) {
+        //cerr << "** ALREADY VISITED : " << code << " ===> " << ppsig(sig) << endl;
+        return;
+    } else if( isRec(sig, id, body) ) {
+        //cerr << "we have a recursive expression non compiled yet : " << ppsig(sig) << endl;
+        setCompiledExpression(sig, "[RecursionVisited]");
+        fClass->openLoop(sig, "count");
+        generateRec(sig, id, body);
+        fClass->closeLoop(sig);
+    } else {
+        // we go down the expression
+        vector<Tree>  subsigs;
+        int n = getSubSignals(sig, subsigs, false);
+        for (int i=0; i<n; i++) { generateCodeRecursions(subsigs[i]); }
+    }
+}
+
+string VectorCompiler::generateCodeNonRec (Tree sig)
+{
+    string  code;
+    if (getCompiledExpression(sig, code)) {
+        // already visited
+        return code;
+    } else {
+        //cerr << "VectorCompiler::generateCodeNonRec( " << ppsig(sig) << " )" << endl;
+        code = generateLoopCode(sig);
+        setCompiledExpression(sig, code);
+        return code;
+    }
+}
+
+/**
+ * Compile a signal
+ * @param sig the signal expression to compile.
+ * @return the C code translation of sig as a string
+ */
+string VectorCompiler::generateLoopCode (Tree sig)
+{
+    int     i;
+    Tree    x;
+    Loop*   l;
+
+    l = fClass->topLoop();
+    assert(l);
+    //cerr << "VectorCompiler::OLDgenerateCode " << ppsig(sig) << endl;
+    if (needSeparateLoop(sig)) {
+        // we need a separate loop unless it's an old recursion
+        if (isProj(sig, &i, x)) {
+            // projection of a recursive group x
+            if (l->hasRecDependencyIn(singleton(x))) {
+                // x is already in the loop stack
+                return ScalarCompiler::generateCode(sig);
+            } else {
+                // x must be defined
+                fClass->openLoop(x, "count");
+                string c = ScalarCompiler::generateCode(sig);
+                fClass->closeLoop(sig);
+                return c;
+            }
+        } else {
+            fClass->openLoop("count");
+            string c = ScalarCompiler::generateCode(sig);
+            fClass->closeLoop(sig);
+            return c;
+        }
+    } else {
+        return ScalarCompiler::generateCode(sig);
+    }
+}
+
+
+/**
+ * Generate cache code for a signal if needed
+ * @param sig the signal expression.
+ * @param exp the corresponding C code.
+ * @return the cached C code
+ */
+string VectorCompiler::generateCacheCode(Tree sig, const string& exp)
+{
+    string      vname, ctype;
+    int         sharing = getSharingCount(sig);
+    Type        t = getCertifiedSigType(sig);
+    Occurences* o = fOccMarkup.retrieve(sig);
+    int         d = o->getMaxDelay();
+
+    if (t->variability() < kSamp) {
+        if (d==0) {
+            // non-sample, not delayed : same as scalar cache
+            return ScalarCompiler::generateCacheCode(sig,exp);
+
+        } else {
+            // it is a non-sample expressions but used delayed
+            // we need a delay line
+                       getTypedNames(getCertifiedSigType(sig), "Vec", ctype, vname);
+            if ((sharing > 1) && !verySimple(sig)) {
+                // first cache this expression because it
+                // it is shared and complex
+                string cachedexp =  generateVariableStore(sig, exp);
+                generateDelayLine(ctype, vname, d, cachedexp);
+                setVectorNameProperty(sig, vname);
+                return cachedexp;
+            } else {
+                // no need to cache this expression because
+                // it is either not shared or very simple
+                generateDelayLine(ctype, vname, d, exp);
+                setVectorNameProperty(sig, vname);
+                return exp;
+            }
+        }
+    } else {
+        // sample-rate signal
+        if (d > 0) {
+            // used delayed : we need a delay line
+            getTypedNames(getCertifiedSigType(sig), "Yec", ctype, vname);
+            generateDelayLine(ctype, vname, d, exp);
+            setVectorNameProperty(sig, vname);
+
+            if (verySimple(sig)) {
+                return exp;
+            } else {
+                if (d < gMaxCopyDelay) {
+                    return subst("$0[i]", vname);
+                } else {
+                    // we use a ring buffer
+                    string mask = T(pow2limit(d + gVecSize)-1);
+                    return subst("$0[($0_idx+i) & $1]", vname, mask);
+                }
+            }
+        } else {
+            // not delayed
+            if ( sharing > 1 && ! verySimple(sig) ) {
+                // shared and not simple : we need a vector
+                // cerr << "ZEC : " << ppsig(sig) << endl;
+                getTypedNames(getCertifiedSigType(sig), "Zec", ctype, vname);
+                generateDelayLine(ctype, vname, d, exp);
+                setVectorNameProperty(sig, vname);
+                return subst("$0[i]", vname);
+           } else {
+                // not shared or simple : no cache needed
+                return exp;
+            }
+        }
+    }
+}
+
+/**
+ * Test if a signal need to be compiled in a separate loop.
+ * @param sig the signal expression to test.
+ * @return true if a separate loop is needed
+ */
+bool VectorCompiler::needSeparateLoop(Tree sig)
+{
+    Occurences* o = fOccMarkup.retrieve(sig);
+    Type        t = getCertifiedSigType(sig);
+    int         c = getSharingCount(sig);
+    bool        b;
+
+    int         i;
+    Tree        x,y;
+
+
+    if (o->getMaxDelay()>0) {
+        //cerr << "DLY "; // delayed expressions require a separate loop
+        b = true;
+    } else if (verySimple(sig) || t->variability()<kSamp) {
+        b = false;      // non sample computation never require a loop
+    } else if (isSigFixDelay(sig, x, y)) {
+        b = false;      //
+    } else if (isProj(sig, &i ,x)) {
+        //cerr << "REC "; // recursive expressions require a separate loop
+        b = true;
+    } else if (c > 1) {
+        //cerr << "SHA(" << c << ") "; // expressions used several times required a separate loop
+        b = true;
+    } else {
+        // sample expressions that are not recursive, not delayed
+        // and not shared, doesn't require a separate loop.
+        b = false;
+    }
+/*    if (b) {
+        cerr << "Separate Loop for " << ppsig(sig) << endl;
+    } else {
+        cerr << "Same Loop for " << ppsig(sig) << endl;
+    }*/
+    return b;
+}
+
+void VectorCompiler::generateDelayLine(const string& ctype, const string& vname, int mxd, const string& exp)
+{
+    if (mxd == 0) {
+        vectorLoop(ctype, vname, exp);
+    } else {
+        dlineLoop(ctype, vname, mxd, exp);
+    }
+}
+
+string VectorCompiler::generateVariableStore(Tree sig, const string& exp)
+{
+    Type        t = getCertifiedSigType(sig);
+
+    if (getCertifiedSigType(sig)->variability() == kSamp) {
+        string      vname, ctype;
+        getTypedNames(t, "Vector", ctype, vname);
+        vectorLoop(ctype, vname, exp);
+        return subst("$0[i]", vname);
+    } else {
+        return ScalarCompiler::generateVariableStore(sig, exp);
+    }
+}
+
+
+/**
+ * Generate code for accessing a delayed signal. The generated code depend of
+ * the maximum delay attached to exp and the gLessTempSwitch.
+ */
+
+string VectorCompiler::generateFixDelay (Tree sig, Tree exp, Tree delay)
+{
+    int     mxd, d;
+    string  vecname;
+
+    //cerr << "VectorCompiler::generateFixDelay " << ppsig(sig) << endl;
+
+    CS(exp); // ensure exp is compiled to have a vector name
+
+    mxd = fOccMarkup.retrieve(exp)->getMaxDelay();
+
+    if (! getVectorNameProperty(exp, vecname)) {
+        cerr << "ERROR no vector name for " << ppsig(exp) << endl;
+        exit(1);
+    }
+
+    if (mxd == 0) {
+        // not a real vector name but a scalar name
+        return subst("$0[i]", vecname);
+
+    } else if (mxd < gMaxCopyDelay){
+        if (isSigInt(delay, &d)) {
+            if (d == 0) {
+                return subst("$0[i]", vecname);
+            } else {
+                return subst("$0[i-$1]", vecname, T(d));
+            }
+        } else {
+            return subst("$0[i-$1]", vecname, CS(delay));
+        }
+
+    } else {
+
+        // long delay : we use a ring buffer of size 2^x
+        int     N   = pow2limit( mxd+gVecSize );
+
+        if (isSigInt(delay, &d)) {
+            if (d == 0) {
+                return subst("$0[($0_idx+i)&$1]", vecname, T(N-1));
+            } else {
+                return subst("$0[($0_idx+i-$2)&$1]", vecname, T(N-1), T(d));
+            }
+        } else {
+            return subst("$0[($0_idx+i-$2)&$1]", vecname, T(N-1), CS(delay));
+        }
+    }
+}
+
+
+/**
+ * Generate code for the delay mecchanism. The generated code depend of the
+ * maximum delay attached to exp and the "less temporaries" switch
+ */
+
+string VectorCompiler::generateDelayVec(Tree sig, const string& exp, const string& ctype, const string& vname, int mxd)
+{
+    // it is a non-sample but used delayed
+    // we need a delay line
+    generateDelayLine(ctype, vname, mxd, exp);
+    setVectorNameProperty(sig, vname);
+    if (verySimple(sig)) {
+        return exp;
+    } else {
+        return subst("$0[i]", vname);
+    }
+}
+
+#if 0
+static int pow2limit(int x)
+{
+    int n = 2;
+    while (n < x) { n = 2*n; }
+    return n;
+}
+#endif
+
+/**
+ * Generate the code for a (short) delay line
+ * @param k the c++ class where the delay line will be placed.
+ * @param l the loop where the code will be placed.
+ * @param tname the name of the C++ type (float or int)
+ * @param dlname the name of the delay line (vector) to be used.
+ * @param delay the maximum delay
+ * @param cexp the content of the signal as a C++ expression
+ */
+void  VectorCompiler::vectorLoop (const string& tname, const string& vecname, const string& cexp)
+{
+    // -- declare the vector
+    fClass->addSharedDecl(vecname);
+
+    // -- variables moved as class fields...
+    fClass->addZone1(subst("$0 \t$1[$2];", tname, vecname, T(gVecSize)));
+
+    // -- compute the new samples
+    fClass->addExecCode(subst("$0[i] = $1;", vecname, cexp));
+}
+
+
+/**
+ * Generate the code for a (short) delay line
+ * @param k the c++ class where the delay line will be placed.
+ * @param l the loop where the code will be placed.
+ * @param tname the name of the C++ type (float or int)
+ * @param dlname the name of the delay line (vector) to be used.
+ * @param delay the maximum delay
+ * @param cexp the content of the signal as a C++ expression
+ */
+void  VectorCompiler::dlineLoop (const string& tname, const string& dlname, int delay, const string& cexp)
+{
+    if (delay < gMaxCopyDelay) {
+
+        // Implementation of a copy based delayline
+
+           // create names for temporary and permanent storage
+           string  buf = subst("$0_tmp", dlname);
+        string  pmem= subst("$0_perm", dlname);
+
+        // constraints delay size to be multiple of 4
+        delay = (delay+3)&-4;
+
+        // allocate permanent storage for delayed samples
+        string  dsize   = T(delay);
+        fClass->addDeclCode(subst("$0 \t$1[$2];", tname, pmem, dsize));
+
+        // init permanent memory
+        fClass->addInitCode(subst("for (int i=0; i<$1; i++) $0[i]=0;", pmem, dsize));
+
+        // compute method
+
+        // -- declare a buffer and a "shifted" vector
+        fClass->addSharedDecl(buf);
+
+        // -- variables moved as class fields...
+        fClass->addZone1(subst("$0 \t$1[$2+$3];", tname, buf, T(gVecSize), dsize));
+
+        fClass->addFirstPrivateDecl(dlname);
+        fClass->addZone2(subst("$0* \t$1 = &$2[$3];", tname, dlname, buf, dsize));
+
+        // -- copy the stored samples to the delay line
+        fClass->addPreCode(subst("for (int i=0; i<$2; i++) $0[i]=$1[i];", buf, pmem, dsize));
+
+        // -- compute the new samples
+        fClass->addExecCode(subst("$0[i] = $1;", dlname, cexp));
+
+        // -- copy back to stored samples
+        fClass->addPostCode(subst("for (int i=0; i<$2; i++) $0[i]=$1[count+i];", pmem, buf, dsize));
+
+    } else {
+
+        // Implementation of a ring-buffer delayline
+
+        // the size should be large enough and aligned on a power of two
+        delay   = pow2limit(delay + gVecSize);
+        string  dsize   = T(delay);
+        string  mask    = T(delay-1);
+
+        // create names for temporary and permanent storage
+        string  idx = subst("$0_idx", dlname);
+        string  idx_save = subst("$0_idx_save", dlname);
+
+        // allocate permanent storage for delayed samples
+        fClass->addDeclCode(subst("$0 \t$1[$2];", tname, dlname, dsize));
+        fClass->addDeclCode(subst("int \t$0;", idx));
+        fClass->addDeclCode(subst("int \t$0;", idx_save));
+
+        // init permanent memory
+        fClass->addInitCode(subst("for (int i=0; i<$1; i++) $0[i]=0;", dlname, dsize));
+        fClass->addInitCode(subst("$0 = 0;", idx));
+        fClass->addInitCode(subst("$0 = 0;", idx_save));
+
+        // -- update index
+        fClass->addPreCode(subst("$0 = ($0+$1)&$2;", idx, idx_save, mask));
+
+        // -- compute the new samples
+        fClass->addExecCode(subst("$0[($2+i)&$3] = $1;", dlname, cexp, idx, mask));
+
+        // -- save index
+        fClass->addPostCode(subst("$0 = count;", idx_save));
+    }
+}
+