Skip to content

Commit

Permalink
[MERGE #4816 @MikeHolman] Add specialized types of Sets and Maps
Browse files Browse the repository at this point in the history
Merge pull request #4816 from MikeHolman:setopt_pr

This change separates sets into three different types: Int, Simple, and Complex.

Int sets are sets which contain only int values. They are represented with a bit vector.
Simple sets contain values that are comparable by pointer alone, and use a normal dictionary.
Complex sets are sets that need full value comparison, and use a dictionary with a custom comparator.

I also added two kinds of maps, Simple and Complex, which function the same as simple and complex sets.

Improves perf of ARES-6 by 6% and VueJS by about 4%.
  • Loading branch information
MikeHolman committed Mar 20, 2018
2 parents adf0ecb + f9639b7 commit e8c4ae6
Show file tree
Hide file tree
Showing 16 changed files with 1,332 additions and 586 deletions.
2 changes: 1 addition & 1 deletion lib/Backend/JITRecyclableObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class JITJavascriptString : JITRecyclableObject
return m_charLength;
}

static bool Equals(Js::Var aLeft, Js::Var aRight)
static bool Equals(JITJavascriptString* aLeft, JITJavascriptString* aRight)
{
return Js::JavascriptStringHelpers<JITJavascriptString>::Equals(aLeft, aRight);
}
Expand Down
23 changes: 12 additions & 11 deletions lib/Runtime/Language/JavascriptConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,19 @@ namespace Js
template<bool zero>
bool JavascriptConversion::SameValueCommon(Var aLeft, Var aRight)
{
if (aLeft == aRight)
{
return true;
}

TypeId leftType = JavascriptOperators::GetTypeId(aLeft);
TypeId rightType = JavascriptOperators::GetTypeId(aRight);

if (JavascriptOperators::IsUndefinedOrNullType(leftType))
{
return leftType == rightType;
return false;
}

TypeId rightType = JavascriptOperators::GetTypeId(aRight);
double dblLeft, dblRight;

switch (leftType)
Expand All @@ -70,7 +75,7 @@ namespace Js
switch (rightType)
{
case TypeIds_Integer:
return aLeft == aRight;
return false;
case TypeIds_Number:
dblLeft = TaggedInt::ToDouble(aLeft);
dblRight = JavascriptNumber::GetValue(aRight);
Expand Down Expand Up @@ -181,17 +186,12 @@ namespace Js
}
break;
case TypeIds_Boolean:
switch (rightType)
{
case TypeIds_Boolean:
return aLeft == aRight;
}
break;
return false;
case TypeIds_String:
switch (rightType)
{
case TypeIds_String:
return JavascriptString::Equals(aLeft, aRight);
return JavascriptString::Equals(JavascriptString::UnsafeFromVar(aLeft), JavascriptString::UnsafeFromVar(aRight));
}
break;
case TypeIds_Symbol:
Expand All @@ -208,7 +208,7 @@ namespace Js
default:
break;
}
return aLeft == aRight;
return false;
}

template bool JavascriptConversion::SameValueCommon<false>(Var aLeft, Var aRight);
Expand Down Expand Up @@ -1521,4 +1521,5 @@ namespace Js

return NumberUtilities::TryToInt64(length);
}

} // namespace Js
10 changes: 10 additions & 0 deletions lib/Runtime/Language/JavascriptConversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,17 @@ namespace Js {
static double LongToDouble(__int64 aValue);
static double ULongToDouble(unsigned __int64 aValue);

template <bool allowNegOne, bool allowLossyConversion>
static Var TryCanonicalizeAsTaggedInt(Var value);
template <bool allowNegOne, bool allowLossyConversion>
static Var TryCanonicalizeAsTaggedInt(Var value, TypeId typeId);
template <bool allowLossyConversion>
static Var TryCanonicalizeAsSimpleVar(Var value);

private:
template <typename T, bool allowNegOne>
static Var TryCanonicalizeIntHelper(T val);

static BOOL ToInt32Finite(double value, int32* result);
template<bool zero>
static bool SameValueCommon(Var aValue, Var bValue);
Expand Down
105 changes: 105 additions & 0 deletions lib/Runtime/Language/JavascriptConversion.inl
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,109 @@ namespace Js {
return SameValueCommon<true>(aValue, bValue);
}

template <typename T, bool allowNegOne>
inline Var JavascriptConversion::TryCanonicalizeIntHelper(T val)
{
if (TaggedInt::IsOverflow(val))
{
return nullptr;
}

if (!allowNegOne && val == -1)
{
return nullptr;
}

return TaggedInt::ToVarUnchecked((int)val);
}

template <bool allowNegOne, bool allowLossyConversion>
inline Var JavascriptConversion::TryCanonicalizeAsTaggedInt(Var value, TypeId typeId)
{
switch (typeId)
{
case TypeIds_Integer:
return (allowNegOne || value != TaggedInt::ToVarUnchecked(-1))
? value
: nullptr;

case TypeIds_Number:
{
double doubleVal = JavascriptNumber::GetValue(value);
int32 intVal = 0;

if (!JavascriptNumber::TryGetInt32Value<allowLossyConversion>(doubleVal, &intVal))
{
return nullptr;
}
return TryCanonicalizeIntHelper<int32, allowNegOne>(intVal);
}
case TypeIds_Int64Number:
{
if (!allowLossyConversion)
{
return nullptr;
}
int64 int64Val = JavascriptInt64Number::UnsafeFromVar(value)->GetValue();

return TryCanonicalizeIntHelper<int64, allowNegOne>(int64Val);

}
case TypeIds_UInt64Number:
{
if (!allowLossyConversion)
{
return nullptr;
}
uint64 uint64Val = JavascriptUInt64Number::UnsafeFromVar(value)->GetValue();

return TryCanonicalizeIntHelper<uint64, allowNegOne>(uint64Val);
}
default:
return nullptr;
}
}

template <bool allowNegOne, bool allowLossyConversion>
inline Var JavascriptConversion::TryCanonicalizeAsTaggedInt(Var value)
{
TypeId typeId = JavascriptOperators::GetTypeId(value);
return TryCanonicalizeAsTaggedInt<allowNegOne, allowLossyConversion>(value, typeId);
}

// Lossy conversion means values are StrictEqual equivalent,
// but we cannot reconstruct the original value after canonicalization
// (e.g. -0 or an Int64Number object)
template <bool allowLossyConversion>
inline Var JavascriptConversion::TryCanonicalizeAsSimpleVar(Var value)
{
TypeId typeId = JavascriptOperators::GetTypeId(value);
switch (typeId)
{
case TypeIds_Integer:
case TypeIds_Number:
case TypeIds_Int64Number:
case TypeIds_UInt64Number:
{
Var taggedInt = TryCanonicalizeAsTaggedInt<true, allowLossyConversion>(value, typeId);
if (taggedInt)
{
return taggedInt;
}

#if FLOATVAR
return value;
#else
return nullptr;
#endif
}
case TypeIds_String:
case TypeIds_Symbol:
return nullptr;

default:
return value;
}
}

} // namespace Js
16 changes: 8 additions & 8 deletions lib/Runtime/Language/JavascriptOperators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -746,14 +746,14 @@ namespace Js
return dblLeft < dblRight;
}

BOOL JavascriptOperators::StrictEqualString(Var aLeft, Var aRight)
BOOL JavascriptOperators::StrictEqualString(Var aLeft, JavascriptString* aRight)
{
Assert(JavascriptOperators::GetTypeId(aRight) == TypeIds_String);

if (JavascriptOperators::GetTypeId(aLeft) != TypeIds_String)
JavascriptString* leftStr = TryFromVar<JavascriptString>(aLeft);
if (!leftStr)
{
return false;

return JavascriptString::Equals(aLeft, aRight);
}
return JavascriptString::Equals(leftStr, aRight);
}

BOOL JavascriptOperators::StrictEqualEmptyString(Var aLeft)
Expand Down Expand Up @@ -785,7 +785,7 @@ namespace Js
switch (rightType)
{
case TypeIds_String:
return JavascriptString::Equals(aLeft, aRight);
return JavascriptString::Equals(JavascriptString::UnsafeFromVar(aLeft), JavascriptString::UnsafeFromVar(aRight));
}
return FALSE;
case TypeIds_Integer:
Expand Down Expand Up @@ -5188,7 +5188,7 @@ namespace Js
return JavascriptBoolean::ToVar(JavascriptOperators::StrictEqual(a, b, scriptContext), scriptContext);
}

Var JavascriptOperators::OP_CmSrEq_String(Var a, Var b, ScriptContext *scriptContext)
Var JavascriptOperators::OP_CmSrEq_String(Var a, JavascriptString* b, ScriptContext *scriptContext)
{
return JavascriptBoolean::ToVar(JavascriptOperators::StrictEqualString(a, b), scriptContext);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/Runtime/Language/JavascriptOperators.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ namespace Js
static BOOL LessEqual(Var aLeft, Var aRight,ScriptContext* scriptContext);
static BOOL NotEqual(Var aLeft, Var aRight,ScriptContext* scriptContext);
static BOOL StrictEqual(Var aLeft, Var aRight,ScriptContext* scriptContext);
static BOOL StrictEqualString(Var aLeft, Var aRight);
static BOOL StrictEqualString(Var aLeft, JavascriptString* aRight);
static BOOL StrictEqualEmptyString(Var aLeft);
static BOOL NotStrictEqual(Var aLeft, Var aRight,ScriptContext* scriptContext);

Expand Down Expand Up @@ -400,7 +400,7 @@ namespace Js
static Var OP_CmEq_A(Js::Var a,Js::Var b,ScriptContext* scriptContext);
static Var OP_CmNeq_A(Js::Var a,Js::Var b,ScriptContext* scriptContext);
static Var OP_CmSrEq_A(Js::Var a,Js::Var b,ScriptContext* scriptContext);
static Var OP_CmSrEq_String(Var a, Var b, ScriptContext *scriptContext);
static Var OP_CmSrEq_String(Var a, JavascriptString* b, ScriptContext *scriptContext);
static Var OP_CmSrEq_EmptyString(Var a, ScriptContext *scriptContext);
static Var OP_CmSrNeq_A(Js::Var a,Js::Var b,ScriptContext* scriptContext);
static Var OP_CmLt_A(Js::Var a,Js::Var b,ScriptContext* scriptContext);
Expand Down
2 changes: 1 addition & 1 deletion lib/Runtime/Library/JavascriptLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5432,7 +5432,7 @@ namespace Js
AssertOrFailFast(hasRight);

// If the strings at this index are not equal, the callsite objects are not equal.
if (!Js::JavascriptString::Equals(varLeft, varRight))
if (!Js::JavascriptString::Equals(JavascriptString::FromVar(varLeft), JavascriptString::FromVar(varRight)))
{
return false;
}
Expand Down
Loading

0 comments on commit e8c4ae6

Please sign in to comment.