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 <math.h>
23 
24 #include "Text.hh"
25 #include "floats.hh"
26 #include "ppsig.hh"
27 #include "sigtyperules.hh"
28 #include "xtended.hh"
29 
30 /********************************************************************************************
31 
32 FTZ is a special primitive injected in recursive signals when the -ftz option is on.
33 The injected code allows to flush to zero denormalized number. This option should be used only
34 when it is not available on the CPU.
35 
36 *********************************************************************************************/
37 
38 const char* FTZPattern[4][3] = {
39     {"???", "???", "???"},                                                                                // not a float
40     {"$0", "((fabsf($0)> FLT_MIN) ? $0 : 0.0f)", "((*(int*)&$0) & 0x7F800000) ? $0 : 0.0f"},              // float  (1)
41     {"$0", "((fabs($0)> DBL_MIN) ? $0 : 0.0 )", "((*(long int*)&$0) & 0x7FF0000000000000) ? $0 : 0.0"},   // double (2)
42     {"$0", "((fabsl($0)> LDBL_MIN) ? $0 : 0.0L)", "((fabsl($0)>LDBL_MIN) ? $0 : 0.0L)"}                   // quad   (3)
43 };
44 
45 class FtzPrim : public xtended {
46    private:
47     static int freshnum;  // counter for fTempFTZxxx fresh variables
48 
49    public:
FtzPrim()50     FtzPrim() : xtended("ftz") {}
51 
arity()52     virtual unsigned int arity() { return 1; }
53 
needCache()54     virtual bool needCache() { return true; }
55 
infereSigType(const vector<Type> & types)56     virtual Type infereSigType(const vector<Type>& types)
57     {
58         faustassert(types.size() == arity());
59         return types[0];
60     }
61 
infereSigOrder(const vector<int> & args)62     virtual int infereSigOrder(const vector<int>& args)
63     {
64         faustassert(args.size() == arity());
65         return args[0];
66     }
67 
computeSigOutput(const vector<Tree> & args)68     virtual Tree computeSigOutput(const vector<Tree>& args)
69     {
70         int    i;
71         double r;
72 
73         faustassert(args.size() == arity());
74 
75         if (isSigInt(args[0], &i)) {
76             return args[0];
77         } else if (isSigReal(args[0], &r)) {
78             return args[0];
79         } else {
80             return tree(symbol(), args[0]);
81         }
82     }
83 
generateCode(CodeContainer * container,const list<ValueInst * > & args,::Type result,vector<::Type> const & types)84     virtual ValueInst* generateCode(CodeContainer* container, const list<ValueInst*>& args, ::Type result,
85                                     vector<::Type> const& types)
86     {
87         faustassert(args.size() == arity());
88         faustassert(types.size() == arity());
89 
90         Type t = infereSigType(types);
91         if ((t->nature() == kReal) && (gGlobal->gFTZMode > 0)) {
92             switch (gGlobal->gFTZMode) {
93                 case 1: {
94                     // "fabs" function has to be declared
95                     list<NamedTyped*> args_types;
96                     args_types.push_back(InstBuilder::genNamedTyped("dummy", InstBuilder::genBasicTyped(itfloat())));
97                     FunTyped* fun_type = InstBuilder::genFunTyped(args_types, InstBuilder::genBasicTyped(itfloat()));
98                     container->pushGlobalDeclare(InstBuilder::genDeclareFunInst(subst("fabs$0", isuffix()), fun_type));
99 
100                     // we need to create a temporary variable to store the expression
101                     string vname = gGlobal->getFreshID("fTempFTZ");
102                     container->addIncludeFile("<float.h>");
103                     container->pushComputeDSPMethod(
104                         InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(itfloat()), *args.begin()));
105                     ValueInst* real_min;
106                     if (gGlobal->gFloatSize == 1) {
107                         real_min = InstBuilder::genFloatNumInst(inummin());
108                     } else {
109                         real_min = InstBuilder::genDoubleNumInst(inummin());
110                     }
111 
112                     list<ValueInst*> args_value;
113                     args_value.push_back(InstBuilder::genLoadStackVar(vname));
114                     return InstBuilder::genSelect2Inst(
115                         InstBuilder::genGreaterThan(InstBuilder::genFunCallInst(subst("fabs$0", isuffix()), args_value),
116                                                     real_min),
117                         InstBuilder::genLoadStackVar(vname), InstBuilder::genTypedZero(itfloat()));
118                 } break;
119 
120                 case 2: {
121                     // Bitcast based solution
122                     string vname = gGlobal->getFreshID("fTempFTZ");
123                     container->pushComputeDSPMethod(
124                         InstBuilder::genDecStackVar(vname, InstBuilder::genBasicTyped(itfloat()), *args.begin()));
125                     switch (gGlobal->gFloatSize) {
126                         case 1:
127                             return InstBuilder::genSelect2Inst(
128                                 InstBuilder::genAnd(InstBuilder::genBitcastInst(InstBuilder::genLoadStackVar(vname),
129                                                                                 InstBuilder::genInt32Typed()),
130                                                     InstBuilder::genInt32NumInst(0x7F800000)),
131                                 InstBuilder::genLoadStackVar(vname), InstBuilder::genTypedZero(itfloat()));
132                         case 2:
133                             return InstBuilder::genSelect2Inst(
134                                 InstBuilder::genAnd(
135                                     InstBuilder::genBitcastInst(InstBuilder::genLoadStackVar(vname),
136                                                                 InstBuilder::genBasicTyped(Typed::kInt64)),
137                                     InstBuilder::genInt64NumInst(0x7FF0000000000000)),
138                                 InstBuilder::genLoadStackVar(vname), InstBuilder::genTypedZero(itfloat()));
139                         default:
140                             faustassert(false);
141                             return *args.begin();
142                     }
143                 } break;
144 
145                 default:
146                     faustassert(false);
147                     return *args.begin();
148             }
149 
150         } else {
151             // No ftz code for integer signals
152             return *args.begin();
153         }
154     }
155 
old_generateCode(Klass * klass,const vector<string> & args,const vector<::Type> & types)156     virtual string old_generateCode(Klass* klass, const vector<string>& args, const vector<::Type>& types)
157     {
158         faustassert(args.size() == arity());
159         faustassert(types.size() == arity());
160 
161         Type t = infereSigType(types);
162         if ((t->nature() == kReal) && (gGlobal->gFTZMode > 0)) {
163             // we need to create a temporary variable to store the expression
164             string ctype = ifloat();
165             string vname = subst("fTempFTZ$0", T(++freshnum));
166             klass->addIncludeFile("<float.h>");
167             klass->addExecCode(Statement("", subst("$0 $1 = $2;", ctype, vname, args[0])));
168             return subst(FTZPattern[gGlobal->gFloatSize][gGlobal->gFTZMode], vname);
169         } else {
170             // No ftz code for integer signals
171             return args[0];
172         }
173     }
174 
generateLateq(Lateq * lateq,const vector<string> & args,const vector<::Type> & types)175     virtual string generateLateq(Lateq* lateq, const vector<string>& args, const vector<::Type>& types)
176     {
177         faustassert(args.size() == arity());
178         faustassert(types.size() == arity());
179         return args[0];
180     }
181 };
182 
183 int FtzPrim::freshnum = 0;
184