Skip to content

Commit

Permalink
Cherry-pick ea191c9. rdar://114103295
Browse files Browse the repository at this point in the history
    [JSC] Make speculationFromValue cheap
    https://bugs.webkit.org/show_bug.cgi?id=259633
    rdar://113098188

    Reviewed by Mark Lam.

    This patch makes speculationFromValue extremely cheap by using JSType based SpeculatedType lookup.
    This function can be called from operationOptimize & GC End phase. And this sometimes takes very long
    time due to huge size of functions. Thus making this function extremely cheap is particularly important
    to make startup time faster. In particular, we observed massive sampling points for speculationFromValue
    in startup times in Speedometer2.1, Speedometer3.0, JetStream2.1, and JetStream3.0.

    1. This patch removes SpecFunctionWithDefaultHasInstance and SpecFunctionWithNonDefaultHasInstance.
       We can just use existing Structure iteration and that can cover almost all possible cases. These
       type makes speculationFromValue costly for JSFunction and derived classes, so rather not so great.
    2. This patch makes some functions JS_EXPORT_PRIVATE to make speculationFromValue non-JS_EXPORT_PRIVATE.
       So we can allow LTO / PGO to inline this if it is useful.
    3. We define JSType via macro, and we list up corresponding SpeculatedType at that time. This allows us
       to construct a mapping between them easily, and this makes speculationFromValue just array lookup,
       which becomes extremely fast.

    * Source/JavaScriptCore/bytecode/SpeculatedType.cpp:
    (JSC::=):
    (JSC::speculationFromStructure):
    (JSC::speculationFromCell):
    * Source/JavaScriptCore/bytecode/SpeculatedType.h:
    * Source/JavaScriptCore/dfg/DFGAbstractValue.cpp:
    (JSC::DFG::AbstractValue::validateOSREntryValue const):
    (JSC::DFG::AbstractValue::validateTypeAcceptingBoxedInt52 const):
    * Source/JavaScriptCore/dfg/DFGAbstractValue.h:
    (JSC::DFG::AbstractValue::observeIndexingTypeTransition):
    (JSC::DFG::AbstractValue::validateOSREntryValue const): Deleted.
    (JSC::DFG::AbstractValue::validateTypeAcceptingBoxedInt52 const): Deleted.
    * Source/JavaScriptCore/dfg/DFGInsertionSet.cpp:
    (JSC::DFG::InsertionSet::insertConstant):
    * Source/JavaScriptCore/dfg/DFGInsertionSet.h:
    * Source/JavaScriptCore/llint/LowLevelInterpreter64.asm:
    * Source/JavaScriptCore/runtime/JSType.h:

    Canonical link: https://commits.webkit.org/266422@main

Identifier: 265870.344@safari-7616.1.27.111-branch
  • Loading branch information
Constellation authored and Dan Robson committed Aug 18, 2023
1 parent ba27ec3 commit a4bb359
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 293 deletions.
117 changes: 16 additions & 101 deletions Source/JavaScriptCore/bytecode/SpeculatedType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,12 +519,8 @@ SpeculatedType speculationFromClassInfoInheritance(const ClassInfo* classInfo)
if (classInfo->isSubClassOf(JSArray::info()))
return SpecArray | SpecDerivedArray;

static_assert(std::is_final_v<JSBoundFunction>);
if (classInfo == JSBoundFunction::info())
return SpecFunctionWithNonDefaultHasInstance;

if (classInfo->isSubClassOf(JSFunction::info()))
return SpecFunctionWithDefaultHasInstance;
return SpecFunction;

if (classInfo->isSubClassOf(JSPromise::info()))
return SpecPromiseObject;
Expand All @@ -542,92 +538,20 @@ SpeculatedType speculationFromClassInfoInheritance(const ClassInfo* classInfo)
return SpecCellOther;
}

using SpeculationMapping = std::array<SpeculatedType, static_cast<unsigned>(UINT8_MAX) + 1>;
static constexpr SpeculationMapping speculatedTypeMapping = ([]() -> SpeculationMapping {
SpeculationMapping result { };
result.fill(SpecObjectOther);
#define JSC_DEFINE_JS_TYPE(type, speculatedType) result[type] = speculatedType;
FOR_EACH_JS_TYPE(JSC_DEFINE_JS_TYPE)
#undef JSC_DEFINE_JS_TYPE
return result;
})();

SpeculatedType speculationFromStructure(Structure* structure)
{
SpeculatedType filteredResult = SpecNone;
JSType type = structure->typeInfo().type();
switch (type) {
case StringType:
filteredResult = SpecString;
break;
case SymbolType:
filteredResult = SpecSymbol;
break;
case HeapBigIntType:
filteredResult = SpecHeapBigInt;
break;
case FinalObjectType:
filteredResult = SpecFinalObject;
break;
case DirectArgumentsType:
filteredResult = SpecDirectArguments;
break;
case ScopedArgumentsType:
filteredResult = SpecScopedArguments;
break;
case RegExpObjectType:
filteredResult = SpecRegExpObject;
break;
case JSDateType:
filteredResult = SpecDateObject;
break;
case JSMapType:
filteredResult = SpecMapObject;
break;
case JSSetType:
filteredResult = SpecSetObject;
break;
case JSWeakMapType:
filteredResult = SpecWeakMapObject;
break;
case JSWeakSetType:
filteredResult = SpecWeakSetObject;
break;
case ProxyObjectType:
filteredResult = SpecProxyObject;
break;
case DataViewType:
filteredResult = SpecDataViewObject;
break;
case DerivedArrayType:
filteredResult = SpecDerivedArray;
break;
case ArrayType:
filteredResult = SpecArray;
break;
case StringObjectType:
filteredResult = SpecStringObject;
break;
// We do not want to accept String.prototype in StringObjectUse, so that we do not include it as SpecStringObject.
case DerivedStringObjectType:
filteredResult = SpecObjectOther;
break;
case JSPromiseType:
filteredResult = SpecPromiseObject;
break;
case JSFunctionType:
static_assert(std::is_final_v<JSBoundFunction>);
if (structure->classInfoForCells() == JSBoundFunction::info())
filteredResult = SpecFunctionWithNonDefaultHasInstance;
else
filteredResult = SpecFunctionWithDefaultHasInstance;
break;

#define JSC_TYPED_ARRAY_CHECK(type) \
case type##ArrayType: \
filteredResult = Spec ## type ## Array; \
break;
FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(JSC_TYPED_ARRAY_CHECK)
#undef JSC_TYPED_ARRAY_CHECK

default:
if (!isObjectType(type))
return SpecCellOther;
return speculationFromClassInfoInheritance(structure->classInfoForCells());
}
ASSERT(filteredResult);
ASSERT(isSubtypeSpeculation(filteredResult, speculationFromClassInfoInheritance(structure->classInfoForCells())));
return filteredResult;
return speculatedTypeMapping[type];
}

SpeculatedType speculationFromCell(JSCell* cell)
Expand All @@ -637,6 +561,7 @@ SpeculatedType speculationFromCell(JSCell* cell)
ASSERT_NOT_REACHED();
return SpecNone;
}

if (cell->isString()) {
JSString* string = jsCast<JSString*>(cell);
if (const StringImpl* impl = string->tryGetValueImpl()) {
Expand All @@ -649,19 +574,9 @@ SpeculatedType speculationFromCell(JSCell* cell)
}
return SpecString;
}
// FIXME: rdar://69036888: undo this when no longer needed.
auto* structure = cell->structureID().tryDecode();
if (UNLIKELY(!Integrity::isSanePointer(structure))) {
ASSERT_NOT_REACHED();
return SpecNone;
}
#if !USE(SYSTEM_MALLOC)
if (UNLIKELY(Gigacage::contains(structure))) {
ASSERT_NOT_REACHED();
return SpecNone;
}
#endif
return speculationFromStructure(structure);

JSType type = cell->type();
return speculatedTypeMapping[type];
}

SpeculatedType speculationFromValue(JSValue value)
Expand Down
6 changes: 2 additions & 4 deletions Source/JavaScriptCore/bytecode/SpeculatedType.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ typedef uint64_t SpeculatedType;
static constexpr SpeculatedType SpecNone = 0; // We don't know anything yet.
static constexpr SpeculatedType SpecFinalObject = 1ull << 0; // It's definitely a JSFinalObject.
static constexpr SpeculatedType SpecArray = 1ull << 1; // It's definitely a JSArray.
static constexpr SpeculatedType SpecFunctionWithDefaultHasInstance = 1ull << 2; // It's definitely a JSFunction that has its ImplementsDefaultHasInstance type info flags bit set.
static constexpr SpeculatedType SpecFunctionWithNonDefaultHasInstance = 1ull << 3; // It's definitely a JSFunction that does not have its ImplementsDefaultHasInstance type info flags bit set.
static constexpr SpeculatedType SpecFunction = SpecFunctionWithDefaultHasInstance | SpecFunctionWithNonDefaultHasInstance; // It's definitely a JSFunction.
static constexpr SpeculatedType SpecFunction = 1ull << 2; // It's definitely a JSFunction.
static constexpr SpeculatedType SpecInt8Array = 1ull << 4; // It's definitely an Int8Array or one of its subclasses.
static constexpr SpeculatedType SpecInt16Array = 1ull << 5; // It's definitely an Int16Array or one of its subclasses.
static constexpr SpeculatedType SpecInt32Array = 1ull << 6; // It's definitely an Int32Array or one of its subclasses.
Expand Down Expand Up @@ -521,7 +519,7 @@ SpeculatedType speculationFromClassInfoInheritance(const ClassInfo*);

SpeculatedType speculationFromStructure(Structure*);
SpeculatedType speculationFromCell(JSCell*);
JS_EXPORT_PRIVATE SpeculatedType speculationFromValue(JSValue);
SpeculatedType speculationFromValue(JSValue);
// If it's an anyInt(), it'll return speculated types from the Int52 lattice.
// Otherwise, it'll return types from the JSValue lattice.
JS_EXPORT_PRIVATE SpeculatedType int52AwareSpeculationFromValue(JSValue);
Expand Down
6 changes: 0 additions & 6 deletions Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Original file line number Diff line number Diff line change
Expand Up @@ -4946,12 +4946,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
const AbstractValue& abstractValue = forNode(node->child1());
unsigned bits = node->typeInfoOperand();
ASSERT(bits);
if (bits == ImplementsDefaultHasInstance) {
if (abstractValue.m_type == SpecFunctionWithDefaultHasInstance) {
m_state.setShouldTryConstantFolding(true);
break;
}
}

if (JSValue value = abstractValue.value()) {
if (value.isCell()) {
Expand Down
57 changes: 57 additions & 0 deletions Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,63 @@ void AbstractValue::fastForwardToSlow(AbstractValueClobberEpoch newEpoch)
checkConsistency();
}

bool AbstractValue::validateOSREntryValue(JSValue value, FlushFormat format) const
{
if (isBytecodeTop())
return true;

if (format == FlushedInt52) {
if (!isInt52Any())
return false;

if (!validateTypeAcceptingBoxedInt52(value))
return false;

if (!!m_value) {
ASSERT(m_value.isAnyInt());
ASSERT(value.isAnyInt());
if (jsDoubleNumber(m_value.asAnyInt()) != jsDoubleNumber(value.asAnyInt()))
return false;
}
} else {
if (!!m_value && m_value != value)
return false;

if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
return false;

if (value.isEmpty()) {
ASSERT(m_type & SpecEmpty);
return true;
}
}

if (!!value && value.isCell()) {
ASSERT(m_type & SpecCell);
Structure* structure = value.asCell()->structure();
return m_structure.contains(structure)
&& (m_arrayModes & arrayModesFromStructure(structure));
}

return true;
}

bool AbstractValue::validateTypeAcceptingBoxedInt52(JSValue value) const
{
if (isBytecodeTop())
return true;

if (m_type & SpecInt52Any) {
if (mergeSpeculations(m_type, int52AwareSpeculationFromValue(value)) == m_type)
return true;
}

if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
return false;

return true;
}

} } // namespace JSC::DFG

#endif // ENABLE(DFG_JIT)
Expand Down
59 changes: 3 additions & 56 deletions Source/JavaScriptCore/dfg/DFGAbstractValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,47 +380,8 @@ struct AbstractValue {

bool contains(RegisteredStructure) const;

bool validateOSREntryValue(JSValue value, FlushFormat format) const
{
if (isBytecodeTop())
return true;

if (format == FlushedInt52) {
if (!isInt52Any())
return false;

if (!validateTypeAcceptingBoxedInt52(value))
return false;
JS_EXPORT_PRIVATE bool validateOSREntryValue(JSValue, FlushFormat) const;

if (!!m_value) {
ASSERT(m_value.isAnyInt());
ASSERT(value.isAnyInt());
if (jsDoubleNumber(m_value.asAnyInt()) != jsDoubleNumber(value.asAnyInt()))
return false;
}
} else {
if (!!m_value && m_value != value)
return false;

if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
return false;

if (value.isEmpty()) {
ASSERT(m_type & SpecEmpty);
return true;
}
}

if (!!value && value.isCell()) {
ASSERT(m_type & SpecCell);
Structure* structure = value.asCell()->structure();
return m_structure.contains(structure)
&& (m_arrayModes & arrayModesFromStructure(structure));
}

return true;
}

bool hasClobberableState() const
{
return m_structure.isNeitherClearNorTop()
Expand Down Expand Up @@ -515,23 +476,9 @@ struct AbstractValue {
if (m_arrayModes & from)
m_arrayModes |= to;
}

bool validateTypeAcceptingBoxedInt52(JSValue value) const
{
if (isBytecodeTop())
return true;

if (m_type & SpecInt52Any) {
if (mergeSpeculations(m_type, int52AwareSpeculationFromValue(value)) == m_type)
return true;
}

if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
return false;

return true;
}

bool validateTypeAcceptingBoxedInt52(JSValue) const;

void makeTop(SpeculatedType top)
{
m_type = top;
Expand Down
7 changes: 0 additions & 7 deletions Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1157,13 +1157,6 @@ class ConstantFoldingPhase : public Phase {
const AbstractValue& abstractValue = m_state.forNode(node->child1());
unsigned bits = node->typeInfoOperand();
ASSERT(bits);
if (bits == ImplementsDefaultHasInstance) {
if (abstractValue.m_type == SpecFunctionWithDefaultHasInstance) {
eliminated = true;
node->remove(m_graph);
break;
}
}

if (JSValue value = abstractValue.value()) {
if (value.isCell()) {
Expand Down
5 changes: 5 additions & 0 deletions Source/JavaScriptCore/dfg/DFGInsertionSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ size_t InsertionSet::execute(BasicBlock* block)
return executeInsertions(*block, m_insertions);
}

Node* InsertionSet::insertConstant(size_t index, NodeOrigin origin, FrozenValue* value, NodeType op)
{
return insertNode(index, speculationFromValue(value->value()), op, origin, OpInfo(value));
}

} } // namespace JSC::DFG

#endif // ENABLE(DFG_JIT)
Expand Down
10 changes: 2 additions & 8 deletions Source/JavaScriptCore/dfg/DFGInsertionSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,8 @@ class InsertionSet {
return insert(index, m_graph.addNode(type, params...));
}

Node* insertConstant(
size_t index, NodeOrigin origin, FrozenValue* value,
NodeType op = JSConstant)
{
return insertNode(
index, speculationFromValue(value->value()), op, origin, OpInfo(value));
}

Node* insertConstant(size_t index, NodeOrigin, FrozenValue*, NodeType op = JSConstant);

Edge insertConstantForUse(
size_t index, NodeOrigin origin, FrozenValue* value, UseKind useKind)
{
Expand Down
2 changes: 1 addition & 1 deletion Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Original file line number Diff line number Diff line change
Expand Up @@ -2183,7 +2183,7 @@ macro llintJumpTrueOrFalseOp(opcodeName, opcodeStruct, miscConditionOp, truthyCe

.maybeCell:
btqnz t0, notCellMask, .slow
bbbeq JSCell::m_type[t0], constexpr JSType::LastMaybeFalsyCellPrimitive, .slow
bbbeq JSCell::m_type[t0], constexpr LastMaybeFalsyCellPrimitive, .slow
btbnz JSCell::m_flags[t0], constexpr MasqueradesAsUndefined, .slow
truthyCellConditionOp(dispatch)

Expand Down
Loading

0 comments on commit a4bb359

Please sign in to comment.