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

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: Created 8 years, 1 month 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
Index: src/hydrogen.cc
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 51ae7f5a44c29fc4b69f66651b95fcfef7a6f2fa..47222a915844c30a535b3e73ec516bc877e4a50e 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -693,6 +693,7 @@ HGraph::HGraph(CompilationInfo* info)
values_(16, info->zone()),
phi_list_(NULL),
uint32_instructions_(NULL),
+ deferred_transitions_(10, info->zone()),
info_(info),
zone_(info->zone()),
is_recursive_(false),
@@ -2502,6 +2503,456 @@ void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
}
+static bool TerminalMatch(void* key1, void* key2) {
+ return key1 == key2;
+}
+
+typedef ZoneList<DeferredTransitions*> DeferredTransitionsList;
+
+class HighestMapAndFamily {
danno 2012/11/14 15:28:18 When you say "highest", do you mean most general?
mvstanton 2012/11/16 15:15:06 I needed to update many locations to be consistent
+ public:
+ HighestMapAndFamily() : highest_map_(NULL), family_(NULL) { }
+ HighestMapAndFamily(HighestMapAndFamily& h)
+ : highest_map_(h.highest_map()),
+ family_(h.family()) {
+ }
+
+ Map* highest_map() const { return highest_map_; }
+ Map* family() const { return family_; }
+
+ void change(Map* new_highest_map, Map* new_family) {
danno 2012/11/14 15:28:18 Maybe "Update" instead?
mvstanton 2012/11/16 15:15:06 Done.
+ if (is_valid()) {
+ ASSERT(new_family == family_); // family shouldn't be changed
+ highest_map_ = new_highest_map;
+ } else {
+ highest_map_ = new_highest_map;
+ family_ = new_family;
+ }
+ }
+
+ void invalidate() {
+ highest_map_ = NULL;
+ family_ = NULL;
+ }
+
+ bool is_valid() const { return highest_map_ != NULL && family_ != NULL; }
+ bool operator==(const HighestMapAndFamily& h) const {
danno 2012/11/14 15:28:18 We tend to avoid operator== (see http://google-sty
mvstanton 2012/11/16 15:15:06 Done.
+ if (!is_valid() || !h.is_valid()) {
+ return is_valid() == h.is_valid();
+ }
+ return highest_map_ == h.highest_map() && family_ == h.family();
+ }
+
+ bool operator!=(const HighestMapAndFamily& h) const {
danno 2012/11/14 15:28:18 operator overloading
mvstanton 2012/11/16 15:15:06 Done.
+ return !(*this == h);
+ }
+
+ HighestMapAndFamily& operator=(const HighestMapAndFamily& h) {
danno 2012/11/14 15:28:18 operator overloading
mvstanton 2012/11/16 15:15:06 this one I need to keep in order to copy these thi
+ if(this != &h) {
+ highest_map_ = h.highest_map();
+ family_ = h.family();
+ }
+ return *this;
+ }
+
+ private:
+ Map *highest_map_;
danno 2012/11/14 15:28:18 Map* highest_map_
mvstanton 2012/11/16 15:15:06 Done.
+ Map *family_;
+};
+
+typedef EnumSet<ElementsKind> ElementsKindSet;
+
+class TerminalMapValue: public ZoneObject {
danno 2012/11/14 15:28:18 TransitionElementsInputValueData?
mvstanton 2012/11/16 15:15:06 Done.
+ public:
+ TerminalMapValue(HInstruction* terminal_node,Zone* zone) :
+ terminal_node_(terminal_node),
+ transitions_(new (zone) DeferredTransitionsList(10,zone)),
+ zone_(zone),
+ last_in_chain_(NULL) {
+
+ }
+
+ void add_transition(DeferredTransitions* todo) {
danno 2012/11/14 15:28:18 AddTransition
mvstanton 2012/11/16 15:15:06 Done.
+ if (todo->terminal_nodes() == 1) {
+ // put at the front of the list. Processing single element
+ // transitions first makes score calculation easier.
+ transitions_->InsertAt(0, todo, zone_);
+ } else {
+ transitions_->Add(todo, zone_);
+ }
+ }
+
+ ElementsKind highest_elementskind() const {
danno 2012/11/14 15:28:18 GetMostGenerateElementsKind()?
mvstanton 2012/11/16 15:15:06 removed method, no callers
+ ASSERT(highest_.highest_map() != NULL);
+ return highest_.highest_map()->elements_kind();
+ }
+
+ HighestMapAndFamily& highest() { return highest_; }
+ void set_highest(HighestMapAndFamily& h) { highest_ = h; }
+
+ int todo_count() const { return transitions_->length(); }
+ DeferredTransitions* todo(int i) { return (*transitions_)[i]; }
+ Zone* zone() { return zone_; }
+
+ // For instruction emission
+
+ bool RequiresTransitionElementsKind(ElementsKind from) const {
+ return !from_elements_.Contains(from);
+ }
+
+ void InsertTransitionElementsKind(HTransitionElementsKind* new_instr) {
+ ElementsKind from_elements_kind = new_instr->original_map()->elements_kind();
+ ASSERT(RequiresTransitionElementsKind(from_elements_kind));
+ // At the site, we maintain a most recent instruction we added, and
+ // new instructions should appear at the end of the chain.
+ HInstruction* addafter = last_in_chain_;
+ if (addafter == NULL) {
+ // This is the first instruction to add.
+ // We must emit a checknonsmi
+ addafter = new(zone()) HCheckNonSmi(terminal_node_);
+ addafter->InsertAfter(terminal_node_);
+ }
+
+ new_instr->InsertAfter(addafter);
+ last_in_chain_ = new_instr;
+ from_elements_.Add(from_elements_kind);
+ }
+
+ HTransitionElementsKind* last_transitionelementskind() const {
+ // Will be NULL if none inserted yet.
+ return last_in_chain_;
+ }
+
+ private:
+ HInstruction* terminal_node_;
danno 2012/11/14 15:28:18 HValue*
mvstanton 2012/11/16 15:15:06 Done.
+ DeferredTransitionsList* transitions_;
+ Zone* zone_;
+ HighestMapAndFamily highest_;
+ ElementsKindSet from_elements_;
+ HTransitionElementsKind* last_in_chain_;
+};
+
+
+// This method walks through the TODOs that mapped to a particular terminal node
+// value. It returns the "TO" map that these nodes unify too. If they return
+// a NULL handle, then that means that the nodes couldn't be unified, and have
+// already been marked as invalid.
+// If they return a non-null value, then all the TODOs have also been updated to
+// recognize the returned map as their "TO" map as well.
+void ComputeHighest(TerminalMapValue* value, HighestMapAndFamily* highest, DeferredTransitions** cannotTransition) {
+ *highest = value->highest();
+
+ // At the end of this function all todos in the list need to agree on the highest map.
+ // so one time through the loop isn't enough.
+ for (int count=0; count<2; count++) {
+ for (int i=0; i<value->todo_count(); i++) {
+ DeferredTransitions* todo = value->todo(i);
+ Map* todo_highest = todo->highest();
+
+ if (!highest->is_valid()) {
+ // First todo, first time through the loop
+ highest->change(todo_highest, todo->map_family());
+ } else if (highest->highest_map() != todo_highest) {
+
+ // Are they in the same family?
+ if (highest->family() != todo->map_family()) {
+ // Yikes. this won't work.
+ *cannotTransition = todo;
+ highest->invalidate();
+ return;
+ }
+
+ // which one is higher? Figure that out and then the other one needs to
+ // be able to find a path to it.
+ ElementsKind unified_highest_elements_kind = GetUnifiedFastElementsKind(
+ highest->highest_map()->elements_kind(), todo_highest->elements_kind());
+
+ // make sure both maps have a path to success
+ Map* unified_highest_map = highest->highest_map()->LookupElementsTransitionMap(unified_highest_elements_kind);
+ Map* unified_todo_highest_map = todo_highest->LookupElementsTransitionMap(unified_highest_elements_kind);
+ if (unified_highest_map == NULL || unified_todo_highest_map == NULL ||
+ (unified_highest_map != unified_todo_highest_map)) {
+ *cannotTransition = todo;
+ highest->invalidate();
+ return;
+ }
+
+ highest->change(unified_highest_map, todo->map_family());
+ todo->set_highest(highest->highest_map());
+ }
+ }
+ }
+}
+
+
+void ScorchTerminalMap(ZoneAllocationPolicy allocationPolicy, Zone* zone,
+ DeferredTransitions* cannotTransition, ZoneHashMap* terminalMap) {
+ ZoneList<DeferredTransitions*> worklist(10, zone);
+ cannotTransition->set_invalid();
+ worklist.Add(cannotTransition, zone);
+
+ while (!worklist.is_empty()) {
+ DeferredTransitions* todo = worklist.RemoveLast();
+ ASSERT(todo->invalid());
+ for (int t=0; t<todo->terminal_nodes(); t++) {
+ HInstruction* terminal_node = todo->terminal_node(t);
+ ZoneHashMap::Entry* entry = terminalMap->Lookup(terminal_node,
+ terminal_node->id(),
+ false,
+ allocationPolicy);
+ if (entry != NULL) {
+ TerminalMapValue* value = reinterpret_cast<TerminalMapValue*>(entry->value);
+ for (int i=0; i<value->todo_count(); i++) {
+ DeferredTransitions* todo = value->todo(i);
+ if (!todo->invalid()) {
+ todo->set_invalid();
+ worklist.Add(todo, zone);
+ }
+ }
+
+ terminalMap->Remove(terminal_node, terminal_node->id());
+ }
+ }
+ }
+}
+
+
+Handle<Map>& StashedMapHandle(ZoneList<DeferredTransitions>* transitions,
+ Map* map) {
+
+ // The guarantee is that a handle does exist for the given map, since
+ // we saved/created it earlier.
+ // TODO(mvstanton): this could be way more efficient. It is brute force.
+ for (int i=0; i<transitions->length(); i++) {
+ DeferredTransitions& todo = transitions->at(i);
+ for (int r=0; r<todo.transitions(); r++) {
+ TransitionRecord& record = todo.transition_ref(r);
+ if (*(record.map_to().location()) == map) {
+ return record.map_to_ref();
+ } else if(*(record.optimistic_holey().location()) == map) {
+ return record.optimistic_holey_ref();
+ }
+ }
+ }
+
+ // This code is marked as unreachable because at runtime we can't actually create
+ // a handle during compiler optimization.
+ UNREACHABLE();
+ return transitions->at(0).transition_ref(0).map_to_ref();
+}
+
+
+void FinalizeTransitionToDos(ZoneList<DeferredTransitions>& transitions, int maximumGraphID) {
danno 2012/11/14 15:28:18 How about FinalizeDeferredTransitionElements?
mvstanton 2012/11/16 15:15:06 Done.
+ for (int i=0; i<transitions.length(); i++) {
+ DeferredTransitions& todo = transitions[i];
+ todo.ComputeTerminalNodes(maximumGraphID);
+ // Also fill in the current highest, which may later be changed externally.
danno 2012/11/14 15:28:18 most general?
mvstanton 2012/11/16 15:15:06 Done.
+ todo.compute_highest();
+ }
+}
+
+void HGraph::InitializeTerminalMap(ZoneHashMap& terminalMap) {
+ ZoneAllocationPolicy allocator(zone());
+ Counters* counters = isolate()->counters();
+
+ for (int i=0; i<deferred_transitions_.length(); i++) {
+ DeferredTransitions& todo = deferred_transitions_[i];
+ ASSERT(todo.transitions() > 0);
+ int site_nesting_depth = todo.instr()->block()->LoopNestingDepth();
+
+ for (int t=0; t<todo.terminal_nodes(); t++) {
+ HInstruction* terminal_node = todo.terminal_node(t);
+
+ // Score computation
+ int terminal_site_nesting_depth = terminal_node->block()->LoopNestingDepth();
+ int loop_delta = site_nesting_depth - terminal_site_nesting_depth;
+ int proposedAdds = todo.transitions();
+ DeferredTransitions::ScoreType scoreType = todo.scoretype_from_loopdelta(loop_delta);
+ int score = (loop_delta==0 ? 1 : loop_delta)*proposedAdds;
+ if (score < 0) score *= -1;
+ todo.increment_score(scoreType,score);
+
+ ZoneHashMap::Entry* entry = terminalMap.Lookup(terminal_node,
+ terminal_node->id(),
+ true,
+ allocator);
+ TerminalMapValue* value = NULL;
+ if (entry->value == NULL) {
+ // It's new, add a value
+ value = new (zone()) TerminalMapValue(terminal_node,zone());
+ entry->value = reinterpret_cast<void*>(value);
+ } else {
+ value = reinterpret_cast<TerminalMapValue*>(entry->value);
+ }
+
+ value->add_transition(&todo);
+ }
+
+ // Export the site score in aggregate for reporting
+ counters->transition_site_positive_score()->Increment(todo.score(DeferredTransitions::SCORE_POSITIVE));
+ counters->transition_site_negative_score()->Increment(todo.score(DeferredTransitions::SCORE_NEGATIVE));
+ counters->transition_site_neutral_score()->Increment(todo.score(DeferredTransitions::SCORE_NEUTRAL));
+ }
+}
+
+void HGraph::InsertElementsTransitions()
+{
+ ZoneAllocationPolicy allocator(zone());
+ HPhase phase("H_Place elements transitions", this);
+
+ // ===========================================================================
+ // Compute the equivalence classes of the LoadKeyed/StoreKeyed sites that need
+ // transitions.
+ FinalizeTransitionToDos(deferred_transitions_, GetMaximumValueID());
+
+ //
+ // IF in a forest of terminal nodes shared among some load/store keyed
+ // instructions, there are map transitions TO maps that are not in the same
+ // "floor" of the map tree, then all transitions involving those terminal
danno 2012/11/14 15:28:18 to abuse the metaphor even more, maybe "trunk" ins
danno 2012/11/14 15:28:18 pre-transitioned input value?
+ // nodes must be left down at the load/store site, rather than factored up.
+ //
+ ZoneHashMap terminalMap(TerminalMatch,32,allocator);
danno 2012/11/14 15:28:18 spaces after ,s
mvstanton 2012/11/16 15:15:06 Done.
+ InitializeTerminalMap(terminalMap);
+
+ // Now that the map is created, run the unification algorithm
+ bool done = false;
+ while (!done) {
+ bool changed = false;
+ for (ZoneHashMap::Entry* p = terminalMap.Start(); p != NULL;
+ p = terminalMap.Next(p)) {
+ TerminalMapValue* value = reinterpret_cast<TerminalMapValue*>(p->value);
+ DeferredTransitions* cannotTransitionTodo = NULL;
+ HighestMapAndFamily newHighest;
+ ComputeHighest(value, &newHighest, &cannotTransitionTodo);
+ if (!newHighest.is_valid()) {
+ ASSERT(cannotTransitionTodo != NULL);
+ ScorchTerminalMap(allocator, zone(), cannotTransitionTodo, &terminalMap);
+ changed = true;
+ break;
+ }
+
+ if (value->highest() != newHighest) {
+ value->set_highest(newHighest);
+ changed = true;
+ }
+ }
+
+ done = !changed;
+ }
+
+ // Now we have the "to" value for each location, and todos have been divided into
+ // sets, including a set that couldn't be transitioned and is no longer represented
+ // in the terminalMap
+ // Handle each todo again.
+ if (FLAG_trace_transition_placement) {
+ for (int i=0; i<deferred_transitions_.length(); i++) {
+ DeferredTransitions& todo = deferred_transitions_[i];
+ HeapStringAllocator string_allocator;
+ StringStream stream(&string_allocator);
+ todo.PrintTo(&stream);
+ PrintF("%s", *stream.ToCString());
+ }
+ }
+
+ // Do the work
+ for (int i=0; i<deferred_transitions_.length(); i++) {
+ DeferredTransitions& todo = deferred_transitions_[i];
+
+ // All todos need to be finalized, even if we don't take action on them.
+ todo.Finalize();
+
+ if (todo.invalid()) {
+ // We aren't moving this todo, leave it alone and visit the next
+ continue;
+ }
+
+ // 1) Remove the nonsmicheck.
+ todo.checknonsmi_instr()->DeleteAndReplaceWith(NULL);
+
+ // 2) alter the elementkind of the loadkeyed/storekeyed instruction if required.
+ // (note that steps a,b,c won't happen if the transition was marked as invalid because
+ // it operates on a different map family)
+ // 2a) unlink transitions associated with this site from their current location
+ // 2b) Create appropriate transition instructions up at the terminal site.
+ // 2c) Fix the checkmaps instruction if required.
+
+ Handle<Map>& handle = StashedMapHandle(&deferred_transitions_, todo.highest());
+
+ for (int t=0; t<todo.terminal_nodes(); t++) {
+ HInstruction* terminal_node = todo.terminal_node(t);
+ ZoneHashMap::Entry* entry = terminalMap.Lookup(terminal_node,
+ terminal_node->id(),
+ false,
+ allocator);
+ CHECK(entry->value != NULL);
+ TerminalMapValue* value = reinterpret_cast<TerminalMapValue*>(entry->value);
+ for (int r=0; r<todo.transitions(); r++) {
+ TransitionRecord& record = todo.transition_ref(r);
+ if (value->RequiresTransitionElementsKind(record.from())) {
+
+ // We need to emit a transition.
+ ASSERT(terminal_node->IsInstruction());
+ HTransitionElementsKind* transition = new(zone())
+ HTransitionElementsKind(terminal_node, record.map_from(), handle);
+ value->InsertTransitionElementsKind(transition);
+
+ // Remove the transition from it's original location
+ if (record.transition_instruction()->block() != NULL) {
+ if (record.transition_instruction()->HasNoUses()) {
+ record.transition_instruction()->DeleteAndReplaceWith(NULL);
+ } else {
+ record.transition_instruction()->DeleteAndReplaceWith(transition);
+ }
+ }
+ }
+ }
+
+ HCheckMaps* checkmaps = todo.checkmaps_instr();
+ if (checkmaps != NULL) {
+ // The checkmaps depends on the transition instruction we just unlinked.
+ // Conveniently, we can make it depend on the last instruction we just emitted.
+
+ // We may need to update the map
+ // TODO(mvstanton) update the map instruction
+ // todo.mapcheck_instr()->
+ // one case:
+ // HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map,
+ // zone(), dependency);
+ // other case:
+ // emitted_checkmaps = new(zone()) HCheckMaps(
+ // elements, isolate()->factory()->fixed_array_map(),
+ // zone(), elements_kind_branch);
+
+ // I'm not sure what to do right now if this is > 1, so
+ // TODO(mvstanton): this assert is temporary
+ ASSERT(checkmaps->map_set()->length() == 1);
+ // Surgically replace the map in there
+ checkmaps->map_set()->Clear();
+ checkmaps->map_set()->Add(handle, zone());
+
+ // Remove the dependency, only keeping it if our new transition is in the same basic
+ // block as the checkmaps.
+ HTransitionElementsKind* lasttransition = value->last_transitionelementskind();
+ if (checkmaps->has_typecheck()) {
+ if (lasttransition->block() == checkmaps->block()) {
+ checkmaps->SetOperandAt(1, lasttransition);
+ } else {
+ checkmaps->SetOperandAt(1, checkmaps->OperandAt(0));
+ }
+ }
+ }
+ }
+ }
+}
+
+
+template<class T>
+void FinishFastKeyedInitialization(T* instruction, ElementsKind elements_kind) {
+ instruction->PerformDeferredInitialization(elements_kind);
+}
+
+
+
void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
HValue* current = value;
while (current != NULL) {
@@ -3310,6 +3761,8 @@ bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
if (FLAG_eliminate_dead_phis) EliminateUnreachablePhis();
CollectPhis();
+ InsertElementsTransitions();
+
if (has_osr_loop_entry()) {
const ZoneList<HPhi*>* phis = osr_loop_entry()->phis();
for (int j = 0; j < phis->length(); j++) {
@@ -6097,7 +6550,8 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
HValue* val,
HValue* load_dependency,
ElementsKind elements_kind,
- bool is_store) {
+ bool is_store,
+ bool defer_initialization) {
if (is_store) {
ASSERT(val != NULL);
switch (elements_kind) {
@@ -6111,7 +6565,7 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
case FAST_DOUBLE_ELEMENTS:
case FAST_HOLEY_DOUBLE_ELEMENTS:
return new(zone()) HStoreKeyed(
- elements, checked_key, val, elements_kind);
+ elements, checked_key, val, elements_kind, defer_initialization);
default:
UNREACHABLE();
return NULL;
@@ -6121,7 +6575,7 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
return new(zone()) HLoadKeyed(elements,
checked_key,
load_dependency,
- elements_kind);
+ elements_kind, defer_initialization);
}
@@ -6130,15 +6584,21 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
HValue* val,
HValue* dependency,
Handle<Map> map,
- bool is_store) {
+ bool is_store,
+ HCheckMaps** checkmap_instr) {
HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map,
zone(), dependency);
AddInstruction(mapcheck);
if (dependency) {
mapcheck->ClearGVNFlag(kDependsOnElementsKind);
}
+
+ if (checkmap_instr != NULL) {
+ *checkmap_instr = mapcheck;
+ }
+
return BuildUncheckedMonomorphicElementAccess(object, key, val,
- mapcheck, map, is_store);
+ mapcheck, map, is_store, dependency != NULL);
}
@@ -6148,7 +6608,8 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
HValue* val,
HCheckMaps* mapcheck,
Handle<Map> map,
- bool is_store) {
+ bool is_store,
+ bool defer_initialization) {
// No GVNFlag is necessary for ElementsKind if there is an explicit dependency
// on a HElementsTransition instruction. The flag can also be removed if the
// map to check has FAST_HOLEY_ELEMENTS, since there can be no further
@@ -6194,7 +6655,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length,
ALLOW_SMI_KEY));
return BuildFastElementAccess(elements, checked_key, val, mapcheck,
- map->elements_kind(), is_store);
+ map->elements_kind(), is_store, defer_initialization);
}
@@ -6246,7 +6707,7 @@ HInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
HCheckMaps* check_maps = new(zone()) HCheckMaps(object, maps, zone());
AddInstruction(check_maps);
HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
- object, key, val, check_maps, most_general_consolidated_map, false);
+ object, key, val, check_maps, most_general_consolidated_map, false, false);
return instr;
}
@@ -6260,11 +6721,13 @@ 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;
if (!is_store) {
+ // TODO(mvstanton): Should I reference this as a TODO for worst case transition?
HInstruction* consolidated_load =
TryBuildConsolidatedElementLoad(object, key, val, maps);
if (consolidated_load != NULL) {
@@ -6306,6 +6769,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
int num_untransitionable_maps = 0;
Handle<Map> untransitionable_map;
HTransitionElementsKind* transition = NULL;
+ ZoneList<TransitionRecord> records(10, zone());
for (int i = 0; i < maps->length(); ++i) {
Handle<Map> map = maps->at(i);
ASSERT(map->IsMap());
@@ -6316,6 +6780,9 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
transition = new(zone()) HTransitionElementsKind(
object, map, transition_target.at(i));
AddInstruction(transition);
+
+ TransitionRecord tr(transition, map, transition_target.at(i));
+ records.Add(tr,zone());
} else {
type_todo[map->elements_kind()] = true;
if (IsExternalArrayElementsKind(map->elements_kind())) {
@@ -6334,8 +6801,18 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
: BuildLoadKeyedGeneric(object, key));
} else {
+ // TODO(mvstanton): when we remove the transition what do we do about the
+ // parameter in the call to BuildMonomorphicElementAccess?
+ HCheckMaps *checkmaps = NULL;
instr = AddInstruction(BuildMonomorphicElementAccess(
- object, key, val, transition, untransitionable_map, is_store));
+ object, key, val, transition, untransitionable_map, is_store,
+ &checkmaps));
+
+ if (records.length() > 0) {
+ DeferredTransitions todo(instr,checknonsmi,checkmaps,zone());
+ todo.add_transitions(records);
+ graph()->AddDeferredTransitions(todo);
+ }
}
*has_side_effects |= instr->HasObservableSideEffects();
if (position != RelocInfo::kNoPosition) instr->set_position(position);
@@ -6387,11 +6864,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:
@@ -6417,7 +6896,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
ALLOW_SMI_KEY));
access = AddInstruction(BuildFastElementAccess(
elements, checked_key, val, elements_kind_branch,
- elements_kind, is_store));
+ elements_kind, is_store, true));
if (!is_store) {
Push(access);
}
@@ -6434,7 +6913,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
ALLOW_SMI_KEY));
access = AddInstruction(BuildFastElementAccess(
elements, checked_key, val, elements_kind_branch,
- elements_kind, is_store));
+ elements_kind, is_store, true));
} else if (elements_kind == DICTIONARY_ELEMENTS) {
if (is_store) {
access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
@@ -6451,6 +6930,17 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
if (!is_store) {
Push(access);
}
+
+ if (records.length() > 0) {
+ // Only move transitions for fast element types
+ if (IsFastElementsKind(elements_kind)) {
+ ASSERT(access->IsLoadKeyed() || access->IsStoreKeyed());
+ DeferredTransitions todo(access,checknonsmi,checkmaps,zone());
+ todo.add_transitions(records);
+ graph()->AddDeferredTransitions(todo);
+ }
+ }
+
current_block()->Goto(join);
set_current_block(if_false);
}
@@ -9698,6 +10188,167 @@ void HEnvironment::PrintToStd() {
}
+TransitionRecord::TransitionRecord(HTransitionElementsKind* transition_instr,
+ Handle<Map> map_from, Handle<Map> map_to)
+ : transition_instr_(transition_instr),
+ map_from_(map_from),
+ map_to_(map_to),
+ optimistic_holey_() {
+
+ // When transition records are created, we have the chance to create map transitions we
+ // might need later. Transitions are unified during optimization, and we may need
+ // to transition from a packed fastmap to a holey version of same. But we can't create
+ // those transitions during optimization. Do it now, recognizing that when the handle
+ // disappears these maps may be collected if they didn't make it into usage in the
+ // optimized graph.
+
+ // TODO(mvstanton): generalize this service into "MakeWorstCaseMapForElementsKindTransition"
+ // (ie, not "holey")
+ if (IsFastPackedElementsKind(map_to->elements_kind())) {
+ ElementsKind holey_kind = GetHoleyElementsKind(map_to->elements_kind());
+ // The transition might already exist
+ Handle<Map> holey_map_handle(FindClosestElementsTransition(*map_to, holey_kind));
+ ASSERT(!holey_map_handle.is_null());
+ if (holey_map_handle->elements_kind() != holey_kind) {
+ MaybeObject* holey_map = AddMissingElementsTransitions(*map_to, holey_kind);
+ holey_map->ToHandle<Map>(&optimistic_holey_);
+ } else {
+ optimistic_holey_ = holey_map_handle;
+ }
+ } else {
+ optimistic_holey_ = map_to;
+ }
+
+ ASSERT(!optimistic_holey_.is_null());
+
+ // fill in map_family_
+ // Walk up to the base map from the map_to();
+ Handle<Map> end_map(FindClosestElementsTransition(*map_to, TERMINAL_FAST_ELEMENTS_KIND));
+ ASSERT(!end_map.is_null());
+ map_family_ = end_map;
+}
+
+
+void DeferredTransitions::compute_highest() {
+ // Walk the transition records and find the "highest to" value, setting it.
+ Map* newHighest = *(transition(0).map_to().location());
+ set_highest(newHighest);
+}
+
+
+void DeferredTransitions::ComputeTerminalNodes(int maximumGraphValueID) {
+ HValue* elements = NULL;
+
+ if(instr()->IsStoreKeyed()) {
+ elements = HStoreKeyed::cast(instr())->elements();
+ } else {
+ CHECK(instr()->IsLoadKeyed());
+ elements = HLoadKeyed::cast(instr())->elements();
+ }
+
+ ASSERT(elements != NULL);
+ // Now get the item from the elements
+ ASSERT(elements->IsLoadElements());
+ HLoadElements *start = HLoadElements::cast(elements);
+
+ ASSERT(terminal_nodes_->length() == 0);
+ BitVector visited(maximumGraphValueID, zone());
+
+ ZoneList<HValue*> worklist(10, zone());
+ worklist.Add(start->value(), zone());
+
+ while (!worklist.is_empty()) {
+ HValue* value = worklist.RemoveLast();
+ // Have we visited this node?
+ if (!visited.Contains(value->id())) {
+ visited.Add(value->id());
+ if (value->IsPhi()) {
+ HPhi *phi = HPhi::cast(value);
+ for (int i=0; i<phi->OperandCount(); i++) {
+ worklist.Add(phi->OperandAt(i), zone());
+ }
+ } else {
+ // It must be a terminal node.
+ ASSERT(value->IsInstruction());
+ terminal_nodes_->Add(HInstruction::cast(value), zone());
+ }
+ }
+ }
+
+ // We must have found something.
+ ASSERT(terminal_nodes_->length() > 0);
+}
+
+
+void DeferredTransitions::PrintTo(StringStream* stream) const {
+ stream->Add("SITE: block%d %d: ", instr()->block()->block_id(), instr()->id());
+ instr()->PrintTo(stream);
+ stream->Add("\n");
+
+ // Print validness
+ stream->Add(" VALIDITY: %s\n", invalid() ? "invalid" : "valid");
+
+ // Print score
+ stream->Add(" SCORE: (+%d,%d,-%d)\n", score_[0], score_[1], score_[2]);
+
+ // Find the def point for the instruction
+ HValue *elementsptr = NULL;
+ if(instr()->IsStoreKeyed()) {
+ elementsptr = HStoreKeyed::cast(instr())->elements();
+ } else {
+ ASSERT(instr()->IsLoadKeyed());
+ elementsptr = HLoadKeyed::cast(instr())->elements();
+ }
+
+ ASSERT(elementsptr != NULL);
+ // Now get the item from the elements
+ ASSERT(elementsptr->IsLoadElements());
+ HLoadElements *elements = HLoadElements::cast(elementsptr);
+ HValue *elementValue = elements->value();
+ stream->Add(" OBJECT: ");
+ elementValue->PrintNameTo(stream);
+ stream->Add(" ");
+ elementValue->PrintTo(stream);
+ stream->Add(" %s\n",elementValue->IsPhi() ? "PHI" : "");
+ stream->Add(" TRANSITIONS:\n");
+ ElementsKind transitionElementsKind = FAST_SMI_ELEMENTS;
+ for (int i = 0; i < transitions(); i++) {
+ stream->Add(" %s", ElementsKindToString(transition(i).from()));
+ stream->Add("(0x%p)-> ", *(transition(i).map_from()));
+ stream->Add("%s", ElementsKindToString(transition(i).to()));
+ stream->Add("(0x%p)\n", *(transition(i).map_to()));
+ transitionElementsKind = transition(i).to();
+ }
+
+ // Print possibly externally computed map
+ const char *signifier = (transitionElementsKind != highest()->elements_kind())
+ ? "*" : "";
+ stream->Add(" COMPUTED HIGHEST: %s%s(0x%p)\n", signifier,
+ ElementsKindToString(highest()->elements_kind()),
+ highest());
+
+ // Print terminal nodes if available
+ stream->Add(" TERMINAL NODES:\n");
+ for (int j=0; j < terminal_nodes(); j++) {
+ HValue* node = terminal_node(j);
+ stream->Add(" block%d %d: ", node->block()->block_id(), node->id());
+ node->PrintNameTo(stream);
+ stream->Add(" ");
+ node->PrintTo(stream);
+ stream->Add("\n");
+ }
+
+}
+
+
+void DeferredTransitions::PrintToStd() const {
+ HeapStringAllocator string_allocator;
+ StringStream trace(&string_allocator);
+ PrintTo(&trace);
+ PrintF("%s", *trace.ToCString());
+}
+
+
void HTracer::TraceCompilation(FunctionLiteral* function) {
Tag tag(this, "compilation");
Handle<String> name = function->debug_name();

Powered by Google App Engine
This is Rietveld 408576698