}
+LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
"perform array bounds checks elimination")
DEFINE_bool(array_index_dehoisting, true,
"perform array index dehoisting")
+DEFINE_bool(analyze_environment_liveness, true,
+ "analyze liveness of environment slots and zap dead values")
DEFINE_bool(dead_code_elimination, true, "use dead code elimination")
DEFINE_bool(fold_constants, true, "use constant folding")
DEFINE_bool(trace_dead_code_elimination, false, "trace dead code elimination")
--- /dev/null
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#include "hydrogen-environment-liveness.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+EnvironmentSlotLivenessAnalyzer::EnvironmentSlotLivenessAnalyzer(
+ HGraph* graph)
+ : graph_(graph),
+ zone_(graph->isolate()),
+ zone_scope_(&zone_, DELETE_ON_EXIT),
+ block_count_(graph->blocks()->length()),
+ maximum_environment_size_(graph->maximum_environment_size()),
+ collect_markers_(true),
+ last_simulate_(NULL) {
+ if (maximum_environment_size_ == 0) return;
+
+ live_at_block_start_ =
+ new(zone()) ZoneList<BitVector*>(block_count_, zone());
+ first_simulate_ = new(zone()) ZoneList<HSimulate*>(block_count_, zone());
+ first_simulate_invalid_for_index_ =
+ new(zone()) ZoneList<BitVector*>(block_count_, zone());
+ markers_ = new(zone())
+ ZoneList<HEnvironmentMarker*>(maximum_environment_size_, zone());
+ went_live_since_last_simulate_ =
+ new(zone()) BitVector(maximum_environment_size_, zone());
+
+ for (int i = 0; i < block_count_; ++i) {
+ live_at_block_start_->Add(
+ new(zone()) BitVector(maximum_environment_size_, zone()), zone());
+ first_simulate_->Add(NULL, zone());
+ first_simulate_invalid_for_index_->Add(
+ new(zone()) BitVector(maximum_environment_size_, zone()), zone());
+ }
+}
+
+
+void EnvironmentSlotLivenessAnalyzer::ZapEnvironmentSlot(int index,
+ HSimulate* simulate) {
+ int operand_index = simulate->ToOperandIndex(index);
+ if (operand_index == -1) {
+ simulate->AddAssignedValue(index, graph_->GetConstantUndefined());
+ } else {
+ simulate->SetOperandAt(operand_index, graph_->GetConstantUndefined());
+ }
+}
+
+
+void EnvironmentSlotLivenessAnalyzer::ZapEnvironmentSlotsInSuccessors(
+ HBasicBlock* block,
+ BitVector* live) {
+ // 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;
+ if (first_simulate_invalid_for_index_->at(successor_id)->Contains(i)) {
+ continue;
+ }
+ HSimulate* simulate = first_simulate_->at(successor_id);
+ if (simulate == NULL) continue;
+ ASSERT(simulate->closure().is_identical_to(
+ block->last_environment()->closure()));
+ ZapEnvironmentSlot(i, simulate);
+ }
+ }
+}
+
+
+void EnvironmentSlotLivenessAnalyzer::ZapEnvironmentSlotsForInstruction(
+ HEnvironmentMarker* marker) {
+ if (!marker->CheckFlag(HValue::kEndsLiveRange)) return;
+ HSimulate* simulate = marker->next_simulate();
+ if (simulate != NULL) {
+ ASSERT(simulate->closure().is_identical_to(marker->closure()));
+ ZapEnvironmentSlot(marker->index(), simulate);
+ }
+}
+
+
+void EnvironmentSlotLivenessAnalyzer::UpdateLivenessAtBlockEnd(
+ HBasicBlock* block,
+ BitVector* live) {
+ // 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 EnvironmentSlotLivenessAnalyzer::UpdateLivenessAtInstruction(
+ HInstruction* instr,
+ BitVector* live) {
+ switch (instr->opcode()) {
+ case HValue::kEnvironmentMarker: {
+ HEnvironmentMarker* marker = HEnvironmentMarker::cast(instr);
+ int index = marker->index();
+ if (!live->Contains(index)) {
+ marker->SetFlag(HValue::kEndsLiveRange);
+ } else {
+ marker->ClearFlag(HValue::kEndsLiveRange);
+ }
+ if (!went_live_since_last_simulate_->Contains(index)) {
+ marker->set_next_simulate(last_simulate_);
+ }
+ if (marker->kind() == HEnvironmentMarker::LOOKUP) {
+ live->Add(index);
+ } else {
+ ASSERT(marker->kind() == HEnvironmentMarker::BIND);
+ live->Remove(index);
+ went_live_since_last_simulate_->Add(index);
+ }
+ if (collect_markers_) {
+ // Populate |markers_| list during the first pass.
+ markers_->Add(marker, &zone_);
+ }
+ break;
+ }
+ case HValue::kLeaveInlined:
+ // No environment values are live at the end of an inlined section.
+ live->Clear();
+ last_simulate_ = NULL;
+
+ // 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));
+ }
+ }
+ last_simulate_ = NULL;
+ 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;
+ }
+ case HValue::kSimulate:
+ last_simulate_ = HSimulate::cast(instr);
+ went_live_since_last_simulate_->Clear();
+ break;
+ default:
+ break;
+ }
+}
+
+
+void EnvironmentSlotLivenessAnalyzer::AnalyzeAndTrim() {
+ HPhase phase("H_EnvironmentLivenessAnalysis", graph_);
+ if (maximum_environment_size_ == 0) return;
+
+ // 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.
+ BitVector* live = new(zone()) BitVector(maximum_environment_size_, zone());
+ BitVector* worklist = new(zone()) BitVector(block_count_, zone());
+ for (int i = 0; i < block_count_; ++i) {
+ worklist->Add(i);
+ }
+ 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);
+ last_simulate_ = NULL;
+
+ HBasicBlock* block = graph_->blocks()->at(block_id);
+ UpdateLivenessAtBlockEnd(block, live);
+
+ for (HInstruction* instr = block->last(); instr != NULL;
+ instr = instr->previous()) {
+ UpdateLivenessAtInstruction(instr, live);
+ }
+
+ // Reached the start of the block, do necessary bookkeeping:
+ // store computed information for this block and add predecessors
+ // to the work list as necessary.
+ first_simulate_->Set(block_id, last_simulate_);
+ first_simulate_invalid_for_index_->at(block_id)->CopyFrom(
+ *went_live_since_last_simulate_);
+ if (live_at_block_start_->at(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());
+ }
+ }
+ }
+ // Only collect bind/lookup instructions during the first pass.
+ collect_markers_ = false;
+ }
+
+ // Analysis finished. Zap dead environment slots.
+ for (int i = 0; i < markers_->length(); ++i) {
+ ZapEnvironmentSlotsForInstruction(markers_->at(i));
+ }
+ for (int block_id = block_count_ - 1; block_id >= 0; --block_id) {
+ HBasicBlock* block = graph_->blocks()->at(block_id);
+ UpdateLivenessAtBlockEnd(block, live);
+ ZapEnvironmentSlotsInSuccessors(block, live);
+ }
+
+ // Finally, remove the HEnvironment{Bind,Lookup} markers.
+ for (int i = 0; i < markers_->length(); ++i) {
+ markers_->at(i)->DeleteAndReplaceWith(NULL);
+ }
+}
+
+} } // namespace v8::internal
--- /dev/null
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_HYDROGEN_ENVIRONMENT_LIVENESS_H_
+#define V8_HYDROGEN_ENVIRONMENT_LIVENESS_H_
+
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Trims live ranges of environment slots by doing explicit liveness analysis.
+// Values in the environment are kept alive by every subsequent LInstruction
+// that is assigned an LEnvironment, which 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.
+// Slots are identified by their index and only affected if whitelisted in
+// HOptimizedGraphBuilder::IsEligibleForEnvironmentLivenessAnalysis().
+class EnvironmentSlotLivenessAnalyzer {
+ public:
+ explicit EnvironmentSlotLivenessAnalyzer(HGraph* graph);
+
+ void AnalyzeAndTrim();
+
+ private:
+ void ZapEnvironmentSlot(int index, HSimulate* simulate);
+ void ZapEnvironmentSlotsInSuccessors(HBasicBlock* block, BitVector* live);
+ void ZapEnvironmentSlotsForInstruction(HEnvironmentMarker* marker);
+ void UpdateLivenessAtBlockEnd(HBasicBlock* block, BitVector* live);
+ void UpdateLivenessAtInstruction(HInstruction* instr, BitVector* live);
+
+ Zone* zone() { return &zone_; }
+
+ HGraph* graph_;
+ // Use a dedicated Zone for this phase, with a ZoneScope to ensure it
+ // gets freed.
+ Zone zone_;
+ ZoneScope zone_scope_;
+
+ int block_count_;
+
+ // Largest number of local variables in any environment in the graph
+ // (including inlined environments).
+ int maximum_environment_size_;
+
+ // Per-block data. All these lists are indexed by block_id.
+ ZoneList<BitVector*>* live_at_block_start_;
+ ZoneList<HSimulate*>* first_simulate_;
+ ZoneList<BitVector*>* first_simulate_invalid_for_index_;
+
+ // List of all HEnvironmentMarker instructions for quick iteration/deletion.
+ // It is populated during the first pass over the graph, controlled by
+ // |collect_markers_|.
+ ZoneList<HEnvironmentMarker*>* markers_;
+ bool collect_markers_;
+
+ // Keeps track of the last simulate seen, as well as the environment slots
+ // for which a new live range has started since (so they must not be zapped
+ // in that simulate when the end of another live range of theirs is found).
+ HSimulate* last_simulate_;
+ BitVector* went_live_since_last_simulate_;
+};
+
+
+} } // namespace v8::internal
+
+#endif /* V8_HYDROGEN_ENVIRONMENT_LIVENESS_H_ */
}
+void HEnvironmentMarker::PrintDataTo(StringStream* stream) {
+ stream->Add("%s var[%d]", kind() == BIND ? "bind" : "lookup", index());
+}
+
+
void HUnaryCall::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream);
stream->Add(" ");
}
+void HEnterInlined::RegisterReturnTarget(HBasicBlock* return_target,
+ Zone* zone) {
+ ASSERT(return_target->IsInlineReturnTarget());
+ return_targets_.Add(return_target, zone);
+}
+
+
void HEnterInlined::PrintDataTo(StringStream* stream) {
SmartArrayPointer<char> name = function()->debug_name()->ToCString();
stream->Add("%s, id=%d", *name, function()->id().ToInt());
V(DummyUse) \
V(ElementsKind) \
V(EnterInlined) \
+ V(EnvironmentMarker) \
V(FixedArrayBaseLength) \
V(ForceRepresentation) \
V(FunctionLiteral) \
kHasNoObservableSideEffects,
// Indicates the instruction is live during dead code elimination.
kIsLive,
- kLastFlag = kIDefsProcessingDone
+
+ // HEnvironmentMarkers are deleted before dead code
+ // elimination takes place, so they can repurpose the kIsLive flag:
+ kEndsLiveRange = kIsLive,
+
+ // TODO(everyone): Don't forget to update this!
+ kLastFlag = kIsLive
};
STATIC_ASSERT(kLastFlag < kBitsPerInt);
class HDeoptimize: public HControlInstruction {
public:
- HDeoptimize(int environment_length, Zone* zone)
- : values_(environment_length, zone) { }
+ HDeoptimize(int environment_length,
+ int first_local_index,
+ int first_expression_index,
+ Zone* zone)
+ : values_(environment_length, zone),
+ first_local_index_(first_local_index),
+ first_expression_index_(first_expression_index) { }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::None();
values_.Add(NULL, zone);
SetOperandAt(values_.length() - 1, value);
}
+ int first_local_index() { return first_local_index_; }
+ int first_expression_index() { return first_expression_index_; }
DECLARE_CONCRETE_INSTRUCTION(Deoptimize)
private:
ZoneList<HValue*> values_;
+ int first_local_index_;
+ int first_expression_index_;
};
void AddPushedValue(HValue* value) {
AddValue(kNoIndex, value);
}
+ int ToOperandIndex(int environment_index) {
+ for (int i = 0; i < assigned_indexes_.length(); ++i) {
+ if (assigned_indexes_[i] == environment_index) return i;
+ }
+ return -1;
+ }
virtual int OperandCount() { return values_.length(); }
virtual HValue* OperandAt(int index) const { return values_[index]; }
#ifdef DEBUG
virtual void Verify();
+ void set_closure(Handle<JSFunction> closure) { closure_ = closure; }
+ Handle<JSFunction> closure() const { return closure_; }
#endif
protected:
ZoneList<int> assigned_indexes_;
Zone* zone_;
RemovableSimulate removable_;
+
+#ifdef DEBUG
+ Handle<JSFunction> closure_;
+#endif
+};
+
+
+class HEnvironmentMarker: public HTemplateInstruction<1> {
+ public:
+ enum Kind { BIND, LOOKUP };
+
+ HEnvironmentMarker(Kind kind, int index)
+ : kind_(kind), index_(index), next_simulate_(NULL) { }
+
+ Kind kind() { return kind_; }
+ int index() { return index_; }
+ HSimulate* next_simulate() { return next_simulate_; }
+ void set_next_simulate(HSimulate* simulate) {
+ next_simulate_ = simulate;
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+#ifdef DEBUG
+ void set_closure(Handle<JSFunction> closure) {
+ ASSERT(closure_.is_null());
+ ASSERT(!closure.is_null());
+ closure_ = closure;
+ }
+ Handle<JSFunction> closure() const { return closure_; }
+#endif
+
+ DECLARE_CONCRETE_INSTRUCTION(EnvironmentMarker);
+
+ private:
+ Kind kind_;
+ int index_;
+ HSimulate* next_simulate_;
+
+#ifdef DEBUG
+ Handle<JSFunction> closure_;
+#endif
};
InliningKind inlining_kind,
Variable* arguments_var,
ZoneList<HValue*>* arguments_values,
- bool undefined_receiver)
+ bool undefined_receiver,
+ Zone* zone)
: closure_(closure),
arguments_count_(arguments_count),
arguments_pushed_(false),
inlining_kind_(inlining_kind),
arguments_var_(arguments_var),
arguments_values_(arguments_values),
- undefined_receiver_(undefined_receiver) {
+ undefined_receiver_(undefined_receiver),
+ return_targets_(2, zone) {
}
+ void RegisterReturnTarget(HBasicBlock* return_target, Zone* zone);
+ ZoneList<HBasicBlock*>* return_targets() { return &return_targets_; }
+
virtual void PrintDataTo(StringStream* stream);
Handle<JSFunction> closure() const { return closure_; }
Variable* arguments_var_;
ZoneList<HValue*>* arguments_values_;
bool undefined_receiver_;
+ ZoneList<HBasicBlock*> return_targets_;
};
#include "codegen.h"
#include "full-codegen.h"
#include "hashmap.h"
+#include "hydrogen-environment-liveness.h"
#include "lithium-allocator.h"
#include "parser.h"
#include "scopeinfo.h"
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),
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());
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::MergeWith() to easily append additional pushed values
// that are older (from further down the stack).
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());
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);
}
+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);
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;
}
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_);
}
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 =
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();
// The AstContext constructor pushed on the context stack. This newed
test_context_ = new TestContext(owner, cond, 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();
Verify(true);
#endif
+ if (FLAG_analyze_environment_liveness) {
+ EnvironmentSlotLivenessAnalyzer esla(this);
+ esla.AnalyzeAndTrim();
+ }
+
PropagateDeoptimizingMark();
if (!CheckConstPhiUses()) {
*bailout_reason = SmartArrayPointer<char>(StrDup(
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);
if (var->mode() == CONST) {
return Bailout("unsupported const compound assignment");
}
- Bind(var, Top());
+ BindIfLive(var, Top());
break;
case Variable::CONTEXT: {
// permitted.
CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
HValue* value = Pop();
- Bind(var, value);
+ BindIfLive(var, value);
return ast_context()->ReturnValue(value);
}
function_state()->inlining_kind(),
function->scope()->arguments(),
arguments_values,
- undefined_receiver);
+ undefined_receiver,
+ zone());
function_state()->set_entry(enter_inlined);
AddInstruction(enter_inlined);
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();
// 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());
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 {
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).
case Variable::PARAMETER:
case Variable::LOCAL:
- Bind(var, after);
+ BindIfLive(var, after);
break;
case Variable::CONTEXT: {
case Variable::LOCAL: {
CHECK_ALIVE(VisitForValue(declaration->fun()));
HValue* value = Pop();
- environment()->Bind(variable, value);
+ BindIfLive(variable, value);
break;
}
case Variable::CONTEXT: {
int LoopNestingDepth() const;
void SetInitialEnvironment(HEnvironment* env);
- void ClearEnvironment() { last_environment_ = NULL; }
+ void ClearEnvironment() {
+ ASSERT(IsFinished());
+ ASSERT(end()->SuccessorCount() == 0);
+ last_environment_ = NULL;
+ }
bool HasEnvironment() const { return last_environment_ != NULL; }
- void UpdateEnvironment(HEnvironment* env) { last_environment_ = env; }
+ void UpdateEnvironment(HEnvironment* env);
HBasicBlock* parent_loop_header() const { return parent_loop_header_; }
void set_parent_loop_header(HBasicBlock* block) {
// Simulate (caller's environment)
// Goto (target block)
bool IsInlineReturnTarget() const { return is_inline_return_target_; }
- void MarkAsInlineReturnTarget() { is_inline_return_target_ = true; }
+ void MarkAsInlineReturnTarget(HBasicBlock* inlined_entry_block) {
+ is_inline_return_target_ = true;
+ inlined_entry_block_ = inlined_entry_block;
+ }
+ HBasicBlock* inlined_entry_block() { return inlined_entry_block_; }
bool IsDeoptimizing() const { return is_deoptimizing_; }
void MarkAsDeoptimizing() { is_deoptimizing_ = true; }
int last_instruction_index_;
ZoneList<int> deleted_phis_;
HBasicBlock* parent_loop_header_;
- bool is_inline_return_target_;
- bool is_deoptimizing_;
- bool dominates_loop_successors_;
- bool is_osr_entry_;
+ // For blocks marked as inline return target: the block with HEnterInlined.
+ HBasicBlock* inlined_entry_block_;
+ bool is_inline_return_target_ : 1;
+ bool is_deoptimizing_ : 1;
+ bool dominates_loop_successors_ : 1;
+ bool is_osr_entry_ : 1;
};
void RestoreActualValues();
void DeadCodeElimination(const char *phase_name);
void PropagateDeoptimizingMark();
+ void AnalyzeAndPruneEnvironmentLiveness();
// Returns false if there are phi-uses of the arguments-object
// which are not supported by the optimizing compiler.
return type_change_checksum_;
}
+ void update_maximum_environment_size(int environment_size) {
+ if (environment_size > maximum_environment_size_) {
+ maximum_environment_size_ = environment_size;
+ }
+ }
+ int maximum_environment_size() { return maximum_environment_size_; }
+
bool use_optimistic_licm() {
return use_optimistic_licm_;
}
bool has_soft_deoptimize_;
bool depends_on_empty_array_proto_elements_;
int type_change_checksum_;
+ int maximum_environment_size_;
DISALLOW_COPY_AND_ASSIGN(HGraph);
};
return parameter_count() + specials_count() + local_count();
}
+ int first_local_index() const {
+ return parameter_count() + specials_count();
+ }
+
void Bind(Variable* variable, HValue* value) {
Bind(IndexFor(variable), value);
}
values_[index] = value;
}
+ // Map a variable to an environment index. Parameter indices are shifted
+ // by 1 (receiver is parameter index -1 but environment index 0).
+ // Stack-allocated local indices are shifted by the number of parameters.
+ int IndexFor(Variable* variable) const {
+ ASSERT(variable->IsStackAllocated());
+ int shift = variable->IsParameter()
+ ? 1
+ : parameter_count_ + specials_count_;
+ return variable->index() + shift;
+ }
+
+ bool is_local_index(int i) const {
+ return i >= first_local_index() &&
+ i < first_expression_index();
+ }
+
void PrintTo(StringStream* stream);
void PrintToStd();
void Initialize(int parameter_count, int local_count, int stack_height);
void Initialize(const HEnvironment* other);
- // Map a variable to an environment index. Parameter indices are shifted
- // by 1 (receiver is parameter index -1 but environment index 0).
- // Stack-allocated local indices are shifted by the number of parameters.
- int IndexFor(Variable* variable) const {
- ASSERT(variable->IsStackAllocated());
- int shift = variable->IsParameter()
- ? 1
- : parameter_count_ + specials_count_;
- return variable->index() + shift;
- }
-
Handle<JSFunction> closure_;
// Value array [parameters] [specials] [locals] [temporaries].
ZoneList<HValue*> values_;
HValue* Top() const { return environment()->Top(); }
void Drop(int n) { environment()->Drop(n); }
void Bind(Variable* var, HValue* value) { environment()->Bind(var, value); }
+ bool IsEligibleForEnvironmentLivenessAnalysis(Variable* var,
+ int index,
+ HValue* value,
+ HEnvironment* env) {
+ if (!FLAG_analyze_environment_liveness) return false;
+ // |this| and |arguments| are always live; zapping parameters isn't
+ // safe because function.arguments can inspect them at any time.
+ return !var->is_this() &&
+ !var->is_arguments() &&
+ !value->IsArgumentsObject() &&
+ env->is_local_index(index);
+ }
+ void BindIfLive(Variable* var, HValue* value) {
+ HEnvironment* env = environment();
+ int index = env->IndexFor(var);
+ env->Bind(index, value);
+ if (IsEligibleForEnvironmentLivenessAnalysis(var, index, value, env)) {
+ HEnvironmentMarker* bind =
+ new(zone()) HEnvironmentMarker(HEnvironmentMarker::BIND, index);
+ AddInstruction(bind);
+#ifdef DEBUG
+ bind->set_closure(env->closure());
+#endif
+ }
+ }
+ HValue* LookupAndMakeLive(Variable* var) {
+ HEnvironment* env = environment();
+ int index = env->IndexFor(var);
+ HValue* value = env->Lookup(index);
+ if (IsEligibleForEnvironmentLivenessAnalysis(var, index, value, env)) {
+ HEnvironmentMarker* lookup =
+ new(zone()) HEnvironmentMarker(HEnvironmentMarker::LOOKUP, index);
+ AddInstruction(lookup);
+#ifdef DEBUG
+ lookup->set_closure(env->closure());
+#endif
+ }
+ return value;
+ }
// The value of the arguments object is allowed in some but not most value
// contexts. (It's allowed in all effect contexts and disallowed in all
}
+LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
}
+LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
}
+LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
a0 = a0 + a0 / 100;
b0 = b0 + b0 / 100;
debugger; // Breakpoint.
+ return a0 + b0;
};
function g3(i, x1, y1) {
a1 = a1 + a1 / 100;
b1 = b1 + b1 / 100;
h(i - 1, a1, b1);
- return a1+b1;
+ return a1 + b1;
};
function g2(i) {
a2 = a2 + a2 / 100;
b2 = b2 + b2 / 100;
g3(i - 1, a2, b2);
+ return a2 + b2;
};
function g1(i, x3, y3, z3) {
a3 = a3 + a3 / 100;
b3 = b3 + b3 / 100;
new g2(i - 1, a3, b3);
+ return a3 + b3;
};
function f(i, x4, y4) {
a4 = a4 + a4 / 100;
b4 = b4 + b4 / 100;
g1(i - 1, a4, b4);
+ return a4 + b4;
};
// Test calling f normally and as a constructor.
var a0 = expected[i].locals.a0;
var b0 = expected[i].locals.b0;
debugger; // Breakpoint.
+ return a0 + b0;
}
function g3(i, x1, y1) {
var a1 = expected[i].locals.a1;
var b1 = expected[i].locals.b1;
h(i - 1, a1, b1);
+ return a1 + b1;
}
function g2(i) {
var a2 = expected[i].locals.a2;
var b2 = expected[i].locals.b2;
g3(i - 1, a2, b2);
+ return a2 + b2;
}
function g1(i, x3, y3, z3) {
var a3 = expected[i].locals.a3;
var b3 = expected[i].locals.b3;
new g2(i - 1, a3, b3);
+ return a3 + b3;
}
function f(i, x4, y4) {
var a4 = expected[i].locals.a4;
var b4 = expected[i].locals.b4;
g1(i - 1, a4, b4);
+ return a4 + b4;
}
// Test calling f normally and as a constructor.
'../../src/heap-snapshot-generator.h',
'../../src/heap.cc',
'../../src/heap.h',
+ '../../src/hydrogen-environment-liveness.cc',
+ '../../src/hydrogen-environment-liveness.h',
'../../src/hydrogen-instructions.cc',
'../../src/hydrogen-instructions.h',
'../../src/hydrogen.cc',