/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #ifndef VALUE_H #define VALUE_H #include "FloatUtils.h" #include "Address.h" #include "LogModule.h" enum Memory // Possible values of a vkMemory Value { mConstant // Memory region representing immutable values // Most memory regions are mutable, but they can never be constants inside a Value, // so they don't have constant values in the Memory enum. }; enum Condition // Possible values of a vkCond Value generated by comparing arg1 with arg2 { cLt = 1, // arg1 < arg2 cEq = 2, // arg1 = arg2 cGt = 4, // arg1 > arg2 cUn = 8 // either arg1 or arg2 is a NaN }; #ifdef DEBUG_LOG int print(LogModuleObject &f, Condition c); #endif enum Condition2 // Two-way conditional { cond0, // 0000 Always false condLt, // 0001 arg1 < arg2 condEq, // 0010 arg1 = arg2 condLe, // 0011 arg1 <= arg2 condGt, // 0100 arg1 > arg2 condLgt, // 0101 arg1 <> arg2 condGe, // 0110 arg1 >= arg2 condOrd, // 0111 arg1 <=> arg2 (i.e. arg1 and arg2 are ordered) condUnord, // 1000 arg1 ? arg2 (i.e. arg1 and arg2 are unordered) condULt, // 1001 arg1 ?< arg2 condUEq, // 1010 arg1 ?= arg2 condULe, // 1011 arg1 ?<= arg2 condUGt, // 1100 arg1 ?> arg2 condNe, // 1101 arg1 != arg2 condUGe, // 1110 arg1 ?>= arg2 cond1 // 1111 Always true }; // Return a condition c' such that for all arg1, arg2: (arg1 c arg2) = (arg2 c' arg1) inline Condition2 reverse(Condition2 c) {return (Condition2)(c&0xA | (c&4)>>2 | (c&1)<<2);} // Return a condition c' such that for all arg1, arg2: !(arg1 c arg2) = (arg1 c' arg2) inline Condition2 invert(Condition2 c) {return (Condition2)(c^cond1);} // Return the result of applying the Condition2 to the Condition. inline bool applyCondition(Condition2 comp, Condition c) {return (comp & c) != 0;} enum Condition3 // Three-way conditional { // lt eq gt un cond3L, // -1 0 1 -1 cond3G, // -1 0 1 1 cond3CL, // 1 0 -1 -1 cond3CG // 1 0 -1 1 }; // Return a condition c' such that for all arg1, arg2: (arg1 c arg2) = (arg2 c' arg1) inline Condition3 reverse(Condition3 c) {return (Condition3)(c^2);} // Return a condition c' such that for all arg1, arg2: -(arg1 c arg2) = (arg1 c' arg2) inline Condition3 invert(Condition3 c) {return (Condition3)(c^cond3CG);} // Return the result of applying the Condition3 to the Condition. inline int applyCondition(Condition3 comp, Condition c) {return c == cEq ? 0 : ((0x91c4>>(comp<<2) & c) != 0)*2 - 1;} // ---------------------------------------------------------------------------- enum ValueKind // Concrete? RegOrMem? #words (as defined by the Java stack model) { // Java? Storable? vkVoid, // yes yes no no N/A // No value vkInt, // yes yes yes yes 1 // 32-bit integer or high or low half of a long vkLong, // yes yes yes yes 2 // 64-bit integer vkFloat, // yes yes yes yes 1 // 32-bit float vkDouble, // yes yes yes yes 2 // 64-bit float vkAddr, // yes yes yes yes 1 // Pointer vkCond, // yes no yes no N/A // The result of a compare vkMemory, // no no yes no N/A // Value representing all of memory in data flow edges vkTuple // no no no no N/A // Value representing a tuple of other values }; const uint nValueKinds = vkTuple + 1; inline ValueKind typeKindToValueKind(TypeKind tk); inline bool isConcreteKind(ValueKind vk) {return vk <= vkCond;} inline bool isJavaKind(ValueKind vk) {return vk <= vkAddr;} inline bool isRegOrMemKind(ValueKind vk) {return vk != vkVoid && vk <= vkMemory;} inline bool isStorableKind(ValueKind vk) {return vk != vkVoid && vk <= vkAddr;} inline bool isVoidKind(ValueKind vk) {return vk == vkVoid;} inline bool isIntegerKind(ValueKind vk) {return vk == vkInt || vk == vkLong;} inline bool isFloatingPointKind(ValueKind vk) {return vk == vkFloat || vk == vkDouble;} inline bool isMemoryKind(ValueKind vk) {return vk == vkMemory;} // isWordKind and isDoublewordKind indicate whether a Java ValueKind takes // one or two words in the Java stack model; this does not necessarily mean // that that value's actual machine representation takes one or two machine words. // For example, the Java stack model states that an address is one word, but on // a 64-bit implementation an address takes two *machine words*. inline bool isWordKind(ValueKind vk) {return vk == vkInt || vk == vkFloat || vk == vkAddr;} inline bool isDoublewordKind(ValueKind vk) {return vk == vkLong || vk == vkDouble;} #ifdef DEBUG_LOG char valueKindShortName(ValueKind vk); int print(LogModuleObject &f, ValueKind vk); #endif union Value { Int32 i; // Integer value Int64 l; // Long value Flt32 f; // Float value Flt64 d; // Double value addr a; // Pointer value Condition c; // Condition value Memory m; // Memory region value bool isNonzero(ValueKind kind) const; bool eq(ValueKind kind, const Value &v2) const; // This should be based on a template instead of a // dummy argument, but some compilers don't support that yet. Int32 getValueContents(Int32 *) const {return i;} #ifdef __MWERKS__ const Int64 &getValueContents(Int64 *) const {return l;} #else Int64 getValueContents(Int64 *) const {return l;} #endif Flt32 getValueContents(Flt32 *) const {return f;} Flt64 getValueContents(Flt64 *) const {return d;} addr getValueContents(addr *) const {return a;} Condition getValueContents(Condition *) const {return c;} Memory getValueContents(Memory *) const {return m;} #define TypeGetValueContents(T) getValueContents((T *)0) void setValueContents(Int32 v) {i = v;} void setValueContents(Int64 v) {l = v;} void setValueContents(Flt32 v) {f = v;} void setValueContents(Flt64 v) {d = v;} void setValueContents(addr v) {a = v;} void setValueContents(Condition v) {c = v;} void setValueContents(Memory v) {m = v;} #ifdef DEBUG_LOG int print(LogModuleObject &f, ValueKind vk) const; #endif }; // Return the kind of the value. #if defined __MWERKS__ || defined __GNUC__ || defined WIN32 // This should be based on a template instead of a dummy argument, but some compilers don't support that yet. inline ValueKind valueKind(Int32 *) {return vkInt;} inline ValueKind valueKind(Int64 *) {return vkLong;} inline ValueKind valueKind(Flt32 *) {return vkFloat;} inline ValueKind valueKind(Flt64 *) {return vkDouble;} inline ValueKind valueKind(addr *) {return vkAddr;} inline ValueKind valueKind(Condition *) {return vkCond;} inline ValueKind valueKind(Memory *) {return vkMemory;} #define TypeValueKind(T) valueKind((T *)0) #else template ValueKind valueKind(); template<> inline ValueKind valueKind() {return vkInt;} template<> inline ValueKind valueKind() {return vkLong;} template<> inline ValueKind valueKind() {return vkFloat;} template<> inline ValueKind valueKind() {return vkDouble;} template<> inline ValueKind valueKind() {return vkAddr;} template<> inline ValueKind valueKind() {return vkCond;} template<> inline ValueKind valueKind() {return vkMemory;} #define TypeValueKind(T) valueKind() #endif // Return true if v is (positive or negative) zero or cEq. inline bool isZero(Int32 v) {return v == 0;} inline bool isZero(Int64 v) {return v == 0;} inline bool isZero(Flt32 v) {return v == 0.0f;} inline bool isZero(Flt64 v) {return v == 0.0;} inline bool isZero(addr v) {return !v;} inline bool isZero(Condition v) {return v == cEq;} inline bool isZero(Memory) {return true;} // Only constant memory regions can be represented as values // Return true if v is infinite. (Equivalent functions for floating-point values located in FloatUtils.h) inline bool isInfinite(Int32) {return false;} inline bool isInfinite(Int64) {return false;} inline bool isInfinite(addr) {return false;} inline bool isInfinite(Condition) {return false;} inline bool isInfinite(Memory) {return false;} // Return true if v is a NaN. inline bool isNaN(Int32) {return false;} inline bool isNaN(Int64) {return false;} //inline bool isNaN(Flt32) // Defined in FloatUtils.h //inline bool isNaN(Flt64) // Defined in FloatUtils.h inline bool isNaN(addr) {return false;} inline bool isNaN(Condition) {return false;} inline bool isNaN(Memory) {return false;} #define VALUE_FITS(mask, v, out, cast) \ PR_BEGIN_MACRO \ if ((~mask & v.i) > 0) \ return (false); \ else \ { \ out = (cast)(mask & v.i); \ return (true); \ } \ PR_END_MACRO inline bool extractU8(const Value& v, Uint8& out) { VALUE_FITS(0xFF, v, out, Uint8); } inline bool extractU16(const Value& v, Uint16& out) { VALUE_FITS(0xFFFF, v, out, Uint16); } inline bool extractS16(const Value& v, Int16& out) { if ((v.i >= -32768 && v.i < 32767)) { out = (Int16) v.i; return (true); } else return (false); } inline bool extractU32(const Value& v, Uint32& out) { out = v.i; return (true); } inline bool isPowerOfTwo(const Uint32 v) { return (!(v & (v-1)) && v); } inline Uint8 leadingZeros(const Uint32 v) { #ifdef __MWERKS__ return (__cntlzw(v)); #else Uint32 mask = 0x80000000; Uint8 leadingZeros; for (leadingZeros = 0; (v & mask) != v; leadingZeros++, mask>>=1) ; return (leadingZeros); #endif } #ifdef _WIN32 #pragma warning( disable : 4035 ) inline uint leastSigBit(Uint32 v) { __asm bsf eax, v } #pragma warning( default : 4035 ) #endif //_WIN32 inline bool valueIsOneByteSigned(Uint32 inValue) { uint temp = inValue >> 7; if(temp == 0 || temp == 0x01ffffff) return true; return false; } // Return true if v+x is identical to v for all x of the same type as v. inline bool isAdditiveAnnihilator(Int32) {return false;} inline bool isAdditiveAnnihilator(Int64) {return false;} inline bool isAdditiveAnnihilator(Flt32 v) {return isNaN(v);} inline bool isAdditiveAnnihilator(Flt64 v) {return isNaN(v);} // Return true if v+x is identical to x for all x of the same type as v. inline bool isAdditiveIdentity(Int32 v) {return v == 0;} inline bool isAdditiveIdentity(Int64 v) {return v == 0;} inline bool isAdditiveIdentity(Flt32 v) {return isNegativeZero(v);} // v=+0.0 doesn't work because 0.0+-0.0 !eq -0.0 inline bool isAdditiveIdentity(Flt64 v) {return isNegativeZero(v);} // v=+0.0 doesn't work because 0.0+-0.0 !eq -0.0 // Return true if v*x is identical to v for all x of the same type as v. inline bool isMultiplicativeAnnihilator(Int32 v) {return v == 0;} inline bool isMultiplicativeAnnihilator(Int64 v) {return v == 0;} inline bool isMultiplicativeAnnihilator(Flt32 v) {return isNaN(v);} // v=+0.0 or -0.0 doesn't work because 0.0*-1.0 !eq 0.0*1.0 inline bool isMultiplicativeAnnihilator(Flt64 v) {return isNaN(v);} // v=+0.0 or -0.0 doesn't work because 0.0*-1.0 !eq 0.0*1.0 // Return true if v*x is identical to x for all x of the same type as v. inline bool isMultiplicativeIdentity(Int32 v) {return v == 1;} inline bool isMultiplicativeIdentity(Int64 v) {return v == 1;} inline bool isMultiplicativeIdentity(Flt32 v) {return v == 1.0f;} inline bool isMultiplicativeIdentity(Flt64 v) {return v == 1.0;} // Return true if v*x is identical to -x for all x of the same type as v. inline bool isMultiplicativeNegator(Int32 v) {return v == -1;} inline bool isMultiplicativeNegator(Int64 v) {return v == -1;} inline bool isMultiplicativeNegator(Flt32 v) {return v == -1.0f;} inline bool isMultiplicativeNegator(Flt64 v) {return v == -1.0;} // Return the (positive) zero element v of the given type. // This should be based on a template instead of a dummy argument, but some compilers don't support that yet. inline Int32 getZero(Int32 *) {return 0;} inline Int64 getZero(Int64 *) {return 0;} inline Flt32 getZero(Flt32 *) {return 0.0f;} inline Flt64 getZero(Flt64 *) {return 0.0;} inline addr getZero(addr *) {return nullAddr;} #define TypeGetZero(T) getZero((T *)0) // Return the element v of the given type such that v+x is identical to x for all x of the same type. // This should be based on a template instead of a dummy argument, but some compilers don't support that yet. inline Int32 getAdditiveIdentity(Int32 *) {return 0;} inline Int64 getAdditiveIdentity(Int64 *) {return 0;} inline Flt32 getAdditiveIdentity(Flt32 *) {return floatNegativeZero;} inline Flt64 getAdditiveIdentity(Flt64 *) {return doubleNegativeZero;} #define TypeGetAdditiveIdentity(T) getAdditiveIdentity((T *)0) // Return the NaN of the given type. // This should be based on a template instead of a dummy argument, but some compilers don't support that yet. inline Int32 getNaN(Int32 *) {trespass("No Int32 NaN"); return 0;} inline Int64 getNaN(Int64 *) {trespass("No Int64 NaN"); return 0;} inline Flt32 getNaN(Flt32 *) {return floatNaN;} inline Flt64 getNaN(Flt64 *) {return doubleNaN;} #define TypeGetNaN(T) getNaN((T *)0) // Convert the signed integer to unsigned. inline Uint8 toUnsigned(Int8 i) {return (Uint8)i;} inline Uint16 toUnsigned(Int16 i) {return (Uint16)i;} inline Uint32 toUnsigned(Int32 i) {return (Uint32)i;} inline Uint64 toUnsigned(Int64 i) {return (Uint64)i;} // Convert the unsigned integer to signed. inline Int8 toSigned(Uint8 i) {return (Int8)i;} inline Int16 toSigned(Uint16 i) {return (Int16)i;} inline Int32 toSigned(Uint32 i) {return (Int32)i;} inline Int64 toSigned(Uint64 i) {return (Int64)i;} // Convert the argument number to the result number using Java's conventions for conversion. inline void convertNumber(Int32 arg, Int64 &result) {result = arg;} inline void convertNumber(Int32 arg, Flt32 &result) {result = int32ToFlt32(arg);} inline void convertNumber(Int32 arg, Flt64 &result) {result = int32ToFlt64(arg);} inline void convertNumber(Int64 arg, Int32 &result) {result = (Int32)arg;} inline void convertNumber(Int64 arg, Flt32 &result) {result = int64ToFlt32(arg);} inline void convertNumber(Int64 arg, Flt64 &result) {result = int64ToFlt64(arg);} inline void convertNumber(Flt32 arg, Int32 &result) {result = flt32ToInt32(arg);} inline void convertNumber(Flt32 arg, Int64 &result) {result = flt32ToInt64(arg);} inline void convertNumber(Flt32 arg, Flt64 &result) {result = arg;} inline void convertNumber(Flt64 arg, Int32 &result) {result = flt64ToInt32(arg);} inline void convertNumber(Flt64 arg, Int64 &result) {result = flt64ToInt64(arg);} inline void convertNumber(Flt64 arg, Flt32 &result) {result = (Flt32)arg;} // Compare arg1 with arg2. If unsignedCompare is true, the values are treated as unsigned. inline Condition compare(Int32 arg1, Int32 arg2, bool unsignedCompare) {return (Condition)((unsignedCompare ? (Uint32)arg1 > (Uint32)arg2 : arg1 > arg2)*(cGt-cLt) + (arg1 == arg2)*(cEq-cLt) + cLt);} inline Condition compare(Int64 arg1, Int64 arg2, bool unsignedCompare) {return (Condition)((unsignedCompare ? (Uint64)arg1 > (Uint64)arg2 : arg1 > arg2)*(cGt-cLt) + (arg1 == arg2)*(cEq-cLt) + cLt);} inline Condition compare(Flt32 arg1, Flt32 arg2, bool) {return isNaN(arg1) || isNaN(arg2) ? cUn : (Condition)((arg1 > arg2)*(cGt-cLt) + (arg1 == arg2)*(cEq-cLt) + cLt);} inline Condition compare(Flt64 arg1, Flt64 arg2, bool) {return isNaN(arg1) || isNaN(arg2) ? cUn : (Condition)((arg1 > arg2)*(cGt-cLt) + (arg1 == arg2)*(cEq-cLt) + cLt);} inline Condition compare(addr arg1, addr arg2, bool) {return (Condition)((arg1 > arg2)*(cGt-cLt) + (arg1 == arg2)*(cEq-cLt) + cLt);} // --- INLINES ---------------------------------------------------------------- extern const ValueKind typeKindValueKinds[nTypeKinds]; // ValueKind for each TypeKind inline ValueKind typeKindToValueKind(TypeKind tk) { return typeKindValueKinds[tk]; } // // Return true if this value is not equal to zero. This is a numerical // comparison, so if this is a float or double, then this function will // return true if this value is either +0.0 or -0.0. A condition value // is considered to be zero if and only if it is cEq. // inline bool Value::isNonzero(ValueKind kind) const { switch (kind) { case vkInt: return i != 0; case vkLong: return l != 0; case vkFloat: return f != 0.0f; case vkDouble: return d != 0.0; case vkAddr: return !a; case vkCond: return c != cEq; default: return true; } } // // Return true if this and v2 are identical values. Both are assumed // to have the given kind. // Note that this is a bitwise identity comparison, not a numerical // value comparison -- for example, a floating-point NaN will compare // equal to itself, while the floating-point operator == would return // false. Moreover, comparing +0.0 with -0.0 will return false, while // using == to compare them would return true. // inline bool Value::eq(ValueKind kind, const Value &v2) const { switch (kind) { case vkInt: case vkFloat: return i == v2.i; case vkLong: case vkDouble: return l == v2.l; case vkAddr: return a == v2.a; case vkCond: return c == v2.c; default: return true; } } #endif