| 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();
|
|
|