Liveness analysis for environment slots in Hydrogen

R=titzer@chromium.org

Review URL: https://codereview.chromium.org/15533004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14938 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 00addf5a
......@@ -698,6 +698,12 @@ LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
}
LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
UNREACHABLE();
return NULL;
}
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
......
......@@ -254,6 +254,8 @@ DEFINE_bool(array_bounds_checks_elimination, true,
"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")
......
This diff is collapsed.
// 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_ */
......@@ -978,6 +978,11 @@ void HDummyUse::PrintDataTo(StringStream* stream) {
}
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(" ");
......@@ -2061,6 +2066,13 @@ void HDeoptimize::PrintDataTo(StringStream* stream) {
}
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());
......
......@@ -108,6 +108,7 @@ class LChunkBuilder;
V(DummyUse) \
V(ElementsKind) \
V(EnterInlined) \
V(EnvironmentMarker) \
V(FixedArrayBaseLength) \
V(ForceRepresentation) \
V(FunctionLiteral) \
......@@ -810,7 +811,13 @@ class HValue: public ZoneObject {
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);
......@@ -1485,8 +1492,13 @@ class HDebugBreak: public HTemplateInstruction<0> {
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();
......@@ -1509,6 +1521,8 @@ class HDeoptimize: public HControlInstruction {
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)
......@@ -1524,6 +1538,8 @@ class HDeoptimize: public HControlInstruction {
private:
ZoneList<HValue*> values_;
int first_local_index_;
int first_expression_index_;
};
......@@ -1840,6 +1856,12 @@ class HSimulate: public HInstruction {
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]; }
......@@ -1854,6 +1876,8 @@ class HSimulate: public HInstruction {
#ifdef DEBUG
virtual void Verify();
void set_closure(Handle<JSFunction> closure) { closure_ = closure; }
Handle<JSFunction> closure() const { return closure_; }
#endif
protected:
......@@ -1883,6 +1907,52 @@ class HSimulate: public HInstruction {
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
};
......@@ -1939,7 +2009,8 @@ class HEnterInlined: public HTemplateInstruction<0> {
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),
......@@ -1947,9 +2018,13 @@ class HEnterInlined: public HTemplateInstruction<0> {
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_; }
......@@ -1978,6 +2053,7 @@ class HEnterInlined: public HTemplateInstruction<0> {
Variable* arguments_var_;
ZoneList<HValue*>* arguments_values_;
bool undefined_receiver_;
ZoneList<HBasicBlock*> return_targets_;
};
......
This diff is collapsed.
......@@ -108,9 +108,13 @@ class HBasicBlock: public ZoneObject {
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) {
......@@ -154,7 +158,11 @@ class HBasicBlock: public ZoneObject {
// 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; }
......@@ -197,10 +205,12 @@ class HBasicBlock: public ZoneObject {
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;
};
......@@ -284,6 +294,7 @@ class HGraph: public ZoneObject {
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.
......@@ -359,6 +370,13 @@ class HGraph: public ZoneObject {
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_;
}
......@@ -452,6 +470,7 @@ class HGraph: public ZoneObject {
bool has_soft_deoptimize_;
bool depends_on_empty_array_proto_elements_;
int type_change_checksum_;
int maximum_environment_size_;
DISALLOW_COPY_AND_ASSIGN(HGraph);
};
......@@ -513,6 +532,10 @@ class HEnvironment: public ZoneObject {
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);
}
......@@ -610,6 +633,22 @@ class HEnvironment: public ZoneObject {
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();
......@@ -637,17 +676,6 @@ class HEnvironment: public ZoneObject {
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_;
......@@ -1522,6 +1550,45 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
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
......
......@@ -757,6 +757,12 @@ LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
}
LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
UNREACHABLE();
return NULL;
}
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
......
......@@ -702,6 +702,12 @@ LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
}
LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
UNREACHABLE();
return NULL;
}
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
......
......@@ -706,6 +706,12 @@ LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
}
LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
UNREACHABLE();
return NULL;
}
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
......
......@@ -185,6 +185,7 @@ function h(i, x0, y0) {
a0 = a0 + a0 / 100;
b0 = b0 + b0 / 100;
debugger; // Breakpoint.
return a0 + b0;
};
function g3(i, x1, y1) {
......@@ -193,7 +194,7 @@ 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) {
......@@ -202,6 +203,7 @@ 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) {
......@@ -210,6 +212,7 @@ 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) {
......@@ -218,6 +221,7 @@ 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.
......
......@@ -174,30 +174,35 @@ function h(i, x0, y0) {
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.
......
......@@ -322,6 +322,8 @@
'../../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',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment