blob: 799a98ae5ff4b0081433bd7788c2e341fe9a95e3 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
enum class ValueStructureKind
{
Generic,
IntConstant,
Int64Constant,
IntRange,
IntBounded,
FloatConstant,
VarConstant,
JsType,
Array
};
class IntConstantValueInfo;
class Int64ConstantValueInfo;
class IntRangeValueInfo;
class IntBoundedValueInfo;
class FloatConstantValueInfo;
class VarConstantValueInfo;
class JsTypeValueInfo;
class EquivalentTypeSetValueInfo;
class ArrayValueInfo;
class ValueInfo : protected ValueType
{
private:
const ValueStructureKind structureKind;
Sym * symStore;
protected:
ValueInfo(const ValueType type, const ValueStructureKind structureKind)
: ValueType(type), structureKind(structureKind)
{
// We can only prove that the representation is a tagged int on a ToVar. Currently, we cannot have more than one value
// info per value number in a block, so a value info specifying tagged int representation cannot be created for a
// specific sym. Instead, a value info can be shared by multiple syms, and hence cannot specify tagged int
// representation. Currently, the tagged int representation info can only be carried on the dst opnd of ToVar, and can't
// even be propagated forward.
Assert(!type.IsTaggedInt());
SetSymStore(nullptr);
}
private:
ValueInfo(const ValueInfo &other, const bool)
: ValueType(other), structureKind(ValueStructureKind::Generic) // uses generic structure kind, as opposed to copying the structure kind
{
SetSymStore(other.GetSymStore());
}
public:
static ValueInfo * New(JitArenaAllocator *const alloc, const ValueType type)
{
return JitAnew(alloc, ValueInfo, type, ValueStructureKind::Generic);
}
static ValueInfo * MergeLikelyIntValueInfo(JitArenaAllocator* alloc, Value *toDataVal, Value *fromDataVal, const ValueType newValueType);
static ValueInfo * NewIntRangeValueInfo(JitArenaAllocator* alloc, int32 min, int32 max, bool wasNegativeZeroPreventedByBailout);
const ValueType & Type() const { return *this; }
ValueType & Type() { return *this; }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ValueType imports. Only importing functions that are appropriate to be called on Value.
public:
using ValueType::IsUninitialized;
using ValueType::IsDefinite;
using ValueType::IsTaggedInt;
using ValueType::IsIntAndLikelyTagged;
using ValueType::IsLikelyTaggedInt;
using ValueType::HasBeenUntaggedInt;
using ValueType::IsIntAndLikelyUntagged;
using ValueType::IsLikelyUntaggedInt;
using ValueType::HasBeenInt;
using ValueType::IsInt;
using ValueType::IsLikelyInt;
using ValueType::IsNotInt;
using ValueType::IsNotNumber;
using ValueType::HasBeenFloat;
using ValueType::IsFloat;
using ValueType::IsLikelyFloat;
using ValueType::HasBeenNumber;
using ValueType::IsNumber;
using ValueType::IsLikelyNumber;
using ValueType::HasBeenUnknownNumber;
using ValueType::IsUnknownNumber;
using ValueType::IsLikelyUnknownNumber;
using ValueType::HasBeenUndefined;
using ValueType::IsUndefined;
using ValueType::IsLikelyUndefined;
using ValueType::HasBeenNull;
using ValueType::IsNull;
using ValueType::IsLikelyNull;
using ValueType::HasBeenBoolean;
using ValueType::IsBoolean;
using ValueType::IsLikelyBoolean;
using ValueType::HasBeenString;
using ValueType::IsString;
using ValueType::HasHadStringTag;
using ValueType::IsLikelyString;
using ValueType::IsNotString;
using ValueType::HasBeenPrimitive;
using ValueType::IsPrimitive;
using ValueType::IsLikelyPrimitive;
using ValueType::HasBeenObject;
using ValueType::IsObject;
using ValueType::IsLikelyObject;
using ValueType::IsNotObject;
using ValueType::CanMergeToObject;
using ValueType::CanMergeToSpecificObjectType;
using ValueType::IsRegExp;
using ValueType::IsLikelyRegExp;
using ValueType::IsArray;
using ValueType::IsLikelyArray;
using ValueType::IsNotArray;
using ValueType::IsArrayOrObjectWithArray;
using ValueType::IsLikelyArrayOrObjectWithArray;
using ValueType::IsNotArrayOrObjectWithArray;
using ValueType::IsNativeArray;
using ValueType::IsLikelyNativeArray;
using ValueType::IsNotNativeArray;
using ValueType::IsNativeIntArray;
using ValueType::IsLikelyNativeIntArray;
using ValueType::IsNativeFloatArray;
using ValueType::IsLikelyNativeFloatArray;
using ValueType::IsTypedArray;
using ValueType::IsLikelyTypedArray;
using ValueType::IsOptimizedTypedArray;
using ValueType::IsLikelyOptimizedTypedArray;
using ValueType::IsLikelyOptimizedVirtualTypedArray;
using ValueType::IsAnyArrayWithNativeFloatValues;
using ValueType::IsLikelyAnyArrayWithNativeFloatValues;
using ValueType::IsAnyArray;
using ValueType::IsLikelyAnyArray;
using ValueType::IsAnyOptimizedArray;
using ValueType::IsLikelyAnyOptimizedArray;
// The following apply to object types only
using ValueType::GetObjectType;
// The following apply to javascript array types only
using ValueType::HasNoMissingValues;
using ValueType::HasIntElements;
using ValueType::HasFloatElements;
using ValueType::HasVarElements;
using ValueType::IsSimd128;
using ValueType::IsSimd128Float32x4;
using ValueType::IsSimd128Int32x4;
using ValueType::IsSimd128Float64x2;
using ValueType::IsLikelySimd128;
using ValueType::IsLikelySimd128Float32x4;
using ValueType::IsLikelySimd128Int32x4;
using ValueType::IsLikelySimd128Float64x2;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public:
bool IsGeneric() const;
private:
bool IsIntConstant() const;
bool IsInt64Constant() const;
const IntConstantValueInfo * AsIntConstant() const;
const Int64ConstantValueInfo * AsInt64Constant() const;
bool IsIntRange() const;
const IntRangeValueInfo * AsIntRange() const;
public:
bool IsIntBounded() const;
const IntBoundedValueInfo * AsIntBounded() const;
bool IsFloatConstant() const;
FloatConstantValueInfo * AsFloatConstant();
const FloatConstantValueInfo * AsFloatConstant() const;
bool IsVarConstant() const;
VarConstantValueInfo * AsVarConstant();
bool IsJsType() const;
JsTypeValueInfo * AsJsType();
const JsTypeValueInfo * AsJsType() const;
#if FALSE
bool IsObjectType() const;
JsTypeValueInfo * AsObjectType();
bool IsEquivalentTypeSet() const;
EquivalentTypeSetValueInfo * AsEquivalentTypeSet();
#endif
bool IsArrayValueInfo() const;
const ArrayValueInfo * AsArrayValueInfo() const;
ArrayValueInfo * AsArrayValueInfo();
public:
bool HasIntConstantValue(const bool includeLikelyInt = false) const;
bool TryGetIntConstantValue(int32 *const intValueRef, const bool includeLikelyInt = false) const;
bool TryGetIntConstantValue(int64 *const intValueRef, const bool isUnsigned) const;
bool TryGetInt64ConstantValue(int64 *const intValueRef, const bool isUnsigned) const;
bool TryGetIntConstantLowerBound(int32 *const intConstantBoundRef, const bool includeLikelyInt = false) const;
bool TryGetIntConstantUpperBound(int32 *const intConstantBoundRef, const bool includeLikelyInt = false) const;
bool TryGetIntConstantBounds(IntConstantBounds *const intConstantBoundsRef, const bool includeLikelyInt = false) const;
bool WasNegativeZeroPreventedByBailout() const;
public:
static bool IsEqualTo(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2);
static bool IsNotEqualTo(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2);
private:
static bool IsEqualTo_NoConverse(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2);
static bool IsNotEqualTo_NoConverse(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2);
public:
static bool IsGreaterThanOrEqualTo(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2);
static bool IsGreaterThan(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2);
static bool IsLessThanOrEqualTo(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2);
static bool IsLessThan(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2);
public:
static bool IsGreaterThanOrEqualTo(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2, const int src2Offset);
static bool IsLessThanOrEqualTo(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2, const int src2Offset);
private:
static bool IsGreaterThanOrEqualTo_NoConverse(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2, const int src2Offset);
static bool IsLessThanOrEqualTo_NoConverse(const Value *const src1Value, const int32 min1, const int32 max1, const Value *const src2Value, const int32 min2, const int32 max2, const int src2Offset);
public:
ValueInfo *SpecializeToInt32(JitArenaAllocator *const allocator, const bool isForLoopBackEdgeCompensation = false);
ValueInfo *SpecializeToFloat64(JitArenaAllocator *const allocator);
// SIMD_JS
ValueInfo *SpecializeToSimd128(IRType type, JitArenaAllocator *const allocator);
ValueInfo *SpecializeToSimd128F4(JitArenaAllocator *const allocator);
ValueInfo *SpecializeToSimd128I4(JitArenaAllocator *const allocator);
public:
Sym * GetSymStore() const { return this->symStore; }
void SetSymStore(Sym * sym)
{
// Sym store should always be a var sym
Assert(sym == nullptr || sym->IsPropertySym() || !sym->AsStackSym()->IsTypeSpec()); // property syms always have a var stack sym
this->symStore = sym;
}
bool GetIsShared() const;
void SetIsShared();
ValueInfo * Copy(JitArenaAllocator * allocator);
ValueInfo * CopyWithGenericStructureKind(JitArenaAllocator * allocator) const
{
return JitAnew(allocator, ValueInfo, *this, false);
}
bool GetIntValMinMax(int *pMin, int *pMax, bool doAggressiveIntTypeSpec);
#if DBG_DUMP
void Dump();
#endif
#if DBG
// Add a vtable in debug builds so that the actual can been inspected easily in the debugger without having to manually cast
virtual void AddVtable() { Assert(false); }
#endif
};
class Value
{
private:
const ValueNumber valueNumber;
ValueInfo *valueInfo;
protected:
Value(const ValueNumber valueNumber, ValueInfo *valueInfo)
: valueNumber(valueNumber), valueInfo(valueInfo)
{
};
public:
static Value *New(JitArenaAllocator *const allocator, const ValueNumber valueNumber, ValueInfo *valueInfo)
{
return JitAnew(allocator, Value, valueNumber, valueInfo);
}
ValueNumber GetValueNumber() const { return this->valueNumber; }
ValueInfo * GetValueInfo() { return this->valueInfo; }
ValueInfo const * GetValueInfo() const { return this->valueInfo; }
ValueInfo * ShareValueInfo() const { this->valueInfo->SetIsShared(); return this->valueInfo; }
void SetValueInfo(ValueInfo * newValueInfo) { Assert(newValueInfo); this->valueInfo = newValueInfo; }
Value * Copy(JitArenaAllocator * allocator, ValueNumber newValueNumber) const { return Value::New(allocator, newValueNumber, this->ShareValueInfo()); }
#if DBG_DUMP
_NOINLINE void Dump() const { Output::Print(_u("0x%X ValueNumber: %3d, -> "), this, this->valueNumber); this->valueInfo->Dump(); }
#endif
};
template<> inline
ValueNumber JsUtil::ValueToKey<ValueNumber, Value *>::ToKey(Value *const &value)
{
Assert(value);
return value->GetValueNumber();
}
template <typename T, typename U, ValueStructureKind kind>
class _IntConstantValueInfo : public ValueInfo
{
private:
const T intValue;
public:
static U *New(JitArenaAllocator *const allocator, const T intValue)
{
return JitAnew(allocator, U, intValue);
}
U *Copy(JitArenaAllocator *const allocator) const
{
return JitAnew(allocator, U, *((U*)this));
}
_IntConstantValueInfo(const T intValue)
: ValueInfo(GetInt(IsTaggable(intValue)), kind),
intValue(intValue)
{}
static bool IsTaggable(const T i)
{
return U::IsTaggable(i);
}
T IntValue() const
{
return intValue;
}
};
class IntConstantValueInfo : public _IntConstantValueInfo<int, IntConstantValueInfo, ValueStructureKind::IntConstant>
{
public:
static IntConstantValueInfo *New(JitArenaAllocator *const allocator, const int intValue)
{
return _IntConstantValueInfo::New(allocator, intValue);
}
private:
IntConstantValueInfo(int value) : _IntConstantValueInfo(value) {};
static bool IsTaggable(const int i)
{
#if INT32VAR
// All 32-bit ints are taggable on 64-bit architectures
return true;
#else
return i >= Js::Constants::Int31MinValue && i <= Js::Constants::Int31MaxValue;
#endif
}
friend _IntConstantValueInfo;
};
class Int64ConstantValueInfo : public _IntConstantValueInfo<int64, Int64ConstantValueInfo, ValueStructureKind::Int64Constant>
{
public:
static Int64ConstantValueInfo *New(JitArenaAllocator *const allocator, const int64 intValue)
{
return _IntConstantValueInfo::New(allocator, intValue);
}
private:
static bool IsTaggable(const int64 i) { return false; }
Int64ConstantValueInfo(int64 value) : _IntConstantValueInfo(value) {};
friend _IntConstantValueInfo;
};
class IntRangeValueInfo : public ValueInfo, public IntConstantBounds
{
private:
// Definitely-int values are inherently not negative zero. This member variable, if true, indicates that this value was
// produced by an int-specialized instruction that prevented a negative zero result using a negative zero bailout
// (BailOutOnNegativeZero). Negative zero tracking in the dead-store phase tracks this information to see if some of these
// negative zero bailout checks can be removed.
bool wasNegativeZeroPreventedByBailout;
protected:
IntRangeValueInfo(
const IntConstantBounds &constantBounds,
const bool wasNegativeZeroPreventedByBailout,
const ValueStructureKind structureKind = ValueStructureKind::IntRange)
: ValueInfo(constantBounds.GetValueType(), structureKind),
IntConstantBounds(constantBounds),
wasNegativeZeroPreventedByBailout(wasNegativeZeroPreventedByBailout)
{
Assert(!wasNegativeZeroPreventedByBailout || constantBounds.LowerBound() <= 0 && constantBounds.UpperBound() >= 0);
}
public:
static IntRangeValueInfo *New(
JitArenaAllocator *const allocator,
const int32 lowerBound,
const int32 upperBound,
const bool wasNegativeZeroPreventedByBailout)
{
return
JitAnew(
allocator,
IntRangeValueInfo,
IntConstantBounds(lowerBound, upperBound),
wasNegativeZeroPreventedByBailout);
}
IntRangeValueInfo *Copy(JitArenaAllocator *const allocator) const
{
return JitAnew(allocator, IntRangeValueInfo, *this);
}
public:
bool WasNegativeZeroPreventedByBailout() const
{
return wasNegativeZeroPreventedByBailout;
}
};
class FloatConstantValueInfo : public ValueInfo
{
private:
const FloatConstType floatValue;
public:
FloatConstantValueInfo(const FloatConstType floatValue)
: ValueInfo(Float, ValueStructureKind::FloatConstant), floatValue(floatValue)
{
}
static FloatConstantValueInfo *New(
JitArenaAllocator *const allocator,
const FloatConstType floatValue)
{
return JitAnew(allocator, FloatConstantValueInfo, floatValue);
}
FloatConstantValueInfo *Copy(JitArenaAllocator *const allocator) const
{
return JitAnew(allocator, FloatConstantValueInfo, *this);
}
public:
FloatConstType FloatValue() const
{
return floatValue;
}
};
class VarConstantValueInfo : public ValueInfo
{
private:
Js::Var const varValue;
Js::Var const localVarValue;
bool isFunction;
public:
VarConstantValueInfo(Js::Var varValue, ValueType valueType, bool isFunction = false, Js::Var localVarValue = nullptr)
: ValueInfo(valueType, ValueStructureKind::VarConstant),
varValue(varValue), localVarValue(localVarValue), isFunction(isFunction)
{
}
static VarConstantValueInfo *New(JitArenaAllocator *const allocator, Js::Var varValue, ValueType valueType, bool isFunction = false, Js::Var localVarValue = nullptr)
{
return JitAnew(allocator, VarConstantValueInfo, varValue, valueType, isFunction, localVarValue);
}
VarConstantValueInfo *Copy(JitArenaAllocator *const allocator) const
{
return JitAnew(allocator, VarConstantValueInfo, *this);
}
public:
Js::Var VarValue(bool useLocal = false) const
{
if(useLocal && this->localVarValue)
{
return this->localVarValue;
}
else
{
return this->varValue;
}
}
bool IsFunction() const
{
return this->isFunction;
}
};
struct ObjectTypePropertyEntry
{
ObjTypeSpecFldInfo* fldInfo;
uint blockNumber;
};
typedef JsUtil::BaseDictionary<Js::PropertyId, ObjectTypePropertyEntry, JitArenaAllocator> ObjectTypePropertyMap;
class JsTypeValueInfo : public ValueInfo
{
private:
JITTypeHolder jsType;
Js::EquivalentTypeSet * jsTypeSet;
bool isShared;
public:
JsTypeValueInfo(JITTypeHolder type)
: ValueInfo(Uninitialized, ValueStructureKind::JsType),
jsType(type), jsTypeSet(nullptr), isShared(false)
{
}
JsTypeValueInfo(Js::EquivalentTypeSet * typeSet)
: ValueInfo(Uninitialized, ValueStructureKind::JsType),
jsType(nullptr), jsTypeSet(typeSet), isShared(false)
{
}
JsTypeValueInfo(const JsTypeValueInfo& other)
: ValueInfo(Uninitialized, ValueStructureKind::JsType),
jsType(other.jsType), jsTypeSet(other.jsTypeSet)
{
}
static JsTypeValueInfo * New(JitArenaAllocator *const allocator, JITTypeHolder typeSet)
{
return JitAnew(allocator, JsTypeValueInfo, typeSet);
}
static JsTypeValueInfo * New(JitArenaAllocator *const allocator, Js::EquivalentTypeSet * typeSet)
{
return JitAnew(allocator, JsTypeValueInfo, typeSet);
}
JsTypeValueInfo(const JITTypeHolder type, Js::EquivalentTypeSet * typeSet)
: ValueInfo(Uninitialized, ValueStructureKind::JsType),
jsType(type), jsTypeSet(typeSet), isShared(false)
{
}
static JsTypeValueInfo * New(JitArenaAllocator *const allocator, const JITTypeHolder type, Js::EquivalentTypeSet * typeSet)
{
return JitAnew(allocator, JsTypeValueInfo, type, typeSet);
}
public:
JsTypeValueInfo * Copy(JitArenaAllocator *const allocator) const
{
JsTypeValueInfo * newInfo = JitAnew(allocator, JsTypeValueInfo, *this);
newInfo->isShared = false;
return newInfo;
}
JITTypeHolder GetJsType() const
{
return this->jsType;
}
void SetJsType(const JITTypeHolder value)
{
Assert(!this->isShared);
this->jsType = value;
}
Js::EquivalentTypeSet * GetJsTypeSet() const
{
return this->jsTypeSet;
}
void SetJsTypeSet(Js::EquivalentTypeSet * value)
{
Assert(!this->isShared);
this->jsTypeSet = value;
}
bool GetIsShared() const { return this->isShared; }
void SetIsShared() { this->isShared = true; }
};
class ArrayValueInfo : public ValueInfo
{
private:
StackSym *const headSegmentSym;
StackSym *const headSegmentLengthSym;
StackSym *const lengthSym;
private:
ArrayValueInfo(
const ValueType valueType,
StackSym *const headSegmentSym,
StackSym *const headSegmentLengthSym,
StackSym *const lengthSym,
Sym *const symStore = nullptr)
: ValueInfo(valueType, ValueStructureKind::Array),
headSegmentSym(headSegmentSym),
headSegmentLengthSym(headSegmentLengthSym),
lengthSym(lengthSym)
{
Assert(valueType.IsAnyOptimizedArray());
Assert(!(valueType.IsLikelyTypedArray() && !valueType.IsOptimizedTypedArray()));
// For typed arrays, the head segment length is the same as the array length. For objects with internal arrays, the
// length behaves like a regular object's property rather than like an array length.
Assert(!lengthSym || valueType.IsLikelyArray());
Assert(!lengthSym || lengthSym != headSegmentLengthSym);
if(symStore)
{
SetSymStore(symStore);
}
}
public:
static ArrayValueInfo *New(
JitArenaAllocator *const allocator,
const ValueType valueType,
StackSym *const headSegmentSym,
StackSym *const headSegmentLengthSym,
StackSym *const lengthSym,
Sym *const symStore = nullptr)
{
Assert(allocator);
return JitAnew(allocator, ArrayValueInfo, valueType, headSegmentSym, headSegmentLengthSym, lengthSym, symStore);
}
ValueInfo *Copy(
JitArenaAllocator *const allocator,
const bool copyHeadSegment = true,
const bool copyHeadSegmentLength = true,
const bool copyLength = true) const
{
Assert(allocator);
return
(copyHeadSegment && headSegmentSym) || (copyHeadSegmentLength && headSegmentLengthSym) || (copyLength && lengthSym)
? New(
allocator,
Type(),
copyHeadSegment ? headSegmentSym : nullptr,
copyHeadSegmentLength ? headSegmentLengthSym : nullptr,
copyLength ? lengthSym : nullptr,
GetSymStore())
: CopyWithGenericStructureKind(allocator);
}
public:
StackSym *HeadSegmentSym() const
{
return headSegmentSym;
}
StackSym *HeadSegmentLengthSym() const
{
return headSegmentLengthSym;
}
StackSym *LengthSym() const
{
return lengthSym;
}
IR::ArrayRegOpnd *CreateOpnd(
IR::RegOpnd *const previousArrayOpnd,
const bool needsHeadSegment,
const bool needsHeadSegmentLength,
const bool needsLength,
const bool eliminatedLowerBoundCheck,
const bool eliminatedUpperBoundCheck,
Func *const func) const
{
Assert(previousArrayOpnd);
Assert(func);
return
IR::ArrayRegOpnd::New(
previousArrayOpnd,
Type(),
needsHeadSegment ? headSegmentSym : nullptr,
needsHeadSegmentLength ? headSegmentLengthSym : nullptr,
needsLength ? lengthSym : nullptr,
eliminatedLowerBoundCheck,
eliminatedUpperBoundCheck,
func);
}
};