Index: src/compiler/bytecode-graph-builder.cc |
diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc |
index c87e1412cbcf208c50b98a95acd06b260473c5be..a7a1cb58f40c883488876446f3ddba8fe70121cc 100644 |
--- a/src/compiler/bytecode-graph-builder.cc |
+++ b/src/compiler/bytecode-graph-builder.cc |
@@ -5,6 +5,7 @@ |
#include "src/compiler/bytecode-graph-builder.h" |
#include "src/ast/ast.h" |
+#include "src/ast/scopes.h" |
#include "src/compilation-info.h" |
#include "src/compiler/bytecode-branch-analysis.h" |
#include "src/compiler/linkage.h" |
@@ -29,6 +30,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject { |
Node* LookupAccumulator() const; |
Node* LookupRegister(interpreter::Register the_register) const; |
+ void MarkAllRegistersLive(); |
void BindAccumulator(Node* node, FrameStateBeforeAndAfter* states = nullptr); |
void BindRegister(interpreter::Register the_register, Node* node, |
@@ -45,7 +47,8 @@ class BytecodeGraphBuilder::Environment : public ZoneObject { |
// Preserve a checkpoint of the environment for the IR graph. Any |
// further mutation of the environment will not affect checkpoints. |
- Node* Checkpoint(BailoutId bytecode_offset, OutputFrameStateCombine combine); |
+ Node* Checkpoint(BailoutId bytecode_offset, OutputFrameStateCombine combine, |
+ bool owner_has_exception); |
// Returns true if the state values are up to date with the current |
// environment. |
@@ -60,7 +63,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject { |
Node* Context() const { return context_; } |
void SetContext(Node* new_context) { context_ = new_context; } |
- Environment* CopyForConditional() const; |
+ Environment* CopyForConditional(); |
Environment* CopyForLoop(); |
void Merge(Environment* other); |
void PrepareForOsr(); |
@@ -68,19 +71,27 @@ class BytecodeGraphBuilder::Environment : public ZoneObject { |
void PrepareForLoopExit(Node* loop); |
private: |
- explicit Environment(const Environment* copy); |
+ Environment(const Environment* copy, LivenessAnalyzerBlock* liveness_block); |
void PrepareForLoop(); |
+ |
+ enum { kNotCached, kCached }; |
+ |
bool StateValuesAreUpToDate(Node** state_values, int offset, int count, |
- int output_poke_start, int output_poke_end); |
+ int output_poke_start, int output_poke_end, |
+ int cached = kNotCached); |
bool StateValuesRequireUpdate(Node** state_values, int offset, int count); |
void UpdateStateValues(Node** state_values, int offset, int count); |
+ void UpdateStateValuesWithCache(Node** state_values, int offset, int count); |
int RegisterToValuesIndex(interpreter::Register the_register) const; |
+ bool IsLivenessBlockConsistent() const; |
+ |
Zone* zone() const { return builder_->local_zone(); } |
Graph* graph() const { return builder_->graph(); } |
CommonOperatorBuilder* common() const { return builder_->common(); } |
BytecodeGraphBuilder* builder() const { return builder_; } |
+ LivenessAnalyzerBlock* liveness_block() const { return liveness_block_; } |
const NodeVector* values() const { return &values_; } |
NodeVector* values() { return &values_; } |
int register_base() const { return register_base_; } |
@@ -89,6 +100,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject { |
BytecodeGraphBuilder* builder_; |
int register_count_; |
int parameter_count_; |
+ LivenessAnalyzerBlock* liveness_block_; |
Node* context_; |
Node* control_dependency_; |
Node* effect_dependency_; |
@@ -112,7 +124,7 @@ class BytecodeGraphBuilder::FrameStateBeforeAndAfter { |
output_poke_count_(0) { |
BailoutId id_before(builder->bytecode_iterator().current_offset()); |
frame_state_before_ = builder_->environment()->Checkpoint( |
- id_before, OutputFrameStateCombine::Ignore()); |
+ id_before, OutputFrameStateCombine::Ignore(), false); |
id_after_ = BailoutId(id_before.ToInt() + |
builder->bytecode_iterator().current_bytecode_size()); |
// Create an explicit checkpoint node for before the operation. |
@@ -139,8 +151,9 @@ class BytecodeGraphBuilder::FrameStateBeforeAndAfter { |
// Add the frame state for after the operation. |
DCHECK_EQ(IrOpcode::kDead, |
NodeProperties::GetFrameStateInput(node)->opcode()); |
- Node* frame_state_after = |
- builder_->environment()->Checkpoint(id_after_, combine); |
+ bool has_exception = NodeProperties::IsExceptionalCall(node); |
+ Node* frame_state_after = builder_->environment()->Checkpoint( |
+ id_after_, combine, has_exception); |
NodeProperties::ReplaceFrameStateInput(node, frame_state_after); |
} |
@@ -174,6 +187,9 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder, |
: builder_(builder), |
register_count_(register_count), |
parameter_count_(parameter_count), |
+ liveness_block_(builder->is_liveness_analysis_enabled_ |
+ ? builder_->liveness_analyzer()->NewBlock() |
+ : nullptr), |
context_(context), |
control_dependency_(control_dependency), |
effect_dependency_(control_dependency), |
@@ -207,12 +223,13 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder, |
values()->push_back(undefined_constant); |
} |
- |
BytecodeGraphBuilder::Environment::Environment( |
- const BytecodeGraphBuilder::Environment* other) |
+ const BytecodeGraphBuilder::Environment* other, |
+ LivenessAnalyzerBlock* liveness_block) |
: builder_(other->builder_), |
register_count_(other->register_count_), |
parameter_count_(other->parameter_count_), |
+ liveness_block_(liveness_block), |
context_(other->context_), |
control_dependency_(other->control_dependency_), |
effect_dependency_(other->effect_dependency_), |
@@ -235,6 +252,10 @@ int BytecodeGraphBuilder::Environment::RegisterToValuesIndex( |
} |
} |
+bool BytecodeGraphBuilder::Environment::IsLivenessBlockConsistent() const { |
+ return !builder_->IsLivenessAnalysisEnabled() == |
+ (liveness_block() == nullptr); |
+} |
Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const { |
return values()->at(accumulator_base_); |
@@ -251,10 +272,22 @@ Node* BytecodeGraphBuilder::Environment::LookupRegister( |
return builder()->GetNewTarget(); |
} else { |
int values_index = RegisterToValuesIndex(the_register); |
+ if (liveness_block() != nullptr && !the_register.is_parameter()) { |
+ DCHECK(IsLivenessBlockConsistent()); |
+ liveness_block()->Lookup(the_register.index()); |
+ } |
return values()->at(values_index); |
} |
} |
+void BytecodeGraphBuilder::Environment::MarkAllRegistersLive() { |
+ DCHECK(IsLivenessBlockConsistent()); |
+ if (liveness_block() != nullptr) { |
+ for (int i = 0; i < register_count(); ++i) { |
+ liveness_block()->Lookup(i); |
+ } |
+ } |
+} |
void BytecodeGraphBuilder::Environment::BindAccumulator( |
Node* node, FrameStateBeforeAndAfter* states) { |
@@ -274,6 +307,10 @@ void BytecodeGraphBuilder::Environment::BindRegister( |
values_index)); |
} |
values()->at(values_index) = node; |
+ if (liveness_block() != nullptr && !the_register.is_parameter()) { |
+ DCHECK(IsLivenessBlockConsistent()); |
+ liveness_block()->Bind(the_register.index()); |
+ } |
} |
@@ -301,18 +338,35 @@ void BytecodeGraphBuilder::Environment::RecordAfterState( |
BytecodeGraphBuilder::Environment* |
BytecodeGraphBuilder::Environment::CopyForLoop() { |
PrepareForLoop(); |
- return new (zone()) Environment(this); |
+ if (liveness_block() != nullptr) { |
+ // Finish the current block before copying. |
+ liveness_block_ = builder_->liveness_analyzer()->NewBlock(liveness_block()); |
+ } |
+ return new (zone()) Environment(this, liveness_block()); |
} |
- |
BytecodeGraphBuilder::Environment* |
-BytecodeGraphBuilder::Environment::CopyForConditional() const { |
- return new (zone()) Environment(this); |
+BytecodeGraphBuilder::Environment::CopyForConditional() { |
+ LivenessAnalyzerBlock* copy_liveness_block = nullptr; |
+ if (liveness_block() != nullptr) { |
+ copy_liveness_block = |
+ builder_->liveness_analyzer()->NewBlock(liveness_block()); |
+ liveness_block_ = builder_->liveness_analyzer()->NewBlock(liveness_block()); |
+ } |
+ return new (zone()) Environment(this, copy_liveness_block); |
} |
void BytecodeGraphBuilder::Environment::Merge( |
BytecodeGraphBuilder::Environment* other) { |
+ if (builder_->is_liveness_analysis_enabled_) { |
+ if (GetControlDependency()->opcode() != IrOpcode::kLoop) { |
+ liveness_block_ = |
+ builder()->liveness_analyzer()->NewBlock(liveness_block()); |
+ } |
+ liveness_block()->AddPredecessor(other->liveness_block()); |
+ } |
+ |
// Create a merge of the control dependencies of both environments and update |
// the current environment's control dependency accordingly. |
Node* control = builder()->MergeControl(GetControlDependency(), |
@@ -437,13 +491,19 @@ void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values, |
} |
} |
+void BytecodeGraphBuilder::Environment::UpdateStateValuesWithCache( |
+ Node** state_values, int offset, int count) { |
+ Node** env_values = (count == 0) ? nullptr : &values()->at(offset); |
+ *state_values = builder_->state_values_cache_.GetNodeForValues( |
+ env_values, static_cast<size_t>(count)); |
+} |
Node* BytecodeGraphBuilder::Environment::Checkpoint( |
- BailoutId bailout_id, OutputFrameStateCombine combine) { |
- // TODO(rmcilroy): Consider using StateValuesCache for some state values. |
+ BailoutId bailout_id, OutputFrameStateCombine combine, |
+ bool owner_has_exception) { |
UpdateStateValues(¶meters_state_values_, 0, parameter_count()); |
- UpdateStateValues(®isters_state_values_, register_base(), |
- register_count()); |
+ UpdateStateValuesWithCache(®isters_state_values_, register_base(), |
+ register_count()); |
UpdateStateValues(&accumulator_state_values_, accumulator_base(), 1); |
const Operator* op = common()->FrameState( |
@@ -453,19 +513,42 @@ Node* BytecodeGraphBuilder::Environment::Checkpoint( |
accumulator_state_values_, Context(), builder()->GetFunctionClosure(), |
builder()->graph()->start()); |
+ if (liveness_block() != nullptr) { |
+ // If the owning node has an exception, register the checkpoint to the |
+ // predecessor so that the checkpoint is used for both the normal and the |
+ // exceptional paths. Yes, this is a terrible hack and we might want |
+ // to use an explicit frame state for the exceptional path. |
+ if (owner_has_exception) { |
+ liveness_block()->GetPredecessor()->Checkpoint(result); |
+ } else { |
+ liveness_block()->Checkpoint(result); |
+ } |
+ } |
+ |
return result; |
} |
- |
bool BytecodeGraphBuilder::Environment::StateValuesAreUpToDate( |
Node** state_values, int offset, int count, int output_poke_start, |
- int output_poke_end) { |
+ int output_poke_end, int cached) { |
DCHECK_LE(static_cast<size_t>(offset + count), values()->size()); |
- for (int i = 0; i < count; i++, offset++) { |
- if (offset < output_poke_start || offset >= output_poke_end) { |
- if ((*state_values)->InputAt(i) != values()->at(offset)) { |
- return false; |
+ if (cached == kNotCached) { |
+ for (int i = 0; i < count; i++, offset++) { |
+ if (offset < output_poke_start || offset >= output_poke_end) { |
+ if ((*state_values)->InputAt(i) != values()->at(offset)) { |
+ return false; |
+ } |
+ } |
+ } |
+ } else { |
+ for (StateValuesAccess::TypedNode state_value : |
+ StateValuesAccess(*state_values)) { |
+ if (offset < output_poke_start || offset >= output_poke_end) { |
+ if (state_value.node != values()->at(offset)) { |
+ return false; |
+ } |
} |
+ ++offset; |
} |
} |
return true; |
@@ -481,7 +564,7 @@ bool BytecodeGraphBuilder::Environment::StateValuesAreUpToDate( |
output_poke_start, output_poke_end) && |
StateValuesAreUpToDate(®isters_state_values_, register_base(), |
register_count(), output_poke_start, |
- output_poke_end) && |
+ output_poke_end, kCached) && |
StateValuesAreUpToDate(&accumulator_state_values_, accumulator_base(), |
1, output_poke_start, output_poke_end); |
} |
@@ -505,7 +588,13 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(Zone* local_zone, |
current_exception_handler_(0), |
input_buffer_size_(0), |
input_buffer_(nullptr), |
- exit_controls_(local_zone) {} |
+ exit_controls_(local_zone), |
+ is_liveness_analysis_enabled_(FLAG_analyze_environment_liveness && |
+ info->is_deoptimization_enabled()), |
+ state_values_cache_(jsgraph), |
+ liveness_analyzer_( |
+ static_cast<size_t>(bytecode_array()->register_count()), local_zone) { |
+} |
Node* BytecodeGraphBuilder::GetNewTarget() { |
if (!new_target_.is_set()) { |
@@ -587,9 +676,25 @@ bool BytecodeGraphBuilder::CreateGraph() { |
Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs); |
graph()->SetEnd(end); |
+ ClearNonLiveSlotsInFrameStates(); |
+ |
return true; |
} |
+void BytecodeGraphBuilder::ClearNonLiveSlotsInFrameStates() { |
+ if (!IsLivenessAnalysisEnabled()) { |
+ return; |
+ } |
+ NonLiveFrameStateSlotReplacer replacer( |
+ &state_values_cache_, jsgraph()->OptimizedOutConstant(), |
+ liveness_analyzer()->local_count(), local_zone()); |
+ liveness_analyzer()->Run(&replacer); |
+ if (FLAG_trace_environment_liveness) { |
+ OFStream os(stdout); |
+ liveness_analyzer()->Print(os); |
+ } |
+} |
+ |
void BytecodeGraphBuilder::VisitBytecodes() { |
BytecodeBranchAnalysis analysis(bytecode_array(), local_zone()); |
BytecodeLoopAnalysis loop_analysis(bytecode_array(), &analysis, local_zone()); |
@@ -1548,6 +1653,7 @@ void BytecodeGraphBuilder::VisitDebugger() { |
Node* call = |
NewNode(javascript()->CallRuntime(Runtime::kHandleDebuggerStatement)); |
environment()->BindAccumulator(call, &states); |
+ environment()->MarkAllRegistersLive(); |
} |
// We cannot create a graph from the debugger copy of the bytecode array. |