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

Unified Diff: src/hydrogen.cc

Issue 15533004: Liveness analysis for environment slots in Hydrogen (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: fixed bug: markers were deleted too early Created 7 years, 7 months 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 b53e9b514e78ab19fe140422a23c1fb303809fa1..96f610faf5929665cf86a79dda76f6efda9d53db 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -71,6 +71,7 @@ HBasicBlock::HBasicBlock(HGraph* graph)
last_instruction_index_(-1),
deleted_phis_(4, graph->zone()),
parent_loop_header_(NULL),
+ inlined_entry_block_(NULL),
is_inline_return_target_(false),
is_deoptimizing_(false),
dominates_loop_successors_(false),
@@ -130,10 +131,13 @@ HDeoptimize* HBasicBlock::CreateDeoptimize(
HDeoptimize::UseEnvironment has_uses) {
ASSERT(HasEnvironment());
if (has_uses == HDeoptimize::kNoUses)
- return new(zone()) HDeoptimize(0, zone());
+ return new(zone()) HDeoptimize(0, 0, 0, zone());
HEnvironment* environment = last_environment();
- HDeoptimize* instr = new(zone()) HDeoptimize(environment->length(), zone());
+ int first_local_index = environment->first_local_index();
+ int first_expression_index = environment->first_expression_index();
+ HDeoptimize* instr = new(zone()) HDeoptimize(
+ environment->length(), first_local_index, first_expression_index, zone());
for (int i = 0; i < environment->length(); i++) {
HValue* val = environment->values()->at(i);
instr->AddEnvironmentValue(val, zone());
@@ -156,6 +160,9 @@ HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id,
HSimulate* instr =
new(zone()) HSimulate(ast_id, pop_count, zone(), removable);
+#ifdef DEBUG
+ instr->set_closure(environment->closure());
+#endif
// Order of pushed values: newest (top of stack) first. This allows
// HSimulate::MergeInto() to easily append additional pushed values
// that are older (from further down the stack).
@@ -192,7 +199,7 @@ void HBasicBlock::Goto(HBasicBlock* block,
if (block->IsInlineReturnTarget()) {
AddInstruction(new(zone()) HLeaveInlined());
- last_environment_ = last_environment()->DiscardInlined(drop_extra);
+ UpdateEnvironment(last_environment()->DiscardInlined(drop_extra));
}
if (add_simulate) AddSimulate(BailoutId::None());
@@ -209,7 +216,7 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value,
ASSERT(target->IsInlineReturnTarget());
ASSERT(return_value != NULL);
AddInstruction(new(zone()) HLeaveInlined());
- last_environment_ = last_environment()->DiscardInlined(drop_extra);
+ UpdateEnvironment(last_environment()->DiscardInlined(drop_extra));
last_environment()->Push(return_value);
AddSimulate(BailoutId::None());
HGoto* instr = new(zone()) HGoto(target);
@@ -224,6 +231,12 @@ void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
}
+void HBasicBlock::UpdateEnvironment(HEnvironment* env) {
+ last_environment_ = env;
+ graph()->update_maximum_environment_size(env->first_expression_index());
+}
+
+
void HBasicBlock::SetJoinId(BailoutId ast_id) {
int length = predecessors_.length();
ASSERT(length > 0);
@@ -731,8 +744,7 @@ HInstruction* HGraphBuilder::IfBuilder::IfCompare(
HInstruction* HGraphBuilder::IfBuilder::IfCompareMap(HValue* left,
Handle<Map> map) {
HCompareMap* compare =
- new(zone()) HCompareMap(left, map,
- first_true_block_, first_false_block_);
+ new(zone()) HCompareMap(left, map, first_true_block_, first_false_block_);
AddCompare(compare);
return compare;
}
@@ -811,9 +823,16 @@ void HGraphBuilder::IfBuilder::Then() {
did_then_ = true;
if (needs_compare_) {
// Handle if's without any expressions, they jump directly to the "else"
- // branch.
- builder_->current_block()->GotoNoSimulate(first_false_block_);
- first_true_block_ = NULL;
+ // branch. However, we must pretend that the "then" branch is reachable,
+ // so that the graph builder visits it and sees any live range extending
+ // constructs within it.
+ HConstant* constant_false = builder_->graph()->GetConstantFalse();
+ ToBooleanStub::Types boolean_type = ToBooleanStub::no_types();
+ boolean_type.Add(ToBooleanStub::BOOLEAN);
+ HBranch* branch =
+ new(zone()) HBranch(constant_false, first_true_block_,
+ first_false_block_, boolean_type);
+ builder_->current_block()->Finish(branch);
}
builder_->set_current_block(first_true_block_);
}
@@ -2103,7 +2122,8 @@ HGraph::HGraph(CompilationInfo* info)
use_optimistic_licm_(false),
has_soft_deoptimize_(false),
depends_on_empty_array_proto_elements_(false),
- type_change_checksum_(0) {
+ type_change_checksum_(0),
+ maximum_environment_size_(0) {
if (info->IsStub()) {
HydrogenCodeStub* stub = info->code_stub();
CodeStubInterfaceDescriptor* descriptor =
@@ -2486,6 +2506,235 @@ void HGraph::AssignDominators() {
}
+HSimulate* HGraph::ZapEnvironmentSlotInNextSimulate(int index,
titzer 2013/05/27 11:35:28 In this function, we have to walk forward in the g
+ HInstruction* from_here) {
+ HInstruction* current = from_here;
+ for (; current != NULL && !current->IsSimulate(); current = current->next()) {
+ if (current->IsLeaveInlined()) return NULL;
+ // There's always a simulate before an EnterInlined.
+ ASSERT(!current->IsEnterInlined());
+ // Don't zap if a new live range starts before the next simulate.
+ if (current->IsEnvironmentBind() &&
+ HEnvironmentBind::cast(current)->index() == index) {
+ return NULL;
+ }
+ }
+ if (current == NULL) return NULL;
+ HSimulate* simulate = HSimulate::cast(current);
+ int operand_index = simulate->ToOperandIndex(index);
+ if (operand_index == -1) {
+ simulate->AddAssignedValue(index, GetConstantUndefined());
+ } else {
+ simulate->SetOperandAt(operand_index, GetConstantUndefined());
+ }
+ return simulate;
+}
+
+
+void HGraph::ZapEnvironmentSlotsInSuccessors(
+ BitVector* live,
+ HBasicBlock* block,
+ ZoneList<BitVector*>* live_at_block_start) {
+ // When a value is live in successor A but dead in B, we must
+ // explicitly zap it in B.
+ for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
+ HBasicBlock* successor = it.Current();
+ int successor_id = successor->block_id();
+ BitVector* live_in_successor = live_at_block_start->at(successor_id);
+ if (live_in_successor->Equals(*live)) continue;
+ for (int i = 0; i < live->length(); ++i) {
+ if (!live->Contains(i)) continue;
+ if (live_in_successor->Contains(i)) continue;
+ HSimulate* sim = ZapEnvironmentSlotInNextSimulate(i, successor->first());
+ ASSERT(sim == NULL ||
+ sim->closure().is_identical_to(
+ block->last_environment()->closure()));
+ USE(sim);
+ }
+ }
+}
+
+
+void HGraph::ZapEnvironmentSlotsForInstruction(BitVector* live,
+ HInstruction* instr) {
+ switch (instr->opcode()) {
+ case HValue::kEnvironmentLookup: {
+ int index = HEnvironmentLookup::cast(instr)->index();
+ if (!live->Contains(index)) {
+ // The Simulate following the point where the environment value
+ // dies is extended or modified to zap that value's slot.
+ HSimulate* sim = ZapEnvironmentSlotInNextSimulate(index, instr->next());
+ ASSERT(sim == NULL ||
+ sim->closure().is_identical_to(
+ HEnvironmentLookup::cast(instr)->closure()));
+ USE(sim);
+ }
+ break;
+ }
+ case HValue::kEnvironmentBind: {
+ int index = HEnvironmentBind::cast(instr)->index();
+ if (!live->Contains(index)) {
+ // Don't bother binding this value if it's never looked up.
+ HSimulate* sim = ZapEnvironmentSlotInNextSimulate(index, instr->next());
+ ASSERT(sim == NULL ||
+ sim->closure().is_identical_to(
+ HEnvironmentBind::cast(instr)->closure()));
+ USE(sim);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
+void HGraph::UpdateLivenessAtBlockEnd(
+ BitVector* live,
+ HBasicBlock* block,
+ ZoneList<BitVector*>* live_at_block_start) {
+ // Liveness at the end of each block: union of liveness in successors.
+ live->Clear();
+ for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
+ live->Union(*live_at_block_start->at(it.Current()->block_id()));
+ }
+}
+
+
+void HGraph::UpdateLivenessAtInstruction(
+ BitVector* live,
+ HInstruction* instr,
+ ZoneList<BitVector*>* live_at_block_start) {
+ switch (instr->opcode()) {
+ case HValue::kEnvironmentLookup:
+ live->Add(HEnvironmentLookup::cast(instr)->index());
+ break;
+ case HValue::kEnvironmentBind:
+ live->Remove(HEnvironmentBind::cast(instr)->index());
+ break;
+ case HValue::kLeaveInlined:
+ // No environment values are live at the end of an inlined section.
+ live->Clear();
+
+ // The following ASSERTs guard the assumption used in case
+ // kEnterInlined below:
+ ASSERT(instr->next()->IsSimulate());
+ ASSERT(instr->next()->next()->IsGoto());
+
+ break;
+ case HValue::kEnterInlined: {
+ // Those environment values are live that are live at any return
+ // target block. Here we make use of the fact that the end of an
+ // inline sequence always looks like this: HLeaveInlined, HSimulate,
+ // HGoto (to return_target block), with no environment lookups in
+ // between (see ASSERTs above).
+ HEnterInlined* enter = HEnterInlined::cast(instr);
+ live->Clear();
+ for (int i = 0; i < enter->return_targets()->length(); ++i) {
+ int return_id = enter->return_targets()->at(i)->block_id();
+ // When an AbnormalExit is involved, it can happen that the return
+ // target block doesn't actually exist.
+ if (return_id < live_at_block_start->length()) {
+ live->Union(*live_at_block_start->at(return_id));
+ }
+ }
+ break;
+ }
+ case HValue::kDeoptimize: {
+ // Keep all environment slots alive.
+ HDeoptimize* deopt = HDeoptimize::cast(instr);
+ for (int i = deopt->first_local_index();
+ i < deopt->first_expression_index(); ++i) {
+ live->Add(i);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
+// Values in the environment are kept alive by every subsequent LInstruction
+// that is assigned an LEnvironment. This creates register pressure and
+// unnecessary spill slot moves. Therefore it is beneficial to trim the
+// live ranges of environment slots by zapping them with a constant after
+// the last lookup that refers to them. This is similar but not identical
+// to live range analysis for HValues, so we perform an explicit liveness
+// analysis on environment slots (identified by their index).
+// This only affects slots whitelisted by
+// HOptimizedGraphBuilder::IsEligibleForEnvironmentLivenessAnalysis().
+void HGraph::AnalyzeAndPruneEnvironmentLiveness() {
+ HPhase phase("H_EnvironmentLivenessAnalysis", this);
+ if (maximum_environment_size_ == 0) return;
+ int block_count = blocks()->length();
+ ZoneList<BitVector*> live_at_block_start(block_count, zone());
+ BitVector* worklist = new(zone()) BitVector(block_count, zone());
+ for (int i = 0; i < block_count; ++i) {
+ live_at_block_start.Add(
+ new(zone()) BitVector(maximum_environment_size_, zone()),
+ zone());
+ worklist->Add(i);
+ }
+ BitVector live(maximum_environment_size_, zone());
+
+ // Main iteration. Compute liveness of environment slots, and store it
+ // for each block until it doesn't change any more. For efficiency, visit
+ // blocks in reverse order and walk backwards through each block. We
+ // need several iterations to propagate liveness through nested loops.
+ while (!worklist->IsEmpty()) {
+ for (int block_id = block_count - 1; block_id >= 0; --block_id) {
+ if (!worklist->Contains(block_id)) {
+ continue;
+ }
+ worklist->Remove(block_id);
+
+ HBasicBlock* block = blocks()->at(block_id);
+ UpdateLivenessAtBlockEnd(&live, block, &live_at_block_start);
+
+ for (HInstruction* instr = block->last(); instr != NULL;
+ instr = instr->previous()) {
+ UpdateLivenessAtInstruction(&live, instr, &live_at_block_start);
+ }
+
+ // Reached the start of the block, do necessary bookkeeping.
+ if (live_at_block_start[block_id]->UnionIsChanged(live)) {
+ for (int i = 0; i < block->predecessors()->length(); ++i) {
+ worklist->Add(block->predecessors()->at(i)->block_id());
+ }
+ if (block->IsInlineReturnTarget()) {
+ worklist->Add(block->inlined_entry_block()->block_id());
+ }
+ }
+ }
+ }
+
+ // Analysis finished. Zap dead environment slots.
titzer 2013/05/27 11:35:28 I don't think we need the last pass over the block
+ for (int block_id = block_count - 1; block_id >= 0; --block_id) {
+ HBasicBlock* block = blocks()->at(block_id);
+ UpdateLivenessAtBlockEnd(&live, block, &live_at_block_start);
+ ZapEnvironmentSlotsInSuccessors(&live, block, &live_at_block_start);
+
+ for (HInstruction* instr = block->last(); instr != NULL;
+ instr = instr->previous()) {
+ ZapEnvironmentSlotsForInstruction(&live, instr);
+ UpdateLivenessAtInstruction(&live, instr, &live_at_block_start);
+ }
+ }
+
+ // Finally, remove the HEnvironment{Bind,Lookup} markers.
+ for (int block_id = block_count - 1; block_id >= 0; --block_id) {
+ HBasicBlock* block = blocks()->at(block_id);
+ for (HInstruction* instr = block->last(); instr != NULL;
+ instr = instr->previous()) {
+ if (instr->IsEnvironmentBind() || instr->IsEnvironmentLookup()) {
+ instr->DeleteAndReplaceWith(NULL);
+ }
+ }
+ }
+}
+
+
// Mark all blocks that are dominated by an unconditional soft deoptimize to
// prevent code motion across those blocks.
void HGraph::PropagateDeoptimizingMark() {
@@ -4431,8 +4680,8 @@ FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
if (owner->ast_context()->IsTest()) {
HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
- if_true->MarkAsInlineReturnTarget();
- if_false->MarkAsInlineReturnTarget();
+ if_true->MarkAsInlineReturnTarget(owner->current_block());
+ if_false->MarkAsInlineReturnTarget(owner->current_block());
TestContext* outer_test_context = TestContext::cast(owner->ast_context());
Expression* cond = outer_test_context->condition();
TypeFeedbackOracle* outer_oracle = outer_test_context->oracle();
@@ -4442,7 +4691,7 @@ FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
new TestContext(owner, cond, outer_oracle, if_true, if_false);
} else {
function_return_ = owner->graph()->CreateBasicBlock();
- function_return()->MarkAsInlineReturnTarget();
+ function_return()->MarkAsInlineReturnTarget(owner->current_block());
}
// Set this after possibly allocating a new TestContext above.
call_context_ = owner->ast_context();
@@ -4862,6 +5111,10 @@ bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
Verify(true);
#endif
+ if (FLAG_analyze_environment_liveness) {
+ AnalyzeAndPruneEnvironmentLiveness();
+ }
+
PropagateDeoptimizingMark();
if (!CheckConstPhiUses()) {
*bailout_reason = SmartArrayPointer<char>(StrDup(
@@ -6553,7 +6806,7 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
case Variable::PARAMETER:
case Variable::LOCAL: {
- HValue* value = environment()->Lookup(variable);
+ HValue* value = LookupAndMakeLive(variable);
if (value == graph()->GetConstantHole()) {
ASSERT(IsDeclaredVariableMode(variable->mode()) &&
variable->mode() != VAR);
@@ -7553,7 +7806,7 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
if (var->mode() == CONST) {
return Bailout("unsupported const compound assignment");
}
- Bind(var, Top());
+ BindIfLive(var, Top());
break;
case Variable::CONTEXT: {
@@ -7782,7 +8035,7 @@ void HOptimizedGraphBuilder::VisitAssignment(Assignment* expr) {
// permitted.
CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
HValue* value = Pop();
- Bind(var, value);
+ BindIfLive(var, value);
return ast_context()->ReturnValue(value);
}
@@ -9002,7 +9255,8 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
function_state()->inlining_kind(),
function->scope()->arguments(),
arguments_values,
- undefined_receiver);
+ undefined_receiver,
+ zone());
function_state()->set_entry(enter_inlined);
AddInstruction(enter_inlined);
@@ -9081,6 +9335,8 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
HBasicBlock* if_true = inlined_test_context()->if_true();
HBasicBlock* if_false = inlined_test_context()->if_false();
+ HEnterInlined* entry = function_state()->entry();
+
// Pop the return test context from the expression context stack.
ASSERT(ast_context() == inlined_test_context());
ClearInlinedTestContext();
@@ -9088,11 +9344,13 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
// Forward to the real test context.
if (if_true->HasPredecessor()) {
+ entry->RegisterReturnTarget(if_true, zone());
if_true->SetJoinId(ast_id);
HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
if_true->Goto(true_target, function_state());
}
if (if_false->HasPredecessor()) {
+ entry->RegisterReturnTarget(if_false, zone());
if_false->SetJoinId(ast_id);
HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
if_false->Goto(false_target, function_state());
@@ -9101,6 +9359,7 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
return true;
} else if (function_return()->HasPredecessor()) {
+ function_state()->entry()->RegisterReturnTarget(function_return(), zone());
function_return()->SetJoinId(ast_id);
set_current_block(function_return());
} else {
@@ -9407,7 +9666,7 @@ bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
VariableProxy* arg_two = args->at(1)->AsVariableProxy();
if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
- HValue* arg_two_value = environment()->Lookup(arg_two->var());
+ HValue* arg_two_value = LookupAndMakeLive(arg_two->var());
if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
// Found pattern f.apply(receiver, arguments).
@@ -10118,7 +10377,7 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
case Variable::PARAMETER:
case Variable::LOCAL:
- Bind(var, after);
+ BindIfLive(var, after);
break;
case Variable::CONTEXT: {
@@ -11201,7 +11460,7 @@ void HOptimizedGraphBuilder::VisitFunctionDeclaration(
case Variable::LOCAL: {
CHECK_ALIVE(VisitForValue(declaration->fun()));
HValue* value = Pop();
- environment()->Bind(variable, value);
+ BindIfLive(variable, value);
break;
}
case Variable::CONTEXT: {
« 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