mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
10949 lines
461 KiB
C++
10949 lines
461 KiB
C++
#line 1 "fpoptimizer/fpoptimizer_header.txt"
|
|
/***************************************************************************\
|
|
|* Function Parser for C++ v4.0.3 *|
|
|
|*-------------------------------------------------------------------------*|
|
|
|* Function optimizer *|
|
|
|*-------------------------------------------------------------------------*|
|
|
|* Copyright: Joel Yliluoma *|
|
|
\***************************************************************************/
|
|
|
|
/* NOTE:
|
|
This is a concatenation of all the header and source files of the
|
|
original optimizer source code. All the code has been concatenated
|
|
into this single file for convenience of usage (in other words, to
|
|
simply use the optimizer, it's enough to add this file to the project
|
|
rather than a multitude of files which the original optimizer source
|
|
code is composed of).
|
|
|
|
Thus this file exists for the usage of the Function parser library
|
|
only, and is not suitable for developing it further. If you want to
|
|
develop the library further, you should download the development
|
|
version of the library, which has all the original source files.
|
|
*/
|
|
|
|
#include "fpconfig.h"
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
#define FPOPTIMIZER_MERGED_FILE
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_hash.h"
|
|
#ifndef FPoptimizerHashH
|
|
#define FPoptimizerHashH
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
typedef unsigned long long fphash_value_t;
|
|
#define FPHASH_CONST(x) x##ULL
|
|
|
|
#else
|
|
|
|
#include <stdint.h>
|
|
typedef uint_fast64_t fphash_value_t;
|
|
#define FPHASH_CONST(x) x##ULL
|
|
|
|
#endif
|
|
|
|
namespace FUNCTIONPARSERTYPES
|
|
{
|
|
struct fphash_t
|
|
{
|
|
fphash_value_t hash1, hash2;
|
|
|
|
bool operator==(const fphash_t& rhs) const
|
|
{ return hash1 == rhs.hash1 && hash2 == rhs.hash2; }
|
|
|
|
bool operator!=(const fphash_t& rhs) const
|
|
{ return hash1 != rhs.hash1 || hash2 != rhs.hash2; }
|
|
|
|
bool operator<(const fphash_t& rhs) const
|
|
{ return hash1 != rhs.hash1 ? hash1 < rhs.hash1 : hash2 < rhs.hash2; }
|
|
};
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_autoptr.h"
|
|
#ifndef FPOptimizerAutoPtrH
|
|
#define FPOptimizerAutoPtrH
|
|
|
|
template<typename Ref>
|
|
class FPOPT_autoptr
|
|
{
|
|
public:
|
|
FPOPT_autoptr() : p(0) { }
|
|
FPOPT_autoptr(Ref* b) : p(b) { Birth(); }
|
|
FPOPT_autoptr(const FPOPT_autoptr& b) : p(b.p) { Birth(); }
|
|
|
|
inline Ref& operator* () const { return *p; }
|
|
inline Ref* operator->() const { return p; }
|
|
|
|
FPOPT_autoptr& operator= (Ref* b) { Set(b); return *this; }
|
|
FPOPT_autoptr& operator= (const FPOPT_autoptr& b) { Set(b.p); return *this; }
|
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
|
FPOPT_autoptr(FPOPT_autoptr&& b) : p(b.p) { b.p = 0; }
|
|
FPOPT_autoptr& operator= (FPOPT_autoptr&& b) { if(p != b.p) { Forget(); p=b.p; b.p=0; }
|
|
return *this; }
|
|
#endif
|
|
|
|
~FPOPT_autoptr() { Forget(); }
|
|
|
|
void UnsafeSetP(Ref* newp) { p = newp; }
|
|
void swap(FPOPT_autoptr<Ref>& b) { Ref* tmp=p; p=b.p; b.p=tmp; }
|
|
|
|
private:
|
|
inline static void Have(Ref* p2);
|
|
inline void Forget();
|
|
inline void Birth();
|
|
inline void Set(Ref* p2);
|
|
private:
|
|
Ref* p;
|
|
};
|
|
|
|
//
|
|
template<typename Ref>
|
|
inline void FPOPT_autoptr<Ref>::Forget()
|
|
{
|
|
if(!p) return;
|
|
p->RefCount -= 1;
|
|
if(!p->RefCount) delete p;
|
|
//assert(p->RefCount >= 0);
|
|
}
|
|
template<typename Ref>
|
|
inline void FPOPT_autoptr<Ref>::Have(Ref* p2)
|
|
{
|
|
if(p2) ++(p2->RefCount);
|
|
}
|
|
template<typename Ref>
|
|
inline void FPOPT_autoptr<Ref>::Birth()
|
|
{
|
|
Have(p);
|
|
}
|
|
template<typename Ref>
|
|
inline void FPOPT_autoptr<Ref>::Set(Ref* p2)
|
|
{
|
|
Have(p2);
|
|
Forget();
|
|
p = p2;
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_codetree.h"
|
|
#ifndef FPOptimizer_CodeTreeH
|
|
#define FPOptimizer_CodeTreeH
|
|
|
|
#include "fpconfig.h"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
#include <vector>
|
|
#include <utility>
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_hash.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_autoptr.h"
|
|
|
|
#ifdef FP_EPSILON
|
|
#define NEGATIVE_MAXIMUM (-FP_EPSILON)
|
|
#else
|
|
#define NEGATIVE_MAXIMUM (-1e-14)
|
|
#endif
|
|
|
|
namespace FPoptimizer_Grammar
|
|
{
|
|
struct Grammar;
|
|
}
|
|
|
|
namespace FPoptimizer_ByteCode
|
|
{
|
|
class ByteCodeSynth;
|
|
}
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
class CodeTree;
|
|
|
|
struct MinMaxTree
|
|
{
|
|
double min,max;
|
|
bool has_min, has_max;
|
|
MinMaxTree() : min(),max(),has_min(false),has_max(false) { }
|
|
MinMaxTree(double mi,double ma): min(mi),max(ma),has_min(true),has_max(true) { }
|
|
MinMaxTree(bool,double ma): min(),max(ma),has_min(false),has_max(true) { }
|
|
MinMaxTree(double mi,bool): min(mi),max(),has_min(true),has_max(false) { }
|
|
};
|
|
|
|
struct CodeTreeData;
|
|
class CodeTree
|
|
{
|
|
typedef FPOPT_autoptr<CodeTreeData> DataP;
|
|
DataP data;
|
|
|
|
public:
|
|
public:
|
|
CodeTree();
|
|
~CodeTree();
|
|
|
|
explicit CodeTree(double v); // produce an immed
|
|
struct VarTag { };
|
|
explicit CodeTree(unsigned varno, VarTag); // produce a var reference
|
|
struct CloneTag { };
|
|
explicit CodeTree(const CodeTree& b, CloneTag);
|
|
|
|
/* Generates a CodeTree from the given bytecode */
|
|
void GenerateFrom(
|
|
const std::vector<unsigned>& byteCode,
|
|
const std::vector<double>& immed,
|
|
const FunctionParser::Data& data,
|
|
bool keep_powi = false);
|
|
|
|
void GenerateFrom(
|
|
const std::vector<unsigned>& byteCode,
|
|
const std::vector<double>& immed,
|
|
const FunctionParser::Data& data,
|
|
const std::vector<CodeTree>& var_trees,
|
|
bool keep_powi = false);
|
|
|
|
void SynthesizeByteCode(
|
|
std::vector<unsigned>& byteCode,
|
|
std::vector<double>& immed,
|
|
size_t& stacktop_max);
|
|
|
|
void SynthesizeByteCode(
|
|
FPoptimizer_ByteCode::ByteCodeSynth& synth,
|
|
bool MustPopTemps=true) const;
|
|
|
|
size_t SynthCommonSubExpressions(
|
|
FPoptimizer_ByteCode::ByteCodeSynth& synth) const;
|
|
|
|
void SetParams(const std::vector<CodeTree>& RefParams);
|
|
void SetParamsMove(std::vector<CodeTree>& RefParams);
|
|
|
|
CodeTree GetUniqueRef();
|
|
// ^use this when CodeTree tmp=x; tmp.CopyOnWrite(); does not do exactly what you want
|
|
|
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
|
void SetParams(std::vector<CodeTree>&& RefParams);
|
|
#endif
|
|
void SetParam(size_t which, const CodeTree& b);
|
|
void SetParamMove(size_t which, CodeTree& b);
|
|
void AddParam(const CodeTree& param);
|
|
void AddParamMove(CodeTree& param);
|
|
void AddParams(const std::vector<CodeTree>& RefParams);
|
|
void AddParamsMove(std::vector<CodeTree>& RefParams);
|
|
void AddParamsMove(std::vector<CodeTree>& RefParams, size_t replacing_slot);
|
|
void DelParam(size_t index);
|
|
void DelParams();
|
|
|
|
void Become(const CodeTree& b);
|
|
|
|
inline size_t GetParamCount() const { return GetParams().size(); }
|
|
inline CodeTree& GetParam(size_t n) { return GetParams()[n]; }
|
|
inline const CodeTree& GetParam(size_t n) const { return GetParams()[n]; }
|
|
inline void SetOpcode(FUNCTIONPARSERTYPES::OPCODE o);
|
|
inline void SetFuncOpcode(FUNCTIONPARSERTYPES::OPCODE o, unsigned f);
|
|
inline void SetVar(unsigned v);
|
|
inline void SetImmed(double v);
|
|
inline FUNCTIONPARSERTYPES::OPCODE GetOpcode() const;
|
|
inline FUNCTIONPARSERTYPES::fphash_t GetHash() const;
|
|
inline const std::vector<CodeTree>& GetParams() const;
|
|
inline std::vector<CodeTree>& GetParams();
|
|
inline size_t GetDepth() const;
|
|
inline const double& GetImmed() const;
|
|
inline unsigned GetVar() const;
|
|
inline unsigned GetFuncNo() const;
|
|
inline bool IsDefined() const { return GetOpcode() != FUNCTIONPARSERTYPES::cNop; }
|
|
|
|
inline bool IsImmed() const { return GetOpcode() == FUNCTIONPARSERTYPES::cImmed; }
|
|
inline bool IsVar() const { return GetOpcode() == FUNCTIONPARSERTYPES::VarBegin; }
|
|
bool IsLongIntegerImmed() const { return IsImmed() && GetImmed() == (double)GetLongIntegerImmed(); }
|
|
long GetLongIntegerImmed() const { return (long)GetImmed(); }
|
|
bool IsLogicalValue() const;
|
|
inline unsigned GetRefCount() const;
|
|
/* This function calculates the minimum and maximum values
|
|
* of the tree's result. If an estimate cannot be made,
|
|
* has_min/has_max are indicated as false.
|
|
*/
|
|
MinMaxTree CalculateResultBoundaries_do() const;
|
|
MinMaxTree CalculateResultBoundaries() const;
|
|
|
|
enum TriTruthValue { IsAlways, IsNever, Unknown };
|
|
TriTruthValue GetEvennessInfo() const;
|
|
|
|
bool IsAlwaysSigned(bool positive) const;
|
|
bool IsAlwaysParity(bool odd) const
|
|
{ return GetEvennessInfo() == (odd?IsNever:IsAlways); }
|
|
bool IsAlwaysInteger(bool integer) const;
|
|
|
|
void ConstantFolding();
|
|
bool ConstantFolding_AndLogic();
|
|
bool ConstantFolding_OrLogic();
|
|
bool ConstantFolding_MulLogicItems();
|
|
bool ConstantFolding_AddLogicItems();
|
|
bool ConstantFolding_IfOperations();
|
|
bool ConstantFolding_PowOperations();
|
|
bool ConstantFolding_ComparisonOperations();
|
|
template<typename CondType>
|
|
bool ConstantFolding_LogicCommon(CondType cond_type, bool is_logical);
|
|
bool ConstantFolding_MulGrouping();
|
|
bool ConstantFolding_AddGrouping();
|
|
bool ConstantFolding_Assimilate();
|
|
|
|
void Rehash(bool constantfolding = true);
|
|
inline void Mark_Incompletely_Hashed();
|
|
inline bool Is_Incompletely_Hashed() const;
|
|
|
|
inline const FPoptimizer_Grammar::Grammar* GetOptimizedUsing() const;
|
|
inline void SetOptimizedUsing(const FPoptimizer_Grammar::Grammar* g);
|
|
|
|
bool RecreateInversionsAndNegations(bool prefer_base2 = false);
|
|
void FixIncompleteHashes();
|
|
|
|
void swap(CodeTree& b) { data.swap(b.data); }
|
|
bool IsIdenticalTo(const CodeTree& b) const;
|
|
void CopyOnWrite();
|
|
};
|
|
|
|
struct CodeTreeData
|
|
{
|
|
int RefCount;
|
|
|
|
/* Describing the codetree node */
|
|
FUNCTIONPARSERTYPES::OPCODE Opcode;
|
|
union
|
|
{
|
|
double Value; // In case of cImmed: value of the immed
|
|
unsigned Var; // In case of VarBegin: variable number
|
|
unsigned Funcno; // In case of cFCall or cPCall
|
|
};
|
|
|
|
// Parameters for the function
|
|
// These use the sign:
|
|
// For cAdd: operands to add together (0 to n)
|
|
// sign indicates that the value is negated before adding (0-x)
|
|
// For cMul: operands to multiply together (0 to n)
|
|
// sign indicates that the value is inverted before multiplying (1/x)
|
|
// For cAnd: operands to bitwise-and together (0 to n)
|
|
// sign indicates that the value is inverted before anding (!x)
|
|
// For cOr: operands to bitwise-or together (0 to n)
|
|
// sign indicates that the value is inverted before orring (!x)
|
|
// These don't use the sign (sign is always false):
|
|
// For cMin: operands to select the minimum of
|
|
// For cMax: operands to select the maximum of
|
|
// For cImmed, not used
|
|
// For VarBegin, not used
|
|
// For cIf: operand 1 = condition, operand 2 = yes-branch, operand 3 = no-branch
|
|
// For anything else: the parameters required by the operation/function
|
|
std::vector<CodeTree> Params;
|
|
|
|
/* Internal operation */
|
|
FUNCTIONPARSERTYPES::fphash_t Hash;
|
|
size_t Depth;
|
|
const FPoptimizer_Grammar::Grammar* OptimizedUsing;
|
|
|
|
CodeTreeData();
|
|
CodeTreeData(const CodeTreeData& b);
|
|
explicit CodeTreeData(double i);
|
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
|
CodeTreeData(CodeTreeData&& b);
|
|
#endif
|
|
|
|
bool IsIdenticalTo(const CodeTreeData& b) const;
|
|
void Sort();
|
|
void Recalculate_Hash_NoRecursion();
|
|
};
|
|
|
|
inline void CodeTree::SetOpcode(FUNCTIONPARSERTYPES::OPCODE o)
|
|
{ data->Opcode = o; }
|
|
inline void CodeTree::SetFuncOpcode(FUNCTIONPARSERTYPES::OPCODE o, unsigned f)
|
|
{ SetOpcode(o); data->Funcno = f; }
|
|
inline void CodeTree::SetVar(unsigned v)
|
|
{ SetOpcode(FUNCTIONPARSERTYPES::VarBegin); data->Var = v; }
|
|
inline void CodeTree::SetImmed(double v)
|
|
{ SetOpcode(FUNCTIONPARSERTYPES::cImmed); data->Value = v; }
|
|
inline FUNCTIONPARSERTYPES::OPCODE CodeTree::GetOpcode() const { return data->Opcode; }
|
|
inline FUNCTIONPARSERTYPES::fphash_t CodeTree::GetHash() const { return data->Hash; }
|
|
inline const std::vector<CodeTree>& CodeTree::GetParams() const { return data->Params; }
|
|
inline std::vector<CodeTree>& CodeTree::GetParams() { return data->Params; }
|
|
inline size_t CodeTree::GetDepth() const { return data->Depth; }
|
|
inline const double& CodeTree::GetImmed() const { return data->Value; }
|
|
inline unsigned CodeTree::GetVar() const { return data->Var; }
|
|
inline unsigned CodeTree::GetFuncNo() const { return data->Funcno; }
|
|
|
|
inline const FPoptimizer_Grammar::Grammar* CodeTree::GetOptimizedUsing() const
|
|
{ return data->OptimizedUsing; }
|
|
inline void CodeTree::SetOptimizedUsing(const FPoptimizer_Grammar::Grammar* g)
|
|
{ data->OptimizedUsing = g; }
|
|
inline unsigned CodeTree::GetRefCount() const { return data->RefCount; }
|
|
|
|
inline void CodeTree::Mark_Incompletely_Hashed() { data->Depth = 0; }
|
|
inline bool CodeTree::Is_Incompletely_Hashed() const { return data->Depth == 0; }
|
|
|
|
#ifdef FUNCTIONPARSER_SUPPORT_DEBUG_OUTPUT
|
|
void DumpHashes(const FPoptimizer_CodeTree::CodeTree& tree, std::ostream& o = std::cout);
|
|
void DumpTree(const FPoptimizer_CodeTree::CodeTree& tree, std::ostream& o = std::cout);
|
|
void DumpTreeWithIndent(const FPoptimizer_CodeTree::CodeTree& tree, std::ostream& o = std::cout, const std::string& indent = "\\");
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_grammar.h"
|
|
#ifndef FPOPT_NAN_CONST
|
|
|
|
#include <iostream>
|
|
|
|
#include "fpconfig.h"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
|
|
#define FPOPT_NAN_CONST (-1712345.25) /* Would use 0.0 / 0.0 here, but some compilers don't accept it. */
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
class CodeTree;
|
|
}
|
|
|
|
namespace FPoptimizer_Grammar
|
|
{
|
|
enum ImmedConstraint_Value
|
|
{
|
|
ValueMask = 0x07,
|
|
Value_AnyNum = 0x0, // any value
|
|
Value_EvenInt = 0x1, // any even integer (0,2,4, etc)
|
|
Value_OddInt = 0x2, // any odd integer (1,3, etc)
|
|
Value_IsInteger = 0x3, // any integer-value (excludes e.g. 0.2)
|
|
Value_NonInteger = 0x4, // any non-integer (excludes e.g. 1 or 5)
|
|
Value_Logical = 0x5 // a result of cNot,cNotNot,cAnd,cOr or comparators
|
|
};
|
|
enum ImmedConstraint_Sign
|
|
{
|
|
SignMask = 0x18,
|
|
Sign_AnySign = 0x00, // - or +
|
|
Sign_Positive = 0x08, // positive only
|
|
Sign_Negative = 0x10, // negative only
|
|
Sign_NoIdea = 0x18 // where sign cannot be guessed
|
|
};
|
|
enum ImmedConstraint_Oneness
|
|
{
|
|
OnenessMask = 0x60,
|
|
Oneness_Any = 0x00,
|
|
Oneness_One = 0x20, // +1 or -1
|
|
Oneness_NotOne = 0x40 // anything but +1 or -1
|
|
};
|
|
enum ImmedConstraint_Constness
|
|
{
|
|
ConstnessMask = 0x80,
|
|
Constness_Any = 0x00,
|
|
Constness_Const = 0x80
|
|
};
|
|
|
|
/* The param_opcode field of the ParamSpec has the following
|
|
* possible values (from enum SpecialOpcode):
|
|
* NumConstant:
|
|
* this describes a specific constant value (constvalue)
|
|
* that must be matched / synthesized.
|
|
* ParamHolder:
|
|
* this describes any node
|
|
* that must be matched / synthesized.
|
|
* "index" is the ID of the NamedHolder:
|
|
* In matching, all NamedHolders having the same ID
|
|
* must match the identical node.
|
|
* In synthesizing, the node matched by
|
|
* a NamedHolder with this ID must be synthesized.
|
|
* SubFunction:
|
|
* this describes a subtree
|
|
* that must be matched / synthesized.
|
|
* The subtree is described in subfunc_opcode,param_begin..+param_count.
|
|
* If the type is GroupFunction, the tree is expected
|
|
* to yield a constant value which is tested.
|
|
*/
|
|
enum SpecialOpcode
|
|
{
|
|
NumConstant, // Holds a particular value (syntax-time constant)
|
|
ParamHolder, // Holds a particular named param
|
|
SubFunction // Holds an opcode and the params
|
|
};
|
|
|
|
enum ParamMatchingType
|
|
{
|
|
PositionalParams, // this set of params in this order
|
|
SelectedParams, // this set of params in any order
|
|
AnyParams, // these params are included
|
|
GroupFunction // this function represents a constant value
|
|
};
|
|
|
|
enum RuleType
|
|
{
|
|
ProduceNewTree, // replace self with the first (and only) from replaced_param
|
|
ReplaceParams // replace indicate params with replaced_params
|
|
};
|
|
|
|
#ifdef __GNUC__
|
|
# define PACKED_GRAMMAR_ATTRIBUTE __attribute__((packed))
|
|
#else
|
|
# define PACKED_GRAMMAR_ATTRIBUTE
|
|
#endif
|
|
|
|
enum { PARAM_INDEX_BITS = 9 };
|
|
|
|
/* A ParamSpec object describes
|
|
* either a parameter (leaf, node) that must be matched,
|
|
* or a parameter (leaf, node) that must be synthesized.
|
|
*/
|
|
typedef std::pair<SpecialOpcode, const void*> ParamSpec;
|
|
ParamSpec ParamSpec_Extract(unsigned paramlist, unsigned index);
|
|
bool ParamSpec_Compare(const void* a, const void* b, SpecialOpcode type);
|
|
unsigned ParamSpec_GetDepCode(const ParamSpec& b);
|
|
|
|
struct ParamSpec_ParamHolder
|
|
{
|
|
unsigned index : 8; // holder ID
|
|
unsigned constraints : 8; // constraints
|
|
unsigned depcode :16;
|
|
} PACKED_GRAMMAR_ATTRIBUTE;
|
|
|
|
struct ParamSpec_NumConstant
|
|
{
|
|
double constvalue; // the value
|
|
} PACKED_GRAMMAR_ATTRIBUTE;
|
|
|
|
struct ParamSpec_SubFunctionData
|
|
{
|
|
/* Expected parameters (leaves) of the tree: */
|
|
unsigned param_count : 2;
|
|
unsigned param_list : 30;
|
|
/* The opcode that the tree must have when SubFunction */
|
|
FUNCTIONPARSERTYPES::OPCODE subfunc_opcode : 8;
|
|
|
|
/* When matching, type describes the method of matching.
|
|
*
|
|
* Sample input tree: (cOr 2 3) (cOr 2 4) (cOr 3 2) (cOr 4 2 3) (cOr 2)
|
|
* Possible methods:
|
|
* PositionalParams, e.g. (cOr [2 3]): match no match no match no match no match
|
|
* The nodes described here are
|
|
* to be matched, in this order.
|
|
* SelectedParams, e.g. (cOr {2 3}): match no match match no match no match
|
|
* The nodes described here are
|
|
* to be matched, in any order.
|
|
* AnyParams, e.g. (cOr 2 3 ): match no match match match no match
|
|
* At least the nodes described here
|
|
* are to be matched, in any order.
|
|
* When synthesizing, the type is ignored.
|
|
*/
|
|
ParamMatchingType match_type : 3; /* When SubFunction */
|
|
/* Note: match_type needs 2, but we specify 3 because
|
|
* otherwise Microsoft VC++ borks things up
|
|
* as it interprets the value as signed.
|
|
*/
|
|
/* Optional restholder index for capturing the rest of parameters (0=not used)
|
|
* Only valid when match_type = AnyParams
|
|
*/
|
|
unsigned restholder_index : 5;
|
|
} PACKED_GRAMMAR_ATTRIBUTE; // size: 2+30+6+2+8=48 bits=6 bytes
|
|
|
|
struct ParamSpec_SubFunction
|
|
{
|
|
ParamSpec_SubFunctionData data;
|
|
unsigned constraints : 8; // constraints
|
|
unsigned depcode : 8;
|
|
} PACKED_GRAMMAR_ATTRIBUTE; // 8 bytes
|
|
|
|
/* Theoretical minimal sizes in each param_opcode cases:
|
|
* Assume param_opcode needs 3 bits.
|
|
* NumConstant: 3 + 64 (or 3+4 if just use index to clist[])
|
|
* ParamHolder: 3 + 7 + 2 (7 for constraints, 2 for immed index)
|
|
* SubFunction: 3 + 7 + 2 + 2 + 3*9 = 41
|
|
*/
|
|
|
|
/* A rule describes a pattern for matching
|
|
* and the method how to reconstruct the
|
|
* matched node(s) in the tree.
|
|
*/
|
|
struct Rule
|
|
{
|
|
/* If the rule matched, this field describes how to perform
|
|
* the replacement.
|
|
* When type==ProduceNewTree,
|
|
* the source tree is replaced entirely with
|
|
* the new tree described at repl_param_begin[0].
|
|
* When type==ReplaceParams,
|
|
* the matching leaves in the source tree are removed
|
|
* and new leaves are constructedfrom the trees
|
|
* described at repl_param_begin[0..repl_param_count].
|
|
* Other leaves remain intact.
|
|
*/
|
|
RuleType ruletype : 2;
|
|
bool logical_context : 1;
|
|
|
|
/* The replacement parameters (if NewTree, begin[0] represents the new tree) */
|
|
unsigned repl_param_count : 2; /* Assumed to be 1 when type == ProduceNewTree */
|
|
unsigned repl_param_list : 27;
|
|
|
|
/* The function that we must match. Always a SubFunction. */
|
|
ParamSpec_SubFunctionData match_tree;
|
|
} PACKED_GRAMMAR_ATTRIBUTE; // size: 2+1+2+27 + 48 = 80 bits = 10 bytes
|
|
|
|
/* Grammar is a set of rules for tree substitutions. */
|
|
struct Grammar
|
|
{
|
|
/* The rules of this grammar */
|
|
unsigned rule_count;
|
|
unsigned char rule_list[999]; // maximum limit...
|
|
/* Note: Changing the limit has no effect to performance of
|
|
* fparser. The limit is only actually used within grammar_parser.
|
|
* A too low limit causes a memory corruption during the parse.
|
|
* A too high limit just may cause inconvenience.
|
|
* The actual grammar items linked to fparser are optimized for size,
|
|
* and the size of the Grammar object may be considerably smaller
|
|
* than what is indicated by this prototype.
|
|
*/
|
|
};
|
|
|
|
extern "C" {
|
|
extern const Rule grammar_rules[];
|
|
#ifndef FPOPTIMIZER_MERGED_FILE
|
|
extern const Grammar grammar_optimize_round1;
|
|
extern const Grammar grammar_optimize_round2;
|
|
extern const Grammar grammar_optimize_round3;
|
|
extern const Grammar grammar_optimize_round4;
|
|
extern const Grammar grammar_optimize_shortcut_logical_evaluation;
|
|
extern const Grammar grammar_optimize_nonshortcut_logical_evaluation;
|
|
extern const Grammar grammar_optimize_ignore_if_sideeffects;
|
|
extern const Grammar grammar_optimize_abslogical;
|
|
extern const Grammar grammar_optimize_base2_expand;
|
|
#endif
|
|
}
|
|
|
|
void DumpParam(const ParamSpec& p, std::ostream& o = std::cout);
|
|
void DumpParams(unsigned paramlist, unsigned count, std::ostream& o = std::cout);
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_consts.h"
|
|
#ifndef M_PI
|
|
#define M_PI 3.1415926535897932384626433832795
|
|
#endif
|
|
|
|
#define CONSTANT_E 2.71828182845904523536 // exp(1)
|
|
#define CONSTANT_EI 0.3678794411714423215955 // exp(-1)
|
|
#define CONSTANT_2E 7.3890560989306502272304 // exp(2)
|
|
#define CONSTANT_2EI 0.135335283236612691894 // exp(-2)
|
|
#define CONSTANT_PI M_PI // atan2(0,-1)
|
|
#define CONSTANT_L10 2.30258509299404590109 // log(10)
|
|
#define CONSTANT_L2 0.69314718055994530943 // log(2)
|
|
#define CONSTANT_L10I 0.43429448190325176116 // 1/log(10)
|
|
#define CONSTANT_L2I 1.4426950408889634074 // 1/log(2)
|
|
#define CONSTANT_L10E CONSTANT_L10I // log10(e)
|
|
#define CONSTANT_L10EI CONSTANT_L10 // 1/log10(e)
|
|
#define CONSTANT_L10B 0.3010299956639811952137 // log10(2)
|
|
#define CONSTANT_L10BI 3.3219280948873623478703 // 1/log10(2)
|
|
#define CONSTANT_LB10 CONSTANT_L10BI // log2(10)
|
|
#define CONSTANT_LB10I CONSTANT_L10B // 1/log2(10)
|
|
#define CONSTANT_L2E CONSTANT_L2I // log2(e)
|
|
#define CONSTANT_L2EI CONSTANT_L2 // 1/log2(e)
|
|
#define CONSTANT_DR (180.0 / M_PI) // 180/pi
|
|
#define CONSTANT_RD (M_PI / 180.0) // pi/180
|
|
|
|
#define CONSTANT_POS_INF HUGE_VAL // positive infinity, from math.h
|
|
#define CONSTANT_NEG_INF (-HUGE_VAL) // negative infinity
|
|
#define CONSTANT_PIHALF (M_PI / 2)
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_optimize.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
#include <vector>
|
|
#include <utility>
|
|
#include <iostream>
|
|
|
|
//#define DEBUG_SUBSTITUTIONS
|
|
|
|
namespace FPoptimizer_Optimize
|
|
{
|
|
using namespace FPoptimizer_Grammar;
|
|
using namespace FPoptimizer_CodeTree;
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
|
|
/* This struct collects information regarding the matching process so far */
|
|
class MatchInfo
|
|
{
|
|
public:
|
|
std::vector<std::pair<bool,std::vector<CodeTree>
|
|
> > restholder_matches;
|
|
std::vector<CodeTree> paramholder_matches;
|
|
std::vector<unsigned> matched_params;
|
|
public:
|
|
/* These functions save data from matching */
|
|
bool SaveOrTestRestHolder(
|
|
unsigned restholder_index,
|
|
const std::vector<CodeTree>& treelist)
|
|
{
|
|
if(restholder_matches.size() <= restholder_index)
|
|
{
|
|
restholder_matches.resize(restholder_index+1);
|
|
restholder_matches[restholder_index].first = true;
|
|
restholder_matches[restholder_index].second = treelist;
|
|
return true;
|
|
}
|
|
if(restholder_matches[restholder_index].first == false)
|
|
{
|
|
restholder_matches[restholder_index].first = true;
|
|
restholder_matches[restholder_index].second = treelist;
|
|
return true;
|
|
}
|
|
const std::vector<CodeTree>& found =
|
|
restholder_matches[restholder_index].second;
|
|
if(treelist.size() != found.size())
|
|
return false;
|
|
for(size_t a=0; a<treelist.size(); ++a)
|
|
if(!treelist[a].IsIdenticalTo(found[a]))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void SaveRestHolder(
|
|
unsigned restholder_index,
|
|
std::vector<CodeTree>& treelist)
|
|
{
|
|
if(restholder_matches.size() <= restholder_index)
|
|
restholder_matches.resize(restholder_index+1);
|
|
restholder_matches[restholder_index].first = true;
|
|
restholder_matches[restholder_index].second.swap(treelist);
|
|
}
|
|
|
|
bool SaveOrTestParamHolder(
|
|
unsigned paramholder_index,
|
|
const CodeTree& treeptr)
|
|
{
|
|
if(paramholder_matches.size() <= paramholder_index)
|
|
{
|
|
paramholder_matches.reserve(paramholder_index+1);
|
|
paramholder_matches.resize(paramholder_index);
|
|
paramholder_matches.push_back(treeptr);
|
|
return true;
|
|
}
|
|
if(!paramholder_matches[paramholder_index].IsDefined())
|
|
{
|
|
paramholder_matches[paramholder_index] = treeptr;
|
|
return true;
|
|
}
|
|
return treeptr.IsIdenticalTo(paramholder_matches[paramholder_index]);
|
|
}
|
|
|
|
void SaveMatchedParamIndex(unsigned index)
|
|
{
|
|
matched_params.push_back(index);
|
|
}
|
|
|
|
/* These functions retrieve the data from matching
|
|
* for use when synthesizing the resulting tree.
|
|
*/
|
|
const CodeTree& GetParamHolderValueIfFound( unsigned paramholder_index ) const
|
|
{
|
|
static const CodeTree dummytree;
|
|
if(paramholder_matches.size() <= paramholder_index)
|
|
return dummytree;
|
|
return paramholder_matches[paramholder_index];
|
|
}
|
|
|
|
const CodeTree& GetParamHolderValue( unsigned paramholder_index ) const
|
|
{ return paramholder_matches[paramholder_index]; }
|
|
|
|
bool HasRestHolder(unsigned restholder_index) const
|
|
{ return restholder_matches.size() > restholder_index
|
|
&& restholder_matches[restholder_index].first == true; }
|
|
|
|
const std::vector<CodeTree>& GetRestHolderValues( unsigned restholder_index ) const
|
|
{
|
|
static const std::vector<CodeTree> empty_result;
|
|
if(restholder_matches.size() <= restholder_index)
|
|
return empty_result;
|
|
return restholder_matches[restholder_index].second;
|
|
}
|
|
|
|
const std::vector<unsigned>& GetMatchedParamIndexes() const
|
|
{ return matched_params; }
|
|
|
|
/* */
|
|
void swap(MatchInfo& b)
|
|
{
|
|
restholder_matches.swap(b.restholder_matches);
|
|
paramholder_matches.swap(b.paramholder_matches);
|
|
matched_params.swap(b.matched_params);
|
|
}
|
|
MatchInfo& operator=(const MatchInfo& b)
|
|
{
|
|
restholder_matches = b.restholder_matches;
|
|
paramholder_matches = b.paramholder_matches;
|
|
matched_params = b.matched_params;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
class MatchPositionSpecBase;
|
|
|
|
// For iterating through match candidates
|
|
typedef FPOPT_autoptr<MatchPositionSpecBase> MatchPositionSpecBaseP;
|
|
|
|
class MatchPositionSpecBase
|
|
{
|
|
public:
|
|
int RefCount;
|
|
public:
|
|
MatchPositionSpecBase() : RefCount(0) { }
|
|
virtual ~MatchPositionSpecBase() { }
|
|
};
|
|
struct MatchResultType
|
|
{
|
|
bool found;
|
|
MatchPositionSpecBaseP specs;
|
|
|
|
MatchResultType(bool f) : found(f), specs() { }
|
|
MatchResultType(bool f,
|
|
const MatchPositionSpecBaseP& s) : found(f), specs(s) { }
|
|
};
|
|
|
|
/* Synthesize the given grammatic rule's replacement into the codetree */
|
|
void SynthesizeRule(
|
|
const Rule& rule,
|
|
CodeTree& tree,
|
|
MatchInfo& info);
|
|
|
|
/* Test the given parameter to a given CodeTree */
|
|
MatchResultType TestParam(
|
|
const ParamSpec& parampair,
|
|
const CodeTree& tree,
|
|
const MatchPositionSpecBaseP& start_at,
|
|
MatchInfo& info);
|
|
|
|
/* Test the list of parameters to a given CodeTree */
|
|
MatchResultType TestParams(
|
|
const ParamSpec_SubFunctionData& model_tree,
|
|
const CodeTree& tree,
|
|
const MatchPositionSpecBaseP& start_at,
|
|
MatchInfo& info,
|
|
bool TopLevel);
|
|
|
|
bool ApplyGrammar(const Grammar& grammar,
|
|
FPoptimizer_CodeTree::CodeTree& tree,
|
|
bool from_logical_context = false);
|
|
void ApplyGrammars(FPoptimizer_CodeTree::CodeTree& tree);
|
|
|
|
bool IsLogisticallyPlausibleParamsMatch(
|
|
const ParamSpec_SubFunctionData& params,
|
|
const CodeTree& tree);
|
|
}
|
|
|
|
namespace FPoptimizer_Grammar
|
|
{
|
|
void DumpMatch(const Rule& rule,
|
|
const FPoptimizer_CodeTree::CodeTree& tree,
|
|
const FPoptimizer_Optimize::MatchInfo& info,
|
|
bool DidMatch,
|
|
std::ostream& o = std::cout);
|
|
void DumpMatch(const Rule& rule,
|
|
const FPoptimizer_CodeTree::CodeTree& tree,
|
|
const FPoptimizer_Optimize::MatchInfo& info,
|
|
const char* whydump,
|
|
std::ostream& o = std::cout);
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/crc32.h"
|
|
/* crc32 */
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
typedef unsigned int crc32_t;
|
|
|
|
#else
|
|
|
|
#include <stdint.h>
|
|
typedef uint_least32_t crc32_t;
|
|
|
|
#endif
|
|
|
|
namespace crc32
|
|
{
|
|
enum { startvalue = 0xFFFFFFFFUL, poly = 0xEDB88320UL };
|
|
|
|
/* This code constructs the CRC32 table at compile-time,
|
|
* avoiding the need for a huge explicitly written table of magical numbers. */
|
|
template<crc32_t crc> // One byte of a CRC32 (eight bits):
|
|
struct b8
|
|
{
|
|
enum { b1 = (crc & 1) ? (poly ^ (crc >> 1)) : (crc >> 1),
|
|
b2 = (b1 & 1) ? (poly ^ (b1 >> 1)) : (b1 >> 1),
|
|
b3 = (b2 & 1) ? (poly ^ (b2 >> 1)) : (b2 >> 1),
|
|
b4 = (b3 & 1) ? (poly ^ (b3 >> 1)) : (b3 >> 1),
|
|
b5 = (b4 & 1) ? (poly ^ (b4 >> 1)) : (b4 >> 1),
|
|
b6 = (b5 & 1) ? (poly ^ (b5 >> 1)) : (b5 >> 1),
|
|
b7 = (b6 & 1) ? (poly ^ (b6 >> 1)) : (b6 >> 1),
|
|
res= (b7 & 1) ? (poly ^ (b7 >> 1)) : (b7 >> 1) };
|
|
};
|
|
inline crc32_t update(crc32_t crc, unsigned/* char */b) // __attribute__((pure))
|
|
{
|
|
// Four values of the table
|
|
#define B4(n) b8<n>::res,b8<n+1>::res,b8<n+2>::res,b8<n+3>::res
|
|
// Sixteen values of the table
|
|
#define R(n) B4(n),B4(n+4),B4(n+8),B4(n+12)
|
|
// The whole table, index by steps of 16
|
|
static const crc32_t table[256] =
|
|
{ R(0x00),R(0x10),R(0x20),R(0x30), R(0x40),R(0x50),R(0x60),R(0x70),
|
|
R(0x80),R(0x90),R(0xA0),R(0xB0), R(0xC0),R(0xD0),R(0xE0),R(0xF0) };
|
|
#undef R
|
|
#undef B4
|
|
return ((crc >> 8) /* & 0x00FFFFFF*/) ^ table[/*(unsigned char)*/(crc^b)&0xFF];
|
|
}
|
|
inline crc32_t calc_upd(crc32_t c, const unsigned char* buf, size_t size)
|
|
{
|
|
crc32_t value = c;
|
|
for(size_t p=0; p<size; ++p) value = update(value, buf[p]);
|
|
return value;
|
|
}
|
|
inline crc32_t calc(const unsigned char* buf, size_t size)
|
|
{
|
|
return calc_upd(startvalue, buf, size);
|
|
}
|
|
}
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_opcodename.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
#include <string>
|
|
|
|
const std::string FP_GetOpcodeName(FPoptimizer_Grammar::SpecialOpcode opcode, bool pad=false);
|
|
const std::string FP_GetOpcodeName(FUNCTIONPARSERTYPES::OPCODE opcode, bool pad=false);
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_opcodename.c"
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <assert.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include "fpconfig.h"
|
|
#include "fptypes.h"
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_opcodename.h"
|
|
|
|
using namespace FPoptimizer_Grammar;
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
|
|
const std::string FP_GetOpcodeName(FPoptimizer_Grammar::SpecialOpcode opcode, bool pad)
|
|
{
|
|
#if 1
|
|
/* Symbolic meanings for the opcodes? */
|
|
const char* p = 0;
|
|
switch( opcode )
|
|
{
|
|
case NumConstant: p = "NumConstant"; break;
|
|
case ParamHolder: p = "ParamHolder"; break;
|
|
case SubFunction: p = "SubFunction"; break;
|
|
}
|
|
std::ostringstream tmp;
|
|
//if(!p) std::cerr << "o=" << opcode << "\n";
|
|
assert(p);
|
|
tmp << p;
|
|
if(pad) while(tmp.str().size() < 12) tmp << ' ';
|
|
return tmp.str();
|
|
#else
|
|
/* Just numeric meanings */
|
|
std::ostringstream tmp;
|
|
tmp << opcode;
|
|
if(pad) while(tmp.str().size() < 5) tmp << ' ';
|
|
return tmp.str();
|
|
#endif
|
|
}
|
|
|
|
const std::string FP_GetOpcodeName(FUNCTIONPARSERTYPES::OPCODE opcode, bool pad)
|
|
{
|
|
#if 1
|
|
/* Symbolic meanings for the opcodes? */
|
|
const char* p = 0;
|
|
switch(opcode)
|
|
{
|
|
case cAbs: p = "cAbs"; break;
|
|
case cAcos: p = "cAcos"; break;
|
|
case cAcosh: p = "cAcosh"; break;
|
|
case cAsin: p = "cAsin"; break;
|
|
case cAsinh: p = "cAsinh"; break;
|
|
case cAtan: p = "cAtan"; break;
|
|
case cAtan2: p = "cAtan2"; break;
|
|
case cAtanh: p = "cAtanh"; break;
|
|
case cCbrt: p = "cCbrt"; break;
|
|
case cCeil: p = "cCeil"; break;
|
|
case cCos: p = "cCos"; break;
|
|
case cCosh: p = "cCosh"; break;
|
|
case cCot: p = "cCot"; break;
|
|
case cCsc: p = "cCsc"; break;
|
|
case cEval: p = "cEval"; break;
|
|
case cExp: p = "cExp"; break;
|
|
case cExp2: p = "cExp2"; break;
|
|
case cFloor: p = "cFloor"; break;
|
|
case cIf: p = "cIf"; break;
|
|
case cInt: p = "cInt"; break;
|
|
case cLog: p = "cLog"; break;
|
|
case cLog2: p = "cLog2"; break;
|
|
case cLog10: p = "cLog10"; break;
|
|
case cMax: p = "cMax"; break;
|
|
case cMin: p = "cMin"; break;
|
|
case cPow: p = "cPow"; break;
|
|
case cSec: p = "cSec"; break;
|
|
case cSin: p = "cSin"; break;
|
|
case cSinh: p = "cSinh"; break;
|
|
case cSqrt: p = "cSqrt"; break;
|
|
case cTan: p = "cTan"; break;
|
|
case cTanh: p = "cTanh"; break;
|
|
case cTrunc: p = "cTrunc"; break;
|
|
case cImmed: p = "cImmed"; break;
|
|
case cJump: p = "cJump"; break;
|
|
case cNeg: p = "cNeg"; break;
|
|
case cAdd: p = "cAdd"; break;
|
|
case cSub: p = "cSub"; break;
|
|
case cMul: p = "cMul"; break;
|
|
case cDiv: p = "cDiv"; break;
|
|
case cMod: p = "cMod"; break;
|
|
case cEqual: p = "cEqual"; break;
|
|
case cNEqual: p = "cNEqual"; break;
|
|
case cLess: p = "cLess"; break;
|
|
case cLessOrEq: p = "cLessOrEq"; break;
|
|
case cGreater: p = "cGreater"; break;
|
|
case cGreaterOrEq: p = "cGreaterOrEq"; break;
|
|
case cNot: p = "cNot"; break;
|
|
case cAnd: p = "cAnd"; break;
|
|
case cOr: p = "cOr"; break;
|
|
case cDeg: p = "cDeg"; break;
|
|
case cRad: p = "cRad"; break;
|
|
case cFCall: p = "cFCall"; break;
|
|
case cPCall: p = "cPCall"; break;
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
case cFetch: p = "cFetch"; break;
|
|
case cPopNMov: p = "cPopNMov"; break;
|
|
case cLog2by: p = "cLog2by"; break;
|
|
#endif
|
|
case cAbsNot: p = "cAbsNot"; break;
|
|
case cAbsNotNot: p = "cAbsNotNot"; break;
|
|
case cAbsAnd: p = "cAbsAnd"; break;
|
|
case cAbsOr: p = "cAbsOr"; break;
|
|
case cAbsIf: p = "cAbsIf"; break;
|
|
case cDup: p = "cDup"; break;
|
|
case cInv: p = "cInv"; break;
|
|
case cSqr: p = "cSqr"; break;
|
|
case cRDiv: p = "cRDiv"; break;
|
|
case cRSub: p = "cRSub"; break;
|
|
case cNotNot: p = "cNotNot"; break;
|
|
case cRSqrt: p = "cRSqrt"; break;
|
|
case cNop: p = "cNop"; break;
|
|
case VarBegin: p = "VarBegin"; break;
|
|
}
|
|
std::ostringstream tmp;
|
|
//if(!p) std::cerr << "o=" << opcode << "\n";
|
|
assert(p);
|
|
tmp << p;
|
|
if(pad) while(tmp.str().size() < 12) tmp << ' ';
|
|
return tmp.str();
|
|
#else
|
|
/* Just numeric meanings */
|
|
std::ostringstream tmp;
|
|
tmp << opcode;
|
|
if(pad) while(tmp.str().size() < 5) tmp << ' ';
|
|
return tmp.str();
|
|
#endif
|
|
}
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_bytecodesynth.h"
|
|
#include "fpconfig.h"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
#include <vector>
|
|
#include <utility>
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
|
|
#ifndef FP_GENERATING_POWI_TABLE
|
|
enum { MAX_POWI_BYTECODE_LENGTH = 20 };
|
|
#else
|
|
enum { MAX_POWI_BYTECODE_LENGTH = 999 };
|
|
#endif
|
|
enum { MAX_MULI_BYTECODE_LENGTH = 3 };
|
|
|
|
namespace FPoptimizer_ByteCode
|
|
{
|
|
class ByteCodeSynth
|
|
{
|
|
public:
|
|
ByteCodeSynth()
|
|
: ByteCode(), Immed(), StackTop(0), StackMax(0)
|
|
{
|
|
/* estimate the initial requirements as such */
|
|
ByteCode.reserve(64);
|
|
Immed.reserve(8);
|
|
StackState.reserve(16);
|
|
}
|
|
|
|
void Pull(std::vector<unsigned>& bc,
|
|
std::vector<double>& imm,
|
|
size_t& StackTop_max)
|
|
{
|
|
ByteCode.swap(bc);
|
|
Immed.swap(imm);
|
|
StackTop_max = StackMax;
|
|
}
|
|
|
|
size_t GetByteCodeSize() const { return ByteCode.size(); }
|
|
size_t GetStackTop() const { return StackTop; }
|
|
|
|
void PushVar(unsigned varno)
|
|
{
|
|
ByteCode.push_back(varno);
|
|
SetStackTop(StackTop+1);
|
|
}
|
|
|
|
void PushImmed(double immed)
|
|
{
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
ByteCode.push_back(cImmed);
|
|
Immed.push_back(immed);
|
|
SetStackTop(StackTop+1);
|
|
}
|
|
|
|
void StackTopIs(const FPoptimizer_CodeTree::CodeTree& tree)
|
|
{
|
|
if(StackTop > 0)
|
|
{
|
|
StackState[StackTop-1].first = true;
|
|
StackState[StackTop-1].second = tree;
|
|
}
|
|
}
|
|
|
|
void EatNParams(unsigned eat_count)
|
|
{
|
|
SetStackTop(StackTop - eat_count);
|
|
}
|
|
|
|
void ProducedNParams(unsigned produce_count)
|
|
{
|
|
SetStackTop(StackTop + produce_count);
|
|
}
|
|
|
|
void AddOperation(unsigned opcode, unsigned eat_count, unsigned produce_count = 1)
|
|
{
|
|
EatNParams(eat_count);
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
|
|
if(!ByteCode.empty() && opcode == cMul && ByteCode.back() == cDup)
|
|
ByteCode.back() = cSqr;
|
|
else
|
|
ByteCode.push_back(opcode);
|
|
|
|
ProducedNParams(produce_count);
|
|
}
|
|
|
|
void DoPopNMov(size_t targetpos, size_t srcpos)
|
|
{
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
ByteCode.push_back(cPopNMov);
|
|
ByteCode.push_back( (unsigned) targetpos);
|
|
ByteCode.push_back( (unsigned) srcpos);
|
|
|
|
SetStackTop(srcpos+1);
|
|
StackState[targetpos] = StackState[srcpos];
|
|
SetStackTop(targetpos+1);
|
|
}
|
|
|
|
void DoDup(size_t src_pos)
|
|
{
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
if(src_pos == StackTop-1)
|
|
{
|
|
ByteCode.push_back(cDup);
|
|
}
|
|
else
|
|
{
|
|
ByteCode.push_back(cFetch);
|
|
ByteCode.push_back( (unsigned) src_pos);
|
|
}
|
|
SetStackTop(StackTop + 1);
|
|
StackState[StackTop-1] = StackState[src_pos];
|
|
}
|
|
|
|
size_t FindPos(const FPoptimizer_CodeTree::CodeTree& tree) const
|
|
{
|
|
/*std::cout << "Stack state now(" << StackTop << "):\n";
|
|
for(size_t a=0; a<StackTop; ++a)
|
|
{
|
|
std::cout << a << ": ";
|
|
if(StackState[a].first)
|
|
DumpTree(StackState[a].second);
|
|
else
|
|
std::cout << "?";
|
|
std::cout << "\n";
|
|
}*/
|
|
for(size_t a=StackTop; a-->0; )
|
|
if(StackState[a].first && StackState[a].second.IsIdenticalTo(tree))
|
|
return a;
|
|
return ~size_t(0);
|
|
}
|
|
|
|
bool Find(const FPoptimizer_CodeTree::CodeTree& tree) const
|
|
{
|
|
return FindPos(tree) != ~size_t(0);
|
|
}
|
|
|
|
bool FindAndDup(const FPoptimizer_CodeTree::CodeTree& tree)
|
|
{
|
|
size_t pos = FindPos(tree);
|
|
if(pos != ~size_t(0))
|
|
{
|
|
DoDup(pos);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct IfData
|
|
{
|
|
size_t ofs;
|
|
};
|
|
|
|
void SynthIfStep1(IfData& ifdata, FUNCTIONPARSERTYPES::OPCODE op)
|
|
{
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
SetStackTop(StackTop-1); // the If condition was popped.
|
|
|
|
ifdata.ofs = ByteCode.size();
|
|
ByteCode.push_back(op);
|
|
ByteCode.push_back(0); // code index
|
|
ByteCode.push_back(0); // Immed index
|
|
}
|
|
void SynthIfStep2(IfData& ifdata)
|
|
{
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
SetStackTop(StackTop-1); // ignore the pushed then-branch result.
|
|
|
|
ByteCode[ifdata.ofs+1] = unsigned( ByteCode.size()+2 );
|
|
ByteCode[ifdata.ofs+2] = unsigned( Immed.size() );
|
|
|
|
ifdata.ofs = ByteCode.size();
|
|
ByteCode.push_back(cJump);
|
|
ByteCode.push_back(0); // code index
|
|
ByteCode.push_back(0); // Immed index
|
|
}
|
|
void SynthIfStep3(IfData& ifdata)
|
|
{
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
SetStackTop(StackTop-1); // ignore the pushed else-branch result.
|
|
|
|
ByteCode[ifdata.ofs+1] = unsigned( ByteCode.size()-1 );
|
|
ByteCode[ifdata.ofs+2] = unsigned( Immed.size() );
|
|
|
|
SetStackTop(StackTop+1); // one or the other was pushed.
|
|
|
|
/* Threading jumps:
|
|
* If there are any cJumps that point
|
|
* to the cJump instruction we just changed,
|
|
* change them to point to this target as well.
|
|
* This screws up PrintByteCode() majorly.
|
|
*/
|
|
for(size_t a=0; a<ifdata.ofs; ++a)
|
|
{
|
|
if(ByteCode[a] == cJump
|
|
&& ByteCode[a+1] == ifdata.ofs-1)
|
|
{
|
|
ByteCode[a+1] = unsigned( ByteCode.size()-1 );
|
|
ByteCode[a+2] = unsigned( Immed.size() );
|
|
}
|
|
switch(ByteCode[a])
|
|
{
|
|
case cAbsIf:
|
|
case cIf:
|
|
case cJump:
|
|
case cPopNMov: a += 2; break;
|
|
case cFCall:
|
|
case cPCall:
|
|
case cFetch: a += 1; break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
void SetStackTop(size_t value)
|
|
{
|
|
StackTop = value;
|
|
if(StackTop > StackMax)
|
|
{
|
|
StackMax = StackTop;
|
|
StackState.resize(StackMax);
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::vector<unsigned> ByteCode;
|
|
std::vector<double> Immed;
|
|
|
|
std::vector<
|
|
std::pair<bool/*known*/, FPoptimizer_CodeTree::CodeTree/*tree*/>
|
|
> StackState;
|
|
size_t StackTop;
|
|
size_t StackMax;
|
|
};
|
|
|
|
struct SequenceOpCode;
|
|
extern const SequenceOpCode AddSequence; /* Multiplication implemented with adds */
|
|
extern const SequenceOpCode MulSequence; /* Exponentiation implemented with muls */
|
|
|
|
/* Generate a sequence that multiplies or exponentifies the
|
|
* last operand in the stack by the given constant integer
|
|
* amount (positive or negative).
|
|
*/
|
|
void AssembleSequence(
|
|
long count,
|
|
const SequenceOpCode& sequencing,
|
|
ByteCodeSynth& synth);
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_bytecodesynth.c"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_bytecodesynth.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
|
|
namespace FPoptimizer_ByteCode
|
|
{
|
|
const struct SequenceOpCode
|
|
{
|
|
double basevalue;
|
|
unsigned op_flip;
|
|
unsigned op_normal, op_normal_flip;
|
|
unsigned op_inverse, op_inverse_flip;
|
|
} AddSequence = {0.0, cNeg, cAdd, cAdd, cSub, cRSub },
|
|
MulSequence = {1.0, cInv, cMul, cMul, cDiv, cRDiv };
|
|
}
|
|
|
|
using namespace FPoptimizer_ByteCode;
|
|
|
|
#define POWI_TABLE_SIZE 256
|
|
#define POWI_WINDOW_SIZE 3
|
|
namespace FPoptimizer_ByteCode
|
|
{
|
|
#ifndef FP_GENERATING_POWI_TABLE
|
|
extern const
|
|
unsigned char powi_table[POWI_TABLE_SIZE];
|
|
const
|
|
#endif
|
|
unsigned char powi_table[POWI_TABLE_SIZE] =
|
|
{
|
|
0, 1, 1, 1, 2, 1, 2, 1, /* 0 - 7 */
|
|
4, 1, 2, 1, 4, 1, 2, 131, /* 8 - 15 */
|
|
8, 1, 2, 1, 4, 1, 2, 1, /* 16 - 23 */
|
|
8, 133, 2, 131, 4, 1, 15, 1, /* 24 - 31 */
|
|
16, 1, 2, 1, 4, 1, 2, 131, /* 32 - 39 */
|
|
8, 1, 2, 1, 4, 133, 2, 1, /* 40 - 47 */
|
|
16, 1, 25, 131, 4, 1, 27, 5, /* 48 - 55 */
|
|
8, 3, 2, 1, 30, 1, 31, 3, /* 56 - 63 */
|
|
32, 1, 2, 1, 4, 1, 2, 1, /* 64 - 71 */
|
|
8, 1, 2, 131, 4, 1, 39, 1, /* 72 - 79 */
|
|
16, 137, 2, 1, 4, 133, 2, 131, /* 80 - 87 */
|
|
8, 1, 45, 135, 4, 31, 2, 5, /* 88 - 95 */
|
|
32, 1, 2, 131, 50, 1, 51, 1, /* 96 - 103 */
|
|
8, 3, 2, 1, 54, 1, 55, 3, /* 104 - 111 */
|
|
16, 1, 57, 133, 4, 137, 2, 135, /* 112 - 119 */
|
|
60, 1, 61, 3, 62, 133, 63, 1, /* 120 - 127 */
|
|
130, 1, 2, 1, 130, 1, 2, 131, /* 128 - 135 */
|
|
130, 1, 2, 1, 130, 1, 2, 139, /* 136 - 143 */
|
|
130, 1, 2, 131, 130, 1, 30, 1, /* 144 - 151 */
|
|
130, 137, 2, 31, 130, 1, 2, 131, /* 152 - 159 */
|
|
130, 1, 130, 1, 130, 133, 2, 1, /* 160 - 167 */
|
|
130, 1, 130, 1, 2, 1, 130, 133, /* 168 - 175 */
|
|
130, 1, 2, 1, 130, 1, 2, 61, /* 176 - 183 */
|
|
130, 133, 62, 139, 130, 137, 130, 1, /* 184 - 191 */
|
|
130, 1, 2, 131, 130, 1, 130, 1, /* 192 - 199 */
|
|
130, 1, 2, 1, 130, 1, 2, 131, /* 200 - 207 */
|
|
130, 1, 130, 1, 130, 131, 2, 133, /* 208 - 215 */
|
|
130, 1, 2, 131, 130, 141, 130, 1, /* 216 - 223 */
|
|
130, 133, 2, 1, 130, 1, 5, 135, /* 224 - 231 */
|
|
130, 1, 130, 1, 2, 131, 130, 1, /* 232 - 239 */
|
|
130, 1, 2, 131, 130, 133, 130, 141, /* 240 - 247 */
|
|
130, 131, 130, 1, 130, 1, 2, 131 /* 248 - 255 */
|
|
}; /* as in gcc, but custom-optimized for stack calculation */
|
|
}
|
|
static const int POWI_CACHE_SIZE = 256;
|
|
|
|
#define FPO(x) /**/
|
|
//#define FPO(x) x
|
|
|
|
|
|
namespace
|
|
{
|
|
class PowiCache
|
|
{
|
|
private:
|
|
int cache[POWI_CACHE_SIZE];
|
|
int cache_needed[POWI_CACHE_SIZE];
|
|
|
|
public:
|
|
PowiCache()
|
|
: cache(), cache_needed() /* Assume we have no factors in the cache */
|
|
{
|
|
/* Decide which factors we would need multiple times.
|
|
* Output:
|
|
* cache[] = these factors were generated
|
|
* cache_needed[] = number of times these factors were desired
|
|
*/
|
|
cache[1] = 1; // We have this value already.
|
|
}
|
|
|
|
bool Plan_Add(long value, int count)
|
|
{
|
|
if(value >= POWI_CACHE_SIZE) return false;
|
|
//FPO(fprintf(stderr, "%ld will be needed %d times more\n", count, need_count));
|
|
cache_needed[value] += count;
|
|
return cache[value] != 0;
|
|
}
|
|
|
|
void Plan_Has(long value)
|
|
{
|
|
if(value < POWI_CACHE_SIZE)
|
|
cache[value] = 1; // This value has been generated
|
|
}
|
|
|
|
void Start(size_t value1_pos)
|
|
{
|
|
for(int n=2; n<POWI_CACHE_SIZE; ++n)
|
|
cache[n] = -1; /* Stack location for each component */
|
|
|
|
Remember(1, value1_pos);
|
|
|
|
DumpContents();
|
|
}
|
|
|
|
int Find(long value) const
|
|
{
|
|
if(value < POWI_CACHE_SIZE)
|
|
{
|
|
if(cache[value] >= 0)
|
|
{
|
|
// found from the cache
|
|
FPO(fprintf(stderr, "* I found %ld from cache (%u,%d)\n",
|
|
value, (unsigned)cache[value], cache_needed[value]));
|
|
return cache[value];
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Remember(long value, size_t stackpos)
|
|
{
|
|
if(value >= POWI_CACHE_SIZE) return;
|
|
|
|
FPO(fprintf(stderr, "* Remembering that %ld can be found at %u (%d uses remain)\n",
|
|
value, (unsigned)stackpos, cache_needed[value]));
|
|
cache[value] = (int) stackpos;
|
|
}
|
|
|
|
void DumpContents() const
|
|
{
|
|
FPO(for(int a=1; a<POWI_CACHE_SIZE; ++a)
|
|
if(cache[a] >= 0 || cache_needed[a] > 0)
|
|
{
|
|
fprintf(stderr, "== cache: sp=%d, val=%d, needs=%d\n",
|
|
cache[a], a, cache_needed[a]);
|
|
})
|
|
}
|
|
|
|
int UseGetNeeded(long value)
|
|
{
|
|
if(value >= 0 && value < POWI_CACHE_SIZE)
|
|
return --cache_needed[value];
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
|
|
size_t AssembleSequence_Subdivide(
|
|
long count,
|
|
PowiCache& cache,
|
|
const SequenceOpCode& sequencing,
|
|
ByteCodeSynth& synth);
|
|
|
|
void Subdivide_Combine(
|
|
size_t apos, long aval,
|
|
size_t bpos, long bval,
|
|
PowiCache& cache,
|
|
|
|
unsigned cumulation_opcode,
|
|
unsigned cimulation_opcode_flip,
|
|
|
|
ByteCodeSynth& synth);
|
|
|
|
void PlanNtimesCache
|
|
(long value,
|
|
PowiCache& cache,
|
|
int need_count,
|
|
int recursioncount=0)
|
|
{
|
|
if(value < 1) return;
|
|
|
|
#ifdef FP_GENERATING_POWI_TABLE
|
|
if(recursioncount > 32) throw false;
|
|
#endif
|
|
|
|
if(cache.Plan_Add(value, need_count)) return;
|
|
|
|
long half = 1;
|
|
if(value < POWI_TABLE_SIZE)
|
|
{
|
|
half = powi_table[value];
|
|
if(half & 128)
|
|
{
|
|
half &= 127;
|
|
if(half & 64)
|
|
half = -(half & 63) - 1;
|
|
|
|
FPO(fprintf(stderr, "value=%ld, half=%ld, otherhalf=%ld\n", value,half,value/half));
|
|
|
|
PlanNtimesCache(half, cache, 1, recursioncount+1);
|
|
cache.Plan_Has(half);
|
|
return;
|
|
}
|
|
else if(half & 64)
|
|
{
|
|
half = -(half & 63) - 1;
|
|
}
|
|
}
|
|
else if(value & 1)
|
|
half = value & ((1 << POWI_WINDOW_SIZE) - 1); // that is, value & 7
|
|
else
|
|
half = value / 2;
|
|
|
|
long otherhalf = value-half;
|
|
if(half > otherhalf || half<0) std::swap(half,otherhalf);
|
|
|
|
FPO(fprintf(stderr, "value=%ld, half=%ld, otherhalf=%ld\n", value,half,otherhalf));
|
|
|
|
if(half == otherhalf)
|
|
{
|
|
PlanNtimesCache(half, cache, 2, recursioncount+1);
|
|
}
|
|
else
|
|
{
|
|
PlanNtimesCache(half, cache, 1, recursioncount+1);
|
|
PlanNtimesCache(otherhalf>0?otherhalf:-otherhalf,
|
|
cache, 1, recursioncount+1);
|
|
}
|
|
cache.Plan_Has(value);
|
|
}
|
|
|
|
size_t AssembleSequence_Subdivide(
|
|
long value,
|
|
PowiCache& cache,
|
|
const SequenceOpCode& sequencing,
|
|
ByteCodeSynth& synth)
|
|
{
|
|
int cachepos = cache.Find(value);
|
|
if(cachepos >= 0)
|
|
{
|
|
// found from the cache
|
|
return cachepos;
|
|
}
|
|
|
|
long half = 1;
|
|
if(value < POWI_TABLE_SIZE)
|
|
{
|
|
half = powi_table[value];
|
|
if(half & 128)
|
|
{
|
|
half &= 127;
|
|
if(half & 64)
|
|
half = -(half & 63) - 1;
|
|
|
|
FPO(fprintf(stderr, "* I want %ld, my plan is %ld * %ld\n", value, half, value/half));
|
|
size_t half_pos = AssembleSequence_Subdivide(half, cache, sequencing, synth);
|
|
if(cache.UseGetNeeded(half) > 0
|
|
|| half_pos != synth.GetStackTop()-1)
|
|
{
|
|
synth.DoDup(half_pos);
|
|
cache.Remember(half, synth.GetStackTop()-1);
|
|
}
|
|
AssembleSequence(value/half, sequencing, synth);
|
|
size_t stackpos = synth.GetStackTop()-1;
|
|
cache.Remember(value, stackpos);
|
|
cache.DumpContents();
|
|
return stackpos;
|
|
}
|
|
else if(half & 64)
|
|
{
|
|
half = -(half & 63) - 1;
|
|
}
|
|
}
|
|
else if(value & 1)
|
|
half = value & ((1 << POWI_WINDOW_SIZE) - 1); // that is, value & 7
|
|
else
|
|
half = value / 2;
|
|
|
|
long otherhalf = value-half;
|
|
if(half > otherhalf || half<0) std::swap(half,otherhalf);
|
|
|
|
FPO(fprintf(stderr, "* I want %ld, my plan is %ld + %ld\n", value, half, value-half));
|
|
|
|
if(half == otherhalf)
|
|
{
|
|
size_t half_pos = AssembleSequence_Subdivide(half, cache, sequencing, synth);
|
|
|
|
// self-cumulate the subdivide result
|
|
Subdivide_Combine(half_pos,half, half_pos,half, cache,
|
|
sequencing.op_normal, sequencing.op_normal_flip,
|
|
synth);
|
|
}
|
|
else
|
|
{
|
|
long part1 = half;
|
|
long part2 = otherhalf>0?otherhalf:-otherhalf;
|
|
|
|
size_t part1_pos = AssembleSequence_Subdivide(part1, cache, sequencing, synth);
|
|
size_t part2_pos = AssembleSequence_Subdivide(part2, cache, sequencing, synth);
|
|
|
|
FPO(fprintf(stderr, "Subdivide(%ld: %ld, %ld)\n", value, half, otherhalf));
|
|
|
|
Subdivide_Combine(part1_pos,part1, part2_pos,part2, cache,
|
|
otherhalf>0 ? sequencing.op_normal : sequencing.op_inverse,
|
|
otherhalf>0 ? sequencing.op_normal_flip : sequencing.op_inverse_flip,
|
|
synth);
|
|
}
|
|
|
|
size_t stackpos = synth.GetStackTop()-1;
|
|
cache.Remember(value, stackpos);
|
|
cache.DumpContents();
|
|
return stackpos;
|
|
}
|
|
|
|
void Subdivide_Combine(
|
|
size_t apos, long aval,
|
|
size_t bpos, long bval,
|
|
PowiCache& cache,
|
|
unsigned cumulation_opcode,
|
|
unsigned cumulation_opcode_flip,
|
|
ByteCodeSynth& synth)
|
|
{
|
|
/*FPO(fprintf(stderr, "== making result for (sp=%u, val=%d, needs=%d) and (sp=%u, val=%d, needs=%d), stacktop=%u\n",
|
|
(unsigned)apos, aval, aval>=0 ? cache_needed[aval] : -1,
|
|
(unsigned)bpos, bval, bval>=0 ? cache_needed[bval] : -1,
|
|
(unsigned)synth.GetStackTop()));*/
|
|
|
|
// Figure out whether we can trample a and b
|
|
int a_needed = cache.UseGetNeeded(aval);
|
|
int b_needed = cache.UseGetNeeded(bval);
|
|
|
|
bool flipped = false;
|
|
|
|
#define DUP_BOTH() do { \
|
|
if(apos < bpos) { size_t tmp=apos; apos=bpos; bpos=tmp; flipped=!flipped; } \
|
|
FPO(fprintf(stderr, "-> dup(%u) dup(%u) op\n", (unsigned)apos, (unsigned)bpos)); \
|
|
synth.DoDup(apos); \
|
|
synth.DoDup(apos==bpos ? synth.GetStackTop()-1 : bpos); } while(0)
|
|
#define DUP_ONE(p) do { \
|
|
FPO(fprintf(stderr, "-> dup(%u) op\n", (unsigned)p)); \
|
|
synth.DoDup(p); \
|
|
} while(0)
|
|
|
|
if(a_needed > 0)
|
|
{
|
|
if(b_needed > 0)
|
|
{
|
|
// If they must both be preserved, make duplicates
|
|
// First push the one that is at the larger stack
|
|
// address. This increases the odds of possibly using cDup.
|
|
DUP_BOTH();
|
|
|
|
//SCENARIO 1:
|
|
// Input: x B A x x
|
|
// Temp: x B A x x A B
|
|
// Output: x B A x x R
|
|
//SCENARIO 2:
|
|
// Input: x A B x x
|
|
// Temp: x A B x x B A
|
|
// Output: x A B x x R
|
|
}
|
|
else
|
|
{
|
|
// A must be preserved, but B can be trampled over
|
|
|
|
// SCENARIO 1:
|
|
// Input: x B x x A
|
|
// Temp: x B x x A A B (dup both, later first)
|
|
// Output: x B x x A R
|
|
// SCENARIO 2:
|
|
// Input: x A x x B
|
|
// Temp: x A x x B A
|
|
// Output: x A x x R -- only commutative cases
|
|
// SCENARIO 3:
|
|
// Input: x x x B A
|
|
// Temp: x x x B A A B (dup both, later first)
|
|
// Output: x x x B A R
|
|
// SCENARIO 4:
|
|
// Input: x x x A B
|
|
// Temp: x x x A B A -- only commutative cases
|
|
// Output: x x x A R
|
|
// SCENARIO 5:
|
|
// Input: x A B x x
|
|
// Temp: x A B x x A B (dup both, later first)
|
|
// Output: x A B x x R
|
|
|
|
// if B is not at the top, dup both.
|
|
if(bpos != synth.GetStackTop()-1)
|
|
DUP_BOTH(); // dup both
|
|
else
|
|
{
|
|
DUP_ONE(apos); // just dup A
|
|
flipped=!flipped;
|
|
}
|
|
}
|
|
}
|
|
else if(b_needed > 0)
|
|
{
|
|
// B must be preserved, but A can be trampled over
|
|
// This is a mirror image of the a_needed>0 case, so I'll cut the chase
|
|
if(apos != synth.GetStackTop()-1)
|
|
DUP_BOTH();
|
|
else
|
|
DUP_ONE(bpos);
|
|
}
|
|
else
|
|
{
|
|
// Both can be trampled over.
|
|
// SCENARIO 1:
|
|
// Input: x B x x A
|
|
// Temp: x B x x A B
|
|
// Output: x B x x R
|
|
// SCENARIO 2:
|
|
// Input: x A x x B
|
|
// Temp: x A x x B A
|
|
// Output: x A x x R -- only commutative cases
|
|
// SCENARIO 3:
|
|
// Input: x x x B A
|
|
// Output: x x x R -- only commutative cases
|
|
// SCENARIO 4:
|
|
// Input: x x x A B
|
|
// Output: x x x R
|
|
// SCENARIO 5:
|
|
// Input: x A B x x
|
|
// Temp: x A B x x A B (dup both, later first)
|
|
// Output: x A B x x R
|
|
// SCENARIO 6:
|
|
// Input: x x x C
|
|
// Temp: x x x C C (c is both A and B)
|
|
// Output: x x x R
|
|
|
|
if(apos == bpos && apos == synth.GetStackTop()-1)
|
|
DUP_ONE(apos); // scenario 6
|
|
else if(apos == synth.GetStackTop()-1 && bpos == synth.GetStackTop()-2)
|
|
{
|
|
FPO(fprintf(stderr, "-> op\n")); // scenario 3
|
|
flipped=!flipped;
|
|
}
|
|
else if(apos == synth.GetStackTop()-2 && bpos == synth.GetStackTop()-1)
|
|
FPO(fprintf(stderr, "-> op\n")); // scenario 4
|
|
else if(apos == synth.GetStackTop()-1)
|
|
DUP_ONE(bpos); // scenario 1
|
|
else if(bpos == synth.GetStackTop()-1)
|
|
{
|
|
DUP_ONE(apos); // scenario 2
|
|
flipped=!flipped;
|
|
}
|
|
else
|
|
DUP_BOTH(); // scenario 5
|
|
}
|
|
// Add them together.
|
|
synth.AddOperation(flipped ? cumulation_opcode_flip : cumulation_opcode, 2);
|
|
}
|
|
|
|
void LightWeight(
|
|
long count,
|
|
const SequenceOpCode& sequencing,
|
|
ByteCodeSynth& synth)
|
|
{
|
|
while(count < 256)
|
|
{
|
|
int half = FPoptimizer_ByteCode::powi_table[count];
|
|
if(half & 128)
|
|
{
|
|
half &= 127;
|
|
LightWeight(half, sequencing, synth);
|
|
count /= half;
|
|
}
|
|
else break;
|
|
}
|
|
if(count == 1) return;
|
|
if(!(count & 1))
|
|
{
|
|
synth.AddOperation(cSqr, 1);
|
|
LightWeight(count/2, sequencing, synth);
|
|
}
|
|
else
|
|
{
|
|
synth.DoDup(synth.GetStackTop()-1);
|
|
LightWeight(count-1, sequencing, synth);
|
|
synth.AddOperation(cMul, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace FPoptimizer_ByteCode
|
|
{
|
|
void AssembleSequence(
|
|
long count,
|
|
const SequenceOpCode& sequencing,
|
|
ByteCodeSynth& synth)
|
|
{
|
|
if(count == 0)
|
|
synth.PushImmed(sequencing.basevalue);
|
|
else
|
|
{
|
|
bool needs_flip = false;
|
|
if(count < 0)
|
|
{
|
|
needs_flip = true;
|
|
count = -count;
|
|
}
|
|
|
|
if(false)
|
|
LightWeight(count,sequencing,synth);
|
|
else if(count > 1)
|
|
{
|
|
/* To prevent calculating the same factors over and over again,
|
|
* we use a cache. */
|
|
PowiCache cache;
|
|
PlanNtimesCache(count, cache, 1);
|
|
|
|
size_t stacktop_desired = synth.GetStackTop();
|
|
|
|
cache.Start( synth.GetStackTop()-1 );
|
|
|
|
FPO(fprintf(stderr, "Calculating result for %ld...\n", count));
|
|
size_t res_stackpos = AssembleSequence_Subdivide(
|
|
count, cache, sequencing,
|
|
synth);
|
|
|
|
size_t n_excess = synth.GetStackTop() - stacktop_desired;
|
|
if(n_excess > 0 || res_stackpos != stacktop_desired-1)
|
|
{
|
|
// Remove the cache values
|
|
synth.DoPopNMov(stacktop_desired-1, res_stackpos);
|
|
}
|
|
}
|
|
|
|
if(needs_flip)
|
|
synth.AddOperation(sequencing.op_flip, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_codetree.c"
|
|
#include <list>
|
|
#include <algorithm>
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
#include "fptypes.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_consts.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
//using namespace FPoptimizer_Grammar;
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
CodeTree::CodeTree()
|
|
: data(new CodeTreeData)
|
|
{
|
|
data->Opcode = cNop;
|
|
}
|
|
|
|
CodeTree::CodeTree(double i)
|
|
: data(new CodeTreeData(i))
|
|
{
|
|
data->Recalculate_Hash_NoRecursion();
|
|
}
|
|
|
|
CodeTree::CodeTree(unsigned v, CodeTree::VarTag)
|
|
: data(new CodeTreeData)
|
|
{
|
|
data->Opcode = VarBegin;
|
|
data->Var = v;
|
|
data->Recalculate_Hash_NoRecursion();
|
|
}
|
|
|
|
CodeTree::CodeTree(const CodeTree& b, CodeTree::CloneTag)
|
|
: data(new CodeTreeData(*b.data))
|
|
{
|
|
}
|
|
|
|
CodeTree::~CodeTree()
|
|
{
|
|
}
|
|
|
|
struct ParamComparer
|
|
{
|
|
bool operator() (const CodeTree& a, const CodeTree& b) const
|
|
{
|
|
if(a.GetDepth() != b.GetDepth())
|
|
return a.GetDepth() > b.GetDepth();
|
|
return a.GetHash() < b.GetHash();
|
|
}
|
|
};
|
|
|
|
void CodeTreeData::Sort()
|
|
{
|
|
/* If the tree is commutative, order the parameters
|
|
* in a set order in order to make equality tests
|
|
* efficient in the optimizer
|
|
*/
|
|
switch(Opcode)
|
|
{
|
|
case cAdd:
|
|
case cMul:
|
|
case cMin:
|
|
case cMax:
|
|
case cAnd:
|
|
case cOr:
|
|
case cEqual:
|
|
case cNEqual:
|
|
std::sort(Params.begin(), Params.end(), ParamComparer());
|
|
break;
|
|
case cLess:
|
|
if(ParamComparer() (Params[1], Params[0]))
|
|
{ std::swap(Params[0], Params[1]); Opcode = cGreater; }
|
|
break;
|
|
case cLessOrEq:
|
|
if(ParamComparer() (Params[1], Params[0]))
|
|
{ std::swap(Params[0], Params[1]); Opcode = cGreaterOrEq; }
|
|
break;
|
|
case cGreater:
|
|
if(ParamComparer() (Params[1], Params[0]))
|
|
{ std::swap(Params[0], Params[1]); Opcode = cLess; }
|
|
break;
|
|
case cGreaterOrEq:
|
|
if(ParamComparer() (Params[1], Params[0]))
|
|
{ std::swap(Params[0], Params[1]); Opcode = cLessOrEq; }
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CodeTree::AddParam(const CodeTree& param)
|
|
{
|
|
//std::cout << "AddParam called\n";
|
|
data->Params.push_back(param);
|
|
}
|
|
void CodeTree::AddParamMove(CodeTree& param)
|
|
{
|
|
data->Params.push_back(CodeTree());
|
|
data->Params.back().swap(param);
|
|
}
|
|
void CodeTree::SetParam(size_t which, const CodeTree& b)
|
|
{
|
|
DataP slot_holder ( data->Params[which].data );
|
|
data->Params[which] = b;
|
|
}
|
|
void CodeTree::SetParamMove(size_t which, CodeTree& b)
|
|
{
|
|
DataP slot_holder ( data->Params[which].data );
|
|
data->Params[which].swap(b);
|
|
}
|
|
|
|
void CodeTree::AddParams(const std::vector<CodeTree>& RefParams)
|
|
{
|
|
data->Params.insert(data->Params.end(), RefParams.begin(), RefParams.end());
|
|
}
|
|
void CodeTree::AddParamsMove(std::vector<CodeTree>& RefParams)
|
|
{
|
|
size_t endpos = data->Params.size(), added = RefParams.size();
|
|
data->Params.resize(endpos + added, CodeTree());
|
|
for(size_t p=0; p<added; ++p)
|
|
data->Params[endpos+p].swap( RefParams[p] );
|
|
}
|
|
void CodeTree::AddParamsMove(std::vector<CodeTree>& RefParams, size_t replacing_slot)
|
|
{
|
|
DataP slot_holder ( data->Params[replacing_slot].data );
|
|
DelParam(replacing_slot);
|
|
AddParamsMove(RefParams);
|
|
/*
|
|
const size_t n_added = RefParams.size();
|
|
const size_t oldsize = data->Params.size();
|
|
const size_t newsize = oldsize + n_added - 1;
|
|
if(RefParams.empty())
|
|
DelParam(replacing_slot);
|
|
else
|
|
{
|
|
// 0 1 2 3 4 5 6 7 8 9 10 11
|
|
// a a a a X b b b b b
|
|
// a a a a Y Y Y b b b b b
|
|
//
|
|
// replacing_slot = 4
|
|
// n_added = 3
|
|
// oldsize = 10
|
|
// newsize = 12
|
|
// tail_length = 5
|
|
|
|
data->Params.resize(newsize);
|
|
data->Params[replacing_slot].data = 0;
|
|
const size_t tail_length = oldsize - replacing_slot -1;
|
|
for(size_t tail=0; tail<tail_length; ++tail)
|
|
data->Params[newsize-1-tail].data.UnsafeSetP(
|
|
&*data->Params[newsize-1-tail-(n_added-1)].data);
|
|
for(size_t head=1; head<n_added; ++head)
|
|
data->Params[replacing_slot+head].data.UnsafeSetP( 0 );
|
|
for(size_t p=0; p<n_added; ++p)
|
|
data->Params[replacing_slot+p].swap( RefParams[p] );
|
|
}
|
|
*/
|
|
}
|
|
|
|
void CodeTree::SetParams(const std::vector<CodeTree>& RefParams)
|
|
{
|
|
//std::cout << "SetParams called" << (do_clone ? ", clone" : ", no clone") << "\n";
|
|
std::vector<CodeTree> tmp(RefParams);
|
|
data->Params.swap(tmp);
|
|
}
|
|
|
|
void CodeTree::SetParamsMove(std::vector<CodeTree>& RefParams)
|
|
{
|
|
data->Params.swap(RefParams);
|
|
RefParams.clear();
|
|
}
|
|
|
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
|
void CodeTree::SetParams(std::vector<CodeTree>&& RefParams)
|
|
{
|
|
//std::cout << "SetParams&& called\n";
|
|
SetParamsMove(RefParams);
|
|
}
|
|
#endif
|
|
|
|
void CodeTree::DelParam(size_t index)
|
|
{
|
|
std::vector<CodeTree>& Params = data->Params;
|
|
//std::cout << "DelParam(" << index << ") called\n";
|
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
|
/* rvalue reference semantics makes this optimal */
|
|
Params.erase( Params.begin() + index );
|
|
#else
|
|
/* This labor evades the need for refcount +1/-1 shuffling */
|
|
Params[index].data = 0;
|
|
for(size_t p=index; p+1<Params.size(); ++p)
|
|
Params[p].data.UnsafeSetP( &*Params[p+1].data );
|
|
Params[Params.size()-1].data.UnsafeSetP( 0 );
|
|
Params.resize(Params.size()-1);
|
|
#endif
|
|
}
|
|
|
|
void CodeTree::DelParams()
|
|
{
|
|
data->Params.clear();
|
|
}
|
|
|
|
/* Is the value of this tree definitely odd(true) or even(false)? */
|
|
CodeTree::TriTruthValue CodeTree::GetEvennessInfo() const
|
|
{
|
|
if(!IsImmed()) return Unknown;
|
|
if(!IsLongIntegerImmed()) return Unknown;
|
|
return (GetLongIntegerImmed() & 1) ? IsNever : IsAlways;
|
|
}
|
|
|
|
bool CodeTree::IsLogicalValue() const
|
|
{
|
|
switch(data->Opcode)
|
|
{
|
|
case cImmed:
|
|
return FloatEqual(data->Value, 0.0)
|
|
|| FloatEqual(data->Value, 1.0);
|
|
case cAnd:
|
|
case cOr:
|
|
case cNot:
|
|
case cNotNot:
|
|
case cAbsAnd:
|
|
case cAbsOr:
|
|
case cAbsNot:
|
|
case cAbsNotNot:
|
|
case cEqual:
|
|
case cNEqual:
|
|
case cLess:
|
|
case cLessOrEq:
|
|
case cGreater:
|
|
case cGreaterOrEq:
|
|
/* These operations always produce truth values (0 or 1) */
|
|
return true;
|
|
case cMul:
|
|
{
|
|
std::vector<CodeTree>& Params = data->Params;
|
|
for(size_t a=0; a<Params.size(); ++a)
|
|
if(!Params[a].IsLogicalValue())
|
|
return false;
|
|
return true;
|
|
}
|
|
case cIf:
|
|
case cAbsIf:
|
|
{
|
|
std::vector<CodeTree>& Params = data->Params;
|
|
return Params[1].IsLogicalValue()
|
|
&& Params[2].IsLogicalValue();
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return false; // Not a logical value.
|
|
}
|
|
|
|
bool CodeTree::IsAlwaysInteger(bool integer) const
|
|
{
|
|
switch(data->Opcode)
|
|
{
|
|
case cImmed:
|
|
return IsLongIntegerImmed() ? integer==true : integer==false;
|
|
case cFloor:
|
|
case cInt:
|
|
return integer==true;
|
|
case cAnd:
|
|
case cOr:
|
|
case cNot:
|
|
case cNotNot:
|
|
case cEqual:
|
|
case cNEqual:
|
|
case cLess:
|
|
case cLessOrEq:
|
|
case cGreater:
|
|
case cGreaterOrEq:
|
|
/* These operations always produce truth values (0 or 1) */
|
|
return integer==true; /* 0 and 1 are both integers */
|
|
case cIf:
|
|
{
|
|
std::vector<CodeTree>& Params = data->Params;
|
|
return Params[1].IsAlwaysInteger(integer)
|
|
&& Params[2].IsAlwaysInteger(integer);
|
|
return true; /* 0 and 1 are both integers */
|
|
}
|
|
case cAdd:
|
|
case cMul:
|
|
{
|
|
for(size_t a=GetParamCount(); a-- > 0; )
|
|
if(!GetParam(a).IsAlwaysInteger(integer))
|
|
return false;
|
|
return true;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return false; /* Don't know whether it's integer. */
|
|
}
|
|
|
|
bool CodeTree::IsAlwaysSigned(bool positive) const
|
|
{
|
|
MinMaxTree tmp = CalculateResultBoundaries();
|
|
|
|
if(positive)
|
|
return tmp.has_min && tmp.min >= 0.0
|
|
&& (!tmp.has_max || tmp.max >= 0.0);
|
|
else
|
|
return tmp.has_max && tmp.max < 0.0
|
|
&& (!tmp.has_min || tmp.min < 0.0);
|
|
}
|
|
|
|
bool CodeTree::IsIdenticalTo(const CodeTree& b) const
|
|
{
|
|
//if((!&*data) != (!&*b.data)) return false;
|
|
if(&*data == &*b.data) return true;
|
|
return data->IsIdenticalTo(*b.data);
|
|
}
|
|
|
|
bool CodeTreeData::IsIdenticalTo(const CodeTreeData& b) const
|
|
{
|
|
if(Hash != b.Hash) return false; // a quick catch-all
|
|
if(Opcode != b.Opcode) return false;
|
|
switch(Opcode)
|
|
{
|
|
case cImmed: return FloatEqual(Value, b.Value);
|
|
case VarBegin: return Var == b.Var;
|
|
case cFCall:
|
|
case cPCall: if(Funcno != b.Funcno) return false; break;
|
|
default: break;
|
|
}
|
|
if(Params.size() != b.Params.size()) return false;
|
|
for(size_t a=0; a<Params.size(); ++a)
|
|
{
|
|
if(!Params[a].IsIdenticalTo(b.Params[a])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CodeTree::Become(const CodeTree& b)
|
|
{
|
|
if(&b != this && &*data != &*b.data)
|
|
{
|
|
DataP tmp = b.data;
|
|
CopyOnWrite();
|
|
data.swap(tmp);
|
|
}
|
|
}
|
|
|
|
void CodeTree::CopyOnWrite()
|
|
{
|
|
if(data->RefCount > 1)
|
|
data = new CodeTreeData(*data);
|
|
}
|
|
|
|
CodeTree CodeTree::GetUniqueRef()
|
|
{
|
|
if(data->RefCount > 1)
|
|
return CodeTree(*this, CloneTag());
|
|
return *this;
|
|
}
|
|
|
|
CodeTreeData::CodeTreeData()
|
|
: RefCount(0),
|
|
Opcode(cNop), Params(), Hash(), Depth(1), OptimizedUsing(0)
|
|
{
|
|
}
|
|
|
|
CodeTreeData::CodeTreeData(const CodeTreeData& b)
|
|
: RefCount(0),
|
|
Opcode(b.Opcode),
|
|
Params(b.Params),
|
|
Hash(b.Hash),
|
|
Depth(b.Depth),
|
|
OptimizedUsing(b.OptimizedUsing)
|
|
{
|
|
switch(Opcode)
|
|
{
|
|
case VarBegin: Var = b.Var; break;
|
|
case cImmed: Value = b.Value; break;
|
|
case cPCall:
|
|
case cFCall: Funcno = b.Funcno; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
|
CodeTreeData::CodeTreeData(CodeTreeData&& b)
|
|
: RefCount(0),
|
|
Opcode(b.Opcode),
|
|
Params(b.Params),
|
|
Hash(b.Hash),
|
|
Depth(b.Depth),
|
|
OptimizedUsing(b.OptimizedUsing)
|
|
{
|
|
switch(Opcode)
|
|
{
|
|
case VarBegin: Var = b.Var; break;
|
|
case cImmed: Value = b.Value; break;
|
|
case cPCall:
|
|
case cFCall: Funcno = b.Funcno; break;
|
|
default: break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
CodeTreeData::CodeTreeData(double i)
|
|
: RefCount(0), Opcode(cImmed), Params(), Hash(), Depth(1), OptimizedUsing(0)
|
|
{
|
|
Value = i;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_debug.c"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_opcodename.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <map>
|
|
#include <set>
|
|
#include <iostream>
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
|
|
#ifdef FUNCTIONPARSER_SUPPORT_DEBUG_OUTPUT
|
|
namespace
|
|
{
|
|
void DumpHashesFrom(
|
|
const FPoptimizer_CodeTree::CodeTree& tree,
|
|
std::map<fphash_t, std::set<std::string> >& done,
|
|
std::ostream& o)
|
|
{
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
DumpHashesFrom(tree.GetParam(a), done, o);
|
|
|
|
std::ostringstream buf;
|
|
DumpTree(tree, buf);
|
|
done[tree.GetHash()].insert(buf.str());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
#ifdef FUNCTIONPARSER_SUPPORT_DEBUG_OUTPUT
|
|
void DumpHashes(const CodeTree& tree, std::ostream& o)
|
|
{
|
|
std::map<fphash_t, std::set<std::string> > done;
|
|
DumpHashesFrom(tree, done, o);
|
|
|
|
for(std::map<fphash_t, std::set<std::string> >::const_iterator
|
|
i = done.begin();
|
|
i != done.end();
|
|
++i)
|
|
{
|
|
const std::set<std::string>& flist = i->second;
|
|
if(flist.size() != 1) o << "ERROR - HASH COLLISION?\n";
|
|
for(std::set<std::string>::const_iterator
|
|
j = flist.begin();
|
|
j != flist.end();
|
|
++j)
|
|
{
|
|
o << '[' << std::hex << i->first.hash1
|
|
<< ',' << i->first.hash2
|
|
<< ']' << std::dec;
|
|
o << ": " << *j << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void DumpTree(const CodeTree& tree, std::ostream& o)
|
|
{
|
|
//o << "/*" << tree.Depth << "*/";
|
|
const char* sep2 = " ";
|
|
/*
|
|
o << '[' << std::hex << tree.Hash.hash1
|
|
<< ',' << tree.Hash.hash2
|
|
<< ']' << std::dec;
|
|
*/
|
|
switch(tree.GetOpcode())
|
|
{
|
|
case cImmed:
|
|
o << tree.GetImmed();
|
|
/*
|
|
o << "(" << std::hex
|
|
<< *(const uint_least64_t*)&tree.GetImmed()
|
|
<< std::dec << ")";
|
|
*/
|
|
return;
|
|
case VarBegin: o << "Var" << (tree.GetVar() - VarBegin); return;
|
|
case cAdd: sep2 = " +"; break;
|
|
case cMul: sep2 = " *"; break;
|
|
case cAnd: sep2 = " &"; break;
|
|
case cOr: sep2 = " |"; break;
|
|
case cPow: sep2 = " ^"; break;
|
|
default:
|
|
sep2 = " ";
|
|
o << FP_GetOpcodeName(tree.GetOpcode());
|
|
if(tree.GetOpcode() == cFCall || tree.GetOpcode() == cPCall)
|
|
o << ':' << tree.GetFuncNo();
|
|
}
|
|
o << '(';
|
|
if(tree.GetParamCount() <= 1 && sep2[1]) o << (sep2+1) << ' ';
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
{
|
|
if(a > 0) o << ' ';
|
|
|
|
DumpTree(tree.GetParam(a), o);
|
|
|
|
if(a+1 < tree.GetParamCount()) o << sep2;
|
|
}
|
|
o << ')';
|
|
}
|
|
|
|
void DumpTreeWithIndent(const CodeTree& tree, std::ostream& o, const std::string& indent)
|
|
{
|
|
o << '[' << std::hex << (void*)(&tree.GetParams())
|
|
<< std::dec
|
|
<< ',' << tree.GetRefCount()
|
|
<< ']';
|
|
o << indent << '_';
|
|
|
|
switch(tree.GetOpcode())
|
|
{
|
|
case cImmed: o << "cImmed " << tree.GetImmed(); o << '\n'; return;
|
|
case VarBegin: o << "VarBegin " << (tree.GetVar() - VarBegin); o << '\n'; return;
|
|
default:
|
|
o << FP_GetOpcodeName(tree.GetOpcode());
|
|
if(tree.GetOpcode() == cFCall || tree.GetOpcode() == cPCall)
|
|
o << ':' << tree.GetFuncNo();
|
|
o << '\n';
|
|
}
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
{
|
|
std::string ind = indent;
|
|
for(size_t p=0; p < ind.size(); p+=2)
|
|
if(ind[p] == '\\')
|
|
ind[p] = ' ';
|
|
ind += (a+1 < tree.GetParamCount()) ? " |" : " \\";
|
|
DumpTreeWithIndent(tree.GetParam(a), o, ind);
|
|
}
|
|
o << std::flush;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_grammar.c"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_opcodename.h"
|
|
using namespace FPoptimizer_Grammar;
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
|
|
#include <cctype>
|
|
|
|
namespace FPoptimizer_Grammar
|
|
{
|
|
bool ParamSpec_Compare(const void* aa, const void* bb, SpecialOpcode type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case ParamHolder:
|
|
{
|
|
ParamSpec_ParamHolder& a = *(ParamSpec_ParamHolder*) aa;
|
|
ParamSpec_ParamHolder& b = *(ParamSpec_ParamHolder*) bb;
|
|
return a.constraints == b.constraints
|
|
&& a.index == b.index
|
|
&& a.depcode == b.depcode;
|
|
}
|
|
case NumConstant:
|
|
{
|
|
ParamSpec_NumConstant& a = *(ParamSpec_NumConstant*) aa;
|
|
ParamSpec_NumConstant& b = *(ParamSpec_NumConstant*) bb;
|
|
return FloatEqual(a.constvalue, b.constvalue);
|
|
}
|
|
case SubFunction:
|
|
{
|
|
ParamSpec_SubFunction& a = *(ParamSpec_SubFunction*) aa;
|
|
ParamSpec_SubFunction& b = *(ParamSpec_SubFunction*) bb;
|
|
return a.constraints == b.constraints
|
|
&& a.data.subfunc_opcode == b.data.subfunc_opcode
|
|
&& a.data.match_type == b.data.match_type
|
|
&& a.data.param_count == b.data.param_count
|
|
&& a.data.param_list == b.data.param_list
|
|
&& a.data.restholder_index == b.data.restholder_index
|
|
&& a.depcode == b.depcode;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
unsigned ParamSpec_GetDepCode(const ParamSpec& b)
|
|
{
|
|
switch(b.first)
|
|
{
|
|
case ParamHolder:
|
|
{
|
|
const ParamSpec_ParamHolder* s = (const ParamSpec_ParamHolder*) b.second;
|
|
return s->depcode;
|
|
}
|
|
case SubFunction:
|
|
{
|
|
const ParamSpec_SubFunction* s = (const ParamSpec_SubFunction*) b.second;
|
|
return s->depcode;
|
|
}
|
|
default: break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void DumpParam(const ParamSpec& parampair, std::ostream& o)
|
|
{
|
|
static const char ParamHolderNames[][2] = {"%","&", "x","y","z","a","b","c"};
|
|
|
|
//o << "/*p" << (&p-pack.plist) << "*/";
|
|
unsigned constraints = 0;
|
|
switch(parampair.first)
|
|
{
|
|
case NumConstant:
|
|
{ const ParamSpec_NumConstant& param = *(const ParamSpec_NumConstant*) parampair.second;
|
|
o.precision(12);
|
|
o << param.constvalue; break; }
|
|
case ParamHolder:
|
|
{ const ParamSpec_ParamHolder& param = *(const ParamSpec_ParamHolder*) parampair.second;
|
|
o << ParamHolderNames[param.index];
|
|
constraints = param.constraints;
|
|
break; }
|
|
case SubFunction:
|
|
{ const ParamSpec_SubFunction& param = *(const ParamSpec_SubFunction*) parampair.second;
|
|
constraints = param.constraints;
|
|
if(param.data.match_type == GroupFunction)
|
|
{
|
|
if(param.data.subfunc_opcode == cNeg)
|
|
{ o << "-"; DumpParams(param.data.param_list, param.data.param_count, o); }
|
|
else if(param.data.subfunc_opcode == cInv)
|
|
{ o << "/"; DumpParams(param.data.param_list, param.data.param_count, o); }
|
|
else
|
|
{
|
|
std::string opcode = FP_GetOpcodeName(param.data.subfunc_opcode).substr(1);
|
|
for(size_t a=0; a<opcode.size(); ++a) opcode[a] = (char) std::toupper(opcode[a]);
|
|
o << opcode << "( ";
|
|
DumpParams(param.data.param_list, param.data.param_count, o);
|
|
o << " )";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
o << '(' << FP_GetOpcodeName(param.data.subfunc_opcode) << ' ';
|
|
if(param.data.match_type == PositionalParams) o << '[';
|
|
if(param.data.match_type == SelectedParams) o << '{';
|
|
DumpParams(param.data.param_list, param.data.param_count, o);
|
|
if(param.data.restholder_index != 0)
|
|
o << " <" << param.data.restholder_index << '>';
|
|
if(param.data.match_type == PositionalParams) o << "]";
|
|
if(param.data.match_type == SelectedParams) o << "}";
|
|
o << ')';
|
|
}
|
|
break; }
|
|
}
|
|
switch( ImmedConstraint_Value(constraints & ValueMask) )
|
|
{
|
|
case ValueMask: break;
|
|
case Value_AnyNum: break;
|
|
case Value_EvenInt: o << "@E"; break;
|
|
case Value_OddInt: o << "@O"; break;
|
|
case Value_IsInteger: o << "@I"; break;
|
|
case Value_NonInteger:o << "@F"; break;
|
|
case Value_Logical: o << "@L"; break;
|
|
}
|
|
switch( ImmedConstraint_Sign(constraints & SignMask) )
|
|
{
|
|
case SignMask: break;
|
|
case Sign_AnySign: break;
|
|
case Sign_Positive: o << "@P"; break;
|
|
case Sign_Negative: o << "@N"; break;
|
|
}
|
|
switch( ImmedConstraint_Oneness(constraints & OnenessMask) )
|
|
{
|
|
case OnenessMask: break;
|
|
case Oneness_Any: break;
|
|
case Oneness_One: o << "@1"; break;
|
|
case Oneness_NotOne: o << "@M"; break;
|
|
}
|
|
}
|
|
|
|
void DumpParams(unsigned paramlist, unsigned count, std::ostream& o)
|
|
{
|
|
for(unsigned a=0; a<count; ++a)
|
|
{
|
|
if(a > 0) o << ' ';
|
|
const ParamSpec& param = ParamSpec_Extract(paramlist,a);
|
|
DumpParam(param, o);
|
|
unsigned depcode = ParamSpec_GetDepCode(param);
|
|
if(depcode != 0)
|
|
o << "@D" << depcode;
|
|
}
|
|
}
|
|
}
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_grammar_data.c"
|
|
/* This file is automatically generated. Do not edit... */
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_consts.h"
|
|
#include "fpconfig.h"
|
|
#include "fptypes.h"
|
|
#include <algorithm>
|
|
|
|
#define P1(a) a
|
|
#define P2(a,b) (P1(a) | (b << PARAM_INDEX_BITS))
|
|
#define P3(a,b,c) (P2(a,b) | (c << (PARAM_INDEX_BITS*2)))
|
|
|
|
#define grammar_optimize_abslogical grammar_optimize_abslogical_tweak
|
|
#define grammar_optimize_ignore_if_sideeffects grammar_optimize_ignore_if_sideeffects_tweak
|
|
#define grammar_optimize_nonshortcut_logical_evaluation grammar_optimize_nonshortcut_logical_evaluation_tweak
|
|
#define grammar_optimize_round1 grammar_optimize_round1_tweak
|
|
#define grammar_optimize_round2 grammar_optimize_round2_tweak
|
|
#define grammar_optimize_round3 grammar_optimize_round3_tweak
|
|
#define grammar_optimize_round4 grammar_optimize_round4_tweak
|
|
#define grammar_optimize_shortcut_logical_evaluation grammar_optimize_shortcut_logical_evaluation_tweak
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
#undef grammar_optimize_abslogical
|
|
#undef grammar_optimize_ignore_if_sideeffects
|
|
#undef grammar_optimize_nonshortcut_logical_evaluation
|
|
#undef grammar_optimize_round1
|
|
#undef grammar_optimize_round2
|
|
#undef grammar_optimize_round3
|
|
#undef grammar_optimize_round4
|
|
#undef grammar_optimize_shortcut_logical_evaluation
|
|
|
|
using namespace FPoptimizer_Grammar;
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
|
|
namespace
|
|
{
|
|
const struct ParamSpec_List
|
|
{
|
|
ParamSpec_ParamHolder plist_p[33];
|
|
#define P(n) (n)
|
|
ParamSpec_NumConstant plist_n[17];
|
|
#define N(n) (n+33)
|
|
ParamSpec_SubFunction plist_s[422];
|
|
#define S(n) (n+33+17)
|
|
} /*PACKED_GRAMMAR_ATTRIBUTE*/ plist =
|
|
{
|
|
{ /* plist_p - ParamSpec_ParamHolder[33] */
|
|
/* 0 */ {0, Sign_Negative | Constness_Const, 0x0}, /* %@N */
|
|
/* 1 */ {0, Constness_Const, 0x0}, /* % */
|
|
/* 2 */ {0, Sign_Positive | Constness_Const, 0x0}, /* %@P */
|
|
/* 3 */ {0, Constness_Const, 0x1}, /* % */
|
|
/* 4 */ {0, Value_IsInteger | Sign_Positive | Constness_Const, 0x0}, /* %@I@P */
|
|
/* 5 */ {0, Oneness_NotOne | Constness_Const, 0x1}, /* %@M */
|
|
/* 6 */ {0, Oneness_NotOne | Constness_Const, 0x0}, /* %@M */
|
|
/* 7 */ {0, Oneness_One | Constness_Const, 0x0}, /* %@1 */
|
|
/* 8 */ {0, Value_Logical | Constness_Const, 0x0}, /* %@L */
|
|
/* 9 */ {1, Constness_Const, 0x0}, /* & */
|
|
/* 10 */ {1, Value_EvenInt | Constness_Const, 0x0}, /* &@E */
|
|
/* 11 */ {1, Oneness_NotOne | Constness_Const, 0x0}, /* &@M */
|
|
/* 12 */ {1, Value_IsInteger | Constness_Const, 0x0}, /* &@I */
|
|
/* 13 */ {1, Sign_Positive | Constness_Const, 0x0}, /* &@P */
|
|
/* 14 */ {2, 0, 0x0}, /* x */
|
|
/* 15 */ {2, 0, 0x4}, /* x */
|
|
/* 16 */ {2, Value_IsInteger, 0x0}, /* x@I */
|
|
/* 17 */ {2, Sign_Positive, 0x0}, /* x@P */
|
|
/* 18 */ {2, Sign_NoIdea, 0x0}, /* x */
|
|
/* 19 */ {2, Value_Logical, 0x0}, /* x@L */
|
|
/* 20 */ {3, Sign_NoIdea, 0x0}, /* y */
|
|
/* 21 */ {3, 0, 0x0}, /* y */
|
|
/* 22 */ {3, Value_Logical, 0x0}, /* y@L */
|
|
/* 23 */ {3, 0, 0x8}, /* y */
|
|
/* 24 */ {3, Value_OddInt, 0x0}, /* y@O */
|
|
/* 25 */ {3, Value_NonInteger, 0x0}, /* y@F */
|
|
/* 26 */ {3, Value_EvenInt, 0x0}, /* y@E */
|
|
/* 27 */ {3, Sign_Positive, 0x0}, /* y@P */
|
|
/* 28 */ {4, 0, 0x0}, /* z */
|
|
/* 29 */ {4, 0, 0x16}, /* z */
|
|
/* 30 */ {4, Value_IsInteger, 0x0}, /* z@I */
|
|
/* 31 */ {5, 0, 0x0}, /* a */
|
|
/* 32 */ {6, 0, 0x0}, /* b */
|
|
},
|
|
|
|
{ /* plist_n - ParamSpec_NumConstant[17] */
|
|
/* 0 */ {-2}, /* -2 */
|
|
/* 1 */ {-CONSTANT_PIHALF}, /* -1.57079632679 */
|
|
/* 2 */ {-1}, /* -1 */
|
|
/* 3 */ {-0.5}, /* -0.5 */
|
|
/* 4 */ {0}, /* 0 */
|
|
/* 5 */ {CONSTANT_RD}, /* 0.0174532925199 */
|
|
/* 6 */ {CONSTANT_EI}, /* 0.367879441171 */
|
|
/* 7 */ {CONSTANT_L10I}, /* 0.434294481903 */
|
|
/* 8 */ {0.5}, /* 0.5 */
|
|
/* 9 */ {CONSTANT_L2}, /* 0.69314718056 */
|
|
/* 10 */ {1}, /* 1 */
|
|
/* 11 */ {CONSTANT_L2I}, /* 1.44269504089 */
|
|
/* 12 */ {CONSTANT_PIHALF}, /* 1.57079632679 */
|
|
/* 13 */ {2}, /* 2 */
|
|
/* 14 */ {CONSTANT_L10}, /* 2.30258509299 */
|
|
/* 15 */ {CONSTANT_E}, /* 2.71828182846 */
|
|
/* 16 */ {CONSTANT_DR}, /* 57.2957795131 */
|
|
},
|
|
|
|
{ /* plist_s - ParamSpec_SubFunction[422] */
|
|
/* 0 */ {{1,P1(P(14)) , cAbs ,PositionalParams,0}, 0, 0x0}, /* (cAbs [x]) */
|
|
/* 1 */ {{1,P1(P(21)) , cAbs ,PositionalParams,0}, 0, 0x0}, /* (cAbs [y]) */
|
|
/* 2 */ {{1,P1(S(283)) , cAbs ,PositionalParams,0}, 0, 0x0}, /* (cAbs [(cMul {x y})]) */
|
|
/* 3 */ {{1,P1(P(14)) , cAcos ,PositionalParams,0}, 0, 0x0}, /* (cAcos [x]) */
|
|
/* 4 */ {{1,P1(P(14)) , cAcosh ,PositionalParams,0}, 0, 0x0}, /* (cAcosh [x]) */
|
|
/* 5 */ {{1,P1(P(14)) , cAsin ,PositionalParams,0}, 0, 0x0}, /* (cAsin [x]) */
|
|
/* 6 */ {{1,P1(P(14)) , cAsinh ,PositionalParams,0}, 0, 0x0}, /* (cAsinh [x]) */
|
|
/* 7 */ {{1,P1(S(242)) , cAsinh ,PositionalParams,0}, 0, 0x0}, /* (cAsinh [(cAdd <1>)]) */
|
|
/* 8 */ {{1,P1(P(14)) , cAtan ,PositionalParams,0}, 0, 0x0}, /* (cAtan [x]) */
|
|
/* 9 */ {{2,P2(P(14),P(21)) , cAtan2 ,PositionalParams,0}, 0, 0x0}, /* (cAtan2 [x y]) */
|
|
/* 10 */ {{2,P2(P(14),S(109)) , cAtan2 ,PositionalParams,0}, 0, 0x0}, /* (cAtan2 [x (cPow [y -%])]) */
|
|
/* 11 */ {{1,P1(P(14)) , cAtanh ,PositionalParams,0}, 0, 0x0}, /* (cAtanh [x]) */
|
|
/* 12 */ {{1,P1(P(14)) , cCeil ,PositionalParams,0}, 0, 0x4}, /* (cCeil [x]) */
|
|
/* 13 */ {{1,P1(S(331)) , cCeil ,PositionalParams,0}, 0, 0x0}, /* (cCeil [(cMul <1>)]) */
|
|
/* 14 */ {{1,P1(P(14)) , cCos ,PositionalParams,0}, 0, 0x0}, /* (cCos [x]) */
|
|
/* 15 */ {{1,P1(P(14)) , cCos ,PositionalParams,0}, 0, 0x4}, /* (cCos [x]) */
|
|
/* 16 */ {{1,P1(P(21)) , cCos ,PositionalParams,0}, 0, 0x0}, /* (cCos [y]) */
|
|
/* 17 */ {{1,P1(S(215)) , cCos ,PositionalParams,0}, 0, 0x0}, /* (cCos [(cAdd {x y})]) */
|
|
/* 18 */ {{1,P1(S(221)) , cCos ,PositionalParams,0}, 0, 0x0}, /* (cCos [(cAdd {x (cMul {-1 y})})]) */
|
|
/* 19 */ {{1,P1(S(242)) , cCos ,PositionalParams,0}, 0, 0x0}, /* (cCos [(cAdd <1>)]) */
|
|
/* 20 */ {{1,P1(S(301)) , cCos ,PositionalParams,0}, 0, 0x0}, /* (cCos [(cMul {-% x})]) */
|
|
/* 21 */ {{1,P1(S(358)) , cCos ,PositionalParams,0}, 0, 0x0}, /* (cCos [(cMul -% <1>)]) */
|
|
/* 22 */ {{1,P1(P(14)) , cCosh ,PositionalParams,0}, 0, 0x4}, /* (cCosh [x]) */
|
|
/* 23 */ {{1,P1(P(14)) , cCosh ,PositionalParams,0}, 0, 0x0}, /* (cCosh [x]) */
|
|
/* 24 */ {{1,P1(S(50)) , cCosh ,PositionalParams,0}, 0, 0x0}, /* (cCosh [(cLog [(cPow [& x])])]) */
|
|
/* 25 */ {{1,P1(S(295)) , cCosh ,PositionalParams,0}, 0, 0x0}, /* (cCosh [(cMul {x LOG( & )})]) */
|
|
/* 26 */ {{1,P1(S(301)) , cCosh ,PositionalParams,0}, 0, 0x0}, /* (cCosh [(cMul {-% x})]) */
|
|
/* 27 */ {{1,P1(P(14)) , cCot ,PositionalParams,0}, 0, 0x0}, /* (cCot [x]) */
|
|
/* 28 */ {{1,P1(P(14)) , cCsc ,PositionalParams,0}, 0, 0x0}, /* (cCsc [x]) */
|
|
/* 29 */ {{1,P1(P(14)) , cFloor ,PositionalParams,0}, 0, 0x4}, /* (cFloor [x]) */
|
|
/* 30 */ {{1,P1(S(331)) , cFloor ,PositionalParams,0}, 0, 0x0}, /* (cFloor [(cMul <1>)]) */
|
|
/* 31 */ {{3,P3(P(14),N(10),S(400)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x 1 (cNotNot [y])]) */
|
|
/* 32 */ {{3,P3(P(14),P(21),P(28)) , cIf ,PositionalParams,0}, 0, 0x4}, /* (cIf [x y z]) */
|
|
/* 33 */ {{3,P3(P(14),P(21),P(28)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x y z]) */
|
|
/* 34 */ {{3,P3(P(14),P(8),S(376)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x %@L (cNot [y])]) */
|
|
/* 35 */ {{3,P3(P(14),P(21),S(374)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x y (cNot [%])]) */
|
|
/* 36 */ {{3,P3(P(14),P(31),P(32)) , cIf ,PositionalParams,0}, 0, 0x4}, /* (cIf [x a b]) */
|
|
/* 37 */ {{3,P3(P(14),S(374),P(21)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cNot [%]) y]) */
|
|
/* 38 */ {{3,P3(P(14),S(376),P(8)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cNot [y]) %@L]) */
|
|
/* 39 */ {{3,P3(P(14),S(58),S(59)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cMax [y a]) (cMax [z b])]) */
|
|
/* 40 */ {{3,P3(P(14),S(61),S(62)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cMin [y a]) (cMin [z b])]) */
|
|
/* 41 */ {{3,P3(P(14),S(222),S(224)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cAdd {y a}) (cAdd {z b})]) */
|
|
/* 42 */ {{3,P3(P(14),S(288),S(289)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cMul {y a}) (cMul {z b})]) */
|
|
/* 43 */ {{3,P3(P(14),S(386),S(387)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cAnd {y a}) (cAnd {z b})]) */
|
|
/* 44 */ {{3,P3(P(14),S(393),S(394)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cOr {y a}) (cOr {z b})]) */
|
|
/* 45 */ {{3,P3(P(14),S(400),N(4)) , cIf ,PositionalParams,0}, 0, 0x0}, /* (cIf [x (cNotNot [y]) 0]) */
|
|
/* 46 */ {{1,P1(S(242)) , cInt ,PositionalParams,0}, 0, 0x0}, /* (cInt [(cAdd <1>)]) */
|
|
/* 47 */ {{1,P1(P(14)) , cLog ,PositionalParams,0}, 0, 0x0}, /* (cLog [x]) */
|
|
/* 48 */ {{1,P1(P(21)) , cLog ,PositionalParams,0}, 0, 0x0}, /* (cLog [y]) */
|
|
/* 49 */ {{1,P1(P(28)) , cLog ,PositionalParams,0}, 0, 0x0}, /* (cLog [z]) */
|
|
/* 50 */ {{1,P1(S(83)) , cLog ,PositionalParams,0}, 0, 0x0}, /* (cLog [(cPow [& x])]) */
|
|
/* 51 */ {{1,P1(S(283)) , cLog ,PositionalParams,0}, 0, 0x0}, /* (cLog [(cMul {x y})]) */
|
|
/* 52 */ {{1,P1(S(331)) , cLog ,PositionalParams,0}, 0, 0x0}, /* (cLog [(cMul <1>)]) */
|
|
/* 53 */ {{1,P1(P(1)) , cLog ,GroupFunction ,0}, Constness_Const, 0x0}, /* LOG( % ) */
|
|
/* 54 */ {{1,P1(P(9)) , cLog ,GroupFunction ,0}, Constness_Const, 0x0}, /* LOG( & ) */
|
|
/* 55 */ {{1,P1(P(14)) , cLog10 ,PositionalParams,0}, 0, 0x0}, /* (cLog10 [x]) */
|
|
/* 56 */ {{1,P1(P(14)) , cLog2 ,PositionalParams,0}, 0, 0x0}, /* (cLog2 [x]) */
|
|
/* 57 */ {{2,P2(P(14),P(21)) , cMax ,PositionalParams,0}, 0, 0x0}, /* (cMax [x y]) */
|
|
/* 58 */ {{2,P2(P(21),P(31)) , cMax ,PositionalParams,0}, 0, 0x0}, /* (cMax [y a]) */
|
|
/* 59 */ {{2,P2(P(28),P(32)) , cMax ,PositionalParams,0}, 0, 0x0}, /* (cMax [z b]) */
|
|
/* 60 */ {{2,P2(P(14),P(21)) , cMin ,PositionalParams,0}, 0, 0x0}, /* (cMin [x y]) */
|
|
/* 61 */ {{2,P2(P(21),P(31)) , cMin ,PositionalParams,0}, 0, 0x0}, /* (cMin [y a]) */
|
|
/* 62 */ {{2,P2(P(28),P(32)) , cMin ,PositionalParams,0}, 0, 0x0}, /* (cMin [z b]) */
|
|
/* 63 */ {{2,P2(P(1),N(10)) , cMin ,GroupFunction ,0}, Constness_Const, 0x0}, /* MIN( % 1 ) */
|
|
/* 64 */ {{2,P2(P(1),P(9)) , cMin ,GroupFunction ,0}, Constness_Const, 0x0}, /* MIN( % & ) */
|
|
/* 65 */ {{2,P2(N(6),P(14)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [0.367879441171 x]) */
|
|
/* 66 */ {{2,P2(N(6),P(14)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [0.367879441171 x]) */
|
|
/* 67 */ {{2,P2(N(15),P(14)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [2.71828182846 x]) */
|
|
/* 68 */ {{2,P2(N(15),P(14)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [2.71828182846 x]) */
|
|
/* 69 */ {{2,P2(N(6),S(6)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [0.367879441171 (cAsinh [x])]) */
|
|
/* 70 */ {{2,P2(N(15),S(6)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [2.71828182846 (cAsinh [x])]) */
|
|
/* 71 */ {{2,P2(N(6),S(7)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [0.367879441171 (cAsinh [(cAdd <1>)])]) */
|
|
/* 72 */ {{2,P2(N(15),S(7)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [2.71828182846 (cAsinh [(cAdd <1>)])]) */
|
|
/* 73 */ {{2,P2(N(15),S(332)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [2.71828182846 (cMul <2>)]) */
|
|
/* 74 */ {{2,P2(P(1),P(14)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [% x]) */
|
|
/* 75 */ {{2,P2(P(1),P(14)) , cPow ,PositionalParams,0}, 0, 0x1}, /* (cPow [% x]) */
|
|
/* 76 */ {{2,P2(P(1),P(21)) , cPow ,PositionalParams,0}, 0, 0x1}, /* (cPow [% y]) */
|
|
/* 77 */ {{2,P2(P(14),P(1)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x %]) */
|
|
/* 78 */ {{2,P2(P(14),P(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x %@P]) */
|
|
/* 79 */ {{2,P2(P(21),P(13)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [y &@P]) */
|
|
/* 80 */ {{2,P2(P(21),P(9)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [y &]) */
|
|
/* 81 */ {{2,P2(P(2),P(14)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [%@P x]) */
|
|
/* 82 */ {{2,P2(P(9),P(14)) , cPow ,PositionalParams,0}, 0, 0x6}, /* (cPow [& x]) */
|
|
/* 83 */ {{2,P2(P(9),P(14)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [& x]) */
|
|
/* 84 */ {{2,P2(P(9),P(21)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [& y]) */
|
|
/* 85 */ {{2,P2(P(9),S(223)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [& (cAdd {y (cMul {z (cLog [x]) /LOG( & )})})]) */
|
|
/* 86 */ {{2,P2(P(14),P(4)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x %@I@P]) */
|
|
/* 87 */ {{2,P2(P(14),P(12)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x &@I]) */
|
|
/* 88 */ {{2,P2(P(1),P(21)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [% y]) */
|
|
/* 89 */ {{2,P2(P(14),N(13)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x 2]) */
|
|
/* 90 */ {{2,P2(P(14),P(24)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x y@O]) */
|
|
/* 91 */ {{2,P2(P(14),P(25)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x y@F]) */
|
|
/* 92 */ {{2,P2(P(17),P(21)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x@P y]) */
|
|
/* 93 */ {{2,P2(P(18),P(21)) , cPow ,PositionalParams,0}, Sign_Positive, 0x0}, /* (cPow [x y])@P */
|
|
/* 94 */ {{2,P2(P(14),P(21)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [x y]) */
|
|
/* 95 */ {{2,P2(P(14),P(28)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x z]) */
|
|
/* 96 */ {{2,P2(P(17),P(28)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x@P z]) */
|
|
/* 97 */ {{2,P2(P(14),S(63)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x MIN( % 1 )]) */
|
|
/* 98 */ {{2,P2(P(14),S(64)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x MIN( % & )]) */
|
|
/* 99 */ {{2,P2(P(14),S(208)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x (cAdd {1 -MIN( % 1 )})]) */
|
|
/* 100 */ {{2,P2(P(14),S(216)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x (cAdd {y z})]) */
|
|
/* 101 */ {{2,P2(P(14),S(218)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x (cAdd {% -MIN( % 1 )})]) */
|
|
/* 102 */ {{2,P2(P(14),S(219)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x (cAdd {% -MIN( % & )})]) */
|
|
/* 103 */ {{2,P2(P(14),S(220)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x (cAdd {& -MIN( % & )})]) */
|
|
/* 104 */ {{2,P2(P(21),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [y -1]) */
|
|
/* 105 */ {{2,P2(P(14),P(21)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x y]) */
|
|
/* 106 */ {{2,P2(P(1),S(242)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [% (cAdd <1>)]) */
|
|
/* 107 */ {{2,P2(P(14),S(53)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [x LOG( % )]) */
|
|
/* 108 */ {{2,P2(P(20),P(0)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [y %@N]) */
|
|
/* 109 */ {{2,P2(P(21),S(194)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [y -%]) */
|
|
/* 110 */ {{2,P2(P(28),S(242)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [z (cAdd <1>)]) */
|
|
/* 111 */ {{2,P2(S(14),N(2)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cCos [x]) -1]) */
|
|
/* 112 */ {{2,P2(S(14),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cCos [x]) -1]) */
|
|
/* 113 */ {{2,P2(S(20),N(2)) , cPow ,PositionalParams,0}, 0, 0x5}, /* (cPow [(cCos [(cMul {-% x})]) -1]) */
|
|
/* 114 */ {{2,P2(S(21),N(2)) , cPow ,PositionalParams,0}, 0, 0x1}, /* (cPow [(cCos [(cMul -% <1>)]) -1]) */
|
|
/* 115 */ {{2,P2(S(23),N(2)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cCosh [x]) -1]) */
|
|
/* 116 */ {{2,P2(S(23),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cCosh [x]) -1]) */
|
|
/* 117 */ {{2,P2(S(14),N(13)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cCos [x]) 2]) */
|
|
/* 118 */ {{2,P2(S(14),N(13)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cCos [x]) 2]) */
|
|
/* 119 */ {{2,P2(S(23),P(1)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cCosh [x]) %]) */
|
|
/* 120 */ {{2,P2(S(23),S(259)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cCosh [x]) ADD( % 1 )]) */
|
|
/* 121 */ {{2,P2(S(26),N(2)) , cPow ,PositionalParams,0}, 0, 0x5}, /* (cPow [(cCosh [(cMul {-% x})]) -1]) */
|
|
/* 122 */ {{2,P2(S(47),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cLog [x]) -1]) */
|
|
/* 123 */ {{2,P2(S(49),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cLog [z]) -1]) */
|
|
/* 124 */ {{2,P2(S(55),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cLog10 [x]) -1]) */
|
|
/* 125 */ {{2,P2(S(56),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cLog2 [x]) -1]) */
|
|
/* 126 */ {{2,P2(S(77),S(417)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cPow [x %]) /%]) */
|
|
/* 127 */ {{2,P2(S(77),S(421)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cPow [x %]) /MIN( % & )]) */
|
|
/* 128 */ {{2,P2(S(80),S(421)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cPow [y &]) /MIN( % & )]) */
|
|
/* 129 */ {{2,P2(S(158),N(2)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cSin [x]) -1]) */
|
|
/* 130 */ {{2,P2(S(170),N(2)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cSinh [x]) -1]) */
|
|
/* 131 */ {{2,P2(S(177),N(2)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cTan [x]) -1]) */
|
|
/* 132 */ {{2,P2(S(177),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cTan [x]) -1]) */
|
|
/* 133 */ {{2,P2(S(186),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cTanh [x]) -1]) */
|
|
/* 134 */ {{2,P2(S(186),N(2)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cTanh [x]) -1]) */
|
|
/* 135 */ {{2,P2(S(189),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cTanh [(cMul {x LOG( % ) 0.5})]) -1]) */
|
|
/* 136 */ {{2,P2(S(199),N(2)) , cPow ,PositionalParams,0}, 0, 0x5}, /* (cPow [(cAdd {-1 (cPow [% x])}) -1]) */
|
|
/* 137 */ {{2,P2(S(201),N(2)) , cPow ,PositionalParams,0}, 0, 0x5}, /* (cPow [(cAdd {1 (cPow [% x])}) -1]) */
|
|
/* 138 */ {{2,P2(S(332),N(2)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cMul <2>) -1]) */
|
|
/* 139 */ {{2,P2(S(346),N(2)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cMul x <2>) -1]) */
|
|
/* 140 */ {{2,P2(S(158),N(13)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cSin [x]) 2]) */
|
|
/* 141 */ {{2,P2(S(158),N(13)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cSin [x]) 2]) */
|
|
/* 142 */ {{2,P2(S(205),N(3)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cAdd {1 (cPow [x 2])}) -0.5]) */
|
|
/* 143 */ {{2,P2(S(205),N(8)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cAdd {1 (cPow [x 2])}) 0.5]) */
|
|
/* 144 */ {{2,P2(S(207),N(3)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cAdd {1 (cPow [(cAdd <1>) 2])}) -0.5]) */
|
|
/* 145 */ {{2,P2(S(207),N(8)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cAdd {1 (cPow [(cAdd <1>) 2])}) 0.5]) */
|
|
/* 146 */ {{2,P2(S(209),N(2)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cAdd {1 (cMul {-1 x})}) -1]) */
|
|
/* 147 */ {{2,P2(S(203),N(8)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cAdd {-1 (cPow [x 2])}) 0.5]) */
|
|
/* 148 */ {{2,P2(S(226),N(8)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cAdd {(cPow [x 2]) -1}) 0.5]) */
|
|
/* 149 */ {{2,P2(S(236),N(8)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cAdd {(cPow [x 2]) 1}) 0.5]) */
|
|
/* 150 */ {{2,P2(S(237),N(8)) , cPow ,PositionalParams,0}, 0, 0x4}, /* (cPow [(cAdd {(cMul {(cPow [x 2]) -1}) 1}) 0.5]) */
|
|
/* 151 */ {{2,P2(S(242),N(13)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cAdd <1>) 2]) */
|
|
/* 152 */ {{2,P2(S(331),P(9)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [(cMul <1>) &]) */
|
|
/* 153 */ {{2,P2(S(418),P(14)) , cPow ,PositionalParams,0}, 0, 0x0}, /* (cPow [/& x]) */
|
|
/* 154 */ {{2,P2(S(418),P(14)) , cPow ,PositionalParams,0}, 0, 0x6}, /* (cPow [/& x]) */
|
|
/* 155 */ {{2,P2(P(1),P(9)) , cPow ,GroupFunction ,0}, Constness_Const, 0x0}, /* POW( % & ) */
|
|
/* 156 */ {{2,P2(P(9),S(417)) , cPow ,GroupFunction ,0}, Constness_Const, 0x0}, /* POW( & /% ) */
|
|
/* 157 */ {{1,P1(P(14)) , cSec ,PositionalParams,0}, 0, 0x0}, /* (cSec [x]) */
|
|
/* 158 */ {{1,P1(P(14)) , cSin ,PositionalParams,0}, 0, 0x0}, /* (cSin [x]) */
|
|
/* 159 */ {{1,P1(P(14)) , cSin ,PositionalParams,0}, 0, 0x4}, /* (cSin [x]) */
|
|
/* 160 */ {{1,P1(P(21)) , cSin ,PositionalParams,0}, 0, 0x0}, /* (cSin [y]) */
|
|
/* 161 */ {{1,P1(S(215)) , cSin ,PositionalParams,0}, 0, 0x0}, /* (cSin [(cAdd {x y})]) */
|
|
/* 162 */ {{1,P1(S(221)) , cSin ,PositionalParams,0}, 0, 0x0}, /* (cSin [(cAdd {x (cMul {-1 y})})]) */
|
|
/* 163 */ {{1,P1(S(242)) , cSin ,PositionalParams,0}, 0, 0x0}, /* (cSin [(cAdd <1>)]) */
|
|
/* 164 */ {{1,P1(S(279)) , cSin ,PositionalParams,0}, 0, 0x5}, /* (cSin [(cMul {% x})]) */
|
|
/* 165 */ {{1,P1(S(331)) , cSin ,PositionalParams,0}, 0, 0x0}, /* (cSin [(cMul <1>)]) */
|
|
/* 166 */ {{1,P1(S(335)) , cSin ,PositionalParams,0}, 0, 0x0}, /* (cSin [(cMul %@N <1>)]) */
|
|
/* 167 */ {{1,P1(S(339)) , cSin ,PositionalParams,0}, 0, 0x1}, /* (cSin [(cMul % <1>)]) */
|
|
/* 168 */ {{1,P1(S(358)) , cSin ,PositionalParams,0}, 0, 0x0}, /* (cSin [(cMul -% <1>)]) */
|
|
/* 169 */ {{1,P1(P(14)) , cSinh ,PositionalParams,0}, 0, 0x4}, /* (cSinh [x]) */
|
|
/* 170 */ {{1,P1(P(14)) , cSinh ,PositionalParams,0}, 0, 0x0}, /* (cSinh [x]) */
|
|
/* 171 */ {{1,P1(S(50)) , cSinh ,PositionalParams,0}, 0, 0x0}, /* (cSinh [(cLog [(cPow [& x])])]) */
|
|
/* 172 */ {{1,P1(S(279)) , cSinh ,PositionalParams,0}, 0, 0x5}, /* (cSinh [(cMul {% x})]) */
|
|
/* 173 */ {{1,P1(S(295)) , cSinh ,PositionalParams,0}, 0, 0x0}, /* (cSinh [(cMul {x LOG( & )})]) */
|
|
/* 174 */ {{1,P1(S(331)) , cSinh ,PositionalParams,0}, 0, 0x0}, /* (cSinh [(cMul <1>)]) */
|
|
/* 175 */ {{1,P1(S(335)) , cSinh ,PositionalParams,0}, 0, 0x0}, /* (cSinh [(cMul %@N <1>)]) */
|
|
/* 176 */ {{1,P1(S(358)) , cSinh ,PositionalParams,0}, 0, 0x0}, /* (cSinh [(cMul -% <1>)]) */
|
|
/* 177 */ {{1,P1(P(14)) , cTan ,PositionalParams,0}, 0, 0x0}, /* (cTan [x]) */
|
|
/* 178 */ {{1,P1(P(14)) , cTan ,PositionalParams,0}, 0, 0x4}, /* (cTan [x]) */
|
|
/* 179 */ {{1,P1(S(210)) , cTan ,PositionalParams,0}, 0, 0x4}, /* (cTan [(cAdd {1.57079632679 (cMul {-1 x})})]) */
|
|
/* 180 */ {{1,P1(S(213)) , cTan ,PositionalParams,0}, 0, 0x0}, /* (cTan [(cAdd {1.57079632679 (cMul -1 <1>)})]) */
|
|
/* 181 */ {{1,P1(S(279)) , cTan ,PositionalParams,0}, 0, 0x0}, /* (cTan [(cMul {% x})]) */
|
|
/* 182 */ {{1,P1(S(331)) , cTan ,PositionalParams,0}, 0, 0x0}, /* (cTan [(cMul <1>)]) */
|
|
/* 183 */ {{1,P1(S(339)) , cTan ,PositionalParams,0}, 0, 0x0}, /* (cTan [(cMul % <1>)]) */
|
|
/* 184 */ {{1,P1(S(335)) , cTan ,PositionalParams,0}, 0, 0x0}, /* (cTan [(cMul %@N <1>)]) */
|
|
/* 185 */ {{1,P1(S(358)) , cTan ,PositionalParams,0}, 0, 0x0}, /* (cTan [(cMul -% <1>)]) */
|
|
/* 186 */ {{1,P1(P(14)) , cTanh ,PositionalParams,0}, 0, 0x0}, /* (cTanh [x]) */
|
|
/* 187 */ {{1,P1(P(14)) , cTanh ,PositionalParams,0}, 0, 0x4}, /* (cTanh [x]) */
|
|
/* 188 */ {{1,P1(S(279)) , cTanh ,PositionalParams,0}, 0, 0x0}, /* (cTanh [(cMul {% x})]) */
|
|
/* 189 */ {{1,P1(S(286)) , cTanh ,PositionalParams,0}, 0, 0x0}, /* (cTanh [(cMul {x LOG( % ) 0.5})]) */
|
|
/* 190 */ {{1,P1(S(331)) , cTanh ,PositionalParams,0}, 0, 0x0}, /* (cTanh [(cMul <1>)]) */
|
|
/* 191 */ {{1,P1(S(335)) , cTanh ,PositionalParams,0}, 0, 0x0}, /* (cTanh [(cMul %@N <1>)]) */
|
|
/* 192 */ {{1,P1(S(358)) , cTanh ,PositionalParams,0}, 0, 0x0}, /* (cTanh [(cMul -% <1>)]) */
|
|
/* 193 */ {{1,P1(P(14)) , cTrunc ,PositionalParams,0}, 0, 0x0}, /* (cTrunc [x]) */
|
|
/* 194 */ {{1,P1(P(1)) , cNeg ,GroupFunction ,0}, Constness_Const, 0x0}, /* -% */
|
|
/* 195 */ {{1,P1(P(1)) , cNeg ,GroupFunction ,0}, Constness_Const, 0x1}, /* -% */
|
|
/* 196 */ {{1,P1(S(63)) , cNeg ,GroupFunction ,0}, Constness_Const, 0x0}, /* -MIN( % 1 ) */
|
|
/* 197 */ {{1,P1(S(64)) , cNeg ,GroupFunction ,0}, Constness_Const, 0x0}, /* -MIN( % & ) */
|
|
/* 198 */ {{2,P2(N(2),S(74)) , cAdd ,SelectedParams ,0}, 0, 0x5}, /* (cAdd {-1 (cPow [% x])}) */
|
|
/* 199 */ {{2,P2(N(2),S(74)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {-1 (cPow [% x])}) */
|
|
/* 200 */ {{2,P2(N(10),P(14)) , cAdd ,SelectedParams ,0}, 0, 0x4}, /* (cAdd {1 x}) */
|
|
/* 201 */ {{2,P2(N(10),S(74)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1 (cPow [% x])}) */
|
|
/* 202 */ {{2,P2(N(10),S(74)) , cAdd ,SelectedParams ,0}, 0, 0x5}, /* (cAdd {1 (cPow [% x])}) */
|
|
/* 203 */ {{2,P2(N(2),S(89)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {-1 (cPow [x 2])}) */
|
|
/* 204 */ {{2,P2(N(2),S(96)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {-1 (cPow [x@P z])}) */
|
|
/* 205 */ {{2,P2(N(10),S(89)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1 (cPow [x 2])}) */
|
|
/* 206 */ {{2,P2(N(10),S(96)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1 (cPow [x@P z])}) */
|
|
/* 207 */ {{2,P2(N(10),S(151)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1 (cPow [(cAdd <1>) 2])}) */
|
|
/* 208 */ {{2,P2(N(10),S(196)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1 -MIN( % 1 )}) */
|
|
/* 209 */ {{2,P2(N(10),S(261)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1 (cMul {-1 x})}) */
|
|
/* 210 */ {{2,P2(N(12),S(261)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1.57079632679 (cMul {-1 x})}) */
|
|
/* 211 */ {{2,P2(N(10),S(303)) , cAdd ,SelectedParams ,0}, 0, 0x1}, /* (cAdd {1 (cMul {(cLog [x]) /%})}) */
|
|
/* 212 */ {{2,P2(N(10),S(334)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1 (cMul -1 <2>)}) */
|
|
/* 213 */ {{2,P2(N(12),S(333)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1.57079632679 (cMul -1 <1>)}) */
|
|
/* 214 */ {{2,P2(N(12),S(335)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {1.57079632679 (cMul %@N <1>)}) */
|
|
/* 215 */ {{2,P2(P(14),P(21)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {x y}) */
|
|
/* 216 */ {{2,P2(P(21),P(28)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {y z}) */
|
|
/* 217 */ {{2,P2(P(7),S(95)) , cAdd ,SelectedParams ,0}, 0, 0x4}, /* (cAdd {%@1 (cPow [x z])}) */
|
|
/* 218 */ {{2,P2(P(1),S(196)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {% -MIN( % 1 )}) */
|
|
/* 219 */ {{2,P2(P(1),S(197)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {% -MIN( % & )}) */
|
|
/* 220 */ {{2,P2(P(9),S(197)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {& -MIN( % & )}) */
|
|
/* 221 */ {{2,P2(P(14),S(262)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {x (cMul {-1 y})}) */
|
|
/* 222 */ {{2,P2(P(21),P(31)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {y a}) */
|
|
/* 223 */ {{2,P2(P(21),S(290)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {y (cMul {z (cLog [x]) /LOG( & )})}) */
|
|
/* 224 */ {{2,P2(P(28),P(32)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {z b}) */
|
|
/* 225 */ {{2,P2(S(47),P(1)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cLog [x]) %}) */
|
|
/* 226 */ {{2,P2(S(89),N(2)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cPow [x 2]) -1}) */
|
|
/* 227 */ {{2,P2(S(280),P(3)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul {% (cPow [x@P z])})@D1 %@D1}) */
|
|
/* 228 */ {{2,P2(S(47),P(9)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cLog [x]) &}) */
|
|
/* 229 */ {{2,P2(S(84),S(85)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cPow [& y]) (cPow [& (cAdd {y (cMul {z (cLog [x]) /LOG( & )})})])}) */
|
|
/* 230 */ {{2,P2(S(147),P(15)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cPow [(cAdd {-1 (cPow [x 2])}) 0.5])@D4 x@D4}) */
|
|
/* 231 */ {{2,P2(S(280),S(195)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul {% (cPow [x@P z])})@D1 -%@D1}) */
|
|
/* 232 */ {{2,P2(S(298),S(85)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul {(cPow [& y]) -1}) (cPow [& (cAdd {y (cMul {z (cLog [x]) /LOG( & )})})])}) */
|
|
/* 233 */ {{2,P2(S(300),S(100)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul {(cPow [x y]) %}) (cPow [x (cAdd {y z})])}) */
|
|
/* 234 */ {{2,P2(S(316),S(52)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul {LOG( % ) y}) (cLog [(cMul <1>)])}) */
|
|
/* 235 */ {{2,P2(S(52),S(53)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cLog [(cMul <1>)]) LOG( % )}) */
|
|
/* 236 */ {{2,P2(S(89),N(10)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cPow [x 2]) 1}) */
|
|
/* 237 */ {{2,P2(S(317),N(10)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul {(cPow [x 2]) -1}) 1}) */
|
|
/* 238 */ {{2,P2(S(341),S(293)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul % & <1>) (cMul {& (cAdd <2>)})}) */
|
|
/* 239 */ {{2,P2(S(362),S(294)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {MUL( % & ) (cMul {& (cAdd <1>)})}) */
|
|
/* 240 */ {{2,P2(S(354),S(353)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul (cPow [x (cAdd {% -MIN( % 1 )})]) <1>) (cMul (cPow [x (cAdd {1 -MIN( % 1 )})]) <2>)}) */
|
|
/* 241 */ {{2,P2(S(355),S(356)) , cAdd ,SelectedParams ,0}, 0, 0x0}, /* (cAdd {(cMul (cPow [x (cAdd {% -MIN( % & )})]) <1>) (cMul (cPow [x (cAdd {& -MIN( % & )})]) <2>)}) */
|
|
/* 242 */ {{0,0 , cAdd ,AnyParams ,1}, 0, 0x0}, /* (cAdd <1>) */
|
|
/* 243 */ {{0,0 , cAdd ,AnyParams ,2}, 0, 0x0}, /* (cAdd <2>) */
|
|
/* 244 */ {{0,0 , cAdd ,AnyParams ,1}, Sign_Positive, 0x0}, /* (cAdd <1>)@P */
|
|
/* 245 */ {{1,P1(N(1)) , cAdd ,AnyParams ,1}, 0, 0x0}, /* (cAdd -1.57079632679 <1>) */
|
|
/* 246 */ {{1,P1(N(8)) , cAdd ,AnyParams ,1}, 0, 0x0}, /* (cAdd 0.5 <1>) */
|
|
/* 247 */ {{1,P1(P(6)) , cAdd ,AnyParams ,1}, 0, 0x0}, /* (cAdd %@M <1>) */
|
|
/* 248 */ {{1,P1(P(1)) , cAdd ,AnyParams ,1}, 0, 0x0}, /* (cAdd % <1>) */
|
|
/* 249 */ {{1,P1(P(11)) , cAdd ,AnyParams ,1}, 0, 0x0}, /* (cAdd &@M <1>) */
|
|
/* 250 */ {{1,P1(P(9)) , cAdd ,AnyParams ,2}, 0, 0x0}, /* (cAdd & <2>) */
|
|
/* 251 */ {{1,P1(P(14)) , cAdd ,AnyParams ,1}, 0, 0x4}, /* (cAdd x <1>) */
|
|
/* 252 */ {{1,P1(P(14)) , cAdd ,AnyParams ,2}, 0, 0x4}, /* (cAdd x <2>) */
|
|
/* 253 */ {{1,P1(P(14)) , cAdd ,AnyParams ,1}, 0, 0x0}, /* (cAdd x <1>) */
|
|
/* 254 */ {{2,P2(P(9),S(194)) , cAdd ,AnyParams ,2}, 0, 0x0}, /* (cAdd & -% <2>) */
|
|
/* 255 */ {{1,P1(S(123)) , cAdd ,AnyParams ,1}, 0, 0x16}, /* (cAdd (cPow [(cLog [z]) -1]) <1>) */
|
|
/* 256 */ {{1,P1(S(334)) , cAdd ,AnyParams ,1}, 0, 0x0}, /* (cAdd (cMul -1 <2>) <1>) */
|
|
/* 257 */ {{1,P1(S(338)) , cAdd ,AnyParams ,2}, 0, 0x0}, /* (cAdd (cMul %@M <1>) <2>) */
|
|
/* 258 */ {{1,P1(S(357)) , cAdd ,AnyParams ,1}, 0, 0x16}, /* (cAdd (cMul (cPow [(cLog [z]) -1]) <2>) <1>) */
|
|
/* 259 */ {{2,P2(P(1),N(10)) , cAdd ,GroupFunction ,0}, Constness_Const, 0x0}, /* ADD( % 1 ) */
|
|
/* 260 */ {{2,P2(P(9),S(194)) , cAdd ,GroupFunction ,0}, Constness_Const, 0x0}, /* ADD( & -% ) */
|
|
/* 261 */ {{2,P2(N(2),P(14)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 x}) */
|
|
/* 262 */ {{2,P2(N(2),P(21)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 y}) */
|
|
/* 263 */ {{2,P2(N(2),S(13)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cCeil [(cMul <1>)])}) */
|
|
/* 264 */ {{2,P2(N(2),S(18)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cCos [(cAdd {x (cMul {-1 y})})])}) */
|
|
/* 265 */ {{2,P2(N(2),S(19)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cCos [(cAdd <1>)])}) */
|
|
/* 266 */ {{2,P2(N(2),S(23)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cCosh [x])}) */
|
|
/* 267 */ {{2,P2(N(2),S(30)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cFloor [(cMul <1>)])}) */
|
|
/* 268 */ {{2,P2(N(2),S(83)) , cMul ,SelectedParams ,0}, 0, 0x6}, /* (cMul {-1 (cPow [& x])}) */
|
|
/* 269 */ {{2,P2(N(2),S(118)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cPow [(cCos [x]) 2])}) */
|
|
/* 270 */ {{2,P2(N(2),S(141)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cPow [(cSin [x]) 2])}) */
|
|
/* 271 */ {{2,P2(N(2),S(153)) , cMul ,SelectedParams ,0}, 0, 0x6}, /* (cMul {-1 (cPow [/& x])}) */
|
|
/* 272 */ {{2,P2(N(2),S(165)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cSin [(cMul <1>)])}) */
|
|
/* 273 */ {{2,P2(N(2),S(170)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cSinh [x])}) */
|
|
/* 274 */ {{2,P2(N(2),S(174)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cSinh [(cMul <1>)])}) */
|
|
/* 275 */ {{2,P2(N(2),S(182)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cTan [(cMul <1>)])}) */
|
|
/* 276 */ {{2,P2(N(2),S(190)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-1 (cTanh [(cMul <1>)])}) */
|
|
/* 277 */ {{2,P2(N(15),S(110)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {2.71828182846 (cPow [z (cAdd <1>)])}) */
|
|
/* 278 */ {{3,P3(P(14),N(8),S(417)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {x 0.5 /%}) */
|
|
/* 279 */ {{2,P2(P(1),P(14)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {% x}) */
|
|
/* 280 */ {{2,P2(P(1),S(96)) , cMul ,SelectedParams ,0}, 0, 0x1}, /* (cMul {% (cPow [x@P z])}) */
|
|
/* 281 */ {{2,P2(P(1),S(212)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {% (cAdd {1 (cMul -1 <2>)})}) */
|
|
/* 282 */ {{2,P2(P(1),S(256)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {% (cAdd (cMul -1 <2>) <1>)}) */
|
|
/* 283 */ {{2,P2(P(14),P(21)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {x y}) */
|
|
/* 284 */ {{2,P2(P(14),S(104)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {x (cPow [y -1])}) */
|
|
/* 285 */ {{2,P2(P(14),S(391)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {x (cAnd <1>)}) */
|
|
/* 286 */ {{3,P3(P(14),S(53),N(8)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {x LOG( % ) 0.5}) */
|
|
/* 287 */ {{2,P2(P(21),P(28)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {y z}) */
|
|
/* 288 */ {{2,P2(P(21),P(31)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {y a}) */
|
|
/* 289 */ {{2,P2(P(28),P(32)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {z b}) */
|
|
/* 290 */ {{3,P3(P(28),S(47),S(420)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {z (cLog [x]) /LOG( & )}) */
|
|
/* 291 */ {{2,P2(P(1),S(83)) , cMul ,SelectedParams ,0}, 0, 0x7}, /* (cMul {% (cPow [& x])}) */
|
|
/* 292 */ {{2,P2(P(1),S(153)) , cMul ,SelectedParams ,0}, 0, 0x7}, /* (cMul {% (cPow [/& x])}) */
|
|
/* 293 */ {{2,P2(P(9),S(243)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {& (cAdd <2>)}) */
|
|
/* 294 */ {{2,P2(P(9),S(242)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {& (cAdd <1>)}) */
|
|
/* 295 */ {{2,P2(P(14),S(54)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {x LOG( & )}) */
|
|
/* 296 */ {{2,P2(P(14),S(108)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {x (cPow [y %@N])}) */
|
|
/* 297 */ {{2,P2(S(23),N(2)) , cMul ,SelectedParams ,0}, 0, 0x4}, /* (cMul {(cCosh [x]) -1}) */
|
|
/* 298 */ {{2,P2(S(84),N(2)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cPow [& y]) -1}) */
|
|
/* 299 */ {{2,P2(S(11),N(13)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cAtanh [x]) 2}) */
|
|
/* 300 */ {{2,P2(S(105),P(1)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cPow [x y]) %}) */
|
|
/* 301 */ {{2,P2(S(194),P(14)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {-% x}) */
|
|
/* 302 */ {{2,P2(S(14),S(16)) , cMul ,SelectedParams ,0}, 0, 0x12}, /* (cMul {(cCos [x]) (cCos [y])}) */
|
|
/* 303 */ {{2,P2(S(47),S(417)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cLog [x]) /%}) */
|
|
/* 304 */ {{2,P2(S(65),N(2)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cPow [0.367879441171 x]) -1}) */
|
|
/* 305 */ {{2,P2(S(65),N(2)) , cMul ,SelectedParams ,0}, 0, 0x4}, /* (cMul {(cPow [0.367879441171 x]) -1}) */
|
|
/* 306 */ {{2,P2(S(67),N(2)) , cMul ,SelectedParams ,0}, 0, 0x4}, /* (cMul {(cPow [2.71828182846 x]) -1}) */
|
|
/* 307 */ {{2,P2(S(170),N(2)) , cMul ,SelectedParams ,0}, 0, 0x4}, /* (cMul {(cSinh [x]) -1}) */
|
|
/* 308 */ {{3,P3(S(25),N(13),P(1)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cCosh [(cMul {x LOG( & )})]) 2 %}) */
|
|
/* 309 */ {{2,P2(S(173),N(0)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cSinh [(cMul {x LOG( & )})]) -2}) */
|
|
/* 310 */ {{2,P2(S(24),N(13)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cCosh [(cLog [(cPow [& x])])]) 2}) */
|
|
/* 311 */ {{2,P2(S(171),N(13)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cSinh [(cLog [(cPow [& x])])]) 2}) */
|
|
/* 312 */ {{3,P3(S(173),N(13),P(1)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cSinh [(cMul {x LOG( & )})]) 2 %}) */
|
|
/* 313 */ {{2,P2(S(194),S(153)) , cMul ,SelectedParams ,0}, 0, 0x7}, /* (cMul {-% (cPow [/& x])}) */
|
|
/* 314 */ {{3,P3(S(14),S(16),N(2)) , cMul ,SelectedParams ,0}, 0, 0x12}, /* (cMul {(cCos [x]) (cCos [y]) -1}) */
|
|
/* 315 */ {{2,P2(S(14),S(160)) , cMul ,SelectedParams ,0}, 0, 0x12}, /* (cMul {(cCos [x]) (cSin [y])}) */
|
|
/* 316 */ {{2,P2(S(53),P(21)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {LOG( % ) y}) */
|
|
/* 317 */ {{2,P2(S(89),N(2)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cPow [x 2]) -1}) */
|
|
/* 318 */ {{2,P2(S(158),S(16)) , cMul ,SelectedParams ,0}, 0, 0x12}, /* (cMul {(cSin [x]) (cCos [y])}) */
|
|
/* 319 */ {{2,P2(S(110),S(73)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cPow [z (cAdd <1>)]) (cPow [2.71828182846 (cMul <2>)])}) */
|
|
/* 320 */ {{2,P2(S(155),S(106)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {POW( % & ) (cPow [% (cAdd <1>)])}) */
|
|
/* 321 */ {{2,P2(S(155),S(107)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {POW( % & ) (cPow [x LOG( % )])}) */
|
|
/* 322 */ {{2,P2(S(155),S(152)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {POW( % & ) (cPow [(cMul <1>) &])}) */
|
|
/* 323 */ {{2,P2(S(158),S(160)) , cMul ,SelectedParams ,0}, 0, 0x12}, /* (cMul {(cSin [x]) (cSin [y])}) */
|
|
/* 324 */ {{3,P3(S(14),S(160),N(2)) , cMul ,SelectedParams ,0}, 0, 0x12}, /* (cMul {(cCos [x]) (cSin [y]) -1}) */
|
|
/* 325 */ {{3,P3(S(158),S(160),N(2)) , cMul ,SelectedParams ,0}, 0, 0x12}, /* (cMul {(cSin [x]) (cSin [y]) -1}) */
|
|
/* 326 */ {{2,P2(S(97),S(240)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cPow [x MIN( % 1 )]) (cAdd {(cMul (cPow [x (cAdd {% -MIN( % 1 )})]) <1>) (cMul (cPow [x (cAdd {1 -MIN( % 1 )})]) <2>)})}) */
|
|
/* 327 */ {{2,P2(S(98),S(241)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cPow [x MIN( % & )]) (cAdd {(cMul (cPow [x (cAdd {% -MIN( % & )})]) <1>) (cMul (cPow [x (cAdd {& -MIN( % & )})]) <2>)})}) */
|
|
/* 328 */ {{2,P2(S(200),S(146)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cAdd {1 x})@D4 (cPow [(cAdd {1 (cMul {-1 x})}) -1])@D4}) */
|
|
/* 329 */ {{2,P2(S(375),P(21)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cNot [x]) y}) */
|
|
/* 330 */ {{2,P2(S(399),P(21)) , cMul ,SelectedParams ,0}, 0, 0x0}, /* (cMul {(cNotNot [x]) y}) */
|
|
/* 331 */ {{0,0 , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul <1>) */
|
|
/* 332 */ {{0,0 , cMul ,AnyParams ,2}, 0, 0x0}, /* (cMul <2>) */
|
|
/* 333 */ {{1,P1(N(2)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul -1 <1>) */
|
|
/* 334 */ {{1,P1(N(2)) , cMul ,AnyParams ,2}, 0, 0x0}, /* (cMul -1 <2>) */
|
|
/* 335 */ {{1,P1(P(0)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul %@N <1>) */
|
|
/* 336 */ {{1,P1(P(2)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul %@P <1>) */
|
|
/* 337 */ {{1,P1(P(2)) , cMul ,AnyParams ,1}, 0, 0x1}, /* (cMul %@P <1>) */
|
|
/* 338 */ {{1,P1(P(6)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul %@M <1>) */
|
|
/* 339 */ {{1,P1(P(1)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul % <1>) */
|
|
/* 340 */ {{1,P1(P(9)) , cMul ,AnyParams ,2}, 0, 0x0}, /* (cMul & <2>) */
|
|
/* 341 */ {{2,P2(P(1),P(9)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul % & <1>) */
|
|
/* 342 */ {{2,P2(P(9),S(417)) , cMul ,AnyParams ,2}, 0, 0x0}, /* (cMul & /% <2>) */
|
|
/* 343 */ {{1,P1(P(14)) , cMul ,AnyParams ,1}, 0, 0x4}, /* (cMul x <1>) */
|
|
/* 344 */ {{1,P1(P(14)) , cMul ,AnyParams ,2}, 0, 0x4}, /* (cMul x <2>) */
|
|
/* 345 */ {{1,P1(P(14)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul x <1>) */
|
|
/* 346 */ {{1,P1(P(14)) , cMul ,AnyParams ,2}, 0, 0x0}, /* (cMul x <2>) */
|
|
/* 347 */ {{1,P1(S(0)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul (cAbs [x]) <1>) */
|
|
/* 348 */ {{1,P1(S(47)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul (cLog [x]) <1>) */
|
|
/* 349 */ {{1,P1(S(53)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul LOG( % ) <1>) */
|
|
/* 350 */ {{1,P1(S(86)) , cMul ,AnyParams ,1}, 0, 0x4}, /* (cMul (cPow [x %@I@P]) <1>) */
|
|
/* 351 */ {{1,P1(S(87)) , cMul ,AnyParams ,2}, 0, 0x4}, /* (cMul (cPow [x &@I]) <2>) */
|
|
/* 352 */ {{1,P1(S(88)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul (cPow [% y]) <1>) */
|
|
/* 353 */ {{1,P1(S(99)) , cMul ,AnyParams ,2}, 0, 0x0}, /* (cMul (cPow [x (cAdd {1 -MIN( % 1 )})]) <2>) */
|
|
/* 354 */ {{1,P1(S(101)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul (cPow [x (cAdd {% -MIN( % 1 )})]) <1>) */
|
|
/* 355 */ {{1,P1(S(102)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul (cPow [x (cAdd {% -MIN( % & )})]) <1>) */
|
|
/* 356 */ {{1,P1(S(103)) , cMul ,AnyParams ,2}, 0, 0x0}, /* (cMul (cPow [x (cAdd {& -MIN( % & )})]) <2>) */
|
|
/* 357 */ {{1,P1(S(123)) , cMul ,AnyParams ,2}, 0, 0x0}, /* (cMul (cPow [(cLog [z]) -1]) <2>) */
|
|
/* 358 */ {{1,P1(S(194)) , cMul ,AnyParams ,1}, 0, 0x0}, /* (cMul -% <1>) */
|
|
/* 359 */ {{1,P1(S(194)) , cMul ,AnyParams ,2}, 0, 0x1}, /* (cMul -% <2>) */
|
|
/* 360 */ {{2,P2(S(123),S(47)) , cMul ,AnyParams ,1}, 0, 0x16}, /* (cMul (cPow [(cLog [z]) -1]) (cLog [x]) <1>) */
|
|
/* 361 */ {{2,P2(S(419),S(47)) , cMul ,AnyParams ,1}, 0, 0x1}, /* (cMul /LOG( % ) (cLog [x]) <1>) */
|
|
/* 362 */ {{2,P2(P(1),P(9)) , cMul ,GroupFunction ,0}, Constness_Const, 0x0}, /* MUL( % & ) */
|
|
/* 363 */ {{2,P2(P(9),S(417)) , cMul ,GroupFunction ,0}, Constness_Const, 0x0}, /* MUL( & /% ) */
|
|
/* 364 */ {{2,P2(S(54),S(419)) , cMul ,GroupFunction ,0}, Constness_Const, 0x0}, /* MUL( LOG( & ) /LOG( % ) ) */
|
|
/* 365 */ {{2,P2(P(14),P(21)) , cEqual ,PositionalParams,0}, 0, 0x12}, /* (cEqual [x y]) */
|
|
/* 366 */ {{2,P2(P(14),P(21)) , cEqual ,PositionalParams,0}, 0, 0x0}, /* (cEqual [x y]) */
|
|
/* 367 */ {{2,P2(P(14),P(28)) , cEqual ,PositionalParams,0}, 0, 0x20}, /* (cEqual [x z]) */
|
|
/* 368 */ {{2,P2(P(21),P(28)) , cEqual ,PositionalParams,0}, 0, 0x24}, /* (cEqual [y z]) */
|
|
/* 369 */ {{2,P2(P(21),P(28)) , cEqual ,PositionalParams,0}, 0, 0x0}, /* (cEqual [y z]) */
|
|
/* 370 */ {{2,P2(N(4),P(14)) , cLess ,PositionalParams,0}, 0, 0x4}, /* (cLess [0 x]) */
|
|
/* 371 */ {{2,P2(P(21),P(14)) , cLess ,PositionalParams,0}, 0, 0x0}, /* (cLess [y x]) */
|
|
/* 372 */ {{2,P2(P(14),P(21)) , cLess ,PositionalParams,0}, 0, 0x12}, /* (cLess [x y]) */
|
|
/* 373 */ {{2,P2(P(14),P(21)) , cLessOrEq ,PositionalParams,0}, 0, 0x0}, /* (cLessOrEq [x y]) */
|
|
/* 374 */ {{1,P1(P(1)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [%]) */
|
|
/* 375 */ {{1,P1(P(14)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [x]) */
|
|
/* 376 */ {{1,P1(P(21)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [y]) */
|
|
/* 377 */ {{1,P1(P(28)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [z]) */
|
|
/* 378 */ {{1,P1(S(278)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [(cMul {x 0.5 /%})]) */
|
|
/* 379 */ {{1,P1(S(385)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [(cAnd {x y})]) */
|
|
/* 380 */ {{1,P1(S(388)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [(cAnd {z (cIf [x y (cNot [%])])})]) */
|
|
/* 381 */ {{1,P1(S(389)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [(cAnd {z (cIf [x (cNot [%]) y])})]) */
|
|
/* 382 */ {{1,P1(S(392)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [(cOr {x y})]) */
|
|
/* 383 */ {{1,P1(S(395)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [(cOr {z (cIf [x y (cNot [%])])})]) */
|
|
/* 384 */ {{1,P1(S(396)) , cNot ,PositionalParams,0}, 0, 0x0}, /* (cNot [(cOr {z (cIf [x (cNot [%]) y])})]) */
|
|
/* 385 */ {{2,P2(P(14),P(21)) , cAnd ,SelectedParams ,0}, 0, 0x0}, /* (cAnd {x y}) */
|
|
/* 386 */ {{2,P2(P(21),P(31)) , cAnd ,SelectedParams ,0}, 0, 0x0}, /* (cAnd {y a}) */
|
|
/* 387 */ {{2,P2(P(28),P(32)) , cAnd ,SelectedParams ,0}, 0, 0x0}, /* (cAnd {z b}) */
|
|
/* 388 */ {{2,P2(P(28),S(35)) , cAnd ,SelectedParams ,0}, 0, 0x0}, /* (cAnd {z (cIf [x y (cNot [%])])}) */
|
|
/* 389 */ {{2,P2(P(28),S(37)) , cAnd ,SelectedParams ,0}, 0, 0x0}, /* (cAnd {z (cIf [x (cNot [%]) y])}) */
|
|
/* 390 */ {{2,P2(S(375),P(21)) , cAnd ,SelectedParams ,0}, 0, 0x0}, /* (cAnd {(cNot [x]) y}) */
|
|
/* 391 */ {{0,0 , cAnd ,AnyParams ,1}, 0, 0x0}, /* (cAnd <1>) */
|
|
/* 392 */ {{2,P2(P(14),P(21)) , cOr ,SelectedParams ,0}, 0, 0x0}, /* (cOr {x y}) */
|
|
/* 393 */ {{2,P2(P(21),P(31)) , cOr ,SelectedParams ,0}, 0, 0x0}, /* (cOr {y a}) */
|
|
/* 394 */ {{2,P2(P(28),P(32)) , cOr ,SelectedParams ,0}, 0, 0x0}, /* (cOr {z b}) */
|
|
/* 395 */ {{2,P2(P(28),S(35)) , cOr ,SelectedParams ,0}, 0, 0x0}, /* (cOr {z (cIf [x y (cNot [%])])}) */
|
|
/* 396 */ {{2,P2(P(28),S(37)) , cOr ,SelectedParams ,0}, 0, 0x0}, /* (cOr {z (cIf [x (cNot [%]) y])}) */
|
|
/* 397 */ {{2,P2(S(375),P(21)) , cOr ,SelectedParams ,0}, 0, 0x0}, /* (cOr {(cNot [x]) y}) */
|
|
/* 398 */ {{0,0 , cOr ,AnyParams ,1}, 0, 0x0}, /* (cOr <1>) */
|
|
/* 399 */ {{1,P1(P(14)) , cNotNot ,PositionalParams,0}, 0, 0x0}, /* (cNotNot [x]) */
|
|
/* 400 */ {{1,P1(P(21)) , cNotNot ,PositionalParams,0}, 0, 0x0}, /* (cNotNot [y]) */
|
|
/* 401 */ {{1,P1(S(215)) , cNotNot ,PositionalParams,0}, 0, 0x0}, /* (cNotNot [(cAdd {x y})]) */
|
|
/* 402 */ {{1,P1(S(253)) , cNotNot ,PositionalParams,0}, 0, 0x0}, /* (cNotNot [(cAdd x <1>)]) */
|
|
/* 403 */ {{1,P1(S(278)) , cNotNot ,PositionalParams,0}, 0, 0x0}, /* (cNotNot [(cMul {x 0.5 /%})]) */
|
|
/* 404 */ {{1,P1(S(285)) , cNotNot ,PositionalParams,0}, 0, 0x0}, /* (cNotNot [(cMul {x (cAnd <1>)})]) */
|
|
/* 405 */ {{1,P1(S(331)) , cDeg ,PositionalParams,0}, 0, 0x0}, /* (cDeg [(cMul <1>)]) */
|
|
/* 406 */ {{1,P1(S(331)) , cRad ,PositionalParams,0}, 0, 0x0}, /* (cRad [(cMul <1>)]) */
|
|
/* 407 */ {{3,P3(P(14),P(21),S(391)) , cAbsAnd ,SelectedParams ,0}, 0, 0x0}, /* (cAbsAnd {x y (cAnd <1>)}) */
|
|
/* 408 */ {{3,P3(P(14),P(21),S(398)) , cAbsOr ,SelectedParams ,0}, 0, 0x0}, /* (cAbsOr {x y (cOr <1>)}) */
|
|
/* 409 */ {{1,P1(P(14)) , cAbsNot ,PositionalParams,0}, 0, 0x0}, /* (cAbsNot [x]) */
|
|
/* 410 */ {{1,P1(P(14)) , cAbsNotNot ,PositionalParams,0}, 0, 0x0}, /* (cAbsNotNot [x]) */
|
|
/* 411 */ {{1,P1(P(21)) , cAbsNotNot ,PositionalParams,0}, 0, 0x0}, /* (cAbsNotNot [y]) */
|
|
/* 412 */ {{1,P1(P(28)) , cAbsNotNot ,PositionalParams,0}, 0, 0x0}, /* (cAbsNotNot [z]) */
|
|
/* 413 */ {{3,P3(P(14),N(10),S(411)) , cAbsIf ,PositionalParams,0}, 0, 0x0}, /* (cAbsIf [x 1 (cAbsNotNot [y])]) */
|
|
/* 414 */ {{3,P3(P(14),P(21),P(28)) , cAbsIf ,PositionalParams,0}, 0, 0x0}, /* (cAbsIf [x y z]) */
|
|
/* 415 */ {{3,P3(P(14),S(331),N(4)) , cAbsIf ,PositionalParams,0}, 0, 0x0}, /* (cAbsIf [x (cMul <1>) 0]) */
|
|
/* 416 */ {{3,P3(P(14),S(411),N(4)) , cAbsIf ,PositionalParams,0}, 0, 0x0}, /* (cAbsIf [x (cAbsNotNot [y]) 0]) */
|
|
/* 417 */ {{1,P1(P(1)) , cInv ,GroupFunction ,0}, Constness_Const, 0x0}, /* /% */
|
|
/* 418 */ {{1,P1(P(9)) , cInv ,GroupFunction ,0}, Constness_Const, 0x0}, /* /& */
|
|
/* 419 */ {{1,P1(S(53)) , cInv ,GroupFunction ,0}, Constness_Const, 0x0}, /* /LOG( % ) */
|
|
/* 420 */ {{1,P1(S(54)) , cInv ,GroupFunction ,0}, Constness_Const, 0x0}, /* /LOG( & ) */
|
|
/* 421 */ {{1,P1(S(64)) , cInv ,GroupFunction ,0}, Constness_Const, 0x0}, /* /MIN( % & ) */
|
|
},
|
|
|
|
};
|
|
}
|
|
namespace FPoptimizer_Grammar
|
|
{
|
|
const Rule grammar_rules[241] =
|
|
{
|
|
/* 0: @L (cAbs [x])
|
|
* -> x
|
|
*/ {ProduceNewTree, true , 1,P1(P(14)) , {1,P1(P(14)) , cAbs ,PositionalParams,0}},
|
|
/* 1: (cAtan [(cMul {x (cPow [y %@N])})])
|
|
* -> (cAtan2 [x (cPow [y -%])])
|
|
*/ {ProduceNewTree, false, 1,P1(S(10)) , {1,P1(S(296)) , cAtan ,PositionalParams,0}},
|
|
/* 2: (cAtan2 [(cPow [(cAdd {(cMul {(cPow [x 2]) -1}) 1}) 0.5])@D4 x@D4])
|
|
* -> (cAcos [x])
|
|
*/ {ProduceNewTree, false, 1,P1(S(3)) , {2,P2(S(150),P(15)) , cAtan2 ,PositionalParams,0}},
|
|
/* 3: (cAtan2 [x@D4 (cPow [(cAdd {(cMul {(cPow [x 2]) -1}) 1}) 0.5])@D4])
|
|
* -> (cAsin [x])
|
|
*/ {ProduceNewTree, false, 1,P1(S(5)) , {2,P2(P(15),S(150)) , cAtan2 ,PositionalParams,0}},
|
|
/* 4: (cAtan2 [(cMul x <1>)@D4 (cMul x <2>)@D4])
|
|
* : (cMul <1>) (cMul <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(332)) , {2,P2(S(343),S(344)) , cAtan2 ,PositionalParams,0}},
|
|
/* 5: (cCeil [x@I])
|
|
* -> x
|
|
*/ {ProduceNewTree, false, 1,P1(P(14)) , {1,P1(P(16)) , cCeil ,PositionalParams,0}},
|
|
/* 6: (cCeil [(cMul -1 <1>)])
|
|
* -> (cMul {-1 (cFloor [(cMul <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(267)) , {1,P1(S(333)) , cCeil ,PositionalParams,0}},
|
|
/* 7: (cCos [(cAdd {1.57079632679 (cMul %@N <1>)})])
|
|
* -> (cSin [(cMul -% <1>)])
|
|
*/ {ProduceNewTree, false, 1,P1(S(168)) , {1,P1(S(214)) , cCos ,PositionalParams,0}},
|
|
/* 8: (cCos [(cAdd -1.57079632679 <1>)])
|
|
* -> (cSin [(cAdd <1>)])
|
|
*/ {ProduceNewTree, false, 1,P1(S(163)) , {1,P1(S(245)) , cCos ,PositionalParams,0}},
|
|
/* 9: (cCos [(cAcos [x])])
|
|
* -> x
|
|
*/ {ProduceNewTree, false, 1,P1(P(14)) , {1,P1(S(3)) , cCos ,PositionalParams,0}},
|
|
/* 10: (cCos [(cAbs [x])])
|
|
* : x
|
|
*/ {ReplaceParams , false, 1,P1(P(14)) , {1,P1(S(0)) , cCos ,PositionalParams,0}},
|
|
/* 11: (cCos [(cMul -1 <1>)])
|
|
* : (cMul <1>)
|
|
*/ {ReplaceParams , false, 1,P1(S(331)) , {1,P1(S(333)) , cCos ,PositionalParams,0}},
|
|
/* 12: (cCosh [(cAsinh [x])])
|
|
* -> (cPow [(cAdd {(cPow [x 2]) 1}) 0.5])
|
|
*/ {ProduceNewTree, false, 1,P1(S(149)) , {1,P1(S(6)) , cCosh ,PositionalParams,0}},
|
|
/* 13: (cCosh [(cAbs [x])])
|
|
* : x
|
|
*/ {ReplaceParams , false, 1,P1(P(14)) , {1,P1(S(0)) , cCosh ,PositionalParams,0}},
|
|
/* 14: (cCosh [(cMul -1 <1>)])
|
|
* : (cMul <1>)
|
|
*/ {ReplaceParams , false, 1,P1(S(331)) , {1,P1(S(333)) , cCosh ,PositionalParams,0}},
|
|
/* 15: (cFloor [x@I])
|
|
* -> x
|
|
*/ {ProduceNewTree, false, 1,P1(P(14)) , {1,P1(P(16)) , cFloor ,PositionalParams,0}},
|
|
/* 16: (cFloor [(cMul -1 <1>)])
|
|
* -> (cMul {-1 (cCeil [(cMul <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(263)) , {1,P1(S(333)) , cFloor ,PositionalParams,0}},
|
|
/* 17: (cFloor [(cAdd 0.5 <1>)])
|
|
* -> (cInt [(cAdd <1>)])
|
|
*/ {ProduceNewTree, false, 1,P1(S(46)) , {1,P1(S(246)) , cFloor ,PositionalParams,0}},
|
|
/* 18: (cIf [x 0 y])
|
|
* -> (cMul {(cNot [x]) y})
|
|
*/ {ProduceNewTree, false, 1,P1(S(329)) , {3,P3(P(14),N(4),P(21)) , cIf ,PositionalParams,0}},
|
|
/* 19: (cIf [x@P y z])
|
|
* -> (cAbsIf [x y z])
|
|
*/ {ProduceNewTree, false, 1,P1(S(414)) , {3,P3(P(17),P(21),P(28)) , cIf ,PositionalParams,0}},
|
|
/* 20: (cIf [x 0 y@L])
|
|
* -> (cAnd {(cNot [x]) y})
|
|
*/ {ProduceNewTree, false, 1,P1(S(390)) , {3,P3(P(14),N(4),P(22)) , cIf ,PositionalParams,0}},
|
|
/* 21: (cIf [x 1 y@L])
|
|
* -> (cOr {x y})
|
|
*/ {ProduceNewTree, false, 1,P1(S(392)) , {3,P3(P(14),N(10),P(22)) , cIf ,PositionalParams,0}},
|
|
/* 22: (cIf [x y 0])
|
|
* -> (cMul {(cNotNot [x]) y})
|
|
*/ {ProduceNewTree, false, 1,P1(S(330)) , {3,P3(P(14),P(21),N(4)) , cIf ,PositionalParams,0}},
|
|
/* 23: (cIf [x y@L 0])
|
|
* -> (cAnd {x y})
|
|
*/ {ProduceNewTree, false, 1,P1(S(385)) , {3,P3(P(14),P(22),N(4)) , cIf ,PositionalParams,0}},
|
|
/* 24: (cIf [x y@L 1])
|
|
* -> (cOr {(cNot [x]) y})
|
|
*/ {ProduceNewTree, false, 1,P1(S(397)) , {3,P3(P(14),P(22),N(10)) , cIf ,PositionalParams,0}},
|
|
/* 25: (cIf [(cLess [x y])@D12 y@D8 x@D4])
|
|
* -> (cMax [x y])
|
|
*/ {ProduceNewTree, false, 1,P1(S(57)) , {3,P3(S(372),P(23),P(15)) , cIf ,PositionalParams,0}},
|
|
/* 26: (cIf [(cLess [x y])@D12 x@D4 y@D8])
|
|
* -> (cMin [x y])
|
|
*/ {ProduceNewTree, false, 1,P1(S(60)) , {3,P3(S(372),P(15),P(23)) , cIf ,PositionalParams,0}},
|
|
/* 27: (cIf [(cLess [0 x])@D4 (cFloor [x])@D4 (cCeil [x])@D4])
|
|
* -> (cTrunc [x])
|
|
*/ {ProduceNewTree, false, 1,P1(S(193)) , {3,P3(S(370),S(29),S(12)) , cIf ,PositionalParams,0}},
|
|
/* 28: (cIf [(cLessOrEq [x y]) z a])
|
|
* : (cLess [y x]) a z
|
|
*/ {ReplaceParams , false, 3,P3(S(371),P(31),P(28)) , {3,P3(S(373),P(28),P(31)) , cIf ,PositionalParams,0}},
|
|
/* 29: @L (cIf [x (cAbsNotNot [y]) z])
|
|
* : x y z
|
|
*/ {ReplaceParams , true , 3,P3(P(14),P(21),P(28)) , {3,P3(P(14),S(411),P(28)) , cIf ,PositionalParams,0}},
|
|
/* 30: @L (cIf [x y (cAbsNotNot [z])])
|
|
* : x y z
|
|
*/ {ReplaceParams , true , 3,P3(P(14),P(21),P(28)) , {3,P3(P(14),P(21),S(412)) , cIf ,PositionalParams,0}},
|
|
/* 31: (cInt [x@I])
|
|
* -> x
|
|
*/ {ProduceNewTree, false, 1,P1(P(14)) , {1,P1(P(16)) , cInt ,PositionalParams,0}},
|
|
/* 32: (cLog [(cMul %@P <1>)])
|
|
* -> (cAdd {(cLog [(cMul <1>)]) LOG( % )})
|
|
*/ {ProduceNewTree, false, 1,P1(S(235)) , {1,P1(S(336)) , cLog ,PositionalParams,0}},
|
|
/* 33: (cLog [(cMul (cPow [% y]) <1>)])
|
|
* -> (cAdd {(cMul {LOG( % ) y}) (cLog [(cMul <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(234)) , {1,P1(S(352)) , cLog ,PositionalParams,0}},
|
|
/* 34: (cLog [(cAdd {(cPow [(cAdd {-1 (cPow [x 2])}) 0.5])@D4 x@D4})])
|
|
* -> (cAcosh [x])
|
|
*/ {ProduceNewTree, false, 1,P1(S(4)) , {1,P1(S(230)) , cLog ,PositionalParams,0}},
|
|
/* 35: (cLog [(cMul {(cAdd {1 x})@D4 (cPow [(cAdd {1 (cMul {-1 x})}) -1])@D4})])
|
|
* -> (cMul {(cAtanh [x]) 2})
|
|
*/ {ProduceNewTree, false, 1,P1(S(299)) , {1,P1(S(328)) , cLog ,PositionalParams,0}},
|
|
/* 36: (cMax x@D4 x@D4)
|
|
* : x
|
|
*/ {ReplaceParams , false, 1,P1(P(14)) , {2,P2(P(15),P(15)) , cMax ,AnyParams ,0}},
|
|
/* 37: (cMax (cIf [x y z])@D4 (cIf [x a b])@D4)
|
|
* : (cIf [x (cMax [y a]) (cMax [z b])])
|
|
*/ {ReplaceParams , false, 1,P1(S(39)) , {2,P2(S(32),S(36)) , cMax ,AnyParams ,0}},
|
|
/* 38: (cMin x@D4 x@D4)
|
|
* : x
|
|
*/ {ReplaceParams , false, 1,P1(P(14)) , {2,P2(P(15),P(15)) , cMin ,AnyParams ,0}},
|
|
/* 39: (cMin (cIf [x y z])@D4 (cIf [x a b])@D4)
|
|
* : (cIf [x (cMin [y a]) (cMin [z b])])
|
|
*/ {ReplaceParams , false, 1,P1(S(40)) , {2,P2(S(32),S(36)) , cMin ,AnyParams ,0}},
|
|
/* 40: (cPow [(cMul %@P <1>) &])
|
|
* -> (cMul {POW( % & ) (cPow [(cMul <1>) &])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(322)) , {2,P2(S(336),P(9)) , cPow ,PositionalParams,0}},
|
|
/* 41: (cPow [% (cAdd {(cLog [x]) &})])
|
|
* -> (cMul {POW( % & ) (cPow [x LOG( % )])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(321)) , {2,P2(P(1),S(228)) , cPow ,PositionalParams,0}},
|
|
/* 42: (cPow [(cMul %@N <1>) &@E])
|
|
* -> (cMul {POW( % & ) (cPow [(cMul <1>) &])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(322)) , {2,P2(S(335),P(10)) , cPow ,PositionalParams,0}},
|
|
/* 43: (cPow [z@D16 (cAdd (cMul (cPow [(cLog [z]) -1]) <2>) <1>)@D16])
|
|
* -> (cMul {(cPow [z (cAdd <1>)]) (cPow [2.71828182846 (cMul <2>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(319)) , {2,P2(P(29),S(258)) , cPow ,PositionalParams,0}},
|
|
/* 44: (cPow [z@D16 (cAdd (cPow [(cLog [z]) -1]) <1>)@D16])
|
|
* -> (cMul {2.71828182846 (cPow [z (cAdd <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(277)) , {2,P2(P(29),S(255)) , cPow ,PositionalParams,0}},
|
|
/* 45: (cPow [% (cAdd &@M <1>)])
|
|
* -> (cMul {POW( % & ) (cPow [% (cAdd <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(320)) , {2,P2(P(1),S(249)) , cPow ,PositionalParams,0}},
|
|
/* 46: (cPow [(cPow [x y@O]) z])
|
|
* : x (cMul {y z})
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(287)) , {2,P2(S(90),P(28)) , cPow ,PositionalParams,0}},
|
|
/* 47: (cPow [(cPow [x y@F]) z])
|
|
* : x (cMul {y z})
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(287)) , {2,P2(S(91),P(28)) , cPow ,PositionalParams,0}},
|
|
/* 48: (cPow [(cPow [x@P y]) z])
|
|
* : x (cMul {y z})
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(287)) , {2,P2(S(92),P(28)) , cPow ,PositionalParams,0}},
|
|
/* 49: (cPow [(cPow [x y])@P z])
|
|
* : (cAbs [x]) (cMul {y z})
|
|
*/ {ReplaceParams , false, 2,P2(S(0),S(287)) , {2,P2(S(93),P(28)) , cPow ,PositionalParams,0}},
|
|
/* 50: (cPow [(cSin [x]) %@N])
|
|
* : (cCsc [x]) -%
|
|
*/ {ReplaceParams , false, 2,P2(S(28),S(194)) , {2,P2(S(158),P(0)) , cPow ,PositionalParams,0}},
|
|
/* 51: (cPow [(cCos [x]) %@N])
|
|
* : (cSec [x]) -%
|
|
*/ {ReplaceParams , false, 2,P2(S(157),S(194)) , {2,P2(S(14),P(0)) , cPow ,PositionalParams,0}},
|
|
/* 52: (cPow [(cTan [x]) %@N])
|
|
* : (cCot [x]) -%
|
|
*/ {ReplaceParams , false, 2,P2(S(27),S(194)) , {2,P2(S(177),P(0)) , cPow ,PositionalParams,0}},
|
|
/* 53: (cPow [% (cLog [x])])
|
|
* : x LOG( % )
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(53)) , {2,P2(P(1),S(47)) , cPow ,PositionalParams,0}},
|
|
/* 54: (cPow [% (cMul (cLog [x]) <1>)])
|
|
* : x (cMul LOG( % ) <1>)
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(349)) , {2,P2(P(1),S(348)) , cPow ,PositionalParams,0}},
|
|
/* 55: (cPow [z@D16 (cMul (cPow [(cLog [z]) -1]) (cLog [x]) <1>)@D16])
|
|
* : x (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(331)) , {2,P2(P(29),S(360)) , cPow ,PositionalParams,0}},
|
|
/* 56: (cPow [%@D1 (cMul /LOG( % ) (cLog [x]) <1>)@D1])
|
|
* : x (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(331)) , {2,P2(P(3),S(361)) , cPow ,PositionalParams,0}},
|
|
/* 57: (cPow [(cPow [x y]) z@I])
|
|
* : x (cMul {y z})
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(287)) , {2,P2(S(105),P(30)) , cPow ,PositionalParams,0}},
|
|
/* 58: (cPow [(cAbs [x]) y@E])
|
|
* : x y
|
|
*/ {ReplaceParams , false, 2,P2(P(14),P(21)) , {2,P2(S(0),P(26)) , cPow ,PositionalParams,0}},
|
|
/* 59: (cPow [(cMul (cAbs [x]) <1>) y@E])
|
|
* : (cMul x <1>) y
|
|
*/ {ReplaceParams , false, 2,P2(S(345),P(21)) , {2,P2(S(347),P(26)) , cPow ,PositionalParams,0}},
|
|
/* 60: (cSin [(cMul -1 <1>)])
|
|
* -> (cMul {-1 (cSin [(cMul <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(272)) , {1,P1(S(333)) , cSin ,PositionalParams,0}},
|
|
/* 61: (cSin [(cAdd {1.57079632679 (cMul %@N <1>)})])
|
|
* -> (cCos [(cMul -% <1>)])
|
|
*/ {ProduceNewTree, false, 1,P1(S(21)) , {1,P1(S(214)) , cSin ,PositionalParams,0}},
|
|
/* 62: (cSin [(cAdd -1.57079632679 <1>)])
|
|
* -> (cMul {-1 (cCos [(cAdd <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(265)) , {1,P1(S(245)) , cSin ,PositionalParams,0}},
|
|
/* 63: (cSin [(cAsin [x])])
|
|
* -> x
|
|
*/ {ProduceNewTree, false, 1,P1(P(14)) , {1,P1(S(5)) , cSin ,PositionalParams,0}},
|
|
/* 64: (cSinh [(cMul -1 <1>)])
|
|
* -> (cMul {-1 (cSinh [(cMul <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(274)) , {1,P1(S(333)) , cSinh ,PositionalParams,0}},
|
|
/* 65: (cSinh [(cAcosh [x])])
|
|
* -> (cPow [(cAdd {(cPow [x 2]) -1}) 0.5])
|
|
*/ {ProduceNewTree, false, 1,P1(S(148)) , {1,P1(S(4)) , cSinh ,PositionalParams,0}},
|
|
/* 66: (cTan [(cMul -1 <1>)])
|
|
* -> (cMul {-1 (cTan [(cMul <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(275)) , {1,P1(S(333)) , cTan ,PositionalParams,0}},
|
|
/* 67: (cTan [(cAtan [x])])
|
|
* -> x
|
|
*/ {ProduceNewTree, false, 1,P1(P(14)) , {1,P1(S(8)) , cTan ,PositionalParams,0}},
|
|
/* 68: (cTan [(cAtan2 [x y])])
|
|
* -> (cMul {x (cPow [y -1])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(284)) , {1,P1(S(9)) , cTan ,PositionalParams,0}},
|
|
/* 69: (cTanh [(cMul -1 <1>)])
|
|
* -> (cMul {-1 (cTanh [(cMul <1>)])})
|
|
*/ {ProduceNewTree, false, 1,P1(S(276)) , {1,P1(S(333)) , cTanh ,PositionalParams,0}},
|
|
/* 70: (cTrunc [x@I])
|
|
* -> x
|
|
*/ {ProduceNewTree, false, 1,P1(P(14)) , {1,P1(P(16)) , cTrunc ,PositionalParams,0}},
|
|
/* 71: (cAdd (cPow [(cAdd {1 (cPow [(cAdd <1>) 2])}) 0.5]) <1>)
|
|
* -> (cPow [2.71828182846 (cAsinh [(cAdd <1>)])])
|
|
*/ {ProduceNewTree, false, 1,P1(S(72)) , {1,P1(S(145)) , cAdd ,AnyParams ,1}},
|
|
/* 72: (cAdd (cPow [(cAdd {1 (cPow [(cAdd <1>) 2])}) -0.5]) <1>)
|
|
* -> (cPow [0.367879441171 (cAsinh [(cAdd <1>)])])
|
|
*/ {ProduceNewTree, false, 1,P1(S(71)) , {1,P1(S(144)) , cAdd ,AnyParams ,1}},
|
|
/* 73: (cAdd (cPow [(cAdd {1 (cPow [x 2])}) 0.5])@D4 x@D4)
|
|
* : (cPow [2.71828182846 (cAsinh [x])])
|
|
*/ {ReplaceParams , false, 1,P1(S(70)) , {2,P2(S(143),P(15)) , cAdd ,AnyParams ,0}},
|
|
/* 74: (cAdd (cPow [(cAdd {1 (cPow [x 2])}) -0.5])@D4 x@D4)
|
|
* : (cPow [0.367879441171 (cAsinh [x])])
|
|
*/ {ReplaceParams , false, 1,P1(S(69)) , {2,P2(S(142),P(15)) , cAdd ,AnyParams ,0}},
|
|
/* 75: (cAdd (cIf [x y z])@D4 (cIf [x a b])@D4)
|
|
* : (cIf [x (cAdd {y a}) (cAdd {z b})])
|
|
*/ {ReplaceParams , false, 1,P1(S(41)) , {2,P2(S(32),S(36)) , cAdd ,AnyParams ,0}},
|
|
/* 76: (cAdd (cLog [x]) (cLog [y]))
|
|
* : (cLog [(cMul {x y})])
|
|
*/ {ReplaceParams , false, 1,P1(S(51)) , {2,P2(S(47),S(48)) , cAdd ,AnyParams ,0}},
|
|
/* 77: (cAdd (cMul (cPow [x %@I@P]) <1>)@D4 (cMul (cPow [x &@I]) <2>)@D4)
|
|
* : (cMul {(cPow [x MIN( % & )]) (cAdd {(cMul (cPow [x (cAdd {% -MIN( % & )})]) <1>) (cMul (cPow [x (cAdd {& -MIN( % & )})]) <2>)})})
|
|
*/ {ReplaceParams , false, 1,P1(S(327)) , {2,P2(S(350),S(351)) , cAdd ,AnyParams ,0}},
|
|
/* 78: (cAdd (cMul (cPow [x %@I@P]) <1>)@D4 (cMul x <2>)@D4)
|
|
* : (cMul {(cPow [x MIN( % 1 )]) (cAdd {(cMul (cPow [x (cAdd {% -MIN( % 1 )})]) <1>) (cMul (cPow [x (cAdd {1 -MIN( % 1 )})]) <2>)})})
|
|
*/ {ReplaceParams , false, 1,P1(S(326)) , {2,P2(S(350),S(344)) , cAdd ,AnyParams ,0}},
|
|
/* 79: (cAdd (cMul %@P <1>)@D1 (cMul -% <2>)@D1)
|
|
* : (cMul {% (cAdd (cMul -1 <2>) <1>)})
|
|
*/ {ReplaceParams , false, 1,P1(S(282)) , {2,P2(S(337),S(359)) , cAdd ,AnyParams ,0}},
|
|
/* 80: (cAdd %@M@D1 (cMul -% <2>)@D1)
|
|
* : (cMul {% (cAdd {1 (cMul -1 <2>)})})
|
|
*/ {ReplaceParams , false, 1,P1(S(281)) , {2,P2(P(5),S(359)) , cAdd ,AnyParams ,0}},
|
|
/* 81: (cAdd (cPow [(cSin [x]) 2])@D4 (cPow [(cCos [x]) 2])@D4)
|
|
* : 1
|
|
*/ {ReplaceParams , false, 1,P1(N(10)) , {2,P2(S(140),S(117)) , cAdd ,AnyParams ,0}},
|
|
/* 82: (cAdd 1 (cMul {-1 (cPow [(cSin [x]) 2])}))
|
|
* : (cPow [(cCos [x]) 2])
|
|
*/ {ReplaceParams , false, 1,P1(S(118)) , {2,P2(N(10),S(270)) , cAdd ,AnyParams ,0}},
|
|
/* 83: (cAdd 1 (cMul {-1 (cPow [(cCos [x]) 2])}))
|
|
* : (cPow [(cSin [x]) 2])
|
|
*/ {ReplaceParams , false, 1,P1(S(141)) , {2,P2(N(10),S(269)) , cAdd ,AnyParams ,0}},
|
|
/* 84: (cAdd (cMul {(cSin [x]) (cCos [y])})@D12 (cMul {(cCos [x]) (cSin [y])})@D12)
|
|
* : (cSin [(cAdd {x y})])
|
|
*/ {ReplaceParams , false, 1,P1(S(161)) , {2,P2(S(318),S(315)) , cAdd ,AnyParams ,0}},
|
|
/* 85: (cAdd (cMul {(cSin [x]) (cCos [y])})@D12 (cMul {(cCos [x]) (cSin [y]) -1})@D12)
|
|
* : (cSin [(cAdd {x (cMul {-1 y})})])
|
|
*/ {ReplaceParams , false, 1,P1(S(162)) , {2,P2(S(318),S(324)) , cAdd ,AnyParams ,0}},
|
|
/* 86: (cAdd (cMul {(cCos [x]) (cCos [y])})@D12 (cMul {(cSin [x]) (cSin [y])})@D12)
|
|
* : (cCos [(cAdd {x y})])
|
|
*/ {ReplaceParams , false, 1,P1(S(17)) , {2,P2(S(302),S(323)) , cAdd ,AnyParams ,0}},
|
|
/* 87: (cAdd (cMul {(cCos [x]) (cCos [y]) -1})@D12 (cMul {(cSin [x]) (cSin [y])})@D12)
|
|
* : (cMul {-1 (cCos [(cAdd {x (cMul {-1 y})})])})
|
|
*/ {ReplaceParams , false, 1,P1(S(264)) , {2,P2(S(314),S(323)) , cAdd ,AnyParams ,0}},
|
|
/* 88: (cAdd (cMul {(cCos [x]) (cCos [y])})@D12 (cMul {(cSin [x]) (cSin [y]) -1})@D12)
|
|
* : (cCos [(cAdd {x (cMul {-1 y})})])
|
|
*/ {ReplaceParams , false, 1,P1(S(18)) , {2,P2(S(302),S(325)) , cAdd ,AnyParams ,0}},
|
|
/* 89: (cAdd (cPow [& x])@D6 (cMul {-1 (cPow [/& x])})@D6)
|
|
* : (cMul {(cSinh [(cLog [(cPow [& x])])]) 2})
|
|
*/ {ReplaceParams , false, 1,P1(S(311)) , {2,P2(S(82),S(271)) , cAdd ,AnyParams ,0}},
|
|
/* 90: (cAdd (cPow [& x])@D6 (cPow [/& x])@D6)
|
|
* : (cMul {(cCosh [(cLog [(cPow [& x])])]) 2})
|
|
*/ {ReplaceParams , false, 1,P1(S(310)) , {2,P2(S(82),S(154)) , cAdd ,AnyParams ,0}},
|
|
/* 91: (cAdd (cMul {-1 (cPow [& x])})@D6 (cPow [/& x])@D6)
|
|
* : (cMul {(cSinh [(cMul {x LOG( & )})]) -2})
|
|
*/ {ReplaceParams , false, 1,P1(S(309)) , {2,P2(S(268),S(154)) , cAdd ,AnyParams ,0}},
|
|
/* 92: (cAdd (cMul {% (cPow [& x])})@D7 (cMul {-% (cPow [/& x])})@D7)
|
|
* : (cMul {(cSinh [(cMul {x LOG( & )})]) 2 %})
|
|
*/ {ReplaceParams , false, 1,P1(S(312)) , {2,P2(S(291),S(313)) , cAdd ,AnyParams ,0}},
|
|
/* 93: (cAdd (cMul {% (cPow [& x])})@D7 (cMul {% (cPow [/& x])})@D7)
|
|
* : (cMul {(cCosh [(cMul {x LOG( & )})]) 2 %})
|
|
*/ {ReplaceParams , false, 1,P1(S(308)) , {2,P2(S(291),S(292)) , cAdd ,AnyParams ,0}},
|
|
/* 94: (cAdd (cCosh [x])@D4 (cSinh [x])@D4)
|
|
* : (cPow [2.71828182846 x])
|
|
*/ {ReplaceParams , false, 1,P1(S(67)) , {2,P2(S(22),S(169)) , cAdd ,AnyParams ,0}},
|
|
/* 95: (cAdd (cMul {(cCosh [x]) -1})@D4 (cSinh [x])@D4)
|
|
* : (cMul {(cPow [0.367879441171 x]) -1})
|
|
*/ {ReplaceParams , false, 1,P1(S(304)) , {2,P2(S(297),S(169)) , cAdd ,AnyParams ,0}},
|
|
/* 96: (cAdd (cCosh [x])@D4 (cMul {(cPow [2.71828182846 x]) -1})@D4)
|
|
* : (cMul {-1 (cSinh [x])})
|
|
*/ {ReplaceParams , false, 1,P1(S(273)) , {2,P2(S(22),S(306)) , cAdd ,AnyParams ,0}},
|
|
/* 97: (cAdd (cSinh [x])@D4 (cMul {(cPow [2.71828182846 x]) -1})@D4)
|
|
* : (cMul {-1 (cCosh [x])})
|
|
*/ {ReplaceParams , false, 1,P1(S(266)) , {2,P2(S(169),S(306)) , cAdd ,AnyParams ,0}},
|
|
/* 98: (cAdd (cCosh [x])@D4 (cMul {(cSinh [x]) -1})@D4)
|
|
* : (cPow [0.367879441171 x])
|
|
*/ {ReplaceParams , false, 1,P1(S(65)) , {2,P2(S(22),S(307)) , cAdd ,AnyParams ,0}},
|
|
/* 99: (cAdd (cMul {(cSinh [x]) -1})@D4 (cPow [2.71828182846 x])@D4)
|
|
* : (cCosh [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(23)) , {2,P2(S(307),S(68)) , cAdd ,AnyParams ,0}},
|
|
/* 100: (cAdd (cMul {(cCosh [x]) -1})@D4 (cPow [2.71828182846 x])@D4)
|
|
* : (cSinh [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(170)) , {2,P2(S(297),S(68)) , cAdd ,AnyParams ,0}},
|
|
/* 101: (cAdd (cCosh [x])@D4 (cMul {(cPow [0.367879441171 x]) -1})@D4)
|
|
* : (cSinh [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(170)) , {2,P2(S(22),S(305)) , cAdd ,AnyParams ,0}},
|
|
/* 102: (cAdd (cSinh [x])@D4 (cPow [0.367879441171 x])@D4)
|
|
* : (cCosh [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(23)) , {2,P2(S(169),S(66)) , cAdd ,AnyParams ,0}},
|
|
/* 103: (cAdd (cMul {(cCosh [x]) -1})@D4 (cPow [0.367879441171 x])@D4)
|
|
* : (cMul {-1 (cSinh [x])})
|
|
*/ {ReplaceParams , false, 1,P1(S(273)) , {2,P2(S(297),S(66)) , cAdd ,AnyParams ,0}},
|
|
/* 104: (cMul {%@D1 (cAdd {1 (cMul {(cLog [x]) /%})})@D1})
|
|
* -> (cAdd {(cLog [x]) %})
|
|
*/ {ProduceNewTree, false, 1,P1(S(225)) , {2,P2(P(3),S(211)) , cMul ,SelectedParams ,0}},
|
|
/* 105: (cMul x@L <1>)
|
|
* -> (cAbsIf [x (cMul <1>) 0])
|
|
*/ {ProduceNewTree, false, 1,P1(S(415)) , {1,P1(P(19)) , cMul ,AnyParams ,1}},
|
|
/* 106: (cMul 57.2957795131 <1>)
|
|
* -> (cDeg [(cMul <1>)])
|
|
*/ {ProduceNewTree, false, 1,P1(S(405)) , {1,P1(N(16)) , cMul ,AnyParams ,1}},
|
|
/* 107: (cMul 0.0174532925199 <1>)
|
|
* -> (cRad [(cMul <1>)])
|
|
*/ {ProduceNewTree, false, 1,P1(S(406)) , {1,P1(N(5)) , cMul ,AnyParams ,1}},
|
|
/* 108: (cMul (cPow [(cMul x <2>) -1])@D4 x@D4)
|
|
* : (cPow [(cMul <2>) -1])
|
|
*/ {ReplaceParams , false, 1,P1(S(138)) , {2,P2(S(139),P(15)) , cMul ,AnyParams ,0}},
|
|
/* 109: (cMul (cIf [x y z])@D4 (cIf [x a b])@D4)
|
|
* : (cIf [x (cMul {y a}) (cMul {z b})])
|
|
*/ {ReplaceParams , false, 1,P1(S(42)) , {2,P2(S(32),S(36)) , cMul ,AnyParams ,0}},
|
|
/* 110: (cMul (cNot [x]) (cNot [y]))
|
|
* : (cNot [(cOr {x y})])
|
|
*/ {ReplaceParams , false, 1,P1(S(382)) , {2,P2(S(375),S(376)) , cMul ,AnyParams ,0}},
|
|
/* 111: (cMul (cNotNot [x]) (cNotNot [y]))
|
|
* : (cAnd {x y})
|
|
*/ {ReplaceParams , false, 1,P1(S(385)) , {2,P2(S(399),S(400)) , cMul ,AnyParams ,0}},
|
|
/* 112: (cMul (cNot [x]) (cNotNot [y]))
|
|
* : (cAnd {(cNot [x]) y})
|
|
*/ {ReplaceParams , false, 1,P1(S(390)) , {2,P2(S(375),S(400)) , cMul ,AnyParams ,0}},
|
|
/* 113: (cMul (cAbs [x]) (cAbs [y]))
|
|
* : (cAbs [(cMul {x y})])
|
|
*/ {ReplaceParams , false, 1,P1(S(2)) , {2,P2(S(0),S(1)) , cMul ,AnyParams ,0}},
|
|
/* 114: (cMul (cAdd (cMul %@M <1>) <2>) &)
|
|
* : (cAdd {(cMul % & <1>) (cMul {& (cAdd <2>)})})
|
|
*/ {ReplaceParams , false, 1,P1(S(238)) , {2,P2(S(257),P(9)) , cMul ,AnyParams ,0}},
|
|
/* 115: (cMul (cAdd %@M <1>) &)
|
|
* : (cAdd {MUL( % & ) (cMul {& (cAdd <1>)})})
|
|
*/ {ReplaceParams , false, 1,P1(S(239)) , {2,P2(S(247),P(9)) , cMul ,AnyParams ,0}},
|
|
/* 116: (cMul (cPow [x y])@D4 (cAdd {%@1 (cPow [x z])})@D4)
|
|
* : (cAdd {(cMul {(cPow [x y]) %}) (cPow [x (cAdd {y z})])})
|
|
*/ {ReplaceParams , false, 1,P1(S(233)) , {2,P2(S(94),S(217)) , cMul ,AnyParams ,0}},
|
|
/* 117: (cMul (cPow [& y]) (cAdd {1 (cPow [x@P z])}))
|
|
* : (cAdd {(cPow [& y]) (cPow [& (cAdd {y (cMul {z (cLog [x]) /LOG( & )})})])})
|
|
*/ {ReplaceParams , false, 1,P1(S(229)) , {2,P2(S(84),S(206)) , cMul ,AnyParams ,0}},
|
|
/* 118: (cMul (cPow [& y]) (cAdd {-1 (cPow [x@P z])}))
|
|
* : (cAdd {(cMul {(cPow [& y]) -1}) (cPow [& (cAdd {y (cMul {z (cLog [x]) /LOG( & )})})])})
|
|
*/ {ReplaceParams , false, 1,P1(S(232)) , {2,P2(S(84),S(204)) , cMul ,AnyParams ,0}},
|
|
/* 119: (cMul -1 (cSin [(cMul %@N <1>)]))
|
|
* : (cSin [(cMul -% <1>)])
|
|
*/ {ReplaceParams , false, 1,P1(S(168)) , {2,P2(N(2),S(166)) , cMul ,AnyParams ,0}},
|
|
/* 120: (cMul -1 (cSinh [(cMul %@N <1>)]))
|
|
* : (cSinh [(cMul -% <1>)])
|
|
*/ {ReplaceParams , false, 1,P1(S(176)) , {2,P2(N(2),S(175)) , cMul ,AnyParams ,0}},
|
|
/* 121: (cMul (cPow [(cSinh [x]) -1])@D4 (cCosh [x])@D4)
|
|
* : (cPow [(cTanh [x]) -1])
|
|
*/ {ReplaceParams , false, 1,P1(S(133)) , {2,P2(S(130),S(22)) , cMul ,AnyParams ,0}},
|
|
/* 122: (cMul (cTanh [x])@D4 (cCosh [x])@D4)
|
|
* : (cSinh [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(170)) , {2,P2(S(187),S(22)) , cMul ,AnyParams ,0}},
|
|
/* 123: (cMul (cPow [(cTanh [x]) -1])@D4 (cSinh [x])@D4)
|
|
* : (cCosh [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(23)) , {2,P2(S(134),S(169)) , cMul ,AnyParams ,0}},
|
|
/* 124: (cMul (cPow [(cTan [x]) -1])@D4 (cSin [x])@D4)
|
|
* : (cCos [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(14)) , {2,P2(S(131),S(159)) , cMul ,AnyParams ,0}},
|
|
/* 125: (cMul (cSin [x])@D4 (cPow [(cCos [x]) -1])@D4)
|
|
* : (cTan [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(177)) , {2,P2(S(159),S(111)) , cMul ,AnyParams ,0}},
|
|
/* 126: (cMul (cTan [x])@D4 (cPow [(cSin [x]) -1])@D4)
|
|
* : (cPow [(cCos [x]) -1])
|
|
*/ {ReplaceParams , false, 1,P1(S(112)) , {2,P2(S(178),S(129)) , cMul ,AnyParams ,0}},
|
|
/* 127: (cMul (cPow [(cSin [x]) -1])@D4 (cCos [x])@D4)
|
|
* : (cPow [(cTan [x]) -1])
|
|
*/ {ReplaceParams , false, 1,P1(S(132)) , {2,P2(S(129),S(15)) , cMul ,AnyParams ,0}},
|
|
/* 128: (cMul (cTan [x])@D4 (cCos [x])@D4)
|
|
* : (cSin [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(158)) , {2,P2(S(178),S(15)) , cMul ,AnyParams ,0}},
|
|
/* 129: (cMul (cTan [(cAdd {1.57079632679 (cMul {-1 x})})])@D4 (cTan [x])@D4)
|
|
* : 1
|
|
*/ {ReplaceParams , false, 1,P1(N(10)) , {2,P2(S(179),S(178)) , cMul ,AnyParams ,0}},
|
|
/* 130: (cMul (cSin [(cMul % <1>)])@D1 (cPow [(cCos [(cMul -% <1>)]) -1])@D1)
|
|
* : (cTan [(cMul % <1>)])
|
|
*/ {ReplaceParams , false, 1,P1(S(183)) , {2,P2(S(167),S(114)) , cMul ,AnyParams ,0}},
|
|
/* 131: (cMul (cTan [(cAdd {1.57079632679 (cMul -1 <1>)})]) (cTan [(cMul <1>)]))
|
|
* : 1
|
|
*/ {ReplaceParams , false, 1,P1(N(10)) , {2,P2(S(180),S(182)) , cMul ,AnyParams ,0}},
|
|
/* 132: (cMul -1 (cTan [(cMul %@N <1>)]))
|
|
* : (cTan [(cMul -% <1>)])
|
|
*/ {ReplaceParams , false, 1,P1(S(185)) , {2,P2(N(2),S(184)) , cMul ,AnyParams ,0}},
|
|
/* 133: (cMul (cSinh [x])@D4 (cPow [(cCosh [x]) -1])@D4)
|
|
* : (cTanh [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(186)) , {2,P2(S(169),S(115)) , cMul ,AnyParams ,0}},
|
|
/* 134: (cMul (cTanh [x])@D4 (cPow [(cSinh [x]) -1])@D4)
|
|
* : (cPow [(cCosh [x]) -1])
|
|
*/ {ReplaceParams , false, 1,P1(S(116)) , {2,P2(S(187),S(130)) , cMul ,AnyParams ,0}},
|
|
/* 135: (cMul (cSinh [(cMul {% x})])@D5 (cPow [(cCosh [(cMul {-% x})]) -1])@D5)
|
|
* : (cTanh [(cMul {% x})])
|
|
*/ {ReplaceParams , false, 1,P1(S(188)) , {2,P2(S(172),S(121)) , cMul ,AnyParams ,0}},
|
|
/* 136: (cMul (cSin [(cMul {% x})])@D5 (cPow [(cCos [(cMul {-% x})]) -1])@D5)
|
|
* : (cTan [(cMul {% x})])
|
|
*/ {ReplaceParams , false, 1,P1(S(181)) , {2,P2(S(164),S(113)) , cMul ,AnyParams ,0}},
|
|
/* 137: (cMul -1 (cTanh [(cMul %@N <1>)]))
|
|
* : (cTanh [(cMul -% <1>)])
|
|
*/ {ReplaceParams , false, 1,P1(S(192)) , {2,P2(N(2),S(191)) , cMul ,AnyParams ,0}},
|
|
/* 138: (cMul (cAdd {-1 (cPow [% x])})@D5 (cPow [(cAdd {1 (cPow [% x])}) -1])@D5)
|
|
* : (cTanh [(cMul {x LOG( % ) 0.5})])
|
|
*/ {ReplaceParams , false, 1,P1(S(189)) , {2,P2(S(198),S(137)) , cMul ,AnyParams ,0}},
|
|
/* 139: (cMul (cAdd {1 (cPow [% x])})@D5 (cPow [(cAdd {-1 (cPow [% x])}) -1])@D5)
|
|
* : (cPow [(cTanh [(cMul {x LOG( % ) 0.5})]) -1])
|
|
*/ {ReplaceParams , false, 1,P1(S(135)) , {2,P2(S(202),S(136)) , cMul ,AnyParams ,0}},
|
|
/* 140: (cMul (cLog [x]) 0.434294481903)
|
|
* : (cLog10 [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(55)) , {2,P2(S(47),N(7)) , cMul ,AnyParams ,0}},
|
|
/* 141: (cMul (cPow [(cLog [x]) -1]) 2.30258509299)
|
|
* : (cPow [(cLog10 [x]) -1])
|
|
*/ {ReplaceParams , false, 1,P1(S(124)) , {2,P2(S(122),N(14)) , cMul ,AnyParams ,0}},
|
|
/* 142: (cMul (cLog [x]) 1.44269504089)
|
|
* : (cLog2 [x])
|
|
*/ {ReplaceParams , false, 1,P1(S(56)) , {2,P2(S(47),N(11)) , cMul ,AnyParams ,0}},
|
|
/* 143: (cMul (cPow [(cLog [x]) -1]) 0.69314718056)
|
|
* : (cPow [(cLog2 [x]) -1])
|
|
*/ {ReplaceParams , false, 1,P1(S(125)) , {2,P2(S(122),N(9)) , cMul ,AnyParams ,0}},
|
|
/* 144: (cMul (cPow [& y]) (cAdd {(cMul {% (cPow [x@P z])})@D1 %@D1}))
|
|
* : % (cAdd {(cPow [& y]) (cPow [& (cAdd {y (cMul {z (cLog [x]) /LOG( & )})})])})
|
|
*/ {ReplaceParams , false, 2,P2(P(1),S(229)) , {2,P2(S(84),S(227)) , cMul ,AnyParams ,0}},
|
|
/* 145: (cMul (cPow [& y]) (cAdd {(cMul {% (cPow [x@P z])})@D1 -%@D1}))
|
|
* : % (cAdd {(cMul {(cPow [& y]) -1}) (cPow [& (cAdd {y (cMul {z (cLog [x]) /LOG( & )})})])})
|
|
*/ {ReplaceParams , false, 2,P2(P(1),S(232)) , {2,P2(S(84),S(231)) , cMul ,AnyParams ,0}},
|
|
/* 146: (cMul (cSinh [x])@D4 (cPow [(cCosh [x]) %])@D4)
|
|
* : (cTanh [x]) (cPow [(cCosh [x]) ADD( % 1 )])
|
|
*/ {ReplaceParams , false, 2,P2(S(186),S(120)) , {2,P2(S(169),S(119)) , cMul ,AnyParams ,0}},
|
|
/* 147: @L (cMul (cAbs [x]))
|
|
* : x
|
|
*/ {ReplaceParams , true , 1,P1(P(14)) , {1,P1(S(0)) , cMul ,AnyParams ,0}},
|
|
/* 148: @L (cMul %@N)
|
|
* : -%
|
|
*/ {ReplaceParams , true , 1,P1(S(194)) , {1,P1(P(0)) , cMul ,AnyParams ,0}},
|
|
/* 149: (cEqual [(cAbs [x]) 0])
|
|
* : x 0
|
|
*/ {ReplaceParams , false, 2,P2(P(14),N(4)) , {2,P2(S(0),N(4)) , cEqual ,PositionalParams,0}},
|
|
/* 150: (cEqual [(cAdd % <1>) &])
|
|
* : (cAdd <1>) ADD( & -% )
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(260)) , {2,P2(S(248),P(9)) , cEqual ,PositionalParams,0}},
|
|
/* 151: (cEqual [(cMul % <1>) &])
|
|
* : (cMul <1>) MUL( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(363)) , {2,P2(S(339),P(9)) , cEqual ,PositionalParams,0}},
|
|
/* 152: (cEqual [(cPow [x %]) &])
|
|
* : (cPow [(cPow [x %]) /%]) POW( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(126),S(156)) , {2,P2(S(77),P(9)) , cEqual ,PositionalParams,0}},
|
|
/* 153: (cEqual [(cAdd % <1>) (cAdd & <2>)])
|
|
* : (cAdd <1>) (cAdd & -% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(254)) , {2,P2(S(248),S(250)) , cEqual ,PositionalParams,0}},
|
|
/* 154: (cEqual [(cAdd x <1>)@D4 (cAdd x <2>)@D4])
|
|
* : (cAdd <1>) (cAdd <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(243)) , {2,P2(S(251),S(252)) , cEqual ,PositionalParams,0}},
|
|
/* 155: (cEqual [(cMul % <1>) (cMul & <2>)])
|
|
* : (cMul <1>) (cMul & /% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(342)) , {2,P2(S(339),S(340)) , cEqual ,PositionalParams,0}},
|
|
/* 156: (cEqual [(cPow [%@P x]) &@P])
|
|
* : x MUL( LOG( & ) /LOG( % ) )
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(364)) , {2,P2(S(81),P(13)) , cEqual ,PositionalParams,0}},
|
|
/* 157: (cEqual [(cPow [x %@P]) (cPow [y &@P])])
|
|
* : (cPow [(cPow [x %]) /MIN( % & )]) (cPow [(cPow [y &]) /MIN( % & )])
|
|
*/ {ReplaceParams , false, 2,P2(S(127),S(128)) , {2,P2(S(78),S(79)) , cEqual ,PositionalParams,0}},
|
|
/* 158: (cEqual [(cPow [% x])@D1 (cPow [% y])@D1])
|
|
* : x y
|
|
*/ {ReplaceParams , false, 2,P2(P(14),P(21)) , {2,P2(S(75),S(76)) , cEqual ,PositionalParams,0}},
|
|
/* 159: (cNEqual [(cAbs [x]) 0])
|
|
* : x 0
|
|
*/ {ReplaceParams , false, 2,P2(P(14),N(4)) , {2,P2(S(0),N(4)) , cNEqual ,PositionalParams,0}},
|
|
/* 160: (cNEqual [(cAdd % <1>) &])
|
|
* : (cAdd <1>) ADD( & -% )
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(260)) , {2,P2(S(248),P(9)) , cNEqual ,PositionalParams,0}},
|
|
/* 161: (cNEqual [(cMul % <1>) &])
|
|
* : (cMul <1>) MUL( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(363)) , {2,P2(S(339),P(9)) , cNEqual ,PositionalParams,0}},
|
|
/* 162: (cNEqual [(cPow [x %]) &])
|
|
* : (cPow [(cPow [x %]) /%]) POW( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(126),S(156)) , {2,P2(S(77),P(9)) , cNEqual ,PositionalParams,0}},
|
|
/* 163: (cNEqual [(cAdd % <1>) (cAdd & <2>)])
|
|
* : (cAdd <1>) (cAdd & -% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(254)) , {2,P2(S(248),S(250)) , cNEqual ,PositionalParams,0}},
|
|
/* 164: (cNEqual [(cAdd x <1>)@D4 (cAdd x <2>)@D4])
|
|
* : (cAdd <1>) (cAdd <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(243)) , {2,P2(S(251),S(252)) , cNEqual ,PositionalParams,0}},
|
|
/* 165: (cNEqual [(cMul % <1>) (cMul & <2>)])
|
|
* : (cMul <1>) (cMul & /% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(342)) , {2,P2(S(339),S(340)) , cNEqual ,PositionalParams,0}},
|
|
/* 166: (cNEqual [(cPow [%@P x]) &@P])
|
|
* : x MUL( LOG( & ) /LOG( % ) )
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(364)) , {2,P2(S(81),P(13)) , cNEqual ,PositionalParams,0}},
|
|
/* 167: (cNEqual [(cPow [x %@P]) (cPow [y &@P])])
|
|
* : (cPow [(cPow [x %]) /MIN( % & )]) (cPow [(cPow [y &]) /MIN( % & )])
|
|
*/ {ReplaceParams , false, 2,P2(S(127),S(128)) , {2,P2(S(78),S(79)) , cNEqual ,PositionalParams,0}},
|
|
/* 168: (cNEqual [(cPow [% x])@D1 (cPow [% y])@D1])
|
|
* : x y
|
|
*/ {ReplaceParams , false, 2,P2(P(14),P(21)) , {2,P2(S(75),S(76)) , cNEqual ,PositionalParams,0}},
|
|
/* 169: (cLess [x 0.5])
|
|
* -> (cAbsNot [x])
|
|
*/ {ProduceNewTree, false, 1,P1(S(409)) , {2,P2(P(14),N(8)) , cLess ,PositionalParams,0}},
|
|
/* 170: (cLess [(cAbs [x]) %])
|
|
* -> (cNot [(cMul {x 0.5 /%})])
|
|
*/ {ProduceNewTree, false, 1,P1(S(378)) , {2,P2(S(0),P(1)) , cLess ,PositionalParams,0}},
|
|
/* 171: (cLess [(cMul %@P <1>) &])
|
|
* : (cMul <1>) MUL( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(363)) , {2,P2(S(336),P(9)) , cLess ,PositionalParams,0}},
|
|
/* 172: (cLess [(cMul %@N <1>) &])
|
|
* : MUL( & /% ) (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(S(363),S(331)) , {2,P2(S(335),P(9)) , cLess ,PositionalParams,0}},
|
|
/* 173: (cLess [(cAdd % <1>) &])
|
|
* : (cAdd <1>) ADD( & -% )
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(260)) , {2,P2(S(248),P(9)) , cLess ,PositionalParams,0}},
|
|
/* 174: (cLess [(cPow [x %]) &])
|
|
* : (cPow [(cPow [x %]) /%]) POW( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(126),S(156)) , {2,P2(S(77),P(9)) , cLess ,PositionalParams,0}},
|
|
/* 175: (cLess [(cAdd % <1>) (cAdd & <2>)])
|
|
* : (cAdd <1>) (cAdd & -% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(254)) , {2,P2(S(248),S(250)) , cLess ,PositionalParams,0}},
|
|
/* 176: (cLess [(cAdd x <1>)@D4 (cAdd x <2>)@D4])
|
|
* : (cAdd <1>) (cAdd <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(243)) , {2,P2(S(251),S(252)) , cLess ,PositionalParams,0}},
|
|
/* 177: (cLess [(cMul %@P <1>) (cMul & <2>)])
|
|
* : (cMul <1>) (cMul & /% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(342)) , {2,P2(S(336),S(340)) , cLess ,PositionalParams,0}},
|
|
/* 178: (cLess [(cMul %@N <1>) (cMul & <2>)])
|
|
* : (cMul & /% <2>) (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(S(342),S(331)) , {2,P2(S(335),S(340)) , cLess ,PositionalParams,0}},
|
|
/* 179: (cLess [(cPow [%@P x]) &@P])
|
|
* : x MUL( LOG( & ) /LOG( % ) )
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(364)) , {2,P2(S(81),P(13)) , cLess ,PositionalParams,0}},
|
|
/* 180: (cLess [(cPow [x %@P]) (cPow [y &@P])])
|
|
* : (cPow [(cPow [x %]) /MIN( % & )]) (cPow [(cPow [y &]) /MIN( % & )])
|
|
*/ {ReplaceParams , false, 2,P2(S(127),S(128)) , {2,P2(S(78),S(79)) , cLess ,PositionalParams,0}},
|
|
/* 181: (cLess [(cPow [% x])@D1 (cPow [% y])@D1])
|
|
* : x y
|
|
*/ {ReplaceParams , false, 2,P2(P(14),P(21)) , {2,P2(S(75),S(76)) , cLess ,PositionalParams,0}},
|
|
/* 182: (cLessOrEq [% (cAbs [x])])
|
|
* -> (cNotNot [(cMul {x 0.5 /%})])
|
|
*/ {ProduceNewTree, false, 1,P1(S(403)) , {2,P2(P(1),S(0)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 183: (cLessOrEq [(cMul %@P <1>) &])
|
|
* : (cMul <1>) MUL( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(363)) , {2,P2(S(336),P(9)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 184: (cLessOrEq [(cMul %@N <1>) &])
|
|
* : MUL( & /% ) (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(S(363),S(331)) , {2,P2(S(335),P(9)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 185: (cLessOrEq [(cAdd % <1>) &])
|
|
* : (cAdd <1>) ADD( & -% )
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(260)) , {2,P2(S(248),P(9)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 186: (cLessOrEq [(cPow [x %]) &])
|
|
* : (cPow [(cPow [x %]) /%]) POW( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(126),S(156)) , {2,P2(S(77),P(9)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 187: (cLessOrEq [(cAdd % <1>) (cAdd & <2>)])
|
|
* : (cAdd <1>) (cAdd & -% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(254)) , {2,P2(S(248),S(250)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 188: (cLessOrEq [(cAdd x <1>)@D4 (cAdd x <2>)@D4])
|
|
* : (cAdd <1>) (cAdd <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(243)) , {2,P2(S(251),S(252)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 189: (cLessOrEq [(cMul %@P <1>) (cMul & <2>)])
|
|
* : (cMul <1>) (cMul & /% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(342)) , {2,P2(S(336),S(340)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 190: (cLessOrEq [(cMul %@N <1>) (cMul & <2>)])
|
|
* : (cMul & /% <2>) (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(S(342),S(331)) , {2,P2(S(335),S(340)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 191: (cLessOrEq [(cPow [%@P x]) &@P])
|
|
* : x MUL( LOG( & ) /LOG( % ) )
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(364)) , {2,P2(S(81),P(13)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 192: (cLessOrEq [(cPow [x %@P]) (cPow [y &@P])])
|
|
* : (cPow [(cPow [x %]) /MIN( % & )]) (cPow [(cPow [y &]) /MIN( % & )])
|
|
*/ {ReplaceParams , false, 2,P2(S(127),S(128)) , {2,P2(S(78),S(79)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 193: (cLessOrEq [(cPow [% x])@D1 (cPow [% y])@D1])
|
|
* : x y
|
|
*/ {ReplaceParams , false, 2,P2(P(14),P(21)) , {2,P2(S(75),S(76)) , cLessOrEq ,PositionalParams,0}},
|
|
/* 194: (cGreater [(cMul %@P <1>) &])
|
|
* : (cMul <1>) MUL( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(363)) , {2,P2(S(336),P(9)) , cGreater ,PositionalParams,0}},
|
|
/* 195: (cGreater [(cMul %@N <1>) &])
|
|
* : MUL( & /% ) (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(S(363),S(331)) , {2,P2(S(335),P(9)) , cGreater ,PositionalParams,0}},
|
|
/* 196: (cGreater [(cAdd % <1>) &])
|
|
* : (cAdd <1>) ADD( & -% )
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(260)) , {2,P2(S(248),P(9)) , cGreater ,PositionalParams,0}},
|
|
/* 197: (cGreater [(cPow [x %]) &])
|
|
* : (cPow [(cPow [x %]) /%]) POW( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(126),S(156)) , {2,P2(S(77),P(9)) , cGreater ,PositionalParams,0}},
|
|
/* 198: (cGreater [(cAdd % <1>) (cAdd & <2>)])
|
|
* : (cAdd <1>) (cAdd & -% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(254)) , {2,P2(S(248),S(250)) , cGreater ,PositionalParams,0}},
|
|
/* 199: (cGreater [(cAdd x <1>)@D4 (cAdd x <2>)@D4])
|
|
* : (cAdd <1>) (cAdd <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(243)) , {2,P2(S(251),S(252)) , cGreater ,PositionalParams,0}},
|
|
/* 200: (cGreater [(cMul %@P <1>) (cMul & <2>)])
|
|
* : (cMul <1>) (cMul & /% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(342)) , {2,P2(S(336),S(340)) , cGreater ,PositionalParams,0}},
|
|
/* 201: (cGreater [(cMul %@N <1>) (cMul & <2>)])
|
|
* : (cMul & /% <2>) (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(S(342),S(331)) , {2,P2(S(335),S(340)) , cGreater ,PositionalParams,0}},
|
|
/* 202: (cGreater [(cPow [%@P x]) &@P])
|
|
* : x MUL( LOG( & ) /LOG( % ) )
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(364)) , {2,P2(S(81),P(13)) , cGreater ,PositionalParams,0}},
|
|
/* 203: (cGreater [(cPow [x %@P]) (cPow [y &@P])])
|
|
* : (cPow [(cPow [x %]) /MIN( % & )]) (cPow [(cPow [y &]) /MIN( % & )])
|
|
*/ {ReplaceParams , false, 2,P2(S(127),S(128)) , {2,P2(S(78),S(79)) , cGreater ,PositionalParams,0}},
|
|
/* 204: (cGreater [(cPow [% x])@D1 (cPow [% y])@D1])
|
|
* : x y
|
|
*/ {ReplaceParams , false, 2,P2(P(14),P(21)) , {2,P2(S(75),S(76)) , cGreater ,PositionalParams,0}},
|
|
/* 205: (cGreaterOrEq [x 0.5])
|
|
* -> (cAbsNotNot [x])
|
|
*/ {ProduceNewTree, false, 1,P1(S(410)) , {2,P2(P(14),N(8)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 206: (cGreaterOrEq [(cMul %@P <1>) &])
|
|
* : (cMul <1>) MUL( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(363)) , {2,P2(S(336),P(9)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 207: (cGreaterOrEq [(cMul %@N <1>) &])
|
|
* : MUL( & /% ) (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(S(363),S(331)) , {2,P2(S(335),P(9)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 208: (cGreaterOrEq [(cAdd % <1>) &])
|
|
* : (cAdd <1>) ADD( & -% )
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(260)) , {2,P2(S(248),P(9)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 209: (cGreaterOrEq [(cPow [x %]) &])
|
|
* : (cPow [(cPow [x %]) /%]) POW( & /% )
|
|
*/ {ReplaceParams , false, 2,P2(S(126),S(156)) , {2,P2(S(77),P(9)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 210: (cGreaterOrEq [(cAdd % <1>) (cAdd & <2>)])
|
|
* : (cAdd <1>) (cAdd & -% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(254)) , {2,P2(S(248),S(250)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 211: (cGreaterOrEq [(cAdd x <1>)@D4 (cAdd x <2>)@D4])
|
|
* : (cAdd <1>) (cAdd <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(242),S(243)) , {2,P2(S(251),S(252)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 212: (cGreaterOrEq [(cMul %@P <1>) (cMul & <2>)])
|
|
* : (cMul <1>) (cMul & /% <2>)
|
|
*/ {ReplaceParams , false, 2,P2(S(331),S(342)) , {2,P2(S(336),S(340)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 213: (cGreaterOrEq [(cMul %@N <1>) (cMul & <2>)])
|
|
* : (cMul & /% <2>) (cMul <1>)
|
|
*/ {ReplaceParams , false, 2,P2(S(342),S(331)) , {2,P2(S(335),S(340)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 214: (cGreaterOrEq [(cPow [%@P x]) &@P])
|
|
* : x MUL( LOG( & ) /LOG( % ) )
|
|
*/ {ReplaceParams , false, 2,P2(P(14),S(364)) , {2,P2(S(81),P(13)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 215: (cGreaterOrEq [(cPow [x %@P]) (cPow [y &@P])])
|
|
* : (cPow [(cPow [x %]) /MIN( % & )]) (cPow [(cPow [y &]) /MIN( % & )])
|
|
*/ {ReplaceParams , false, 2,P2(S(127),S(128)) , {2,P2(S(78),S(79)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 216: (cGreaterOrEq [(cPow [% x])@D1 (cPow [% y])@D1])
|
|
* : x y
|
|
*/ {ReplaceParams , false, 2,P2(P(14),P(21)) , {2,P2(S(75),S(76)) , cGreaterOrEq,PositionalParams,0}},
|
|
/* 217: (cNot [x@P])
|
|
* -> (cAbsNot [x])
|
|
*/ {ProduceNewTree, false, 1,P1(S(409)) , {1,P1(P(17)) , cNot ,PositionalParams,0}},
|
|
/* 218: (cAnd x@L <1>)
|
|
* -> (cNotNot [(cMul {x (cAnd <1>)})])
|
|
*/ {ProduceNewTree, false, 1,P1(S(404)) , {1,P1(P(19)) , cAnd ,AnyParams ,1}},
|
|
/* 219: (cAnd x@P y@P <1>)
|
|
* -> (cAbsAnd {x y (cAnd <1>)})
|
|
*/ {ProduceNewTree, false, 1,P1(S(407)) , {2,P2(P(17),P(27)) , cAnd ,AnyParams ,1}},
|
|
/* 220: (cAnd x y)
|
|
* -> (cIf [x (cNotNot [y]) 0])
|
|
*/ {ProduceNewTree, false, 1,P1(S(45)) , {2,P2(P(14),P(21)) , cAnd ,AnyParams ,0}},
|
|
/* 221: (cAnd (cIf [x y z])@D4 (cIf [x a b])@D4)
|
|
* : (cIf [x (cAnd {y a}) (cAnd {z b})])
|
|
*/ {ReplaceParams , false, 1,P1(S(43)) , {2,P2(S(32),S(36)) , cAnd ,AnyParams ,0}},
|
|
/* 222: (cAnd (cNot [x]) (cNot [y]))
|
|
* : (cNot [(cOr {x y})])
|
|
*/ {ReplaceParams , false, 1,P1(S(382)) , {2,P2(S(375),S(376)) , cAnd ,AnyParams ,0}},
|
|
/* 223: (cAnd (cNot [z]) (cIf [x (cNot [y]) %@L]))
|
|
* : (cNot [(cOr {z (cIf [x y (cNot [%])])})])
|
|
*/ {ReplaceParams , false, 1,P1(S(383)) , {2,P2(S(377),S(38)) , cAnd ,AnyParams ,0}},
|
|
/* 224: (cAnd (cNot [z]) (cIf [x %@L (cNot [y])]))
|
|
* : (cNot [(cOr {z (cIf [x (cNot [%]) y])})])
|
|
*/ {ReplaceParams , false, 1,P1(S(384)) , {2,P2(S(377),S(34)) , cAnd ,AnyParams ,0}},
|
|
/* 225: (cAnd (cEqual [x y])@D12 (cEqual [y z])@D24 (cEqual [x z])@D20)
|
|
* : (cEqual [x y]) (cEqual [y z])
|
|
*/ {ReplaceParams , false, 2,P2(S(366),S(369)) , {3,P3(S(365),S(368),S(367)), cAnd ,AnyParams ,0}},
|
|
/* 226: (cOr x@P y@P <1>)
|
|
* -> (cAbsOr {x y (cOr <1>)})
|
|
*/ {ProduceNewTree, false, 1,P1(S(408)) , {2,P2(P(17),P(27)) , cOr ,AnyParams ,1}},
|
|
/* 227: (cOr x y)
|
|
* -> (cIf [x 1 (cNotNot [y])])
|
|
*/ {ProduceNewTree, false, 1,P1(S(31)) , {2,P2(P(14),P(21)) , cOr ,AnyParams ,0}},
|
|
/* 228: (cOr x@L y@L)
|
|
* : (cNotNot [(cAdd {x y})])
|
|
*/ {ReplaceParams , false, 1,P1(S(401)) , {2,P2(P(19),P(22)) , cOr ,AnyParams ,0}},
|
|
/* 229: (cOr (cIf [x y z])@D4 (cIf [x a b])@D4)
|
|
* : (cIf [x (cOr {y a}) (cOr {z b})])
|
|
*/ {ReplaceParams , false, 1,P1(S(44)) , {2,P2(S(32),S(36)) , cOr ,AnyParams ,0}},
|
|
/* 230: (cOr (cNot [x]) (cNot [y]))
|
|
* : (cNot [(cAnd {x y})])
|
|
*/ {ReplaceParams , false, 1,P1(S(379)) , {2,P2(S(375),S(376)) , cOr ,AnyParams ,0}},
|
|
/* 231: (cOr (cNot [z]) (cIf [x (cNot [y]) %@L]))
|
|
* : (cNot [(cAnd {z (cIf [x y (cNot [%])])})])
|
|
*/ {ReplaceParams , false, 1,P1(S(380)) , {2,P2(S(377),S(38)) , cOr ,AnyParams ,0}},
|
|
/* 232: (cOr (cNot [z]) (cIf [x %@L (cNot [y])]))
|
|
* : (cNot [(cAnd {z (cIf [x (cNot [%]) y])})])
|
|
*/ {ReplaceParams , false, 1,P1(S(381)) , {2,P2(S(377),S(34)) , cOr ,AnyParams ,0}},
|
|
/* 233: (cOr x@L (cAdd <1>)@P)
|
|
* : (cNotNot [(cAdd x <1>)])
|
|
*/ {ReplaceParams , false, 1,P1(S(402)) , {2,P2(P(19),S(244)) , cOr ,AnyParams ,0}},
|
|
/* 234: (cNotNot [x@P])
|
|
* -> (cAbsNotNot [x])
|
|
*/ {ProduceNewTree, false, 1,P1(S(410)) , {1,P1(P(17)) , cNotNot ,PositionalParams,0}},
|
|
/* 235: @L (cNotNot [x])
|
|
* -> x
|
|
*/ {ProduceNewTree, true , 1,P1(P(14)) , {1,P1(P(14)) , cNotNot ,PositionalParams,0}},
|
|
/* 236: (cAbsAnd x y)
|
|
* -> (cAbsIf [x (cAbsNotNot [y]) 0])
|
|
*/ {ProduceNewTree, false, 1,P1(S(416)) , {2,P2(P(14),P(21)) , cAbsAnd ,AnyParams ,0}},
|
|
/* 237: (cAbsOr x y)
|
|
* -> (cAbsIf [x 1 (cAbsNotNot [y])])
|
|
*/ {ProduceNewTree, false, 1,P1(S(413)) , {2,P2(P(14),P(21)) , cAbsOr ,AnyParams ,0}},
|
|
/* 238: @L (cAbsNotNot [x])
|
|
* -> x
|
|
*/ {ProduceNewTree, true , 1,P1(P(14)) , {1,P1(P(14)) , cAbsNotNot ,PositionalParams,0}},
|
|
/* 239: (cAbsIf [(cNotNot [x]) y z])
|
|
* -> (cIf [x y z])
|
|
*/ {ProduceNewTree, false, 1,P1(S(33)) , {3,P3(S(399),P(21),P(28)) , cAbsIf ,PositionalParams,0}},
|
|
/* 240: (cAbsIf [(cLessOrEq [x y]) z a])
|
|
* : (cLess [y x]) a z
|
|
*/ {ReplaceParams , false, 3,P3(S(371),P(31),P(28)) , {3,P3(S(373),P(28),P(31)) , cAbsIf ,PositionalParams,0}},
|
|
};
|
|
#undef P
|
|
#undef N
|
|
#undef S
|
|
|
|
struct grammar_optimize_abslogical_type
|
|
{
|
|
unsigned c;
|
|
unsigned char l[12];
|
|
};
|
|
extern "C"
|
|
{
|
|
grammar_optimize_abslogical_type grammar_optimize_abslogical =
|
|
{
|
|
12,
|
|
{ 19,29,30,169,205,217,219,226,234,238,
|
|
239,240
|
|
} }; }
|
|
struct grammar_optimize_ignore_if_sideeffects_type
|
|
{
|
|
unsigned c;
|
|
unsigned char l[34];
|
|
};
|
|
extern "C"
|
|
{
|
|
grammar_optimize_ignore_if_sideeffects_type grammar_optimize_ignore_if_sideeffects =
|
|
{
|
|
34,
|
|
{ 0,18,20,21,22,23,24,25,26,27,
|
|
28,37,39,75,109,110,111,112,147,148,
|
|
149,159,170,182,221,222,223,224,225,229,
|
|
230,231,232,235
|
|
} }; }
|
|
struct grammar_optimize_nonshortcut_logical_evaluation_type
|
|
{
|
|
unsigned c;
|
|
unsigned char l[31];
|
|
};
|
|
extern "C"
|
|
{
|
|
grammar_optimize_nonshortcut_logical_evaluation_type grammar_optimize_nonshortcut_logical_evaluation =
|
|
{
|
|
31,
|
|
{ 0,25,26,27,28,37,39,75,109,110,
|
|
111,112,147,148,149,159,170,182,218,221,
|
|
222,223,224,225,228,229,230,231,232,233,
|
|
235
|
|
} }; }
|
|
struct grammar_optimize_round1_type
|
|
{
|
|
unsigned c;
|
|
unsigned char l[92];
|
|
};
|
|
extern "C"
|
|
{
|
|
grammar_optimize_round1_type grammar_optimize_round1 =
|
|
{
|
|
92,
|
|
{ 0,1,2,3,4,5,6,7,8,9,
|
|
10,11,15,16,25,26,27,28,31,32,
|
|
33,36,37,38,39,40,41,42,43,44,
|
|
45,46,47,48,49,53,54,55,56,57,
|
|
58,59,60,61,62,63,70,75,76,77,
|
|
78,79,80,81,82,83,84,85,86,87,
|
|
88,104,108,109,110,111,112,113,114,115,
|
|
116,117,118,119,144,145,147,148,149,159,
|
|
170,182,221,222,223,224,225,229,230,231,
|
|
232,235
|
|
} }; }
|
|
struct grammar_optimize_round2_type
|
|
{
|
|
unsigned c;
|
|
unsigned char l[81];
|
|
};
|
|
extern "C"
|
|
{
|
|
grammar_optimize_round2_type grammar_optimize_round2 =
|
|
{
|
|
81,
|
|
{ 0,5,12,13,14,15,25,26,27,28,
|
|
31,34,35,36,37,38,39,40,42,43,
|
|
44,45,46,47,48,49,57,58,59,64,
|
|
65,70,71,72,73,74,75,89,90,91,
|
|
92,93,94,95,96,97,98,99,100,101,
|
|
102,103,108,109,110,111,112,113,116,117,
|
|
118,120,144,145,146,147,148,149,159,170,
|
|
182,221,222,223,224,225,229,230,231,232,
|
|
235
|
|
} }; }
|
|
struct grammar_optimize_round3_type
|
|
{
|
|
unsigned c;
|
|
unsigned char l[85];
|
|
};
|
|
extern "C"
|
|
{
|
|
grammar_optimize_round3_type grammar_optimize_round3 =
|
|
{
|
|
85,
|
|
{ 66,67,68,69,121,122,123,124,125,126,
|
|
127,128,129,130,131,132,133,134,135,136,
|
|
137,138,139,150,151,152,153,154,155,156,
|
|
157,158,160,161,162,163,164,165,166,167,
|
|
168,171,172,173,174,175,176,177,178,179,
|
|
180,181,183,184,185,186,187,188,189,190,
|
|
191,192,193,194,195,196,197,198,199,200,
|
|
201,202,203,204,206,207,208,209,210,211,
|
|
212,213,214,215,216
|
|
} }; }
|
|
struct grammar_optimize_round4_type
|
|
{
|
|
unsigned c;
|
|
unsigned char l[10];
|
|
};
|
|
extern "C"
|
|
{
|
|
grammar_optimize_round4_type grammar_optimize_round4 =
|
|
{
|
|
10,
|
|
{ 17,50,51,52,106,107,140,141,142,143
|
|
} }; }
|
|
struct grammar_optimize_shortcut_logical_evaluation_type
|
|
{
|
|
unsigned c;
|
|
unsigned char l[33];
|
|
};
|
|
extern "C"
|
|
{
|
|
grammar_optimize_shortcut_logical_evaluation_type grammar_optimize_shortcut_logical_evaluation =
|
|
{
|
|
33,
|
|
{ 0,25,26,27,28,37,39,75,105,109,
|
|
110,111,112,147,148,149,159,170,182,220,
|
|
221,222,223,224,225,227,229,230,231,232,
|
|
235,236,237
|
|
} }; }
|
|
}
|
|
#undef P1
|
|
#undef P2
|
|
#undef P3
|
|
|
|
namespace FPoptimizer_Grammar
|
|
{
|
|
ParamSpec ParamSpec_Extract(unsigned paramlist, unsigned index)
|
|
{
|
|
index = (paramlist >> (index * PARAM_INDEX_BITS)) % (1 << PARAM_INDEX_BITS);
|
|
const unsigned p_begin = 0;
|
|
const unsigned n_begin = p_begin + sizeof(plist.plist_p)/sizeof(*plist.plist_p);
|
|
const unsigned s_begin = n_begin + sizeof(plist.plist_n)/sizeof(*plist.plist_n);
|
|
/*const unsigned end = s_begin + sizeof(plist.plist_s)/sizeof(*plist.plist_s);*/
|
|
if(index < s_begin)
|
|
{
|
|
if(index < n_begin)
|
|
return ParamSpec(ParamHolder,(const void*)&plist.plist_p[index-p_begin]);
|
|
else
|
|
return ParamSpec(NumConstant,(const void*)&plist.plist_n[index-n_begin]);
|
|
}
|
|
else
|
|
return ParamSpec(SubFunction,(const void*)&plist.plist_s[index-s_begin]);
|
|
}
|
|
}
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_optimize.c"
|
|
#include "fpconfig.h"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_consts.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_opcodename.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
using namespace FPoptimizer_Grammar;
|
|
using namespace FPoptimizer_CodeTree;
|
|
using namespace FPoptimizer_Optimize;
|
|
|
|
namespace
|
|
{
|
|
/* I have heard that std::equal_range() is practically worthless
|
|
* due to the insane limitation that the two parameters for Comp() must
|
|
* be of the same type. Hence we must reinvent the wheel and implement
|
|
* our own here. This is practically identical to the one from
|
|
* GNU libstdc++, except rewritten. -Bisqwit
|
|
*/
|
|
template<typename It, typename T, typename Comp>
|
|
std::pair<It, It>
|
|
MyEqualRange(It first, It last, const T& val, Comp comp)
|
|
{
|
|
size_t len = last-first;
|
|
while(len > 0)
|
|
{
|
|
size_t half = len/2;
|
|
It middle(first); middle += half;
|
|
if(comp(*middle, val))
|
|
{
|
|
first = middle;
|
|
++first;
|
|
len = len - half - 1;
|
|
}
|
|
else if(comp(val, *middle))
|
|
{
|
|
len = half;
|
|
}
|
|
else
|
|
{
|
|
// The following implements this:
|
|
// // left = lower_bound(first, middle, val, comp);
|
|
It left(first);
|
|
{///
|
|
It& first2 = left;
|
|
It last2(middle);
|
|
size_t len2 = last2-first2;
|
|
while(len2 > 0)
|
|
{
|
|
size_t half2 = len2 / 2;
|
|
It middle2(first2); middle2 += half2;
|
|
if(comp(*middle2, val))
|
|
{
|
|
first2 = middle2;
|
|
++first2;
|
|
len2 = len2 - half2 - 1;
|
|
}
|
|
else
|
|
len2 = half2;
|
|
}
|
|
// left = first2; - not needed, already happens due to reference
|
|
}///
|
|
first += len;
|
|
// The following implements this:
|
|
// // right = upper_bound(++middle, first, val, comp);
|
|
It right(++middle);
|
|
{///
|
|
It& first2 = right;
|
|
It& last2 = first;
|
|
size_t len2 = last2-first2;
|
|
while(len2 > 0)
|
|
{
|
|
size_t half2 = len2 / 2;
|
|
It middle2(first2); middle2 += half2;
|
|
if(comp(val, *middle2))
|
|
len2 = half2;
|
|
else
|
|
{
|
|
first2 = middle2;
|
|
++first2;
|
|
len2 = len2 - half2 - 1;
|
|
}
|
|
}
|
|
// right = first2; - not needed, already happens due to reference
|
|
}///
|
|
return std::pair<It,It> (left,right);
|
|
}
|
|
}
|
|
return std::pair<It,It> (first,first);
|
|
}
|
|
|
|
/* A helper for std::equal_range */
|
|
struct OpcodeRuleCompare
|
|
{
|
|
bool operator() (const CodeTree& tree,
|
|
unsigned rulenumber) const
|
|
{
|
|
/* If this function returns true, len=half.
|
|
*/
|
|
const Rule& rule = grammar_rules[rulenumber];
|
|
return tree.GetOpcode() < rule.match_tree.subfunc_opcode;
|
|
}
|
|
bool operator() (unsigned rulenumber,
|
|
const CodeTree& tree) const
|
|
{
|
|
/* If this function returns true, rule will be excluded from the equal_range
|
|
*/
|
|
const Rule& rule = grammar_rules[rulenumber];
|
|
return rule.match_tree.subfunc_opcode < tree.GetOpcode();
|
|
}
|
|
};
|
|
|
|
/* Test and apply a rule to a given CodeTree */
|
|
bool TestRuleAndApplyIfMatch(
|
|
const Rule& rule,
|
|
CodeTree& tree,
|
|
bool from_logical_context)
|
|
{
|
|
MatchInfo info;
|
|
|
|
MatchResultType found(false, MatchPositionSpecBaseP());
|
|
|
|
if(rule.logical_context && !from_logical_context)
|
|
{
|
|
/* If the rule only applies in logical contexts,
|
|
* but we do not have a logical context, fail the rule
|
|
*/
|
|
goto fail;
|
|
}
|
|
|
|
/*std::cout << "TESTING: ";
|
|
DumpMatch(rule, *tree, info, false);*/
|
|
|
|
for(;;)
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
//DumpMatch(rule, tree, info, "Testing");
|
|
#endif
|
|
found = TestParams(rule.match_tree, tree, found.specs, info, true);
|
|
if(found.found) break;
|
|
if(!&*found.specs)
|
|
{
|
|
fail:;
|
|
// Did not match
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
DumpMatch(rule, tree, info, false);
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
// Matched
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
DumpMatch(rule, tree, info, true);
|
|
#endif
|
|
SynthesizeRule(rule, tree, info);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
namespace FPoptimizer_Optimize
|
|
{
|
|
/* Apply the grammar to a given CodeTree */
|
|
bool ApplyGrammar(
|
|
const Grammar& grammar,
|
|
CodeTree& tree,
|
|
bool from_logical_context)
|
|
{
|
|
if(tree.GetOptimizedUsing() == &grammar)
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Already optimized: ";
|
|
DumpTree(tree);
|
|
std::cout << "\n" << std::flush;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
/* First optimize all children */
|
|
if(true)
|
|
{
|
|
bool changed = false;
|
|
|
|
switch(tree.GetOpcode())
|
|
{
|
|
case cNot:
|
|
case cNotNot:
|
|
case cAnd:
|
|
case cOr:
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
if(ApplyGrammar( grammar, tree.GetParam(a), true))
|
|
changed = true;
|
|
break;
|
|
case cIf:
|
|
case cAbsIf:
|
|
if(ApplyGrammar( grammar, tree.GetParam(0), tree.GetOpcode() == cIf))
|
|
changed = true;
|
|
for(size_t a=1; a<tree.GetParamCount(); ++a)
|
|
if(ApplyGrammar( grammar, tree.GetParam(a), from_logical_context))
|
|
changed = true;
|
|
break;
|
|
default:
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
if(ApplyGrammar( grammar, tree.GetParam(a), false))
|
|
changed = true;
|
|
}
|
|
|
|
if(changed)
|
|
{
|
|
// Give the parent node a rerun at optimization
|
|
tree.Mark_Incompletely_Hashed();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/* Figure out which rules _may_ match this tree */
|
|
typedef const unsigned char* rulenumit;
|
|
|
|
std::pair<rulenumit, rulenumit> range =
|
|
MyEqualRange(grammar.rule_list,
|
|
grammar.rule_list + grammar.rule_count,
|
|
tree,
|
|
OpcodeRuleCompare());
|
|
|
|
if(range.first != range.second)
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::vector<unsigned char> rules;
|
|
rules.reserve(range.second - range.first);
|
|
for(rulenumit r = range.first; r != range.second; ++r)
|
|
{
|
|
//if(grammar_rules[*r].match_tree.subfunc_opcode != tree.GetOpcode()) continue;
|
|
if(IsLogisticallyPlausibleParamsMatch(grammar_rules[*r].match_tree, tree))
|
|
rules.push_back(*r);
|
|
}
|
|
range.first = &rules[0];
|
|
range.second = &rules[rules.size()-1]+1;
|
|
|
|
if(range.first != range.second)
|
|
{
|
|
std::cout << "Input (" << FP_GetOpcodeName(tree.GetOpcode())
|
|
<< ")[" << tree.GetParamCount()
|
|
<< "]";
|
|
if(from_logical_context)
|
|
std::cout << "(Logical)";
|
|
|
|
unsigned first=~unsigned(0), prev=~unsigned(0);
|
|
const char* sep = ", rules ";
|
|
for(rulenumit r = range.first; r != range.second; ++r)
|
|
{
|
|
if(first==~unsigned(0)) first=prev=*r;
|
|
else if(*r == prev+1) prev=*r;
|
|
else
|
|
{
|
|
std::cout << sep << first; sep=",";
|
|
if(prev != first) std::cout << '-' << prev;
|
|
first = prev = *r;
|
|
}
|
|
}
|
|
if(first != ~unsigned(0))
|
|
{
|
|
std::cout << sep << first;
|
|
if(prev != first) std::cout << '-' << prev;
|
|
}
|
|
std::cout << ": ";
|
|
DumpTree(tree);
|
|
std::cout << "\n" << std::flush;
|
|
}
|
|
#endif
|
|
|
|
bool changed = false;
|
|
|
|
for(rulenumit r = range.first; r != range.second; ++r)
|
|
{
|
|
#ifndef DEBUG_SUBSTITUTIONS
|
|
if(!IsLogisticallyPlausibleParamsMatch(grammar_rules[*r].match_tree, tree))
|
|
continue;
|
|
#endif
|
|
if(TestRuleAndApplyIfMatch(grammar_rules[*r], tree, from_logical_context))
|
|
{
|
|
changed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(changed)
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Changed." << std::endl;
|
|
std::cout << "Output: ";
|
|
DumpTree(tree);
|
|
std::cout << "\n" << std::flush;
|
|
#endif
|
|
// Give the parent node a rerun at optimization
|
|
tree.Mark_Incompletely_Hashed();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// No changes, consider the tree properly optimized.
|
|
tree.SetOptimizedUsing(&grammar);
|
|
return false;
|
|
}
|
|
|
|
void ApplyGrammars(FPoptimizer_CodeTree::CodeTree& tree)
|
|
{
|
|
#ifdef FPOPTIMIZER_MERGED_FILE
|
|
#define C *(const Grammar*)&
|
|
#else
|
|
#define C
|
|
#endif
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Applying grammar_optimize_round1\n";
|
|
#endif
|
|
while(ApplyGrammar(C grammar_optimize_round1, tree))
|
|
{ //std::cout << "Rerunning 1\n";
|
|
tree.FixIncompleteHashes();
|
|
}
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Applying grammar_optimize_round2\n";
|
|
#endif
|
|
while(ApplyGrammar(C grammar_optimize_round2, tree))
|
|
{ //std::cout << "Rerunning 2\n";
|
|
tree.FixIncompleteHashes();
|
|
}
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Applying grammar_optimize_round3\n";
|
|
#endif
|
|
while(ApplyGrammar(C grammar_optimize_round3, tree))
|
|
{ //std::cout << "Rerunning 3\n";
|
|
tree.FixIncompleteHashes();
|
|
}
|
|
|
|
#ifndef FP_ENABLE_SHORTCUT_LOGICAL_EVALUATION
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Applying grammar_optimize_nonshortcut_logical_evaluation\n";
|
|
#endif
|
|
while(ApplyGrammar(C grammar_optimize_nonshortcut_logical_evaluation, tree))
|
|
{ //std::cout << "Rerunning 3\n";
|
|
tree.FixIncompleteHashes();
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Applying grammar_optimize_round4\n";
|
|
#endif
|
|
while(ApplyGrammar(C grammar_optimize_round4, tree))
|
|
{ //std::cout << "Rerunning 4\n";
|
|
tree.FixIncompleteHashes();
|
|
}
|
|
|
|
#ifdef FP_ENABLE_SHORTCUT_LOGICAL_EVALUATION
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Applying grammar_optimize_shortcut_logical_evaluation\n";
|
|
#endif
|
|
while(ApplyGrammar(C grammar_optimize_shortcut_logical_evaluation, tree))
|
|
{ //std::cout << "Rerunning 3\n";
|
|
tree.FixIncompleteHashes();
|
|
}
|
|
#endif
|
|
|
|
#ifdef FP_ENABLE_IGNORE_IF_SIDEEFFECTS
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Applying grammar_optimize_ignore_if_sideeffects\n";
|
|
#endif
|
|
while(ApplyGrammar(C grammar_optimize_ignore_if_sideeffects, tree))
|
|
{ //std::cout << "Rerunning 3\n";
|
|
tree.FixIncompleteHashes();
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Applying grammar_optimize_abslogical\n";
|
|
#endif
|
|
while(ApplyGrammar(C grammar_optimize_abslogical, tree))
|
|
{ //std::cout << "Rerunning 3\n";
|
|
tree.FixIncompleteHashes();
|
|
}
|
|
|
|
#undef C
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_optimize_match.c"
|
|
#include "fpconfig.h"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
#include <cstring>
|
|
#include <cmath>
|
|
|
|
#include <memory> /* for auto_ptr */
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
using namespace FPoptimizer_Grammar;
|
|
using namespace FPoptimizer_CodeTree;
|
|
using namespace FPoptimizer_Optimize;
|
|
|
|
namespace
|
|
{
|
|
/* Test the given constraints to a given CodeTree */
|
|
bool TestImmedConstraints(unsigned bitmask, const CodeTree& tree)
|
|
{
|
|
switch(bitmask & ValueMask)
|
|
{
|
|
case Value_AnyNum: case ValueMask: break;
|
|
case Value_EvenInt:
|
|
if(tree.GetEvennessInfo() != CodeTree::IsAlways)
|
|
return false;
|
|
break;
|
|
case Value_OddInt:
|
|
if(tree.GetEvennessInfo() != CodeTree::IsNever)
|
|
return false;
|
|
break;
|
|
case Value_IsInteger:
|
|
if(!tree.IsAlwaysInteger(true)) return false;
|
|
break;
|
|
case Value_NonInteger:
|
|
if(!tree.IsAlwaysInteger(false)) return false;
|
|
break;
|
|
case Value_Logical:
|
|
if(!tree.IsLogicalValue()) return false;
|
|
break;
|
|
}
|
|
switch(bitmask & SignMask)
|
|
{
|
|
case Sign_AnySign: /*case SignMask:*/ break;
|
|
case Sign_Positive:
|
|
if(!tree.IsAlwaysSigned(true)) return false;
|
|
break;
|
|
case Sign_Negative:
|
|
if(!tree.IsAlwaysSigned(false)) return false;
|
|
break;
|
|
case Sign_NoIdea:
|
|
if(tree.IsAlwaysSigned(true)) return false;
|
|
if(tree.IsAlwaysSigned(false)) return false;
|
|
break;
|
|
}
|
|
switch(bitmask & OnenessMask)
|
|
{
|
|
case Oneness_Any: case OnenessMask: break;
|
|
case Oneness_One:
|
|
if(!tree.IsImmed()) return false;
|
|
if(!FloatEqual(fabs(tree.GetImmed()), 1.0)) return false;
|
|
break;
|
|
case Oneness_NotOne:
|
|
if(!tree.IsImmed()) return false;
|
|
if(FloatEqual(fabs(tree.GetImmed()), 1.0)) return false;
|
|
break;
|
|
}
|
|
switch(bitmask & ConstnessMask)
|
|
{
|
|
case Constness_Any: /*case ConstnessMask:*/ break;
|
|
case Constness_Const:
|
|
if(!tree.IsImmed()) return false;
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<unsigned extent, unsigned nbits, typename item_type=unsigned int>
|
|
struct nbitmap
|
|
{
|
|
private:
|
|
static const unsigned bits_in_char = 8;
|
|
static const unsigned per_item = (sizeof(item_type)*bits_in_char)/nbits;
|
|
item_type data[(extent+per_item-1) / per_item];
|
|
public:
|
|
void inc(unsigned index, int by=1)
|
|
{
|
|
data[pos(index)] += by * item_type(1 << shift(index));
|
|
}
|
|
inline void dec(unsigned index) { inc(index, -1); }
|
|
int get(unsigned index) const { return (data[pos(index)] >> shift(index)) & mask(); }
|
|
|
|
static inline unsigned pos(unsigned index) { return index/per_item; }
|
|
static inline unsigned shift(unsigned index) { return nbits * (index%per_item); }
|
|
static inline unsigned mask() { return (1 << nbits)-1; }
|
|
static inline unsigned mask(unsigned index) { return mask() << shift(index); }
|
|
};
|
|
|
|
struct Needs
|
|
{
|
|
int SubTrees : 8; // This many subtrees
|
|
int Others : 8; // This many others (namedholder)
|
|
int minimum_need : 8; // At least this many leaves (restholder may require more)
|
|
int Immeds : 8; // This many immeds
|
|
|
|
nbitmap<VarBegin,2> SubTreesDetail; // This many subtrees of each opcode type
|
|
|
|
Needs()
|
|
{
|
|
std::memset(this, 0, sizeof(*this));
|
|
}
|
|
Needs(const Needs& b)
|
|
{
|
|
std::memcpy(this, &b, sizeof(b));
|
|
}
|
|
Needs& operator= (const Needs& b)
|
|
{
|
|
std::memcpy(this, &b, sizeof(b));
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
Needs CreateNeedList_uncached(const ParamSpec_SubFunctionData& params)
|
|
{
|
|
Needs NeedList;
|
|
|
|
// Figure out what we need
|
|
for(unsigned a = 0; a < params.param_count; ++a)
|
|
{
|
|
const ParamSpec& parampair = ParamSpec_Extract(params.param_list, a);
|
|
switch(parampair.first)
|
|
{
|
|
case SubFunction:
|
|
{
|
|
const ParamSpec_SubFunction& param = *(const ParamSpec_SubFunction*) parampair.second;
|
|
if(param.data.match_type == GroupFunction)
|
|
++NeedList.Immeds;
|
|
else
|
|
{
|
|
++NeedList.SubTrees;
|
|
assert( param.data.subfunc_opcode < VarBegin );
|
|
NeedList.SubTreesDetail.inc(param.data.subfunc_opcode);
|
|
}
|
|
++NeedList.minimum_need;
|
|
break;
|
|
}
|
|
case NumConstant:
|
|
case ParamHolder:
|
|
++NeedList.Others;
|
|
++NeedList.minimum_need;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NeedList;
|
|
}
|
|
|
|
Needs& CreateNeedList(const ParamSpec_SubFunctionData& params)
|
|
{
|
|
typedef std::map<const ParamSpec_SubFunctionData*, Needs> needlist_cached_t;
|
|
static needlist_cached_t needlist_cached;
|
|
|
|
needlist_cached_t::iterator i = needlist_cached.lower_bound(¶ms);
|
|
if(i != needlist_cached.end() && i->first == ¶ms)
|
|
return i->second;
|
|
|
|
return
|
|
needlist_cached.insert(i,
|
|
std::make_pair(¶ms, CreateNeedList_uncached(params))
|
|
)->second;
|
|
}
|
|
/* Construct CodeTree from a GroupFunction, hopefully evaluating to a constant value */
|
|
CodeTree CalculateGroupFunction(
|
|
const ParamSpec& parampair,
|
|
const MatchInfo& info)
|
|
{
|
|
switch( parampair.first )
|
|
{
|
|
case NumConstant:
|
|
{
|
|
const ParamSpec_NumConstant& param = *(const ParamSpec_NumConstant*) parampair.second;
|
|
return CodeTree( param.constvalue ); // Note: calculates hash too.
|
|
}
|
|
case ParamHolder:
|
|
{
|
|
const ParamSpec_ParamHolder& param = *(const ParamSpec_ParamHolder*) parampair.second;
|
|
return info.GetParamHolderValueIfFound( param.index );
|
|
// If the ParamHolder is not defined, it will simply
|
|
// return an Undefined tree. This is ok.
|
|
}
|
|
case SubFunction:
|
|
{
|
|
const ParamSpec_SubFunction& param = *(const ParamSpec_SubFunction*) parampair.second;
|
|
/* Synthesize a CodeTree which will take care of
|
|
* constant-folding our expression. It will also
|
|
* indicate whether the result is, in fact,
|
|
* a constant at all. */
|
|
CodeTree result;
|
|
result.SetOpcode( param.data.subfunc_opcode );
|
|
result.GetParams().reserve(param.data.param_count);
|
|
for(unsigned a=0; a<param.data.param_count; ++a)
|
|
{
|
|
CodeTree tmp(
|
|
CalculateGroupFunction
|
|
(ParamSpec_Extract(param.data.param_list, a), info)
|
|
);
|
|
result.AddParamMove(tmp);
|
|
}
|
|
result.Rehash(); // This will also call ConstantFolding().
|
|
return result;
|
|
}
|
|
}
|
|
// Issue an un-calculatable tree. (This should be unreachable)
|
|
return CodeTree(); // cNop
|
|
}
|
|
}
|
|
|
|
namespace FPoptimizer_Optimize
|
|
{
|
|
/* Test the list of parameters to a given CodeTree */
|
|
/* A helper function which simply checks whether the
|
|
* basic shape of the tree matches what we are expecting
|
|
* i.e. given number of numeric constants, etc.
|
|
*/
|
|
bool IsLogisticallyPlausibleParamsMatch(
|
|
const ParamSpec_SubFunctionData& params,
|
|
const CodeTree& tree)
|
|
{
|
|
/* First, check if the tree has any chances of matching... */
|
|
/* Figure out what we need. */
|
|
Needs NeedList ( CreateNeedList(params) );
|
|
|
|
size_t nparams = tree.GetParamCount();
|
|
|
|
if(nparams < size_t(NeedList.minimum_need))
|
|
{
|
|
// Impossible to satisfy
|
|
return false;
|
|
}
|
|
|
|
// Figure out what we have (note: we already assume that the opcode of the tree matches!)
|
|
for(size_t a=0; a<nparams; ++a)
|
|
{
|
|
unsigned opcode = tree.GetParam(a).GetOpcode();
|
|
switch(opcode)
|
|
{
|
|
case cImmed:
|
|
if(NeedList.Immeds > 0) --NeedList.Immeds;
|
|
else --NeedList.Others;
|
|
break;
|
|
case VarBegin:
|
|
case cFCall:
|
|
case cPCall:
|
|
--NeedList.Others;
|
|
break;
|
|
default:
|
|
assert( opcode < VarBegin );
|
|
if(NeedList.SubTrees > 0
|
|
&& NeedList.SubTreesDetail.get(opcode) > 0)
|
|
{
|
|
--NeedList.SubTrees;
|
|
NeedList.SubTreesDetail.dec(opcode);
|
|
}
|
|
else --NeedList.Others;
|
|
}
|
|
}
|
|
|
|
// Check whether all needs were satisfied
|
|
if(NeedList.Immeds > 0
|
|
|| NeedList.SubTrees > 0
|
|
|| NeedList.Others > 0)
|
|
{
|
|
// Something came short, impossible to satisfy.
|
|
return false;
|
|
}
|
|
|
|
if(params.match_type != AnyParams)
|
|
{
|
|
if(0
|
|
//|| NeedList.Immeds < 0 - already checked
|
|
|| NeedList.SubTrees < 0
|
|
|| NeedList.Others < 0
|
|
//|| params.count != nparams - already checked
|
|
)
|
|
{
|
|
// Something was too much.
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Test the given parameter to a given CodeTree */
|
|
MatchResultType TestParam(
|
|
const ParamSpec& parampair,
|
|
const CodeTree& tree,
|
|
const MatchPositionSpecBaseP& start_at,
|
|
MatchInfo& info)
|
|
{
|
|
/*std::cout << "TestParam(";
|
|
DumpParam(parampair);
|
|
std::cout << ", ";
|
|
DumpTree(tree);
|
|
std::cout << ")\n";*/
|
|
|
|
/* What kind of param are we expecting */
|
|
switch( parampair.first )
|
|
{
|
|
case NumConstant: /* A particular numeric value */
|
|
{
|
|
const ParamSpec_NumConstant& param = *(const ParamSpec_NumConstant*) parampair.second;
|
|
if(!tree.IsImmed()) return false;
|
|
return FloatEqual(tree.GetImmed(), param.constvalue);
|
|
}
|
|
case ParamHolder: /* Any arbitrary node */
|
|
{
|
|
const ParamSpec_ParamHolder& param = *(const ParamSpec_ParamHolder*) parampair.second;
|
|
if(!TestImmedConstraints(param.constraints, tree)) return false;
|
|
return info.SaveOrTestParamHolder(param.index, tree);
|
|
}
|
|
case SubFunction:
|
|
{
|
|
const ParamSpec_SubFunction& param = *(const ParamSpec_SubFunction*) parampair.second;
|
|
if(param.data.match_type == GroupFunction)
|
|
{ /* A constant value acquired from this formula */
|
|
if(!TestImmedConstraints(param.constraints, tree)) return false;
|
|
/* Construct the formula */
|
|
CodeTree grammar_func = CalculateGroupFunction(parampair, info);
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
DumpHashes(grammar_func);
|
|
std::cout << *(const void**)&grammar_func.GetImmed();
|
|
std::cout << "\n";
|
|
std::cout << *(const void**)&tree.GetImmed();
|
|
std::cout << "\n";
|
|
DumpHashes(tree);
|
|
std::cout << "Comparing ";
|
|
DumpTree(grammar_func);
|
|
std::cout << " and ";
|
|
DumpTree(tree);
|
|
std::cout << ": ";
|
|
std::cout << (grammar_func.IsIdenticalTo(tree) ? "true" : "false");
|
|
std::cout << "\n";
|
|
#endif
|
|
/* Evaluate it and compare */
|
|
return grammar_func.IsIdenticalTo(tree);
|
|
}
|
|
else /* A subtree conforming these specs */
|
|
{
|
|
if(!&*start_at)
|
|
{
|
|
if(!TestImmedConstraints(param.constraints, tree)) return false;
|
|
if(tree.GetOpcode() != param.data.subfunc_opcode) return false;
|
|
}
|
|
return TestParams(param.data, tree, start_at, info, false);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct PositionalParams_Rec
|
|
{
|
|
MatchPositionSpecBaseP start_at; /* child's start_at */
|
|
MatchInfo info; /* backup of "info" at start */
|
|
|
|
PositionalParams_Rec(): start_at(), info() { }
|
|
};
|
|
class MatchPositionSpec_PositionalParams
|
|
: public MatchPositionSpecBase,
|
|
public std::vector<PositionalParams_Rec>
|
|
{
|
|
public:
|
|
explicit MatchPositionSpec_PositionalParams(size_t n)
|
|
: MatchPositionSpecBase(),
|
|
std::vector<PositionalParams_Rec> (n)
|
|
{ }
|
|
};
|
|
|
|
struct AnyWhere_Rec
|
|
{
|
|
MatchPositionSpecBaseP start_at; /* child's start_at */
|
|
AnyWhere_Rec() : start_at() { }
|
|
};
|
|
class MatchPositionSpec_AnyWhere
|
|
: public MatchPositionSpecBase,
|
|
public std::vector<AnyWhere_Rec>
|
|
{
|
|
public:
|
|
unsigned trypos; /* which param index to try next */
|
|
|
|
explicit MatchPositionSpec_AnyWhere(size_t n)
|
|
: MatchPositionSpecBase(),
|
|
std::vector<AnyWhere_Rec> (n),
|
|
trypos(0)
|
|
{ }
|
|
};
|
|
|
|
MatchResultType TestParam_AnyWhere(
|
|
const ParamSpec& parampair,
|
|
const CodeTree& tree,
|
|
const MatchPositionSpecBaseP& start_at,
|
|
MatchInfo& info,
|
|
std::vector<bool>& used,
|
|
bool TopLevel)
|
|
{
|
|
FPOPT_autoptr<MatchPositionSpec_AnyWhere> position;
|
|
unsigned a;
|
|
if(&*start_at)
|
|
{
|
|
position = (MatchPositionSpec_AnyWhere*) &*start_at;
|
|
a = position->trypos;
|
|
goto retry_anywhere_2;
|
|
}
|
|
else
|
|
{
|
|
position = new MatchPositionSpec_AnyWhere(tree.GetParamCount());
|
|
a = 0;
|
|
}
|
|
for(; a < tree.GetParamCount(); ++a)
|
|
{
|
|
if(used[a]) continue;
|
|
|
|
retry_anywhere:
|
|
{ MatchResultType r = TestParam(
|
|
parampair,
|
|
tree.GetParam(a),
|
|
(*position)[a].start_at,
|
|
info);
|
|
|
|
(*position)[a].start_at = r.specs;
|
|
if(r.found)
|
|
{
|
|
used[a] = true; // matched
|
|
if(TopLevel) info.SaveMatchedParamIndex(a);
|
|
|
|
position->trypos = a; // in case of backtrack, try a again
|
|
return MatchResultType(true, &*position);
|
|
} }
|
|
retry_anywhere_2:
|
|
if(&*(*position)[a].start_at) // is there another try?
|
|
{
|
|
goto retry_anywhere;
|
|
}
|
|
// no, move on
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct AnyParams_Rec
|
|
{
|
|
MatchPositionSpecBaseP start_at; /* child's start_at */
|
|
MatchInfo info; /* backup of "info" at start */
|
|
std::vector<bool> used; /* which params are remaining */
|
|
|
|
explicit AnyParams_Rec(size_t nparams)
|
|
: start_at(), info(), used(nparams) { }
|
|
};
|
|
class MatchPositionSpec_AnyParams
|
|
: public MatchPositionSpecBase,
|
|
public std::vector<AnyParams_Rec>
|
|
{
|
|
public:
|
|
explicit MatchPositionSpec_AnyParams(size_t n, size_t m)
|
|
: MatchPositionSpecBase(),
|
|
std::vector<AnyParams_Rec> (n, AnyParams_Rec(m))
|
|
{ }
|
|
};
|
|
|
|
/* Test the list of parameters to a given CodeTree */
|
|
MatchResultType TestParams(
|
|
const ParamSpec_SubFunctionData& model_tree,
|
|
const CodeTree& tree,
|
|
const MatchPositionSpecBaseP& start_at,
|
|
MatchInfo& info,
|
|
bool TopLevel)
|
|
{
|
|
/* When PositionalParams or SelectedParams, verify that
|
|
* the number of parameters is exactly as expected.
|
|
*/
|
|
if(model_tree.match_type != AnyParams)
|
|
{
|
|
if(model_tree.param_count != tree.GetParamCount())
|
|
return false;
|
|
}
|
|
|
|
/* Verify that the tree basically conforms the shape we are expecting */
|
|
/* This test is not necessary; it may just save us some work. */
|
|
if(!IsLogisticallyPlausibleParamsMatch(model_tree, tree))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* Verify each parameter that they are found in the tree as expected. */
|
|
switch(model_tree.match_type)
|
|
{
|
|
case PositionalParams:
|
|
{
|
|
/* Simple: Test all given parameters in succession. */
|
|
FPOPT_autoptr<MatchPositionSpec_PositionalParams> position;
|
|
unsigned a;
|
|
if(&*start_at)
|
|
{
|
|
position = (MatchPositionSpec_PositionalParams*) &*start_at;
|
|
a = model_tree.param_count - 1;
|
|
goto retry_positionalparams_2;
|
|
}
|
|
else
|
|
{
|
|
position = new MatchPositionSpec_PositionalParams(model_tree.param_count);
|
|
a = 0;
|
|
}
|
|
|
|
for(; a < model_tree.param_count; ++a)
|
|
{
|
|
(*position)[a].info = info;
|
|
retry_positionalparams:
|
|
{ MatchResultType r = TestParam(
|
|
ParamSpec_Extract(model_tree.param_list, a),
|
|
tree.GetParam(a),
|
|
(*position)[a].start_at,
|
|
info);
|
|
|
|
(*position)[a].start_at = r.specs;
|
|
if(r.found)
|
|
{
|
|
continue;
|
|
} }
|
|
retry_positionalparams_2:
|
|
// doesn't match
|
|
if(&*(*position)[a].start_at) // is there another try?
|
|
{
|
|
info = (*position)[a].info;
|
|
goto retry_positionalparams;
|
|
}
|
|
// no, backtrack
|
|
if(a > 0)
|
|
{
|
|
--a;
|
|
goto retry_positionalparams_2;
|
|
}
|
|
// cannot backtrack
|
|
info = (*position)[0].info;
|
|
return false;
|
|
}
|
|
if(TopLevel)
|
|
for(unsigned a = 0; a < model_tree.param_count; ++a)
|
|
info.SaveMatchedParamIndex(a);
|
|
return MatchResultType(true, &*position);
|
|
}
|
|
case SelectedParams:
|
|
// same as AnyParams, except that model_tree.count==tree.GetParamCount()
|
|
// and that there are no RestHolders
|
|
case AnyParams:
|
|
{
|
|
/* Ensure that all given parameters are found somewhere, in any order */
|
|
|
|
FPOPT_autoptr<MatchPositionSpec_AnyParams> position;
|
|
std::vector<bool> used( tree.GetParamCount() );
|
|
std::vector<unsigned> depcodes( model_tree.param_count );
|
|
std::vector<unsigned> test_order( model_tree.param_count );
|
|
for(unsigned a=0; a<model_tree.param_count; ++a)
|
|
{
|
|
const ParamSpec parampair = ParamSpec_Extract(model_tree.param_list, a);
|
|
depcodes[a] = ParamSpec_GetDepCode(parampair);
|
|
}
|
|
{ unsigned b=0;
|
|
for(unsigned a=0; a<model_tree.param_count; ++a)
|
|
if(depcodes[a] != 0)
|
|
test_order[b++] = a;
|
|
for(unsigned a=0; a<model_tree.param_count; ++a)
|
|
if(depcodes[a] == 0)
|
|
test_order[b++] = a;
|
|
}
|
|
|
|
unsigned a;
|
|
if(&*start_at)
|
|
{
|
|
position = (MatchPositionSpec_AnyParams*) &*start_at;
|
|
if(model_tree.param_count == 0)
|
|
{
|
|
a = 0;
|
|
goto retry_anyparams_4;
|
|
}
|
|
a = model_tree.param_count - 1;
|
|
goto retry_anyparams_2;
|
|
}
|
|
else
|
|
{
|
|
position = new MatchPositionSpec_AnyParams(model_tree.param_count,
|
|
tree.GetParamCount());
|
|
a = 0;
|
|
if(model_tree.param_count != 0)
|
|
{
|
|
(*position)[0].info = info;
|
|
(*position)[0].used = used;
|
|
}
|
|
}
|
|
// Match all but restholders
|
|
for(; a < model_tree.param_count; ++a)
|
|
{
|
|
if(a > 0) // this test is not necessary, but it saves from doing
|
|
{ // duplicate work, because [0] was already saved above.
|
|
(*position)[a].info = info;
|
|
(*position)[a].used = used;
|
|
}
|
|
retry_anyparams:
|
|
{ MatchResultType r = TestParam_AnyWhere(
|
|
ParamSpec_Extract(model_tree.param_list, test_order[a]),
|
|
tree,
|
|
(*position)[a].start_at,
|
|
info,
|
|
used,
|
|
TopLevel);
|
|
(*position)[a].start_at = r.specs;
|
|
if(r.found)
|
|
{
|
|
continue;
|
|
} }
|
|
retry_anyparams_2:
|
|
// doesn't match
|
|
if(&*(*position)[a].start_at) // is there another try?
|
|
{
|
|
info = (*position)[a].info;
|
|
used = (*position)[a].used;
|
|
goto retry_anyparams;
|
|
}
|
|
// no, backtrack
|
|
retry_anyparams_3:
|
|
if(a > 0)
|
|
{
|
|
--a;
|
|
goto retry_anyparams_2;
|
|
}
|
|
// cannot backtrack
|
|
info = (*position)[0].info;
|
|
return false;
|
|
}
|
|
retry_anyparams_4:
|
|
// Capture anything remaining in the restholder
|
|
if(model_tree.restholder_index != 0)
|
|
{
|
|
//std::vector<bool> used_backup(used);
|
|
//MatchInfo info_backup(info);
|
|
|
|
if(!TopLevel
|
|
|| !info.HasRestHolder(model_tree.restholder_index))
|
|
{
|
|
std::vector<CodeTree> matches;
|
|
matches.reserve(tree.GetParamCount());
|
|
for(unsigned b = 0; b < tree.GetParamCount(); ++b)
|
|
{
|
|
if(used[b]) continue; // Ignore subtrees that were already used
|
|
// Save this tree to this restholder
|
|
|
|
matches.push_back(tree.GetParam(b));
|
|
used[b] = true;
|
|
if(TopLevel) info.SaveMatchedParamIndex(b);
|
|
}
|
|
if(!info.SaveOrTestRestHolder(model_tree.restholder_index, matches))
|
|
{
|
|
// Failure at restholder matching. Backtrack if possible.
|
|
//used.swap(used_backup);
|
|
//info.swap(info_backup);
|
|
goto retry_anyparams_3;
|
|
}
|
|
//std::cout << "Saved restholder " << model_tree.restholder_index << "\n";
|
|
}
|
|
else
|
|
{
|
|
const std::vector<CodeTree>& matches
|
|
= info.GetRestHolderValues(model_tree.restholder_index);
|
|
//std::cout << "Testing restholder " << model_tree.restholder_index << std::flush;
|
|
for(size_t a=0; a<matches.size(); ++a)
|
|
{
|
|
bool found = false;
|
|
for(unsigned b = 0; b < tree.GetParamCount(); ++b)
|
|
{
|
|
if(used[b]) continue;
|
|
if(matches[a].IsIdenticalTo(tree.GetParam(b)))
|
|
{
|
|
used[b] = true;
|
|
if(TopLevel) info.SaveMatchedParamIndex(b);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!found)
|
|
{
|
|
//std::cout << " ... failed\n";
|
|
// Failure at restholder matching. Backtrack if possible.
|
|
//used.swap(used_backup);
|
|
//info.swap(info_backup);
|
|
goto retry_anyparams_3;
|
|
}
|
|
}
|
|
//std::cout << " ... ok\n";
|
|
}
|
|
}
|
|
return MatchResultType(true, model_tree.param_count ? &*position : 0);
|
|
}
|
|
case GroupFunction: // never occurs
|
|
break;
|
|
}
|
|
return false; // doesn't match
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_optimize_synth.c"
|
|
#include "fpconfig.h"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
|
|
namespace FPoptimizer_Optimize
|
|
{
|
|
/* Synthesize the given grammatic parameter into the codetree */
|
|
void SynthesizeParam(
|
|
const ParamSpec& parampair,
|
|
CodeTree& tree,
|
|
MatchInfo& info,
|
|
bool inner = true)
|
|
{
|
|
switch( parampair.first )
|
|
{
|
|
case NumConstant:
|
|
{ const ParamSpec_NumConstant& param = *(const ParamSpec_NumConstant*) parampair.second;
|
|
tree.SetImmed( param.constvalue );
|
|
if(inner) tree.Rehash(false);
|
|
break; }
|
|
case ParamHolder:
|
|
{ const ParamSpec_ParamHolder& param = *(const ParamSpec_ParamHolder*) parampair.second;
|
|
tree.Become( info.GetParamHolderValue( param.index ) );
|
|
break; }
|
|
case SubFunction:
|
|
{ const ParamSpec_SubFunction& param = *(const ParamSpec_SubFunction*) parampair.second;
|
|
tree.SetOpcode( param.data.subfunc_opcode );
|
|
for(unsigned a=0; a < param.data.param_count; ++a)
|
|
{
|
|
CodeTree nparam;
|
|
SynthesizeParam( ParamSpec_Extract(param.data.param_list, a), nparam, info, true );
|
|
tree.AddParamMove(nparam);
|
|
}
|
|
if(param.data.restholder_index != 0)
|
|
{
|
|
std::vector<CodeTree> trees
|
|
( info.GetRestHolderValues( param.data.restholder_index ) );
|
|
tree.AddParamsMove(trees);
|
|
// ^note: this fails if the same restholder is synth'd twice
|
|
if(tree.GetParamCount() == 1)
|
|
{
|
|
/* Convert cMul <1> into <1> when <1> only contains one operand.
|
|
* This is redundant code; it is also done in ConstantFolding(),
|
|
* but it might be better for performance to do it here, too.
|
|
*/
|
|
assert(tree.GetOpcode() == cAdd || tree.GetOpcode() == cMul
|
|
|| tree.GetOpcode() == cMin || tree.GetOpcode() == cMax
|
|
|| tree.GetOpcode() == cAnd || tree.GetOpcode() == cOr);
|
|
tree.Become(tree.GetParam(0));
|
|
}
|
|
}
|
|
if(inner)
|
|
tree.Rehash();
|
|
break; }
|
|
}
|
|
}
|
|
|
|
void SynthesizeRule(
|
|
const Rule& rule,
|
|
CodeTree& tree,
|
|
MatchInfo& info)
|
|
{
|
|
switch(rule.ruletype)
|
|
{
|
|
case ProduceNewTree:
|
|
{
|
|
tree.DelParams();
|
|
SynthesizeParam( ParamSpec_Extract(rule.repl_param_list, 0), tree, info, false );
|
|
break;
|
|
}
|
|
case ReplaceParams:
|
|
default:
|
|
{
|
|
/* Delete the matched parameters from the source tree */
|
|
std::vector<unsigned> list = info.GetMatchedParamIndexes();
|
|
std::sort(list.begin(), list.end());
|
|
for(size_t a=list.size(); a-->0; )
|
|
tree.DelParam( list[a] );
|
|
|
|
/* Synthesize the replacement params */
|
|
for(unsigned a=0; a < rule.repl_param_count; ++a)
|
|
{
|
|
CodeTree nparam;
|
|
SynthesizeParam( ParamSpec_Extract(rule.repl_param_list, a), nparam, info, true );
|
|
tree.AddParamMove(nparam);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_optimize_debug.c"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_opcodename.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
|
|
#include <sstream>
|
|
#include <cstring>
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
using namespace FPoptimizer_Grammar;
|
|
using namespace FPoptimizer_CodeTree;
|
|
using namespace FPoptimizer_Optimize;
|
|
|
|
namespace FPoptimizer_Grammar
|
|
{
|
|
void DumpMatch(const Rule& rule,
|
|
const CodeTree& tree,
|
|
const MatchInfo& info,
|
|
bool DidMatch,
|
|
std::ostream& o)
|
|
{
|
|
DumpMatch(rule,tree,info,DidMatch?"Found match":"Found mismatch",o);
|
|
}
|
|
|
|
void DumpMatch(const Rule& rule,
|
|
const CodeTree& tree,
|
|
const MatchInfo& info,
|
|
const char* whydump,
|
|
std::ostream& o)
|
|
{
|
|
static const char ParamHolderNames[][2] = {"%","&","x","y","z","a","b","c"};
|
|
|
|
o << whydump
|
|
<< " (rule " << (&rule - grammar_rules) << ")"
|
|
<< ":\n"
|
|
" Pattern : ";
|
|
{ ParamSpec tmp;
|
|
tmp.first = SubFunction;
|
|
ParamSpec_SubFunction tmp2;
|
|
tmp2.data = rule.match_tree;
|
|
tmp.second = (const void*) &tmp2;
|
|
DumpParam(tmp, o);
|
|
}
|
|
o << "\n"
|
|
" Replacement: ";
|
|
DumpParams(rule.repl_param_list, rule.repl_param_count, o);
|
|
o << "\n";
|
|
|
|
o <<
|
|
" Tree : ";
|
|
DumpTree(tree, o);
|
|
o << "\n";
|
|
if(!std::strcmp(whydump,"Found match")) DumpHashes(tree, o);
|
|
|
|
for(size_t a=0; a<info.paramholder_matches.size(); ++a)
|
|
{
|
|
if(!info.paramholder_matches[a].IsDefined()) continue;
|
|
o << " " << ParamHolderNames[a] << " = ";
|
|
DumpTree(info.paramholder_matches[a], o);
|
|
o << "\n";
|
|
}
|
|
|
|
for(size_t b=0; b<info.restholder_matches.size(); ++b)
|
|
{
|
|
if(!info.restholder_matches[b].first) continue;
|
|
for(size_t a=0; a<info.restholder_matches[b].second.size(); ++a)
|
|
{
|
|
o << " <" << b << "> = ";
|
|
DumpTree(info.restholder_matches[b].second[a], o);
|
|
o << std::endl;
|
|
}
|
|
}
|
|
o << std::flush;
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_hash.c"
|
|
#include <list>
|
|
#include <algorithm>
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
#include "fptypes.h"
|
|
// line removed for fpoptimizer.c: #include "crc32.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
//using namespace FPoptimizer_Grammar;
|
|
|
|
namespace
|
|
{
|
|
bool MarkIncompletes(FPoptimizer_CodeTree::CodeTree& tree)
|
|
{
|
|
if(tree.Is_Incompletely_Hashed())
|
|
return true;
|
|
|
|
bool needs_rehash = false;
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
needs_rehash |= MarkIncompletes(tree.GetParam(a));
|
|
if(needs_rehash)
|
|
tree.Mark_Incompletely_Hashed();
|
|
return needs_rehash;
|
|
}
|
|
|
|
void FixIncompletes(FPoptimizer_CodeTree::CodeTree& tree)
|
|
{
|
|
if(tree.Is_Incompletely_Hashed())
|
|
{
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
FixIncompletes(tree.GetParam(a));
|
|
tree.Rehash();
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
void CodeTree::Rehash(bool constantfolding)
|
|
{
|
|
if(constantfolding)
|
|
ConstantFolding();
|
|
data->Sort();
|
|
data->Recalculate_Hash_NoRecursion();
|
|
}
|
|
|
|
void CodeTreeData::Recalculate_Hash_NoRecursion()
|
|
{
|
|
fphash_t NewHash = { Opcode * FPHASH_CONST(0x3A83A83A83A83A0),
|
|
Opcode * FPHASH_CONST(0x1131462E270012B)};
|
|
Depth = 1;
|
|
switch(Opcode)
|
|
{
|
|
case cImmed:
|
|
if(Value != 0.0)
|
|
{
|
|
crc32_t crc = crc32::calc( (const unsigned char*) &Value,
|
|
sizeof(Value) );
|
|
NewHash.hash1 ^= crc | (fphash_value_t(crc) << FPHASH_CONST(32));
|
|
NewHash.hash2 += ((~fphash_value_t(crc)) * 3) ^ 1234567;
|
|
}
|
|
break; // no params
|
|
case VarBegin:
|
|
NewHash.hash1 ^= (Var<<24) | (Var>>24);
|
|
NewHash.hash2 += (fphash_value_t(Var)*5) ^ 2345678;
|
|
break; // no params
|
|
case cFCall: case cPCall:
|
|
{
|
|
crc32_t crc = crc32::calc( (const unsigned char*) &Funcno, sizeof(Funcno) );
|
|
NewHash.hash1 ^= (crc<<24) | (crc>>24);
|
|
NewHash.hash2 += ((~fphash_value_t(crc)) * 7) ^ 3456789;
|
|
/* passthru */
|
|
}
|
|
default:
|
|
{
|
|
size_t MaxChildDepth = 0;
|
|
for(size_t a=0; a<Params.size(); ++a)
|
|
{
|
|
if(Params[a].GetDepth() > MaxChildDepth)
|
|
MaxChildDepth = Params[a].GetDepth();
|
|
|
|
NewHash.hash1 += (1)*FPHASH_CONST(0x2492492492492492);
|
|
NewHash.hash1 *= FPHASH_CONST(1099511628211);
|
|
//assert(&*Params[a] != this);
|
|
NewHash.hash1 += Params[a].GetHash().hash1;
|
|
|
|
NewHash.hash2 += (3)*FPHASH_CONST(0x9ABCD801357);
|
|
NewHash.hash2 *= FPHASH_CONST(0xECADB912345);
|
|
NewHash.hash2 += (~Params[a].GetHash().hash1) ^ 4567890;
|
|
}
|
|
Depth += MaxChildDepth;
|
|
}
|
|
}
|
|
if(Hash != NewHash)
|
|
{
|
|
Hash = NewHash;
|
|
OptimizedUsing = 0;
|
|
}
|
|
}
|
|
|
|
void CodeTree::FixIncompleteHashes()
|
|
{
|
|
MarkIncompletes(*this);
|
|
FixIncompletes(*this);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_makebytecode.c"
|
|
#include <cmath>
|
|
#include <list>
|
|
#include <cassert>
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
#include "fptypes.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_consts.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_bytecodesynth.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
//using namespace FPoptimizer_Grammar;
|
|
|
|
namespace
|
|
{
|
|
using namespace FPoptimizer_CodeTree;
|
|
|
|
bool AssembleSequence(
|
|
const CodeTree& tree, long count,
|
|
const FPoptimizer_ByteCode::SequenceOpCode& sequencing,
|
|
FPoptimizer_ByteCode::ByteCodeSynth& synth,
|
|
size_t max_bytecode_grow_length);
|
|
}
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
void CodeTree::SynthesizeByteCode(
|
|
std::vector<unsigned>& ByteCode,
|
|
std::vector<double>& Immed,
|
|
size_t& stacktop_max)
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Making bytecode for:\n";
|
|
DumpTreeWithIndent(*this);
|
|
#endif
|
|
while(RecreateInversionsAndNegations())
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "One change issued, produced:\n";
|
|
DumpTreeWithIndent(*this);
|
|
#endif
|
|
FixIncompleteHashes();
|
|
}
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Actually synthesizing, after recreating inv/neg:\n";
|
|
DumpTreeWithIndent(*this);
|
|
#endif
|
|
|
|
FPoptimizer_ByteCode::ByteCodeSynth synth;
|
|
|
|
/* Then synthesize the actual expression */
|
|
SynthesizeByteCode(synth, false);
|
|
/* The "false" parameters tells SynthesizeByteCode
|
|
* that at the outermost synthesizing level, it does
|
|
* not matter if leftover temps are left in the stack.
|
|
*/
|
|
synth.Pull(ByteCode, Immed, stacktop_max);
|
|
}
|
|
|
|
void CodeTree::SynthesizeByteCode(
|
|
FPoptimizer_ByteCode::ByteCodeSynth& synth,
|
|
bool MustPopTemps) const
|
|
{
|
|
// If the synth can already locate our operand in the stack,
|
|
// never mind synthesizing it again, just dup it.
|
|
if(synth.FindAndDup(*this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
size_t n_subexpressions_synthesized = SynthCommonSubExpressions(synth);
|
|
|
|
switch(GetOpcode())
|
|
{
|
|
case VarBegin:
|
|
synth.PushVar(GetVar());
|
|
break;
|
|
case cImmed:
|
|
synth.PushImmed(GetImmed());
|
|
break;
|
|
case cAdd:
|
|
case cMul:
|
|
case cMin:
|
|
case cMax:
|
|
case cAnd:
|
|
case cOr:
|
|
case cAbsAnd:
|
|
case cAbsOr:
|
|
{
|
|
if(GetOpcode() == cMul) // Special treatment for cMul sequences
|
|
{
|
|
// If the paramlist contains an Immed, and that Immed
|
|
// fits in a long-integer, try to synthesize it
|
|
// as add-sequences instead.
|
|
bool did_muli = false;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
if(GetParam(a).IsLongIntegerImmed())
|
|
{
|
|
long value = GetParam(a).GetLongIntegerImmed();
|
|
|
|
CodeTree tmp(*this, CodeTree::CloneTag());
|
|
tmp.DelParam(a);
|
|
tmp.Rehash();
|
|
if(AssembleSequence(
|
|
tmp, value, FPoptimizer_ByteCode::AddSequence,
|
|
synth,
|
|
MAX_MULI_BYTECODE_LENGTH))
|
|
{
|
|
did_muli = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(did_muli)
|
|
break; // done
|
|
}
|
|
|
|
int n_stacked = 0;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
GetParam(a).SynthesizeByteCode(synth);
|
|
++n_stacked;
|
|
|
|
if(n_stacked > 1)
|
|
{
|
|
// Cumulate at the earliest opportunity.
|
|
synth.AddOperation(GetOpcode(), 2); // stack state: -2+1 = -1
|
|
n_stacked = n_stacked - 2 + 1;
|
|
}
|
|
}
|
|
if(n_stacked == 0)
|
|
{
|
|
// Uh, we got an empty cAdd/cMul/whatever...
|
|
// Synthesize a default value.
|
|
// This should never happen.
|
|
switch(GetOpcode())
|
|
{
|
|
case cAdd:
|
|
case cOr:
|
|
case cAbsOr:
|
|
synth.PushImmed(0);
|
|
break;
|
|
case cMul:
|
|
case cAnd:
|
|
case cAbsAnd:
|
|
synth.PushImmed(1);
|
|
break;
|
|
case cMin:
|
|
case cMax:
|
|
//synth.PushImmed(NaN);
|
|
synth.PushImmed(0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
++n_stacked;
|
|
}
|
|
assert(n_stacked == 1);
|
|
break;
|
|
}
|
|
case cPow:
|
|
{
|
|
const CodeTree& p0 = GetParam(0);
|
|
const CodeTree& p1 = GetParam(1);
|
|
|
|
if(!p1.IsLongIntegerImmed()
|
|
|| !AssembleSequence( /* Optimize integer exponents */
|
|
p0, p1.GetLongIntegerImmed(),
|
|
FPoptimizer_ByteCode::MulSequence,
|
|
synth,
|
|
MAX_POWI_BYTECODE_LENGTH)
|
|
)
|
|
{
|
|
p0.SynthesizeByteCode(synth);
|
|
p1.SynthesizeByteCode(synth);
|
|
synth.AddOperation(GetOpcode(), 2); // Create a vanilla cPow.
|
|
}
|
|
break;
|
|
}
|
|
case cIf:
|
|
case cAbsIf:
|
|
{
|
|
// Assume that the parameter count is 3 as it should.
|
|
FPoptimizer_ByteCode::ByteCodeSynth::IfData ifdata;
|
|
|
|
GetParam(0).SynthesizeByteCode(synth); // expression
|
|
|
|
synth.SynthIfStep1(ifdata, GetOpcode());
|
|
|
|
GetParam(1).SynthesizeByteCode(synth); // true branch
|
|
|
|
synth.SynthIfStep2(ifdata);
|
|
|
|
GetParam(2).SynthesizeByteCode(synth); // false branch
|
|
|
|
synth.SynthIfStep3(ifdata);
|
|
break;
|
|
}
|
|
case cFCall:
|
|
case cPCall:
|
|
{
|
|
// Assume that the parameter count is as it should.
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
GetParam(a).SynthesizeByteCode(synth);
|
|
synth.AddOperation(GetOpcode(), (unsigned) GetParamCount());
|
|
synth.AddOperation(GetFuncNo(), 0, 0);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Assume that the parameter count is as it should.
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
GetParam(a).SynthesizeByteCode(synth);
|
|
synth.AddOperation(GetOpcode(), (unsigned) GetParamCount());
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Tell the synthesizer which tree was just produced in the stack
|
|
synth.StackTopIs(*this);
|
|
|
|
// If we added subexpressions, peel them off the stack now
|
|
if(MustPopTemps && n_subexpressions_synthesized > 0)
|
|
{
|
|
size_t top = synth.GetStackTop();
|
|
synth.DoPopNMov(top-1-n_subexpressions_synthesized, top-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool AssembleSequence(
|
|
const CodeTree& tree, long count,
|
|
const FPoptimizer_ByteCode::SequenceOpCode& sequencing,
|
|
FPoptimizer_ByteCode::ByteCodeSynth& synth,
|
|
size_t max_bytecode_grow_length)
|
|
{
|
|
if(count != 0)
|
|
{
|
|
FPoptimizer_ByteCode::ByteCodeSynth backup = synth;
|
|
|
|
tree.SynthesizeByteCode(synth);
|
|
|
|
// Ignore the size generated by subtree
|
|
size_t bytecodesize_backup = synth.GetByteCodeSize();
|
|
|
|
FPoptimizer_ByteCode::AssembleSequence(count, sequencing, synth);
|
|
|
|
size_t bytecode_grow_amount = synth.GetByteCodeSize() - bytecodesize_backup;
|
|
if(bytecode_grow_amount > max_bytecode_grow_length)
|
|
{
|
|
synth = backup;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
FPoptimizer_ByteCode::AssembleSequence(count, sequencing, synth);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_readbytecode.c"
|
|
#include <cmath>
|
|
#include <cassert>
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_opcodename.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_grammar.h"
|
|
#include "fptypes.h"
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_consts.h"
|
|
#include "fparser.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
//using namespace FPoptimizer_Grammar;
|
|
|
|
namespace
|
|
{
|
|
using namespace FPoptimizer_CodeTree;
|
|
|
|
typedef std::vector<double> FactorStack;
|
|
|
|
const struct PowiMuliType
|
|
{
|
|
unsigned opcode_square;
|
|
unsigned opcode_cumulate;
|
|
unsigned opcode_invert;
|
|
unsigned opcode_half;
|
|
unsigned opcode_invhalf;
|
|
} iseq_powi = {cSqr,cMul,cInv,cSqrt,cRSqrt},
|
|
iseq_muli = {~unsigned(0), cAdd,cNeg, ~unsigned(0),~unsigned(0) };
|
|
|
|
double ParsePowiMuli(
|
|
const PowiMuliType& opcodes,
|
|
const std::vector<unsigned>& ByteCode, size_t& IP,
|
|
size_t limit,
|
|
size_t factor_stack_base,
|
|
FactorStack& stack)
|
|
{
|
|
double result = 1;
|
|
while(IP < limit)
|
|
{
|
|
if(ByteCode[IP] == opcodes.opcode_square)
|
|
{
|
|
if(!IsIntegerConst(result)) break;
|
|
result *= 2;
|
|
++IP;
|
|
continue;
|
|
}
|
|
if(ByteCode[IP] == opcodes.opcode_invert)
|
|
{
|
|
result = -result;
|
|
++IP;
|
|
continue;
|
|
}
|
|
if(ByteCode[IP] == opcodes.opcode_half)
|
|
{
|
|
if(IsIntegerConst(result) && result > 0 && ((long)result) % 2 == 0)
|
|
break;
|
|
result *= 0.5;
|
|
++IP;
|
|
continue;
|
|
}
|
|
if(ByteCode[IP] == opcodes.opcode_invhalf)
|
|
{
|
|
if(IsIntegerConst(result) && result > 0 && ((long)result) % 2 == 0)
|
|
break;
|
|
result *= -0.5;
|
|
++IP;
|
|
continue;
|
|
}
|
|
|
|
size_t dup_fetch_pos = IP;
|
|
double lhs = 1.0;
|
|
|
|
if(ByteCode[IP] == cFetch)
|
|
{
|
|
unsigned index = ByteCode[++IP];
|
|
if(index < factor_stack_base
|
|
|| size_t(index-factor_stack_base) >= stack.size())
|
|
{
|
|
// It wasn't a powi-fetch after all
|
|
IP = dup_fetch_pos;
|
|
break;
|
|
}
|
|
lhs = stack[index - factor_stack_base];
|
|
// Note: ^This assumes that cFetch of recentmost
|
|
// is always converted into cDup.
|
|
goto dup_or_fetch;
|
|
}
|
|
if(ByteCode[IP] == cDup)
|
|
{
|
|
lhs = result;
|
|
goto dup_or_fetch;
|
|
|
|
dup_or_fetch:
|
|
stack.push_back(result);
|
|
++IP;
|
|
double subexponent = ParsePowiMuli
|
|
(opcodes,
|
|
ByteCode, IP, limit,
|
|
factor_stack_base, stack);
|
|
if(IP >= limit || ByteCode[IP] != opcodes.opcode_cumulate)
|
|
{
|
|
// It wasn't a powi-dup after all
|
|
IP = dup_fetch_pos;
|
|
break;
|
|
}
|
|
++IP; // skip opcode_cumulate
|
|
stack.pop_back();
|
|
result += lhs*subexponent;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
double ParsePowiSequence(const std::vector<unsigned>& ByteCode, size_t& IP,
|
|
size_t limit,
|
|
size_t factor_stack_base)
|
|
{
|
|
FactorStack stack;
|
|
stack.push_back(1.0);
|
|
return ParsePowiMuli(iseq_powi, ByteCode, IP, limit, factor_stack_base, stack);
|
|
}
|
|
|
|
double ParseMuliSequence(const std::vector<unsigned>& ByteCode, size_t& IP,
|
|
size_t limit,
|
|
size_t factor_stack_base)
|
|
{
|
|
FactorStack stack;
|
|
stack.push_back(1.0);
|
|
return ParsePowiMuli(iseq_muli, ByteCode, IP, limit, factor_stack_base, stack);
|
|
}
|
|
|
|
class CodeTreeParserData
|
|
{
|
|
public:
|
|
explicit CodeTreeParserData(bool k_powi)
|
|
: stack(), keep_powi(k_powi) { }
|
|
|
|
void Eat(size_t nparams, OPCODE opcode)
|
|
{
|
|
CodeTree newnode;
|
|
newnode.SetOpcode(opcode);
|
|
|
|
std::vector<CodeTree> params = Pop(nparams);
|
|
newnode.SetParamsMove(params);
|
|
|
|
if(!keep_powi)
|
|
switch(opcode)
|
|
{
|
|
// asinh: log(x + sqrt(x*x + 1))
|
|
//cAsinh [x] -> cLog (cAdd x (cPow (cAdd (cPow x 2) 1) 0.5))
|
|
// Note: ^ Replacement function refers to x twice
|
|
|
|
// acosh: log(x + sqrt(x*x - 1))
|
|
//cAcosh [x] -> cLog (cAdd x (cPow (cAdd (cPow x 2) -1) 0.5))
|
|
|
|
// atanh: log( (1+x) / (1-x)) / 2
|
|
//cAtanh [x] -> cMul (cLog (cMul (cAdd 1 x) (cPow (cAdd 1 (cMul -1 x)) -1))) 0.5
|
|
|
|
// asin: atan2(x, sqrt(1-x*x))
|
|
//cAsin[x] -> cAtan2 [x (cPow [(cAdd 1 (cMul (cPow [x 2] -1)) 0.5])]
|
|
|
|
// acos: atan2(sqrt(1-x*x), x)
|
|
//cAcos[x] -> cAtan2 [(cPow [(cAdd 1 (cMul (cPow [x 2] -1)) 0.5]) x]
|
|
|
|
// The hyperbolic functions themselves are:
|
|
// sinh: (exp(x)-exp(-x)) / 2 = exp(-x) * (exp(2*x)-1) / 2
|
|
//cSinh [x] -> cMul 0.5 (cPow [CONSTANT_EI x]) (cAdd [-1 (cPow [CONSTANT_2E x])])
|
|
|
|
// cosh: (exp(x)+exp(-x)) / 2 = exp(-x) * (exp(2*x)+1) / 2
|
|
// cosh(-x) = cosh(x)
|
|
//cCosh [x] -> cMul 0.5 (cPow [CONSTANT_EI x]) (cAdd [ 1 (cPow [CONSTANT_2E x])])
|
|
|
|
// tanh: sinh/cosh = (exp(2*x)-1) / (exp(2*x)+1)
|
|
//cTanh [x] -> (cMul (cAdd {(cPow [CONSTANT_2E x]) -1}) (cPow [(cAdd {(cPow [CONSTANT_2E x]) 1}) -1]))
|
|
case cTanh:
|
|
{
|
|
CodeTree sinh, cosh;
|
|
sinh.SetOpcode(cSinh); sinh.AddParam(newnode.GetParam(0)); sinh.Rehash();
|
|
cosh.SetOpcode(cCosh); cosh.AddParamMove(newnode.GetParam(0)); cosh.Rehash();
|
|
CodeTree pow;
|
|
pow.SetOpcode(cPow);
|
|
pow.AddParamMove(cosh);
|
|
pow.AddParam(CodeTree(-1.0));
|
|
pow.Rehash();
|
|
newnode.SetOpcode(cMul);
|
|
newnode.SetParamMove(0, sinh);
|
|
newnode.AddParamMove(pow);
|
|
break;
|
|
}
|
|
|
|
// tan: sin/cos
|
|
//cTan [x] -> (cMul (cSin [x]) (cPow [(cCos [x]) -1]))
|
|
case cTan:
|
|
{
|
|
CodeTree sin, cos;
|
|
sin.SetOpcode(cSin); sin.AddParam(newnode.GetParam(0)); sin.Rehash();
|
|
cos.SetOpcode(cCos); cos.AddParamMove(newnode.GetParam(0)); cos.Rehash();
|
|
CodeTree pow;
|
|
pow.SetOpcode(cPow);
|
|
pow.AddParamMove(cos);
|
|
pow.AddParam(CodeTree(-1.0));
|
|
pow.Rehash();
|
|
newnode.SetOpcode(cMul);
|
|
newnode.SetParamMove(0, sin);
|
|
newnode.AddParamMove(pow);
|
|
break;
|
|
}
|
|
|
|
case cPow:
|
|
{
|
|
const CodeTree& p0 = newnode.GetParam(0);
|
|
const CodeTree& p1 = newnode.GetParam(1);
|
|
if(p1.GetOpcode() == cAdd)
|
|
{
|
|
// convert x^(a + b) into x^a * x^b just so that
|
|
// some optimizations can be run on it.
|
|
// For instance, exp(log(x)*-61.1 + log(z)*-59.1)
|
|
// won't be changed into exp(log(x*z)*-61.1)*z^2
|
|
// unless we do this.
|
|
std::vector<CodeTree> mulgroup(p1.GetParamCount());
|
|
for(size_t a=0; a<p1.GetParamCount(); ++a)
|
|
{
|
|
CodeTree pow;
|
|
pow.SetOpcode(cPow);
|
|
pow.AddParam(p0);
|
|
pow.AddParam(p1.GetParam(a));
|
|
pow.Rehash();
|
|
mulgroup[a].swap(pow);
|
|
}
|
|
newnode.SetOpcode(cMul);
|
|
newnode.SetParamsMove(mulgroup);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Should we change sin(x) into cos(pi/2-x)
|
|
// or cos(x) into sin(pi/2-x)?
|
|
// note: cos(x-pi/2) = cos(pi/2-x) = sin(x)
|
|
// note: sin(x-pi/2) = -sin(pi/2-x) = -cos(x)
|
|
default: break;
|
|
}
|
|
|
|
newnode.Rehash(!keep_powi);
|
|
/*
|
|
using namespace FPoptimizer_Grammar;
|
|
bool recurse = false;
|
|
while(ApplyGrammar(pack.glist[0], newnode, recurse)) // intermediate
|
|
{ //std::cout << "Rerunning 1\n";
|
|
newnode.FixIncompleteHashes();
|
|
recurse = true;
|
|
}
|
|
*/
|
|
FindClone(newnode, false);
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "POP " << nparams << ", " << FP_GetOpcodeName(opcode)
|
|
<< "->" << FP_GetOpcodeName(newnode.GetOpcode())
|
|
<< ": PUSH ";
|
|
DumpTree(newnode);
|
|
std::cout <<std::endl;
|
|
#endif
|
|
stack.push_back(newnode);
|
|
}
|
|
|
|
void EatFunc(size_t nparams, OPCODE opcode, unsigned funcno)
|
|
{
|
|
CodeTree newnode;
|
|
newnode.SetFuncOpcode(opcode, funcno);
|
|
std::vector<CodeTree> params = Pop(nparams);
|
|
newnode.SetParamsMove(params);
|
|
newnode.Rehash(false);
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "POP " << nparams << ", PUSH ";
|
|
DumpTree(newnode);
|
|
std::cout << std::endl;
|
|
#endif
|
|
FindClone(newnode);
|
|
stack.push_back(newnode);
|
|
}
|
|
|
|
void AddConst(double value)
|
|
{
|
|
CodeTree newnode(value);
|
|
FindClone(newnode);
|
|
Push(newnode);
|
|
}
|
|
|
|
void AddVar(unsigned varno)
|
|
{
|
|
CodeTree newnode(varno, CodeTree::VarTag());
|
|
FindClone(newnode);
|
|
Push(newnode);
|
|
}
|
|
|
|
void SwapLastTwoInStack()
|
|
{
|
|
stack[stack.size()-1].swap( stack[stack.size()-2] );
|
|
}
|
|
|
|
void Dup()
|
|
{
|
|
Fetch(stack.size()-1);
|
|
}
|
|
|
|
void Fetch(size_t which)
|
|
{
|
|
Push(stack[which]);
|
|
}
|
|
|
|
template<typename T>
|
|
void Push(T tree)
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "PUSH ";
|
|
DumpTree(tree);
|
|
std::cout << std::endl;
|
|
#endif
|
|
stack.push_back(tree);
|
|
}
|
|
|
|
void PopNMov(size_t target, size_t source)
|
|
{
|
|
stack[target] = stack[source];
|
|
stack.resize(target+1);
|
|
}
|
|
|
|
CodeTree PullResult()
|
|
{
|
|
clones.clear();
|
|
CodeTree result(stack.back());
|
|
stack.resize(stack.size()-1);
|
|
return result;
|
|
}
|
|
std::vector<CodeTree> Pop(unsigned n_pop)
|
|
{
|
|
std::vector<CodeTree> result(n_pop);
|
|
for(unsigned n=0; n<n_pop; ++n)
|
|
result[n].swap(stack[stack.size()-n_pop+n]);
|
|
stack.resize(stack.size()-n_pop);
|
|
return result;
|
|
}
|
|
|
|
size_t GetStackTop() const { return stack.size(); }
|
|
private:
|
|
void FindClone(CodeTree& /*tree*/, bool /*recurse*/ = true)
|
|
{
|
|
// Disabled: Causes problems in optimization when
|
|
// the same subtree is included in logical and non-logical
|
|
// contexts: optimizations applied to the logical one will
|
|
// mess up the non-logical one.
|
|
return;
|
|
/*
|
|
std::multimap<fphash_t, CodeTree>::const_iterator
|
|
i = clones.lower_bound(tree.GetHash());
|
|
for(; i != clones.end() && i->first == tree.GetHash(); ++i)
|
|
{
|
|
if(i->second.IsIdenticalTo(tree))
|
|
tree.Become(i->second);
|
|
}
|
|
if(recurse)
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
FindClone(tree.GetParam(a));
|
|
clones.insert(std::make_pair(tree.GetHash(), tree));
|
|
*/
|
|
}
|
|
private:
|
|
std::vector<CodeTree> stack;
|
|
std::multimap<fphash_t, CodeTree> clones;
|
|
|
|
bool keep_powi;
|
|
|
|
private:
|
|
CodeTreeParserData(const CodeTreeParserData&);
|
|
CodeTreeParserData& operator=(const CodeTreeParserData&);
|
|
};
|
|
|
|
struct IfInfo
|
|
{
|
|
CodeTree condition;
|
|
CodeTree thenbranch;
|
|
size_t endif_location;
|
|
};
|
|
}
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
void CodeTree::GenerateFrom(
|
|
const std::vector<unsigned>& ByteCode,
|
|
const std::vector<double>& Immed,
|
|
const FunctionParser::Data& fpdata,
|
|
bool keep_powi)
|
|
{
|
|
std::vector<CodeTree> var_trees;
|
|
var_trees.reserve(fpdata.numVariables);
|
|
for(unsigned n=0; n<fpdata.numVariables; ++n)
|
|
{
|
|
var_trees.push_back( CodeTree(n+VarBegin, CodeTree::VarTag()) );
|
|
}
|
|
GenerateFrom(ByteCode,Immed,fpdata,var_trees,keep_powi);
|
|
}
|
|
|
|
void CodeTree::GenerateFrom(
|
|
const std::vector<unsigned>& ByteCode,
|
|
const std::vector<double>& Immed,
|
|
const FunctionParser::Data& fpdata,
|
|
const std::vector<CodeTree>& var_trees,
|
|
bool keep_powi)
|
|
{
|
|
CodeTreeParserData sim(keep_powi);
|
|
std::vector<IfInfo> if_stack;
|
|
|
|
for(size_t IP=0, DP=0; ; ++IP)
|
|
{
|
|
after_powi:
|
|
while(!if_stack.empty() &&
|
|
( // Normal If termination rule:
|
|
if_stack.back().endif_location == IP
|
|
// This rule matches when cJumps are threaded:
|
|
|| (IP < ByteCode.size() && ByteCode[IP] == cJump
|
|
&& if_stack.back().thenbranch.IsDefined())
|
|
))
|
|
{
|
|
// The "else" of an "if" ends here
|
|
CodeTree elsebranch = sim.PullResult();
|
|
sim.Push(if_stack.back().condition);
|
|
sim.Push(if_stack.back().thenbranch);
|
|
sim.Push(elsebranch);
|
|
sim.Eat(3, cIf);
|
|
if_stack.pop_back();
|
|
}
|
|
if(IP >= ByteCode.size()) break;
|
|
|
|
unsigned opcode = ByteCode[IP];
|
|
if((opcode == cSqr || opcode == cDup
|
|
|| opcode == cInv || opcode == cNeg
|
|
|| opcode == cSqrt || opcode == cRSqrt
|
|
|| opcode == cFetch))
|
|
{
|
|
// Parse a powi sequence
|
|
size_t was_ip = IP;
|
|
double exponent = ParsePowiSequence(
|
|
ByteCode, IP, if_stack.empty() ? ByteCode.size() : if_stack.back().endif_location,
|
|
sim.GetStackTop()-1);
|
|
if(exponent != 1.0)
|
|
{
|
|
//std::cout << "Found exponent at " << was_ip << ": " << exponent << "\n";
|
|
sim.AddConst(exponent);
|
|
sim.Eat(2, cPow);
|
|
goto after_powi;
|
|
}
|
|
if(opcode == cDup
|
|
|| opcode == cFetch
|
|
|| opcode == cNeg)
|
|
{
|
|
double factor = ParseMuliSequence(
|
|
ByteCode, IP, if_stack.empty() ? ByteCode.size() : if_stack.back().endif_location,
|
|
sim.GetStackTop()-1);
|
|
if(factor != 1.0)
|
|
{
|
|
//std::cout << "Found factor at " << was_ip << ": " << factor << "\n";
|
|
sim.AddConst(factor);
|
|
sim.Eat(2, cMul);
|
|
goto after_powi;
|
|
}
|
|
}
|
|
IP = was_ip;
|
|
}
|
|
if(OPCODE(opcode) >= VarBegin)
|
|
{
|
|
sim.Push(var_trees[opcode-VarBegin]);
|
|
}
|
|
else
|
|
{
|
|
switch( OPCODE(opcode) )
|
|
{
|
|
// Specials
|
|
case cIf:
|
|
case cAbsIf:
|
|
{
|
|
if_stack.resize(if_stack.size() + 1);
|
|
CodeTree res( sim.PullResult() );
|
|
if_stack.back().condition.swap( res );
|
|
if_stack.back().endif_location = ByteCode.size();
|
|
IP += 2; // dp,sp for elsebranch are irrelevant.
|
|
continue;
|
|
}
|
|
case cJump:
|
|
{
|
|
CodeTree res( sim.PullResult() );
|
|
if_stack.back().thenbranch.swap( res );
|
|
if_stack.back().endif_location = ByteCode[IP+1]+1;
|
|
IP += 2;
|
|
continue;
|
|
}
|
|
case cImmed:
|
|
sim.AddConst(Immed[DP++]);
|
|
break;
|
|
case cDup:
|
|
sim.Dup();
|
|
break;
|
|
case cNop:
|
|
break;
|
|
case cFCall:
|
|
{
|
|
unsigned funcno = ByteCode[++IP];
|
|
assert(funcno < fpdata.FuncPtrs.size());
|
|
unsigned params = fpdata.FuncPtrs[funcno].params;
|
|
sim.EatFunc(params, OPCODE(opcode), funcno);
|
|
break;
|
|
}
|
|
case cPCall:
|
|
{
|
|
unsigned funcno = ByteCode[++IP];
|
|
assert(funcno < fpdata.FuncParsers.size());
|
|
const FunctionParserBase<double>& p =
|
|
*fpdata.FuncParsers[funcno].parserPtr;
|
|
unsigned params = fpdata.FuncParsers[funcno].params;
|
|
|
|
/* Inline the procedure call */
|
|
/* Works because cPCalls can never recurse */
|
|
std::vector<CodeTree> paramlist = sim.Pop(params);
|
|
CodeTree pcall_tree;
|
|
pcall_tree.GenerateFrom(p.data->ByteCode, p.data->Immed, *p.data,
|
|
paramlist);
|
|
sim.Push(pcall_tree);
|
|
break;
|
|
}
|
|
// Unary operators requiring special attention
|
|
case cInv: // already handled by powi_opt
|
|
//sim.Eat(1, cInv);
|
|
//break;
|
|
sim.AddConst(1);
|
|
sim.SwapLastTwoInStack();
|
|
sim.Eat(2, cDiv);
|
|
break;
|
|
case cNeg: // already handled by powi_opt
|
|
sim.Eat(1, cNeg);
|
|
break;
|
|
sim.AddConst(0);
|
|
sim.SwapLastTwoInStack();
|
|
sim.Eat(2, cSub);
|
|
break;
|
|
case cSqr: // already handled by powi_opt
|
|
//sim.Eat(1, cSqr);
|
|
//break;
|
|
sim.AddConst(2);
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
// Unary functions requiring special attention
|
|
case cSqrt: // already handled by powi_opt
|
|
sim.AddConst(0.5);
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
case cRSqrt: // already handled by powi_opt
|
|
sim.AddConst(-0.5);
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
case cCbrt:
|
|
sim.AddConst(1.0 / 3.0);
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
case cDeg:
|
|
sim.AddConst(CONSTANT_DR);
|
|
sim.Eat(2, cMul);
|
|
break;
|
|
case cRad:
|
|
sim.AddConst(CONSTANT_RD);
|
|
sim.Eat(2, cMul);
|
|
break;
|
|
case cExp:
|
|
if(keep_powi) goto default_function_handling;
|
|
sim.AddConst(CONSTANT_E);
|
|
sim.SwapLastTwoInStack();
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
case cExp2: // from fpoptimizer
|
|
if(keep_powi) goto default_function_handling;
|
|
sim.AddConst(2.0);
|
|
sim.SwapLastTwoInStack();
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
case cCot:
|
|
sim.Eat(1, cTan);
|
|
if(keep_powi) { sim.Eat(1, cInv); break; }
|
|
sim.AddConst(-1);
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
case cCsc:
|
|
sim.Eat(1, cSin);
|
|
if(keep_powi) { sim.Eat(1, cInv); break; }
|
|
sim.AddConst(-1);
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
case cSec:
|
|
sim.Eat(1, cCos);
|
|
if(keep_powi) { sim.Eat(1, cInv); break; }
|
|
sim.AddConst(-1);
|
|
sim.Eat(2, cPow);
|
|
break;
|
|
case cInt: // int(x) = floor(x + 0.5)
|
|
#ifndef __x86_64
|
|
if(keep_powi) { sim.Eat(1, cInt); break; }
|
|
#endif
|
|
sim.AddConst(0.5);
|
|
sim.Eat(2, cAdd);
|
|
sim.Eat(1, cFloor);
|
|
break;
|
|
case cLog10:
|
|
sim.Eat(1, cLog);
|
|
sim.AddConst(CONSTANT_L10I);
|
|
sim.Eat(2, cMul);
|
|
break;
|
|
case cLog2:
|
|
sim.Eat(1, cLog);
|
|
sim.AddConst(CONSTANT_L2I);
|
|
sim.Eat(2, cMul);
|
|
break;
|
|
case cLog2by: // x y -> log(x)*CONSTANT_L2I*y
|
|
sim.SwapLastTwoInStack(); // y x
|
|
sim.Eat(1, cLog); // y log(x)
|
|
sim.AddConst(CONSTANT_L2I); // y log(x) CONSTANT_L2I
|
|
sim.Eat(3, cMul); // y*log(x)*CONSTANT_L2I
|
|
break;
|
|
//case cLog:
|
|
// sim.Eat(1, cLog2);
|
|
// sim.AddConst(CONSTANT_L2);
|
|
// sim.Eat(2, cMul);
|
|
// break;
|
|
// Binary operators requiring special attention
|
|
case cSub:
|
|
if(keep_powi) { sim.Eat(2, cSub); break; }
|
|
sim.AddConst(-1);
|
|
sim.Eat(2, cMul); // -x is x*-1
|
|
sim.Eat(2, cAdd); // Minus is negative adding
|
|
break;
|
|
case cRSub: // from fpoptimizer
|
|
sim.SwapLastTwoInStack();
|
|
if(keep_powi) { sim.Eat(2, cSub); break; }
|
|
sim.AddConst(-1);
|
|
sim.Eat(2, cMul); // -x is x*-1
|
|
sim.Eat(2, cAdd);
|
|
break;
|
|
case cDiv:
|
|
if(keep_powi) { sim.Eat(2, cDiv); break; }
|
|
sim.AddConst(-1);
|
|
sim.Eat(2, cPow); // 1/x is x^-1
|
|
sim.Eat(2, cMul); // Divide is inverse multiply
|
|
break;
|
|
case cRDiv: // from fpoptimizer
|
|
sim.SwapLastTwoInStack();
|
|
if(keep_powi) { sim.Eat(2, cDiv); break; }
|
|
sim.AddConst(-1);
|
|
sim.Eat(2, cPow); // 1/x is x^-1
|
|
sim.Eat(2, cMul); // Divide is inverse multiply
|
|
break;
|
|
// Binary operators not requiring special attention
|
|
case cAdd: case cMul:
|
|
case cMod: case cPow:
|
|
case cEqual: case cLess: case cGreater:
|
|
case cNEqual: case cLessOrEq: case cGreaterOrEq:
|
|
case cAnd: case cOr:
|
|
case cAbsAnd: case cAbsOr:
|
|
sim.Eat(2, OPCODE(opcode));
|
|
break;
|
|
// Unary operators not requiring special attention
|
|
case cNot:
|
|
case cNotNot: // from fpoptimizer
|
|
case cAbsNot:
|
|
case cAbsNotNot:
|
|
sim.Eat(1, OPCODE(opcode));
|
|
break;
|
|
// Special opcodes generated by fpoptimizer itself
|
|
case cFetch:
|
|
sim.Fetch(ByteCode[++IP]);
|
|
break;
|
|
case cPopNMov:
|
|
{
|
|
unsigned stackOffs_target = ByteCode[++IP];
|
|
unsigned stackOffs_source = ByteCode[++IP];
|
|
sim.PopNMov(stackOffs_target, stackOffs_source);
|
|
break;
|
|
}
|
|
// Other functions
|
|
#ifndef FP_DISABLE_EVAL
|
|
case cEval:
|
|
{
|
|
size_t paramcount = fpdata.numVariables;
|
|
sim.Eat(paramcount, OPCODE(opcode));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
default_function_handling:;
|
|
unsigned funcno = opcode-cAbs;
|
|
assert(funcno < FUNC_AMOUNT);
|
|
const FuncDefinition& func = Functions[funcno];
|
|
sim.Eat(func.params, OPCODE(opcode));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Become(sim.PullResult());
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Produced tree:\n";
|
|
DumpTreeWithIndent(*this);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_constantfolding.c"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_consts.h"
|
|
|
|
#include <cmath> /* for CalculateResultBoundaries() */
|
|
#include <algorithm>
|
|
|
|
#include "fpconfig.h"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
using namespace FPoptimizer_CodeTree;
|
|
|
|
#define FP_MUL_COMBINE_EXPONENTS
|
|
|
|
#ifdef _MSC_VER
|
|
#include <float.h>
|
|
#define isinf(x) (!_finite(x))
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
bool IsLogicalTrueValue(const MinMaxTree& p, bool abs)
|
|
{
|
|
if(p.has_min && p.min >= 0.5) return true;
|
|
if(!abs && p.has_max && p.max <= -0.5) return true;
|
|
return false;
|
|
}
|
|
bool IsLogicalFalseValue(const MinMaxTree& p, bool abs)
|
|
{
|
|
if(abs)
|
|
return p.has_max && p.max < 0.5;
|
|
else
|
|
return p.has_min && p.has_max
|
|
&& p.min > -0.5 && p.max < 0.5;
|
|
}
|
|
int GetLogicalValue(const MinMaxTree& p, bool abs)
|
|
{
|
|
if(IsLogicalTrueValue(p, abs)) return 1;
|
|
if(IsLogicalFalseValue(p, abs)) return 0;
|
|
return -1;
|
|
}
|
|
|
|
struct ComparisonSet /* For optimizing And, Or */
|
|
{
|
|
static const int Lt_Mask = 0x1; // 1=less
|
|
static const int Eq_Mask = 0x2; // 2=equal
|
|
static const int Le_Mask = 0x3; // 1+2 = Less or Equal
|
|
static const int Gt_Mask = 0x4; // 4=greater
|
|
static const int Ne_Mask = 0x5; // 4+1 = Greater or Less, i.e. Not equal
|
|
static const int Ge_Mask = 0x6; // 4+2 = Greater or Equal
|
|
static int Swap_Mask(int m) { return (m&Eq_Mask)
|
|
| ((m&Lt_Mask) ? Gt_Mask : 0)
|
|
| ((m&Gt_Mask) ? Lt_Mask : 0); }
|
|
struct Comparison
|
|
{
|
|
CodeTree a;
|
|
CodeTree b;
|
|
int relationship;
|
|
};
|
|
std::vector<Comparison> relationships;
|
|
struct Item
|
|
{
|
|
CodeTree value;
|
|
bool negated;
|
|
};
|
|
std::vector<Item> plain_set;
|
|
int const_offset;
|
|
|
|
enum RelationshipResult
|
|
{
|
|
Ok,
|
|
BecomeZero,
|
|
BecomeOne,
|
|
Suboptimal
|
|
};
|
|
enum ConditionType
|
|
{
|
|
cond_or,
|
|
cond_and,
|
|
cond_mul,
|
|
cond_add
|
|
};
|
|
|
|
ComparisonSet():
|
|
relationships(),
|
|
plain_set(),
|
|
const_offset(0)
|
|
{
|
|
}
|
|
|
|
RelationshipResult AddItem(const CodeTree& a, bool negated, ConditionType type)
|
|
{
|
|
for(size_t c=0; c<plain_set.size(); ++c)
|
|
if(plain_set[c].value.IsIdenticalTo(a))
|
|
{
|
|
if(negated != plain_set[c].negated)
|
|
{
|
|
switch(type)
|
|
{
|
|
case cond_or:
|
|
return BecomeOne;
|
|
case cond_add:
|
|
plain_set.erase(plain_set.begin() + c);
|
|
const_offset += 1;
|
|
return Suboptimal;
|
|
case cond_and:
|
|
case cond_mul:
|
|
return BecomeZero;
|
|
}
|
|
}
|
|
return Suboptimal;
|
|
}
|
|
Item pole;
|
|
pole.value = a;
|
|
pole.negated = negated;
|
|
plain_set.push_back(pole);
|
|
return Ok;
|
|
}
|
|
|
|
RelationshipResult AddRelationship(CodeTree a, CodeTree b, int reltype, ConditionType type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case cond_or:
|
|
if(reltype == 7) return BecomeOne;
|
|
break;
|
|
case cond_add:
|
|
if(reltype == 7) { const_offset += 1; return Suboptimal; }
|
|
break;
|
|
case cond_and:
|
|
case cond_mul:
|
|
if(reltype == 0) return BecomeZero;
|
|
break;
|
|
}
|
|
|
|
if(!(a.GetHash() < b.GetHash()))
|
|
{
|
|
a.swap(b);
|
|
reltype = Swap_Mask(reltype);
|
|
}
|
|
|
|
for(size_t c=0; c<relationships.size(); ++c)
|
|
{
|
|
if(relationships[c].a.IsIdenticalTo(a)
|
|
&& relationships[c].b.IsIdenticalTo(b))
|
|
{
|
|
switch(type)
|
|
{
|
|
case cond_or:
|
|
{
|
|
int newrel = relationships[c].relationship | reltype;
|
|
if(newrel == 7) return BecomeOne;
|
|
relationships[c].relationship = newrel;
|
|
break;
|
|
}
|
|
case cond_and:
|
|
case cond_mul:
|
|
{
|
|
int newrel = relationships[c].relationship & reltype;
|
|
if(newrel == 0) return BecomeZero;
|
|
relationships[c].relationship = newrel;
|
|
break;
|
|
}
|
|
case cond_add:
|
|
{
|
|
int newrel_or = relationships[c].relationship | reltype;
|
|
int newrel_and = relationships[c].relationship & reltype;
|
|
if(newrel_or == 5 // < + >
|
|
&& newrel_and == 0)
|
|
{
|
|
// (x<y) + (x>y) = x!=y
|
|
relationships[c].relationship = Ne_Mask;
|
|
return Suboptimal;
|
|
}
|
|
if(newrel_or == 7
|
|
&& newrel_and == 0)
|
|
{
|
|
// (x<y) + (x>=y) = 1
|
|
// (x<=y) + (x>y) = 1
|
|
// (x=y) + (x!=y) = 1
|
|
const_offset += 1;
|
|
relationships.erase(relationships.begin()+c);
|
|
return Suboptimal;
|
|
}
|
|
if(newrel_or == 7
|
|
&& newrel_and == Eq_Mask)
|
|
{
|
|
// (x<=y) + (x>=y) = 1 + (x=y)
|
|
relationships[c].relationship = Eq_Mask;
|
|
const_offset += 1;
|
|
return Suboptimal;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
return Suboptimal;
|
|
}
|
|
}
|
|
Comparison comp;
|
|
comp.a = a;
|
|
comp.b = b;
|
|
comp.relationship = reltype;
|
|
relationships.push_back(comp);
|
|
return Ok;
|
|
}
|
|
};
|
|
|
|
struct CollectionSet /* For optimizing Add, Mul */
|
|
{
|
|
struct Collection
|
|
{
|
|
CodeTree value;
|
|
CodeTree factor;
|
|
bool factor_needs_rehashing;
|
|
|
|
Collection() : value(),factor(), factor_needs_rehashing(false) { }
|
|
Collection(const CodeTree& v, const CodeTree& f)
|
|
: value(v), factor(f), factor_needs_rehashing(false) { }
|
|
};
|
|
std::multimap<fphash_t, Collection> collections;
|
|
|
|
enum CollectionResult
|
|
{
|
|
Ok,
|
|
Suboptimal
|
|
};
|
|
|
|
typedef std::multimap<fphash_t, Collection>::iterator PositionType;
|
|
|
|
PositionType FindIdenticalValueTo(const CodeTree& value)
|
|
{
|
|
fphash_t hash = value.GetHash();
|
|
for(PositionType
|
|
i = collections.lower_bound(hash);
|
|
i != collections.end() && i->first == hash;
|
|
++i)
|
|
{
|
|
if(value.IsIdenticalTo(i->second.value))
|
|
return i;
|
|
}
|
|
return collections.end();
|
|
}
|
|
bool Found(const PositionType& b) { return b != collections.end(); }
|
|
|
|
CollectionResult AddCollectionTo(const CodeTree& factor,
|
|
const PositionType& into_which)
|
|
{
|
|
Collection& c = into_which->second;
|
|
if(c.factor_needs_rehashing)
|
|
c.factor.AddParam(factor);
|
|
else
|
|
{
|
|
CodeTree add;
|
|
add.SetOpcode(cAdd);
|
|
add.AddParamMove(c.factor);
|
|
add.AddParam(factor);
|
|
c.factor.swap(add);
|
|
c.factor_needs_rehashing = true;
|
|
}
|
|
return Suboptimal;
|
|
}
|
|
|
|
CollectionResult AddCollection(const CodeTree& value, const CodeTree& factor)
|
|
{
|
|
const fphash_t hash = value.GetHash();
|
|
PositionType i = collections.lower_bound(hash);
|
|
for(; i != collections.end() && i->first == hash; ++i)
|
|
{
|
|
if(i->second.value.IsIdenticalTo(value))
|
|
return AddCollectionTo(factor, i);
|
|
}
|
|
collections.insert(
|
|
i,
|
|
std::make_pair( hash, Collection(value, factor) ) );
|
|
return Ok;
|
|
}
|
|
|
|
CollectionResult AddCollection(const CodeTree& a)
|
|
{
|
|
return AddCollection(a, CodeTree(1.0) );
|
|
}
|
|
};
|
|
|
|
struct Select2ndRev
|
|
{
|
|
template<typename T>
|
|
inline bool operator() (const T& a, const T& b) const
|
|
{
|
|
return a.second > b.second;
|
|
}
|
|
};
|
|
struct Select1st
|
|
{
|
|
template<typename T1, typename T2>
|
|
inline bool operator() (const std::pair<T1,T2>& a,
|
|
const std::pair<T1,T2>& b) const
|
|
{
|
|
return a.first < b.first;
|
|
}
|
|
|
|
template<typename T1, typename T2>
|
|
inline bool operator() (const std::pair<T1,T2>& a, T1 b) const
|
|
{
|
|
return a.first < b;
|
|
}
|
|
|
|
template<typename T1, typename T2>
|
|
inline bool operator() (T1 a, const std::pair<T1,T2>& b) const
|
|
{
|
|
return a < b.first;
|
|
}
|
|
};
|
|
|
|
bool IsEvenIntegerConst(double v)
|
|
{
|
|
return IsIntegerConst(v) && ((long)v % 2) == 0;
|
|
}
|
|
|
|
struct ConstantExponentCollection
|
|
{
|
|
typedef std::pair<double, std::vector<CodeTree> > ExponentInfo;
|
|
std::vector<ExponentInfo> data;
|
|
|
|
void MoveToSet_Unique(double exponent, std::vector<CodeTree>& source_set)
|
|
{
|
|
data.push_back( std::pair<double, std::vector<CodeTree> >
|
|
(exponent, std::vector<CodeTree>() ) );
|
|
data.back().second.swap(source_set);
|
|
}
|
|
void MoveToSet_NonUnique(double exponent, std::vector<CodeTree>& source_set)
|
|
{
|
|
std::vector<ExponentInfo>::iterator i
|
|
= std::lower_bound(data.begin(), data.end(), exponent, Select1st());
|
|
if(i != data.end() && i->first == exponent)
|
|
{
|
|
i->second.insert(i->second.end(), source_set.begin(), source_set.end());
|
|
}
|
|
else
|
|
{
|
|
//MoveToSet_Unique(exponent, source_set);
|
|
data.insert(i, std::pair<double, std::vector<CodeTree> >
|
|
(exponent, source_set) );
|
|
}
|
|
}
|
|
|
|
bool Optimize()
|
|
{
|
|
/* TODO: Group them such that:
|
|
*
|
|
* x^3 * z^2 becomes (x*z)^2 * x^1
|
|
* x^3 * y^2.5 * z^2 becomes (x*z*y)^2 * y^0.5 * x^1
|
|
* rather than (x*y*z)^2 * (x*y)^0.5 * x^0.5
|
|
*
|
|
* x^4.5 * z^2.5 becomes (z * x)^2.5 * x^2
|
|
* becomes (x*z*x)^2 * (z*x)^0.5
|
|
* becomes (z*x*x*z*x)^0.5 * (z*x*x)^1.5 -- buzz, bad.
|
|
*
|
|
*/
|
|
bool changed = false;
|
|
std::sort( data.begin(), data.end(), Select1st() );
|
|
redo:
|
|
/* Supposed algorithm:
|
|
* For the smallest pair of data[] where the difference
|
|
* between the two is a "neat value" (x*16 is positive integer),
|
|
* do the combining as indicated above.
|
|
*/
|
|
/*
|
|
* NOTE: Hanged in Testbed test P44, looping the following
|
|
* (Var0 ^ 0.75) * ((1.5 * Var0) ^ 1.0)
|
|
* = (Var0 ^ 1.75) * (1.5 ^ 1.0)
|
|
* Fixed by limiting to cases where (exp_a != 1.0).
|
|
*
|
|
* NOTE: Converting (x*z)^0.5 * x^16.5
|
|
* into x^17 * z^0.5
|
|
* is handled by code within CollectMulGroup().
|
|
* However, bacause it is prone for infinite looping,
|
|
* the use of "IsIdenticalTo(before)" is added at the
|
|
* end of ConstantFolding_MulGrouping().
|
|
*
|
|
* This algorithm could make it into (x*z*x)^0.5 * x^16,
|
|
* but this is wrong, for it falsely includes x^evenint.. twice.
|
|
*/
|
|
for(size_t a=0; a<data.size(); ++a)
|
|
{
|
|
double exp_a = data[a].first;
|
|
if(FloatEqual(exp_a, 1.0)) continue;
|
|
for(size_t b=a+1; b<data.size(); ++b)
|
|
{
|
|
double exp_b = data[b].first;
|
|
double exp_diff = exp_b - exp_a;
|
|
if(exp_diff >= fabs(exp_a)) break;
|
|
double exp_diff_still_probable_integer = exp_diff * 16.0;
|
|
if(IsIntegerConst(exp_diff_still_probable_integer)
|
|
&& !(IsIntegerConst(exp_b) && !IsIntegerConst(exp_diff))
|
|
)
|
|
{
|
|
/* When input is x^3 * z^2,
|
|
* exp_a = 2
|
|
* a_set = z
|
|
* exp_b = 3
|
|
* b_set = x
|
|
* exp_diff = 3-2 = 1
|
|
*/
|
|
std::vector<CodeTree>& a_set = data[a].second;
|
|
std::vector<CodeTree>& b_set = data[b].second;
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Before ConstantExponentCollection iteration:\n";
|
|
Dump(std::cout);
|
|
#endif
|
|
if(IsIntegerConst(exp_b)
|
|
&& IsEvenIntegerConst(exp_b)
|
|
//&& !IsEvenIntegerConst(exp_diff)
|
|
&& !IsEvenIntegerConst(exp_diff+exp_a))
|
|
{
|
|
CodeTree tmp2;
|
|
tmp2.SetOpcode(cMul);
|
|
tmp2.SetParamsMove(b_set);
|
|
tmp2.Rehash();
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cAbs);
|
|
tmp.AddParamMove(tmp2);
|
|
tmp.Rehash();
|
|
b_set.resize(1);
|
|
b_set[0].swap(tmp);
|
|
}
|
|
|
|
a_set.insert(a_set.end(), b_set.begin(), b_set.end());
|
|
|
|
std::vector<CodeTree> b_copy = b_set;
|
|
data.erase(data.begin() + b);
|
|
MoveToSet_NonUnique(exp_diff, b_copy);
|
|
changed = true;
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "After ConstantExponentCollection iteration:\n";
|
|
Dump(std::cout);
|
|
#endif
|
|
goto redo;
|
|
}
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
void Dump(std::ostream& out)
|
|
{
|
|
for(size_t a=0; a<data.size(); ++a)
|
|
{
|
|
out.precision(12);
|
|
out << data[a].first << ": ";
|
|
for(size_t b=0; b<data[a].second.size(); ++b)
|
|
{
|
|
if(b > 0) out << '*';
|
|
DumpTree(data[a].second[b], out);
|
|
}
|
|
out << std::endl;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
};
|
|
|
|
struct RangeComparisonData
|
|
{
|
|
enum Decision
|
|
{
|
|
MakeFalse=0,
|
|
MakeTrue=1,
|
|
MakeNEqual=2,
|
|
MakeEqual=3,
|
|
MakeNotNotP0=4,
|
|
MakeNotNotP1=5,
|
|
MakeNotP0=6,
|
|
MakeNotP1=7,
|
|
Unchanged=8
|
|
};
|
|
enum WhatDoWhenCase
|
|
{
|
|
Never =0,
|
|
Eq0 =1, // val==0
|
|
Eq1 =2, // val==1
|
|
Gt0Le1=3, // val>0 && val<=1
|
|
Ge0Lt1=4 // val>=0 && val<1
|
|
};
|
|
Decision if_identical; // What to do when operands are identical
|
|
Decision if_always[4]; // What to do if Always <, <=, >, >=
|
|
struct { Decision what : 4; WhatDoWhenCase when : 4; }
|
|
p0_logical_a, p1_logical_a,
|
|
p0_logical_b, p1_logical_b;
|
|
|
|
Decision Analyze(const CodeTree& a, const CodeTree& b) const
|
|
{
|
|
if(a.IsIdenticalTo(b))
|
|
return if_identical;
|
|
|
|
MinMaxTree p0 = a.CalculateResultBoundaries();
|
|
MinMaxTree p1 = b.CalculateResultBoundaries();
|
|
if(p0.has_max && p1.has_min)
|
|
{
|
|
if(p0.max < p1.min && if_always[0] != Unchanged)
|
|
return if_always[0]; // p0 < p1
|
|
if(p0.max <= p1.min && if_always[1] != Unchanged)
|
|
return if_always[1]; // p0 <= p1
|
|
}
|
|
if(p0.has_min && p1.has_max)
|
|
{
|
|
if(p0.min > p1.max && if_always[2] != Unchanged)
|
|
return if_always[2]; // p0 > p1
|
|
if(p0.min >= p1.max && if_always[3] != Unchanged)
|
|
return if_always[3]; // p0 >= p1
|
|
}
|
|
|
|
if(a.IsLogicalValue())
|
|
{
|
|
if(p0_logical_a.what != Unchanged)
|
|
if(TestCase(p0_logical_a.when, p1)) return p0_logical_a.what;
|
|
if(p0_logical_b.what != Unchanged)
|
|
if(TestCase(p0_logical_b.when, p1)) return p0_logical_b.what;
|
|
}
|
|
if(b.IsLogicalValue())
|
|
{
|
|
if(p1_logical_a.what != Unchanged)
|
|
if(TestCase(p1_logical_a.when, p0)) return p1_logical_a.what;
|
|
if(p1_logical_b.what != Unchanged)
|
|
if(TestCase(p1_logical_b.when, p0)) return p1_logical_b.what;
|
|
}
|
|
return Unchanged;
|
|
}
|
|
static bool TestCase(WhatDoWhenCase when, const MinMaxTree& p)
|
|
{
|
|
if(!p.has_min || !p.has_max) return false;
|
|
switch(when)
|
|
{
|
|
case Eq0: return p.min==0.0 && p.max==p.min;
|
|
case Eq1: return p.min==1.0 && p.max==p.max;
|
|
case Gt0Le1: return p.min>0 && p.max<=1;
|
|
case Ge0Lt1: return p.min>=0 && p.max<1;
|
|
default:;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
template<typename CondType> /* ComparisonSet::ConditionType */
|
|
bool CodeTree::ConstantFolding_LogicCommon(CondType cond_type, bool is_logical)
|
|
{
|
|
bool should_regenerate = false;
|
|
ComparisonSet comp;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
ComparisonSet::RelationshipResult change = ComparisonSet::Ok;
|
|
const CodeTree& atree = GetParam(a);
|
|
switch(atree.GetOpcode())
|
|
{
|
|
case cEqual:
|
|
change = comp.AddRelationship(atree.GetParam(0), atree.GetParam(1), ComparisonSet::Eq_Mask, cond_type);
|
|
break;
|
|
case cNEqual:
|
|
change = comp.AddRelationship(atree.GetParam(0), atree.GetParam(1), ComparisonSet::Ne_Mask, cond_type);
|
|
break;
|
|
case cLess:
|
|
change = comp.AddRelationship(atree.GetParam(0), atree.GetParam(1), ComparisonSet::Lt_Mask, cond_type);
|
|
break;
|
|
case cLessOrEq:
|
|
change = comp.AddRelationship(atree.GetParam(0), atree.GetParam(1), ComparisonSet::Le_Mask, cond_type);
|
|
break;
|
|
case cGreater:
|
|
change = comp.AddRelationship(atree.GetParam(0), atree.GetParam(1), ComparisonSet::Gt_Mask, cond_type);
|
|
break;
|
|
case cGreaterOrEq:
|
|
change = comp.AddRelationship(atree.GetParam(0), atree.GetParam(1), ComparisonSet::Ge_Mask, cond_type);
|
|
break;
|
|
case cNot:
|
|
change = comp.AddItem(atree.GetParam(0), true, cond_type);
|
|
break;
|
|
case cNotNot:
|
|
change = comp.AddItem(atree.GetParam(0), false, cond_type);
|
|
break;
|
|
default:
|
|
if(is_logical || atree.IsLogicalValue())
|
|
change = comp.AddItem(atree, false, cond_type);
|
|
}
|
|
switch(change)
|
|
{
|
|
ReplaceTreeWithZero:
|
|
data = new CodeTreeData(0.0);
|
|
return true;
|
|
ReplaceTreeWithOne:
|
|
data = new CodeTreeData(1.0);
|
|
return true;
|
|
case ComparisonSet::Ok: // ok
|
|
break;
|
|
case ComparisonSet::BecomeZero: // whole set was invalidated
|
|
goto ReplaceTreeWithZero;
|
|
case ComparisonSet::BecomeOne: // whole set was validated
|
|
goto ReplaceTreeWithOne;
|
|
case ComparisonSet::Suboptimal: // something was changed
|
|
should_regenerate = true;
|
|
break;
|
|
}
|
|
}
|
|
if(should_regenerate)
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Before ConstantFolding_LogicCommon: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
|
|
if(is_logical)
|
|
{
|
|
DelParams(); // delete all params
|
|
}
|
|
else
|
|
{
|
|
// Delete only logical params
|
|
for(size_t a=GetParamCount(); a-- > 0; )
|
|
{
|
|
const CodeTree& atree = GetParam(a);
|
|
if(atree.IsLogicalValue())
|
|
DelParam(a);
|
|
}
|
|
}
|
|
|
|
for(size_t a=0; a<comp.plain_set.size(); ++a)
|
|
{
|
|
if(comp.plain_set[a].negated)
|
|
{
|
|
CodeTree r;
|
|
r.SetOpcode(cNot);
|
|
r.AddParamMove(comp.plain_set[a].value);
|
|
r.Rehash();
|
|
AddParamMove(r);
|
|
}
|
|
else if(!is_logical)
|
|
{
|
|
CodeTree r;
|
|
r.SetOpcode(cNotNot);
|
|
r.AddParamMove(comp.plain_set[a].value);
|
|
r.Rehash();
|
|
AddParamMove(r);
|
|
}
|
|
else
|
|
AddParamMove(comp.plain_set[a].value);
|
|
}
|
|
for(size_t a=0; a<comp.relationships.size(); ++a)
|
|
{
|
|
CodeTree r;
|
|
r.SetOpcode(cNop); // dummy
|
|
switch(comp.relationships[a].relationship)
|
|
{
|
|
case ComparisonSet::Lt_Mask: r.SetOpcode( cLess ); break;
|
|
case ComparisonSet::Eq_Mask: r.SetOpcode( cEqual ); break;
|
|
case ComparisonSet::Gt_Mask: r.SetOpcode( cGreater ); break;
|
|
case ComparisonSet::Le_Mask: r.SetOpcode( cLessOrEq ); break;
|
|
case ComparisonSet::Ne_Mask: r.SetOpcode( cNEqual ); break;
|
|
case ComparisonSet::Ge_Mask: r.SetOpcode( cGreaterOrEq ); break;
|
|
}
|
|
r.AddParamMove(comp.relationships[a].a);
|
|
r.AddParamMove(comp.relationships[a].b);
|
|
r.Rehash();
|
|
AddParamMove(r);
|
|
}
|
|
if(comp.const_offset != 0)
|
|
AddParam( CodeTree( double(comp.const_offset) ) );
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "After ConstantFolding_LogicCommon: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
return true;
|
|
}
|
|
/*
|
|
Note: One thing this does not yet do, is to detect chains
|
|
such as x=y & y=z & x=z, which could be optimized
|
|
to x=y & x=z.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
bool CodeTree::ConstantFolding_AndLogic()
|
|
{
|
|
return ConstantFolding_LogicCommon( ComparisonSet::cond_and, true );
|
|
}
|
|
bool CodeTree::ConstantFolding_OrLogic()
|
|
{
|
|
return ConstantFolding_LogicCommon( ComparisonSet::cond_or, true );
|
|
}
|
|
bool CodeTree::ConstantFolding_AddLogicItems()
|
|
{
|
|
return ConstantFolding_LogicCommon( ComparisonSet::cond_add, false );
|
|
}
|
|
bool CodeTree::ConstantFolding_MulLogicItems()
|
|
{
|
|
return ConstantFolding_LogicCommon( ComparisonSet::cond_mul, false );
|
|
}
|
|
|
|
static CodeTree CollectMulGroup_Item(
|
|
CodeTree& value,
|
|
bool& has_highlevel_opcodes)
|
|
{
|
|
switch(value.GetOpcode())
|
|
{
|
|
case cPow:
|
|
{
|
|
CodeTree exponent = value.GetParam(1);
|
|
value.Become( value.GetParam(0) );
|
|
return exponent;
|
|
}
|
|
/* - disabled to avoid clashes with powi
|
|
case cCbrt:
|
|
value.Become( value.GetParam(0) );
|
|
has_highlevel_opcodes = true;
|
|
return CodeTree(1.0 / 3.0);
|
|
case cSqrt:
|
|
value.Become( value.GetParam(0) );
|
|
has_highlevel_opcodes = true;
|
|
return CodeTree(0.5);
|
|
*/
|
|
case cRSqrt:
|
|
value.Become( value.GetParam(0) );
|
|
has_highlevel_opcodes = true;
|
|
return CodeTree(-0.5);
|
|
case cInv:
|
|
value.Become( value.GetParam(0) );
|
|
has_highlevel_opcodes = true;
|
|
return CodeTree(-1.0);
|
|
default: break;
|
|
}
|
|
return CodeTree(1.0);
|
|
}
|
|
|
|
static void CollectMulGroup(
|
|
CollectionSet& mul, const CodeTree& tree, const CodeTree& factor,
|
|
bool& should_regenerate,
|
|
bool& has_highlevel_opcodes
|
|
)
|
|
{
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
{
|
|
CodeTree value(tree.GetParam(a));
|
|
|
|
CodeTree exponent ( CollectMulGroup_Item(value, has_highlevel_opcodes) );
|
|
|
|
if(!factor.IsImmed() || factor.GetImmed() != 1.0)
|
|
{
|
|
CodeTree new_exp;
|
|
new_exp.SetOpcode(cMul);
|
|
new_exp.AddParam( exponent );
|
|
new_exp.AddParam( factor );
|
|
new_exp.Rehash();
|
|
exponent.swap( new_exp );
|
|
}
|
|
#if 0 /* FIXME: This does not work */
|
|
if(value.GetOpcode() == cMul)
|
|
{
|
|
if(1)
|
|
{
|
|
// Avoid erroneously converting
|
|
// (x*z)^0.5 * z^2
|
|
// into x^0.5 * z^2.5
|
|
// It should be x^0.5 * abs(z)^2.5, but this is not a good conversion.
|
|
bool exponent_is_even = exponent.IsImmed() && IsEvenIntegerConst(exponent.GetImmed());
|
|
|
|
for(size_t b=0; b<value.GetParamCount(); ++b)
|
|
{
|
|
bool tmp=false;
|
|
CodeTree val(value.GetParam(b));
|
|
CodeTree exp(CollectMulGroup_Item(val, tmp));
|
|
if(exponent_is_even
|
|
|| (exp.IsImmed() && IsEvenIntegerConst(exp.GetImmed())))
|
|
{
|
|
CodeTree new_exp;
|
|
new_exp.SetOpcode(cMul);
|
|
new_exp.AddParam(exponent);
|
|
new_exp.AddParamMove(exp);
|
|
new_exp.ConstantFolding();
|
|
if(!new_exp.IsImmed() || !IsEvenIntegerConst(new_exp.GetImmed()))
|
|
{
|
|
goto cannot_adopt_mul;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CollectMulGroup(mul, value, exponent,
|
|
should_regenerate,
|
|
has_highlevel_opcodes);
|
|
}
|
|
else cannot_adopt_mul:
|
|
#endif
|
|
{
|
|
if(mul.AddCollection(value, exponent) == CollectionSet::Suboptimal)
|
|
should_regenerate = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CodeTree::ConstantFolding_MulGrouping()
|
|
{
|
|
bool has_highlevel_opcodes = false;
|
|
bool should_regenerate = false;
|
|
CollectionSet mul;
|
|
|
|
CollectMulGroup(mul, *this, CodeTree(1.0),
|
|
should_regenerate,
|
|
has_highlevel_opcodes);
|
|
|
|
typedef std::pair<CodeTree/*exponent*/,
|
|
std::vector<CodeTree>/*base value (mul group)*/
|
|
> exponent_list;
|
|
typedef std::multimap<fphash_t,/*exponent hash*/
|
|
exponent_list> exponent_map;
|
|
exponent_map by_exponent;
|
|
|
|
for(CollectionSet::PositionType
|
|
j = mul.collections.begin();
|
|
j != mul.collections.end();
|
|
++j)
|
|
{
|
|
CodeTree& value = j->second.value;
|
|
CodeTree& exponent = j->second.factor;
|
|
if(j->second.factor_needs_rehashing) exponent.Rehash();
|
|
const fphash_t exponent_hash = exponent.GetHash();
|
|
|
|
exponent_map::iterator i = by_exponent.lower_bound(exponent_hash);
|
|
for(; i != by_exponent.end() && i->first == exponent_hash; ++i)
|
|
if(i->second.first.IsIdenticalTo(exponent))
|
|
{
|
|
if(!exponent.IsImmed() || !FloatEqual(exponent.GetImmed(), 1.0))
|
|
should_regenerate = true;
|
|
i->second.second.push_back(value);
|
|
goto skip_b;
|
|
}
|
|
by_exponent.insert(i, std::make_pair(exponent_hash,
|
|
std::make_pair(exponent,
|
|
std::vector<CodeTree> (size_t(1), value)
|
|
)));
|
|
skip_b:;
|
|
}
|
|
|
|
#ifdef FP_MUL_COMBINE_EXPONENTS
|
|
ConstantExponentCollection by_float_exponent;
|
|
for(exponent_map::iterator
|
|
j,i = by_exponent.begin();
|
|
i != by_exponent.end();
|
|
i=j)
|
|
{
|
|
j=i; ++j;
|
|
exponent_list& list = i->second;
|
|
if(list.first.IsImmed())
|
|
{
|
|
double exponent = list.first.GetImmed();
|
|
if(!(exponent == 0.0))
|
|
by_float_exponent.MoveToSet_Unique(exponent, list.second);
|
|
by_exponent.erase(i);
|
|
}
|
|
}
|
|
if(by_float_exponent.Optimize())
|
|
should_regenerate = true;
|
|
#endif
|
|
|
|
if(should_regenerate)
|
|
{
|
|
CodeTree before = *this;
|
|
before.CopyOnWrite();
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Before ConstantFolding_MulGrouping: "; DumpTree(before);
|
|
std::cout << "\n";
|
|
#endif
|
|
DelParams();
|
|
|
|
/* Group by exponents */
|
|
/* First handle non-constant exponents */
|
|
for(exponent_map::iterator
|
|
i = by_exponent.begin();
|
|
i != by_exponent.end();
|
|
++i)
|
|
{
|
|
exponent_list& list = i->second;
|
|
#ifndef FP_MUL_COMBINE_EXPONENTS
|
|
if(list.first.IsImmed())
|
|
{
|
|
double exponent = list.first.GetImmed();
|
|
if(exponent == 0.0) continue;
|
|
if(FloatEqual(exponent, 1.0))
|
|
{
|
|
AddParamsMove(list.second);
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
CodeTree mul;
|
|
mul.SetOpcode(cMul);
|
|
mul.SetParamsMove( list.second);
|
|
mul.Rehash();
|
|
|
|
if(has_highlevel_opcodes && list.first.IsImmed())
|
|
{
|
|
if(list.first.GetImmed() == 1.0 / 3.0)
|
|
{
|
|
CodeTree cbrt;
|
|
cbrt.SetOpcode(cCbrt);
|
|
cbrt.AddParamMove(mul);
|
|
cbrt.Rehash();
|
|
AddParamMove(cbrt);
|
|
continue;
|
|
}
|
|
if(list.first.GetImmed() == 0.5)
|
|
{
|
|
CodeTree sqrt;
|
|
sqrt.SetOpcode(cSqrt);
|
|
sqrt.AddParamMove(mul);
|
|
sqrt.Rehash();
|
|
AddParamMove(sqrt);
|
|
continue;
|
|
}
|
|
if(list.first.GetImmed() == -0.5)
|
|
{
|
|
CodeTree rsqrt;
|
|
rsqrt.SetOpcode(cRSqrt);
|
|
rsqrt.AddParamMove(mul);
|
|
rsqrt.Rehash();
|
|
AddParamMove(rsqrt);
|
|
continue;
|
|
}
|
|
if(list.first.GetImmed() == -1.0)
|
|
{
|
|
CodeTree inv;
|
|
inv.SetOpcode(cInv);
|
|
inv.AddParamMove(mul);
|
|
inv.Rehash();
|
|
AddParamMove(inv);
|
|
continue;
|
|
}
|
|
}
|
|
CodeTree pow;
|
|
pow.SetOpcode(cPow);
|
|
pow.AddParamMove(mul);
|
|
pow.AddParamMove( list.first );
|
|
pow.Rehash();
|
|
AddParamMove(pow);
|
|
}
|
|
#ifdef FP_MUL_COMBINE_EXPONENTS
|
|
by_exponent.clear();
|
|
/* Then handle constant exponents */
|
|
for(size_t a=0; a<by_float_exponent.data.size(); ++a)
|
|
{
|
|
double exponent = by_float_exponent.data[a].first;
|
|
if(FloatEqual(exponent, 1.0))
|
|
{
|
|
AddParamsMove(by_float_exponent.data[a].second);
|
|
continue;
|
|
}
|
|
CodeTree mul;
|
|
mul.SetOpcode(cMul);
|
|
mul.SetParamsMove( by_float_exponent.data[a].second );
|
|
mul.Rehash();
|
|
CodeTree pow;
|
|
pow.SetOpcode(cPow);
|
|
pow.AddParamMove(mul);
|
|
pow.AddParam( CodeTree( exponent ) );
|
|
pow.Rehash();
|
|
AddParamMove(pow);
|
|
}
|
|
#endif
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "After ConstantFolding_MulGrouping: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
// return true;
|
|
return !IsIdenticalTo(before); // avoids infinite looping
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CodeTree::ConstantFolding_AddGrouping()
|
|
{
|
|
bool should_regenerate = false;
|
|
CollectionSet add;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
if(GetParam(a).GetOpcode() == cMul) continue;
|
|
if(add.AddCollection(GetParam(a)) == CollectionSet::Suboptimal)
|
|
should_regenerate = true;
|
|
// This catches x + x and x - x
|
|
}
|
|
std::vector<bool> remaining ( GetParamCount() );
|
|
size_t has_mulgroups_remaining = 0;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
const CodeTree& mulgroup = GetParam(a);
|
|
if(mulgroup.GetOpcode() == cMul)
|
|
{
|
|
// This catches x + y*x*z, producing x*(1 + y*z)
|
|
//
|
|
// However we avoid changing 7 + 7*x into 7*(x+1),
|
|
// because it may lead us into producing code such
|
|
// as 20*x + 50*(x+1) + 10, which would be much
|
|
// better expressed as 70*x + 60, and converting
|
|
// back to that format would be needlessly hairy.
|
|
for(size_t b=0; b<mulgroup.GetParamCount(); ++b)
|
|
{
|
|
if(mulgroup.GetParam(b).IsImmed()) continue;
|
|
CollectionSet::PositionType c
|
|
= add.FindIdenticalValueTo(mulgroup.GetParam(b));
|
|
if(add.Found(c))
|
|
{
|
|
CodeTree tmp(mulgroup, CodeTree::CloneTag());
|
|
tmp.DelParam(b);
|
|
tmp.Rehash();
|
|
add.AddCollectionTo(tmp, c);
|
|
should_regenerate = true;
|
|
goto done_a;
|
|
}
|
|
}
|
|
remaining[a] = true;
|
|
has_mulgroups_remaining += 1;
|
|
done_a:;
|
|
}
|
|
}
|
|
|
|
if(has_mulgroups_remaining > 0)
|
|
{
|
|
if(has_mulgroups_remaining > 1) // is it possible to find a duplicate?
|
|
{
|
|
std::vector< std::pair<CodeTree, size_t> > occurance_counts;
|
|
std::multimap<fphash_t, size_t> occurance_pos;
|
|
bool found_dup = false;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
if(remaining[a])
|
|
{
|
|
// This catches x*a + x*b, producing x*(a+b)
|
|
for(size_t b=0; b<GetParam(a).GetParamCount(); ++b)
|
|
{
|
|
const CodeTree& p = GetParam(a).GetParam(b);
|
|
const fphash_t p_hash = p.GetHash();
|
|
for(std::multimap<fphash_t, size_t>::const_iterator
|
|
i = occurance_pos.lower_bound(p_hash);
|
|
i != occurance_pos.end() && i->first == p_hash;
|
|
++i)
|
|
{
|
|
if(occurance_counts[i->second].first.IsIdenticalTo(p))
|
|
{
|
|
occurance_counts[i->second].second += 1;
|
|
found_dup = true;
|
|
goto found_mulgroup_item_dup;
|
|
}
|
|
}
|
|
occurance_counts.push_back(std::make_pair(p, size_t(1)));
|
|
occurance_pos.insert(std::make_pair(p_hash, occurance_counts.size()-1));
|
|
found_mulgroup_item_dup:;
|
|
}
|
|
}
|
|
if(found_dup)
|
|
{
|
|
// Find the "x" to group by
|
|
CodeTree group_by; { size_t max = 0;
|
|
for(size_t p=0; p<occurance_counts.size(); ++p)
|
|
if(occurance_counts[p].second <= 1)
|
|
occurance_counts[p].second = 0;
|
|
else
|
|
{
|
|
occurance_counts[p].second *= occurance_counts[p].first.GetDepth();
|
|
if(occurance_counts[p].second > max)
|
|
{ group_by = occurance_counts[p].first; max = occurance_counts[p].second; }
|
|
} }
|
|
// Collect the items for adding in the group (a+b)
|
|
CodeTree group_add;
|
|
group_add.SetOpcode(cAdd);
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Duplicate across some trees: ";
|
|
DumpTree(group_by);
|
|
std::cout << " in ";
|
|
DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
if(remaining[a])
|
|
for(size_t b=0; b<GetParam(a).GetParamCount(); ++b)
|
|
if(group_by.IsIdenticalTo(GetParam(a).GetParam(b)))
|
|
{
|
|
CodeTree tmp(GetParam(a), CodeTree::CloneTag());
|
|
tmp.DelParam(b);
|
|
tmp.Rehash();
|
|
group_add.AddParamMove(tmp);
|
|
remaining[a] = false;
|
|
break;
|
|
}
|
|
group_add.Rehash();
|
|
CodeTree group;
|
|
group.SetOpcode(cMul);
|
|
group.AddParamMove(group_by);
|
|
group.AddParamMove(group_add);
|
|
group.Rehash();
|
|
add.AddCollection(group);
|
|
should_regenerate = true;
|
|
}
|
|
}
|
|
|
|
// all remaining mul-groups.
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
if(remaining[a])
|
|
{
|
|
if(add.AddCollection(GetParam(a)) == CollectionSet::Suboptimal)
|
|
should_regenerate = true;
|
|
}
|
|
}
|
|
|
|
if(should_regenerate)
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Before ConstantFolding_AddGrouping: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
DelParams();
|
|
|
|
for(CollectionSet::PositionType
|
|
j = add.collections.begin();
|
|
j != add.collections.end();
|
|
++j)
|
|
{
|
|
CodeTree& value = j->second.value;
|
|
CodeTree& coeff = j->second.factor;
|
|
if(j->second.factor_needs_rehashing) coeff.Rehash();
|
|
|
|
if(coeff.IsImmed())
|
|
{
|
|
if(coeff.GetImmed() == 0.0)
|
|
continue;
|
|
if(FloatEqual(coeff.GetImmed(), 1.0))
|
|
{
|
|
AddParamMove(value);
|
|
continue;
|
|
}
|
|
}
|
|
CodeTree mul;
|
|
mul.SetOpcode(cMul);
|
|
mul.AddParamMove(value);
|
|
mul.AddParamMove(coeff);
|
|
mul.Rehash();
|
|
AddParamMove(mul);
|
|
}
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "After ConstantFolding_AddGrouping: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CodeTree::ConstantFolding_IfOperations()
|
|
{
|
|
// If the If() condition begins with a cNot,
|
|
// remove the cNot and swap the branches.
|
|
for(;;)
|
|
{
|
|
if(GetParam(0).GetOpcode() == cNot)
|
|
{
|
|
SetOpcode(cIf);
|
|
GetParam(0).Become( GetParam(0).GetParam(0) );
|
|
GetParam(1).swap(GetParam(2));
|
|
}
|
|
else if(GetParam(0).GetOpcode() == cAbsNot)
|
|
{
|
|
SetOpcode(cAbsIf);
|
|
GetParam(0).Become( GetParam(0).GetParam(0) );
|
|
GetParam(1).swap(GetParam(2));
|
|
}
|
|
else break;
|
|
}
|
|
if(GetParam(0).GetOpcode() == cIf
|
|
|| GetParam(0).GetOpcode() == cAbsIf)
|
|
{
|
|
// if(if(x, a,b), c,d)
|
|
// -> if(x, if(a, c,d), if(b, c,d))
|
|
// when either a or b is constantly true/false
|
|
CodeTree cond = GetParam(0);
|
|
CodeTree truth_a;
|
|
truth_a.SetOpcode(cond.GetOpcode() == cIf ? cNotNot : cAbsNotNot);
|
|
truth_a.AddParam(cond.GetParam(1));
|
|
truth_a.ConstantFolding();
|
|
CodeTree truth_b;
|
|
truth_b.SetOpcode(cond.GetOpcode() == cIf ? cNotNot : cAbsNotNot);
|
|
truth_b.AddParam(cond.GetParam(2));
|
|
truth_b.ConstantFolding();
|
|
if(truth_a.IsImmed() || truth_b.IsImmed())
|
|
{
|
|
CodeTree then_tree;
|
|
then_tree.SetOpcode(cond.GetOpcode());
|
|
then_tree.AddParam(cond.GetParam(1));
|
|
then_tree.AddParam(GetParam(1));
|
|
then_tree.AddParam(GetParam(2));
|
|
then_tree.Rehash();
|
|
CodeTree else_tree;
|
|
else_tree.SetOpcode(cond.GetOpcode());
|
|
else_tree.AddParam(cond.GetParam(2));
|
|
else_tree.AddParam(GetParam(1));
|
|
else_tree.AddParam(GetParam(2));
|
|
else_tree.Rehash();
|
|
SetOpcode(cond.GetOpcode());
|
|
SetParam(0, cond.GetParam(0));
|
|
SetParamMove(1, then_tree);
|
|
SetParamMove(2, else_tree);
|
|
return true; // rerun cIf optimization
|
|
}
|
|
}
|
|
if(GetParam(1).GetOpcode() == GetParam(2).GetOpcode()
|
|
&& (GetParam(1).GetOpcode() == cIf
|
|
|| GetParam(1).GetOpcode() == cAbsIf))
|
|
{
|
|
CodeTree& leaf1 = GetParam(1);
|
|
CodeTree& leaf2 = GetParam(2);
|
|
if(leaf1.GetParam(0).IsIdenticalTo(leaf2.GetParam(0))
|
|
&& (leaf1.GetParam(1).IsIdenticalTo(leaf2.GetParam(1))
|
|
|| leaf1.GetParam(2).IsIdenticalTo(leaf2.GetParam(2))))
|
|
{
|
|
// if(x, if(y,a,b), if(y,c,d))
|
|
// -> if(y, if(x,a,c), if(x,b,d))
|
|
// when either a,c are identical or b,d are identical
|
|
CodeTree then_tree;
|
|
then_tree.SetOpcode(GetOpcode());
|
|
then_tree.AddParam(GetParam(0));
|
|
then_tree.AddParam(leaf1.GetParam(1));
|
|
then_tree.AddParam(leaf2.GetParam(1));
|
|
then_tree.Rehash();
|
|
CodeTree else_tree;
|
|
else_tree.SetOpcode(GetOpcode());
|
|
else_tree.AddParam(GetParam(0));
|
|
else_tree.AddParam(leaf1.GetParam(2));
|
|
else_tree.AddParam(leaf2.GetParam(2));
|
|
else_tree.Rehash();
|
|
SetOpcode(leaf1.GetOpcode());
|
|
SetParam(0, leaf1.GetParam(0));
|
|
SetParamMove(1, then_tree);
|
|
SetParamMove(2, else_tree);
|
|
return true; // rerun cIf optimization
|
|
// cIf [x (cIf [y a z]) (cIf [y z b])] : (cXor x y) z (cIf[x a b])
|
|
// ^ if only we had cXor opcode.
|
|
}
|
|
if(leaf1.GetParam(1).IsIdenticalTo(leaf2.GetParam(1))
|
|
&& leaf1.GetParam(2).IsIdenticalTo(leaf2.GetParam(2)))
|
|
{
|
|
// if(x, if(y,a,b), if(z,a,b))
|
|
// -> if( if(x, y,z), a,b)
|
|
CodeTree cond_tree;
|
|
cond_tree.SetOpcode(GetOpcode());
|
|
cond_tree.AddParamMove(GetParam(0));
|
|
cond_tree.AddParam(leaf1.GetParam(0));
|
|
cond_tree.AddParam(leaf2.GetParam(0));
|
|
cond_tree.Rehash();
|
|
SetOpcode(leaf1.GetOpcode());
|
|
SetParamMove(0, cond_tree);
|
|
SetParam(2, leaf1.GetParam(2));
|
|
SetParam(1, leaf1.GetParam(1));
|
|
return true; // rerun cIf optimization
|
|
}
|
|
if(leaf1.GetParam(1).IsIdenticalTo(leaf2.GetParam(2))
|
|
&& leaf1.GetParam(2).IsIdenticalTo(leaf2.GetParam(1)))
|
|
{
|
|
// if(x, if(y,a,b), if(z,b,a))
|
|
// -> if( if(x, y,!z), a,b)
|
|
CodeTree not_tree;
|
|
not_tree.SetOpcode(leaf2.GetOpcode() == cIf ? cNot : cAbsNot);
|
|
not_tree.AddParam(leaf2.GetParam(0));
|
|
not_tree.Rehash();
|
|
CodeTree cond_tree;
|
|
cond_tree.SetOpcode(GetOpcode());
|
|
cond_tree.AddParamMove(GetParam(0));
|
|
cond_tree.AddParam(leaf1.GetParam(0));
|
|
cond_tree.AddParamMove(not_tree);
|
|
cond_tree.Rehash();
|
|
SetOpcode(leaf1.GetOpcode());
|
|
SetParamMove(0, cond_tree);
|
|
SetParam(2, leaf1.GetParam(2));
|
|
SetParam(1, leaf1.GetParam(1));
|
|
return true; // rerun cIf optimization
|
|
}
|
|
}
|
|
|
|
// If the sub-expression evaluates to approx. zero, yield param3.
|
|
// If the sub-expression evaluates to approx. nonzero, yield param2.
|
|
MinMaxTree p = GetParam(0).CalculateResultBoundaries();
|
|
switch(GetLogicalValue(p, GetOpcode()==cAbsIf))
|
|
{
|
|
case 1: // true
|
|
Become(GetParam(1));
|
|
return true; // rerun optimization (opcode changed)
|
|
case 0: // false
|
|
Become(GetParam(2));
|
|
return true; // rerun optimization (opcode changed)
|
|
default: ;
|
|
}
|
|
|
|
CodeTree& branch1 = GetParam(1);
|
|
CodeTree& branch2 = GetParam(2);
|
|
|
|
if(branch1.IsIdenticalTo(branch2))
|
|
{
|
|
// If both branches of an If() are identical, the test becomes unnecessary
|
|
Become(GetParam(1));
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
|
|
const OPCODE op1 = branch1.GetOpcode();
|
|
const OPCODE op2 = branch2.GetOpcode();
|
|
if(op1 == op2)
|
|
{
|
|
// If both branches apply the same unary function to different values,
|
|
// extract the function. E.g. if(x,sin(a),sin(b)) -> sin(if(x,a,b))
|
|
if(branch1.GetParamCount() == 1)
|
|
{
|
|
CodeTree changed_if;
|
|
changed_if.SetOpcode(GetOpcode());
|
|
changed_if.AddParamMove(GetParam(0));
|
|
changed_if.AddParam(branch1.GetParam(0));
|
|
changed_if.AddParam(branch2.GetParam(0));
|
|
changed_if.Rehash();
|
|
SetOpcode(op1);
|
|
DelParams();
|
|
AddParamMove(changed_if);
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
if(op1 == cAdd || op1 == cMul
|
|
|| op1 == cAnd || op1 == cOr
|
|
|| op1 == cAbsAnd || op1 == cAbsOr
|
|
|| op1 == cMin || op1 == cMax)
|
|
{
|
|
// If the two groups contain one or more
|
|
// identical values, extract them.
|
|
std::vector<CodeTree> overlap;
|
|
for(size_t a=branch1.GetParamCount(); a-- > 0; )
|
|
{
|
|
for(size_t b=branch2.GetParamCount(); b-- > 0; )
|
|
{
|
|
if(branch1.GetParam(a).IsIdenticalTo(branch2.GetParam(b)))
|
|
{
|
|
if(overlap.empty()) { branch1.CopyOnWrite(); branch2.CopyOnWrite(); }
|
|
overlap.push_back(branch1.GetParam(a));
|
|
branch2.DelParam(b);
|
|
branch1.DelParam(a);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!overlap.empty())
|
|
{
|
|
branch1.Rehash();
|
|
branch2.Rehash();
|
|
CodeTree changed_if;
|
|
changed_if.SetOpcode(GetOpcode());
|
|
changed_if.SetParamsMove(GetParams());
|
|
changed_if.Rehash();
|
|
SetOpcode(op1);
|
|
SetParamsMove(overlap);
|
|
AddParamMove(changed_if);
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
}
|
|
}
|
|
// if(x, y+z, y) -> if(x, z,0)+y
|
|
if(op1 == cAdd
|
|
|| op1 == cMul
|
|
|| (op1 == cAnd && branch2.IsLogicalValue())
|
|
|| (op1 == cOr && branch2.IsLogicalValue())
|
|
)
|
|
{
|
|
for(size_t a=branch1.GetParamCount(); a-- > 0; )
|
|
if(branch1.GetParam(a).IsIdenticalTo(branch2))
|
|
{
|
|
branch1.CopyOnWrite();
|
|
branch1.DelParam(a);
|
|
branch1.Rehash();
|
|
CodeTree branch2_backup = branch2;
|
|
branch2 = CodeTree( (op1==cAdd||op1==cOr) ? 0.0 : 1.0 );
|
|
CodeTree changed_if;
|
|
changed_if.SetOpcode(GetOpcode());
|
|
changed_if.SetParamsMove(GetParams());
|
|
changed_if.Rehash();
|
|
SetOpcode(op1);
|
|
AddParamMove(branch2_backup);
|
|
AddParamMove(changed_if);
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
}
|
|
// if(x, y&z, !!y) -> if(x, z, 1) & y
|
|
if((op1 == cAnd || op1 == cOr) && op2 == cNotNot)
|
|
{
|
|
CodeTree& branch2op = branch2.GetParam(0);
|
|
for(size_t a=branch1.GetParamCount(); a-- > 0; )
|
|
if(branch1.GetParam(a).IsIdenticalTo(branch2op))
|
|
{
|
|
branch1.CopyOnWrite();
|
|
branch1.DelParam(a);
|
|
branch1.Rehash();
|
|
CodeTree branch2_backup = branch2op;
|
|
branch2 = CodeTree( (op1==cOr) ? 0.0 : 1.0 );
|
|
CodeTree changed_if;
|
|
changed_if.SetOpcode(GetOpcode());
|
|
changed_if.SetParamsMove(GetParams());
|
|
changed_if.Rehash();
|
|
SetOpcode(op1);
|
|
AddParamMove(branch2_backup);
|
|
AddParamMove(changed_if);
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
}
|
|
// if(x, y, y+z) -> if(x, 0,z)+y
|
|
if(op2 == cAdd
|
|
|| op2 == cMul
|
|
|| (op2 == cAnd && branch1.IsLogicalValue())
|
|
|| (op2 == cOr && branch1.IsLogicalValue())
|
|
)
|
|
{
|
|
for(size_t a=branch2.GetParamCount(); a-- > 0; )
|
|
if(branch2.GetParam(a).IsIdenticalTo(branch1))
|
|
{
|
|
branch2.CopyOnWrite();
|
|
branch2.DelParam(a);
|
|
branch2.Rehash();
|
|
CodeTree branch1_backup = branch1;
|
|
branch1 = CodeTree( (op2==cAdd||op2==cOr) ? 0.0 : 1.0 );
|
|
CodeTree changed_if;
|
|
changed_if.SetOpcode(GetOpcode());
|
|
changed_if.SetParamsMove(GetParams());
|
|
changed_if.Rehash();
|
|
SetOpcode(op2);
|
|
AddParamMove(branch1_backup);
|
|
AddParamMove(changed_if);
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
}
|
|
// if(x, !!y, y&z) -> if(x, 1, z) & y
|
|
if((op2 == cAnd || op2 == cOr) && op1 == cNotNot)
|
|
{
|
|
CodeTree& branch1op = branch1.GetParam(0);
|
|
for(size_t a=branch2.GetParamCount(); a-- > 0; )
|
|
if(branch2.GetParam(a).IsIdenticalTo(branch1op))
|
|
{
|
|
branch2.CopyOnWrite();
|
|
branch2.DelParam(a);
|
|
branch2.Rehash();
|
|
CodeTree branch1_backup = branch1op;
|
|
branch1 = CodeTree( (op2==cOr) ? 0.0 : 1.0 );
|
|
CodeTree changed_if;
|
|
changed_if.SetOpcode(GetOpcode());
|
|
changed_if.SetParamsMove(GetParams());
|
|
changed_if.Rehash();
|
|
SetOpcode(op2);
|
|
AddParamMove(branch1_backup);
|
|
AddParamMove(changed_if);
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
}
|
|
return false; // No changes
|
|
}
|
|
|
|
bool CodeTree::ConstantFolding_PowOperations()
|
|
{
|
|
if(GetParam(0).IsImmed()
|
|
&& GetParam(1).IsImmed())
|
|
{
|
|
double const_value = fp_pow(GetParam(0).GetImmed(),
|
|
GetParam(1).GetImmed());
|
|
data = new CodeTreeData(const_value);
|
|
return false;
|
|
}
|
|
if(GetParam(1).IsImmed()
|
|
&& (float)GetParam(1).GetImmed() == 1.0)
|
|
{
|
|
// Conversion through a float type value gets rid of
|
|
// awkward abs(x)^1 generated from exp(log(x^6)/6),
|
|
// without sacrificing as much precision as FloatEqual() does.
|
|
// x^1 = x
|
|
Become(GetParam(0));
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
if(GetParam(0).IsImmed()
|
|
&& (float)GetParam(0).GetImmed() == 1.0)
|
|
{
|
|
// 1^x = 1
|
|
data = new CodeTreeData(1.0);
|
|
return false;
|
|
}
|
|
|
|
// 5^(20*x) = (5^20)^x
|
|
if(GetParam(0).IsImmed()
|
|
&& GetParam(1).GetOpcode() == cMul)
|
|
{
|
|
bool changes = false;
|
|
double base_immed = GetParam(0).GetImmed();
|
|
CodeTree mulgroup = GetParam(1);
|
|
for(size_t a=mulgroup.GetParamCount(); a-->0; )
|
|
if(mulgroup.GetParam(a).IsImmed())
|
|
{
|
|
double imm = mulgroup.GetParam(a).GetImmed();
|
|
//if(imm >= 0.0)
|
|
{
|
|
double new_base_immed = fp_pow(base_immed, imm);
|
|
if(std::isinf(new_base_immed) || new_base_immed == 0.0)
|
|
{
|
|
// It produced an infinity. Do not change.
|
|
break;
|
|
}
|
|
|
|
if(!changes)
|
|
{
|
|
changes = true;
|
|
mulgroup.CopyOnWrite();
|
|
}
|
|
base_immed = new_base_immed;
|
|
mulgroup.DelParam(a);
|
|
break; //
|
|
}
|
|
}
|
|
if(changes)
|
|
{
|
|
mulgroup.Rehash();
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Before pow-mul change: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
GetParam(0).Become(CodeTree(base_immed));
|
|
GetParam(1).Become(mulgroup);
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "After pow-mul change: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
}
|
|
}
|
|
// (x*20)^2 = x^2 * 20^2
|
|
if(GetParam(1).IsImmed()
|
|
&& GetParam(0).GetOpcode() == cMul)
|
|
{
|
|
double exponent_immed = GetParam(1).GetImmed();
|
|
double factor_immed = 1.0;
|
|
bool changes = false;
|
|
CodeTree& mulgroup = GetParam(0);
|
|
for(size_t a=mulgroup.GetParamCount(); a-->0; )
|
|
if(mulgroup.GetParam(a).IsImmed())
|
|
{
|
|
double imm = mulgroup.GetParam(a).GetImmed();
|
|
//if(imm >= 0.0)
|
|
{
|
|
double new_factor_immed = fp_pow(imm, exponent_immed);
|
|
if(std::isinf(new_factor_immed) || new_factor_immed == 0.0)
|
|
{
|
|
// It produced an infinity. Do not change.
|
|
break;
|
|
}
|
|
if(!changes)
|
|
{
|
|
changes = true;
|
|
mulgroup.CopyOnWrite();
|
|
}
|
|
factor_immed *= new_factor_immed;
|
|
mulgroup.DelParam(a);
|
|
break; //
|
|
}
|
|
}
|
|
if(changes)
|
|
{
|
|
mulgroup.Rehash();
|
|
CodeTree newpow;
|
|
newpow.SetOpcode(cPow);
|
|
newpow.SetParamsMove(GetParams());
|
|
SetOpcode(cMul);
|
|
AddParamMove(newpow);
|
|
AddParam( CodeTree(factor_immed) );
|
|
return true; // rerun optimization (opcode changed)
|
|
}
|
|
}
|
|
|
|
// (x^3)^2 = x^6
|
|
// NOTE: If 3 is even and 3*2 is not, x must be changed to abs(x).
|
|
if(GetParam(0).GetOpcode() == cPow
|
|
&& GetParam(1).IsImmed()
|
|
&& GetParam(0).GetParam(1).IsImmed())
|
|
{
|
|
double a = GetParam(0).GetParam(1).GetImmed();
|
|
double b = GetParam(1).GetImmed();
|
|
double c = a * b; // new exponent
|
|
if(IsEvenIntegerConst(a) // a is an even int?
|
|
&& !IsEvenIntegerConst(c)) // c is not?
|
|
{
|
|
CodeTree newbase;
|
|
newbase.SetOpcode(cAbs);
|
|
newbase.AddParam(GetParam(0).GetParam(0));
|
|
newbase.Rehash();
|
|
SetParamMove(0, newbase);
|
|
}
|
|
else
|
|
SetParam(0, GetParam(0).GetParam(0));
|
|
SetParam(1, CodeTree(c));
|
|
}
|
|
return false; // No changes that require a rerun
|
|
}
|
|
|
|
bool CodeTree::ConstantFolding_ComparisonOperations()
|
|
{
|
|
static const RangeComparisonData Data[6] =
|
|
{
|
|
// cEqual:
|
|
// Case: p0 == p1 Antonym: p0 != p1
|
|
// Synonym: p1 == p0 Antonym: p1 != p0
|
|
{ RangeComparisonData::MakeTrue, // If identical: always true
|
|
{RangeComparisonData::MakeFalse, // If Always p0 < p1: always false
|
|
RangeComparisonData::Unchanged,
|
|
RangeComparisonData::MakeFalse, // If Always p0 > p1: always false
|
|
RangeComparisonData::Unchanged},
|
|
// NotNot(p0) if p1==1 NotNot(p1) if p0==1
|
|
// Not(p0) if p1==0 Not(p1) if p0==0
|
|
{RangeComparisonData::MakeNotNotP0, RangeComparisonData::Eq1},
|
|
{RangeComparisonData::MakeNotNotP1, RangeComparisonData::Eq1},
|
|
{RangeComparisonData::MakeNotP0, RangeComparisonData::Eq0},
|
|
{RangeComparisonData::MakeNotP1, RangeComparisonData::Eq0}
|
|
},
|
|
// cNEqual:
|
|
// Case: p0 != p1 Antonym: p0 == p1
|
|
// Synonym: p1 != p0 Antonym: p1 == p0
|
|
{ RangeComparisonData::MakeFalse, // If identical: always false
|
|
{RangeComparisonData::MakeTrue, // If Always p0 < p1: always true
|
|
RangeComparisonData::Unchanged,
|
|
RangeComparisonData::MakeTrue, // If Always p0 > p1: always true
|
|
RangeComparisonData::Unchanged},
|
|
// NotNot(p0) if p1==0 NotNot(p1) if p0==0
|
|
// Not(p0) if p1==1 Not(p1) if p0==1
|
|
{RangeComparisonData::MakeNotNotP0, RangeComparisonData::Eq0},
|
|
{RangeComparisonData::MakeNotNotP1, RangeComparisonData::Eq0},
|
|
{RangeComparisonData::MakeNotP0, RangeComparisonData::Eq1},
|
|
{RangeComparisonData::MakeNotP1, RangeComparisonData::Eq1}
|
|
},
|
|
// cLess:
|
|
// Case: p0 < p1 Antonym: p0 >= p1
|
|
// Synonym: p1 > p0 Antonym: p1 <= p0
|
|
{ RangeComparisonData::MakeFalse, // If identical: always false
|
|
{RangeComparisonData::MakeTrue, // If Always p0 < p1: always true
|
|
RangeComparisonData::MakeNEqual,
|
|
RangeComparisonData::MakeFalse, // If Always p0 > p1: always false
|
|
RangeComparisonData::MakeFalse},// If Always p0 >= p1: always false
|
|
// Not(p0) if p1>0 & p1<=1 -- NotNot(p1) if p0>=0 & p0<1
|
|
{RangeComparisonData::MakeNotP0, RangeComparisonData::Gt0Le1},
|
|
{RangeComparisonData::MakeNotNotP1, RangeComparisonData::Ge0Lt1},
|
|
{RangeComparisonData::Unchanged, RangeComparisonData::Never},
|
|
{RangeComparisonData::Unchanged, RangeComparisonData::Never}
|
|
},
|
|
// cLessOrEq:
|
|
// Case: p0 <= p1 Antonym: p0 > p1
|
|
// Synonym: p1 >= p0 Antonym: p1 < p0
|
|
{ RangeComparisonData::MakeTrue, // If identical: always true
|
|
{RangeComparisonData::Unchanged, // If Always p0 < p1: ?
|
|
RangeComparisonData::MakeTrue, // If Always p0 <= p1: always true
|
|
RangeComparisonData::MakeFalse, // If Always p0 > p1: always false
|
|
RangeComparisonData::MakeEqual},// If Never p0 < p1: use cEqual
|
|
// Not(p0) if p1>=0 & p1<1 -- NotNot(p1) if p0>0 & p0<=1
|
|
{RangeComparisonData::MakeNotP0, RangeComparisonData::Ge0Lt1},
|
|
{RangeComparisonData::MakeNotNotP1, RangeComparisonData::Gt0Le1},
|
|
{RangeComparisonData::Unchanged, RangeComparisonData::Never},
|
|
{RangeComparisonData::Unchanged, RangeComparisonData::Never}
|
|
},
|
|
// cGreater:
|
|
// Case: p0 > p1 Antonym: p0 <= p1
|
|
// Synonym: p1 < p0 Antonym: p1 >= p0
|
|
{ RangeComparisonData::MakeFalse, // If identical: always false
|
|
{RangeComparisonData::MakeFalse, // If Always p0 < p1: always false
|
|
RangeComparisonData::MakeFalse, // If Always p0 <= p1: always false
|
|
RangeComparisonData::MakeTrue, // If Always p0 > p1: always true
|
|
RangeComparisonData::MakeNEqual},
|
|
// NotNot(p0) if p1>=0 & p1<1 -- Not(p1) if p0>0 & p0<=1
|
|
{RangeComparisonData::MakeNotNotP0, RangeComparisonData::Ge0Lt1},
|
|
{RangeComparisonData::MakeNotP1, RangeComparisonData::Gt0Le1},
|
|
{RangeComparisonData::Unchanged, RangeComparisonData::Never},
|
|
{RangeComparisonData::Unchanged, RangeComparisonData::Never}
|
|
},
|
|
// cGreaterOrEq:
|
|
// Case: p0 >= p1 Antonym: p0 < p1
|
|
// Synonym: p1 <= p0 Antonym: p1 > p0
|
|
{ RangeComparisonData::MakeTrue, // If identical: always true
|
|
{RangeComparisonData::MakeFalse, // If Always p0 < p1: always false
|
|
RangeComparisonData::MakeEqual, // If Always p0 >= p1: always true
|
|
RangeComparisonData::Unchanged, // If always p0 > p1: ?
|
|
RangeComparisonData::MakeTrue}, // If Never p0 > p1: use cEqual
|
|
// NotNot(p0) if p1>0 & p1<=1 -- Not(p1) if p0>=0 & p0<1
|
|
{RangeComparisonData::MakeNotNotP0, RangeComparisonData::Gt0Le1},
|
|
{RangeComparisonData::MakeNotP1, RangeComparisonData::Ge0Lt1},
|
|
{RangeComparisonData::Unchanged, RangeComparisonData::Never},
|
|
{RangeComparisonData::Unchanged, RangeComparisonData::Never}
|
|
}
|
|
};
|
|
switch(Data[GetOpcode()-cEqual].Analyze(GetParam(0), GetParam(1)))
|
|
{
|
|
case RangeComparisonData::MakeFalse:
|
|
data = new CodeTreeData(0.0); return true;
|
|
case RangeComparisonData::MakeTrue:
|
|
data = new CodeTreeData(1.0); return true;
|
|
case RangeComparisonData::MakeEqual: SetOpcode(cEqual); return true;
|
|
case RangeComparisonData::MakeNEqual: SetOpcode(cNEqual); return true;
|
|
case RangeComparisonData::MakeNotNotP0: SetOpcode(cNotNot); DelParam(1); return true;
|
|
case RangeComparisonData::MakeNotNotP1: SetOpcode(cNotNot); DelParam(0); return true;
|
|
case RangeComparisonData::MakeNotP0: SetOpcode(cNot); DelParam(1); return true;
|
|
case RangeComparisonData::MakeNotP1: SetOpcode(cNot); DelParam(0); return true;
|
|
case RangeComparisonData::Unchanged:;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CodeTree::ConstantFolding_Assimilate()
|
|
{
|
|
/* If the list contains another list of the same kind, assimilate it */
|
|
bool assimilated = false;
|
|
for(size_t a=GetParamCount(); a-- > 0; )
|
|
if(GetParam(a).GetOpcode() == GetOpcode())
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
if(!assimilated)
|
|
{
|
|
std::cout << "Before assimilation: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
assimilated = true;
|
|
}
|
|
#endif
|
|
// Assimilate its children and remove it
|
|
AddParamsMove(GetParam(a).GetUniqueRef().GetParams(), a);
|
|
}
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
if(assimilated)
|
|
{
|
|
std::cout << "After assimilation: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
}
|
|
#endif
|
|
return assimilated;
|
|
}
|
|
|
|
void CodeTree::ConstantFolding()
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Runs ConstantFolding for: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
using namespace std;
|
|
redo:;
|
|
|
|
// Insert here any hardcoded constant-folding optimizations
|
|
// that you want to be done whenever a new subtree is generated.
|
|
/* Not recursive. */
|
|
|
|
double const_value = 1.0;
|
|
if(GetOpcode() != cImmed)
|
|
{
|
|
MinMaxTree p = CalculateResultBoundaries();
|
|
if(p.has_min && p.has_max && p.min == p.max)
|
|
{
|
|
// Replace us with this immed
|
|
const_value = p.min;
|
|
goto ReplaceTreeWithConstValue;
|
|
}
|
|
}
|
|
|
|
if(false)
|
|
{
|
|
ReplaceTreeWithOne:
|
|
const_value = 1.0;
|
|
goto ReplaceTreeWithConstValue;
|
|
ReplaceTreeWithZero:
|
|
const_value = 0.0;
|
|
ReplaceTreeWithConstValue:
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Replacing "; DumpTree(*this);
|
|
if(IsImmed())
|
|
std::cout << "(" << std::hex
|
|
<< *(const uint_least64_t*)&GetImmed()
|
|
<< std::dec << ")";
|
|
std::cout << " with const value " << const_value;
|
|
std::cout << "(" << std::hex
|
|
<< *(const uint_least64_t*)&const_value
|
|
<< std::dec << ")";
|
|
std::cout << "\n";
|
|
#endif
|
|
data = new CodeTreeData(const_value);
|
|
return;
|
|
ReplaceTreeWithParam0:
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Before replace: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
Become(GetParam(0));
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "After replace: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
goto redo;
|
|
}
|
|
|
|
/* Constant folding */
|
|
switch(GetOpcode())
|
|
{
|
|
case cImmed:
|
|
break; // nothing to do
|
|
case VarBegin:
|
|
break; // nothing to do
|
|
|
|
case cAnd:
|
|
case cAbsAnd:
|
|
{
|
|
ConstantFolding_Assimilate();
|
|
for(size_t a=GetParamCount(); a-- > 0; )
|
|
switch(GetLogicalValue(GetParam(a).CalculateResultBoundaries(),
|
|
GetOpcode()==cAbsAnd))
|
|
{
|
|
case 0: goto ReplaceTreeWithZero;
|
|
case 1: DelParam(a); break; // x & y & 1 = x & y; x & 1 = !!x
|
|
default: ;
|
|
}
|
|
switch(GetParamCount())
|
|
{
|
|
case 0: goto ReplaceTreeWithOne;
|
|
case 1: SetOpcode(GetOpcode()==cAnd ? cNotNot : cAbsNotNot); goto redo; // Replace self with the single operand
|
|
default: if(GetOpcode()==cAnd) if(ConstantFolding_AndLogic()) goto redo;
|
|
}
|
|
break;
|
|
}
|
|
case cOr:
|
|
case cAbsOr:
|
|
{
|
|
ConstantFolding_Assimilate();
|
|
for(size_t a=GetParamCount(); a-- > 0; )
|
|
switch(GetLogicalValue(GetParam(a).CalculateResultBoundaries(),
|
|
GetOpcode()==cAbsOr))
|
|
{
|
|
case 1: goto ReplaceTreeWithOne;
|
|
case 0: DelParam(a); break;
|
|
default: ;
|
|
}
|
|
switch(GetParamCount())
|
|
{
|
|
case 0: goto ReplaceTreeWithZero;
|
|
case 1: SetOpcode(GetOpcode()==cOr ? cNotNot : cAbsNotNot); goto redo; // Replace self with the single operand
|
|
default: if(GetOpcode()==cOr) if(ConstantFolding_OrLogic()) goto redo;
|
|
}
|
|
break;
|
|
}
|
|
case cNot:
|
|
case cAbsNot:
|
|
{
|
|
unsigned opposite = 0;
|
|
switch(GetParam(0).GetOpcode())
|
|
{
|
|
case cEqual: opposite = cNEqual; break;
|
|
case cNEqual: opposite = cEqual; break;
|
|
case cLess: opposite = cGreaterOrEq; break;
|
|
case cGreater: opposite = cLessOrEq; break;
|
|
case cLessOrEq: opposite = cGreater; break;
|
|
case cGreaterOrEq: opposite = cLess; break;
|
|
//cNotNot already handled by grammar: @L cNotNot
|
|
case cNot: opposite = cNotNot; break;
|
|
case cAbsNot: opposite = cAbsNotNot; break;
|
|
case cAbsNotNot: opposite = cAbsNot; break;
|
|
default: break;
|
|
}
|
|
if(opposite)
|
|
{
|
|
SetOpcode(OPCODE(opposite));
|
|
SetParamsMove(GetParam(0).GetUniqueRef().GetParams());
|
|
goto redo;
|
|
}
|
|
|
|
// If the sub-expression evaluates to approx. zero, yield one.
|
|
// If the sub-expression evaluates to approx. nonzero, yield zero.
|
|
switch(GetLogicalValue(GetParam(0).CalculateResultBoundaries(),
|
|
GetOpcode()==cAbsNot))
|
|
{
|
|
case 1: goto ReplaceTreeWithZero;
|
|
case 0: goto ReplaceTreeWithOne;
|
|
default: ;
|
|
}
|
|
if(GetOpcode() == cNot && GetParam(0).IsAlwaysSigned(true))
|
|
SetOpcode(cAbsNot);
|
|
|
|
if(GetParam(0).GetOpcode() == cIf
|
|
|| GetParam(0).GetOpcode() == cAbsIf)
|
|
{
|
|
CodeTree iftree = GetParam(0);
|
|
const CodeTree& ifp1 = iftree.GetParam(1);
|
|
const CodeTree& ifp2 = iftree.GetParam(2);
|
|
if(ifp1.GetOpcode() == cNot
|
|
|| ifp1.GetOpcode() == cAbsNot)
|
|
{
|
|
// cNot [(cIf [x (cNot[y]) z])] -> cIf [x (cNotNot[y]) (cNot[z])]
|
|
SetParam(0, iftree.GetParam(0)); // condition
|
|
CodeTree p1;
|
|
p1.SetOpcode(ifp1.GetOpcode()==cNot ? cNotNot : cAbsNotNot);
|
|
p1.AddParam(ifp1.GetParam(0));
|
|
p1.Rehash();
|
|
AddParamMove(p1);
|
|
CodeTree p2;
|
|
p2.SetOpcode(GetOpcode());
|
|
p2.AddParam(ifp2);
|
|
p2.Rehash();
|
|
AddParamMove(p2);
|
|
SetOpcode(iftree.GetOpcode());
|
|
goto redo;
|
|
}
|
|
if(ifp2.GetOpcode() == cNot
|
|
|| ifp2.GetOpcode() == cAbsNot)
|
|
{
|
|
// cNot [(cIf [x y (cNot[z])])] -> cIf [x (cNot[y]) (cNotNot[z])]
|
|
SetParam(0, iftree.GetParam(0)); // condition
|
|
CodeTree p1;
|
|
p1.SetOpcode(GetOpcode());
|
|
p1.AddParam(ifp1);
|
|
p1.Rehash();
|
|
AddParamMove(p1);
|
|
CodeTree p2;
|
|
p2.SetOpcode(ifp2.GetOpcode()==cNot ? cNotNot : cAbsNotNot);
|
|
p2.AddParam(ifp2.GetParam(0));
|
|
p2.Rehash();
|
|
AddParamMove(p2);
|
|
SetOpcode(iftree.GetOpcode());
|
|
goto redo;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case cNotNot:
|
|
case cAbsNotNot:
|
|
{
|
|
// The function of cNotNot is to protect a logical value from
|
|
// changing. If the parameter is already a logical value,
|
|
// then the cNotNot opcode is redundant.
|
|
if(GetParam(0).IsLogicalValue())
|
|
goto ReplaceTreeWithParam0;
|
|
|
|
// If the sub-expression evaluates to approx. zero, yield zero.
|
|
// If the sub-expression evaluates to approx. nonzero, yield one.
|
|
switch(GetLogicalValue(GetParam(0).CalculateResultBoundaries(),
|
|
GetOpcode()==cAbsNotNot))
|
|
{
|
|
case 0: goto ReplaceTreeWithZero;
|
|
case 1: goto ReplaceTreeWithOne;
|
|
default: ;
|
|
}
|
|
if(GetOpcode() == cNotNot && GetParam(0).IsAlwaysSigned(true))
|
|
SetOpcode(cAbsNotNot);
|
|
|
|
if(GetParam(0).GetOpcode() == cIf
|
|
|| GetParam(0).GetOpcode() == cAbsIf)
|
|
{
|
|
CodeTree iftree = GetParam(0);
|
|
const CodeTree& ifp1 = iftree.GetParam(1);
|
|
const CodeTree& ifp2 = iftree.GetParam(2);
|
|
if(ifp1.GetOpcode() == cNot
|
|
|| ifp1.GetOpcode() == cAbsNot)
|
|
{
|
|
// cNotNot [(cIf [x (cNot[y]) z])] -> cIf [x (cNot[y]) (cNotNot[z])]
|
|
SetParam(0, iftree.GetParam(0)); // condition
|
|
AddParam(ifp1);
|
|
CodeTree p2;
|
|
p2.SetOpcode(GetOpcode());
|
|
p2.AddParam(ifp2);
|
|
p2.Rehash();
|
|
AddParamMove(p2);
|
|
SetOpcode(iftree.GetOpcode());
|
|
goto redo;
|
|
}
|
|
if(ifp2.GetOpcode() == cNot
|
|
|| ifp2.GetOpcode() == cAbsNot)
|
|
{
|
|
// cNotNot [(cIf [x y (cNot[z])])] -> cIf [x (cNotNot[y]) (cNot[z])]
|
|
SetParam(0, iftree.GetParam(0)); // condition
|
|
CodeTree p1;
|
|
p1.SetOpcode(GetOpcode());
|
|
p1.AddParam(ifp1);
|
|
p1.Rehash();
|
|
AddParamMove(p1);
|
|
AddParam(ifp2);
|
|
SetOpcode(iftree.GetOpcode());
|
|
goto redo;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case cIf:
|
|
case cAbsIf:
|
|
{
|
|
if(ConstantFolding_IfOperations())
|
|
goto redo;
|
|
break;
|
|
}
|
|
case cMul:
|
|
{
|
|
NowWeAreMulGroup: ;
|
|
ConstantFolding_Assimilate();
|
|
// If one sub-expression evalutes to exact zero, yield zero.
|
|
double immed_product = 1.0;
|
|
size_t n_immeds = 0; bool needs_resynth=false;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
if(!GetParam(a).IsImmed()) continue;
|
|
// ^ Only check constant values
|
|
double immed = GetParam(a).GetImmed();
|
|
if(immed == 0.0) goto ReplaceTreeWithZero;
|
|
immed_product *= immed; ++n_immeds;
|
|
}
|
|
// Merge immeds.
|
|
if(n_immeds > 1 || (n_immeds == 1 && FloatEqual(immed_product, 1.0)))
|
|
needs_resynth = true;
|
|
if(needs_resynth)
|
|
{
|
|
// delete immeds and add new ones
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "cMul: Will add new immed " << immed_product << "\n";
|
|
#endif
|
|
for(size_t a=GetParamCount(); a-->0; )
|
|
if(GetParam(a).IsImmed())
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << " - For that, deleting immed " << GetParam(a).GetImmed();
|
|
std::cout << "\n";
|
|
#endif
|
|
DelParam(a);
|
|
}
|
|
if(!FloatEqual(immed_product, 1.0))
|
|
AddParam( CodeTree(immed_product) );
|
|
}
|
|
switch(GetParamCount())
|
|
{
|
|
case 0: goto ReplaceTreeWithOne;
|
|
case 1: goto ReplaceTreeWithParam0; // Replace self with the single operand
|
|
default:
|
|
if(ConstantFolding_MulGrouping()) goto redo;
|
|
if(ConstantFolding_MulLogicItems()) goto redo;
|
|
}
|
|
break;
|
|
}
|
|
case cAdd:
|
|
{
|
|
ConstantFolding_Assimilate();
|
|
double immed_sum = 0.0;
|
|
size_t n_immeds = 0; bool needs_resynth=false;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
if(!GetParam(a).IsImmed()) continue;
|
|
// ^ Only check constant values
|
|
double immed = GetParam(a).GetImmed();
|
|
immed_sum += immed; ++n_immeds;
|
|
}
|
|
// Merge immeds.
|
|
if(n_immeds > 1 || (n_immeds == 1 && immed_sum == 0.0))
|
|
needs_resynth = true;
|
|
if(needs_resynth)
|
|
{
|
|
// delete immeds and add new ones
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "cAdd: Will add new immed " << immed_sum << "\n";
|
|
std::cout << "In: "; DumpTree(*this);
|
|
std::cout << "\n";
|
|
#endif
|
|
for(size_t a=GetParamCount(); a-->0; )
|
|
if(GetParam(a).IsImmed())
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << " - For that, deleting immed " << GetParam(a).GetImmed();
|
|
std::cout << "\n";
|
|
#endif
|
|
DelParam(a);
|
|
}
|
|
if(!(immed_sum == 0.0))
|
|
AddParam( CodeTree(immed_sum) );
|
|
}
|
|
switch(GetParamCount())
|
|
{
|
|
case 0: goto ReplaceTreeWithZero;
|
|
case 1: goto ReplaceTreeWithParam0; // Replace self with the single operand
|
|
default:
|
|
if(ConstantFolding_AddGrouping()) goto redo;
|
|
if(ConstantFolding_AddLogicItems()) goto redo;
|
|
}
|
|
break;
|
|
}
|
|
case cMin:
|
|
{
|
|
ConstantFolding_Assimilate();
|
|
/* Goal: If there is any pair of two operands, where
|
|
* their ranges form a disconnected set, i.e. as below:
|
|
* xxxxx
|
|
* yyyyyy
|
|
* Then remove the larger one.
|
|
*
|
|
* Algorithm: 1. figure out the smallest maximum of all operands.
|
|
* 2. eliminate all operands where their minimum is
|
|
* larger than the selected maximum.
|
|
*/
|
|
size_t preserve=0;
|
|
MinMaxTree smallest_maximum;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
MinMaxTree p = GetParam(a).CalculateResultBoundaries();
|
|
if(p.has_max && (!smallest_maximum.has_max || p.max < smallest_maximum.max))
|
|
{
|
|
smallest_maximum.max = p.max;
|
|
smallest_maximum.has_max = true;
|
|
preserve=a;
|
|
} }
|
|
if(smallest_maximum.has_max)
|
|
for(size_t a=GetParamCount(); a-- > 0; )
|
|
{
|
|
MinMaxTree p = GetParam(a).CalculateResultBoundaries();
|
|
if(p.has_min && a != preserve && p.min >= smallest_maximum.max)
|
|
DelParam(a);
|
|
}
|
|
//fprintf(stderr, "Remains: %u\n", (unsigned)GetParamCount());
|
|
if(GetParamCount() == 1)
|
|
{
|
|
// Replace self with the single operand
|
|
goto ReplaceTreeWithParam0;
|
|
}
|
|
break;
|
|
}
|
|
case cMax:
|
|
{
|
|
ConstantFolding_Assimilate();
|
|
/* Goal: If there is any pair of two operands, where
|
|
* their ranges form a disconnected set, i.e. as below:
|
|
* xxxxx
|
|
* yyyyyy
|
|
* Then remove the smaller one.
|
|
*
|
|
* Algorithm: 1. figure out the biggest minimum of all operands.
|
|
* 2. eliminate all operands where their maximum is
|
|
* smaller than the selected minimum.
|
|
*/
|
|
size_t preserve=0;
|
|
MinMaxTree biggest_minimum;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
MinMaxTree p = GetParam(a).CalculateResultBoundaries();
|
|
if(p.has_min && (!biggest_minimum.has_min || p.min > biggest_minimum.min))
|
|
{
|
|
biggest_minimum.min = p.min;
|
|
biggest_minimum.has_min = true;
|
|
preserve=a;
|
|
} }
|
|
if(biggest_minimum.has_min)
|
|
{
|
|
//fprintf(stderr, "Removing all where max < %g\n", biggest_minimum.min);
|
|
for(size_t a=GetParamCount(); a-- > 0; )
|
|
{
|
|
MinMaxTree p = GetParam(a).CalculateResultBoundaries();
|
|
if(p.has_max && a != preserve && p.max < biggest_minimum.min)
|
|
{
|
|
//fprintf(stderr, "Removing %g\n", p.max);
|
|
DelParam(a);
|
|
}
|
|
}
|
|
}
|
|
//fprintf(stderr, "Remains: %u\n", (unsigned)GetParamCount());
|
|
if(GetParamCount() == 1)
|
|
{
|
|
// Replace self with the single operand
|
|
goto ReplaceTreeWithParam0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case cEqual:
|
|
case cNEqual:
|
|
case cLess:
|
|
case cGreater:
|
|
case cLessOrEq:
|
|
case cGreaterOrEq:
|
|
if(ConstantFolding_ComparisonOperations()) goto redo;
|
|
// Any reversible functions:
|
|
// sin(x) -> ASIN: Not doable, x can be cyclic
|
|
// asin(x) -> SIN: doable.
|
|
// Invalid combinations are caught by
|
|
// range-estimation. Threshold is at |pi/2|.
|
|
// acos(x) -> COS: doable.
|
|
// Invalid combinations are caught by
|
|
// range-estimation. Note that though
|
|
// the range is contiguous, it is direction-flipped.
|
|
// log(x) -> EXP: no problem
|
|
// exp2, exp10: Converted to cPow, done by grammar.
|
|
// atan(x) -> TAN: doable.
|
|
// Invalid combinations are caught by
|
|
// range-estimation. Threshold is at |pi/2|.
|
|
// sinh(x) -> ASINH: no problem
|
|
// tanh(x) -> ATANH: no problem, but atanh is limited to -1..1
|
|
// Invalid combinations are caught by
|
|
// range-estimation, but the exact value
|
|
// of 1.0 still needs checking, because
|
|
// it involves infinity.
|
|
if(GetParam(1).IsImmed())
|
|
switch(GetParam(0).GetOpcode())
|
|
{
|
|
case cAsin:
|
|
SetParam(0, GetParam(0).GetParam(0));
|
|
SetParam(1, CodeTree(fp_sin(GetParam(1).GetImmed())));
|
|
goto redo;
|
|
case cAcos:
|
|
// -1..+1 --> pi..0 (polarity-flipping)
|
|
SetParam(0, GetParam(0).GetParam(0));
|
|
SetParam(1, CodeTree(fp_cos(GetParam(1).GetImmed())));
|
|
SetOpcode( GetOpcode()==cLess ? cGreater
|
|
: GetOpcode()==cLessOrEq ? cGreaterOrEq
|
|
: GetOpcode()==cGreater ? cLess
|
|
: GetOpcode()==cGreaterOrEq ? cLessOrEq
|
|
: GetOpcode() );
|
|
goto redo;
|
|
case cAtan:
|
|
SetParam(0, GetParam(0).GetParam(0));
|
|
SetParam(1, CodeTree(fp_tan(GetParam(1).GetImmed())));
|
|
goto redo;
|
|
case cLog:
|
|
// Different logarithms have a constant-multiplication,
|
|
// which is no problem.
|
|
SetParam(0, GetParam(0).GetParam(0));
|
|
SetParam(1, CodeTree(fp_exp(GetParam(1).GetImmed())));
|
|
goto redo;
|
|
case cSinh:
|
|
SetParam(0, GetParam(0).GetParam(0));
|
|
SetParam(1, CodeTree(fp_asinh(GetParam(1).GetImmed())));
|
|
goto redo;
|
|
case cTanh:
|
|
if(fabs(GetParam(1).GetImmed()) < 1.0)
|
|
{
|
|
SetParam(0, GetParam(0).GetParam(0));
|
|
SetParam(1, CodeTree(fp_atanh(GetParam(1).GetImmed())));
|
|
goto redo;
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
break;
|
|
|
|
case cAbs:
|
|
{
|
|
/* If we know the operand is always positive, cAbs is redundant.
|
|
* If we know the operand is always negative, use actual negation.
|
|
*/
|
|
MinMaxTree p0 = GetParam(0).CalculateResultBoundaries();
|
|
if(p0.has_min && p0.min >= 0.0)
|
|
goto ReplaceTreeWithParam0;
|
|
if(p0.has_max && p0.max <= NEGATIVE_MAXIMUM)
|
|
{
|
|
/* abs(negative) = negative*-1 */
|
|
SetOpcode(cMul);
|
|
AddParam( CodeTree(-1.0) );
|
|
/* The caller of ConstantFolding() will do Sort() and Rehash() next.
|
|
* Thus, no need to do it here. */
|
|
/* We were changed into a cMul group. Do cMul folding. */
|
|
goto NowWeAreMulGroup;
|
|
}
|
|
/* If the operand is a cMul group, find elements
|
|
* that are always positive and always negative,
|
|
* and move them out, e.g. abs(p*n*x*y) = p*(-n)*abs(x*y)
|
|
*/
|
|
if(GetParam(0).GetOpcode() == cMul)
|
|
{
|
|
const CodeTree& p = GetParam(0);
|
|
std::vector<CodeTree> pos_set;
|
|
std::vector<CodeTree> neg_set;
|
|
for(size_t a=0; a<p.GetParamCount(); ++a)
|
|
{
|
|
p0 = p.GetParam(a).CalculateResultBoundaries();
|
|
if(p0.has_min && p0.min >= 0.0)
|
|
{ pos_set.push_back(p.GetParam(a)); }
|
|
if(p0.has_max && p0.max <= NEGATIVE_MAXIMUM)
|
|
{ neg_set.push_back(p.GetParam(a)); }
|
|
}
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "Abs: mul group has " << pos_set.size()
|
|
<< " pos, " << neg_set.size() << "neg\n";
|
|
#endif
|
|
if(!pos_set.empty() || !neg_set.empty())
|
|
{
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "AbsReplace-Before: ";
|
|
DumpTree(*this);
|
|
std::cout << "\n" << std::flush;
|
|
DumpHashes(*this, std::cout);
|
|
#endif
|
|
CodeTree pclone;
|
|
pclone.SetOpcode(cMul);
|
|
for(size_t a=0; a<p.GetParamCount(); ++a)
|
|
{
|
|
p0 = p.GetParam(a).CalculateResultBoundaries();
|
|
if((p0.has_min && p0.min >= 0.0)
|
|
|| (p0.has_max && p0.max <= NEGATIVE_MAXIMUM))
|
|
{/*pclone.DelParam(a);*/}
|
|
else
|
|
pclone.AddParam( p.GetParam(a) );
|
|
/* Here, p*n*x*y -> x*y.
|
|
* p is saved in pos_set[]
|
|
* n is saved in neg_set[]
|
|
*/
|
|
}
|
|
pclone.Rehash();
|
|
CodeTree abs_mul;
|
|
abs_mul.SetOpcode(cAbs);
|
|
abs_mul.AddParamMove(pclone);
|
|
abs_mul.Rehash();
|
|
CodeTree mulgroup;
|
|
mulgroup.SetOpcode(cMul);
|
|
mulgroup.AddParamMove(abs_mul); // cAbs[whatever remains in p]
|
|
mulgroup.AddParamsMove(pos_set);
|
|
/* Now:
|
|
* mulgroup = p * Abs(x*y)
|
|
*/
|
|
if(!neg_set.empty())
|
|
{
|
|
if(neg_set.size() % 2)
|
|
mulgroup.AddParam( CodeTree(-1.0) );
|
|
mulgroup.AddParamsMove(neg_set);
|
|
/* Now:
|
|
* mulgroup = p * n * -1 * Abs(x*y)
|
|
*/
|
|
}
|
|
Become(mulgroup);
|
|
#ifdef DEBUG_SUBSTITUTIONS
|
|
std::cout << "AbsReplace-After: ";
|
|
DumpTree(*this, std::cout);
|
|
std::cout << "\n" << std::flush;
|
|
DumpHashes(*this, std::cout);
|
|
#endif
|
|
/* We were changed into a cMul group. Do cMul folding. */
|
|
goto NowWeAreMulGroup;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
#define HANDLE_UNARY_CONST_FUNC(funcname) \
|
|
if(GetParam(0).IsImmed()) \
|
|
{ const_value = funcname(GetParam(0).GetImmed()); \
|
|
goto ReplaceTreeWithConstValue; }
|
|
|
|
case cLog:
|
|
HANDLE_UNARY_CONST_FUNC(log);
|
|
if(GetParam(0).GetOpcode() == cPow)
|
|
{
|
|
CodeTree pow = GetParam(0);
|
|
if(pow.GetParam(0).IsAlwaysSigned(true)) // log(posi ^ y) = y*log(posi)
|
|
{
|
|
pow.CopyOnWrite();
|
|
pow.SetOpcode(cLog);
|
|
SetOpcode(cMul);
|
|
AddParamMove(pow.GetParam(1));
|
|
pow.DelParam(1);
|
|
pow.Rehash();
|
|
SetParamMove(0, pow);
|
|
goto NowWeAreMulGroup;
|
|
}
|
|
if(pow.GetParam(1).IsAlwaysParity(false)) // log(x ^ even) = even*log(abs(x))
|
|
{
|
|
pow.CopyOnWrite();
|
|
CodeTree abs;
|
|
abs.SetOpcode(cAbs);
|
|
abs.AddParamMove(pow.GetParam(0));
|
|
abs.Rehash();
|
|
pow.SetOpcode(cLog);
|
|
SetOpcode(cMul);
|
|
pow.SetParamMove(0, abs);
|
|
AddParamMove(pow.GetParam(1));
|
|
pow.DelParam(1);
|
|
pow.Rehash();
|
|
SetParamMove(0, pow);
|
|
goto NowWeAreMulGroup;
|
|
}
|
|
}
|
|
else if(GetParam(0).GetOpcode() == cAbs)
|
|
{
|
|
// log(abs(x^y)) = y*log(abs(x))
|
|
CodeTree pow = GetParam(0).GetParam(0);
|
|
if(pow.GetOpcode() == cPow)
|
|
{
|
|
pow.CopyOnWrite();
|
|
CodeTree abs;
|
|
abs.SetOpcode(cAbs);
|
|
abs.AddParamMove(pow.GetParam(0));
|
|
abs.Rehash();
|
|
pow.SetOpcode(cLog);
|
|
SetOpcode(cMul);
|
|
pow.SetParamMove(0, abs);
|
|
AddParamMove(pow.GetParam(1));
|
|
pow.DelParam(1);
|
|
pow.Rehash();
|
|
SetParamMove(0, pow);
|
|
goto NowWeAreMulGroup;
|
|
}
|
|
}
|
|
break;
|
|
case cAcosh: HANDLE_UNARY_CONST_FUNC(fp_acosh); break;
|
|
case cAsinh: HANDLE_UNARY_CONST_FUNC(fp_asinh); break;
|
|
case cAtanh: HANDLE_UNARY_CONST_FUNC(fp_atanh); break;
|
|
case cAcos: HANDLE_UNARY_CONST_FUNC(fp_acos); break;
|
|
case cAsin: HANDLE_UNARY_CONST_FUNC(fp_asin); break;
|
|
case cAtan: HANDLE_UNARY_CONST_FUNC(fp_atan); break;
|
|
case cCosh: HANDLE_UNARY_CONST_FUNC(fp_cosh); break;
|
|
case cSinh: HANDLE_UNARY_CONST_FUNC(fp_sinh); break;
|
|
case cTanh: HANDLE_UNARY_CONST_FUNC(fp_tanh); break;
|
|
case cSin: HANDLE_UNARY_CONST_FUNC(fp_sin); break;
|
|
case cCos: HANDLE_UNARY_CONST_FUNC(fp_cos); break;
|
|
case cTan: HANDLE_UNARY_CONST_FUNC(fp_tan); break;
|
|
case cCeil: HANDLE_UNARY_CONST_FUNC(fp_ceil); break;
|
|
case cTrunc: HANDLE_UNARY_CONST_FUNC(fp_trunc); break;
|
|
case cFloor: HANDLE_UNARY_CONST_FUNC(fp_floor); break;
|
|
case cCbrt: HANDLE_UNARY_CONST_FUNC(fp_cbrt); break; // converted into cPow x 0.33333
|
|
case cSqrt: HANDLE_UNARY_CONST_FUNC(fp_sqrt); break; // converted into cPow x 0.5
|
|
case cExp: HANDLE_UNARY_CONST_FUNC(fp_exp); break; // convered into cPow CONSTANT_E x
|
|
case cInt: HANDLE_UNARY_CONST_FUNC(fp_int); break;
|
|
case cLog2: HANDLE_UNARY_CONST_FUNC(fp_log2); break;
|
|
case cLog10: HANDLE_UNARY_CONST_FUNC(fp_log10); break;
|
|
|
|
case cLog2by:
|
|
if(GetParam(0).IsImmed()
|
|
&& GetParam(1).IsImmed())
|
|
{ const_value = fp_log2(GetParam(0).GetImmed()) * GetParam(1).GetImmed();
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
|
|
case cMod: /* Can more be done than this? */
|
|
if(GetParam(0).IsImmed()
|
|
&& GetParam(1).IsImmed())
|
|
{ const_value = fmod(GetParam(0).GetImmed(), GetParam(1).GetImmed());
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
|
|
case cAtan2:
|
|
{
|
|
/* Range based optimizations for (y,x):
|
|
* If y is +0 and x <= -0, +pi is returned
|
|
* If y is -0 and x <= -0, -pi is returned (assumed never happening)
|
|
* If y is +0 and x >= +0, +0 is returned
|
|
* If y is -0 and x >= +0, -0 is returned (assumed never happening)
|
|
* If x is +-0 and y < 0, -pi/2 is returned
|
|
* If x is +-0 and y > 0, +pi/2 is returned
|
|
* Otherwise, perform constant folding when available
|
|
* If we know x <> 0, convert into atan(y / x)
|
|
* TODO: Figure out whether the above step is wise
|
|
* It allows e.g. atan2(6*x, 3*y) -> atan(2*x/y)
|
|
* when we know y != 0
|
|
*/
|
|
MinMaxTree p0 = GetParam(0).CalculateResultBoundaries();
|
|
MinMaxTree p1 = GetParam(1).CalculateResultBoundaries();
|
|
if(GetParam(0).IsImmed()
|
|
&& FloatEqual(GetParam(0).GetImmed(), 0.0)) // y == 0
|
|
{
|
|
if(p1.has_max && p1.max < 0) // y == 0 && x < 0
|
|
{ const_value = CONSTANT_PI; goto ReplaceTreeWithConstValue; }
|
|
if(p1.has_min && p1.min >= 0.0) // y == 0 && x >= 0.0
|
|
{ const_value = 0.0; goto ReplaceTreeWithConstValue; }
|
|
}
|
|
if(GetParam(1).IsImmed()
|
|
&& FloatEqual(GetParam(1).GetImmed(), 0.0)) // x == 0
|
|
{
|
|
if(p0.has_max && p0.max < 0) // y < 0 && x == 0
|
|
{ const_value = -CONSTANT_PIHALF; goto ReplaceTreeWithConstValue; }
|
|
if(p0.has_min && p0.min > 0) // y > 0 && x == 0
|
|
{ const_value = CONSTANT_PIHALF; goto ReplaceTreeWithConstValue; }
|
|
}
|
|
if(GetParam(0).IsImmed()
|
|
&& GetParam(1).IsImmed())
|
|
{ const_value = atan2(GetParam(0).GetImmed(),
|
|
GetParam(1).GetImmed());
|
|
goto ReplaceTreeWithConstValue; }
|
|
if((p1.has_min && p1.min > 0.0) // p1 != 0.0
|
|
|| (p1.has_max && p1.max < NEGATIVE_MAXIMUM)) // become atan(p0 / p1)
|
|
{
|
|
CodeTree pow_tree;
|
|
pow_tree.SetOpcode(cPow);
|
|
pow_tree.AddParamMove(GetParam(1));
|
|
pow_tree.AddParam(CodeTree(-1.0));
|
|
pow_tree.Rehash();
|
|
CodeTree div_tree;
|
|
div_tree.SetOpcode(cMul);
|
|
div_tree.AddParamMove(GetParam(0));
|
|
div_tree.AddParamMove(pow_tree);
|
|
div_tree.Rehash();
|
|
SetOpcode(cAtan);
|
|
SetParamMove(0, div_tree);
|
|
DelParam(1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case cPow:
|
|
{
|
|
if(ConstantFolding_PowOperations()) goto redo;
|
|
break;
|
|
}
|
|
|
|
/* The following opcodes are processed by GenerateFrom()
|
|
* within fpoptimizer_bytecode_to_codetree.c and thus
|
|
* they will never occur in the calling context for the
|
|
* most of the parsing context. They may however occur
|
|
* at the late phase, so we deal with them.
|
|
*/
|
|
case cDiv: // converted into cPow y -1
|
|
if(GetParam(0).IsImmed()
|
|
&& GetParam(1).IsImmed()
|
|
&& GetParam(1).GetImmed() != 0.0)
|
|
{ const_value = GetParam(0).GetImmed() / GetParam(1).GetImmed();
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
case cInv: // converted into cPow y -1
|
|
if(GetParam(0).IsImmed()
|
|
&& GetParam(0).GetImmed() != 0.0)
|
|
{ const_value = 1.0 / GetParam(0).GetImmed();
|
|
goto ReplaceTreeWithConstValue; }
|
|
// Note: Could use (mulgroup)^immed optimization from cPow
|
|
break;
|
|
case cSub: // converted into cMul y -1
|
|
if(GetParam(0).IsImmed()
|
|
&& GetParam(1).IsImmed())
|
|
{ const_value = GetParam(0).GetImmed() - GetParam(1).GetImmed();
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
case cNeg: // converted into cMul x -1
|
|
if(GetParam(0).IsImmed())
|
|
{ const_value = -GetParam(0).GetImmed();
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
case cRad: // converted into cMul x CONSTANT_RD
|
|
if(GetParam(0).IsImmed())
|
|
{ const_value = GetParam(0).GetImmed() * CONSTANT_RD;
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
case cDeg: // converted into cMul x CONSTANT_DR
|
|
if(GetParam(0).IsImmed())
|
|
{ const_value = GetParam(0).GetImmed() * CONSTANT_DR;
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
case cSqr: // converted into cMul x x
|
|
if(GetParam(0).IsImmed())
|
|
{ const_value = GetParam(0).GetImmed() * GetParam(0).GetImmed();
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
case cExp2: // converted into cPow 2.0 x
|
|
HANDLE_UNARY_CONST_FUNC(fp_exp2); break;
|
|
case cRSqrt: // converted into cPow x -0.5
|
|
if(GetParam(0).IsImmed())
|
|
{ const_value = 1.0 / sqrt(GetParam(0).GetImmed());
|
|
goto ReplaceTreeWithConstValue; }
|
|
break;
|
|
case cCot: // converted into cMul (cPow (cTan x) -1)
|
|
if(GetParam(0).IsImmed())
|
|
{ double tmp = tan(GetParam(0).GetImmed());
|
|
if(tmp != 0.0)
|
|
{ const_value = 1.0 / tmp;
|
|
goto ReplaceTreeWithConstValue; } }
|
|
break;
|
|
case cSec: // converted into cMul (cPow (cCos x) -1)
|
|
if(GetParam(0).IsImmed())
|
|
{ double tmp = cos(GetParam(0).GetImmed());
|
|
if(tmp != 0.0)
|
|
{ const_value = 1.0 / tmp;
|
|
goto ReplaceTreeWithConstValue; } }
|
|
break;
|
|
case cCsc: // converted into cMul (cPow (cSin x) -1)
|
|
if(GetParam(0).IsImmed())
|
|
{ double tmp = sin(GetParam(0).GetImmed());
|
|
if(tmp != 0.0)
|
|
{ const_value = 1.0 / tmp;
|
|
goto ReplaceTreeWithConstValue; } }
|
|
break;
|
|
|
|
/* Opcodes that do not occur in the tree for other reasons */
|
|
case cRDiv: // version of cDiv
|
|
case cRSub: // version of cSub
|
|
case cDup:
|
|
case cFetch:
|
|
case cPopNMov:
|
|
case cNop:
|
|
case cJump:
|
|
break; /* Should never occur */
|
|
|
|
/* Opcodes that we can't do anything about */
|
|
case cPCall:
|
|
case cFCall:
|
|
case cEval:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_rangeestimation.c"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_consts.h"
|
|
|
|
#include <cmath> /* for CalculateResultBoundaries() */
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
using namespace FPoptimizer_CodeTree;
|
|
|
|
//#define DEBUG_SUBSTITUTIONS_extra_verbose
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
MinMaxTree CodeTree::CalculateResultBoundaries() const
|
|
#ifdef DEBUG_SUBSTITUTIONS_extra_verbose
|
|
{
|
|
MinMaxTree tmp = CalculateResultBoundaries_do();
|
|
std::cout << "Estimated boundaries: ";
|
|
if(tmp.has_min) std::cout << tmp.min; else std::cout << "-inf";
|
|
std::cout << " .. ";
|
|
if(tmp.has_max) std::cout << tmp.max; else std::cout << "+inf";
|
|
std::cout << ": ";
|
|
FPoptimizer_CodeTree::DumpTree(*this);
|
|
std::cout << std::endl;
|
|
return tmp;
|
|
}
|
|
MinMaxTree CodeTree::CalculateResultBoundaries_do() const
|
|
#endif
|
|
{
|
|
using namespace std;
|
|
switch( GetOpcode() )
|
|
{
|
|
case cImmed:
|
|
return MinMaxTree(GetImmed(), GetImmed()); // a definite value.
|
|
case cAnd:
|
|
case cAbsAnd:
|
|
case cOr:
|
|
case cAbsOr:
|
|
case cNot:
|
|
case cAbsNot:
|
|
case cNotNot:
|
|
case cAbsNotNot:
|
|
case cEqual:
|
|
case cNEqual:
|
|
case cLess:
|
|
case cLessOrEq:
|
|
case cGreater:
|
|
case cGreaterOrEq:
|
|
{
|
|
/* These operations always produce truth values (0 or 1) */
|
|
/* Narrowing them down is a matter of performing Constant optimization */
|
|
return MinMaxTree( 0.0, 1.0 );
|
|
}
|
|
case cAbs:
|
|
{
|
|
/* cAbs always produces a positive value */
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min && m.has_max)
|
|
{
|
|
if(m.min < 0.0 && m.max >= 0.0) // ex. -10..+6 or -6..+10
|
|
{
|
|
/* -x..+y: spans across zero. min=0, max=greater of |x| and |y|. */
|
|
double tmp = -m.min; if(tmp > m.max) m.max = tmp;
|
|
m.min = 0.0; m.has_min = true;
|
|
}
|
|
else if(m.min < 0.0) // ex. -10..-4
|
|
{ double tmp = m.max; m.max = -m.min; m.min = -tmp; }
|
|
}
|
|
else if(!m.has_min && m.has_max && m.max < 0.0) // ex. -inf..-10
|
|
{
|
|
m.min = fabs(m.max); m.has_min = true; m.has_max = false;
|
|
}
|
|
else if(!m.has_max && m.has_min && m.min > 0.0) // ex. +10..+inf
|
|
{
|
|
m.min = fabs(m.min); m.has_min = true; m.has_max = false;
|
|
}
|
|
else // ex. -inf..+inf, -inf..+10, -10..+inf
|
|
{
|
|
// all of these cover -inf..0, 0..+inf, or both
|
|
m.min = 0.0; m.has_min = true; m.has_max = false;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
case cLog: /* Defined for 0.0 < x <= inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) { if(m.min < 0.0) m.has_min = false; else m.min = log(m.min); } // No boundaries
|
|
if(m.has_max) { if(m.max < 0.0) m.has_max = false; else m.max = log(m.max); }
|
|
return m;
|
|
}
|
|
|
|
case cLog2: /* Defined for 0.0 < x <= inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) { if(m.min < 0.0) m.has_min = false; else m.min = fp_log2(m.min); } // No boundaries
|
|
if(m.has_max) { if(m.max < 0.0) m.has_max = false; else m.max = fp_log2(m.max); }
|
|
return m;
|
|
}
|
|
|
|
case cLog10: /* Defined for 0.0 < x <= inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) { if(m.min < 0.0) m.has_min = false; else m.min = fp_log10(m.min); }
|
|
if(m.has_max) { if(m.max < 0.0) m.has_max = false; else m.max = fp_log10(m.max); }
|
|
return m;
|
|
}
|
|
|
|
case cAcosh: /* defined for 1.0 < x <= inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) { if(m.min <= 1.0) m.has_min = false; else m.min = fp_acosh(m.min); } // No boundaries
|
|
if(m.has_max) { if(m.max <= 1.0) m.has_max = false; else m.max = fp_acosh(m.max); }
|
|
return m;
|
|
}
|
|
case cAsinh: /* defined for all values -inf <= x <= inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) m.min = fp_asinh(m.min); // No boundaries
|
|
if(m.has_max) m.max = fp_asinh(m.max);
|
|
return m;
|
|
}
|
|
case cAtanh: /* defined for all values -inf <= x <= inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) m.min = fp_atanh(m.min); // No boundaries
|
|
if(m.has_max) m.max = fp_atanh(m.max);
|
|
return m;
|
|
}
|
|
case cAcos: /* defined for -1.0 <= x < 1, results within CONSTANT_PI..0 */
|
|
{
|
|
/* Somewhat complicated to narrow down from this */
|
|
/* TODO: A resourceful programmer may add it later. */
|
|
return MinMaxTree( 0.0, CONSTANT_PI );
|
|
}
|
|
case cAsin: /* defined for -1.0 <= x < 1, results within -CONSTANT_PIHALF..CONSTANT_PIHALF */
|
|
{
|
|
/* Somewhat complicated to narrow down from this */
|
|
/* TODO: A resourceful programmer may add it later. */
|
|
return MinMaxTree( -CONSTANT_PIHALF, CONSTANT_PIHALF );
|
|
}
|
|
case cAtan: /* defined for all values -inf <= x <= inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) m.min = atan(m.min); else { m.min = -CONSTANT_PIHALF; m.has_min = true; }
|
|
if(m.has_max) m.max = atan(m.max); else { m.max = CONSTANT_PIHALF; m.has_max = true; }
|
|
return m;
|
|
}
|
|
case cAtan2: /* too complicated to estimate */
|
|
{
|
|
MinMaxTree p0 = GetParam(0).CalculateResultBoundaries();
|
|
MinMaxTree p1 = GetParam(1).CalculateResultBoundaries();
|
|
if(GetParam(0).IsImmed()
|
|
&& FloatEqual(GetParam(0).GetImmed(), 0.0)) // y == 0
|
|
{
|
|
// Either 0.0 or CONSTANT_PI
|
|
return MinMaxTree(0.0, CONSTANT_PI);
|
|
}
|
|
if(GetParam(1).IsImmed()
|
|
&& FloatEqual(GetParam(1).GetImmed(), 0.0)) // x == 0
|
|
{
|
|
// EIther -CONSTANT_PIHALF or +CONSTANT_PIHALF
|
|
return MinMaxTree(-CONSTANT_PIHALF, CONSTANT_PIHALF);
|
|
}
|
|
// Anything else
|
|
/* Somewhat complicated to narrow down from this */
|
|
/* TODO: A resourceful programmer may add it later. */
|
|
return MinMaxTree(-CONSTANT_PI, CONSTANT_PI);
|
|
}
|
|
|
|
case cSin:
|
|
case cCos:
|
|
{
|
|
/* Could be narrowed down from here,
|
|
* but it's too complicated due to
|
|
* the cyclic nature of the function. */
|
|
/* TODO: A resourceful programmer may add it later. */
|
|
return MinMaxTree(-1.0, 1.0);
|
|
}
|
|
case cTan:
|
|
{
|
|
/* Could be narrowed down from here,
|
|
* but it's too complicated due to
|
|
* the cyclic nature of the function */
|
|
/* TODO: A resourceful programmer may add it later. */
|
|
return MinMaxTree(); // (CONSTANT_NEG_INF, CONSTANT_POS_INF);
|
|
}
|
|
|
|
case cCeil:
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
m.max = std::ceil(m.max); // ceil() may increase the value, may not decrease
|
|
return m;
|
|
}
|
|
case cFloor:
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
m.min = std::floor(m.min); // floor() may decrease the value, may not increase
|
|
return m;
|
|
}
|
|
case cTrunc:
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
m.min = std::floor(m.min); // trunc() may either increase or decrease the value
|
|
m.max = std::ceil(m.max); // for safety, we assume both
|
|
return m;
|
|
}
|
|
case cInt:
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
m.min = std::floor(m.min); // int() may either increase or decrease the value
|
|
m.max = std::ceil(m.max); // for safety, we assume both
|
|
return m;
|
|
}
|
|
case cSinh: /* defined for all values -inf <= x <= inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) m.min = sinh(m.min); // No boundaries
|
|
if(m.has_max) m.max = sinh(m.max);
|
|
return m;
|
|
}
|
|
case cTanh: /* defined for all values -inf <= x <= inf, results within -1..1 */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min) m.min = tanh(m.min); else { m.has_min = true; m.min =-1.0; }
|
|
if(m.has_max) m.max = tanh(m.max); else { m.has_max = true; m.max = 1.0; }
|
|
return m;
|
|
}
|
|
case cCosh: /* defined for all values -inf <= x <= inf, results within 1..inf */
|
|
{
|
|
MinMaxTree m = GetParam(0).CalculateResultBoundaries();
|
|
if(m.has_min)
|
|
{
|
|
if(m.has_max) // max, min
|
|
{
|
|
if(m.min >= 0.0 && m.max >= 0.0) // +x .. +y
|
|
{ m.min = cosh(m.min); m.max = cosh(m.max); }
|
|
else if(m.min < 0.0 && m.max >= 0.0) // -x .. +y
|
|
{ double tmp = cosh(m.min); m.max = cosh(m.max);
|
|
if(tmp > m.max) m.max = tmp;
|
|
m.min = 1.0; }
|
|
else // -x .. -y
|
|
{ m.min = cosh(m.min); m.max = cosh(m.max);
|
|
std::swap(m.min, m.max); }
|
|
}
|
|
else // min, no max
|
|
{
|
|
if(m.min >= 0.0) // 0..inf -> 1..inf
|
|
{ m.has_max = false; m.min = cosh(m.min); }
|
|
else
|
|
{ m.has_max = false; m.min = 1.0; } // Anything between 1..inf
|
|
}
|
|
}
|
|
else // no min
|
|
{
|
|
m.has_min = true; m.min = 1.0; // always a lower boundary
|
|
if(m.has_max) // max, no min
|
|
{
|
|
m.min = cosh(m.max); // n..inf
|
|
m.has_max = false; // No upper boundary
|
|
}
|
|
else // no max, no min
|
|
m.has_max = false; // No upper boundary
|
|
}
|
|
return m;
|
|
}
|
|
|
|
case cIf:
|
|
case cAbsIf:
|
|
{
|
|
// No guess which branch is chosen. Produce a spanning min & max.
|
|
MinMaxTree res1 = GetParam(1).CalculateResultBoundaries();
|
|
MinMaxTree res2 = GetParam(2).CalculateResultBoundaries();
|
|
if(!res2.has_min) res1.has_min = false; else if(res1.has_min && res2.min < res1.min) res1.min = res2.min;
|
|
if(!res2.has_max) res1.has_max = false; else if(res1.has_max && res2.max > res1.max) res1.max = res2.max;
|
|
return res1;
|
|
}
|
|
|
|
case cMin:
|
|
{
|
|
bool has_unknown_min = false;
|
|
bool has_unknown_max = false;
|
|
|
|
MinMaxTree result;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
MinMaxTree m = GetParam(a).CalculateResultBoundaries();
|
|
if(!m.has_min)
|
|
has_unknown_min = true;
|
|
else if(!result.has_min || m.min < result.min)
|
|
result.min = m.min;
|
|
|
|
if(!m.has_max)
|
|
has_unknown_max = true;
|
|
else if(!result.has_max || m.max < result.max)
|
|
result.max = m.max;
|
|
}
|
|
if(has_unknown_min) result.has_min = false;
|
|
if(has_unknown_max) result.has_max = false;
|
|
return result;
|
|
}
|
|
case cMax:
|
|
{
|
|
bool has_unknown_min = false;
|
|
bool has_unknown_max = false;
|
|
|
|
MinMaxTree result;
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
MinMaxTree m = GetParam(a).CalculateResultBoundaries();
|
|
if(!m.has_min)
|
|
has_unknown_min = true;
|
|
else if(!result.has_min || m.min > result.min)
|
|
result.min = m.min;
|
|
|
|
if(!m.has_max)
|
|
has_unknown_max = true;
|
|
else if(!result.has_max || m.max > result.max)
|
|
result.max = m.max;
|
|
}
|
|
if(has_unknown_min) result.has_min = false;
|
|
if(has_unknown_max) result.has_max = false;
|
|
return result;
|
|
}
|
|
case cAdd:
|
|
{
|
|
/* It's complicated. Follow the logic below. */
|
|
/* Note: This also deals with the following opcodes:
|
|
* cNeg, cSub, cRSub
|
|
*/
|
|
MinMaxTree result(0.0, 0.0);
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
MinMaxTree item = GetParam(a).CalculateResultBoundaries();
|
|
|
|
if(item.has_min) result.min += item.min;
|
|
else result.has_min = false;
|
|
if(item.has_max) result.max += item.max;
|
|
else result.has_max = false;
|
|
|
|
if(!result.has_min && !result.has_max) break; // hopeless
|
|
}
|
|
if(result.has_min && result.has_max
|
|
&& result.min > result.max) std::swap(result.min, result.max);
|
|
return result;
|
|
}
|
|
case cMul:
|
|
{
|
|
/* It's very complicated. Follow the logic below. */
|
|
struct Value
|
|
{
|
|
enum ValueType { Finite, MinusInf, PlusInf };
|
|
ValueType valueType;
|
|
double value;
|
|
|
|
Value(ValueType t): valueType(t), value(0) {}
|
|
Value(double v): valueType(Finite), value(v) {}
|
|
|
|
bool isNegative() const
|
|
{
|
|
return valueType == MinusInf ||
|
|
(valueType == Finite && value < 0.0);
|
|
}
|
|
|
|
void operator*=(const Value& rhs)
|
|
{
|
|
if(valueType == Finite && rhs.valueType == Finite)
|
|
value *= rhs.value;
|
|
else
|
|
valueType = (isNegative() != rhs.isNegative() ?
|
|
MinusInf : PlusInf);
|
|
}
|
|
|
|
bool operator<(const Value& rhs) const
|
|
{
|
|
return
|
|
(valueType == MinusInf && rhs.valueType != MinusInf) ||
|
|
(valueType == Finite &&
|
|
(rhs.valueType == PlusInf ||
|
|
(rhs.valueType == Finite && value < rhs.value)));
|
|
}
|
|
};
|
|
|
|
struct MultiplicationRange
|
|
{
|
|
Value minValue, maxValue;
|
|
|
|
MultiplicationRange():
|
|
minValue(Value::PlusInf),
|
|
maxValue(Value::MinusInf) {}
|
|
|
|
void multiply(Value value1, const Value& value2)
|
|
{
|
|
value1 *= value2;
|
|
if(value1 < minValue) minValue = value1;
|
|
if(maxValue < value1) maxValue = value1;
|
|
}
|
|
};
|
|
|
|
MinMaxTree result(1.0, 1.0);
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
{
|
|
MinMaxTree item = GetParam(a).CalculateResultBoundaries();
|
|
if(!item.has_min && !item.has_max) return MinMaxTree(); // hopeless
|
|
|
|
Value minValue0 = result.has_min ? Value(result.min) : Value(Value::MinusInf);
|
|
Value maxValue0 = result.has_max ? Value(result.max) : Value(Value::PlusInf);
|
|
Value minValue1 = item.has_min ? Value(item.min) : Value(Value::MinusInf);
|
|
Value maxValue1 = item.has_max ? Value(item.max) : Value(Value::PlusInf);
|
|
|
|
MultiplicationRange range;
|
|
range.multiply(minValue0, minValue1);
|
|
range.multiply(minValue0, maxValue1);
|
|
range.multiply(maxValue0, minValue1);
|
|
range.multiply(maxValue0, maxValue1);
|
|
|
|
if(range.minValue.valueType == Value::Finite)
|
|
result.min = range.minValue.value;
|
|
else result.has_min = false;
|
|
|
|
if(range.maxValue.valueType == Value::Finite)
|
|
result.max = range.maxValue.value;
|
|
else result.has_max = false;
|
|
|
|
if(!result.has_min && !result.has_max) break; // hopeless
|
|
}
|
|
if(result.has_min && result.has_max
|
|
&& result.min > result.max) std::swap(result.min, result.max);
|
|
return result;
|
|
}
|
|
case cMod:
|
|
{
|
|
/* TODO: The boundaries of modulo operator could be estimated better. */
|
|
|
|
MinMaxTree x = GetParam(0).CalculateResultBoundaries();
|
|
MinMaxTree y = GetParam(1).CalculateResultBoundaries();
|
|
|
|
if(y.has_max)
|
|
{
|
|
if(y.max >= 0.0)
|
|
{
|
|
if(!x.has_min || x.min < 0)
|
|
return MinMaxTree(-y.max, y.max);
|
|
else
|
|
return MinMaxTree(0.0, y.max);
|
|
}
|
|
else
|
|
{
|
|
if(!x.has_max || x.max >= 0)
|
|
return MinMaxTree(y.max, -y.max);
|
|
else
|
|
return MinMaxTree(y.max, NEGATIVE_MAXIMUM);
|
|
}
|
|
}
|
|
else
|
|
return MinMaxTree();
|
|
}
|
|
case cPow:
|
|
{
|
|
if(GetParam(1).IsImmed() && GetParam(1).GetImmed() == 0.0)
|
|
{
|
|
// Note: This makes 0^0 evaluate into 1.
|
|
return MinMaxTree(1.0, 1.0); // x^0 = 1
|
|
}
|
|
if(GetParam(0).IsImmed() && GetParam(0).GetImmed() == 0.0)
|
|
{
|
|
// Note: This makes 0^0 evaluate into 0.
|
|
return MinMaxTree(0.0, 0.0); // 0^x = 0
|
|
}
|
|
if(GetParam(0).IsImmed() && FloatEqual(GetParam(0).GetImmed(), 1.0))
|
|
{
|
|
return MinMaxTree(1.0, 1.0); // 1^x = 1
|
|
}
|
|
if(GetParam(1).IsImmed()
|
|
&& GetParam(1).GetImmed() > 0
|
|
&& GetParam(1).IsAlwaysParity(false))
|
|
{
|
|
// x ^ even_int_const always produces a non-negative value.
|
|
double exponent = GetParam(1).GetImmed();
|
|
MinMaxTree tmp = GetParam(0).CalculateResultBoundaries();
|
|
MinMaxTree result;
|
|
result.has_min = true;
|
|
result.min = 0;
|
|
if(tmp.has_min && tmp.min >= 0)
|
|
result.min = fp_pow(tmp.min, exponent);
|
|
else if(tmp.has_max && tmp.max <= 0)
|
|
result.min = fp_pow(tmp.max, exponent);
|
|
|
|
result.has_max = false;
|
|
if(tmp.has_min && tmp.has_max)
|
|
{
|
|
result.has_max = true;
|
|
result.max = std::max(fabs(tmp.min), fabs(tmp.max));
|
|
result.max = fp_pow(result.max, exponent);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
MinMaxTree p0 = GetParam(0).CalculateResultBoundaries();
|
|
MinMaxTree p1 = GetParam(1).CalculateResultBoundaries();
|
|
TriTruthValue p0_positivity =
|
|
(p0.has_min && p0.min >= 0.0) ? IsAlways
|
|
: (p0.has_max && p0.max < 0.0 ? IsNever
|
|
: Unknown);
|
|
TriTruthValue p1_evenness = GetParam(1).GetEvennessInfo();
|
|
|
|
/* If param0 IsAlways, the return value is also IsAlways */
|
|
/* If param1 is even, the return value is IsAlways */
|
|
/* If param1 is odd, the return value is same as param0's */
|
|
/* If param0 is negative and param1 is not integer,
|
|
* the return value is imaginary (assumed Unknown)
|
|
*
|
|
* Illustrated in this truth table:
|
|
* P=positive, N=negative
|
|
* E=even, O=odd, U=not integer
|
|
* *=unknown, X=invalid (unknown), x=maybe invalid (unknown)
|
|
*
|
|
* param1: PE PO P* NE NO N* PU NU *
|
|
* param0:
|
|
* PE P P P P P P P P P
|
|
* PO P P P P P P P P P
|
|
* PU P P P P P P P P P
|
|
* P* P P P P P P P P P
|
|
* NE P N * P N * X X x
|
|
* NO P N * P N * X X x
|
|
* NU P N * P N * X X x
|
|
* N* P N * P N * X X x
|
|
* * P * * P * * x x *
|
|
*
|
|
* Note: This also deals with the following opcodes:
|
|
* cSqrt (param0, PU) (x^0.5)
|
|
* cRSqrt (param0, NU) (x^-0.5)
|
|
* cExp (PU, param1) (CONSTANT_E^x)
|
|
*/
|
|
TriTruthValue result_positivity = Unknown;
|
|
switch(p0_positivity)
|
|
{
|
|
case IsAlways:
|
|
// e.g. 5^x = positive.
|
|
result_positivity = IsAlways;
|
|
break;
|
|
case IsNever:
|
|
{
|
|
result_positivity = p1_evenness;
|
|
break;
|
|
}
|
|
default:
|
|
switch(p1_evenness)
|
|
{
|
|
case IsAlways:
|
|
// e.g. x^( 4) = positive
|
|
// e.g. x^(-4) = positive
|
|
result_positivity = IsAlways;
|
|
break;
|
|
case IsNever:
|
|
break;
|
|
case Unknown:
|
|
{
|
|
/* If p1 is const non-integer,
|
|
* assume the result is positive
|
|
* though it may be NaN instead.
|
|
*/
|
|
if(GetParam(1).IsImmed()
|
|
&& GetParam(1).IsAlwaysInteger(false)
|
|
&& GetParam(1).GetImmed() >= 0.0)
|
|
{
|
|
result_positivity = IsAlways;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
switch(result_positivity)
|
|
{
|
|
case IsAlways:
|
|
{
|
|
/* The result is always positive.
|
|
* Figure out whether we know the minimum value. */
|
|
double min = 0.0;
|
|
if(p0.has_min && p1.has_min)
|
|
{
|
|
min = pow(p0.min, p1.min);
|
|
if(p0.min < 0.0 && (!p1.has_max || p1.max >= 0.0) && min >= 0.0)
|
|
min = 0.0;
|
|
}
|
|
if(p0.has_min && p0.min >= 0.0 && p0.has_max && p1.has_max)
|
|
{
|
|
double max = pow(p0.max, p1.max);
|
|
if(min > max) std::swap(min, max);
|
|
return MinMaxTree(min, max);
|
|
}
|
|
return MinMaxTree(min, false);
|
|
}
|
|
case IsNever:
|
|
{
|
|
/* The result is always negative.
|
|
* TODO: Figure out whether we know the maximum value.
|
|
*/
|
|
return MinMaxTree(false, NEGATIVE_MAXIMUM);
|
|
}
|
|
default:
|
|
{
|
|
/* It can be negative or positive.
|
|
* We know nothing about the boundaries. */
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* The following opcodes are processed by GenerateFrom()
|
|
* within fpoptimizer_bytecode_to_codetree.c and thus
|
|
* they will never occur in the calling context for the
|
|
* most of the parsing context. They may however occur
|
|
* at the late phase, so we deal with them.
|
|
*/
|
|
case cNeg:
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cMul);
|
|
tmp.AddParam(CodeTree(-1.0));
|
|
tmp.AddParam(GetParam(0));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cSub: // converted into cMul y -1
|
|
{
|
|
CodeTree tmp, tmp2;
|
|
tmp2.SetOpcode(cNeg);
|
|
tmp2.AddParam(GetParam(1));
|
|
tmp.SetOpcode(cAdd);
|
|
tmp.AddParam(GetParam(0));
|
|
tmp.AddParamMove(tmp2);
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cInv: // converted into cPow x -1
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cPow);
|
|
tmp.AddParam(GetParam(0));
|
|
tmp.AddParam(CodeTree(-1.0));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cDiv: // converted into cPow y -1
|
|
{
|
|
CodeTree tmp, tmp2;
|
|
tmp2.SetOpcode(cInv);
|
|
tmp2.AddParam(GetParam(1));
|
|
tmp.SetOpcode(cMul);
|
|
tmp.AddParam(GetParam(0));
|
|
tmp.AddParamMove(tmp2);
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cRad: // converted into cMul x CONSTANT_RD
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cMul);
|
|
tmp.AddParam(GetParam(0));
|
|
tmp.AddParam(CodeTree(CONSTANT_RD));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cDeg: // converted into cMul x CONSTANT_DR
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cMul);
|
|
tmp.AddParam(GetParam(0));
|
|
tmp.AddParam(CodeTree(CONSTANT_DR));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cSqr: // converted into cMul x x or cPow x 2
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cPow);
|
|
tmp.AddParam(GetParam(0));
|
|
tmp.AddParam(CodeTree(2.0));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cExp: // converted into cPow CONSTANT_E x
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cPow);
|
|
tmp.AddParam(CodeTree(CONSTANT_E));
|
|
tmp.AddParam(GetParam(0));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cExp2: // converted into cPow 2 x
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cPow);
|
|
tmp.AddParam(CodeTree(2.0));
|
|
tmp.AddParam(GetParam(0));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cCbrt: // converted into cPow x 0.33333333
|
|
{
|
|
// However, contrary to x^(1/3), this allows
|
|
// negative values for x, and produces those
|
|
// as well.
|
|
MinMaxTree result = GetParam(0).CalculateResultBoundaries();
|
|
if(result.has_min) result.min = fp_cbrt(result.min);
|
|
if(result.has_max) result.max = fp_cbrt(result.max);
|
|
return result;
|
|
}
|
|
case cSqrt: // converted into cPow x 0.5
|
|
{
|
|
MinMaxTree result = GetParam(0).CalculateResultBoundaries();
|
|
if(result.has_min) result.min = result.min < 0 ? 0 : fp_sqrt(result.min);
|
|
if(result.has_max) result.max = result.max < 0 ? 0 : fp_sqrt(result.max);
|
|
return result;
|
|
}
|
|
case cRSqrt: // converted into cPow x -0.5
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cPow);
|
|
tmp.AddParam(GetParam(0));
|
|
tmp.AddParam(CodeTree(-0.5));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cLog2by: // converted into cMul y CONSTANT_L2I (cLog x)
|
|
{
|
|
CodeTree tmp, tmp2;
|
|
tmp2.SetOpcode(cLog2);
|
|
tmp2.AddParam(GetParam(0));
|
|
tmp.SetOpcode(cMul);
|
|
tmp.AddParamMove(tmp2);
|
|
tmp.AddParam(GetParam(1));
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cCot: // converted into 1 / cTan
|
|
{
|
|
CodeTree tmp, tmp2;
|
|
tmp2.SetOpcode(cTan);
|
|
tmp2.AddParam(GetParam(0));
|
|
tmp.SetOpcode(cInv);
|
|
tmp.AddParamMove(tmp2);
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cSec: // converted into 1 / cCos
|
|
{
|
|
CodeTree tmp, tmp2;
|
|
tmp2.SetOpcode(cCos);
|
|
tmp2.AddParam(GetParam(0));
|
|
tmp.SetOpcode(cInv);
|
|
tmp.AddParamMove(tmp2);
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
case cCsc: // converted into 1 / cSin
|
|
{
|
|
CodeTree tmp, tmp2;
|
|
tmp2.SetOpcode(cSin);
|
|
tmp2.AddParam(GetParam(0));
|
|
tmp.SetOpcode(cInv);
|
|
tmp.AddParamMove(tmp2);
|
|
return tmp.CalculateResultBoundaries();
|
|
}
|
|
/* The following opcodes are processed by GenerateFrom()
|
|
* within fpoptimizer_bytecode_to_codetree.c and thus
|
|
* they will never occur in the calling context:
|
|
*/
|
|
break; /* Should never occur */
|
|
|
|
/* Opcodes that do not occur in the tree for other reasons */
|
|
case cRDiv: // version of cDiv
|
|
case cRSub: // version of cSub
|
|
case cDup:
|
|
case cFetch:
|
|
case cPopNMov:
|
|
case cNop:
|
|
case cJump:
|
|
case VarBegin:
|
|
break; /* Should never occur */
|
|
|
|
/* Opcodes that are completely unpredictable */
|
|
case cPCall:
|
|
break;
|
|
case cFCall:
|
|
break; // Cannot deduce
|
|
case cEval:
|
|
break; // Cannot deduce
|
|
}
|
|
return MinMaxTree(); /* Cannot deduce */
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_transformations.c"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_bytecodesynth.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_consts.h"
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
//using namespace FPoptimizer_Grammar;
|
|
|
|
//#define DEBUG_POWI
|
|
//#define DEBUG_SUBSTITUTIONS_CSE
|
|
|
|
#if defined(__x86_64) || !defined(FP_SUPPORT_CBRT)
|
|
# define CBRT_IS_SLOW
|
|
#endif
|
|
|
|
namespace FPoptimizer_ByteCode
|
|
{
|
|
extern const unsigned char powi_table[256];
|
|
}
|
|
namespace
|
|
{
|
|
using namespace FPoptimizer_CodeTree;
|
|
|
|
typedef
|
|
std::multimap<fphash_t, std::pair<size_t, CodeTree> >
|
|
TreeCountType;
|
|
|
|
void FindTreeCounts(TreeCountType& TreeCounts, const CodeTree& tree)
|
|
{
|
|
TreeCountType::iterator i = TreeCounts.lower_bound(tree.GetHash());
|
|
bool found = false;
|
|
for(; i != TreeCounts.end() && i->first == tree.GetHash(); ++i)
|
|
{
|
|
if(tree.IsIdenticalTo( i->second.second ) )
|
|
{
|
|
i->second.first += 1;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!found)
|
|
{
|
|
TreeCounts.insert(i, std::make_pair(tree.GetHash(), std::make_pair(size_t(1), tree)));
|
|
}
|
|
|
|
for(size_t a=0; a<tree.GetParamCount(); ++a)
|
|
FindTreeCounts(TreeCounts, tree.GetParam(a));
|
|
}
|
|
|
|
struct BalanceResultType
|
|
{
|
|
bool BalanceGood;
|
|
bool FoundChild;
|
|
};
|
|
BalanceResultType IfBalanceGood(const CodeTree& root, const CodeTree& child)
|
|
{
|
|
if(root.IsIdenticalTo(child))
|
|
{
|
|
BalanceResultType result = {true,true};
|
|
return result;
|
|
}
|
|
|
|
BalanceResultType result = {true,false};
|
|
|
|
if(root.GetOpcode() == cIf
|
|
|| root.GetOpcode() == cAbsIf)
|
|
{
|
|
BalanceResultType cond = IfBalanceGood(root.GetParam(0), child);
|
|
BalanceResultType branch1 = IfBalanceGood(root.GetParam(1), child);
|
|
BalanceResultType branch2 = IfBalanceGood(root.GetParam(2), child);
|
|
|
|
if(cond.FoundChild || branch1.FoundChild || branch2.FoundChild)
|
|
{ result.FoundChild = true; }
|
|
|
|
// balance is good if:
|
|
// branch1.found = branch2.found OR (cond.found AND cond.goodbalance)
|
|
// AND cond.goodbalance OR (branch1.found AND branch2.found)
|
|
// AND branch1.goodbalance OR (cond.found AND cond.goodbalance)
|
|
// AND branch2.goodbalance OR (cond.found AND cond.goodbalance)
|
|
|
|
result.BalanceGood =
|
|
( (branch1.FoundChild == branch2.FoundChild)
|
|
|| (cond.FoundChild && cond.BalanceGood) )
|
|
&& (cond.BalanceGood || (branch1.FoundChild && branch2.FoundChild))
|
|
&& (branch1.BalanceGood || (cond.FoundChild && cond.BalanceGood))
|
|
&& (branch2.BalanceGood || (cond.FoundChild && cond.BalanceGood));
|
|
}
|
|
else
|
|
{
|
|
bool has_bad_balance = false;
|
|
bool has_good_balance_found = false;
|
|
|
|
// Balance is bad if one of the children has bad balance
|
|
// Unless one of the children has good balance & found
|
|
|
|
for(size_t b=root.GetParamCount(), a=0; a<b; ++a)
|
|
{
|
|
BalanceResultType tmp = IfBalanceGood(root.GetParam(a), child);
|
|
if(tmp.FoundChild)
|
|
result.FoundChild = true;
|
|
|
|
if(tmp.BalanceGood == false)
|
|
has_bad_balance = true;
|
|
else if(tmp.FoundChild)
|
|
has_good_balance_found = true;
|
|
|
|
// if the expression is
|
|
// if(x, sin(x), 0) + sin(x)
|
|
// then sin(x) is a good subexpression
|
|
// even though it occurs in unbalance.
|
|
}
|
|
if(has_bad_balance && !has_good_balance_found)
|
|
result.BalanceGood = false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool IsOptimizableUsingPowi(long immed, long penalty = 0)
|
|
{
|
|
FPoptimizer_ByteCode::ByteCodeSynth synth;
|
|
synth.PushVar(0);
|
|
// Ignore the size generated by subtree
|
|
size_t bytecodesize_backup = synth.GetByteCodeSize();
|
|
FPoptimizer_ByteCode::AssembleSequence(immed, FPoptimizer_ByteCode::MulSequence, synth);
|
|
|
|
size_t bytecode_grow_amount = synth.GetByteCodeSize() - bytecodesize_backup;
|
|
|
|
return bytecode_grow_amount < size_t(MAX_POWI_BYTECODE_LENGTH - penalty);
|
|
}
|
|
|
|
void ChangeIntoRootChain(
|
|
CodeTree& tree,
|
|
bool inverted,
|
|
long sqrt_count,
|
|
long cbrt_count)
|
|
{
|
|
while(cbrt_count > 0)
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cCbrt);
|
|
tmp.AddParamMove(tree);
|
|
tmp.Rehash();
|
|
tree.swap(tmp);
|
|
--cbrt_count;
|
|
}
|
|
while(sqrt_count > 0)
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cSqrt);
|
|
if(inverted)
|
|
{
|
|
tmp.SetOpcode(cRSqrt);
|
|
inverted = false;
|
|
}
|
|
tmp.AddParamMove(tree);
|
|
tmp.Rehash();
|
|
tree.swap(tmp);
|
|
--sqrt_count;
|
|
}
|
|
if(inverted)
|
|
{
|
|
CodeTree tmp;
|
|
tmp.SetOpcode(cInv);
|
|
tmp.AddParamMove(tree);
|
|
tree.swap(tmp);
|
|
}
|
|
}
|
|
|
|
double CalculatePowiFactorCost(long abs_int_exponent)
|
|
{
|
|
static std::map<long, double> cache;
|
|
std::map<long,double>::iterator i = cache.lower_bound(abs_int_exponent);
|
|
if(i != cache.end() && i->first == abs_int_exponent)
|
|
return i->second;
|
|
std::pair<long, double> result(abs_int_exponent, 0.0);
|
|
double& cost = result.second;
|
|
|
|
while(abs_int_exponent > 1)
|
|
{
|
|
int factor = 0;
|
|
if(abs_int_exponent < 256)
|
|
{
|
|
factor = FPoptimizer_ByteCode::powi_table[abs_int_exponent];
|
|
if(factor & 128) factor &= 127; else factor = 0;
|
|
if(factor & 64) factor = -(factor&63) - 1;
|
|
}
|
|
if(factor)
|
|
{
|
|
cost += CalculatePowiFactorCost(factor);
|
|
abs_int_exponent /= factor;
|
|
continue;
|
|
}
|
|
if(!(abs_int_exponent & 1))
|
|
{
|
|
abs_int_exponent /= 2;
|
|
cost += 3; // sqr
|
|
}
|
|
else
|
|
{
|
|
cost += 3.5; // dup+mul
|
|
abs_int_exponent -= 1;
|
|
}
|
|
}
|
|
|
|
cache.insert(i, result);
|
|
return cost;
|
|
}
|
|
|
|
struct PowiResolver
|
|
{
|
|
/* Any exponentiation can be turned into one of these:
|
|
*
|
|
* x^y -> sqrt(x)^(y*2) = x Sqrt y*2 Pow
|
|
* x^y -> cbrt(x)^(y*3) = x Cbrt y*3 Pow
|
|
* x^y -> rsqrt(x)^(y*-2) = x RSqrt y*-2 Pow
|
|
* x^y -> x^(y-1/2) * sqrt(x) = x Sqrt x y-0.5 Pow Mul
|
|
* x^y -> x^(y-1/3) * cbrt(x) = x Cbrt x y-0.33 Pow Mul
|
|
* x^y -> x^(y+1/2) * rsqrt(x) = x Sqrt x y+0.5 Pow Mul
|
|
* x^y -> inv(x)^(-y) = x Inv -y Pow
|
|
*
|
|
* These rules can be applied recursively.
|
|
* The goal is to find the optimal chain of operations
|
|
* that results in the least number of sqrt,cbrt operations;
|
|
* an integer value of y, and that the integer is as close
|
|
* to zero as possible.
|
|
*/
|
|
static const unsigned MaxSep = 4;
|
|
|
|
struct PowiResult
|
|
{
|
|
PowiResult() :
|
|
n_int_sqrt(0),
|
|
n_int_cbrt(0),
|
|
resulting_exponent(0),
|
|
sep_list() { }
|
|
|
|
int n_int_sqrt;
|
|
int n_int_cbrt;
|
|
long resulting_exponent;
|
|
int sep_list[MaxSep];
|
|
};
|
|
|
|
PowiResult CreatePowiResult(double exponent) const
|
|
{
|
|
static const double RootPowers[(1+4)*(1+3)] =
|
|
{
|
|
// (sqrt^n(x))
|
|
1.0,
|
|
1.0 / (2),
|
|
1.0 / (2*2),
|
|
1.0 / (2*2*2),
|
|
1.0 / (2*2*2*2),
|
|
// cbrt^1(sqrt^n(x))
|
|
1.0 / (3),
|
|
1.0 / (3*2),
|
|
1.0 / (3*2*2),
|
|
1.0 / (3*2*2*2),
|
|
1.0 / (3*2*2*2*2),
|
|
// cbrt^2(sqrt^n(x))
|
|
1.0 / (3*3),
|
|
1.0 / (3*3*2),
|
|
1.0 / (3*3*2*2),
|
|
1.0 / (3*3*2*2*2),
|
|
1.0 / (3*3*2*2*2*2),
|
|
// cbrt^3(sqrt^n(x))
|
|
1.0 / (3*3*3),
|
|
1.0 / (3*3*3*2),
|
|
1.0 / (3*3*3*2*2),
|
|
1.0 / (3*3*3*2*2*2),
|
|
1.0 / (3*3*3*2*2*2*2)
|
|
};
|
|
|
|
PowiResult result;
|
|
|
|
int best_factor = FindIntegerFactor(exponent);
|
|
if(best_factor == 0)
|
|
{
|
|
#ifdef DEBUG_POWI
|
|
printf("no factor found for %g\n", exponent);
|
|
#endif
|
|
return result; // Unoptimizable
|
|
}
|
|
|
|
double best_cost = EvaluateFactorCost(best_factor, 0, 0, 0)
|
|
+ CalculatePowiFactorCost(long(exponent*best_factor));
|
|
int s_count = 0;
|
|
int c_count = 0;
|
|
int mul_count = 0;
|
|
|
|
#ifdef DEBUG_POWI
|
|
printf("orig = %g\n", exponent);
|
|
printf("plain factor = %d, cost %g\n", best_factor, best_cost);
|
|
#endif
|
|
|
|
for(unsigned n_s=0; n_s<MaxSep; ++n_s)
|
|
{
|
|
int best_selected_sep = 0;
|
|
double best_sep_cost = best_cost;
|
|
int best_sep_factor = best_factor;
|
|
for(int s=1; s<5*4; ++s)
|
|
{
|
|
#ifdef CBRT_IS_SLOW
|
|
if(s >= 5) break;
|
|
// When cbrt is implemented through exp and log,
|
|
// there is no advantage over exp(log()), so don't support it.
|
|
#endif
|
|
int n_sqrt = s%5;
|
|
int n_cbrt = s/5;
|
|
if(n_sqrt + n_cbrt > 4) continue;
|
|
|
|
double changed_exponent = exponent;
|
|
changed_exponent -= RootPowers[s];
|
|
|
|
int factor = FindIntegerFactor(changed_exponent);
|
|
if(factor != 0)
|
|
{
|
|
double cost = EvaluateFactorCost
|
|
(factor, s_count + n_sqrt, c_count + n_cbrt, mul_count + 1)
|
|
+ CalculatePowiFactorCost(long(changed_exponent*factor));
|
|
|
|
#ifdef DEBUG_POWI
|
|
printf("%d sqrt %d cbrt factor = %d, cost %g\n",
|
|
n_sqrt, n_cbrt, factor, cost);
|
|
#endif
|
|
if(cost < best_sep_cost)
|
|
{
|
|
best_selected_sep = s;
|
|
best_sep_factor = factor;
|
|
best_sep_cost = cost;
|
|
}
|
|
}
|
|
}
|
|
if(!best_selected_sep) break;
|
|
|
|
result.sep_list[n_s] = best_selected_sep;
|
|
exponent -= RootPowers[best_selected_sep];
|
|
s_count += best_selected_sep % 5;
|
|
c_count += best_selected_sep / 5;
|
|
best_cost = best_sep_cost;
|
|
best_factor = best_sep_factor;
|
|
mul_count += 1;
|
|
}
|
|
|
|
result.resulting_exponent = (long) (exponent * best_factor + 0.5);
|
|
while(best_factor % 2 == 0)
|
|
{
|
|
++result.n_int_sqrt;
|
|
best_factor /= 2;
|
|
}
|
|
while(best_factor % 3 == 0)
|
|
{
|
|
++result.n_int_cbrt;
|
|
best_factor /= 3;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
// Find the integer that "value" must be multiplied
|
|
// with to produce an integer...
|
|
// Consisting of factors 2 and 3 only.
|
|
bool MakesInteger(double value, int factor) const
|
|
{
|
|
double v = value * double(factor);
|
|
double diff = fabs(v - (double)(long)(v+0.5));
|
|
//printf("factor %d: v=%.20f, diff=%.20f\n", factor,v, diff);
|
|
return diff < 1e-9;
|
|
}
|
|
int FindIntegerFactor(double value) const
|
|
{
|
|
int factor = (2*2*2*2);
|
|
#ifdef CBRT_IS_SLOW
|
|
// When cbrt is implemented through exp and log,
|
|
// there is no advantage over exp(log()), so don't support it.
|
|
#else
|
|
factor *= (3*3*3);
|
|
#endif
|
|
int result = 0;
|
|
if(MakesInteger(value, factor))
|
|
{
|
|
result = factor;
|
|
while((factor % 2) == 0 && MakesInteger(value, factor/2))
|
|
result = factor /= 2;
|
|
while((factor % 3) == 0 && MakesInteger(value, factor/3))
|
|
result = factor /= 3;
|
|
}
|
|
#ifdef CBRT_IS_SLOW
|
|
if(result == 0)
|
|
{
|
|
/* Note: Even if we allow one cbrt,
|
|
* cbrt(cbrt(x)) still gets turned into
|
|
* exp(log(x)*0.111111)
|
|
* which gives an error when x < 0...
|
|
* should we use a special system here?
|
|
* i.e. exp(log(-5)*y)
|
|
* = -exp(log(5)*y)
|
|
* except when y is an even integer,
|
|
* when = exp(log(5)*y)
|
|
* We use a custom fp_pow() function
|
|
* in order to handle these situations.
|
|
*/
|
|
if(MakesInteger(value, 3)) return 3; // single cbrt opcode
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
int EvaluateFactorCost(int factor, int s, int c, int nmuls) const
|
|
{
|
|
const int sqrt_cost = 6;
|
|
#ifdef CBRT_IS_SLOW
|
|
const int cbrt_cost = 25;
|
|
#else
|
|
const int cbrt_cost = 8;
|
|
#endif
|
|
int result = s * sqrt_cost + c * cbrt_cost;
|
|
while(factor % 2 == 0) { factor /= 2; result += sqrt_cost; }
|
|
while(factor % 3 == 0) { factor /= 3; result += cbrt_cost; }
|
|
result += nmuls;
|
|
return result;
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace FPoptimizer_CodeTree
|
|
{
|
|
bool CodeTree::RecreateInversionsAndNegations(bool prefer_base2)
|
|
{
|
|
bool changed = false;
|
|
|
|
for(size_t a=0; a<GetParamCount(); ++a)
|
|
if(GetParam(a).RecreateInversionsAndNegations(prefer_base2))
|
|
changed = true;
|
|
|
|
if(changed)
|
|
{
|
|
exit_changed:
|
|
Mark_Incompletely_Hashed();
|
|
return true;
|
|
}
|
|
|
|
switch(GetOpcode()) // Recreate inversions and negations
|
|
{
|
|
case cMul:
|
|
{
|
|
std::vector<CodeTree> div_params;
|
|
CodeTree found_log2, found_log2by;
|
|
|
|
if(true)
|
|
{
|
|
/* This lengthy bit of code
|
|
* changes log2(x)^3 * 5
|
|
* to log2by(x, 5^(1/3)) ^ 3
|
|
* which is better for runtime
|
|
* than log2by(x,1)^3 * 5
|
|
*/
|
|
bool found_log2_on_exponent = false;
|
|
double log2_exponent = 0;
|
|
for(size_t a = GetParamCount(); a-- > 0; )
|
|
{
|
|
const CodeTree& powgroup = GetParam(a);
|
|
if(powgroup.GetOpcode() == cPow
|
|
&& powgroup.GetParam(0).GetOpcode() == cLog2
|
|
&& powgroup.GetParam(1).IsImmed())
|
|
{
|
|
// Found log2 on exponent
|
|
found_log2_on_exponent = true;
|
|
log2_exponent = powgroup.GetParam(1).GetImmed();
|
|
break;
|
|
}
|
|
}
|
|
if(found_log2_on_exponent)
|
|
{
|
|
double immeds = 1.0;
|
|
for(size_t a = GetParamCount(); a-- > 0; )
|
|
{
|
|
const CodeTree& powgroup = GetParam(a);
|
|
if(powgroup.IsImmed())
|
|
{
|
|
immeds *= powgroup.GetImmed();
|
|
DelParam(a);
|
|
}
|
|
}
|
|
for(size_t a = GetParamCount(); a-- > 0; )
|
|
{
|
|
CodeTree& powgroup = GetParam(a);
|
|
if(powgroup.GetOpcode() == cPow
|
|
&& powgroup.GetParam(0).GetOpcode() == cLog2
|
|
&& powgroup.GetParam(1).IsImmed())
|
|
{
|
|
CodeTree& log2 = powgroup.GetParam(0);
|
|
log2.CopyOnWrite();
|
|
log2.SetOpcode(cLog2by);
|
|
log2.AddParam( CodeTree( fp_pow(immeds, 1.0 / log2_exponent) ) );
|
|
log2.Rehash();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for(size_t a = GetParamCount(); a-- > 0; )
|
|
{
|
|
const CodeTree& powgroup = GetParam(a);
|
|
if(powgroup.GetOpcode() == cPow
|
|
&& powgroup.GetParam(1).IsImmed())
|
|
{
|
|
const CodeTree& exp_param = powgroup.GetParam(1);
|
|
double exponent = exp_param.GetImmed();
|
|
if(FloatEqual(exponent, -1.0))
|
|
{
|
|
CopyOnWrite();
|
|
div_params.push_back(GetParam(a).GetParam(0));
|
|
DelParam(a); // delete the pow group
|
|
}
|
|
else if(exponent < 0 && IsIntegerConst(exponent))
|
|
{
|
|
CodeTree edited_powgroup;
|
|
edited_powgroup.SetOpcode(cPow);
|
|
edited_powgroup.AddParam(powgroup.GetParam(0));
|
|
edited_powgroup.AddParam(CodeTree(-exponent));
|
|
edited_powgroup.Rehash();
|
|
div_params.push_back(edited_powgroup);
|
|
CopyOnWrite();
|
|
DelParam(a); // delete the pow group
|
|
}
|
|
}
|
|
else if(powgroup.GetOpcode() == cLog2 && !found_log2.IsDefined())
|
|
{
|
|
found_log2 = powgroup.GetParam(0);
|
|
CopyOnWrite();
|
|
DelParam(a);
|
|
}
|
|
else if(powgroup.GetOpcode() == cLog2by && !found_log2by.IsDefined())
|
|
{
|
|
found_log2by = powgroup;
|
|
CopyOnWrite();
|
|
DelParam(a);
|
|
}
|
|
}
|
|
if(!div_params.empty())
|
|
{
|
|
changed = true;
|
|
|
|
CodeTree divgroup;
|
|
divgroup.SetOpcode(cMul);
|
|
divgroup.SetParamsMove(div_params);
|
|
divgroup.Rehash(); // will reduce to div_params[0] if only one item
|
|
CodeTree mulgroup;
|
|
mulgroup.SetOpcode(cMul);
|
|
mulgroup.SetParamsMove(GetParams());
|
|
mulgroup.Rehash(); // will reduce to 1.0 if none remained in this cMul
|
|
if(mulgroup.IsImmed() && FloatEqual(mulgroup.GetImmed(), 1.0))
|
|
{
|
|
SetOpcode(cInv);
|
|
AddParamMove(divgroup);
|
|
}
|
|
/*else if(mulgroup.IsImmed() && FloatEqual(mulgroup.GetImmed(), -1.0))
|
|
{
|
|
CodeTree invgroup;
|
|
invgroup.SetOpcode(cInv);
|
|
invgroup.AddParamMove(divgroup);
|
|
invgroup.Rehash();
|
|
SetOpcode(cNeg);
|
|
AddParamMove(invgroup);
|
|
}*/
|
|
else
|
|
{
|
|
if(mulgroup.GetDepth() >= divgroup.GetDepth())
|
|
{
|
|
SetOpcode(cDiv);
|
|
AddParamMove(mulgroup);
|
|
AddParamMove(divgroup);
|
|
}
|
|
else
|
|
{
|
|
SetOpcode(cRDiv);
|
|
AddParamMove(divgroup);
|
|
AddParamMove(mulgroup);
|
|
}
|
|
}
|
|
}
|
|
if(found_log2.IsDefined())
|
|
{
|
|
CodeTree mulgroup;
|
|
mulgroup.SetOpcode(GetOpcode());
|
|
mulgroup.SetParamsMove(GetParams());
|
|
mulgroup.Rehash();
|
|
while(mulgroup.RecreateInversionsAndNegations(prefer_base2))
|
|
mulgroup.FixIncompleteHashes();
|
|
SetOpcode(cLog2by);
|
|
AddParamMove(found_log2);
|
|
AddParamMove(mulgroup);
|
|
changed = true;
|
|
}
|
|
if(found_log2by.IsDefined())
|
|
{
|
|
CodeTree mulgroup;
|
|
mulgroup.SetOpcode(cMul);
|
|
mulgroup.AddParamMove(found_log2by.GetParam(1));
|
|
mulgroup.AddParamsMove(GetParams());
|
|
mulgroup.Rehash();
|
|
while(mulgroup.RecreateInversionsAndNegations(prefer_base2))
|
|
mulgroup.FixIncompleteHashes();
|
|
DelParams();
|
|
SetOpcode(cLog2by);
|
|
AddParamMove(found_log2by.GetParam(0));
|
|
AddParamMove(mulgroup);
|
|
changed = true;
|
|
}
|
|
break;
|
|
}
|
|
case cAdd:
|
|
{
|
|
std::vector<CodeTree> sub_params;
|
|
|
|
for(size_t a = GetParamCount(); a-- > 0; )
|
|
if(GetParam(a).GetOpcode() == cMul)
|
|
{
|
|
bool is_signed = false; // if the mul group has a -1 constant...
|
|
|
|
CodeTree& mulgroup = GetParam(a);
|
|
|
|
for(size_t b=mulgroup.GetParamCount(); b-- > 0; )
|
|
{
|
|
if(mulgroup.GetParam(b).IsImmed())
|
|
{
|
|
double factor = mulgroup.GetParam(b).GetImmed();
|
|
if(FloatEqual(factor, -1.0))
|
|
{
|
|
mulgroup.CopyOnWrite();
|
|
mulgroup.DelParam(b);
|
|
is_signed = !is_signed;
|
|
}
|
|
else if(FloatEqual(factor, -2.0))
|
|
{
|
|
mulgroup.CopyOnWrite();
|
|
mulgroup.DelParam(b);
|
|
mulgroup.AddParam( CodeTree(2.0) );
|
|
is_signed = !is_signed;
|
|
}
|
|
}
|
|
}
|
|
if(is_signed)
|
|
{
|
|
mulgroup.Rehash();
|
|
sub_params.push_back(mulgroup);
|
|
CopyOnWrite();
|
|
DelParam(a);
|
|
}
|
|
}
|
|
else if(GetParam(a).GetOpcode() == cDiv)
|
|
{
|
|
bool is_signed = false;
|
|
CodeTree& divgroup = GetParam(a);
|
|
if(divgroup.GetParam(0).IsImmed())
|
|
{
|
|
if(FloatEqual(divgroup.GetParam(0).GetImmed(), -1.0))
|
|
{
|
|
divgroup.CopyOnWrite();
|
|
divgroup.DelParam(0);
|
|
divgroup.SetOpcode(cInv);
|
|
is_signed = !is_signed;
|
|
}
|
|
}
|
|
if(is_signed)
|
|
{
|
|
divgroup.Rehash();
|
|
sub_params.push_back(divgroup);
|
|
CopyOnWrite();
|
|
DelParam(a);
|
|
}
|
|
}
|
|
else if(GetParam(a).GetOpcode() == cRDiv)
|
|
{
|
|
bool is_signed = false;
|
|
CodeTree& divgroup = GetParam(a);
|
|
if(divgroup.GetParam(1).IsImmed())
|
|
{
|
|
if(FloatEqual(divgroup.GetParam(1).GetImmed(), -1.0))
|
|
{
|
|
divgroup.CopyOnWrite();
|
|
divgroup.DelParam(1);
|
|
divgroup.SetOpcode(cInv);
|
|
is_signed = !is_signed;
|
|
}
|
|
}
|
|
if(is_signed)
|
|
{
|
|
divgroup.Rehash();
|
|
sub_params.push_back(divgroup);
|
|
CopyOnWrite();
|
|
DelParam(a);
|
|
}
|
|
}
|
|
if(!sub_params.empty())
|
|
{
|
|
CodeTree subgroup;
|
|
subgroup.SetOpcode(cAdd);
|
|
subgroup.SetParamsMove(sub_params);
|
|
subgroup.Rehash(); // will reduce to sub_params[0] if only one item
|
|
CodeTree addgroup;
|
|
addgroup.SetOpcode(cAdd);
|
|
addgroup.SetParamsMove(GetParams());
|
|
addgroup.Rehash(); // will reduce to 0.0 if none remained in this cAdd
|
|
if(addgroup.IsImmed() && FloatEqual(addgroup.GetImmed(), 0.0))
|
|
{
|
|
SetOpcode(cNeg);
|
|
AddParamMove(subgroup);
|
|
}
|
|
else
|
|
{
|
|
if(addgroup.GetDepth() == 1)
|
|
{
|
|
/* 5 - (x+y+z) is best expressed as rsub(x+y+z, 5);
|
|
* this has lowest stack usage.
|
|
* This is identified by addgroup having just one member.
|
|
*/
|
|
SetOpcode(cRSub);
|
|
AddParamMove(subgroup);
|
|
AddParamMove(addgroup);
|
|
}
|
|
else if(subgroup.GetOpcode() == cAdd)
|
|
{
|
|
/* a+b-(x+y+z) is expressed as a+b-x-y-z.
|
|
* Making a long chain of cSubs is okay, because the
|
|
* cost of cSub is the same as the cost of cAdd.
|
|
* Thus we get the lowest stack usage.
|
|
* This approach cannot be used for cDiv.
|
|
*/
|
|
SetOpcode(cSub);
|
|
AddParamMove(addgroup);
|
|
AddParamMove(subgroup.GetParam(0));
|
|
for(size_t a=1; a<subgroup.GetParamCount(); ++a)
|
|
{
|
|
CodeTree innersub;
|
|
innersub.SetOpcode(cSub);
|
|
innersub.SetParamsMove(GetParams());
|
|
innersub.Rehash(false);
|
|
//DelParams();
|
|
AddParamMove(innersub);
|
|
AddParamMove(subgroup.GetParam(a));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetOpcode(cSub);
|
|
AddParamMove(addgroup);
|
|
AddParamMove(subgroup);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case cPow:
|
|
{
|
|
const CodeTree& p0 = GetParam(0);
|
|
const CodeTree& p1 = GetParam(1);
|
|
if(p1.IsImmed())
|
|
{
|
|
if(p1.GetImmed() != 0.0 && !p1.IsLongIntegerImmed())
|
|
{
|
|
PowiResolver::PowiResult
|
|
r = PowiResolver().CreatePowiResult(fabs(p1.GetImmed()));
|
|
|
|
if(r.resulting_exponent != 0)
|
|
{
|
|
bool signed_chain = false;
|
|
|
|
if(p1.GetImmed() < 0
|
|
&& r.sep_list[0] == 0
|
|
&& r.n_int_sqrt > 0)
|
|
{
|
|
// If one of the internal sqrts can be changed into rsqrt
|
|
signed_chain = true;
|
|
}
|
|
|
|
#ifdef DEBUG_POWI
|
|
printf("Will resolve powi %g as powi(chain(%d,%d),%ld)",
|
|
fabs(p1.GetImmed()),
|
|
r.n_int_sqrt,
|
|
r.n_int_cbrt,
|
|
r.resulting_exponent);
|
|
for(unsigned n=0; n<PowiResolver::MaxSep; ++n)
|
|
{
|
|
if(r.sep_list[n] == 0) break;
|
|
int n_sqrt = r.sep_list[n] % 5;
|
|
int n_cbrt = r.sep_list[n] / 5;
|
|
printf("*chain(%d,%d)", n_sqrt,n_cbrt);
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
|
|
CodeTree source_tree = GetParam(0);
|
|
|
|
CodeTree pow_item = source_tree;
|
|
pow_item.CopyOnWrite();
|
|
ChangeIntoRootChain(pow_item,
|
|
signed_chain,
|
|
r.n_int_sqrt,
|
|
r.n_int_cbrt);
|
|
pow_item.Rehash();
|
|
|
|
CodeTree pow;
|
|
if(r.resulting_exponent != 1)
|
|
{
|
|
pow.SetOpcode(cPow);
|
|
pow.AddParamMove(pow_item);
|
|
pow.AddParam(CodeTree( double(r.resulting_exponent) ));
|
|
}
|
|
else
|
|
pow.swap(pow_item);
|
|
|
|
CodeTree mul;
|
|
mul.SetOpcode(cMul);
|
|
mul.AddParamMove(pow);
|
|
|
|
for(unsigned n=0; n<PowiResolver::MaxSep; ++n)
|
|
{
|
|
if(r.sep_list[n] == 0) break;
|
|
int n_sqrt = r.sep_list[n] % 5;
|
|
int n_cbrt = r.sep_list[n] / 5;
|
|
|
|
CodeTree mul_item = source_tree;
|
|
mul_item.CopyOnWrite();
|
|
ChangeIntoRootChain(mul_item, false, n_sqrt, n_cbrt);
|
|
mul_item.Rehash();
|
|
mul.AddParamMove(mul_item);
|
|
}
|
|
|
|
if(p1.GetImmed() < 0 && !signed_chain)
|
|
{
|
|
mul.Rehash();
|
|
SetOpcode(cInv);
|
|
SetParamMove(0, mul);
|
|
DelParam(1);
|
|
}
|
|
else
|
|
{
|
|
SetOpcode(cMul);
|
|
SetParamsMove(mul.GetParams());
|
|
}
|
|
#ifdef DEBUG_POWI
|
|
DumpTreeWithIndent(*this);
|
|
#endif
|
|
changed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(GetOpcode() == cPow
|
|
&& (!p1.IsLongIntegerImmed()
|
|
|| !IsOptimizableUsingPowi(p1.GetLongIntegerImmed())))
|
|
{
|
|
if(p0.IsImmed() && p0.GetImmed() > 0.0)
|
|
{
|
|
// Convert into cExp or Exp2.
|
|
// x^y = exp(log(x) * y) =
|
|
// Can only be done when x is positive, though.
|
|
if(prefer_base2)
|
|
{
|
|
double mulvalue = fp_log2( p0.GetImmed() );
|
|
if(mulvalue == 1.0)
|
|
{
|
|
// exp2(1)^x becomes exp2(x)
|
|
DelParam(0);
|
|
}
|
|
else
|
|
{
|
|
// exp2(4)^x becomes exp2(4*x)
|
|
CodeTree exponent;
|
|
exponent.SetOpcode(cMul);
|
|
exponent.AddParam( CodeTree( mulvalue ) );
|
|
exponent.AddParam(p1);
|
|
exponent.Rehash();
|
|
SetParamMove(0, exponent);
|
|
DelParam(1);
|
|
}
|
|
SetOpcode(cExp2);
|
|
changed = true;
|
|
}
|
|
else
|
|
{
|
|
double mulvalue = std::log( p0.GetImmed() );
|
|
if(mulvalue == 1.0)
|
|
{
|
|
// exp(1)^x becomes exp(x)
|
|
DelParam(0);
|
|
}
|
|
else
|
|
{
|
|
// exp(4)^x becomes exp(4*x)
|
|
CodeTree exponent;
|
|
exponent.SetOpcode(cMul);
|
|
exponent.AddParam( CodeTree( mulvalue ) );
|
|
exponent.AddParam(p1);
|
|
exponent.Rehash();
|
|
SetParamMove(0, exponent);
|
|
DelParam(1);
|
|
}
|
|
SetOpcode(cExp);
|
|
changed = true;
|
|
}
|
|
}
|
|
else if(p0.IsAlwaysSigned(true))
|
|
{
|
|
if(prefer_base2)
|
|
{
|
|
CodeTree log;
|
|
log.SetOpcode(cLog2);
|
|
log.AddParam(p0);
|
|
log.Rehash();
|
|
CodeTree exponent;
|
|
exponent.SetOpcode(cMul);
|
|
exponent.AddParam(p1);
|
|
exponent.AddParamMove(log);
|
|
exponent.Rehash();
|
|
SetOpcode(cExp2);
|
|
SetParamMove(0, exponent);
|
|
DelParam(1);
|
|
changed = true;
|
|
}
|
|
else
|
|
{
|
|
CodeTree log;
|
|
log.SetOpcode(cLog);
|
|
log.AddParam(p0);
|
|
log.Rehash();
|
|
CodeTree exponent;
|
|
exponent.SetOpcode(cMul);
|
|
exponent.AddParam(p1);
|
|
exponent.AddParamMove(log);
|
|
exponent.Rehash();
|
|
SetOpcode(cExp);
|
|
SetParamMove(0, exponent);
|
|
DelParam(1);
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: break;
|
|
}
|
|
|
|
if(changed)
|
|
goto exit_changed;
|
|
|
|
return changed;
|
|
}
|
|
|
|
bool ContainsOtherCandidates(
|
|
const CodeTree& within,
|
|
const CodeTree& tree,
|
|
const FPoptimizer_ByteCode::ByteCodeSynth& synth,
|
|
const TreeCountType& TreeCounts)
|
|
{
|
|
for(size_t b=tree.GetParamCount(), a=0; a<b; ++a)
|
|
{
|
|
const CodeTree& leaf = tree.GetParam(a);
|
|
|
|
TreeCountType::iterator synth_it;
|
|
for(TreeCountType::const_iterator
|
|
i = TreeCounts.begin();
|
|
i != TreeCounts.end();
|
|
++i)
|
|
{
|
|
if(i->first != leaf.GetHash())
|
|
continue;
|
|
|
|
size_t score = i->second.first;
|
|
const CodeTree& candidate = i->second.second;
|
|
|
|
// It must not yet have been synthesized
|
|
if(synth.Find(candidate))
|
|
continue;
|
|
|
|
// And it must not be a simple expression
|
|
// Because cImmed, VarBegin are faster than cFetch
|
|
if(leaf.GetDepth() <= 1)
|
|
continue;
|
|
|
|
// It must always occur at least twice
|
|
if(score < 2)
|
|
continue;
|
|
|
|
// And it must either appear on both sides
|
|
// of a cIf, or neither
|
|
if(IfBalanceGood(within, leaf).BalanceGood == false)
|
|
continue;
|
|
|
|
return true;
|
|
}
|
|
if(ContainsOtherCandidates(within, leaf, synth, TreeCounts))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t CodeTree::SynthCommonSubExpressions(
|
|
FPoptimizer_ByteCode::ByteCodeSynth& synth) const
|
|
{
|
|
size_t stacktop_before = synth.GetStackTop();
|
|
|
|
/* Find common subtrees */
|
|
TreeCountType TreeCounts;
|
|
FindTreeCounts(TreeCounts, *this);
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS_CSE
|
|
DumpHashes(*this);
|
|
#endif
|
|
|
|
/* Synthesize some of the most common ones */
|
|
for(;;)
|
|
{
|
|
size_t best_score = 0;
|
|
TreeCountType::iterator synth_it;
|
|
for(TreeCountType::iterator
|
|
j,i = TreeCounts.begin();
|
|
i != TreeCounts.end();
|
|
i=j)
|
|
{
|
|
j=i; ++j;
|
|
|
|
size_t score = i->second.first;
|
|
const CodeTree& tree = i->second.second;
|
|
|
|
#ifdef DEBUG_SUBSTITUTIONS_CSE
|
|
std::cout << "Score " << score << ":\n";
|
|
DumpTreeWithIndent(tree);
|
|
#endif
|
|
|
|
// It must not yet have been synthesized
|
|
if(synth.Find(tree))
|
|
{
|
|
TreeCounts.erase(i);
|
|
continue;
|
|
}
|
|
|
|
// And it must not be a simple expression
|
|
// Because cImmed, VarBegin are faster than cFetch
|
|
if(tree.GetDepth() <= 1)
|
|
{
|
|
TreeCounts.erase(i);
|
|
continue;
|
|
}
|
|
|
|
// It must always occur at least twice
|
|
if(score < 2)
|
|
{
|
|
TreeCounts.erase(i);
|
|
continue;
|
|
}
|
|
|
|
// And it must either appear on both sides
|
|
// of a cIf, or neither
|
|
if(IfBalanceGood(*this, tree).BalanceGood == false)
|
|
{
|
|
TreeCounts.erase(i);
|
|
continue;
|
|
}
|
|
|
|
// It must not contain other candidates
|
|
if(ContainsOtherCandidates(*this, tree, synth, TreeCounts))
|
|
{
|
|
// Don't erase it; it may be a proper candidate later
|
|
continue;
|
|
}
|
|
|
|
// Is a candidate.
|
|
score *= tree.GetDepth();
|
|
if(score > best_score)
|
|
{ best_score = score; synth_it = i; }
|
|
}
|
|
|
|
if(best_score <= 0) break; // Didn't find anything.
|
|
|
|
const CodeTree& tree = synth_it->second.second;
|
|
#ifdef DEBUG_SUBSTITUTIONS_CSE
|
|
std::cout << "Found Common Subexpression:"; DumpTree(tree); std::cout << "\n";
|
|
#endif
|
|
/* Synthesize the selected tree */
|
|
tree.SynthesizeByteCode(synth, false);
|
|
TreeCounts.erase(synth_it);
|
|
#ifdef DEBUG_SUBSTITUTIONS_CSE
|
|
std::cout << "Done with Common Subexpression:"; DumpTree(tree); std::cout << "\n";
|
|
#endif
|
|
}
|
|
|
|
return synth.GetStackTop() - stacktop_before;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_main.c"
|
|
#include "fpconfig.h"
|
|
#include "fparser.h"
|
|
#include "fptypes.h"
|
|
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_codetree.h"
|
|
// line removed for fpoptimizer.c: #include "fpoptimizer_optimize.h"
|
|
|
|
using namespace FUNCTIONPARSERTYPES;
|
|
|
|
#ifdef FP_SUPPORT_OPTIMIZER
|
|
using namespace FPoptimizer_CodeTree;
|
|
|
|
template<>
|
|
void FunctionParserBase<double>::Optimize()
|
|
{
|
|
typedef double Value_t;
|
|
|
|
CopyOnWrite();
|
|
|
|
//PrintByteCode(std::cout);
|
|
|
|
CodeTree tree;
|
|
tree.GenerateFrom(data->ByteCode, data->Immed, *data);
|
|
|
|
FPoptimizer_Optimize::ApplyGrammars(tree);
|
|
|
|
std::vector<unsigned> byteCode;
|
|
std::vector<Value_t> immed;
|
|
size_t stacktop_max = 0;
|
|
tree.SynthesizeByteCode(byteCode, immed, stacktop_max);
|
|
|
|
/*std::cout << std::flush;
|
|
std::cerr << std::flush;
|
|
fprintf(stderr, "Estimated stacktop %u\n", (unsigned)stacktop_max);
|
|
fflush(stderr);*/
|
|
|
|
if(data->StackSize != stacktop_max)
|
|
{
|
|
data->StackSize = stacktop_max; // note: gcc warning is meaningful
|
|
#ifndef FP_USE_THREAD_SAFE_EVAL
|
|
data->Stack.resize(stacktop_max);
|
|
#endif
|
|
}
|
|
|
|
data->ByteCode.swap(byteCode);
|
|
data->Immed.swap(immed);
|
|
|
|
//PrintByteCode(std::cout);
|
|
}
|
|
|
|
template<>
|
|
void FunctionParserBase<float>::Optimize()
|
|
{}
|
|
|
|
template<>
|
|
void FunctionParserBase<long double>::Optimize()
|
|
{}
|
|
|
|
template<>
|
|
void FunctionParserBase<long>::Optimize()
|
|
{}
|
|
|
|
#ifdef FP_SUPPORT_MPFR_FLOAT_TYPE
|
|
template<>
|
|
void FunctionParserBase<MpfrFloat>::Optimize()
|
|
{}
|
|
#endif
|
|
|
|
#ifdef FP_SUPPORT_GMP_INT_TYPE
|
|
template<>
|
|
void FunctionParserBase<GmpInt>::Optimize()
|
|
{}
|
|
#endif
|
|
|
|
|
|
FUNCTIONPARSER_INSTANTIATE_TYPES
|
|
|
|
#endif
|
|
|
|
#line 1 "fpoptimizer/fpoptimizer_footer.txt"
|
|
|
|
#endif
|
|
|