зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1341265 - Part 11: Optimise Set.prototype.has for objects in CacheIR. r=iain
Inline `Set.prototype.has` in CacheIR when called with objects. Implementing `MacroAssembler::hashObject()` on 32-bit platforms is difficult, because it uses 64-bit operations, so we either have to allocate twice as much registers for `Register64` or alternatively spill the values on the stack. For now just punt and only support this optimisation on 64-bit platforms. Differential Revision: https://phabricator.services.mozilla.com/D118977
This commit is contained in:
Родитель
d78d215605
Коммит
dca8370c53
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -7820,9 +7820,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:
|
||||
|
|
|
@ -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__);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <typename OrderedHashTable>
|
||||
void MacroAssembler::orderedHashTableLookup(Register setOrMapObj,
|
||||
ValueOperand value, Register hash,
|
||||
|
|
|
@ -4680,6 +4680,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 };
|
||||
|
|
|
@ -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) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче