Reimplement Maps and Sets in JS

Previously, the only optimized code path for Maps and Sets was for String keys.
This was achieved through an implementation of various complex operations
in Hydrogen. This approach was neither scalable nor forward-compatible.

This patch adds the necessary intrinsics to implement Maps and Sets almost entirely
in JS. The added intrinsics are:

  %_FixedArrayGet
  %_FixedArraySet
  %_TheHole
  %_JSCollectionGetTable
  %_StringGetRawHashField

With these additions, as well as a few changes to what's exposed as runtime functions,
most of the C++ code backing Maps and Sets is gone (including both runtime code in
objects.cc and Crankshaft in hydrogen.cc).

Review URL: https://codereview.chromium.org/947683002

Cr-Commit-Position: refs/heads/master@{#27605}
This commit is contained in:
adamk 2015-04-06 17:11:48 -07:00 committed by Commit bot
parent 31bbcc3e19
commit 909500aa1d
18 changed files with 353 additions and 1064 deletions

View File

@ -1005,16 +1005,6 @@ void CEntryStub::Generate(MacroAssembler* masm) {
__ VFPEnsureFPSCRState(r2);
// Runtime functions should not return 'the hole'. Allowing it to escape may
// lead to crashes in the IC code later.
if (FLAG_debug_code) {
Label okay;
__ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
__ b(ne, &okay);
__ stop("The hole escaped");
__ bind(&okay);
}
// Check result for exception sentinel.
Label exception_returned;
__ CompareRoot(r0, Heap::kExceptionRootIndex);

View File

@ -12,6 +12,86 @@ var $Set = global.Set;
var $Map = global.Map;
// Used by harmony-templates.js
var $MapGet;
var $MapSet;
(function() {
function HashToEntry(table, hash, numBuckets) {
var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
return ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
}
%SetInlineBuiltinFlag(HashToEntry);
function SetFindEntry(table, numBuckets, key, hash) {
var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key);
for (var entry = HashToEntry(table, hash, numBuckets);
entry !== NOT_FOUND;
entry = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets)) {
var candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets);
if (key === candidate) {
return entry;
}
if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) {
return entry;
}
}
return NOT_FOUND;
}
%SetInlineBuiltinFlag(SetFindEntry);
function MapFindEntry(table, numBuckets, key, hash) {
var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key);
for (var entry = HashToEntry(table, hash, numBuckets);
entry !== NOT_FOUND;
entry = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets)) {
var candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets);
if (key === candidate) {
return entry;
}
if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) {
return entry;
}
}
return NOT_FOUND;
}
%SetInlineBuiltinFlag(MapFindEntry);
function ComputeIntegerHash(key, seed) {
var hash = key;
hash = hash ^ seed;
hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1;
hash = hash ^ (hash >>> 12);
hash = hash + (hash << 2);
hash = hash ^ (hash >>> 4);
hash = (hash * 2057) | 0; // hash = (hash + (hash << 3)) + (hash << 11);
hash = hash ^ (hash >>> 16);
return hash;
}
%SetInlineBuiltinFlag(ComputeIntegerHash);
function GetHash(key) {
if (%_IsSmi(key)) {
return ComputeIntegerHash(key, 0);
}
if (IS_STRING(key)) {
var field = %_StringGetRawHashField(key);
if ((field & 1 /* Name::kHashNotComputedMask */) === 0) {
return field >>> 2 /* Name::kHashShift */;
}
}
return %GenericHash(key);
}
%SetInlineBuiltinFlag(GetHash);
// -------------------------------------------------------------------
// Harmony Set
@ -35,7 +115,7 @@ function SetConstructor(iterable) {
}
function SetAddJS(key) {
function SetAdd(key) {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.add', this]);
@ -47,34 +127,76 @@ function SetAddJS(key) {
if (key === 0) {
key = 0;
}
return %_SetAdd(this, key);
var table = %_JSCollectionGetTable(this);
var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
var hash = GetHash(key);
if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this;
var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
var capacity = numBuckets << 1;
if ((nof + nod) >= capacity) {
// Need to grow, bail out to runtime.
%SetGrow(this);
// Re-load state from the grown backing store.
table = %_JSCollectionGetTable(this);
numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
}
var entry = nof + nod;
var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
FIXED_ARRAY_SET(table, index, key);
FIXED_ARRAY_SET_SMI(table, index + 1, chainEntry);
return this;
}
function SetHasJS(key) {
function SetHas(key) {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.has', this]);
}
return %_SetHas(this, key);
var table = %_JSCollectionGetTable(this);
var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
var hash = GetHash(key);
return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
}
function SetDeleteJS(key) {
function SetDelete(key) {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.delete', this]);
}
return %_SetDelete(this, key);
var table = %_JSCollectionGetTable(this);
var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
var hash = GetHash(key);
var entry = SetFindEntry(table, numBuckets, key, hash);
if (entry === NOT_FOUND) return false;
var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
FIXED_ARRAY_SET(table, index, %_TheHole());
ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
if (nof < (numBuckets >>> 1)) %SetShrink(this);
return true;
}
function SetGetSizeJS() {
function SetGetSize() {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.size', this]);
}
return %_SetGetSize(this);
var table = %_JSCollectionGetTable(this);
return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
}
@ -130,11 +252,11 @@ function SetUpSet() {
%FunctionSetLength(SetForEach, 1);
// Set up the non-enumerable functions on the Set prototype object.
InstallGetter($Set.prototype, "size", SetGetSizeJS);
InstallGetter($Set.prototype, "size", SetGetSize);
InstallFunctions($Set.prototype, DONT_ENUM, $Array(
"add", SetAddJS,
"has", SetHasJS,
"delete", SetDeleteJS,
"add", SetAdd,
"has", SetHas,
"delete", SetDelete,
"clear", SetClearJS,
"forEach", SetForEach
));
@ -169,16 +291,21 @@ function MapConstructor(iterable) {
}
function MapGetJS(key) {
function MapGet(key) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.get', this]);
}
return %_MapGet(this, key);
var table = %_JSCollectionGetTable(this);
var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
var hash = GetHash(key);
var entry = MapFindEntry(table, numBuckets, key, hash);
if (entry === NOT_FOUND) return UNDEFINED;
return ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets);
}
function MapSetJS(key, value) {
function MapSet(key, value) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.set', this]);
@ -190,34 +317,84 @@ function MapSetJS(key, value) {
if (key === 0) {
key = 0;
}
return %_MapSet(this, key, value);
var table = %_JSCollectionGetTable(this);
var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
var hash = GetHash(key);
var entry = MapFindEntry(table, numBuckets, key, hash);
if (entry !== NOT_FOUND) {
var existingIndex = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
FIXED_ARRAY_SET(table, existingIndex + 1, value);
return this;
}
var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
var capacity = numBuckets << 1;
if ((nof + nod) >= capacity) {
// Need to grow, bail out to runtime.
%MapGrow(this);
// Re-load state from the grown backing store.
table = %_JSCollectionGetTable(this);
numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
}
entry = nof + nod;
var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
FIXED_ARRAY_SET(table, index, key);
FIXED_ARRAY_SET(table, index + 1, value);
FIXED_ARRAY_SET(table, index + 2, chainEntry);
return this;
}
function MapHasJS(key) {
function MapHas(key) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.has', this]);
}
return %_MapHas(this, key);
var table = %_JSCollectionGetTable(this);
var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
var hash = GetHash(key);
return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
}
function MapDeleteJS(key) {
function MapDelete(key) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.delete', this]);
}
return %_MapDelete(this, key);
var table = %_JSCollectionGetTable(this);
var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
var hash = GetHash(key);
var entry = MapFindEntry(table, numBuckets, key, hash);
if (entry === NOT_FOUND) return false;
var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
FIXED_ARRAY_SET(table, index, %_TheHole());
FIXED_ARRAY_SET(table, index + 1, %_TheHole());
ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
if (nof < (numBuckets >>> 1)) %MapShrink(this);
return true;
}
function MapGetSizeJS() {
function MapGetSize() {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.size', this]);
}
return %_MapGetSize(this);
var table = %_JSCollectionGetTable(this);
return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
}
@ -271,15 +448,20 @@ function SetUpMap() {
%FunctionSetLength(MapForEach, 1);
// Set up the non-enumerable functions on the Map prototype object.
InstallGetter($Map.prototype, "size", MapGetSizeJS);
InstallGetter($Map.prototype, "size", MapGetSize);
InstallFunctions($Map.prototype, DONT_ENUM, $Array(
"get", MapGetJS,
"set", MapSetJS,
"has", MapHasJS,
"delete", MapDeleteJS,
"get", MapGet,
"set", MapSet,
"has", MapHas,
"delete", MapDelete,
"clear", MapClearJS,
"forEach", MapForEach
));
$MapGet = MapGet;
$MapSet = MapSet;
}
SetUpMap();
})();

View File

@ -12055,528 +12055,54 @@ void HOptimizedGraphBuilder::GenerateUnlikely(CallRuntime* call) {
}
HValue* HOptimizedGraphBuilder::BuildOrderedHashTableHashToBucket(
HValue* hash, HValue* num_buckets) {
HValue* mask = AddUncasted<HSub>(num_buckets, graph()->GetConstant1());
mask->ChangeRepresentation(Representation::Integer32());
mask->ClearFlag(HValue::kCanOverflow);
return AddUncasted<HBitwise>(Token::BIT_AND, hash, mask);
}
template <typename CollectionType>
HValue* HOptimizedGraphBuilder::BuildOrderedHashTableHashToEntry(
HValue* table, HValue* hash, HValue* num_buckets) {
HValue* bucket = BuildOrderedHashTableHashToBucket(hash, num_buckets);
HValue* entry_index = AddUncasted<HAdd>(
bucket, Add<HConstant>(CollectionType::kHashTableStartIndex));
entry_index->ClearFlag(HValue::kCanOverflow);
HValue* entry = Add<HLoadKeyed>(table, entry_index, nullptr, FAST_ELEMENTS);
entry->set_type(HType::Smi());
return entry;
}
template <typename CollectionType>
HValue* HOptimizedGraphBuilder::BuildOrderedHashTableEntryToIndex(
HValue* entry, HValue* num_buckets) {
HValue* index =
AddUncasted<HMul>(entry, Add<HConstant>(CollectionType::kEntrySize));
index->ClearFlag(HValue::kCanOverflow);
index = AddUncasted<HAdd>(index, num_buckets);
index->ClearFlag(HValue::kCanOverflow);
index = AddUncasted<HAdd>(
index, Add<HConstant>(CollectionType::kHashTableStartIndex));
index->ClearFlag(HValue::kCanOverflow);
return index;
}
template <typename CollectionType>
HValue* HOptimizedGraphBuilder::BuildOrderedHashTableFindEntry(HValue* table,
HValue* key,
HValue* hash) {
HValue* num_buckets = Add<HLoadNamedField>(
table, nullptr,
HObjectAccess::ForOrderedHashTableNumberOfBuckets<CollectionType>());
HValue* entry = BuildOrderedHashTableHashToEntry<CollectionType>(table, hash,
num_buckets);
Push(entry);
LoopBuilder loop(this);
loop.BeginBody(1);
entry = Pop();
{
IfBuilder if_not_found(this);
if_not_found.If<HCompareNumericAndBranch>(
entry, Add<HConstant>(CollectionType::kNotFound), Token::EQ);
if_not_found.Then();
Push(entry);
loop.Break();
}
HValue* key_index =
BuildOrderedHashTableEntryToIndex<CollectionType>(entry, num_buckets);
HValue* candidate_key =
Add<HLoadKeyed>(table, key_index, nullptr, FAST_ELEMENTS);
{
IfBuilder if_keys_equal(this);
if_keys_equal.If<HIsStringAndBranch>(candidate_key);
if_keys_equal.AndIf<HStringCompareAndBranch>(candidate_key, key,
Token::EQ_STRICT);
if_keys_equal.Then();
Push(key_index);
loop.Break();
}
// BuildChainAt
HValue* chain_index = AddUncasted<HAdd>(
key_index, Add<HConstant>(CollectionType::kChainOffset));
chain_index->ClearFlag(HValue::kCanOverflow);
entry = Add<HLoadKeyed>(table, chain_index, nullptr, FAST_ELEMENTS);
entry->set_type(HType::Smi());
Push(entry);
loop.EndBody();
return Pop();
}
void HOptimizedGraphBuilder::GenerateMapGet(CallRuntime* call) {
void HOptimizedGraphBuilder::GenerateFixedArrayGet(CallRuntime* call) {
DCHECK(call->arguments()->length() == 2);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* key = Pop();
HValue* receiver = Pop();
NoObservableSideEffectsScope no_effects(this);
HIfContinuation continuation;
HValue* hash =
BuildStringHashLoadIfIsStringAndHashComputed(key, &continuation);
{
IfBuilder string_checker(this, &continuation);
string_checker.Then();
{
HValue* table = Add<HLoadNamedField>(
receiver, nullptr, HObjectAccess::ForJSCollectionTable());
HValue* key_index =
BuildOrderedHashTableFindEntry<OrderedHashMap>(table, key, hash);
IfBuilder if_found(this);
if_found.If<HCompareNumericAndBranch>(
key_index, Add<HConstant>(OrderedHashMap::kNotFound), Token::NE);
if_found.Then();
{
HValue* value_index = AddUncasted<HAdd>(
key_index, Add<HConstant>(OrderedHashMap::kValueOffset));
value_index->ClearFlag(HValue::kCanOverflow);
Push(Add<HLoadKeyed>(table, value_index, nullptr, FAST_ELEMENTS));
}
if_found.Else();
Push(graph()->GetConstantUndefined());
if_found.End();
}
string_checker.Else();
{
Add<HPushArguments>(receiver, key);
Push(Add<HCallRuntime>(call->name(),
Runtime::FunctionForId(Runtime::kMapGet), 2));
}
}
return ast_context()->ReturnValue(Pop());
HValue* index = Pop();
HValue* object = Pop();
HInstruction* result = New<HLoadKeyed>(
object, index, nullptr, FAST_HOLEY_ELEMENTS, ALLOW_RETURN_HOLE);
return ast_context()->ReturnInstruction(result, call->id());
}
HValue* HOptimizedGraphBuilder::BuildStringHashLoadIfIsStringAndHashComputed(
HValue* object, HIfContinuation* continuation) {
IfBuilder string_checker(this);
string_checker.If<HIsStringAndBranch>(object);
string_checker.And();
HValue* hash = Add<HLoadNamedField>(object, nullptr,
HObjectAccess::ForStringHashField());
HValue* hash_not_computed_mask = Add<HConstant>(String::kHashNotComputedMask);
HValue* hash_computed_test =
AddUncasted<HBitwise>(Token::BIT_AND, hash, hash_not_computed_mask);
string_checker.If<HCompareNumericAndBranch>(
hash_computed_test, graph()->GetConstant0(), Token::EQ);
string_checker.Then();
HValue* shifted_hash =
AddUncasted<HShr>(hash, Add<HConstant>(String::kHashShift));
string_checker.CaptureContinuation(continuation);
return shifted_hash;
}
template <typename CollectionType>
void HOptimizedGraphBuilder::BuildJSCollectionHas(
CallRuntime* call, const Runtime::Function* c_function) {
DCHECK(call->arguments()->length() == 2);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* key = Pop();
HValue* receiver = Pop();
NoObservableSideEffectsScope no_effects(this);
HIfContinuation continuation;
HValue* hash =
BuildStringHashLoadIfIsStringAndHashComputed(key, &continuation);
{
IfBuilder string_checker(this, &continuation);
string_checker.Then();
{
HValue* table = Add<HLoadNamedField>(
receiver, nullptr, HObjectAccess::ForJSCollectionTable());
HValue* key_index =
BuildOrderedHashTableFindEntry<CollectionType>(table, key, hash);
{
IfBuilder if_found(this);
if_found.If<HCompareNumericAndBranch>(
key_index, Add<HConstant>(CollectionType::kNotFound), Token::NE);
if_found.Then();
Push(graph()->GetConstantTrue());
if_found.Else();
Push(graph()->GetConstantFalse());
}
}
string_checker.Else();
{
Add<HPushArguments>(receiver, key);
Push(Add<HCallRuntime>(call->name(), c_function, 2));
}
}
return ast_context()->ReturnValue(Pop());
}
void HOptimizedGraphBuilder::GenerateMapHas(CallRuntime* call) {
BuildJSCollectionHas<OrderedHashMap>(
call, Runtime::FunctionForId(Runtime::kMapHas));
}
void HOptimizedGraphBuilder::GenerateSetHas(CallRuntime* call) {
BuildJSCollectionHas<OrderedHashSet>(
call, Runtime::FunctionForId(Runtime::kSetHas));
}
template <typename CollectionType>
HValue* HOptimizedGraphBuilder::BuildOrderedHashTableAddEntry(
HValue* table, HValue* key, HValue* hash,
HIfContinuation* join_continuation) {
HValue* num_buckets = Add<HLoadNamedField>(
table, nullptr,
HObjectAccess::ForOrderedHashTableNumberOfBuckets<CollectionType>());
HValue* capacity = AddUncasted<HMul>(
num_buckets, Add<HConstant>(CollectionType::kLoadFactor));
capacity->ClearFlag(HValue::kCanOverflow);
HValue* num_elements = Add<HLoadNamedField>(
table, nullptr,
HObjectAccess::ForOrderedHashTableNumberOfElements<CollectionType>());
HValue* num_deleted = Add<HLoadNamedField>(
table, nullptr, HObjectAccess::ForOrderedHashTableNumberOfDeletedElements<
CollectionType>());
HValue* used = AddUncasted<HAdd>(num_elements, num_deleted);
used->ClearFlag(HValue::kCanOverflow);
IfBuilder if_space_available(this);
if_space_available.If<HCompareNumericAndBranch>(capacity, used, Token::GT);
if_space_available.Then();
HValue* bucket = BuildOrderedHashTableHashToBucket(hash, num_buckets);
HValue* entry = used;
HValue* key_index =
BuildOrderedHashTableEntryToIndex<CollectionType>(entry, num_buckets);
HValue* bucket_index = AddUncasted<HAdd>(
bucket, Add<HConstant>(CollectionType::kHashTableStartIndex));
bucket_index->ClearFlag(HValue::kCanOverflow);
HValue* chain_entry =
Add<HLoadKeyed>(table, bucket_index, nullptr, FAST_ELEMENTS);
chain_entry->set_type(HType::Smi());
HValue* chain_index = AddUncasted<HAdd>(
key_index, Add<HConstant>(CollectionType::kChainOffset));
chain_index->ClearFlag(HValue::kCanOverflow);
Add<HStoreKeyed>(table, bucket_index, entry, FAST_ELEMENTS);
Add<HStoreKeyed>(table, chain_index, chain_entry, FAST_ELEMENTS);
Add<HStoreKeyed>(table, key_index, key, FAST_ELEMENTS);
HValue* new_num_elements =
AddUncasted<HAdd>(num_elements, graph()->GetConstant1());
new_num_elements->ClearFlag(HValue::kCanOverflow);
Add<HStoreNamedField>(
table,
HObjectAccess::ForOrderedHashTableNumberOfElements<CollectionType>(),
new_num_elements);
if_space_available.JoinContinuation(join_continuation);
return key_index;
}
void HOptimizedGraphBuilder::GenerateMapSet(CallRuntime* call) {
void HOptimizedGraphBuilder::GenerateFixedArraySet(CallRuntime* call) {
DCHECK(call->arguments()->length() == 3);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
HValue* value = Pop();
HValue* key = Pop();
HValue* receiver = Pop();
HValue* index = Pop();
HValue* object = Pop();
NoObservableSideEffectsScope no_effects(this);
HIfContinuation return_or_call_runtime_continuation(
graph()->CreateBasicBlock(), graph()->CreateBasicBlock());
HIfContinuation got_string_hash;
HValue* hash =
BuildStringHashLoadIfIsStringAndHashComputed(key, &got_string_hash);
IfBuilder string_checker(this, &got_string_hash);
string_checker.Then();
{
HValue* table = Add<HLoadNamedField>(receiver, nullptr,
HObjectAccess::ForJSCollectionTable());
HValue* key_index =
BuildOrderedHashTableFindEntry<OrderedHashMap>(table, key, hash);
{
IfBuilder if_found(this);
if_found.If<HCompareNumericAndBranch>(
key_index, Add<HConstant>(OrderedHashMap::kNotFound), Token::NE);
if_found.Then();
{
HValue* value_index = AddUncasted<HAdd>(
key_index, Add<HConstant>(OrderedHashMap::kValueOffset));
value_index->ClearFlag(HValue::kCanOverflow);
Add<HStoreKeyed>(table, value_index, value, FAST_ELEMENTS);
}
if_found.Else();
{
HIfContinuation did_add(graph()->CreateBasicBlock(),
graph()->CreateBasicBlock());
HValue* key_index = BuildOrderedHashTableAddEntry<OrderedHashMap>(
table, key, hash, &did_add);
IfBuilder if_did_add(this, &did_add);
if_did_add.Then();
{
HValue* value_index = AddUncasted<HAdd>(
key_index, Add<HConstant>(OrderedHashMap::kValueOffset));
value_index->ClearFlag(HValue::kCanOverflow);
Add<HStoreKeyed>(table, value_index, value, FAST_ELEMENTS);
}
if_did_add.JoinContinuation(&return_or_call_runtime_continuation);
}
}
}
string_checker.JoinContinuation(&return_or_call_runtime_continuation);
{
IfBuilder return_or_call_runtime(this,
&return_or_call_runtime_continuation);
return_or_call_runtime.Then();
Push(receiver);
return_or_call_runtime.Else();
Add<HPushArguments>(receiver, key, value);
Push(Add<HCallRuntime>(call->name(),
Runtime::FunctionForId(Runtime::kMapSet), 3));
}
return ast_context()->ReturnValue(Pop());
Add<HStoreKeyed>(object, index, value, FAST_HOLEY_ELEMENTS);
return ast_context()->ReturnValue(graph()->GetConstantUndefined());
}
void HOptimizedGraphBuilder::GenerateSetAdd(CallRuntime* call) {
DCHECK(call->arguments()->length() == 2);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* key = Pop();
HValue* receiver = Pop();
NoObservableSideEffectsScope no_effects(this);
HIfContinuation return_or_call_runtime_continuation(
graph()->CreateBasicBlock(), graph()->CreateBasicBlock());
HIfContinuation got_string_hash;
HValue* hash =
BuildStringHashLoadIfIsStringAndHashComputed(key, &got_string_hash);
IfBuilder string_checker(this, &got_string_hash);
string_checker.Then();
{
HValue* table = Add<HLoadNamedField>(receiver, nullptr,
HObjectAccess::ForJSCollectionTable());
HValue* key_index =
BuildOrderedHashTableFindEntry<OrderedHashSet>(table, key, hash);
{
IfBuilder if_not_found(this);
if_not_found.If<HCompareNumericAndBranch>(
key_index, Add<HConstant>(OrderedHashSet::kNotFound), Token::EQ);
if_not_found.Then();
BuildOrderedHashTableAddEntry<OrderedHashSet>(
table, key, hash, &return_or_call_runtime_continuation);
}
}
string_checker.JoinContinuation(&return_or_call_runtime_continuation);
{
IfBuilder return_or_call_runtime(this,
&return_or_call_runtime_continuation);
return_or_call_runtime.Then();
Push(receiver);
return_or_call_runtime.Else();
Add<HPushArguments>(receiver, key);
Push(Add<HCallRuntime>(call->name(),
Runtime::FunctionForId(Runtime::kSetAdd), 2));
}
return ast_context()->ReturnValue(Pop());
void HOptimizedGraphBuilder::GenerateTheHole(CallRuntime* call) {
DCHECK(call->arguments()->length() == 0);
return ast_context()->ReturnValue(graph()->GetConstantHole());
}
template <typename CollectionType>
void HOptimizedGraphBuilder::BuildJSCollectionDelete(
CallRuntime* call, const Runtime::Function* c_function) {
DCHECK(call->arguments()->length() == 2);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* key = Pop();
HValue* receiver = Pop();
NoObservableSideEffectsScope no_effects(this);
HIfContinuation return_or_call_runtime_continuation(
graph()->CreateBasicBlock(), graph()->CreateBasicBlock());
HIfContinuation got_string_hash;
HValue* hash =
BuildStringHashLoadIfIsStringAndHashComputed(key, &got_string_hash);
IfBuilder string_checker(this, &got_string_hash);
string_checker.Then();
{
HValue* table = Add<HLoadNamedField>(receiver, nullptr,
HObjectAccess::ForJSCollectionTable());
HValue* key_index =
BuildOrderedHashTableFindEntry<CollectionType>(table, key, hash);
{
IfBuilder if_found(this);
if_found.If<HCompareNumericAndBranch>(
key_index, Add<HConstant>(CollectionType::kNotFound), Token::NE);
if_found.Then();
{
// If we're removing an element, we might need to shrink.
// If we do need to shrink, we'll be bailing out to the runtime.
HValue* num_elements = Add<HLoadNamedField>(
table, nullptr, HObjectAccess::ForOrderedHashTableNumberOfElements<
CollectionType>());
num_elements = AddUncasted<HSub>(num_elements, graph()->GetConstant1());
num_elements->ClearFlag(HValue::kCanOverflow);
HValue* num_buckets = Add<HLoadNamedField>(
table, nullptr, HObjectAccess::ForOrderedHashTableNumberOfBuckets<
CollectionType>());
// threshold is capacity >> 2; we simplify this to num_buckets >> 1
// since kLoadFactor is 2.
STATIC_ASSERT(CollectionType::kLoadFactor == 2);
HValue* threshold =
AddUncasted<HShr>(num_buckets, graph()->GetConstant1());
IfBuilder if_need_not_shrink(this);
if_need_not_shrink.If<HCompareNumericAndBranch>(num_elements, threshold,
Token::GTE);
if_need_not_shrink.Then();
{
Add<HStoreKeyed>(table, key_index, graph()->GetConstantHole(),
FAST_ELEMENTS);
// For maps, also need to clear the value.
if (CollectionType::kChainOffset > 1) {
HValue* value_index =
AddUncasted<HAdd>(key_index, graph()->GetConstant1());
value_index->ClearFlag(HValue::kCanOverflow);
Add<HStoreKeyed>(table, value_index, graph()->GetConstantHole(),
FAST_ELEMENTS);
}
STATIC_ASSERT(CollectionType::kChainOffset <= 2);
HValue* num_deleted = Add<HLoadNamedField>(
table, nullptr,
HObjectAccess::ForOrderedHashTableNumberOfDeletedElements<
CollectionType>());
num_deleted = AddUncasted<HAdd>(num_deleted, graph()->GetConstant1());
num_deleted->ClearFlag(HValue::kCanOverflow);
Add<HStoreNamedField>(
table, HObjectAccess::ForOrderedHashTableNumberOfElements<
CollectionType>(),
num_elements);
Add<HStoreNamedField>(
table, HObjectAccess::ForOrderedHashTableNumberOfDeletedElements<
CollectionType>(),
num_deleted);
Push(graph()->GetConstantTrue());
}
if_need_not_shrink.JoinContinuation(
&return_or_call_runtime_continuation);
}
if_found.Else();
{
// Not found, so we're done.
Push(graph()->GetConstantFalse());
}
}
}
string_checker.JoinContinuation(&return_or_call_runtime_continuation);
{
IfBuilder return_or_call_runtime(this,
&return_or_call_runtime_continuation);
return_or_call_runtime.Then();
return_or_call_runtime.Else();
Add<HPushArguments>(receiver, key);
Push(Add<HCallRuntime>(call->name(), c_function, 2));
}
return ast_context()->ReturnValue(Pop());
}
void HOptimizedGraphBuilder::GenerateMapDelete(CallRuntime* call) {
BuildJSCollectionDelete<OrderedHashMap>(
call, Runtime::FunctionForId(Runtime::kMapDelete));
}
void HOptimizedGraphBuilder::GenerateSetDelete(CallRuntime* call) {
BuildJSCollectionDelete<OrderedHashSet>(
call, Runtime::FunctionForId(Runtime::kSetDelete));
}
void HOptimizedGraphBuilder::GenerateSetGetSize(CallRuntime* call) {
void HOptimizedGraphBuilder::GenerateJSCollectionGetTable(CallRuntime* call) {
DCHECK(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* receiver = Pop();
HValue* table = Add<HLoadNamedField>(receiver, nullptr,
HObjectAccess::ForJSCollectionTable());
HInstruction* result = New<HLoadNamedField>(
table, nullptr,
HObjectAccess::ForOrderedHashTableNumberOfElements<OrderedHashSet>());
receiver, nullptr, HObjectAccess::ForJSCollectionTable());
return ast_context()->ReturnInstruction(result, call->id());
}
void HOptimizedGraphBuilder::GenerateMapGetSize(CallRuntime* call) {
void HOptimizedGraphBuilder::GenerateStringGetRawHashField(CallRuntime* call) {
DCHECK(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* receiver = Pop();
HValue* table = Add<HLoadNamedField>(receiver, nullptr,
HObjectAccess::ForJSCollectionTable());
HValue* object = Pop();
HInstruction* result = New<HLoadNamedField>(
table, nullptr,
HObjectAccess::ForOrderedHashTableNumberOfElements<OrderedHashMap>());
object, nullptr, HObjectAccess::ForStringHashField());
return ast_context()->ReturnInstruction(result, call->id());
}

View File

@ -2222,18 +2222,14 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
F(MathLogRT) \
/* ES6 Collections */ \
F(MapClear) \
F(MapDelete) \
F(MapGet) \
F(MapGetSize) \
F(MapHas) \
F(MapInitialize) \
F(MapSet) \
F(SetAdd) \
F(SetClear) \
F(SetDelete) \
F(SetGetSize) \
F(SetHas) \
F(SetInitialize) \
F(FixedArrayGet) \
F(FixedArraySet) \
F(JSCollectionGetTable) \
F(StringGetRawHashField) \
F(TheHole) \
/* Arrays */ \
F(HasFastPackedElements) \
F(GetPrototype) \

View File

@ -2494,16 +2494,6 @@ void CEntryStub::Generate(MacroAssembler* masm) {
__ call(ebx);
// Result is in eax or edx:eax - do not destroy these registers!
// Runtime functions should not return 'the hole'. Allowing it to escape may
// lead to crashes in the IC code later.
if (FLAG_debug_code) {
Label okay;
__ cmp(eax, isolate()->factory()->the_hole_value());
__ j(not_equal, &okay, Label::kNear);
__ int3();
__ bind(&okay);
}
// Check result for exception sentinel.
Label exception_returned;
__ cmp(eax, isolate()->factory()->exception());

View File

@ -276,6 +276,33 @@ const ITERATOR_KIND_KEYS = 1;
const ITERATOR_KIND_VALUES = 2;
const ITERATOR_KIND_ENTRIES = 3;
macro FIXED_ARRAY_GET(array, index) = (%_FixedArrayGet(array, (index) | 0));
macro FIXED_ARRAY_SET(array, index, value) = (%_FixedArraySet(array, (index) | 0, value));
# TODO(adamk): Find a more robust way to force Smi representation.
macro FIXED_ARRAY_SET_SMI(array, index, value) = (FIXED_ARRAY_SET(array, index, (value) | 0));
macro ORDERED_HASH_TABLE_BUCKET_COUNT(table) = (FIXED_ARRAY_GET(table, 0));
macro ORDERED_HASH_TABLE_ELEMENT_COUNT(table) = (FIXED_ARRAY_GET(table, 1));
macro ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, count) = (FIXED_ARRAY_SET_SMI(table, 1, count));
macro ORDERED_HASH_TABLE_DELETED_COUNT(table) = (FIXED_ARRAY_GET(table, 2));
macro ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, count) = (FIXED_ARRAY_SET_SMI(table, 2, count));
macro ORDERED_HASH_TABLE_BUCKET_AT(table, bucket) = (FIXED_ARRAY_GET(table, 3 + (bucket)));
macro ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry) = (FIXED_ARRAY_SET(table, 3 + (bucket), entry));
macro ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets) = (hash & ((numBuckets) - 1));
macro ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets) = (3 + (numBuckets) + ((entry) << 1));
macro ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets)));
macro ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets) + 1));
macro ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) = (3 + (numBuckets) + ((entry) * 3));
macro ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets)));
macro ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) + 1));
macro ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) + 2));
# Must match OrderedHashTable::kNotFound.
const NOT_FOUND = -1;
# Check whether debug is active.
const DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0);
macro DEBUG_IS_STEPPING(function) = (%_DebugIsActive() != 0 && %DebugCallbackSupportsStepping(function));

View File

@ -1108,16 +1108,6 @@ void CEntryStub::Generate(MacroAssembler* masm) {
}
// Runtime functions should not return 'the hole'. Allowing it to escape may
// lead to crashes in the IC code later.
if (FLAG_debug_code) {
Label okay;
__ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
__ Branch(&okay, ne, v0, Operand(t0));
__ stop("The hole escaped");
__ bind(&okay);
}
// Check result for exception sentinel.
Label exception_returned;
__ LoadRoot(t0, Heap::kExceptionRootIndex);

View File

@ -1103,16 +1103,6 @@ void CEntryStub::Generate(MacroAssembler* masm) {
masm->InstructionsGeneratedSince(&find_ra));
}
// Runtime functions should not return 'the hole'. Allowing it to escape may
// lead to crashes in the IC code later.
if (FLAG_debug_code) {
Label okay;
__ LoadRoot(a4, Heap::kTheHoleValueRootIndex);
__ Branch(&okay, ne, v0, Operand(a4));
__ stop("The hole escaped");
__ bind(&okay);
}
// Check result for exception sentinel.
Label exception_returned;
__ LoadRoot(a4, Heap::kExceptionRootIndex);

View File

@ -807,17 +807,19 @@ Map* Object::GetRootMap(Isolate* isolate) {
Object* Object::GetHash() {
// The object is either a number, a name, an odd-ball,
// The object is either a Smi, a HeapNumber, a name, an odd-ball,
// a real JS object, or a Harmony proxy.
if (IsSmi()) {
int num = Smi::cast(this)->value();
uint32_t hash = ComputeLongHash(double_to_uint64(static_cast<double>(num)));
uint32_t hash = ComputeIntegerHash(Smi::cast(this)->value(), kZeroHashSeed);
return Smi::FromInt(hash & Smi::kMaxValue);
}
if (IsHeapNumber()) {
double num = HeapNumber::cast(this)->value();
if (std::isnan(num)) return Smi::FromInt(Smi::kMaxValue);
if (i::IsMinusZero(num)) num = 0;
if (IsSmiDouble(num)) {
return Smi::FromInt(FastD2I(num))->GetHash();
}
uint32_t hash = ComputeLongHash(double_to_uint64(num));
return Smi::FromInt(hash & Smi::kMaxValue);
}
@ -16257,20 +16259,6 @@ Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Clear(
}
template<class Derived, class Iterator, int entrysize>
Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Remove(
Handle<Derived> table, Handle<Object> key, bool* was_present) {
int entry = table->FindEntry(key);
if (entry == kNotFound) {
*was_present = false;
return table;
}
*was_present = true;
table->RemoveEntry(entry);
return Shrink(table);
}
template<class Derived, class Iterator, int entrysize>
Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Rehash(
Handle<Derived> table, int new_capacity) {
@ -16316,61 +16304,6 @@ Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Rehash(
}
template <class Derived, class Iterator, int entrysize>
int OrderedHashTable<Derived, Iterator, entrysize>::FindEntry(
Handle<Object> key, int hash) {
DCHECK(!IsObsolete());
DisallowHeapAllocation no_gc;
DCHECK(!key->IsTheHole());
for (int entry = HashToEntry(hash); entry != kNotFound;
entry = ChainAt(entry)) {
Object* candidate = KeyAt(entry);
if (candidate->SameValueZero(*key))
return entry;
}
return kNotFound;
}
template <class Derived, class Iterator, int entrysize>
int OrderedHashTable<Derived, Iterator, entrysize>::FindEntry(
Handle<Object> key) {
DisallowHeapAllocation no_gc;
Object* hash = key->GetHash();
if (!hash->IsSmi()) return kNotFound;
return FindEntry(key, Smi::cast(hash)->value());
}
template <class Derived, class Iterator, int entrysize>
int OrderedHashTable<Derived, Iterator, entrysize>::AddEntry(int hash) {
DCHECK(!IsObsolete());
int entry = UsedCapacity();
int bucket = HashToBucket(hash);
int index = EntryToIndex(entry);
Object* chain_entry = get(kHashTableStartIndex + bucket);
set(kHashTableStartIndex + bucket, Smi::FromInt(entry));
set(index + kChainOffset, chain_entry);
SetNumberOfElements(NumberOfElements() + 1);
return index;
}
template<class Derived, class Iterator, int entrysize>
void OrderedHashTable<Derived, Iterator, entrysize>::RemoveEntry(int entry) {
DCHECK(!IsObsolete());
int index = EntryToIndex(entry);
for (int i = 0; i < entrysize; ++i) {
set_the_hole(index + i);
}
SetNumberOfElements(NumberOfElements() - 1);
SetNumberOfDeletedElements(NumberOfDeletedElements() + 1);
}
template Handle<OrderedHashSet>
OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Allocate(
Isolate* isolate, int capacity, PretenureFlag pretenure);
@ -16387,21 +16320,6 @@ template Handle<OrderedHashSet>
OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Clear(
Handle<OrderedHashSet> table);
template Handle<OrderedHashSet>
OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Remove(
Handle<OrderedHashSet> table, Handle<Object> key, bool* was_present);
template int OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::FindEntry(
Handle<Object> key, int hash);
template int OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::FindEntry(
Handle<Object> key);
template int
OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::AddEntry(int hash);
template void
OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::RemoveEntry(int entry);
template Handle<OrderedHashMap>
OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Allocate(
@ -16419,69 +16337,6 @@ template Handle<OrderedHashMap>
OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Clear(
Handle<OrderedHashMap> table);
template Handle<OrderedHashMap>
OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Remove(
Handle<OrderedHashMap> table, Handle<Object> key, bool* was_present);
template int OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::FindEntry(
Handle<Object> key, int hash);
template int OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::FindEntry(
Handle<Object> key);
template int
OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::AddEntry(int hash);
template void
OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::RemoveEntry(int entry);
bool OrderedHashSet::Contains(Handle<Object> key) {
return FindEntry(key) != kNotFound;
}
Handle<OrderedHashSet> OrderedHashSet::Add(Handle<OrderedHashSet> table,
Handle<Object> key) {
int hash = GetOrCreateHash(table->GetIsolate(), key)->value();
if (table->FindEntry(key, hash) != kNotFound) return table;
table = EnsureGrowable(table);
int index = table->AddEntry(hash);
table->set(index, *key);
return table;
}
Object* OrderedHashMap::Lookup(Handle<Object> key) {
DisallowHeapAllocation no_gc;
int entry = FindEntry(key);
if (entry == kNotFound) return GetHeap()->the_hole_value();
return ValueAt(entry);
}
Handle<OrderedHashMap> OrderedHashMap::Put(Handle<OrderedHashMap> table,
Handle<Object> key,
Handle<Object> value) {
DCHECK(!key->IsTheHole());
int hash = GetOrCreateHash(table->GetIsolate(), key)->value();
int entry = table->FindEntry(key, hash);
if (entry != kNotFound) {
table->set(table->EntryToIndex(entry) + kValueOffset, *value);
return table;
}
table = EnsureGrowable(table);
int index = table->AddEntry(hash);
table->set(index, *key);
table->set(index + kValueOffset, *value);
return table;
}
template<class Derived, class TableType>
void OrderedHashTableIterator<Derived, TableType>::Transition() {

View File

@ -3974,17 +3974,6 @@ class OrderedHashTable: public FixedArray {
// exisiting iterators can be updated.
static Handle<Derived> Clear(Handle<Derived> table);
// Returns an OrderedHashTable (possibly |table|) where |key| has been
// removed.
static Handle<Derived> Remove(Handle<Derived> table, Handle<Object> key,
bool* was_present);
// Returns kNotFound if the key isn't present.
int FindEntry(Handle<Object> key, int hash);
// Like the above, but doesn't require the caller to provide a hash.
int FindEntry(Handle<Object> key);
int NumberOfElements() {
return Smi::cast(get(kNumberOfElementsIndex))->value();
}
@ -3999,15 +3988,6 @@ class OrderedHashTable: public FixedArray {
return Smi::cast(get(kNumberOfBucketsIndex))->value();
}
// Returns the index into the data table where the new entry
// should be placed. The table is assumed to have enough space
// for a new entry.
int AddEntry(int hash);
// Removes the entry, and puts the_hole in entrysize pointers
// (leaving the hash table chain intact).
void RemoveEntry(int entry);
// Returns an index into |this| for the given entry.
int EntryToIndex(int entry) {
return kHashTableStartIndex + NumberOfBuckets() + (entry * kEntrySize);
@ -4078,20 +4058,6 @@ class OrderedHashTable: public FixedArray {
return NumberOfBuckets() * kLoadFactor;
}
// Returns the next entry for the given entry.
int ChainAt(int entry) {
return Smi::cast(get(EntryToIndex(entry) + kChainOffset))->value();
}
int HashToBucket(int hash) {
return hash & (NumberOfBuckets() - 1);
}
int HashToEntry(int hash) {
int bucket = HashToBucket(hash);
return Smi::cast(get(kHashTableStartIndex + bucket))->value();
}
void SetNextTable(Derived* next_table) {
set(kNextTableIndex, next_table);
}
@ -4115,27 +4081,17 @@ class OrderedHashSet: public OrderedHashTable<
OrderedHashSet, JSSetIterator, 1> {
public:
DECLARE_CAST(OrderedHashSet)
bool Contains(Handle<Object> key);
static Handle<OrderedHashSet> Add(
Handle<OrderedHashSet> table, Handle<Object> key);
};
class JSMapIterator;
class OrderedHashMap:public OrderedHashTable<
OrderedHashMap, JSMapIterator, 2> {
class OrderedHashMap
: public OrderedHashTable<OrderedHashMap, JSMapIterator, 2> {
public:
DECLARE_CAST(OrderedHashMap)
Object* Lookup(Handle<Object> key);
static Handle<OrderedHashMap> Put(
Handle<OrderedHashMap> table,
Handle<Object> key,
Handle<Object> value);
Object* ValueAt(int entry) {
return get(EntryToIndex(entry) + kValueOffset);
}

View File

@ -1086,16 +1086,6 @@ void CEntryStub::Generate(MacroAssembler* masm) {
}
#endif
// Runtime functions should not return 'the hole'. Allowing it to escape may
// lead to crashes in the IC code later.
if (FLAG_debug_code) {
Label okay;
__ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
__ bne(&okay);
__ stop("The hole escaped");
__ bind(&okay);
}
// Check result for exception sentinel.
Label exception_returned;
__ CompareRoot(r3, Heap::kExceptionRootIndex);

View File

@ -11,6 +11,60 @@
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_StringGetRawHashField) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
return *isolate->factory()->NewNumberFromUint(string->hash_field());
}
RUNTIME_FUNCTION(Runtime_TheHole) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 0);
return isolate->heap()->the_hole_value();
}
RUNTIME_FUNCTION(Runtime_FixedArrayGet) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_CHECKED(FixedArray, object, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
return object->get(index);
}
RUNTIME_FUNCTION(Runtime_FixedArraySet) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 3);
CONVERT_ARG_CHECKED(FixedArray, object, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
CONVERT_ARG_CHECKED(Object, value, 2);
object->set(index, value);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_JSCollectionGetTable) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, object, 0);
RUNTIME_ASSERT(object->IsJSSet() || object->IsJSMap());
return static_cast<JSCollection*>(object)->table();
}
RUNTIME_FUNCTION(Runtime_GenericHash) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
Handle<Smi> hash = Object::GetOrCreateHash(isolate, object);
return *hash;
}
RUNTIME_FUNCTION(Runtime_SetInitialize) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
@ -21,38 +75,25 @@ RUNTIME_FUNCTION(Runtime_SetInitialize) {
}
RUNTIME_FUNCTION(Runtime_SetAdd) {
RUNTIME_FUNCTION(Runtime_SetGrow) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
table = OrderedHashSet::Add(table, key);
table = OrderedHashSet::EnsureGrowable(table);
holder->set_table(*table);
return *holder;
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetHas) {
RUNTIME_FUNCTION(Runtime_SetShrink) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
return isolate->heap()->ToBoolean(table->Contains(key));
}
RUNTIME_FUNCTION(Runtime_SetDelete) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
bool was_present = false;
table = OrderedHashSet::Remove(table, key, &was_present);
table = OrderedHashSet::Shrink(table);
holder->set_table(*table);
return isolate->heap()->ToBoolean(was_present);
return isolate->heap()->undefined_value();
}
@ -67,15 +108,6 @@ RUNTIME_FUNCTION(Runtime_SetClear) {
}
RUNTIME_FUNCTION(Runtime_SetGetSize) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
return Smi::FromInt(table->NumberOfElements());
}
RUNTIME_FUNCTION(Runtime_SetIteratorInitialize) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
@ -141,39 +173,14 @@ RUNTIME_FUNCTION(Runtime_MapInitialize) {
}
RUNTIME_FUNCTION(Runtime_MapGet) {
RUNTIME_FUNCTION(Runtime_MapShrink) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
Handle<Object> lookup(table->Lookup(key), isolate);
return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
}
RUNTIME_FUNCTION(Runtime_MapHas) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
Handle<Object> lookup(table->Lookup(key), isolate);
return isolate->heap()->ToBoolean(!lookup->IsTheHole());
}
RUNTIME_FUNCTION(Runtime_MapDelete) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
bool was_present = false;
Handle<OrderedHashMap> new_table =
OrderedHashMap::Remove(table, key, &was_present);
holder->set_table(*new_table);
return isolate->heap()->ToBoolean(was_present);
table = OrderedHashMap::Shrink(table);
holder->set_table(*table);
return isolate->heap()->undefined_value();
}
@ -188,25 +195,14 @@ RUNTIME_FUNCTION(Runtime_MapClear) {
}
RUNTIME_FUNCTION(Runtime_MapSet) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
Handle<OrderedHashMap> new_table = OrderedHashMap::Put(table, key, value);
holder->set_table(*new_table);
return *holder;
}
RUNTIME_FUNCTION(Runtime_MapGetSize) {
RUNTIME_FUNCTION(Runtime_MapGrow) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
return Smi::FromInt(table->NumberOfElements());
table = OrderedHashMap::EnsureGrowable(table);
holder->set_table(*table);
return isolate->heap()->undefined_value();
}

View File

@ -301,6 +301,13 @@ namespace internal {
F(GetConstructTrap, 1, 1) \
F(Fix, 1, 1) \
\
/* ES6 collections */ \
F(MapGrow, 1, 1) \
F(MapShrink, 1, 1) \
F(SetGrow, 1, 1) \
F(SetShrink, 1, 1) \
F(GenericHash, 1, 1) \
\
/* ES6 collection iterators */ \
F(SetIteratorInitialize, 3, 1) \
F(SetIteratorClone, 1, 1) \
@ -705,18 +712,14 @@ namespace internal {
F(MathLogRT, 1, 1) \
/* ES6 Collections */ \
F(MapClear, 1, 1) \
F(MapDelete, 2, 1) \
F(MapGet, 2, 1) \
F(MapGetSize, 1, 1) \
F(MapHas, 2, 1) \
F(MapInitialize, 1, 1) \
F(MapSet, 3, 1) \
F(SetAdd, 2, 1) \
F(SetClear, 1, 1) \
F(SetDelete, 2, 1) \
F(SetGetSize, 1, 1) \
F(SetHas, 2, 1) \
F(SetInitialize, 1, 1) \
F(FixedArrayGet, 2, 1) \
F(FixedArraySet, 3, 1) \
F(JSCollectionGetTable, 1, 1) \
F(StringGetRawHashField, 1, 1) \
F(TheHole, 0, 1) \
/* Arrays */ \
F(HasFastPackedElements, 1, 1) \
F(GetPrototype, 1, 1) \

View File

@ -21,7 +21,7 @@ function SameCallSiteElements(rawStrings, other) {
function GetCachedCallSite(siteObj, hash) {
var obj = %MapGet(callSiteCache, hash);
var obj = %_CallFunction(callSiteCache, hash, $MapGet);
if (IS_UNDEFINED(obj)) return;
@ -33,13 +33,13 @@ function GetCachedCallSite(siteObj, hash) {
function SetCachedCallSite(siteObj, hash) {
var obj = %MapGet(callSiteCache, hash);
var obj = %_CallFunction(callSiteCache, hash, $MapGet);
var array;
if (IS_UNDEFINED(obj)) {
array = new InternalArray(1);
array[0] = siteObj;
%MapSet(callSiteCache, hash, array);
%_CallFunction(callSiteCache, hash, array, $MapSet);
} else {
obj.push(siteObj);
}

View File

@ -2415,16 +2415,6 @@ void CEntryStub::Generate(MacroAssembler* masm) {
}
#endif // _WIN64
// Runtime functions should not return 'the hole'. Allowing it to escape may
// lead to crashes in the IC code later.
if (FLAG_debug_code) {
Label okay;
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
__ j(not_equal, &okay, Label::kNear);
__ int3();
__ bind(&okay);
}
// Check result for exception sentinel.
Label exception_returned;
__ CompareRoot(rax, Heap::kExceptionRootIndex);

View File

@ -141,7 +141,6 @@
'test-mementos.cc',
'test-migrations.cc',
'test-object-observe.cc',
'test-ordered-hash-table.cc',
'test-parsing.cc',
'test-platform.cc',
'test-profile-generator.cc',

View File

@ -110,7 +110,6 @@ TEST(HashMap) {
v8::HandleScope scope(context->GetIsolate());
Isolate* isolate = CcTest::i_isolate();
TestHashMap(ObjectHashTable::New(isolate, 23));
TestHashMap(isolate->factory()->NewOrderedHashMap());
}
@ -199,15 +198,6 @@ static void TestHashSetCausesGC(Handle<HashSet> table) {
table = HashSet::Add(table, key);
CHECK(gc_count < isolate->heap()->gc_count());
}
TEST(ObjectHashSetCausesGC) {
i::FLAG_stress_compaction = false;
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
Isolate* isolate = CcTest::i_isolate();
TestHashSetCausesGC(isolate->factory()->NewOrderedHashSet());
}
#endif
@ -246,9 +236,7 @@ TEST(ObjectHashTableCausesGC) {
v8::HandleScope scope(context->GetIsolate());
Isolate* isolate = CcTest::i_isolate();
TestHashMapCausesGC(ObjectHashTable::New(isolate, 1));
TestHashMapCausesGC(isolate->factory()->NewOrderedHashMap());
}
#endif
}

View File

@ -1,179 +0,0 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdlib.h>
#include "src/v8.h"
#include "src/factory.h"
#include "test/cctest/cctest.h"
namespace {
using namespace v8::internal;
TEST(Set) {
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
HandleScope scope(isolate);
Handle<OrderedHashSet> ordered_set = factory->NewOrderedHashSet();
CHECK_EQ(2, ordered_set->NumberOfBuckets());
CHECK_EQ(0, ordered_set->NumberOfElements());
CHECK_EQ(0, ordered_set->NumberOfDeletedElements());
Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
Handle<JSObject> obj = factory->NewJSObjectFromMap(map);
CHECK(!ordered_set->Contains(obj));
ordered_set = OrderedHashSet::Add(ordered_set, obj);
CHECK_EQ(1, ordered_set->NumberOfElements());
CHECK(ordered_set->Contains(obj));
bool was_present = false;
ordered_set = OrderedHashSet::Remove(ordered_set, obj, &was_present);
CHECK(was_present);
CHECK_EQ(0, ordered_set->NumberOfElements());
CHECK(!ordered_set->Contains(obj));
// Removing a not-present object should set was_present to false.
ordered_set = OrderedHashSet::Remove(ordered_set, obj, &was_present);
CHECK(!was_present);
// Test for collisions/chaining
Handle<JSObject> obj1 = factory->NewJSObjectFromMap(map);
ordered_set = OrderedHashSet::Add(ordered_set, obj1);
Handle<JSObject> obj2 = factory->NewJSObjectFromMap(map);
ordered_set = OrderedHashSet::Add(ordered_set, obj2);
Handle<JSObject> obj3 = factory->NewJSObjectFromMap(map);
ordered_set = OrderedHashSet::Add(ordered_set, obj3);
CHECK_EQ(3, ordered_set->NumberOfElements());
CHECK(ordered_set->Contains(obj1));
CHECK(ordered_set->Contains(obj2));
CHECK(ordered_set->Contains(obj3));
// Test growth
ordered_set = OrderedHashSet::Add(ordered_set, obj);
Handle<JSObject> obj4 = factory->NewJSObjectFromMap(map);
ordered_set = OrderedHashSet::Add(ordered_set, obj4);
CHECK(ordered_set->Contains(obj));
CHECK(ordered_set->Contains(obj1));
CHECK(ordered_set->Contains(obj2));
CHECK(ordered_set->Contains(obj3));
CHECK(ordered_set->Contains(obj4));
CHECK_EQ(5, ordered_set->NumberOfElements());
CHECK_EQ(0, ordered_set->NumberOfDeletedElements());
CHECK_EQ(4, ordered_set->NumberOfBuckets());
// Test shrinking
ordered_set = OrderedHashSet::Remove(ordered_set, obj, &was_present);
CHECK(was_present);
ordered_set = OrderedHashSet::Remove(ordered_set, obj1, &was_present);
CHECK(was_present);
ordered_set = OrderedHashSet::Remove(ordered_set, obj2, &was_present);
CHECK(was_present);
ordered_set = OrderedHashSet::Remove(ordered_set, obj3, &was_present);
CHECK(was_present);
CHECK_EQ(1, ordered_set->NumberOfElements());
CHECK_EQ(2, ordered_set->NumberOfBuckets());
}
TEST(Map) {
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
HandleScope scope(isolate);
Handle<OrderedHashMap> ordered_map = factory->NewOrderedHashMap();
CHECK_EQ(2, ordered_map->NumberOfBuckets());
CHECK_EQ(0, ordered_map->NumberOfElements());
CHECK_EQ(0, ordered_map->NumberOfDeletedElements());
Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
Handle<JSObject> obj = factory->NewJSObjectFromMap(map);
Handle<JSObject> val = factory->NewJSObjectFromMap(map);
CHECK(ordered_map->Lookup(obj)->IsTheHole());
ordered_map = OrderedHashMap::Put(ordered_map, obj, val);
CHECK_EQ(1, ordered_map->NumberOfElements());
Object* lookup = ordered_map->Lookup(obj);
CHECK(lookup->SameValue(*val));
bool was_present = false;
ordered_map = OrderedHashMap::Remove(ordered_map, obj, &was_present);
CHECK(was_present);
CHECK_EQ(0, ordered_map->NumberOfElements());
CHECK(ordered_map->Lookup(obj)->IsTheHole());
// Test for collisions/chaining
Handle<JSObject> obj1 = factory->NewJSObjectFromMap(map);
Handle<JSObject> obj2 = factory->NewJSObjectFromMap(map);
Handle<JSObject> obj3 = factory->NewJSObjectFromMap(map);
Handle<JSObject> val1 = factory->NewJSObjectFromMap(map);
Handle<JSObject> val2 = factory->NewJSObjectFromMap(map);
Handle<JSObject> val3 = factory->NewJSObjectFromMap(map);
ordered_map = OrderedHashMap::Put(ordered_map, obj1, val1);
ordered_map = OrderedHashMap::Put(ordered_map, obj2, val2);
ordered_map = OrderedHashMap::Put(ordered_map, obj3, val3);
CHECK_EQ(3, ordered_map->NumberOfElements());
lookup = ordered_map->Lookup(obj1);
CHECK(lookup->SameValue(*val1));
lookup = ordered_map->Lookup(obj2);
CHECK(lookup->SameValue(*val2));
lookup = ordered_map->Lookup(obj3);
CHECK(lookup->SameValue(*val3));
// Test growth
ordered_map = OrderedHashMap::Put(ordered_map, obj, val);
Handle<JSObject> obj4 = factory->NewJSObjectFromMap(map);
Handle<JSObject> val4 = factory->NewJSObjectFromMap(map);
ordered_map = OrderedHashMap::Put(ordered_map, obj4, val4);
lookup = ordered_map->Lookup(obj);
CHECK(lookup->SameValue(*val));
lookup = ordered_map->Lookup(obj1);
CHECK(lookup->SameValue(*val1));
lookup = ordered_map->Lookup(obj2);
CHECK(lookup->SameValue(*val2));
lookup = ordered_map->Lookup(obj3);
CHECK(lookup->SameValue(*val3));
lookup = ordered_map->Lookup(obj4);
CHECK(lookup->SameValue(*val4));
CHECK_EQ(5, ordered_map->NumberOfElements());
CHECK_EQ(4, ordered_map->NumberOfBuckets());
// Test shrinking
ordered_map = OrderedHashMap::Remove(ordered_map, obj, &was_present);
CHECK(was_present);
ordered_map = OrderedHashMap::Remove(ordered_map, obj1, &was_present);
CHECK(was_present);
ordered_map = OrderedHashMap::Remove(ordered_map, obj2, &was_present);
CHECK(was_present);
ordered_map = OrderedHashMap::Remove(ordered_map, obj3, &was_present);
CHECK(was_present);
CHECK_EQ(1, ordered_map->NumberOfElements());
CHECK_EQ(2, ordered_map->NumberOfBuckets());
}
}