diff --git a/js/src/ds/OrderedHashTable.h b/js/src/ds/OrderedHashTable.h index 691c31461d4c..a75f86ac4b9c 100644 --- a/js/src/ds/OrderedHashTable.h +++ b/js/src/ds/OrderedHashTable.h @@ -612,6 +612,15 @@ class OrderedHashTable { static constexpr size_t offsetOfDataChain() { return offsetof(Data, chain); } static constexpr size_t sizeofData() { return sizeof(Data); } + static constexpr size_t offsetOfHcsK0() { + return offsetof(OrderedHashTable, hcs) + + mozilla::HashCodeScrambler::offsetOfMK0(); + } + static constexpr size_t offsetOfHcsK1() { + return offsetof(OrderedHashTable, hcs) + + mozilla::HashCodeScrambler::offsetOfMK1(); + } + private: /* Logarithm base 2 of the number of buckets in the hash table initially. */ static uint32_t initialBucketsLog2() { return 1; } @@ -922,6 +931,9 @@ class OrderedHashSet { } static constexpr size_t sizeofImplData() { return Impl::sizeofData(); } + static constexpr size_t offsetOfImplHcsK0() { return Impl::offsetOfHcsK0(); } + static constexpr size_t offsetOfImplHcsK1() { return Impl::offsetOfHcsK1(); } + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { return impl.sizeOfExcludingThis(mallocSizeOf); } diff --git a/js/src/jit-test/tests/cacheir/set-has-object.js b/js/src/jit-test/tests/cacheir/set-has-object.js new file mode 100644 index 000000000000..ea1b690d709d --- /dev/null +++ b/js/src/jit-test/tests/cacheir/set-has-object.js @@ -0,0 +1,30 @@ +// Return a new set, possibly filling some dummy entries to enforce creating +// multiple hash buckets. +function createSet(values, n) { + var xs = [...values]; + for (var i = 0; i < n; ++i) { + xs.push({}); + } + return new Set(xs); +} + +function runTest(fn) { + fn(0); + fn(100); +} + +function test(n) { + var xs = [{}, {}]; + var ys = [{}, {}]; + var zs = [...xs, ...ys]; + var set = createSet(xs, n); + + var N = 100; + var c = 0; + for (var i = 0; i < N; ++i) { + var z = zs[i & 3]; + if (set.has(z)) c++; + } + assertEq(c, N / 2); +} +runTest(test); diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 86b05053060c..a88fca4ea85a 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -7821,9 +7821,16 @@ AttachDecision CallIRGenerator::tryAttachSetHas(HandleFunction callee) { writer.setHasBigIntResult(objId, bigIntId); break; } - case ValueType::Object: + case ValueType::Object: { + // Currently only supported on 64-bit platforms. +# ifdef JS_PUNBOX64 + ObjOperandId valId = writer.guardToObject(argId); + writer.setHasObjectResult(objId, valId); +# else writer.setHasResult(objId, argId); +# endif break; + } case ValueType::Magic: case ValueType::PrivateGCThing: diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index 30359e022aad..1834ef4832ea 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -8236,6 +8236,30 @@ bool CacheIRCompiler::emitSetHasBigIntResult(ObjOperandId setId, return true; } +bool CacheIRCompiler::emitSetHasObjectResult(ObjOperandId setId, + ObjOperandId objId) { + JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); + + AutoOutputRegister output(*this); + Register set = allocator.useRegister(masm, setId); + Register obj = allocator.useRegister(masm, objId); + + AutoScratchRegister scratch1(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); + AutoScratchRegister scratch3(allocator, masm); + AutoScratchRegister scratch4(allocator, masm); + AutoScratchRegister scratch5(allocator, masm); + + masm.tagValue(JSVAL_TYPE_OBJECT, obj, output.valueReg()); + masm.prepareHashObject(set, output.valueReg(), scratch1, scratch2, scratch3, + scratch4, scratch5); + + masm.setObjectHasNonBigInt(set, output.valueReg(), scratch1, scratch2, + scratch3, scratch4); + masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch2, output.valueReg()); + return true; +} + bool CacheIRCompiler::emitBailout() { JitSpew(JitSpew_Codegen, "%s", __FUNCTION__); diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml index 6d30ebe2e10e..a933366951e9 100644 --- a/js/src/jit/CacheIROps.yaml +++ b/js/src/jit/CacheIROps.yaml @@ -2653,6 +2653,14 @@ set: ObjId bigInt: BigIntId +- name: SetHasObjectResult + shared: true + transpile: false + cost_estimate: 3 + args: + set: ObjId + obj: ObjId + - name: CallPrintString shared: true transpile: false diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 603ad5879a32..c048b9fa1fbd 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -4819,6 +4819,127 @@ void MacroAssembler::prepareHashBigInt(Register bigInt, Register result, scrambleHashCode(result); } +void MacroAssembler::prepareHashObject(Register setObj, ValueOperand value, + Register result, Register temp1, + Register temp2, Register temp3, + Register temp4) { +#ifdef JS_PUNBOX64 + // Inline implementation of |OrderedHashTable::prepareHash()| and + // |HashCodeScrambler::scramble(v.asRawBits())|. + + // Load the |ValueSet|. + loadPrivate(Address(setObj, SetObject::getDataSlotOffset()), temp1); + + // Load |HashCodeScrambler::mK0| and |HashCodeScrambler::mK0|. + auto k0 = Register64(temp1); + auto k1 = Register64(temp2); + load64(Address(temp1, ValueSet::offsetOfImplHcsK1()), k1); + load64(Address(temp1, ValueSet::offsetOfImplHcsK0()), k0); + + // Hash numbers are 32-bit values, so only hash the lower double-word. + static_assert(sizeof(mozilla::HashNumber) == 4); + move64To32(value.toRegister64(), result); + + // Inline implementation of |SipHasher::sipHash()|. + auto m = Register64(result); + auto v0 = Register64(temp3); + auto v1 = Register64(temp4); + auto v2 = k0; + auto v3 = k1; + + auto sipRound = [&]() { + // mV0 = WrappingAdd(mV0, mV1); + add64(v1, v0); + + // mV1 = RotateLeft(mV1, 13); + rotateLeft64(Imm32(13), v1, v1, InvalidReg); + + // mV1 ^= mV0; + xor64(v0, v1); + + // mV0 = RotateLeft(mV0, 32); + rotateLeft64(Imm32(32), v0, v0, InvalidReg); + + // mV2 = WrappingAdd(mV2, mV3); + add64(v3, v2); + + // mV3 = RotateLeft(mV3, 16); + rotateLeft64(Imm32(16), v3, v3, InvalidReg); + + // mV3 ^= mV2; + xor64(v2, v3); + + // mV0 = WrappingAdd(mV0, mV3); + add64(v3, v0); + + // mV3 = RotateLeft(mV3, 21); + rotateLeft64(Imm32(21), v3, v3, InvalidReg); + + // mV3 ^= mV0; + xor64(v0, v3); + + // mV2 = WrappingAdd(mV2, mV1); + add64(v1, v2); + + // mV1 = RotateLeft(mV1, 17); + rotateLeft64(Imm32(17), v1, v1, InvalidReg); + + // mV1 ^= mV2; + xor64(v2, v1); + + // mV2 = RotateLeft(mV2, 32); + rotateLeft64(Imm32(32), v2, v2, InvalidReg); + }; + + // 1. Initialization. + // mV0 = aK0 ^ UINT64_C(0x736f6d6570736575); + move64(Imm64(0x736f6d6570736575), v0); + xor64(k0, v0); + + // mV1 = aK1 ^ UINT64_C(0x646f72616e646f6d); + move64(Imm64(0x646f72616e646f6d), v1); + xor64(k1, v1); + + // mV2 = aK0 ^ UINT64_C(0x6c7967656e657261); + MOZ_ASSERT(v2 == k0); + xor64(Imm64(0x6c7967656e657261), v2); + + // mV3 = aK1 ^ UINT64_C(0x7465646279746573); + MOZ_ASSERT(v3 == k1); + xor64(Imm64(0x7465646279746573), v3); + + // 2. Compression. + // mV3 ^= aM; + xor64(m, v3); + + // sipRound(); + sipRound(); + + // mV0 ^= aM; + xor64(m, v0); + + // 3. Finalization. + // mV2 ^= 0xff; + xor64(Imm64(0xff), v2); + + // for (int i = 0; i < 3; i++) sipRound(); + for (int i = 0; i < 3; i++) { + sipRound(); + } + + // return mV0 ^ mV1 ^ mV2 ^ mV3; + xor64(v1, v0); + xor64(v2, v3); + xor64(v3, v0); + + move64To32(v0, result); + + scrambleHashCode(result); +#else + MOZ_CRASH("Not implemented"); +#endif +} + template void MacroAssembler::orderedHashTableLookup(Register setOrMapObj, ValueOperand value, Register hash, diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 35b41f183588..1c7c39a460b8 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -4684,6 +4684,9 @@ class MacroAssembler : public MacroAssemblerSpecific { void prepareHashSymbol(Register sym, Register result); void prepareHashBigInt(Register bigInt, Register result, Register temp1, Register temp2, Register temp3); + void prepareHashObject(Register setObj, ValueOperand value, Register result, + Register temp1, Register temp2, Register temp3, + Register temp4); private: enum class IsBigInt { No, Yes, Maybe }; diff --git a/mfbt/HashFunctions.h b/mfbt/HashFunctions.h index f0d5705b89cc..4b740a3db16b 100644 --- a/mfbt/HashFunctions.h +++ b/mfbt/HashFunctions.h @@ -361,6 +361,14 @@ class HashCodeScrambler { return HashNumber(hasher.sipHash(aHashCode)); } + static constexpr size_t offsetOfMK0() { + return offsetof(HashCodeScrambler, mK0); + } + + static constexpr size_t offsetOfMK1() { + return offsetof(HashCodeScrambler, mK1); + } + private: struct SipHasher { SipHasher(uint64_t aK0, uint64_t aK1) {