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