Skip to content
Permalink
Browse files
Add for-in OwnStructureMode optimizations to LLInt
https://bugs.webkit.org/show_bug.cgi?id=229038

Reviewed by Saam Barati.

This patch adds the optimizations we have for OwnStructureMode in
the Baseline to the LLInt. The patch also adds redundant self move
(i.e. move a, a) elimination to arm64. Finally, a bunch of the
property offset functions are now marked constexpr and return
intptr_t rather than size_t as the values can be negative.

There's also a minor fix to disable MSVC's signed to unsigned
cast warning for LLIntOffsetsExtractor as we don't care about
signedness for extracting constants.

* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileEnumeratorGetByVal):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_enumerator_get_by_val):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* offlineasm/arm64.rb:
* offlineasm/generate_offset_extractor.rb:
* runtime/Butterfly.h:
(JSC::Butterfly::indexOfPropertyStorage):
* runtime/JSObject.h:
(JSC::offsetInButterfly):
* runtime/PropertyOffset.h:
(JSC::checkOffset):
(JSC::validateOffset):
(JSC::isValidOffset):
(JSC::isInlineOffset):
(JSC::isOutOfLineOffset):
(JSC::offsetInInlineStorage):
(JSC::offsetInOutOfLineStorage):
(JSC::offsetInRespectiveStorage):
(JSC::numberOfOutOfLineSlotsForMaxOffset):
(JSC::numberOfSlotsForMaxOffset):
(JSC::offsetForPropertyNumber):


Canonical link: https://commits.webkit.org/240896@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@281523 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
kmiller68 committed Aug 24, 2021
1 parent a6951e6 commit 7f6fd334bec493e7bd0c05e905cd41372bf31e88
Showing 12 changed files with 199 additions and 38 deletions.
@@ -1,3 +1,48 @@
2021-08-24 Keith Miller <keith_miller@apple.com>

Add for-in OwnStructureMode optimizations to LLInt
https://bugs.webkit.org/show_bug.cgi?id=229038

Reviewed by Saam Barati.

This patch adds the optimizations we have for OwnStructureMode in
the Baseline to the LLInt. The patch also adds redundant self move
(i.e. move a, a) elimination to arm64. Finally, a bunch of the
property offset functions are now marked constexpr and return
intptr_t rather than size_t as the values can be negative.

There's also a minor fix to disable MSVC's signed to unsigned
cast warning for LLIntOffsetsExtractor as we don't care about
signedness for extracting constants.

* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileEnumeratorGetByVal):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_enumerator_get_by_val):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* offlineasm/arm64.rb:
* offlineasm/generate_offset_extractor.rb:
* runtime/Butterfly.h:
(JSC::Butterfly::indexOfPropertyStorage):
* runtime/JSObject.h:
(JSC::offsetInButterfly):
* runtime/PropertyOffset.h:
(JSC::checkOffset):
(JSC::validateOffset):
(JSC::isValidOffset):
(JSC::isInlineOffset):
(JSC::isOutOfLineOffset):
(JSC::offsetInInlineStorage):
(JSC::offsetInOutOfLineStorage):
(JSC::offsetInRespectiveStorage):
(JSC::numberOfOutOfLineSlotsForMaxOffset):
(JSC::numberOfSlotsForMaxOffset):
(JSC::offsetForPropertyNumber):

2021-08-24 Yusuke Suzuki <ysuzuki@apple.com>

[JSC] Add Intl Enumeration APIs
@@ -13647,7 +13647,7 @@ void SpeculativeJIT::compileEnumeratorGetByVal(Node* node)
m_jit.signExtend32ToPtr(scratchGPR, scratchGPR);
if (!haveStorage)
m_jit.loadPtr(MacroAssembler::Address(baseCellGPR, JSObject::butterflyOffset()), storageGPR);
int32_t offsetOfFirstProperty = static_cast<int32_t>(offsetInButterfly(firstOutOfLineOffset)) * sizeof(EncodedJSValue);
constexpr intptr_t offsetOfFirstProperty = offsetInButterfly(firstOutOfLineOffset) * static_cast<intptr_t>(sizeof(EncodedJSValue));
m_jit.loadValue(MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, offsetOfFirstProperty), resultRegs);
doneCases.append(m_jit.jump());
}
@@ -13258,7 +13258,7 @@ class LowerDFGToB3 {

LValue realIndex = m_out.signExt32To64(
m_out.neg(m_out.sub(index, inlineCapacity)));
int32_t offsetOfFirstProperty = static_cast<int32_t>(offsetInButterfly(firstOutOfLineOffset)) * sizeof(EncodedJSValue);
constexpr intptr_t offsetOfFirstProperty = offsetInButterfly(firstOutOfLineOffset) * static_cast<intptr_t>(sizeof(EncodedJSValue));
results.append(m_out.anchor(
m_out.load64(m_out.baseIndex(m_heaps.properties.atAnyNumber(), storage, realIndex, ScaleEight, offsetOfFirstProperty))));
m_out.jump(continuation);
@@ -3089,7 +3089,7 @@ void JIT::emit_op_enumerator_get_by_val(const Instruction* currentInstruction)
sub32(Address(regT2, JSPropertyNameEnumerator::cachedInlineCapacityOffset()), regT3);
neg32(regT3);
signExtend32ToPtr(regT3, regT3);
int32_t offsetOfFirstProperty = static_cast<int32_t>(offsetInButterfly(firstOutOfLineOffset)) * sizeof(EncodedJSValue);
constexpr intptr_t offsetOfFirstProperty = offsetInButterfly(firstOutOfLineOffset) * static_cast<intptr_t>(sizeof(EncodedJSValue));
load64(BaseIndex(regT0, regT3, TimesEight, offsetOfFirstProperty), resultGPR);
doneCases.append(jump());

@@ -1994,10 +1994,6 @@ slowPathOp(define_accessor_property)
slowPathOp(define_data_property)
slowPathOp(get_by_val_with_this)
slowPathOp(get_property_enumerator)
slowPathOp(enumerator_next)
slowPathOp(enumerator_get_by_val)
slowPathOp(enumerator_in_by_val)
slowPathOp(enumerator_has_own_property)

if not JSVALUE64
slowPathOp(get_prototype_of)
@@ -3079,3 +3079,7 @@ llintOpWithMetadata(op_set_private_brand, OpSetPrivateBrand, macro (size, get, d
dispatch()
end)

slowPathOp(enumerator_next)
slowPathOp(enumerator_get_by_val)
slowPathOp(enumerator_in_by_val)
slowPathOp(enumerator_has_own_property)
@@ -534,6 +534,11 @@ macro loadVariable(get, fieldName, valueReg)
loadq [cfr, valueReg, 8], valueReg
end

macro storeVariable(get, fieldName, newValueReg, scratchReg)
get(fieldName, scratchReg)
storeq newValueReg, [cfr, scratchReg, 8]
end

# Index and value must be different registers. Index may be clobbered.
macro loadConstant(size, index, value)
macro loadNarrow()
@@ -3073,8 +3078,7 @@ llintOpWithMetadata(op_iterator_next, OpIteratorNext, macro (size, get, dispatch
.notDone:
macro storeValueAndDispatch(v)
move v, t2
get(m_value, t1)
storeq t2, [cfr, t1, 8]
storeVariable(get, m_value, t2, t1)
checkStackPointerAlignment(t0, 0xbaddb01e)
dispatch()
end
@@ -3089,6 +3093,113 @@ llintOpWithMetadata(op_iterator_next, OpIteratorNext, macro (size, get, dispatch
dispatch()
end)

llintOp(op_enumerator_next, OpEnumeratorNext, macro (size, get, dispatch)
# Note: this will always call the slow path on at least the first/last execution of EnumeratorNext for any given loop.
# The upside this is that we don't have to record any metadata or mode information here as the slow path will do it for us when transitioning from InitMode/IndexedMode to OwnStructureMode, or from OwnStructureMode to GenericMode.
get(m_mode, t0)
bbneq t0, constexpr JSPropertyNameEnumerator::OwnStructureMode, .nextSlowPath

get(m_base, t1)
loadConstantOrVariableCell(size, t1, t0, .nextSlowPath)

loadVariable(get, m_enumerator, t1)
loadi JSPropertyNameEnumerator::m_cachedStructureID[t1], t2
bineq t2, JSCell::m_structureID[t0], .nextSlowPath

loadVariable(get, m_index, t2)
addq 1, t2
loadi JSPropertyNameEnumerator::m_endStructurePropertyIndex, t3
biaeq t2, t3, .nextSlowPath

storeVariable(get, m_index, t2, t3)
loadp JSPropertyNameEnumerator::m_propertyNames[t1], t3
zxi2q t2, t2
loadp [t3, t2, PtrSize], t3

storeVariable(get, m_propertyName, t3, t2)
dispatch()

.nextSlowPath:
callSlowPath(_slow_path_enumerator_next)
dispatch()
end)

llintOpWithMetadata(op_enumerator_get_by_val, OpEnumeratorGetByVal, macro (size, get, dispatch, metadata, return)
metadata(t5, t0)

loadVariable(get, m_mode, t0)

# FIXME: This should be orb but that doesn't exist for some reason... https://bugs.webkit.org/show_bug.cgi?id=229445
loadb OpEnumeratorGetByVal::Metadata::m_enumeratorMetadata[t5], t1
ori t0, t1
storeb t1, OpEnumeratorGetByVal::Metadata::m_enumeratorMetadata[t5]

bbneq t0, constexpr JSPropertyNameEnumerator::OwnStructureMode, .getSlowPath

get(m_base, t1)
loadConstantOrVariableCell(size, t1, t0, .getSlowPath)

loadVariable(get, m_enumerator, t1)
loadi JSPropertyNameEnumerator::m_cachedStructureID[t1], t2
bineq t2, JSCell::m_structureID[t0], .getSlowPath

loadVariable(get, m_index, t2)
loadi JSPropertyNameEnumerator::m_cachedInlineCapacity[t1], t1
biaeq t2, t1, .outOfLine

zxi2q t2, t2
loadq sizeof JSObject[t0, t2, 8], t2
jmp .done

.outOfLine:
loadp JSObject::m_butterfly[t0], t0
subi t1, t2
negi t2
sxi2q t2, t2
loadq constexpr ((offsetInButterfly(firstOutOfLineOffset)) * sizeof(EncodedJSValue))[t0, t2, 8], t2

.done:
valueProfile(OpEnumeratorGetByVal, m_profile, t5, t2)
return(t2)

.getSlowPath:
callSlowPath(_slow_path_enumerator_get_by_val)
dispatch()
end)

macro hasPropertyImpl(opcodeStruct, size, get, dispatch, metadata, return, slowPath)
metadata(t5, t0)

loadVariable(get, m_mode, t0)
# FIXME: This should be orb but that doesn't exist for some reason... https://bugs.webkit.org/show_bug.cgi?id=229445
loadb %opcodeStruct%::Metadata::m_enumeratorMetadata[t5], t1
ori t0, t1
storeb t1, %opcodeStruct%::Metadata::m_enumeratorMetadata[t5]

bbneq t0, constexpr JSPropertyNameEnumerator::OwnStructureMode, .callSlowPath

get(m_base, t1)
loadConstantOrVariableCell(size, t1, t0, .callSlowPath)

loadVariable(get, m_enumerator, t1)
loadi JSPropertyNameEnumerator::m_cachedStructureID[t1], t2
bineq t2, JSCell::m_structureID[t0], .callSlowPath

move ValueTrue, t2
return(t2)

.callSlowPath:
callSlowPath(slowPath)
dispatch()
end

llintOpWithMetadata(op_enumerator_in_by_val, OpEnumeratorInByVal, macro (size, get, dispatch, metadata, return)
hasPropertyImpl(OpEnumeratorInByVal, size, get, dispatch, metadata, return, _slow_path_enumerator_in_by_val)
end)

llintOpWithMetadata(op_enumerator_has_own_property, OpEnumeratorHasOwnProperty, macro (size, get, dispatch, metadata, return)
hasPropertyImpl(OpEnumeratorHasOwnProperty, size, get, dispatch, metadata, return, _slow_path_enumerator_has_own_property)
end)

llintOpWithProfile(op_get_internal_field, OpGetInternalField, macro (size, get, dispatch, return)
loadVariable(get, m_base, t1)
@@ -884,7 +884,7 @@ def lowerARM64
when "move"
if operands[0].immediate?
emitARM64MoveImmediate(operands[0].value, operands[1])
else
elsif operands[0] != operands[1]
emitARM64("mov", operands, :quad)
end
when "moved"
@@ -92,6 +92,11 @@ def emitMagicNumber
constsList = constsList(lowLevelAST)

emitCodeInConfiguration(concreteSettings, lowLevelAST, backend) {

# Windows complains about signed integers being cast to unsigned but we just want the bits.
outp.puts "\#if COMPILER(MSVC)"
outp.puts "\#pragma warning(disable:4308)"
outp.puts "\#endif"
constsList.each_with_index {
| const, index |
outp.puts "constexpr int64_t constValue#{index} = static_cast<int64_t>(#{const.value});"
@@ -209,7 +209,7 @@ class Butterfly {
}

static ptrdiff_t offsetOfPropertyStorage() { return -static_cast<ptrdiff_t>(sizeof(IndexingHeader)); }
static int indexOfPropertyStorage()
constexpr static int indexOfPropertyStorage()
{
ASSERT(sizeof(IndexingHeader) == sizeof(EncodedJSValue));
return -1;
@@ -1564,7 +1564,7 @@ inline bool JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value
return putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot);
}

inline size_t offsetInButterfly(PropertyOffset offset)
constexpr inline intptr_t offsetInButterfly(PropertyOffset offset)
{
return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
}
@@ -37,26 +37,26 @@ static constexpr PropertyOffset knownPolyProtoOffset = 0;
static_assert(knownPolyProtoOffset < firstOutOfLineOffset, "We assume in all the JITs that the poly proto offset is an inline offset");

// Declare all of the functions because they tend to do forward calls.
inline void checkOffset(PropertyOffset);
inline void checkOffset(PropertyOffset, int inlineCapacity);
inline void validateOffset(PropertyOffset);
inline void validateOffset(PropertyOffset, int inlineCapacity);
inline bool isValidOffset(PropertyOffset);
inline bool isInlineOffset(PropertyOffset);
inline bool isOutOfLineOffset(PropertyOffset);
inline size_t offsetInInlineStorage(PropertyOffset);
inline size_t offsetInOutOfLineStorage(PropertyOffset);
inline size_t offsetInRespectiveStorage(PropertyOffset);
inline size_t numberOfOutOfLineSlotsForMaxOffset(PropertyOffset);
inline size_t numberOfSlotsForMaxOffset(PropertyOffset, int inlineCapacity);

inline void checkOffset(PropertyOffset offset)
constexpr inline void checkOffset(PropertyOffset);
constexpr inline void checkOffset(PropertyOffset, int inlineCapacity);
constexpr inline void validateOffset(PropertyOffset);
constexpr inline void validateOffset(PropertyOffset, int inlineCapacity);
constexpr inline bool isValidOffset(PropertyOffset);
constexpr inline bool isInlineOffset(PropertyOffset);
constexpr inline bool isOutOfLineOffset(PropertyOffset);
constexpr inline intptr_t offsetInInlineStorage(PropertyOffset);
constexpr inline intptr_t offsetInOutOfLineStorage(PropertyOffset);
constexpr inline intptr_t offsetInRespectiveStorage(PropertyOffset);
constexpr inline size_t numberOfOutOfLineSlotsForMaxOffset(PropertyOffset);
constexpr inline size_t numberOfSlotsForMaxOffset(PropertyOffset, int inlineCapacity);

constexpr inline void checkOffset(PropertyOffset offset)
{
UNUSED_PARAM(offset);
ASSERT(offset >= invalidOffset);
}

inline void checkOffset(PropertyOffset offset, int inlineCapacity)
constexpr inline void checkOffset(PropertyOffset offset, int inlineCapacity)
{
UNUSED_PARAM(offset);
UNUSED_PARAM(inlineCapacity);
@@ -66,74 +66,74 @@ inline void checkOffset(PropertyOffset offset, int inlineCapacity)
|| isOutOfLineOffset(offset));
}

inline void validateOffset(PropertyOffset offset)
constexpr inline void validateOffset(PropertyOffset offset)
{
checkOffset(offset);
ASSERT(isValidOffset(offset));
}

inline void validateOffset(PropertyOffset offset, int inlineCapacity)
constexpr inline void validateOffset(PropertyOffset offset, int inlineCapacity)
{
checkOffset(offset, inlineCapacity);
ASSERT(isValidOffset(offset));
}

inline bool isValidOffset(PropertyOffset offset)
constexpr inline bool isValidOffset(PropertyOffset offset)
{
checkOffset(offset);
return offset != invalidOffset;
}

inline bool isInlineOffset(PropertyOffset offset)
constexpr inline bool isInlineOffset(PropertyOffset offset)
{
checkOffset(offset);
return offset < firstOutOfLineOffset;
}

inline bool isOutOfLineOffset(PropertyOffset offset)
constexpr inline bool isOutOfLineOffset(PropertyOffset offset)
{
checkOffset(offset);
return !isInlineOffset(offset);
}

inline size_t offsetInInlineStorage(PropertyOffset offset)
constexpr inline intptr_t offsetInInlineStorage(PropertyOffset offset)
{
validateOffset(offset);
ASSERT(isInlineOffset(offset));
return offset;
}

inline size_t offsetInOutOfLineStorage(PropertyOffset offset)
constexpr inline intptr_t offsetInOutOfLineStorage(PropertyOffset offset)
{
validateOffset(offset);
ASSERT(isOutOfLineOffset(offset));
return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 1;
}

inline size_t offsetInRespectiveStorage(PropertyOffset offset)
constexpr inline intptr_t offsetInRespectiveStorage(PropertyOffset offset)
{
if (isInlineOffset(offset))
return offsetInInlineStorage(offset);
return offsetInOutOfLineStorage(offset);
}

inline size_t numberOfOutOfLineSlotsForMaxOffset(PropertyOffset offset)
constexpr inline size_t numberOfOutOfLineSlotsForMaxOffset(PropertyOffset offset)
{
checkOffset(offset);
if (offset < firstOutOfLineOffset)
return 0;
return offset - firstOutOfLineOffset + 1;
}

inline size_t numberOfSlotsForMaxOffset(PropertyOffset offset, int inlineCapacity)
constexpr inline size_t numberOfSlotsForMaxOffset(PropertyOffset offset, int inlineCapacity)
{
checkOffset(offset, inlineCapacity);
if (offset < inlineCapacity)
return offset + 1;
return inlineCapacity + numberOfOutOfLineSlotsForMaxOffset(offset);
}

inline PropertyOffset offsetForPropertyNumber(int propertyNumber, int inlineCapacity)
constexpr inline PropertyOffset offsetForPropertyNumber(int propertyNumber, int inlineCapacity)
{
PropertyOffset offset = propertyNumber;
if (offset >= inlineCapacity) {

0 comments on commit 7f6fd33

Please sign in to comment.