// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler/state-values-utils.h" namespace v8 { namespace internal { namespace compiler { StateValuesCache::StateValuesCache(JSGraph* js_graph) : js_graph_(js_graph), hash_map_(AreKeysEqual, ZoneHashMap::kDefaultHashMapCapacity, ZoneAllocationPolicy(zone())), working_space_(zone()), empty_state_values_(nullptr) {} // static bool StateValuesCache::AreKeysEqual(void* key1, void* key2) { NodeKey* node_key1 = reinterpret_cast(key1); NodeKey* node_key2 = reinterpret_cast(key2); if (node_key1->node == nullptr) { if (node_key2->node == nullptr) { return AreValueKeysEqual(reinterpret_cast(key1), reinterpret_cast(key2)); } else { return IsKeysEqualToNode(reinterpret_cast(key1), node_key2->node); } } else { if (node_key2->node == nullptr) { // If the nodes are already processed, they must be the same. return IsKeysEqualToNode(reinterpret_cast(key2), node_key1->node); } else { return node_key1->node == node_key2->node; } } UNREACHABLE(); } // static bool StateValuesCache::IsKeysEqualToNode(StateValuesKey* key, Node* node) { if (key->count != static_cast(node->InputCount())) { return false; } for (size_t i = 0; i < key->count; i++) { if (key->values[i] != node->InputAt(static_cast(i))) { return false; } } return true; } // static bool StateValuesCache::AreValueKeysEqual(StateValuesKey* key1, StateValuesKey* key2) { if (key1->count != key2->count) { return false; } for (size_t i = 0; i < key1->count; i++) { if (key1->values[i] != key2->values[i]) { return false; } } return true; } Node* StateValuesCache::GetEmptyStateValues() { if (empty_state_values_ == nullptr) { empty_state_values_ = graph()->NewNode(common()->StateValues(0)); } return empty_state_values_; } NodeVector* StateValuesCache::GetWorkingSpace(size_t level) { while (working_space_.size() <= level) { void* space = zone()->New(sizeof(NodeVector)); working_space_.push_back(new (space) NodeVector(kMaxInputCount, nullptr, zone())); } return working_space_[level]; } namespace { int StateValuesHashKey(Node** nodes, size_t count) { size_t hash = count; for (size_t i = 0; i < count; i++) { hash = hash * 23 + nodes[i]->id(); } return static_cast(hash & 0x7fffffff); } } // namespace Node* StateValuesCache::GetValuesNodeFromCache(Node** nodes, size_t count) { StateValuesKey key(count, nodes); int hash = StateValuesHashKey(nodes, count); ZoneHashMap::Entry* lookup = hash_map_.LookupOrInsert(&key, hash, ZoneAllocationPolicy(zone())); DCHECK_NOT_NULL(lookup); Node* node; if (lookup->value == nullptr) { int input_count = static_cast(count); node = graph()->NewNode(common()->StateValues(input_count), input_count, nodes); NodeKey* new_key = new (zone()->New(sizeof(NodeKey))) NodeKey(node); lookup->key = new_key; lookup->value = node; } else { node = reinterpret_cast(lookup->value); } return node; } class StateValuesCache::ValueArrayIterator { public: ValueArrayIterator(Node** values, size_t count) : values_(values), count_(count), current_(0) {} void Advance() { if (!done()) { current_++; } } bool done() { return current_ >= count_; } Node* node() { DCHECK(!done()); return values_[current_]; } private: Node** values_; size_t count_; size_t current_; }; Node* StateValuesCache::BuildTree(ValueArrayIterator* it, size_t max_height) { if (max_height == 0) { Node* node = it->node(); it->Advance(); return node; } DCHECK(!it->done()); NodeVector* buffer = GetWorkingSpace(max_height); size_t count = 0; for (; count < kMaxInputCount; count++) { if (it->done()) break; (*buffer)[count] = BuildTree(it, max_height - 1); } if (count == 1) { return (*buffer)[0]; } else { return GetValuesNodeFromCache(&(buffer->front()), count); } } Node* StateValuesCache::GetNodeForValues(Node** values, size_t count) { #if DEBUG for (size_t i = 0; i < count; i++) { DCHECK_NE(values[i]->opcode(), IrOpcode::kStateValues); DCHECK_NE(values[i]->opcode(), IrOpcode::kTypedStateValues); } #endif if (count == 0) { return GetEmptyStateValues(); } size_t height = 0; size_t max_nodes = 1; while (count > max_nodes) { height++; max_nodes *= kMaxInputCount; } ValueArrayIterator it(values, count); Node* tree = BuildTree(&it, height); // If the 'tree' is a single node, equip it with a StateValues wrapper. if (tree->opcode() != IrOpcode::kStateValues && tree->opcode() != IrOpcode::kTypedStateValues) { tree = GetValuesNodeFromCache(&tree, 1); } return tree; } StateValuesAccess::iterator::iterator(Node* node) : current_depth_(0) { // A hacky way initialize - just set the index before the node we want // to process and then advance to it. stack_[current_depth_].node = node; stack_[current_depth_].index = -1; Advance(); } StateValuesAccess::iterator::StatePos* StateValuesAccess::iterator::Top() { DCHECK(current_depth_ >= 0); DCHECK(current_depth_ < kMaxInlineDepth); return &(stack_[current_depth_]); } void StateValuesAccess::iterator::Push(Node* node) { current_depth_++; CHECK(current_depth_ < kMaxInlineDepth); stack_[current_depth_].node = node; stack_[current_depth_].index = 0; } void StateValuesAccess::iterator::Pop() { DCHECK(current_depth_ >= 0); current_depth_--; } bool StateValuesAccess::iterator::done() { return current_depth_ < 0; } void StateValuesAccess::iterator::Advance() { // Advance the current index. Top()->index++; // Fix up the position to point to a valid node. while (true) { // TODO(jarin): Factor to a separate method. Node* node = Top()->node; int index = Top()->index; if (index >= node->InputCount()) { // Pop stack and move to the next sibling. Pop(); if (done()) { // Stack is exhausted, we have reached the end. return; } Top()->index++; } else if (node->InputAt(index)->opcode() == IrOpcode::kStateValues || node->InputAt(index)->opcode() == IrOpcode::kTypedStateValues) { // Nested state, we need to push to the stack. Push(node->InputAt(index)); } else { // We are on a valid node, we can stop the iteration. return; } } } Node* StateValuesAccess::iterator::node() { return Top()->node->InputAt(Top()->index); } MachineType StateValuesAccess::iterator::type() { Node* state = Top()->node; if (state->opcode() == IrOpcode::kStateValues) { return MachineType::AnyTagged(); } else { DCHECK_EQ(IrOpcode::kTypedStateValues, state->opcode()); const ZoneVector* types = OpParameter*>(state); return (*types)[Top()->index]; } } bool StateValuesAccess::iterator::operator!=(iterator& other) { // We only allow comparison with end(). CHECK(other.done()); return !done(); } StateValuesAccess::iterator& StateValuesAccess::iterator::operator++() { Advance(); return *this; } StateValuesAccess::TypedNode StateValuesAccess::iterator::operator*() { return TypedNode(node(), type()); } size_t StateValuesAccess::size() { size_t count = 0; for (int i = 0; i < node_->InputCount(); i++) { if (node_->InputAt(i)->opcode() == IrOpcode::kStateValues || node_->InputAt(i)->opcode() == IrOpcode::kTypedStateValues) { count += StateValuesAccess(node_->InputAt(i)).size(); } else { count++; } } return count; } } // namespace compiler } // namespace internal } // namespace v8