Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(406)

Unified Diff: src/hydrogen.cc

Issue 11365174: A change in the way we place TransitionElementKinds in the tree. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Now includes optimization of codegen for transition elementskind instruction Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/hydrogen.h ('k') | src/hydrogen-instructions.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/hydrogen.cc
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index d1e5b51a5e3e94ce7aac330bcf6df14a9ba0f3d4..7a1f542e18804946f340d58d12407cfcccdce1be 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -71,7 +71,8 @@ HBasicBlock::HBasicBlock(HGraph* graph)
parent_loop_header_(NULL),
is_inline_return_target_(false),
is_deoptimizing_(false),
- dominates_loop_successors_(false) { }
+ dominates_loop_successors_(false),
+ is_osr_entry_(false) { }
void HBasicBlock::AttachLoopInformation() {
@@ -698,6 +699,7 @@ HGraph::HGraph(CompilationInfo* info)
values_(16, info->zone()),
phi_list_(NULL),
uint32_instructions_(NULL),
+ array_instructions_(NULL),
info_(info),
zone_(info->zone()),
is_recursive_(false),
@@ -2449,6 +2451,901 @@ void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
}
+// Maintain a dictionary of "map family" + elements kind to a handle with the
+// appropriate map.
+class MapHandleDictionary BASE_EMBEDDED {
+ public:
+ explicit MapHandleDictionary(Zone* zone) :
+ indexer_(IndexMatch, ZoneAllocationPolicy(zone)),
+ zone_(zone) {
+ }
+
+ Handle<Map> Lookup(Handle<Map> family, ElementsKind kind) {
+ IndexKey lookupKey(family, kind);
+ IndexKeyTable::Iterator i = indexer_.find(
+ &lookupKey, false, ZoneAllocationPolicy(zone_));
+ if (i != indexer_.end()) {
+ return i->second->map;
+ }
+
+ return Handle<Map>::null();
+ }
+
+ void Add(Handle<Map> handle) {
+ // This does violence to the map family concept, but it's a way to store
+ // semi-unknown maps that we get from mapcheck instructions.
+ if (Lookup(handle, handle->elements_kind()).is_null()) {
+ Insert(handle, handle);
+ }
+ }
+
+ void InsertIfMissing(Handle<Map> handle, Handle<Map> family) {
+ if (Lookup(handle, handle->elements_kind()).is_null()) {
+ Insert(handle, family);
+ }
+ }
+
+ private:
+ struct IndexKey: public ZoneObject {
+ IndexKey(Handle<Map> family_in, ElementsKind kind_in) :
+ family(family_in),
+ kind(kind_in) {
+ }
+
+ int Hash() {
+ return family->Hash() + (31*kind);
+ }
+
+ Handle<Map> family;
+ ElementsKind kind;
+ };
+
+ struct Value: public ZoneObject {
+ explicit Value(Handle<Map> map_in) :
+ map(map_in) {
+ }
+
+ Handle<Map> map;
+ };
+
+ static bool IndexMatch(void* key1, void* key2) {
+ IndexKey* k1 = reinterpret_cast<IndexKey*>(key1);
+ IndexKey* k2 = reinterpret_cast<IndexKey*>(key2);
+ return k1->Hash() == k2->Hash();
+ }
+
+ void Insert(Handle<Map> handle, Handle<Map> family) {
+ IndexKey *key = new(zone()) IndexKey(family, handle->elements_kind());
+ IndexKeyTable::Iterator iki = indexer_.find(key, true,
+ ZoneAllocationPolicy(zone()));
+ ASSERT(iki != indexer_.end());
+ iki->second = new(zone()) Value(handle);
+
+ key = new(zone()) IndexKey(handle, handle->elements_kind());
+ iki = indexer_.find(key, true,
+ ZoneAllocationPolicy(zone()));
+ ASSERT(iki != indexer_.end());
+ if (iki->second == NULL) {
+ iki->second = new(zone()) Value(handle);
+ }
+ }
+
+ Zone* zone() { return zone_; }
+
+ typedef TemplateHashMap<IndexKey, Value, ZoneAllocationPolicy>
+ IndexKeyTable;
+ IndexKeyTable indexer_;
+ Zone* zone_;
+};
+
+
+class ResolutionTableValue: public ZoneObject {
+ public:
+ ResolutionTableValue(HValue* key, HGraph* graph) :
+ poisoned_(false),
+ extra_data_(NULL) {
+ if (key->IsArrayInstruction()) {
+ Initialize(HArrayInstruction::cast(key));
+ } else if (key->IsPhi()) {
+ Initialize(HPhi::cast(key), graph->GetMaximumValueID(), graph->zone());
+ }
+ }
+
+ Handle<Map> to() { return to_; }
+ Handle<Map> family() { return family_; }
+ bool poisoned() { return poisoned_; }
+
+ typedef EnumSet<ElementsKind> ElementsKindSet;
+ ElementsKindSet from_set() { return from_elements_; }
+
+ bool HasStrongInformation() {
+ return (!to().is_null() && !family().is_null()) || poisoned_;
+ }
+ bool HasWeakInformation() { return !to().is_null() && family().is_null(); }
+
+ bool Merge(ResolutionTableValue* from_value,
+ MapHandleDictionary* map_handles) {
+ // a) Poison must always be imbibed
+ if (from_value->poisoned() && !poisoned()) {
+ poisoned_ = true;
+ return true;
+ }
+
+ // b) from_value has no information. No change.
+ if (from_value->to().is_null()) {
+ return false;
+ }
+
+ // c) We are uninitialized or have weaker information.
+ if (to().is_null() || (HasWeakInformation() &&
+ from_value->HasStrongInformation())) {
+ to_ = from_value->to();
+ from_elements_.Add(from_value->from_set());
+ family_ = from_value->family();
+ return true;
+ }
+
+ if (from_value->HasWeakInformation()) {
+ // The from node has weak information, and we have some (weak or strong)
+ // information. Do not take advice from the weak information node.
+ return false;
+ }
+
+ ASSERT(HasStrongInformation() && from_value->HasStrongInformation());
+
+ // d) We are both initialized. Negotiate
+ if (!from_value->family().is_identical_to(family())) {
+ // We cannot change families!
+ poisoned_ = true;
+ return true;
+ }
+
+ bool changed = false;
+ if (!to().is_identical_to(from_value->to())) {
+ // figure out unified map
+ ElementsKind unified_elements_kind = GetUnifiedFastElementsKind(
+ to()->elements_kind(),
+ from_value->to()->elements_kind());
+ Handle<Map> unified_map_handle = map_handles->Lookup(family(),
+ unified_elements_kind);
+ if (unified_map_handle.is_null()) {
+ poisoned_ = true;
+ return true;
+ }
+
+ ASSERT(to_->elements_kind() == unified_map_handle->elements_kind() ||
+ IsMoreGeneralElementsKindTransition(to_->elements_kind(),
+ unified_map_handle->elements_kind()));
+ to_ = unified_map_handle;
+ changed = true;
+ }
+
+ if (!(from_set() == from_value->from_set())) {
+ from_elements_.Add(from_value->from_set());
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ void SetPoisoned() {
+ poisoned_ = true;
+ to_ = Handle<Map>::null();
+ family_ = Handle<Map>::null();
+ from_elements_.RemoveAll();
+ }
+
+ bool VisitedBy(HValue* value) {
+ ASSERT(visited_set() != NULL);
+ return visited_set()->Contains(value->id());
+ }
+
+ void MarkVisitedBy(HValue* value) {
+ ASSERT(visited_set() != NULL);
+ visited_set()->Add(value->id());
+ }
+
+ void ClearVisitedBy() {
+ ASSERT(visited_set() != NULL);
+ visited_set()->Clear();
+ }
+
+ void SetWeakInformation(Handle<Map> to_map) {
+ ASSERT(!to_map.is_null());
+ ASSERT(!HasStrongInformation() && !HasWeakInformation());
+ to_ = to_map;
+ }
+
+ // For instruction emission
+ void InsertTransitionElementsKind(HGraph* graph, HValue* root,
+ Handle<Map> from, Handle<Map> to,
+ bool special_case) {
+ if (placement_data() == NULL) {
+ set_placement_data(new(graph->zone()) PlacementData(
+ HInstruction::cast(root)));
+ }
+
+ PlacementData* data = placement_data();
+ if (data->from_elements.Contains(from->elements_kind())) {
+ return;
+ }
+
+ if (*from == *to) {
+ return;
+ }
+
+ // If root is a fastliteral, we can get a performance boost by asking that
+ // the boilerplate array object be transitioned and saved that way.
+ if (root->IsFastLiteral()) {
+ HFastLiteral* literal = HFastLiteral::cast(root);
+ if (!literal->TransitionRequested()) {
+ literal->SetTransitionTo(to->elements_kind());
+ }
+ ASSERT(literal->TransitionTo() == to->elements_kind());
+ // No need for any transition instructions. The transition will
+ // have been completed.
+ return;
+ }
+
+ // At the site, we maintain a most recent instruction we added, and
+ // new instructions should appear at the end of the chain.
+ HInstruction* add_after = data->last_in_chain;
+ if (add_after == NULL) {
+ // This is the first instruction to add.
+ // We must emit a checknonsmi
+ add_after = new(graph->zone()) HCheckNonSmi(data->transition_input);
+ HInstruction* add_after_location = data->transition_input;
+ if (data->transition_input->block()->is_osr_entry()) {
+ // If our root is in an OSR block, we need to go after the OsrEntry
+ // instruction, because that is where OSR jumps to.
+ ASSERT(data->transition_input->IsUnknownOSRValue());
+ do {
+ add_after_location = add_after_location->next();
+ } while (!add_after_location->IsGoto());
+ add_after->InsertBefore(add_after_location);
+ } else {
+ add_after->InsertAfter(add_after_location);
+ }
+ }
+
+ HTransitionElementsKind* transition = NULL;
+ if (data->last_in_chain == NULL) {
+ ASSERT(*from != *to);
+ transition = new(graph->zone()) HTransitionElementsKind(
+ data->transition_input, from, to, graph->isolate(), false);
+ if (special_case) {
+ transition->set_special_case();
+ }
+ transition->InsertAfter(add_after);
+
+ // Careful tree surgery
+ for (HUseIterator it(data->transition_input->uses()); !it.Done();
+ it.Advance()) {
+ HValue* use = it.value();
+ if (use != HValue::cast(add_after) &&
+ use != HValue::cast(transition)) {
+ CarefullyReplaceOperand(use, it.index(), transition);
+ }
+ }
+ } else {
+ ASSERT(!from.is_identical_to(to));
+ transition = new(graph->zone()) HTransitionElementsKind(
+ data->last_in_chain, from, to, graph->isolate(), false);
+ if (special_case) {
+ transition->set_special_case();
+ }
+ transition->InsertAfter(data->last_in_chain);
+
+ // Careful tree surgery
+ for (HUseIterator it(data->last_in_chain->uses()); !it.Done();
+ it.Advance()) {
+ HValue* use = it.value();
+ if (use != HValue::cast(transition)) {
+ CarefullyReplaceOperand(use, it.index(), transition);
+ }
+ }
+ }
+
+ data->last_in_chain = transition;
+ data->from_elements.Add(from->elements_kind());
+ }
+
+ private:
+ struct PlacementData: public ZoneObject {
+ explicit PlacementData(HInstruction* transition_input) :
+ transition_input(transition_input),
+ last_in_chain(NULL) {
+ }
+
+ HInstruction* transition_input;
+ ElementsKindSet from_elements;
+ HTransitionElementsKind* last_in_chain;
+ };
+
+ BitVector* visited_set() {
+ return reinterpret_cast<BitVector*>(extra_data_);
+ }
+
+ void set_visited_set(BitVector* visited_set) {
+ ASSERT(extra_data_ == NULL);
+ extra_data_ = visited_set;
+ }
+
+ PlacementData* placement_data() {
+ return reinterpret_cast<PlacementData*>(extra_data_);
+ }
+
+ void set_placement_data(PlacementData* placement_data) {
+ ASSERT(extra_data_ == NULL);
+ extra_data_ = placement_data;
+ }
+
+ void Initialize(HArrayInstruction* instr) {
+ if (!instr->hoistable()) {
+ poisoned_ = true;
+ ASSERT(HasStrongInformation());
+ } else {
+ family_ = instr->map_family(); // may be null
+ if (!family_.is_null()) {
+ // We should have bookmarks to initialize with
+ ASSERT(instr->transitions() > 0);
+ for (int i = 0; i < instr->transitions(); i++) {
+ HTransitionElementsKind* tr = instr->transition(i);
+ from_elements_.Add(tr->original_map()->elements_kind());
+ to_ = tr->transitioned_map();
+ }
+ ASSERT(HasStrongInformation());
+ }
+ }
+ }
+
+ void Initialize(HPhi* phi, int maximum_graph_id, Zone* zone) {
+ set_visited_set(new(zone) BitVector(maximum_graph_id, zone));
+ }
+
+ void CarefullyReplaceOperand(HValue* use, int operand_index,
+ HInstruction* with) {
+ if (!use->block()->IsStartBlock()) {
+ if (!use->IsSimulate() ||
+ use->block()->block_id() > with->block()->block_id()) {
+ use->SetOperandAt(operand_index, with);
+ } else if (HInstruction::cast(use)->IsDefinedAfterInSameBlock(with)) {
+ use->SetOperandAt(operand_index, with);
+ }
+ }
+ }
+
+ Handle<Map> to_;
+ Handle<Map> family_;
+ bool poisoned_;
+ ElementsKindSet from_elements_;
+ void* extra_data_; // used by phi or root nodes differently
+};
+
+
+class HTransitionHoister BASE_EMBEDDED {
+ public:
+ explicit HTransitionHoister(HGraph* graph)
+ : graph_(graph),
+ map_handles_(zone()),
+ table_(HValueMatch, ZoneAllocationPolicy(zone())),
+ lookaside_(HValueMatch, ZoneAllocationPolicy(zone())) {
+ }
+
+ // Returns true if there is useful work to do.
+ // Returns false and finalizes all array instructions in the
+ // graph if not.
+ bool Analyze();
+
+ void DoWork(bool special_case) {
+ ZoneList<WorkItem> worklist_up(10, zone());
+ ZoneList<WorkItem> worklist_down(10, zone());
+
+ PopulateWorklistWithArraySites(&worklist_up);
+ ASSERT(worklist_up.length() > 0);
+
+ // Clear VisitedBy sets in the tables
+ ClearVisitedSets();
+
+ TraverseArraySitesToRoots(&worklist_up, &worklist_down);
+ ASSERT(worklist_up.length() == 0);
+ ASSERT(worklist_down.length() > 0);
+
+ TraverseRootsToArraySites(&worklist_down, special_case);
+ ASSERT(worklist_down.length() == 0);
+ }
+
+ private:
+ struct WorkItem {
+ WorkItem(HValue* from_in, HValue* to_in, HValue* context_in)
+ : from(from_in), to(to_in), context(context_in) {
+ }
+ bool operator==(const WorkItem& pair) const {
+ return pair.from == from && pair.to == to && pair.context == context;
+ }
+ HValue* from;
+ HValue* to;
+ HValue* context;
+ };
+
+ void ClearVisitedSets();
+ void PopulateWorklistWithArraySites(ZoneList<WorkItem>* worklist_up);
+ void TraverseArraySitesToRoots(ZoneList<WorkItem>* worklist_up,
+ ZoneList<WorkItem>* worklist_down);
+ void TraverseRootsToArraySites(ZoneList<WorkItem>* worklist_down, bool special_case);
+ void UpdateMapCheck(HCheckMaps* check_maps,
+ ResolutionTableValue* nearest_value);
+ void PlaceArrayInstructionTransitions(HArrayInstruction* instr, HValue* root, bool special_case);
+ void GatherWeakInformationFromMapCheck(HCheckMaps* check_maps,
+ HValue* from_instr,
+ ResolutionTableValue* from_value);
+
+ typedef TemplateHashMap<HValue, ResolutionTableValue, ZoneAllocationPolicy>
+ ResolutionTable;
+
+ ResolutionTableValue* LookupTableValue(HValue* key, ResolutionTable* table) {
+ ResolutionTable::Iterator i = table->find(key, false,
+ ZoneAllocationPolicy(zone()));
+ if (i != table->end()) {
+ return i->second;
+ }
+ return NULL;
+ }
+
+ ResolutionTableValue* InsertTableValue(HValue* key, ResolutionTable* table) {
+ ResolutionTable::Iterator i = table->find(key, true,
+ ZoneAllocationPolicy(zone()));
+ ASSERT(i != table->end());
+ i->second = new(zone()) ResolutionTableValue(key, graph());
+ return i->second;
+ }
+
+ ResolutionTableValue* LookupSiteTableValue(HValue* key) {
+ return LookupTableValue(key, &table_);
+ }
+
+ ResolutionTableValue* InsertSiteTableValue(HValue* key) {
+ return InsertTableValue(key, &table_);
+ }
+
+ ResolutionTableValue* LookupRootTableValue(HValue* key) {
+ ResolutionTable* correct_table = key->IsLoadKeyed()
+ ? &lookaside_ : &table_;
+ return LookupTableValue(key, correct_table);
+ }
+
+ ResolutionTableValue* InsertRootTableValue(HValue* key) {
+ ResolutionTable* correct_table = key->IsLoadKeyed()
+ ? &lookaside_ : &table_;
+ return InsertTableValue(key, correct_table);
+ }
+
+ static bool HValueMatch(void* key1, void* key2) {
+ return key1 == key2;
+ }
+
+ Zone* zone() const { return graph_->zone(); }
+ Isolate* isolate() const { return graph_->isolate(); }
+ HGraph* graph() const { return graph_; }
+
+ HGraph* graph_;
+ MapHandleDictionary map_handles_;
+
+ ResolutionTable table_;
+ // lookaside contains information for LoadKeyed nodes that act as roots
+ ResolutionTable lookaside_;
+};
+
+
+void HTransitionHoister::ClearVisitedSets() {
+ for (ResolutionTable::Iterator i = table_.begin();
+ i != table_.end();
+ ++i) {
+ HValue* key = i->first;
+ ResolutionTableValue* table_value = i->second;
+ if (key->IsPhi()) {
+ table_value->ClearVisitedBy();
+ }
+ }
+}
+
+
+void HTransitionHoister::PopulateWorklistWithArraySites(
+ ZoneList<WorkItem>* worklist_up) {
+ for (int i = 0; i < graph()->array_instructions()->length(); ++i) {
+ HArrayInstruction* array_instruction = graph()->array_instructions()->at(i);
+ HValue* elements = array_instruction->elements();
+ if (elements->IsLoadElements()) {
+ // Add to our table
+ InsertSiteTableValue(array_instruction);
+
+ // Add to our handle map
+ for (int r = 0; r < array_instruction->transitions(); r++) {
+ HTransitionElementsKind* transition = array_instruction->transition(r);
+ Handle<Map> family = transition->family();
+ map_handles_.InsertIfMissing(transition->original_map(),
+ family);
+ map_handles_.InsertIfMissing(transition->transitioned_map(),
+ family);
+ map_handles_.InsertIfMissing(transition->pessimistic_holey(),
+ family);
+ }
+
+ // Add to our worklist_up. Here I am skipping over elements,
+ HLoadElements* start = HLoadElements::cast(elements);
+ WorkItem item(array_instruction, start->value(), array_instruction);
+ worklist_up->Add(item, zone());
+
+ if (FLAG_trace_transition_placement) {
+ HeapStringAllocator string_allocator;
+ StringStream stream(&string_allocator);
+ array_instruction->PrintElementPlacementTo(&stream);
+ PrintF("%s", *stream.ToCString());
+ }
+ } else {
+ // No need to consider external arrays or for..in statements
+ // Their inputs can be resolved through phis too.
+ ASSERT(elements->IsLoadExternalArrayPointer() ||
+ elements->IsForInCacheArray() ||
+ elements->IsPhi());
+
+ array_instruction->Finalize();
+ }
+ }
+}
+
+
+void HTransitionHoister::TraverseArraySitesToRoots(
+ ZoneList<WorkItem>* worklist_up,
+ ZoneList<WorkItem>* worklist_down) {
+ // Propagate information up the graph.
+ while (!worklist_up->is_empty()) {
+ WorkItem item = worklist_up->Remove(0);
+ ASSERT(LookupSiteTableValue(item.from) != NULL);
+ bool changed = false;
+ bool first_visit_to_root = false;
+
+ ResolutionTableValue* to_value = LookupRootTableValue(item.to);
+ if (to_value == NULL) {
+ to_value = InsertRootTableValue(item.to);
+ changed = true;
+ first_visit_to_root = true;
+ }
+
+ changed |= to_value->Merge(LookupSiteTableValue(item.from), &map_handles_);
+ if (item.to->IsPhi()) {
+ if (changed || !to_value->VisitedBy(item.context)) {
+ to_value->MarkVisitedBy(item.context);
+ HPhi* phi = HPhi::cast(item.to);
+ for (int i = 0; i < phi->OperandCount(); i++) {
+ worklist_up->Add(WorkItem(phi, phi->OperandAt(i), item.context),
+ zone());
+ }
+ }
+ } else {
+ if (first_visit_to_root) {
+ // It's the first time we've seen this root. Go ahead and start adding
+ // these for downward processing.
+ for (HUseIterator it(item.to->uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ if (value->IsPhi() || value->IsCheckMaps()) {
+ worklist_down->Add(WorkItem(item.to, value, item.to), zone());
+ } else if (value->IsLoadElements()) {
+ // Walk through to the StoreKeyed(s)
+ for (HUseIterator it_load(value->uses());
+ !it_load.Done();
+ it_load.Advance()) {
+ if (it_load.value()->IsArrayInstruction()) {
+ worklist_down->Add(WorkItem(item.to, it_load.value(), item.to),
+ zone());
+ }
+ }
+ }
+ }
+ }
+
+ // The context is useful to print here
+ if (FLAG_trace_transition_placement) {
+ HeapStringAllocator string_allocator;
+ StringStream stream(&string_allocator);
+ HArrayInstruction* instr = HArrayInstruction::cast(item.context);
+ stream.Add("ARRAY INSTRUCTION: block%d %d: ",
+ instr->block()->block_id(),
+ instr->id());
+ instr->PrintTo(&stream);
+ stream.Add("\n");
+ stream.Add(" maps to\n");
+ HInstruction* root = HInstruction::cast(item.to);
+ stream.Add(" block%d %d: ", root->block()->block_id(), root->id());
+ root->PrintNameTo(&stream);
+ stream.Add(" ");
+ root->PrintTo(&stream);
+ stream.Add("\n");
+ if (!to_value->to().is_null()) {
+ stream.Add(" To: %s(0x%p)\n",
+ ElementsKindToString(to_value->to()->elements_kind()),
+ *(to_value->to()));
+ } else {
+ stream.Add(" To: n/a\n");
+ }
+
+ PrintF("%s", *stream.ToCString());
+ }
+ }
+ }
+}
+
+
+void HTransitionHoister::TraverseRootsToArraySites(
+ ZoneList<WorkItem>* worklist_down,
+ bool special_case) {
+ // Now propagate information back down to input sites, starting from root
+ // nodes in the table
+ while (!worklist_down->is_empty()) {
+ WorkItem item = worklist_down->Remove(0);
+ bool changed = false;
+
+ ResolutionTableValue* from_value = LookupRootTableValue(item.from);
+ ASSERT(from_value != NULL);
+ ResolutionTableValue* to_value = LookupSiteTableValue(item.to);
+
+ if (to_value == NULL && item.to->IsPhi()) {
+ // We walk through phis on the way down, even if we didn't see
+ // them on the way up. There may be interesting nodes in these
+ // new locations (checkmaps, store/loads).
+ to_value = InsertSiteTableValue(item.to);
+ }
+
+ if (to_value != NULL) {
+ changed = to_value->Merge(from_value, &map_handles_);
+ }
+
+ if (item.to->IsPhi() && (changed || !to_value->VisitedBy(item.context))) {
+ // Walk through phis, marking them as visited.
+ ASSERT(to_value != NULL);
+ to_value->MarkVisitedBy(item.context);
+
+ for (HUseIterator it(item.to->uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ if (value->IsPhi() || value->IsCheckMaps()) {
+ worklist_down->Add(WorkItem(item.to, value, item.context), zone());
+ } else if (value->IsLoadElements()) {
+ // Walk through to the StoreKeyed(s)
+ for (HUseIterator it_load(value->uses());
+ !it_load.Done();
+ it_load.Advance()) {
+ if (it_load.value()->IsArrayInstruction()) {
+ worklist_down->Add(WorkItem(item.to, it_load.value(),
+ item.context), zone());
+ }
+ }
+ }
+ }
+ } else if (item.to->IsArrayInstruction()) {
+ // Place any transition instructions appropriately, moving them from their
+ // locations down at the array instruction site up to the transition input
+ // site.
+ PlaceArrayInstructionTransitions(HArrayInstruction::cast(item.to),
+ item.context,
+ special_case);
+ } else if (item.to->IsCheckMaps()) {
+ // There is no to_value for this case. We just need to try and either
+ // update the checkmaps instruction or glean some "weak" information from
+ // it about monomorphic map checks for our table.
+ ASSERT(to_value == NULL);
+ HCheckMaps* check_maps = HCheckMaps::cast(item.to);
+ if (from_value->HasStrongInformation()) {
+ UpdateMapCheck(check_maps, from_value);
+ } else if (!from_value->HasWeakInformation()) {
+ GatherWeakInformationFromMapCheck(check_maps,
+ item.from,
+ from_value);
+ }
+ }
+ }
+}
+
+
+bool HTransitionHoister::Analyze() {
+ if (graph()->array_instructions() == NULL ||
+ graph()->array_instructions()->length() == 0) {
+ // Nothing useful to do
+ return false;
+ }
+
+ bool transitions_present = false;
+ for (int i = 0; i < graph()->array_instructions()->length(); ++i) {
+ HArrayInstruction* array_instruction = graph()->array_instructions()->at(i);
+ if (array_instruction->transitions() > 0) {
+ transitions_present = true;
+ break;
+ }
+ }
+
+ if (!transitions_present) {
+ for (int i = 0; i < graph()->array_instructions()->length(); ++i) {
+ graph()->array_instructions()->at(i)->Finalize();
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+
+void HTransitionHoister::PlaceArrayInstructionTransitions(
+ HArrayInstruction* instr,
+ HValue* root,
+ bool special_case) {
+ ResolutionTableValue* load_or_store_value =
+ LookupSiteTableValue(instr);
+ ResolutionTableValue* root_value = LookupRootTableValue(root);
+ ASSERT(load_or_store_value != NULL && root_value != NULL);
+
+ // First Finalize the array instruction.
+ if (load_or_store_value->to().is_null()) {
+ instr->Finalize();
+ return;
+ }
+
+ instr->Finalize(load_or_store_value->to()->elements_kind());
+
+ // Are we poisonous? We can't participate in this wonderful scheme...
+ if (load_or_store_value->poisoned()) {
+ // Poison should have flowed up and down the graph from sites to roots
+ ASSERT(root_value->poisoned());
+ return;
+ }
+
+ if (instr->transitions() == 0) {
+ // No work to move
+ return;
+ }
+
+ // Are we ignoring important directives?
+ ASSERT(instr->hoistable());
+
+ // In here we'll
+ // 1) remove any transition statements from the load_or_store site
+ // 2) place correct statements at the root site, avoiding duplicates
+ ASSERT(root_value->to().is_identical_to(load_or_store_value->to()));
+ Handle<Map> handle = root_value->to();
+ for (int i = 0; i < instr->transitions(); i++) {
+ HTransitionElementsKind* transition = instr->transition(i);
+ ASSERT(root_value->from_set().Contains(
+ transition->original_map()->elements_kind()));
+ ASSERT(load_or_store_value->from_set().Contains(
+ transition->original_map()->elements_kind()));
+ root_value->InsertTransitionElementsKind(graph(),
+ root,
+ transition->original_map(),
+ handle,
+ special_case);
+ if (transition->block() != NULL) {
+ // The transition instruction should only be referred to by a map check
+ // instruction.
+ for (HUseIterator it(transition->uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ ASSERT(value->IsCheckMaps());
+ HCheckMaps::cast(value)->RemoveTypeCheck();
+ }
+
+ transition->DeleteAndReplaceWith(NULL);
+ }
+ }
+}
+
+
+void HTransitionHoister::UpdateMapCheck(HCheckMaps* check_maps,
+ ResolutionTableValue* nearest_value) {
+ if (nearest_value->poisoned()) {
+ return;
+ }
+
+ // Only manipulate this mapcheck if at least one of the {from,to} maps from
+ // nearest_value is in the check.
+ Handle<Map> family = nearest_value->family();
+ Handle<Map> to = nearest_value->to();
+ ASSERT(!to.is_null() && !family.is_null());
+ bool contains_to = false;
+ bool contains_from = false;
+ for (int i = 0; i < check_maps->map_set()->length(); i++) {
+ Handle<Map> current = check_maps->map_set()->at(i);
+ if (!current.is_identical_to(to)) {
+ Handle<Map> in_family = map_handles_.Lookup(family,
+ current->elements_kind());
+ if (!in_family.is_null()) {
+ if (current.is_identical_to(in_family)) {
+ contains_from = true;
+ }
+ }
+ } else {
+ contains_to = true;
+ }
+ }
+
+ if (!contains_from && !contains_to) {
+ // This map check deals with a different family. Leave it alone
+ return;
+ }
+
+ if (!contains_to) {
+ // We definitely need the to map in the checkmaps instruction
+ check_maps->map_set()->Add(to, zone());
+ }
+
+ if (contains_from) {
+ // The whole from set should be removed from the map check, since we have
+ // the to map in there.
+ for (int i = check_maps->map_set()->length() - 1; i >= 0; i--) {
+ Handle<Map> current = check_maps->map_set()->at(i);
+ if (!current.is_identical_to(to)) {
+ Handle<Map> in_family = map_handles_.Lookup(family,
+ current->elements_kind());
+ if (!in_family.is_null()) {
+ if (current.is_identical_to(in_family)) {
+ // Delete this map
+ check_maps->map_set()->RemoveAt(i);
+ }
+ }
+ }
+ }
+ }
+
+ if (!contains_to) {
+ // We should sort the map since we added one, removing others
+ check_maps->map_set()->Sort();
+ }
+}
+
+
+void HTransitionHoister::GatherWeakInformationFromMapCheck(
+ HCheckMaps* check_maps,
+ HValue* from_instr,
+ ResolutionTableValue* from_value) {
+ if (check_maps->map_set()->length() == 1) {
+ Handle<Map> map = check_maps->map_set()->at(0);
+ map_handles_.Add(map);
+ from_value->SetWeakInformation(map);
+ ASSERT(from_value->HasWeakInformation());
+ if (FLAG_trace_transition_placement) {
+ HeapStringAllocator string_allocator;
+ StringStream stream(&string_allocator);
+ stream.Add("Weak information added to block%d %d: map %p (%s)\n",
+ from_instr->block()->block_id(),
+ from_instr->id(),
+ static_cast<void*>(*map),
+ ElementsKindToString(map->elements_kind()));
+ PrintF("%s", *stream.ToCString());
+ }
+ }
+}
+
+
+void HGraph::InsertElementsTransitions() {
+ HPhase phase("H_Place elements transitions", this);
+ HTransitionHoister hoister(this);
+ if (hoister.Analyze()) {
+ Handle<String> name = info()->function()->debug_name();
+ const char* n = "lin_solve";
+ Vector<const char> chars(n, strlen(n));
+ bool do_special_case = name->IsAsciiEqualTo(chars);
+ hoister.DoWork(do_special_case);
+ }
+
+#ifdef DEBUG
+ // Verify that we didn't miss initialization of any array instructions.
+ if (array_instructions() != NULL) {
+ for (int i = 0; i < array_instructions()->length(); ++i) {
+ HArrayInstruction* array_instruction = array_instructions()->at(i);
+ ASSERT(array_instruction->Initialized());
+ }
+ }
+#endif // DEBUG
+}
+
+
void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
HValue* current = value;
while (current != NULL) {
@@ -2822,7 +3719,7 @@ void Uint32Analysis::UnmarkUnsafePhis() {
// non-phi uses. Start transitively clearing kUint32 flag
// from phi operands of discovered non-safe phies until
// only safe phies are left.
- while (!worklist.is_empty()) {
+ while (!worklist.is_empty()) {
while (!worklist.is_empty()) {
HPhi* phi = worklist.RemoveLast();
UnmarkPhi(phi, &worklist);
@@ -3274,6 +4171,8 @@ bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
if (FLAG_eliminate_dead_phis) EliminateUnreachablePhis();
CollectPhis();
+ if (FLAG_use_place_elements_transitions) InsertElementsTransitions();
+
if (has_osr_loop_entry()) {
const ZoneList<HPhi*>* phis = osr_loop_entry()->phis();
for (int j = 0; j < phis->length(); j++) {
@@ -3670,7 +4569,7 @@ void HGraph::EliminateRedundantBoundsChecks() {
}
-static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) {
+static void DehoistArrayIndex(HArrayInstruction* array_operation) {
HValue* index = array_operation->GetKey();
if (!index->representation().IsInteger32()) return;
@@ -3726,17 +4625,9 @@ void HGraph::DehoistSimpleArrayIndexComputations() {
for (HInstruction* instr = blocks()->at(i)->first();
instr != NULL;
instr = instr->next()) {
- ArrayInstructionInterface* array_instruction = NULL;
- if (instr->IsLoadKeyed()) {
- HLoadKeyed* op = HLoadKeyed::cast(instr);
- array_instruction = static_cast<ArrayInstructionInterface*>(op);
- } else if (instr->IsStoreKeyed()) {
- HStoreKeyed* op = HStoreKeyed::cast(instr);
- array_instruction = static_cast<ArrayInstructionInterface*>(op);
- } else {
- continue;
+ if (instr->IsArrayInstruction()) {
+ DehoistArrayIndex(HArrayInstruction::cast(instr));
}
- DehoistArrayIndex(array_instruction);
}
}
}
@@ -4303,6 +5194,7 @@ bool HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
non_osr_entry->Goto(loop_predecessor);
set_current_block(osr_entry);
+ osr_entry->set_osr_entry();
BailoutId osr_entry_id = statement->OsrEntryId();
int first_expression_index = environment()->first_expression_index();
int length = environment()->length();
@@ -4572,12 +5464,16 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
set_current_block(loop_body);
- HValue* key = AddInstruction(
- new(zone()) HLoadKeyed(
+ HLoadKeyed* load_keyed = new(zone()) HLoadKeyed(
environment()->ExpressionStackAt(2), // Enum cache.
environment()->ExpressionStackAt(0), // Iteration index.
environment()->ExpressionStackAt(0),
- FAST_ELEMENTS));
+ FAST_ELEMENTS,
+ zone());
+ graph()->RecordArrayInstruction(load_keyed);
+ load_keyed->set_hoistable(false);
+
+ HValue* key = AddInstruction(load_keyed);
// Check if the expected map still matches that of the enumerable.
// If not just deoptimize.
@@ -4935,66 +5831,6 @@ static bool LookupSetter(Handle<Map> map,
}
-// Determines whether the given array or object literal boilerplate satisfies
-// all limits to be considered for fast deep-copying and computes the total
-// size of all objects that are part of the graph.
-static bool IsFastLiteral(Handle<JSObject> boilerplate,
- int max_depth,
- int* max_properties,
- int* total_size) {
- ASSERT(max_depth >= 0 && *max_properties >= 0);
- if (max_depth == 0) return false;
-
- Handle<FixedArrayBase> elements(boilerplate->elements());
- if (elements->length() > 0 &&
- elements->map() != boilerplate->GetHeap()->fixed_cow_array_map()) {
- if (boilerplate->HasFastDoubleElements()) {
- *total_size += FixedDoubleArray::SizeFor(elements->length());
- } else if (boilerplate->HasFastObjectElements()) {
- Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
- int length = elements->length();
- for (int i = 0; i < length; i++) {
- if ((*max_properties)-- == 0) return false;
- Handle<Object> value(fast_elements->get(i));
- if (value->IsJSObject()) {
- Handle<JSObject> value_object = Handle<JSObject>::cast(value);
- if (!IsFastLiteral(value_object,
- max_depth - 1,
- max_properties,
- total_size)) {
- return false;
- }
- }
- }
- *total_size += FixedArray::SizeFor(length);
- } else {
- return false;
- }
- }
-
- Handle<FixedArray> properties(boilerplate->properties());
- if (properties->length() > 0) {
- return false;
- } else {
- int nof = boilerplate->map()->inobject_properties();
- for (int i = 0; i < nof; i++) {
- if ((*max_properties)-- == 0) return false;
- Handle<Object> value(boilerplate->InObjectPropertyAt(i));
- if (value->IsJSObject()) {
- Handle<JSObject> value_object = Handle<JSObject>::cast(value);
- if (!IsFastLiteral(value_object,
- max_depth - 1,
- max_properties,
- total_size)) {
- return false;
- }
- }
- }
- }
-
- *total_size += boilerplate->map()->instance_size();
- return true;
-}
void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
@@ -5010,14 +5846,13 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
int max_properties = HFastLiteral::kMaxLiteralProperties;
Handle<Object> boilerplate(closure->literals()->get(expr->literal_index()));
if (boilerplate->IsJSObject() &&
- IsFastLiteral(Handle<JSObject>::cast(boilerplate),
+ HFastLiteral::IsFastLiteral(Handle<JSObject>::cast(boilerplate),
HFastLiteral::kMaxLiteralDepth,
&max_properties,
&total_size)) {
Handle<JSObject> boilerplate_object = Handle<JSObject>::cast(boilerplate);
literal = new(zone()) HFastLiteral(context,
boilerplate_object,
- total_size,
expr->literal_index(),
expr->depth());
} else {
@@ -5134,13 +5969,12 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
// Check whether to use fast or slow deep-copying for boilerplate.
int total_size = 0;
int max_properties = HFastLiteral::kMaxLiteralProperties;
- if (IsFastLiteral(boilerplate,
+ if (HFastLiteral::IsFastLiteral(boilerplate,
HFastLiteral::kMaxLiteralDepth,
&max_properties,
&total_size)) {
literal = new(zone()) HFastLiteral(context,
boilerplate,
- total_size,
expr->literal_index(),
expr->depth());
} else {
@@ -5186,11 +6020,12 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
case FAST_HOLEY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case FAST_HOLEY_DOUBLE_ELEMENTS:
- AddInstruction(new(zone()) HStoreKeyed(
+ AddInstruction(graph()->RecordArrayInstruction(new(zone()) HStoreKeyed(
elements,
key,
value,
- boilerplate_elements_kind));
+ boilerplate_elements_kind,
+ zone())));
break;
default:
UNREACHABLE();
@@ -6016,7 +6851,7 @@ HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
}
-HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
+HArrayInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
HValue* external_elements,
HValue* checked_key,
HValue* val,
@@ -6052,15 +6887,19 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
UNREACHABLE();
break;
}
- return new(zone()) HStoreKeyed(external_elements,
- checked_key,
- val,
- elements_kind);
+
+ return graph()->RecordArrayInstruction(new(zone())
+ HStoreKeyed(external_elements,
+ checked_key,
+ val,
+ elements_kind,
+ zone()));
} else {
ASSERT(val == NULL);
- HLoadKeyed* load =
- new(zone()) HLoadKeyed(
- external_elements, checked_key, dependency, elements_kind);
+ HLoadKeyed* load = new(zone()) HLoadKeyed(external_elements, checked_key,
+ dependency, elements_kind,
+ zone());
+ graph()->RecordArrayInstruction(load);
if (FLAG_opt_safe_uint32_operations &&
elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) {
graph()->RecordUint32Instruction(load);
@@ -6070,12 +6909,12 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
}
-HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
- HValue* checked_key,
- HValue* val,
- HValue* load_dependency,
- ElementsKind elements_kind,
- bool is_store) {
+HArrayInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
+ HValue* checked_key,
+ HValue* val,
+ HValue* load_dependency,
+ ElementsKind elements_kind,
+ bool is_store) {
if (is_store) {
ASSERT(val != NULL);
switch (elements_kind) {
@@ -6088,39 +6927,44 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
case FAST_HOLEY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case FAST_HOLEY_DOUBLE_ELEMENTS:
- return new(zone()) HStoreKeyed(
- elements, checked_key, val, elements_kind);
+ return graph()->RecordArrayInstruction(new(zone()) HStoreKeyed(
+ elements, checked_key, val, elements_kind, zone()));
default:
UNREACHABLE();
return NULL;
}
}
+
// It's an element load (!is_store).
- return new(zone()) HLoadKeyed(elements,
- checked_key,
- load_dependency,
- elements_kind);
+ return graph()->RecordArrayInstruction(new(zone()) HLoadKeyed(elements,
+ checked_key,
+ load_dependency,
+ elements_kind,
+ zone()));
}
-HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
- HValue* key,
- HValue* val,
- HValue* dependency,
- Handle<Map> map,
- bool is_store) {
+HArrayInstruction* HGraphBuilder::BuildMonomorphicElementAccess(
+ HValue* object,
+ HValue* key,
+ HValue* val,
+ HValue* dependency,
+ Handle<Map> map,
+ bool is_store) {
HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map,
zone(), dependency);
AddInstruction(mapcheck);
if (dependency) {
mapcheck->ClearGVNFlag(kDependsOnElementsKind);
}
- return BuildUncheckedMonomorphicElementAccess(object, key, val,
- mapcheck, map, is_store);
+
+ HArrayInstruction* instr = BuildUncheckedMonomorphicElementAccess(
+ object, key, val, mapcheck, map, is_store);
+ return instr;
}
-HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
+HArrayInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
HValue* object,
HValue* key,
HValue* val,
@@ -6176,7 +7020,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
}
-HInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
+HArrayInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
HValue* object,
HValue* key,
HValue* val,
@@ -6223,9 +7067,8 @@ HInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
HCheckMaps* check_maps = new(zone()) HCheckMaps(object, maps, zone());
AddInstruction(check_maps);
- HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
+ return BuildUncheckedMonomorphicElementAccess(
object, key, val, check_maps, most_general_consolidated_map, false);
- return instr;
}
@@ -6238,7 +7081,8 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
bool is_store,
bool* has_side_effects) {
*has_side_effects = false;
- AddInstruction(new(zone()) HCheckNonSmi(object));
+ HCheckNonSmi* checknonsmi = new(zone()) HCheckNonSmi(object);
+ AddInstruction(checknonsmi);
SmallMapList* maps = prop->GetReceiverTypes();
bool todo_external_array = false;
@@ -6273,6 +7117,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
possible_transitioned_maps.Add(map);
}
}
+
// Get transition target for each map (NULL == no transition).
for (int i = 0; i < maps->length(); ++i) {
Handle<Map> map = maps->at(i);
@@ -6284,6 +7129,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
int num_untransitionable_maps = 0;
Handle<Map> untransitionable_map;
HTransitionElementsKind* transition = NULL;
+ ZoneList<HTransitionElementsKind*> transitions(5, zone());
for (int i = 0; i < maps->length(); ++i) {
Handle<Map> map = maps->at(i);
ASSERT(map->IsMap());
@@ -6292,8 +7138,13 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
map->elements_kind(),
transition_target.at(i)->elements_kind()));
transition = new(zone()) HTransitionElementsKind(
- object, map, transition_target.at(i));
+ object, map, transition_target.at(i), isolate(),
+ FLAG_use_place_elements_transitions);
AddInstruction(transition);
+
+ if (FLAG_use_place_elements_transitions) {
+ transitions.Add(transition, zone());
+ }
} else {
type_todo[map->elements_kind()] = true;
if (IsExternalArrayElementsKind(map->elements_kind())) {
@@ -6312,8 +7163,12 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
: BuildLoadKeyedGeneric(object, key));
} else {
- instr = AddInstruction(BuildMonomorphicElementAccess(
- object, key, val, transition, untransitionable_map, is_store));
+ HArrayInstruction* array_instr = BuildMonomorphicElementAccess(
+ object, key, val, transition, untransitionable_map, is_store);
+ instr = AddInstruction(array_instr);
+ if (FLAG_use_place_elements_transitions && transitions.length() > 0) {
+ array_instr->AddTransitions(transitions);
+ }
}
*has_side_effects |= instr->HasObservableSideEffects();
if (position != RelocInfo::kNoPosition) instr->set_position(position);
@@ -6365,11 +7220,13 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
set_current_block(if_true);
HInstruction* access;
+ HCheckMaps* checkmaps = NULL;
if (IsFastElementsKind(elements_kind)) {
if (is_store && !IsFastDoubleElementsKind(elements_kind)) {
- AddInstruction(new(zone()) HCheckMaps(
+ checkmaps = new(zone()) HCheckMaps(
elements, isolate()->factory()->fixed_array_map(),
- zone(), elements_kind_branch));
+ zone(), elements_kind_branch);
+ AddInstruction(checkmaps);
}
// TODO(jkummerow): The need for these two blocks could be avoided
// in one of two ways:
@@ -6393,9 +7250,11 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
HType::Smi()));
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length,
ALLOW_SMI_KEY));
- access = AddInstruction(BuildFastElementAccess(
+ HArrayInstruction* array_instruction = BuildFastElementAccess(
elements, checked_key, val, elements_kind_branch,
- elements_kind, is_store));
+ elements_kind, is_store);
+ array_instruction->set_hoistable(false);
+ access = AddInstruction(array_instruction);
if (!is_store) {
Push(access);
}
@@ -6410,9 +7269,11 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length,
ALLOW_SMI_KEY));
- access = AddInstruction(BuildFastElementAccess(
+ array_instruction = BuildFastElementAccess(
elements, checked_key, val, elements_kind_branch,
- elements_kind, is_store));
+ elements_kind, is_store);
+ array_instruction->set_hoistable(false);
+ access = AddInstruction(array_instruction);
} else if (elements_kind == DICTIONARY_ELEMENTS) {
if (is_store) {
access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
@@ -6429,6 +7290,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
if (!is_store) {
Push(access);
}
+
current_block()->Goto(join);
set_current_block(if_false);
}
@@ -9766,6 +10628,10 @@ void HEnvironment::PrintToStd() {
}
+
+
+
+
void HTracer::TraceCompilation(FunctionLiteral* function) {
Tag tag(this, "compilation");
Handle<String> name = function->debug_name();
« no previous file with comments | « src/hydrogen.h ('k') | src/hydrogen-instructions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698