1 #include <cmath>
2 #include <cassert>
3 
4 #include "codetree.hh"
5 #include "optimize.hh"
6 #include "opcodename.hh"
7 #include "grammar.hh"
8 #include "extrasrc/fptypes.hh"
9 
10 #include "consts.hh"
11 #include "fparser.hh"
12 
13 // There are several case statements in this file where we
14 // intentionally want to fall through, so let's not get warned about
15 // each one.
16 #ifdef __GNUC__
17 #if (__GNUC__ > 6)
18 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
19 #endif
20 #endif
21 
22 #ifdef FP_SUPPORT_OPTIMIZER
23 
24 using namespace FUNCTIONPARSERTYPES;
25 //using namespace FPoptimizer_Grammar;
26 
27 namespace
28 {
29     using namespace FPoptimizer_CodeTree;
30 
31     #define FactorStack std::vector /* typedef*/
32 
33     const struct PowiMuliType
34     {
35         unsigned opcode_square;
36         unsigned opcode_cumulate;
37         unsigned opcode_invert;
38         unsigned opcode_half;
39         unsigned opcode_invhalf;
40     } iseq_powi = {cSqr,cMul,cInv,cSqrt,cRSqrt},
41       iseq_muli = {~unsigned(0), cAdd,cNeg, ~unsigned(0),~unsigned(0) };
42 
43     template<typename Value_t>
ParsePowiMuli(const PowiMuliType & opcodes,const std::vector<unsigned> & ByteCode,size_t & IP,size_t limit,size_t factor_stack_base,FactorStack<Value_t> & stack)44     Value_t ParsePowiMuli(
45         const PowiMuliType& opcodes,
46         const std::vector<unsigned>& ByteCode, size_t& IP,
47         size_t limit,
48         size_t factor_stack_base,
49         FactorStack<Value_t>& stack)
50     {
51         Value_t result(1);
52         while(IP < limit)
53         {
54             if(ByteCode[IP] == opcodes.opcode_square)
55             {
56                 if(!isInteger(result)) break;
57                 result *= 2;
58                 ++IP;
59                 continue;
60             }
61             if(ByteCode[IP] == opcodes.opcode_invert)
62             {
63                 result = -result;
64                 ++IP;
65                 continue;
66             }
67             if(ByteCode[IP] == opcodes.opcode_half)
68             {
69                 if(result > Value_t(0) && isEvenInteger(result))
70                     break;
71                 result *= Value_t(0.5);
72                 ++IP;
73                 continue;
74             }
75             if(ByteCode[IP] == opcodes.opcode_invhalf)
76             {
77                 if(result > Value_t(0) && isEvenInteger(result))
78                     break;
79                 result *= Value_t(-0.5);
80                 ++IP;
81                 continue;
82             }
83 
84             size_t dup_fetch_pos = IP;
85             Value_t lhs(1);
86 
87             if(ByteCode[IP] == cFetch)
88             {
89                 unsigned index = ByteCode[++IP];
90                 if(index < factor_stack_base
91                 || size_t(index-factor_stack_base) >= stack.size())
92                 {
93                     // It wasn't a powi-fetch after all
94                     IP = dup_fetch_pos;
95                     break;
96                 }
97                 lhs = stack[index - factor_stack_base];
98                 // Note: ^This assumes that cFetch of recentmost
99                 //        is always converted into cDup.
100                 goto dup_or_fetch;
101             }
102             if(ByteCode[IP] == cDup)
103             {
104                 lhs = result;
105                 goto dup_or_fetch;
106 
107             dup_or_fetch:
108                 stack.push_back(result);
109                 ++IP;
110                 Value_t subexponent = ParsePowiMuli
111                     (opcodes,
112                      ByteCode, IP, limit,
113                      factor_stack_base, stack);
114                 if(IP >= limit || ByteCode[IP] != opcodes.opcode_cumulate)
115                 {
116                     // It wasn't a powi-dup after all
117                     IP = dup_fetch_pos;
118                     break;
119                 }
120                 ++IP; // skip opcode_cumulate
121                 stack.pop_back();
122                 result += lhs*subexponent;
123                 continue;
124             }
125             break;
126         }
127         return result;
128     }
129 
130     template<typename Value_t>
ParsePowiSequence(const std::vector<unsigned> & ByteCode,size_t & IP,size_t limit,size_t factor_stack_base)131     Value_t ParsePowiSequence
132         (const std::vector<unsigned>& ByteCode, size_t& IP,
133          size_t limit, size_t factor_stack_base)
134     {
135         FactorStack<Value_t> stack;
136         stack.push_back( Value_t(1) );
137         return ParsePowiMuli(iseq_powi, ByteCode, IP, limit, factor_stack_base, stack);
138     }
139 
140     template<typename Value_t>
ParseMuliSequence(const std::vector<unsigned> & ByteCode,size_t & IP,size_t limit,size_t factor_stack_base)141     Value_t ParseMuliSequence
142         (const std::vector<unsigned>& ByteCode, size_t& IP,
143          size_t limit, size_t factor_stack_base)
144     {
145         FactorStack<Value_t> stack;
146         stack.push_back( Value_t(1) );
147         return ParsePowiMuli(iseq_muli, ByteCode, IP, limit, factor_stack_base, stack);
148     }
149 
150     template<typename Value_t>
151     class CodeTreeParserData
152     {
153     public:
CodeTreeParserData(bool k_powi)154         explicit CodeTreeParserData(bool k_powi)
155             : stack(), clones(), keep_powi(k_powi) { }
156 
Eat(size_t nparams,OPCODE opcode)157         void Eat(size_t nparams, OPCODE opcode)
158         {
159             CodeTree<Value_t> newnode;
160             newnode.SetOpcode(opcode);
161 
162             std::vector<CodeTree<Value_t> > params = Pop(nparams);
163             newnode.SetParamsMove(params);
164 
165             if(!keep_powi)
166             switch(opcode)
167             {
168                 //        asinh: log(x + sqrt(x*x + 1))
169                 //cAsinh [x] -> cLog (cAdd x (cPow (cAdd (cPow x 2) 1) 0.5))
170                 // Note: ^ Replacement function refers to x twice
171 
172                 //        acosh: log(x + sqrt(x*x - 1))
173                 //cAcosh [x] -> cLog (cAdd x (cPow (cAdd (cPow x 2) -1) 0.5))
174 
175                 //        atanh: log( (1+x) / (1-x)) / 2
176                 //cAtanh [x] -> cMul (cLog (cMul (cAdd 1 x) (cPow (cAdd 1 (cMul -1 x)) -1))) 0.5
177 
178                 //        asin: atan2(x, sqrt(1-x*x))
179                 //cAsin[x] -> cAtan2 [x (cPow [(cAdd 1 (cMul (cPow [x 2] -1)) 0.5])]
180 
181                 //        acos: atan2(sqrt(1-x*x), x)
182                 //cAcos[x] -> cAtan2 [(cPow [(cAdd 1 (cMul (cPow [x 2] -1)) 0.5]) x]
183 
184                 //     The hyperbolic functions themselves are:
185                 //        sinh: (exp(x)-exp(-x)) / 2  = exp(-x) * (exp(2*x)-1) / 2
186                 //cSinh [x] -> cMul 0.5 (cPow [CONSTANT_EI x]) (cAdd [-1 (cPow [CONSTANT_2E x])])
187 
188                 //        cosh: (exp(x)+exp(-x)) / 2  = exp(-x) * (exp(2*x)+1) / 2
189                 //        cosh(-x) = cosh(x)
190                 //cCosh [x] -> cMul 0.5 (cPow [CONSTANT_EI x]) (cAdd [ 1 (cPow [CONSTANT_2E x])])
191 
192                 //        tanh: sinh/cosh = (exp(2*x)-1) / (exp(2*x)+1)
193                 //cTanh [x] -> (cMul (cAdd {(cPow [CONSTANT_2E x]) -1}) (cPow [(cAdd {(cPow [CONSTANT_2E x]) 1}) -1]))
194                 case cTanh:
195                 {
196                     CodeTree<Value_t> sinh, cosh;
197                     sinh.SetOpcode(cSinh); sinh.AddParam(newnode.GetParam(0)); sinh.Rehash();
198                     cosh.SetOpcode(cCosh); cosh.AddParamMove(newnode.GetParam(0)); cosh.Rehash();
199                     CodeTree<Value_t> pow;
200                     pow.SetOpcode(cPow);
201                     pow.AddParamMove(cosh);
202                     pow.AddParam(CodeTreeImmed(Value_t(-1)));
203                     pow.Rehash();
204                     newnode.SetOpcode(cMul);
205                     newnode.SetParamMove(0, sinh);
206                     newnode.AddParamMove(pow);
207                     break;
208                 }
209 
210                 //        tan: sin/cos
211                 //cTan [x] -> (cMul (cSin [x]) (cPow [(cCos [x]) -1]))
212                 case cTan:
213                 {
214                     CodeTree<Value_t> sin, cos;
215                     sin.SetOpcode(cSin); sin.AddParam(newnode.GetParam(0)); sin.Rehash();
216                     cos.SetOpcode(cCos); cos.AddParamMove(newnode.GetParam(0)); cos.Rehash();
217                     CodeTree<Value_t> pow;
218                     pow.SetOpcode(cPow);
219                     pow.AddParamMove(cos);
220                     pow.AddParam(CodeTreeImmed(Value_t(-1)));
221                     pow.Rehash();
222                     newnode.SetOpcode(cMul);
223                     newnode.SetParamMove(0, sin);
224                     newnode.AddParamMove(pow);
225                     break;
226                 }
227 
228                 case cPow:
229                 {
230                     const CodeTree<Value_t>& p0 = newnode.GetParam(0);
231                     const CodeTree<Value_t>& p1 = newnode.GetParam(1);
232                     if(p1.GetOpcode() == cAdd)
233                     {
234                         // convert x^(a + b) into x^a * x^b just so that
235                         // some optimizations can be run on it.
236                         // For instance, exp(log(x)*-61.1 + log(z)*-59.1)
237                         // won't be changed into exp(log(x*z)*-61.1)*z^2
238                         // unless we do this.
239                         std::vector<CodeTree<Value_t> > mulgroup(p1.GetParamCount());
240                         for(size_t a=0; a<p1.GetParamCount(); ++a)
241                         {
242                             CodeTree<Value_t> pow;
243                             pow.SetOpcode(cPow);
244                             pow.AddParam(p0);
245                             pow.AddParam(p1.GetParam(a));
246                             pow.Rehash();
247                             mulgroup[a].swap(pow);
248                         }
249                         newnode.SetOpcode(cMul);
250                         newnode.SetParamsMove(mulgroup);
251                     }
252                     break;
253                 }
254 
255                 // Should we change sin(x) into cos(pi/2-x)
256                 //               or cos(x) into sin(pi/2-x)?
257                 //                        note: cos(x-pi/2) = cos(pi/2-x) = sin(x)
258                 //                        note: sin(x-pi/2) = -sin(pi/2-x) = -cos(x)
259                 default: break;
260             }
261 
262             newnode.Rehash(!keep_powi);
263         /*
264             using namespace FPoptimizer_Grammar;
265             bool recurse = false;
266             while(ApplyGrammar(pack.glist[0], newnode, recurse)) // intermediate
267             { //std::cout << "Rerunning 1\n";
268                 newnode.FixIncompleteHashes();
269                 recurse = true;
270             }
271         */
272             FindClone(newnode, false);
273         #ifdef DEBUG_SUBSTITUTIONS
274             std::cout << "POP " << nparams << ", " << FP_GetOpcodeName(opcode)
275                       << "->" << FP_GetOpcodeName(newnode.GetOpcode())
276                       << ": PUSH ";
277             DumpTree(newnode);
278             std::cout <<std::endl;
279             DumpHashes(newnode);
280         #endif
281             stack.push_back(newnode);
282         }
283 
EatFunc(size_t nparams,OPCODE opcode,unsigned funcno)284         void EatFunc(size_t nparams, OPCODE opcode, unsigned funcno)
285         {
286             CodeTree<Value_t> newnode = CodeTreeFuncOp<Value_t> (opcode, funcno);
287             std::vector<CodeTree<Value_t> > params = Pop(nparams);
288             newnode.SetParamsMove(params);
289             newnode.Rehash(false);
290         #ifdef DEBUG_SUBSTITUTIONS
291             std::cout << "POP " << nparams << ", PUSH ";
292             DumpTree(newnode);
293             std::cout << std::endl;
294             DumpHashes(newnode);
295         #endif
296             FindClone(newnode);
297             stack.push_back(newnode);
298         }
299 
AddConst(const Value_t & value)300         void AddConst(const Value_t& value)
301         {
302             CodeTree<Value_t> newnode = CodeTreeImmed(value);
303             FindClone(newnode);
304             Push(newnode);
305         }
306 
AddVar(unsigned varno)307         void AddVar(unsigned varno)
308         {
309             CodeTree<Value_t> newnode = CodeTreeVar<Value_t>(varno);
310             FindClone(newnode);
311             Push(newnode);
312         }
313 
SwapLastTwoInStack()314         void SwapLastTwoInStack()
315         {
316             stack[stack.size()-1].swap( stack[stack.size()-2] );
317         }
318 
Dup()319         void Dup()
320         {
321             Fetch(stack.size()-1);
322         }
323 
Fetch(size_t which)324         void Fetch(size_t which)
325         {
326             Push(stack[which]);
327         }
328 
329         template<typename T>
Push(T tree)330         void Push(T tree)
331         {
332         #ifdef DEBUG_SUBSTITUTIONS
333             std::cout << "PUSH ";
334             DumpTree(tree);
335             std::cout << std::endl;
336             DumpHashes(tree);
337         #endif
338             stack.push_back(tree);
339         }
340 
PopNMov(size_t target,size_t source)341         void PopNMov(size_t target, size_t source)
342         {
343             stack[target] = stack[source];
344             stack.resize(target+1);
345         }
346 
PullResult()347         CodeTree<Value_t> PullResult()
348         {
349             clones.clear();
350             CodeTree<Value_t> result(stack.back());
351             stack.resize(stack.size()-1);
352             return result;
353         }
Pop(size_t n_pop)354         std::vector<CodeTree<Value_t> > Pop(size_t n_pop)
355         {
356             std::vector<CodeTree<Value_t> > result(n_pop);
357             for(unsigned n=0; n<n_pop; ++n)
358                 result[n].swap(stack[stack.size()-n_pop+n]);
359         #ifdef DEBUG_SUBSTITUTIONS
360             for(size_t n=n_pop; n-- > 0; )
361             {
362                 std::cout << "POP ";
363                 DumpTree(result[n]);
364                 std::cout << std::endl;
365                 DumpHashes(result[n]);
366             }
367         #endif
368             stack.resize(stack.size()-n_pop);
369             return result;
370         }
371 
GetStackTop() const372         size_t GetStackTop() const { return stack.size(); }
373     private:
FindClone(CodeTree<Value_t> &,bool=true)374         void FindClone(CodeTree<Value_t> & /*tree*/, bool /*recurse*/ = true)
375         {
376             // Disabled: Causes problems in optimization when
377             // the same subtree is included in logical and non-logical
378             // contexts: optimizations applied to the logical one will
379             // mess up the non-logical one.
380             return;
381             /*
382             std::multimap<fphash_t, CodeTree>::const_iterator
383                 i = clones.lower_bound(tree.GetHash());
384             for(; i != clones.end() && i->first == tree.GetHash(); ++i)
385             {
386                 if(i->second.IsIdenticalTo(tree))
387                     tree.Become(i->second);
388             }
389             if(recurse)
390                 for(size_t a=0; a<tree.GetParamCount(); ++a)
391                     FindClone(tree.GetParam(a));
392             clones.insert(std::make_pair(tree.GetHash(), tree));
393             */
394         }
395     private:
396         std::vector<CodeTree<Value_t> > stack;
397         std::multimap<fphash_t, CodeTree<Value_t> > clones;
398 
399         bool keep_powi;
400 
401     private:
402         CodeTreeParserData(const CodeTreeParserData&);
403         CodeTreeParserData& operator=(const CodeTreeParserData&);
404     };
405 
406     template<typename Value_t>
407     struct IfInfo
408     {
409         CodeTree<Value_t> condition;
410         CodeTree<Value_t> thenbranch;
411         size_t endif_location;
412 
IfInfo__anonbb570fb40111::IfInfo413         IfInfo(): condition(), thenbranch(), endif_location() { }
414     };
415 }
416 
417 namespace FPoptimizer_CodeTree
418 {
419     template<typename Value_t>
GenerateFrom(const typename FunctionParserBase<Value_t>::Data & fpdata,bool keep_powi)420     void CodeTree<Value_t>::GenerateFrom(
421         const typename FunctionParserBase<Value_t>::Data& fpdata,
422         bool keep_powi)
423     {
424         std::vector<CodeTree<Value_t> > var_trees;
425         var_trees.reserve(fpdata.mVariablesAmount);
426         for(unsigned n=0; n<fpdata.mVariablesAmount; ++n)
427         {
428             var_trees.push_back( CodeTreeVar<Value_t> (n+VarBegin) );
429         }
430         GenerateFrom(fpdata,var_trees,keep_powi);
431     }
432 
433     template<typename Value_t>
GenerateFrom(const typename FunctionParserBase<Value_t>::Data & fpdata,const std::vector<CodeTree> & var_trees,bool keep_powi)434     void CodeTree<Value_t>::GenerateFrom(
435         const typename FunctionParserBase<Value_t>::Data& fpdata,
436         const std::vector<CodeTree>& var_trees,
437         bool keep_powi)
438     {
439         const std::vector<unsigned>& ByteCode = fpdata.mByteCode;
440         const std::vector<Value_t>&  Immed    = fpdata.mImmed;
441 
442         /*for(unsigned i=0; i<ByteCode.size(); ++i)
443             fprintf(stderr, "by[%u/%u]=%u\n", i, (unsigned)ByteCode.size(), (unsigned) ByteCode[i]);*/
444 
445     #ifdef DEBUG_SUBSTITUTIONS
446         std::cout << "ENTERS GenerateFrom()\n";
447     #endif
448         CodeTreeParserData<Value_t> sim(keep_powi);
449         std::vector<IfInfo<Value_t> > if_stack;
450 
451         for(size_t IP=0, DP=0; ; ++IP)
452         {
453         after_powi:
454             while(!if_stack.empty() &&
455               (   // Normal If termination rule:
456                   if_stack.back().endif_location == IP
457                   // This rule matches when cJumps are threaded:
458                || (IP < ByteCode.size() && ByteCode[IP] == cJump
459                    && if_stack.back().thenbranch.IsDefined())
460               ))
461             {
462                 // The "else" of an "if" ends here
463                 CodeTree elsebranch = sim.PullResult();
464                 sim.Push(if_stack.back().condition);
465                 sim.Push(if_stack.back().thenbranch);
466                 sim.Push(elsebranch);
467                 sim.Eat(3, cIf);
468                 if_stack.pop_back();
469             }
470             if(IP >= ByteCode.size()) break;
471 
472             unsigned opcode = ByteCode[IP];
473             if((opcode == cSqr || opcode == cDup
474              || (opcode == cInv && !IsIntType<Value_t>::result)
475              || opcode == cNeg
476              || opcode == cSqrt || opcode == cRSqrt
477              || opcode == cFetch))
478             {
479                 // Parse a powi sequence
480                 size_t was_ip = IP;
481                 Value_t exponent = ParsePowiSequence<Value_t>(
482                     ByteCode, IP, if_stack.empty() ? ByteCode.size() : if_stack.back().endif_location,
483                     sim.GetStackTop()-1);
484                 if(exponent != Value_t(1.0))
485                 {
486                     //std::cout << "Found exponent at " << was_ip << ": " << exponent << "\n";
487                     sim.AddConst(exponent);
488                     sim.Eat(2, cPow);
489                     goto after_powi;
490                 }
491                 if(opcode == cDup
492                 || opcode == cFetch
493                 || opcode == cNeg)
494                 {
495                     Value_t factor = ParseMuliSequence<Value_t>(
496                         ByteCode, IP, if_stack.empty() ? ByteCode.size() : if_stack.back().endif_location,
497                         sim.GetStackTop()-1);
498                     if(factor != Value_t(1.0))
499                     {
500                         //std::cout << "Found factor at " << was_ip << ": " << factor << "\n";
501                         sim.AddConst(factor);
502                         sim.Eat(2, cMul);
503                         goto after_powi;
504                     }
505                 }
506                 IP = was_ip;
507             }
508             if(OPCODE(opcode) >= VarBegin)
509             {
510                 unsigned index = opcode-VarBegin;
511                 /*std::fprintf(stderr, "IP=%u, opcode=%u, VarBegin=%u, ind=%u\n",
512                     (unsigned)IP,(unsigned)opcode,(unsigned)VarBegin, (unsigned)index);*/
513                 sim.Push(var_trees[index]);
514             }
515             else
516             {
517                 switch( OPCODE(opcode) )
518                 {
519                     // Specials
520                     case cIf:
521                     case cAbsIf:
522                     {
523                         if_stack.resize(if_stack.size() + 1);
524                         CodeTree res( sim.PullResult() );
525                         if_stack.back().condition.swap( res );
526                         if_stack.back().endif_location = ByteCode.size();
527                         IP += 2; // dp,sp for elsebranch are irrelevant.
528                         continue;
529                     }
530                     case cJump:
531                     {
532                         CodeTree res( sim.PullResult() );
533                         if_stack.back().thenbranch.swap( res );
534                         if_stack.back().endif_location = ByteCode[IP+1]+1;
535                         IP += 2;
536                         continue;
537                     }
538                     case cImmed:
539                         sim.AddConst(Immed[DP++]);
540                         break;
541                     case cDup:
542                         sim.Dup();
543                         break;
544                     case cNop:
545                         break;
546                     case cFCall:
547                     {
548                         unsigned funcno = ByteCode[++IP];
549                         assert(funcno < fpdata.mFuncPtrs.size());
550                         unsigned params = fpdata.mFuncPtrs[funcno].mParams;
551                         sim.EatFunc(params, OPCODE(opcode), funcno);
552                         break;
553                     }
554                     case cPCall:
555                     {
556                         unsigned funcno = ByteCode[++IP];
557                         assert(funcno < fpdata.mFuncParsers.size());
558                         const FunctionParserBase<Value_t>& p =
559                             *fpdata.mFuncParsers[funcno].mParserPtr;
560                         unsigned params = fpdata.mFuncParsers[funcno].mParams;
561 
562                         /* Inline the procedure call */
563                         /* Works because cPCalls can never recurse */
564                         std::vector<CodeTree> paramlist = sim.Pop(params);
565                         CodeTree pcall_tree;
566                         pcall_tree.GenerateFrom(*p.mData, paramlist);
567                         sim.Push(pcall_tree);
568                         break;
569                     }
570                     // Unary operators requiring special attention
571                     case cInv:  // already handled by powi_opt
572                         //sim.Eat(1, cInv);
573                         //break;
574                         sim.AddConst(1);
575                         sim.SwapLastTwoInStack();
576                         sim.Eat(2, cDiv);
577                         break;
578                     case cNeg: // already handled by powi_opt
579                         sim.Eat(1, cNeg);
580                         break;
581                         sim.AddConst(0);
582                         sim.SwapLastTwoInStack();
583                         sim.Eat(2, cSub);
584                         break;
585                     case cSqr: // already handled by powi_opt
586                         //sim.Eat(1, cSqr);
587                         //break;
588                         sim.AddConst(2);
589                         sim.Eat(2, cPow);
590                         break;
591                     // Unary functions requiring special attention
592                     case cSqrt: // already handled by powi_opt
593                         sim.AddConst( Value_t(0.5) );
594                         sim.Eat(2, cPow);
595                         break;
596                     case cRSqrt: // already handled by powi_opt
597                         sim.AddConst( Value_t(-0.5) );
598                         sim.Eat(2, cPow);
599                         break;
600                     case cCbrt:
601                         sim.AddConst(Value_t(1) / Value_t(3));
602                         sim.Eat(2, cPow);
603                         break;
604                     case cDeg:
605                         sim.AddConst(fp_const_rad_to_deg<Value_t>());
606                         sim.Eat(2, cMul);
607                         break;
608                     case cRad:
609                         sim.AddConst(fp_const_deg_to_rad<Value_t>());
610                         sim.Eat(2, cMul);
611                         break;
612                     case cExp:
613                         if(keep_powi) goto default_function_handling;
614                         sim.AddConst(fp_const_e<Value_t>());
615                         sim.SwapLastTwoInStack();
616                         sim.Eat(2, cPow);
617                         break;
618                     case cExp2: // from fpoptimizer
619                         if(keep_powi) goto default_function_handling;
620                         sim.AddConst(2.0);
621                         sim.SwapLastTwoInStack();
622                         sim.Eat(2, cPow);
623                         break;
624                     case cCot:
625                         sim.Eat(1, cTan);
626                         if(keep_powi) { sim.Eat(1, cInv); break; }
627                         sim.AddConst(-1);
628                         sim.Eat(2, cPow);
629                         break;
630                     case cCsc:
631                         sim.Eat(1, cSin);
632                         if(keep_powi) { sim.Eat(1, cInv); break; }
633                         sim.AddConst(-1);
634                         sim.Eat(2, cPow);
635                         break;
636                     case cSec:
637                         sim.Eat(1, cCos);
638                         if(keep_powi) { sim.Eat(1, cInv); break; }
639                         sim.AddConst(-1);
640                         sim.Eat(2, cPow);
641                         break;
642                     case cInt: // int(x) = floor(x + 0.5)
643                     #ifndef __x86_64
644                         if(keep_powi) { sim.Eat(1, cInt); break; }
645                     #endif
646                         sim.AddConst( Value_t(0.5) );
647                         sim.Eat(2, cAdd);
648                         sim.Eat(1, cFloor);
649                         break;
650                     case cLog10:
651                         sim.Eat(1, cLog);
652                         sim.AddConst(fp_const_log10inv<Value_t>());
653                         sim.Eat(2, cMul);
654                         break;
655                     case cLog2:
656                         sim.Eat(1, cLog);
657                         sim.AddConst(fp_const_log2inv<Value_t>());
658                         sim.Eat(2, cMul);
659                         break;
660                     case cLog2by: // x y     -> log(x)*CONSTANT_L2I*y
661                         sim.SwapLastTwoInStack();   // y x
662                         sim.Eat(1, cLog);           // y log(x)
663                         sim.AddConst(fp_const_log2inv<Value_t>()); // y log(x) CONSTANT_L2I
664                         sim.Eat(3, cMul);           // y*log(x)*CONSTANT_L2I
665                         break;
666                     case cHypot: // x y -> sqrt(x*x + y*y)
667                         sim.AddConst(2);
668                         sim.Eat(2, cPow); // x y^2
669                         sim.SwapLastTwoInStack();
670                         sim.AddConst(2);
671                         sim.Eat(2, cPow); // y^2 x^2
672                         sim.Eat(2, cAdd); // y^2 + x^2
673                         sim.AddConst( Value_t(0.5) );
674                         sim.Eat(2, cPow); // (y^2 + x^2)^0.5
675                         break;
676                     case cSinCos:
677                         sim.Dup();
678                         sim.Eat(1, cSin);
679                         sim.SwapLastTwoInStack();
680                         sim.Eat(1, cCos);
681                         break;
682                     case cSinhCosh:
683                         sim.Dup();
684                         sim.Eat(1, cSinh);
685                         sim.SwapLastTwoInStack();
686                         sim.Eat(1, cCosh);
687                         break;
688                     //case cLog:
689                     //    sim.Eat(1, cLog2);
690                     //    sim.AddConst(fp_const_log2<Value_t>());
691                     //    sim.Eat(2, cMul);
692                     //    break;
693                     // Binary operators requiring special attention
694                     case cRSub: // from fpoptimizer
695                         sim.SwapLastTwoInStack();
696                         // Passthru to cSub
697                     case cSub:
698                         if(keep_powi) { sim.Eat(2, cSub); break; }
699                         sim.AddConst(-1);
700                         sim.Eat(2, cMul); // -x is x*-1
701                         sim.Eat(2, cAdd); // Minus is negative adding
702                         break;
703                     case cRDiv: // from fpoptimizer
704                         sim.SwapLastTwoInStack();
705                         // Passthru to cDiv
706                     case cDiv:
707                         if(keep_powi || IsIntType<Value_t>::result)
708                         {
709                             sim.Eat(2, cDiv);
710                             break;
711                         }
712                         sim.AddConst(-1);
713                         sim.Eat(2, cPow); // 1/x is x^-1
714                         sim.Eat(2, cMul); // Divide is inverse multiply
715                         break;
716                     // Binary operators not requiring special attention
717                     case cAdd: case cMul:
718                     case cMod: case cPow:
719                     case cEqual: case cLess: case cGreater:
720                     case cNEqual: case cLessOrEq: case cGreaterOrEq:
721                     case cAnd: case cOr:
722                     case cAbsAnd: case cAbsOr:
723                         sim.Eat(2, OPCODE(opcode));
724                         break;
725                     // Unary operators not requiring special attention
726                     case cNot:
727                     case cNotNot: // from fpoptimizer
728                     case cAbsNot:
729                     case cAbsNotNot:
730                         sim.Eat(1, OPCODE(opcode));
731                         break;
732                     // Special opcodes generated by fpoptimizer itself
733                     case cFetch:
734                         sim.Fetch(ByteCode[++IP]);
735                         break;
736                     case cPopNMov:
737                     {
738                         unsigned stackOffs_target = ByteCode[++IP];
739                         unsigned stackOffs_source = ByteCode[++IP];
740                         sim.PopNMov(stackOffs_target, stackOffs_source);
741                         break;
742                     }
743 
744                     default:
745                     default_function_handling:;
746                         unsigned funcno = opcode-cAbs;
747                         assert(funcno < FUNC_AMOUNT);
748                         const FuncDefinition& func = Functions[funcno];
749                         sim.Eat(func.params, OPCODE(opcode));
750                         break;
751                 }
752             }
753         }
754         Become(sim.PullResult());
755     #ifdef DEBUG_SUBSTITUTIONS
756         std::cout << "Produced tree:\n";
757         DumpTreeWithIndent(*this);
758     #endif
759     }
760 }
761 
762 /* BEGIN_EXPLICIT_INSTANTATION */
763 #include "instantiate.hh"
764 namespace FPoptimizer_CodeTree
765 {
766 #define FP_INSTANTIATE(type) \
767     template \
768     void CodeTree<type>::GenerateFrom( \
769         const FunctionParserBase<type>::Data& fpdata, \
770         bool keep_powi);
771     FPOPTIMIZER_EXPLICITLY_INSTANTIATE(FP_INSTANTIATE)
772 #undef FP_INSTANTIATE
773 }
774 /* END_EXPLICIT_INSTANTATION */
775 
776 #endif
777