1 #include "fpconfig.hh"
2 #include "fparser.hh"
3 #include "extrasrc/fptypes.hh"
4 
5 #ifdef FP_SUPPORT_OPTIMIZER
6 
7 #include <vector>
8 #include <utility>
9 
10 #include "codetree.hh"
11 
12 #ifndef FP_GENERATING_POWI_TABLE
13 enum { MAX_POWI_BYTECODE_LENGTH = 20 };
14 #else
15 enum { MAX_POWI_BYTECODE_LENGTH = 999 };
16 #endif
17 enum { MAX_MULI_BYTECODE_LENGTH = 3 };
18 
19 namespace FPoptimizer_ByteCode
20 {
21     template<typename Value_t>
22     class ByteCodeSynth
23     {
24     public:
ByteCodeSynth()25         ByteCodeSynth()
26             : ByteCode(), Immed(), StackState(), StackTop(0), StackMax(0)
27         {
28             /* estimate the initial requirements as such */
29             ByteCode.reserve(64);
30             Immed.reserve(8);
31             StackState.reserve(16);
32         }
33 
Pull(std::vector<unsigned> & bc,std::vector<Value_t> & imm,size_t & StackTop_max)34         void Pull(std::vector<unsigned>& bc,
35                   std::vector<Value_t>&   imm,
36                   size_t& StackTop_max)
37         {
38             /* The bitmask 0x80000000u was added to each non-opcode
39              * value within ByteCode[] (opcode parameters) to prevent
40              * them being interpreted as opcodes by fp_opcode_add.inc.
41              * fparser uses cNop for the same purpose.
42              */
43             for(unsigned a=0; a<ByteCode.size(); ++a)
44             {
45                 ByteCode[a] &= ~0x80000000u;
46             }
47             ByteCode.swap(bc);
48             Immed.swap(imm);
49             StackTop_max = StackMax;
50         }
51 
GetByteCodeSize() const52         size_t GetByteCodeSize() const { return ByteCode.size(); }
GetStackTop() const53         size_t GetStackTop()     const { return StackTop; }
54 
PushVar(unsigned varno)55         void PushVar(unsigned varno)
56         {
57             ByteCode.push_back(varno);
58             SetStackTop(StackTop+1);
59         }
60 
PushImmed(Value_t immed)61         void PushImmed(Value_t immed)
62         {
63             using namespace FUNCTIONPARSERTYPES;
64             ByteCode.push_back(cImmed);
65             Immed.push_back(immed);
66             SetStackTop(StackTop+1);
67         }
68 
StackTopIs(const FPoptimizer_CodeTree::CodeTree<Value_t> & tree,int offset=0)69         void StackTopIs(const FPoptimizer_CodeTree::CodeTree<Value_t>& tree, int offset = 0)
70         {
71             if((int)StackTop > offset)
72             {
73                 StackState[StackTop-1-offset].first = true;
74                 StackState[StackTop-1-offset].second = tree;
75             }
76         }
77 
IsStackTop(const FPoptimizer_CodeTree::CodeTree<Value_t> & tree,int offset=0) const78         bool IsStackTop(const FPoptimizer_CodeTree::CodeTree<Value_t>& tree, int offset = 0) const
79         {
80             return (int)StackTop > offset
81                && StackState[StackTop-1-offset].first
82                && StackState[StackTop-1-offset].second.IsIdenticalTo(tree);
83         }
84 
EatNParams(unsigned eat_count)85         inline void EatNParams(unsigned eat_count)
86         {
87             StackTop -= eat_count;
88         }
89 
ProducedNParams(unsigned produce_count)90         void ProducedNParams(unsigned produce_count)
91         {
92             SetStackTop(StackTop + produce_count);
93         }
94 
DoPopNMov(size_t targetpos,size_t srcpos)95         void DoPopNMov(size_t targetpos, size_t srcpos)
96         {
97             using namespace FUNCTIONPARSERTYPES;
98             ByteCode.push_back(cPopNMov);
99             ByteCode.push_back( 0x80000000u | (unsigned) targetpos);
100             ByteCode.push_back( 0x80000000u | (unsigned) srcpos);
101 
102             SetStackTop(srcpos+1);
103             StackState[targetpos] = StackState[srcpos];
104             SetStackTop(targetpos+1);
105         }
106 
DoDup(size_t src_pos)107         void DoDup(size_t src_pos)
108         {
109             using namespace FUNCTIONPARSERTYPES;
110             if(src_pos == StackTop-1)
111             {
112                 ByteCode.push_back(cDup);
113             }
114             else
115             {
116                 ByteCode.push_back(cFetch);
117                 ByteCode.push_back( 0x80000000u | (unsigned) src_pos);
118             }
119             SetStackTop(StackTop + 1);
120             StackState[StackTop-1] = StackState[src_pos];
121         }
122 
123 #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING
124         template<int/*defer*/>
Dump()125         void Dump()
126         {
127             std::ostream& o = std::cout;
128             o << "Stack state now(" << StackTop << "):\n";
129             for(size_t a=0; a<StackTop; ++a)
130             {
131                 o << a << ": ";
132                 if(StackState[a].first)
133                 {
134                     const FPoptimizer_CodeTree::CodeTree<Value_t>
135                         & tree = StackState[a].second;
136                     o << '[' << std::hex << (void*)(&tree.GetParams())
137                              << std::dec
138                              << ',' << tree.GetRefCount()
139                              << ']';
140                     DumpTree(tree, o);
141                 }
142                 else
143                     o << "?";
144                 o << "\n";
145             }
146             o << std::flush;
147         }
148 #endif
149 
FindPos(const FPoptimizer_CodeTree::CodeTree<Value_t> & tree) const150         size_t FindPos(const FPoptimizer_CodeTree::CodeTree<Value_t>& tree) const
151         {
152             for(size_t a=StackTop; a-->0; )
153                 if(StackState[a].first && StackState[a].second.IsIdenticalTo(tree))
154                     return a;
155             return ~size_t(0);
156         }
157 
Find(const FPoptimizer_CodeTree::CodeTree<Value_t> & tree) const158         bool Find(const FPoptimizer_CodeTree::CodeTree<Value_t>& tree) const
159         {
160             return FindPos(tree) != ~size_t(0);
161         }
162 
FindAndDup(const FPoptimizer_CodeTree::CodeTree<Value_t> & tree)163         bool FindAndDup(const FPoptimizer_CodeTree::CodeTree<Value_t>& tree)
164         {
165             size_t pos = FindPos(tree);
166             if(pos != ~size_t(0))
167             {
168             #ifdef DEBUG_SUBSTITUTIONS
169                 std::cout << "Found duplicate at [" << pos <<"]: ";
170                 DumpTree(tree);
171                 std::cout << " -- issuing cDup or cFetch\n";
172             #endif
173                 DoDup(pos);
174                 return true;
175             }
176             return false;
177         }
178 
179         struct IfData
180         {
181             size_t ofs;
182         };
183 
SynthIfStep1(IfData & ifdata,FUNCTIONPARSERTYPES::OPCODE op)184         void SynthIfStep1(IfData& ifdata, FUNCTIONPARSERTYPES::OPCODE op)
185         {
186             using namespace FUNCTIONPARSERTYPES;
187             SetStackTop(StackTop-1); // the If condition was popped.
188 
189             ifdata.ofs = ByteCode.size();
190             ByteCode.push_back(op);
191             ByteCode.push_back(0x80000000u); // code index
192             ByteCode.push_back(0x80000000u); // Immed index
193         }
SynthIfStep2(IfData & ifdata)194         void SynthIfStep2(IfData& ifdata)
195         {
196             using namespace FUNCTIONPARSERTYPES;
197             SetStackTop(StackTop-1); // ignore the pushed then-branch result.
198 
199             ByteCode[ifdata.ofs+1] = 0x80000000u | unsigned( ByteCode.size()+2 );
200             ByteCode[ifdata.ofs+2] = 0x80000000u | unsigned( Immed.size()      );
201 
202             ifdata.ofs = ByteCode.size();
203             ByteCode.push_back(cJump);
204             ByteCode.push_back(0x80000000u); // code index
205             ByteCode.push_back(0x80000000u); // Immed index
206         }
SynthIfStep3(IfData & ifdata)207         void SynthIfStep3(IfData& ifdata)
208         {
209             using namespace FUNCTIONPARSERTYPES;
210             SetStackTop(StackTop-1); // ignore the pushed else-branch result.
211 
212             ByteCode.back() |= 0x80000000u;
213             // ^Necessary for guarding against if(x,1,2)+1 being changed
214             //  into if(x,1,3) by fp_opcode_add.inc
215 
216             ByteCode[ifdata.ofs+1] = 0x80000000u | unsigned( ByteCode.size()-1 );
217             ByteCode[ifdata.ofs+2] = 0x80000000u | unsigned( Immed.size()      );
218 
219             SetStackTop(StackTop+1); // one or the other was pushed.
220 
221             /* Threading jumps:
222              * If there are any cJumps that point
223              * to the cJump instruction we just changed,
224              * change them to point to this target as well.
225              * This screws up PrintByteCode() majorly.
226              */
227             for(size_t a=0; a<ifdata.ofs; ++a)
228             {
229                 if(ByteCode[a]   == cJump
230                 && ByteCode[a+1] == (0x80000000u | (ifdata.ofs-1)))
231                 {
232                     ByteCode[a+1] = 0x80000000u | unsigned( ByteCode.size()-1 );
233                     ByteCode[a+2] = 0x80000000u | unsigned( Immed.size()      );
234                 }
235                 switch(ByteCode[a])
236                 {
237                     case cAbsIf:
238                     case cIf:
239                     case cJump:
240                     case cPopNMov: a += 2; break;
241                     case cFCall:
242                     case cPCall:
243                     case cFetch: a += 1; break;
244                     default: break;
245                 }
246             }
247         }
248 
249     protected:
SetStackTop(size_t value)250         void SetStackTop(size_t value)
251         {
252             StackTop = value;
253             if(StackTop > StackMax)
254             {
255                 StackMax = StackTop;
256                 StackState.resize(StackMax);
257             }
258         }
259 
260     protected:
261         std::vector<unsigned> ByteCode;
262         std::vector<Value_t>   Immed;
263 
264         std::vector<
265             std::pair<bool/*known*/,
266                       FPoptimizer_CodeTree::CodeTree<Value_t>/*tree*/>
267                    > StackState;
268         size_t StackTop;
269         size_t StackMax;
270     private:
incStackPtr()271         void incStackPtr()
272         {
273             if(StackTop+2 > StackMax) StackState.resize(StackMax=StackTop+2);
274         }
275 
276         template<bool IsIntType, bool IsComplexType>
277         struct Specializer { };
278     public:
AddOperation(unsigned opcode,unsigned eat_count,unsigned produce_count=1)279         void AddOperation(unsigned opcode, unsigned eat_count, unsigned produce_count = 1)
280         {
281             EatNParams(eat_count);
282             AddFunctionOpcode(opcode);
283             ProducedNParams(produce_count);
284         }
285 
286         void AddFunctionOpcode(unsigned opcode, Specializer<false,false>);
287         void AddFunctionOpcode(unsigned opcode, Specializer<false,true>);
288         void AddFunctionOpcode(unsigned opcode, Specializer<true,false>);
289         void AddFunctionOpcode(unsigned opcode, Specializer<true,true>);
AddFunctionOpcode(unsigned opcode)290         inline void AddFunctionOpcode(unsigned opcode)
291         {
292             AddFunctionOpcode
293                 (opcode,
294                  Specializer< bool(FUNCTIONPARSERTYPES::IsIntType<Value_t>::result),
295                               bool(FUNCTIONPARSERTYPES::IsComplexType<Value_t>::result)
296                            > ()
297                          );
298         }
299     };
300 
301     template<typename Value_t>
302     struct SequenceOpCode;
303     template<typename Value_t>
304     struct SequenceOpcodes
305     {
306         /* Multiplication implemented with adds */
307         static const SequenceOpCode<Value_t> AddSequence;
308         /* Exponentiation implemented with muls */
309         static const SequenceOpCode<Value_t> MulSequence;
310     };
311 
312     /* Generate a sequence that multiplies or exponentifies the
313      * last operand in the stack by the given constant integer
314      * amount (positive or negative).
315      */
316     template<typename Value_t>
317     void AssembleSequence(
318         long count,
319         const SequenceOpCode<Value_t>& sequencing,
320         ByteCodeSynth<Value_t>& synth);
321 }
322 
323 #endif
324