Commit b5829880 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[turbofan] Move bytecode analysis to the serializer

When --concurrent-inlining is on, run bytecode analysis for all relevant
functions at serialization time, and store the results in the broker.

Change bytecode analysis such that running it for OSR produces information
that subsumes the non-OSR case. This lets us avoid doing and storing two
analyses for the top-level function in case we do OSR and the function
gets inlined into itself.

Bug: v8:7790
Change-Id: I7d5df0b2652e6e5c758c85578e51b4f8d041b0d9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1690959
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62711}
parent 4ee7697c
...@@ -79,17 +79,21 @@ ResumeJumpTarget ResumeJumpTarget::AtLoopHeader(int loop_header_offset, ...@@ -79,17 +79,21 @@ ResumeJumpTarget ResumeJumpTarget::AtLoopHeader(int loop_header_offset,
} }
BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
Zone* zone, bool do_liveness_analysis) Zone* zone, BailoutId osr_bailout_id,
bool analyze_liveness)
: bytecode_array_(bytecode_array), : bytecode_array_(bytecode_array),
do_liveness_analysis_(do_liveness_analysis),
zone_(zone), zone_(zone),
osr_bailout_id_(osr_bailout_id),
analyze_liveness_(analyze_liveness),
loop_stack_(zone), loop_stack_(zone),
loop_end_index_queue_(zone), loop_end_index_queue_(zone),
resume_jump_targets_(zone), resume_jump_targets_(zone),
end_to_header_(zone), end_to_header_(zone),
header_to_info_(zone), header_to_info_(zone),
osr_entry_point_(-1), osr_entry_point_(-1),
liveness_map_(bytecode_array->length(), zone) {} liveness_map_(bytecode_array->length(), zone) {
Analyze();
}
namespace { namespace {
...@@ -315,15 +319,13 @@ void UpdateAssignments( ...@@ -315,15 +319,13 @@ void UpdateAssignments(
} // namespace } // namespace
void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { void BytecodeAnalysis::Analyze() {
loop_stack_.push({-1, nullptr}); loop_stack_.push({-1, nullptr});
BytecodeLivenessState* next_bytecode_in_liveness = nullptr; BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
bool is_osr = !osr_bailout_id.IsNone();
int osr_loop_end_offset = is_osr ? osr_bailout_id.ToInt() : -1;
int generator_switch_index = -1; int generator_switch_index = -1;
int osr_loop_end_offset = osr_bailout_id_.ToInt();
DCHECK_EQ(osr_loop_end_offset < 0, osr_bailout_id_.IsNone());
interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
for (iterator.GoToEnd(); iterator.IsValid(); --iterator) { for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
...@@ -345,14 +347,14 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { ...@@ -345,14 +347,14 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
if (current_offset == osr_loop_end_offset) { if (current_offset == osr_loop_end_offset) {
osr_entry_point_ = loop_header; osr_entry_point_ = loop_header;
} else if (current_offset < osr_loop_end_offset) { } else if (current_offset < osr_loop_end_offset) {
// Check we've found the osr_entry_point if we've gone past the // Assert that we've found the osr_entry_point if we've gone past the
// osr_loop_end_offset. Note, we are iterating the bytecode in reverse, // osr_loop_end_offset. Note, we are iterating the bytecode in reverse,
// so the less than in the check is correct. // so the less-than in the above condition is correct.
DCHECK_NE(-1, osr_entry_point_); DCHECK_LE(0, osr_entry_point_);
} }
// Save the index so that we can do another pass later. // Save the index so that we can do another pass later.
if (do_liveness_analysis_) { if (analyze_liveness_) {
loop_end_index_queue_.push_back(iterator.current_index()); loop_end_index_queue_.push_back(iterator.current_index());
} }
} else if (loop_stack_.size() > 1) { } else if (loop_stack_.size() > 1) {
...@@ -365,8 +367,8 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { ...@@ -365,8 +367,8 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
// information we currently have. // information we currently have.
UpdateAssignments(bytecode, current_loop_info->assignments(), iterator); UpdateAssignments(bytecode, current_loop_info->assignments(), iterator);
// Update suspend counts for this loop, though only if not OSR. // Update suspend counts for this loop.
if (!is_osr && bytecode == Bytecode::kSuspendGenerator) { if (bytecode == Bytecode::kSuspendGenerator) {
int suspend_id = iterator.GetUnsignedImmediateOperand(3); int suspend_id = iterator.GetUnsignedImmediateOperand(3);
int resume_offset = current_offset + iterator.current_bytecode_size(); int resume_offset = current_offset + iterator.current_bytecode_size();
current_loop_info->AddResumeTarget( current_loop_info->AddResumeTarget(
...@@ -420,7 +422,7 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { ...@@ -420,7 +422,7 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
} }
} }
} }
} else if (!is_osr && bytecode == Bytecode::kSuspendGenerator) { } else if (bytecode == Bytecode::kSuspendGenerator) {
// If we're not in a loop, we still need to look for suspends. // If we're not in a loop, we still need to look for suspends.
// TODO(leszeks): It would be nice to de-duplicate this with the in-loop // TODO(leszeks): It would be nice to de-duplicate this with the in-loop
// case // case
...@@ -430,7 +432,7 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { ...@@ -430,7 +432,7 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
ResumeJumpTarget::Leaf(suspend_id, resume_offset)); ResumeJumpTarget::Leaf(suspend_id, resume_offset));
} }
if (do_liveness_analysis_) { if (analyze_liveness_) {
BytecodeLiveness& liveness = liveness_map_.InitializeLiveness( BytecodeLiveness& liveness = liveness_map_.InitializeLiveness(
current_offset, bytecode_array()->register_count(), zone()); current_offset, bytecode_array()->register_count(), zone());
UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator, UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
...@@ -443,7 +445,7 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { ...@@ -443,7 +445,7 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
DCHECK(ResumeJumpTargetsAreValid()); DCHECK(ResumeJumpTargetsAreValid());
if (!do_liveness_analysis_) return; if (!analyze_liveness_) return;
// At this point, every bytecode has a valid in and out liveness, except for // At this point, every bytecode has a valid in and out liveness, except for
// propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness
...@@ -547,7 +549,7 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { ...@@ -547,7 +549,7 @@ void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
} }
} }
DCHECK(do_liveness_analysis_); DCHECK(analyze_liveness_);
if (FLAG_trace_environment_liveness) { if (FLAG_trace_environment_liveness) {
StdoutStream of; StdoutStream of;
PrintLivenessTo(of); PrintLivenessTo(of);
...@@ -619,14 +621,14 @@ const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const { ...@@ -619,14 +621,14 @@ const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const {
const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor( const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor(
int offset) const { int offset) const {
if (!do_liveness_analysis_) return nullptr; if (!analyze_liveness_) return nullptr;
return liveness_map_.GetInLiveness(offset); return liveness_map_.GetInLiveness(offset);
} }
const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor( const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor(
int offset) const { int offset) const {
if (!do_liveness_analysis_) return nullptr; if (!analyze_liveness_) return nullptr;
return liveness_map_.GetOutLiveness(offset); return liveness_map_.GetOutLiveness(offset);
} }
...@@ -671,9 +673,8 @@ bool BytecodeAnalysis::ResumeJumpTargetsAreValid() { ...@@ -671,9 +673,8 @@ bool BytecodeAnalysis::ResumeJumpTargetsAreValid() {
} }
// If the iterator is invalid, we've reached the end without finding the // If the iterator is invalid, we've reached the end without finding the
// generator switch. Similarly, if we are OSR-ing, we're not resuming, so we // generator switch. So, ensure there are no jump targets and exit.
// need no jump targets. So, ensure there are no jump targets and exit. if (!iterator.IsValid()) {
if (!iterator.IsValid() || HasOsrEntryPoint()) {
// Check top-level. // Check top-level.
if (!resume_jump_targets().empty()) { if (!resume_jump_targets().empty()) {
PrintF(stderr, PrintF(stderr,
......
...@@ -92,18 +92,14 @@ struct V8_EXPORT_PRIVATE LoopInfo { ...@@ -92,18 +92,14 @@ struct V8_EXPORT_PRIVATE LoopInfo {
ZoneVector<ResumeJumpTarget> resume_jump_targets_; ZoneVector<ResumeJumpTarget> resume_jump_targets_;
}; };
class V8_EXPORT_PRIVATE BytecodeAnalysis { // Analyze the bytecodes to find the loop ranges, loop nesting, loop assignments
// and liveness. NOTE: The broker/serializer relies on the fact that an
// analysis for OSR (osr_bailout_id is not None) subsumes an analysis for
// non-OSR (osr_bailout_id is None).
class V8_EXPORT_PRIVATE BytecodeAnalysis : public ZoneObject {
public: public:
BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, Zone* zone, BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, Zone* zone,
bool do_liveness_analysis); BailoutId osr_bailout_id, bool analyze_liveness);
// Analyze the bytecodes to find the loop ranges, loop nesting, loop
// assignments and liveness, under the assumption that there is an OSR bailout
// at {osr_bailout_id}.
//
// No other methods in this class return valid information until this has been
// called.
void Analyze(BailoutId osr_bailout_id);
// Return true if the given offset is a loop header // Return true if the given offset is a loop header
bool IsLoopHeader(int offset) const; bool IsLoopHeader(int offset) const;
...@@ -118,23 +114,30 @@ class V8_EXPORT_PRIVATE BytecodeAnalysis { ...@@ -118,23 +114,30 @@ class V8_EXPORT_PRIVATE BytecodeAnalysis {
return resume_jump_targets_; return resume_jump_targets_;
} }
// True if the current analysis has an OSR entry point. // Gets the in-/out-liveness for the bytecode at {offset}.
bool HasOsrEntryPoint() const { return osr_entry_point_ != -1; }
int osr_entry_point() const { return osr_entry_point_; }
// Gets the in-liveness for the bytecode at {offset}.
const BytecodeLivenessState* GetInLivenessFor(int offset) const; const BytecodeLivenessState* GetInLivenessFor(int offset) const;
// Gets the out-liveness for the bytecode at {offset}.
const BytecodeLivenessState* GetOutLivenessFor(int offset) const; const BytecodeLivenessState* GetOutLivenessFor(int offset) const;
// In the case of OSR, the analysis also computes the (bytecode offset of the)
// OSR entry point from the {osr_bailout_id} that was given to the
// constructor.
int osr_entry_point() const {
CHECK_LE(0, osr_entry_point_);
return osr_entry_point_;
}
// Return the osr_bailout_id (for verification purposes).
BailoutId osr_bailout_id() const { return osr_bailout_id_; }
// Return whether liveness analysis was performed (for verification purposes).
bool liveness_analyzed() const { return analyze_liveness_; }
private: private:
struct LoopStackEntry { struct LoopStackEntry {
int header_offset; int header_offset;
LoopInfo* loop_info; LoopInfo* loop_info;
}; };
void Analyze();
void PushLoop(int loop_header, int loop_end); void PushLoop(int loop_header, int loop_end);
#if DEBUG #if DEBUG
...@@ -153,17 +156,15 @@ class V8_EXPORT_PRIVATE BytecodeAnalysis { ...@@ -153,17 +156,15 @@ class V8_EXPORT_PRIVATE BytecodeAnalysis {
std::ostream& PrintLivenessTo(std::ostream& os) const; std::ostream& PrintLivenessTo(std::ostream& os) const;
Handle<BytecodeArray> const bytecode_array_; Handle<BytecodeArray> const bytecode_array_;
bool const do_liveness_analysis_;
Zone* const zone_; Zone* const zone_;
BailoutId const osr_bailout_id_;
bool const analyze_liveness_;
ZoneStack<LoopStackEntry> loop_stack_; ZoneStack<LoopStackEntry> loop_stack_;
ZoneVector<int> loop_end_index_queue_; ZoneVector<int> loop_end_index_queue_;
ZoneVector<ResumeJumpTarget> resume_jump_targets_; ZoneVector<ResumeJumpTarget> resume_jump_targets_;
ZoneMap<int, int> end_to_header_; ZoneMap<int, int> end_to_header_;
ZoneMap<int, LoopInfo> header_to_info_; ZoneMap<int, LoopInfo> header_to_info_;
int osr_entry_point_; int osr_entry_point_;
BytecodeLivenessMap liveness_map_; BytecodeLivenessMap liveness_map_;
DISALLOW_COPY_AND_ASSIGN(BytecodeAnalysis); DISALLOW_COPY_AND_ASSIGN(BytecodeAnalysis);
......
...@@ -340,8 +340,6 @@ class BytecodeGraphBuilder { ...@@ -340,8 +340,6 @@ class BytecodeGraphBuilder {
return bytecode_analysis_; return bytecode_analysis_;
} }
void RunBytecodeAnalysis() { bytecode_analysis_.Analyze(osr_offset_); }
int currently_peeled_loop_offset() const { int currently_peeled_loop_offset() const {
return currently_peeled_loop_offset_; return currently_peeled_loop_offset_;
} }
...@@ -385,9 +383,9 @@ class BytecodeGraphBuilder { ...@@ -385,9 +383,9 @@ class BytecodeGraphBuilder {
const FrameStateFunctionInfo* const frame_state_function_info_; const FrameStateFunctionInfo* const frame_state_function_info_;
std::unique_ptr<SourcePositionTableIterator> source_position_iterator_; std::unique_ptr<SourcePositionTableIterator> source_position_iterator_;
interpreter::BytecodeArrayIterator bytecode_iterator_; interpreter::BytecodeArrayIterator bytecode_iterator_;
BytecodeAnalysis bytecode_analysis_; BytecodeAnalysis const& bytecode_analysis_;
Environment* environment_; Environment* environment_;
BailoutId const osr_offset_; bool const osr_;
int currently_peeled_loop_offset_; int currently_peeled_loop_offset_;
bool skip_next_stack_check_; bool skip_next_stack_check_;
...@@ -958,11 +956,12 @@ BytecodeGraphBuilder::BytecodeGraphBuilder( ...@@ -958,11 +956,12 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
shared_info)), shared_info)),
bytecode_iterator_( bytecode_iterator_(
base::make_unique<OffHeapBytecodeArray>(bytecode_array)), base::make_unique<OffHeapBytecodeArray>(bytecode_array)),
bytecode_analysis_( bytecode_analysis_(broker_->GetBytecodeAnalysis(
bytecode_array.object(), local_zone, bytecode_array.object(), osr_offset,
flags & BytecodeGraphBuilderFlag::kAnalyzeEnvironmentLiveness), flags & BytecodeGraphBuilderFlag::kAnalyzeEnvironmentLiveness,
!FLAG_concurrent_inlining)),
environment_(nullptr), environment_(nullptr),
osr_offset_(osr_offset), osr_(!osr_offset.IsNone()),
currently_peeled_loop_offset_(-1), currently_peeled_loop_offset_(-1),
skip_next_stack_check_(flags & skip_next_stack_check_(flags &
BytecodeGraphBuilderFlag::kSkipFirstStackCheck), BytecodeGraphBuilderFlag::kSkipFirstStackCheck),
...@@ -1117,19 +1116,17 @@ class BytecodeGraphBuilder::OsrIteratorState { ...@@ -1117,19 +1116,17 @@ class BytecodeGraphBuilder::OsrIteratorState {
void ProcessOsrPrelude() { void ProcessOsrPrelude() {
ZoneVector<int> outer_loop_offsets(graph_builder_->local_zone()); ZoneVector<int> outer_loop_offsets(graph_builder_->local_zone());
BytecodeAnalysis const& bytecode_analysis = int osr_entry = graph_builder_->bytecode_analysis().osr_entry_point();
graph_builder_->bytecode_analysis();
int osr_offset = bytecode_analysis.osr_entry_point();
// We find here the outermost loop which contains the OSR loop. // We find here the outermost loop which contains the OSR loop.
int outermost_loop_offset = osr_offset; int outermost_loop_offset = osr_entry;
while ((outermost_loop_offset = while ((outermost_loop_offset = graph_builder_->bytecode_analysis()
bytecode_analysis.GetLoopInfoFor(outermost_loop_offset) .GetLoopInfoFor(outermost_loop_offset)
.parent_offset()) != -1) { .parent_offset()) != -1) {
outer_loop_offsets.push_back(outermost_loop_offset); outer_loop_offsets.push_back(outermost_loop_offset);
} }
outermost_loop_offset = outermost_loop_offset =
outer_loop_offsets.empty() ? osr_offset : outer_loop_offsets.back(); outer_loop_offsets.empty() ? osr_entry : outer_loop_offsets.back();
graph_builder_->AdvanceIteratorsTo(outermost_loop_offset); graph_builder_->AdvanceIteratorsTo(outermost_loop_offset);
// We save some iterators states at the offsets of the loop headers of the // We save some iterators states at the offsets of the loop headers of the
...@@ -1147,14 +1144,16 @@ class BytecodeGraphBuilder::OsrIteratorState { ...@@ -1147,14 +1144,16 @@ class BytecodeGraphBuilder::OsrIteratorState {
} }
// Finishing by advancing to the OSR entry // Finishing by advancing to the OSR entry
graph_builder_->AdvanceIteratorsTo(osr_offset); graph_builder_->AdvanceIteratorsTo(osr_entry);
// Enters all remaining exception handler which end before the OSR loop // Enters all remaining exception handler which end before the OSR loop
// so that on next call of VisitSingleBytecode they will get popped from // so that on next call of VisitSingleBytecode they will get popped from
// the exception handlers stack. // the exception handlers stack.
graph_builder_->ExitThenEnterExceptionHandlers(osr_offset); graph_builder_->ExitThenEnterExceptionHandlers(osr_entry);
graph_builder_->set_currently_peeled_loop_offset( graph_builder_->set_currently_peeled_loop_offset(
bytecode_analysis.GetLoopInfoFor(osr_offset).parent_offset()); graph_builder_->bytecode_analysis()
.GetLoopInfoFor(osr_entry)
.parent_offset());
} }
void RestoreState(int target_offset, int new_parent_offset) { void RestoreState(int target_offset, int new_parent_offset) {
...@@ -1203,8 +1202,8 @@ void BytecodeGraphBuilder::RemoveMergeEnvironmentsBeforeOffset( ...@@ -1203,8 +1202,8 @@ void BytecodeGraphBuilder::RemoveMergeEnvironmentsBeforeOffset(
void BytecodeGraphBuilder::AdvanceToOsrEntryAndPeelLoops() { void BytecodeGraphBuilder::AdvanceToOsrEntryAndPeelLoops() {
OsrIteratorState iterator_states(this); OsrIteratorState iterator_states(this);
iterator_states.ProcessOsrPrelude(); iterator_states.ProcessOsrPrelude();
int osr_offset = bytecode_analysis().osr_entry_point(); int osr_entry = bytecode_analysis().osr_entry_point();
DCHECK_EQ(bytecode_iterator().current_offset(), osr_offset); DCHECK_EQ(bytecode_iterator().current_offset(), osr_entry);
environment()->FillWithOsrValues(); environment()->FillWithOsrValues();
...@@ -1222,7 +1221,7 @@ void BytecodeGraphBuilder::AdvanceToOsrEntryAndPeelLoops() { ...@@ -1222,7 +1221,7 @@ void BytecodeGraphBuilder::AdvanceToOsrEntryAndPeelLoops() {
// parent loop entirely, and so on. // parent loop entirely, and so on.
int current_parent_offset = int current_parent_offset =
bytecode_analysis().GetLoopInfoFor(osr_offset).parent_offset(); bytecode_analysis().GetLoopInfoFor(osr_entry).parent_offset();
while (current_parent_offset != -1) { while (current_parent_offset != -1) {
const LoopInfo& current_parent_loop = const LoopInfo& current_parent_loop =
bytecode_analysis().GetLoopInfoFor(current_parent_offset); bytecode_analysis().GetLoopInfoFor(current_parent_offset);
...@@ -1294,14 +1293,12 @@ void BytecodeGraphBuilder::VisitSingleBytecode() { ...@@ -1294,14 +1293,12 @@ void BytecodeGraphBuilder::VisitSingleBytecode() {
} }
void BytecodeGraphBuilder::VisitBytecodes() { void BytecodeGraphBuilder::VisitBytecodes() {
RunBytecodeAnalysis();
if (!bytecode_analysis().resume_jump_targets().empty()) { if (!bytecode_analysis().resume_jump_targets().empty()) {
environment()->BindGeneratorState( environment()->BindGeneratorState(
jsgraph()->SmiConstant(JSGeneratorObject::kGeneratorExecuting)); jsgraph()->SmiConstant(JSGeneratorObject::kGeneratorExecuting));
} }
if (bytecode_analysis().HasOsrEntryPoint()) { if (osr_) {
// We peel the OSR loop and any outer loop containing it except that we // We peel the OSR loop and any outer loop containing it except that we
// leave the nodes corresponding to the whole outermost loop (including // leave the nodes corresponding to the whole outermost loop (including
// the last copies of the loops it contains) to be generated by the normal // the last copies of the loops it contains) to be generated by the normal
......
...@@ -25,6 +25,9 @@ class SourcePositionTable; ...@@ -25,6 +25,9 @@ class SourcePositionTable;
enum class BytecodeGraphBuilderFlag : uint8_t { enum class BytecodeGraphBuilderFlag : uint8_t {
kSkipFirstStackCheck = 1 << 0, kSkipFirstStackCheck = 1 << 0,
// TODO(neis): Remove liveness flag here when concurrent inlining is always
// on, because then the serializer will be the only place where we perform
// bytecode analysis.
kAnalyzeEnvironmentLiveness = 1 << 1, kAnalyzeEnvironmentLiveness = 1 << 1,
kBailoutOnUninitialized = 1 << 2, kBailoutOnUninitialized = 1 << 2,
}; };
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "src/ast/modules.h" #include "src/ast/modules.h"
#include "src/codegen/code-factory.h" #include "src/codegen/code-factory.h"
#include "src/compiler/access-info.h" #include "src/compiler/access-info.h"
#include "src/compiler/bytecode-analysis.h"
#include "src/compiler/graph-reducer.h" #include "src/compiler/graph-reducer.h"
#include "src/compiler/per-isolate-compiler-cache.h" #include "src/compiler/per-isolate-compiler-cache.h"
#include "src/compiler/vector-slot-pair.h" #include "src/compiler/vector-slot-pair.h"
...@@ -1353,20 +1354,18 @@ class BytecodeArrayData : public FixedArrayBaseData { ...@@ -1353,20 +1354,18 @@ class BytecodeArrayData : public FixedArrayBaseData {
void SerializeForCompilation(JSHeapBroker* broker) { void SerializeForCompilation(JSHeapBroker* broker) {
if (is_serialized_for_compilation_) return; if (is_serialized_for_compilation_) return;
DCHECK(bytecodes_.empty());
DCHECK(constant_pool_.empty());
Handle<BytecodeArray> bytecode_array = Handle<BytecodeArray> bytecode_array =
Handle<BytecodeArray>::cast(object()); Handle<BytecodeArray>::cast(object());
DCHECK(bytecodes_.empty());
bytecodes_.reserve(bytecode_array->length()); bytecodes_.reserve(bytecode_array->length());
for (int i = 0; i < bytecode_array->length(); i++) { for (int i = 0; i < bytecode_array->length(); i++) {
bytecodes_.push_back(bytecode_array->get(i)); bytecodes_.push_back(bytecode_array->get(i));
} }
DCHECK(constant_pool_.empty());
Handle<FixedArray> constant_pool(bytecode_array->constant_pool(), Handle<FixedArray> constant_pool(bytecode_array->constant_pool(),
broker->isolate()); broker->isolate());
constant_pool_.reserve(constant_pool->length()); constant_pool_.reserve(constant_pool->length());
for (int i = 0; i < constant_pool->length(); i++) { for (int i = 0; i < constant_pool->length(); i++) {
constant_pool_.push_back(broker->GetOrCreateData(constant_pool->get(i))); constant_pool_.push_back(broker->GetOrCreateData(constant_pool->get(i)));
...@@ -2067,6 +2066,7 @@ JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone, ...@@ -2067,6 +2066,7 @@ JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone,
array_and_object_prototypes_(zone()), array_and_object_prototypes_(zone()),
tracing_enabled_(tracing_enabled), tracing_enabled_(tracing_enabled),
feedback_(zone()), feedback_(zone()),
bytecode_analyses_(zone()),
ais_for_loading_then_(zone()) { ais_for_loading_then_(zone()) {
// Note that this initialization of the refs_ pointer with the minimal // Note that this initialization of the refs_ pointer with the minimal
// initial capacity is redundant in the normal use case (concurrent // initial capacity is redundant in the normal use case (concurrent
...@@ -4050,6 +4050,36 @@ NamedAccessFeedback const* ProcessedFeedback::AsNamedAccess() const { ...@@ -4050,6 +4050,36 @@ NamedAccessFeedback const* ProcessedFeedback::AsNamedAccess() const {
return static_cast<NamedAccessFeedback const*>(this); return static_cast<NamedAccessFeedback const*>(this);
} }
BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis(
Handle<BytecodeArray> bytecode_array, BailoutId osr_bailout_id,
bool analyze_liveness, bool serialize) {
ObjectData* bytecode_array_data = GetData(bytecode_array);
CHECK_NOT_NULL(bytecode_array_data);
auto it = bytecode_analyses_.find(bytecode_array_data);
if (it != bytecode_analyses_.end()) {
// Bytecode analysis can be run for OSR or for non-OSR. In the rare case
// where we optimize for OSR and consider the top-level function itself for
// inlining (because of recursion), we need both the OSR and the non-OSR
// analysis. Fortunately, the only difference between the two lies in
// whether the OSR entry offset gets computed (from the OSR bailout id).
// Hence it's okay to reuse the OSR-version when asked for the non-OSR
// version, such that we need to store at most one analysis result per
// bytecode array.
CHECK_IMPLIES(osr_bailout_id != it->second->osr_bailout_id(),
osr_bailout_id.IsNone());
CHECK_EQ(analyze_liveness, it->second->liveness_analyzed());
return *it->second;
}
CHECK(serialize);
BytecodeAnalysis* analysis = new (zone()) BytecodeAnalysis(
bytecode_array, zone(), osr_bailout_id, analyze_liveness);
DCHECK_EQ(analysis->osr_bailout_id(), osr_bailout_id);
bytecode_analyses_[bytecode_array_data] = analysis;
return *analysis;
}
OffHeapBytecodeArray::OffHeapBytecodeArray(BytecodeArrayRef bytecode_array) OffHeapBytecodeArray::OffHeapBytecodeArray(BytecodeArrayRef bytecode_array)
: array_(bytecode_array) {} : array_(bytecode_array) {}
......
...@@ -22,6 +22,7 @@ namespace v8 { ...@@ -22,6 +22,7 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
class BytecodeAnalysis;
class ObjectRef; class ObjectRef;
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref); std::ostream& operator<<(std::ostream& os, const ObjectRef& ref);
...@@ -108,6 +109,10 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -108,6 +109,10 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess( GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess(
FeedbackSource const& source); FeedbackSource const& source);
BytecodeAnalysis const& GetBytecodeAnalysis(
Handle<BytecodeArray> bytecode_array, BailoutId osr_offset,
bool analyze_liveness, bool serialize);
base::Optional<NameRef> GetNameFeedback(FeedbackNexus const& nexus); base::Optional<NameRef> GetNameFeedback(FeedbackNexus const& nexus);
// If there is no result stored for {map}, we return an Invalid // If there is no result stored for {map}, we return an Invalid
...@@ -144,6 +149,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -144,6 +149,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
ZoneUnorderedMap<FeedbackSource, ProcessedFeedback const*, ZoneUnorderedMap<FeedbackSource, ProcessedFeedback const*,
FeedbackSource::Hash, FeedbackSource::Equal> FeedbackSource::Hash, FeedbackSource::Equal>
feedback_; feedback_;
ZoneUnorderedMap<ObjectData*, BytecodeAnalysis*> bytecode_analyses_;
typedef ZoneUnorderedMap<MapRef, PropertyAccessInfo, ObjectRef::Hash, typedef ZoneUnorderedMap<MapRef, PropertyAccessInfo, ObjectRef::Hash,
ObjectRef::Equal> ObjectRef::Equal>
MapToAccessInfos; MapToAccessInfos;
......
...@@ -1359,12 +1359,13 @@ struct SerializationPhase { ...@@ -1359,12 +1359,13 @@ struct SerializationPhase {
if (data->info()->is_source_positions_enabled()) { if (data->info()->is_source_positions_enabled()) {
flags |= SerializerForBackgroundCompilationFlag::kCollectSourcePositions; flags |= SerializerForBackgroundCompilationFlag::kCollectSourcePositions;
} }
if (data->info()->is_osr()) { if (data->info()->is_analyze_environment_liveness()) {
flags |= SerializerForBackgroundCompilationFlag::kOsr; flags |=
SerializerForBackgroundCompilationFlag::kAnalyzeEnvironmentLiveness;
} }
SerializerForBackgroundCompilation serializer( SerializerForBackgroundCompilation serializer(
data->broker(), data->dependencies(), temp_zone, data->broker(), data->dependencies(), temp_zone,
data->info()->closure(), flags); data->info()->closure(), flags, data->info()->osr_offset());
serializer.Run(); serializer.Run();
} }
}; };
......
...@@ -362,14 +362,16 @@ int SerializerForBackgroundCompilation::Environment::RegisterToLocalIndex( ...@@ -362,14 +362,16 @@ int SerializerForBackgroundCompilation::Environment::RegisterToLocalIndex(
SerializerForBackgroundCompilation::SerializerForBackgroundCompilation( SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone, JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone,
Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags) Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags,
BailoutId osr_offset)
: broker_(broker), : broker_(broker),
dependencies_(dependencies), dependencies_(dependencies),
zone_(zone), zone_(zone),
environment_(new (zone) Environment( environment_(new (zone) Environment(
zone, CompilationSubject(closure, broker_->isolate(), zone))), zone, CompilationSubject(closure, broker_->isolate(), zone))),
jump_target_environments_(zone), jump_target_environments_(zone),
flags_(flags) { flags_(flags),
osr_offset_(osr_offset) {
JSFunctionRef(broker, closure).Serialize(); JSFunctionRef(broker, closure).Serialize();
} }
...@@ -383,8 +385,8 @@ SerializerForBackgroundCompilation::SerializerForBackgroundCompilation( ...@@ -383,8 +385,8 @@ SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
environment_(new (zone) Environment(zone, broker_->isolate(), function, environment_(new (zone) Environment(zone, broker_->isolate(), function,
new_target, arguments)), new_target, arguments)),
jump_target_environments_(zone), jump_target_environments_(zone),
flags_(flags) { flags_(flags),
DCHECK(!(flags_ & SerializerForBackgroundCompilationFlag::kOsr)); osr_offset_(BailoutId::None()) {
TraceScope tracer( TraceScope tracer(
broker_, this, broker_, this,
"SerializerForBackgroundCompilation::SerializerForBackgroundCompilation"); "SerializerForBackgroundCompilation::SerializerForBackgroundCompilation");
...@@ -402,7 +404,7 @@ bool SerializerForBackgroundCompilation::BailoutOnUninitialized( ...@@ -402,7 +404,7 @@ bool SerializerForBackgroundCompilation::BailoutOnUninitialized(
SerializerForBackgroundCompilationFlag::kBailoutOnUninitialized)) { SerializerForBackgroundCompilationFlag::kBailoutOnUninitialized)) {
return false; return false;
} }
if (flags() & SerializerForBackgroundCompilationFlag::kOsr) { if (!osr_offset().IsNone()) {
// Exclude OSR from this optimization because we might end up skipping the // Exclude OSR from this optimization because we might end up skipping the
// OSR entry point. TODO(neis): Support OSR? // OSR entry point. TODO(neis): Support OSR?
return false; return false;
...@@ -483,6 +485,11 @@ void SerializerForBackgroundCompilation::TraverseBytecode() { ...@@ -483,6 +485,11 @@ void SerializerForBackgroundCompilation::TraverseBytecode() {
BytecodeArrayRef bytecode_array( BytecodeArrayRef bytecode_array(
broker(), handle(environment()->function().shared()->GetBytecodeArray(), broker(), handle(environment()->function().shared()->GetBytecodeArray(),
broker()->isolate())); broker()->isolate()));
broker()->GetBytecodeAnalysis(
bytecode_array.object(), osr_offset(),
flags() &
SerializerForBackgroundCompilationFlag::kAnalyzeEnvironmentLiveness,
true);
bytecode_array.SerializeForCompilation(); bytecode_array.SerializeForCompilation();
BytecodeArrayIterator iterator(bytecode_array.object()); BytecodeArrayIterator iterator(bytecode_array.object());
ExceptionHandlerMatcher handler_matcher(iterator, bytecode_array.object()); ExceptionHandlerMatcher handler_matcher(iterator, bytecode_array.object());
...@@ -1010,7 +1017,7 @@ Hints SerializerForBackgroundCompilation::RunChildSerializer( ...@@ -1010,7 +1017,7 @@ Hints SerializerForBackgroundCompilation::RunChildSerializer(
SerializerForBackgroundCompilation child_serializer( SerializerForBackgroundCompilation child_serializer(
broker(), dependencies(), zone(), function, new_target, arguments, broker(), dependencies(), zone(), function, new_target, arguments,
flags().without(SerializerForBackgroundCompilationFlag::kOsr)); flags());
return child_serializer.Run(); return child_serializer.Run();
} }
......
...@@ -284,7 +284,7 @@ using HintsVector = ZoneVector<Hints>; ...@@ -284,7 +284,7 @@ using HintsVector = ZoneVector<Hints>;
enum class SerializerForBackgroundCompilationFlag : uint8_t { enum class SerializerForBackgroundCompilationFlag : uint8_t {
kBailoutOnUninitialized = 1 << 0, kBailoutOnUninitialized = 1 << 0,
kCollectSourcePositions = 1 << 1, kCollectSourcePositions = 1 << 1,
kOsr = 1 << 2, kAnalyzeEnvironmentLiveness = 1 << 2,
}; };
using SerializerForBackgroundCompilationFlags = using SerializerForBackgroundCompilationFlags =
base::Flags<SerializerForBackgroundCompilationFlag>; base::Flags<SerializerForBackgroundCompilationFlag>;
...@@ -340,8 +340,8 @@ class SerializerForBackgroundCompilation { ...@@ -340,8 +340,8 @@ class SerializerForBackgroundCompilation {
public: public:
SerializerForBackgroundCompilation( SerializerForBackgroundCompilation(
JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone, JSHeapBroker* broker, CompilationDependencies* dependencies, Zone* zone,
Handle<JSFunction> closure, Handle<JSFunction> closure, SerializerForBackgroundCompilationFlags flags,
SerializerForBackgroundCompilationFlags flags); BailoutId osr_offset);
Hints Run(); // NOTE: Returns empty for an already-serialized function. Hints Run(); // NOTE: Returns empty for an already-serialized function.
class Environment; class Environment;
...@@ -435,6 +435,7 @@ class SerializerForBackgroundCompilation { ...@@ -435,6 +435,7 @@ class SerializerForBackgroundCompilation {
Zone* zone() const { return zone_; } Zone* zone() const { return zone_; }
Environment* environment() const { return environment_; } Environment* environment() const { return environment_; }
SerializerForBackgroundCompilationFlags flags() const { return flags_; } SerializerForBackgroundCompilationFlags flags() const { return flags_; }
BailoutId osr_offset() const { return osr_offset_; }
JSHeapBroker* const broker_; JSHeapBroker* const broker_;
CompilationDependencies* const dependencies_; CompilationDependencies* const dependencies_;
...@@ -442,6 +443,7 @@ class SerializerForBackgroundCompilation { ...@@ -442,6 +443,7 @@ class SerializerForBackgroundCompilation {
Environment* const environment_; Environment* const environment_;
ZoneUnorderedMap<int, Environment*> jump_target_environments_; ZoneUnorderedMap<int, Environment*> jump_target_environments_;
SerializerForBackgroundCompilationFlags const flags_; SerializerForBackgroundCompilationFlags const flags_;
BailoutId const osr_offset_;
}; };
} // namespace compiler } // namespace compiler
......
...@@ -59,8 +59,7 @@ class BytecodeAnalysisTest : public TestWithIsolateAndZone { ...@@ -59,8 +59,7 @@ class BytecodeAnalysisTest : public TestWithIsolateAndZone {
Handle<BytecodeArray> bytecode, Handle<BytecodeArray> bytecode,
const std::vector<std::pair<std::string, std::string>>& const std::vector<std::pair<std::string, std::string>>&
expected_liveness) { expected_liveness) {
BytecodeAnalysis analysis(bytecode, zone(), true); BytecodeAnalysis analysis(bytecode, zone(), BailoutId::None(), true);
analysis.Analyze(BailoutId::None());
interpreter::BytecodeArrayIterator iterator(bytecode); interpreter::BytecodeArrayIterator iterator(bytecode);
for (auto liveness : expected_liveness) { for (auto liveness : expected_liveness) {
......
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