[turbofan] Generate loop exits in the bytecode graph builder.
Review-Url: https://codereview.chromium.org/2188533002 Cr-Commit-Position: refs/heads/master@{#38429}
This commit is contained in:
parent
b79d7b0486
commit
c38f1011e8
2
BUILD.gn
2
BUILD.gn
@ -967,6 +967,8 @@ v8_source_set("v8_base") {
|
||||
"src/compiler/bytecode-branch-analysis.h",
|
||||
"src/compiler/bytecode-graph-builder.cc",
|
||||
"src/compiler/bytecode-graph-builder.h",
|
||||
"src/compiler/bytecode-loop-analysis.cc",
|
||||
"src/compiler/bytecode-loop-analysis.h",
|
||||
"src/compiler/c-linkage.cc",
|
||||
"src/compiler/checkpoint-elimination.cc",
|
||||
"src/compiler/checkpoint-elimination.h",
|
||||
|
@ -62,6 +62,8 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
|
||||
void Merge(Environment* other);
|
||||
void PrepareForOsr();
|
||||
|
||||
void PrepareForLoopExit(Node* loop);
|
||||
|
||||
private:
|
||||
explicit Environment(const Environment* copy);
|
||||
void PrepareForLoop();
|
||||
@ -397,6 +399,30 @@ bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
|
||||
return false;
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::Environment::PrepareForLoopExit(Node* loop) {
|
||||
DCHECK_EQ(loop->opcode(), IrOpcode::kLoop);
|
||||
|
||||
Node* control = GetControlDependency();
|
||||
|
||||
// Create the loop exit node.
|
||||
Node* loop_exit = graph()->NewNode(common()->LoopExit(), control, loop);
|
||||
UpdateControlDependency(loop_exit);
|
||||
|
||||
// Rename the effect.
|
||||
Node* effect_rename = graph()->NewNode(common()->LoopExitEffect(),
|
||||
GetEffectDependency(), loop_exit);
|
||||
UpdateEffectDependency(effect_rename);
|
||||
|
||||
// Rename the current context.
|
||||
context_ = graph()->NewNode(common()->LoopExitValue(), context_, loop_exit);
|
||||
|
||||
// Rename the environmnent values.
|
||||
for (size_t i = 0; i < values_.size(); i++) {
|
||||
Node* rename =
|
||||
graph()->NewNode(common()->LoopExitValue(), values_[i], loop_exit);
|
||||
values_[i] = rename;
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values,
|
||||
int offset,
|
||||
@ -562,8 +588,11 @@ bool BytecodeGraphBuilder::CreateGraph() {
|
||||
|
||||
void BytecodeGraphBuilder::VisitBytecodes() {
|
||||
BytecodeBranchAnalysis analysis(bytecode_array(), local_zone());
|
||||
BytecodeLoopAnalysis loop_analysis(bytecode_array(), &analysis, local_zone());
|
||||
analysis.Analyze();
|
||||
loop_analysis.Analyze();
|
||||
set_branch_analysis(&analysis);
|
||||
set_loop_analysis(&loop_analysis);
|
||||
interpreter::BytecodeArrayIterator iterator(bytecode_array());
|
||||
set_bytecode_iterator(&iterator);
|
||||
while (!iterator.done()) {
|
||||
@ -1113,6 +1142,7 @@ void BytecodeGraphBuilder::BuildThrow() {
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitThrow() {
|
||||
BuildLoopExitsForFunctionExit();
|
||||
BuildThrow();
|
||||
Node* call = environment()->LookupAccumulator();
|
||||
Node* control = NewNode(common()->Throw(), call);
|
||||
@ -1120,6 +1150,7 @@ void BytecodeGraphBuilder::VisitThrow() {
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitReThrow() {
|
||||
BuildLoopExitsForFunctionExit();
|
||||
Node* value = environment()->LookupAccumulator();
|
||||
Node* call = NewNode(javascript()->CallRuntime(Runtime::kReThrow), value);
|
||||
Node* control = NewNode(common()->Throw(), call);
|
||||
@ -1432,6 +1463,7 @@ void BytecodeGraphBuilder::VisitOsrPoll() {
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitReturn() {
|
||||
BuildLoopExitsForFunctionExit();
|
||||
Node* control =
|
||||
NewNode(common()->Return(), environment()->LookupAccumulator());
|
||||
MergeControlToLeaveFunction(control);
|
||||
@ -1578,6 +1610,7 @@ void BytecodeGraphBuilder::BuildLoopHeaderEnvironment(int current_offset) {
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::MergeIntoSuccessorEnvironment(int target_offset) {
|
||||
BuildLoopExitsForBranch(target_offset);
|
||||
if (merge_environments_[target_offset] == nullptr) {
|
||||
// Append merge nodes to the environment. We may merge here with another
|
||||
// environment. So add a place holder for merge nodes. We may add redundant
|
||||
@ -1596,6 +1629,28 @@ void BytecodeGraphBuilder::MergeControlToLeaveFunction(Node* exit) {
|
||||
set_environment(nullptr);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::BuildLoopExitsForBranch(int target_offset) {
|
||||
int origin_offset = bytecode_iterator().current_offset();
|
||||
// Only build loop exits for forward edges.
|
||||
if (target_offset > origin_offset) {
|
||||
BuildLoopExitsUntilLoop(loop_analysis()->GetLoopOffsetFor(target_offset));
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::BuildLoopExitsUntilLoop(int loop_offset) {
|
||||
int origin_offset = bytecode_iterator().current_offset();
|
||||
int current_loop = loop_analysis()->GetLoopOffsetFor(origin_offset);
|
||||
while (loop_offset < current_loop) {
|
||||
Node* loop_node = merge_environments_[current_loop]->GetControlDependency();
|
||||
environment()->PrepareForLoopExit(loop_node);
|
||||
current_loop = loop_analysis()->GetParentLoopFor(current_loop);
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::BuildLoopExitsForFunctionExit() {
|
||||
BuildLoopExitsUntilLoop(-1);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::BuildJump() {
|
||||
MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "src/compiler.h"
|
||||
#include "src/compiler/bytecode-branch-analysis.h"
|
||||
#include "src/compiler/bytecode-loop-analysis.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/interpreter/bytecode-array-iterator.h"
|
||||
#include "src/interpreter/bytecode-flags.h"
|
||||
@ -148,6 +149,12 @@ class BytecodeGraphBuilder {
|
||||
// Simulates control flow that exits the function body.
|
||||
void MergeControlToLeaveFunction(Node* exit);
|
||||
|
||||
// Builds loop exit nodes for every exited loop between the current bytecode
|
||||
// offset and {target_offset}.
|
||||
void BuildLoopExitsForBranch(int target_offset);
|
||||
void BuildLoopExitsForFunctionExit();
|
||||
void BuildLoopExitsUntilLoop(int loop_offset);
|
||||
|
||||
// Simulates entry and exit of exception handlers.
|
||||
void EnterAndExitExceptionHandlers(int current_offset);
|
||||
|
||||
@ -203,6 +210,12 @@ class BytecodeGraphBuilder {
|
||||
branch_analysis_ = branch_analysis;
|
||||
}
|
||||
|
||||
const BytecodeLoopAnalysis* loop_analysis() const { return loop_analysis_; }
|
||||
|
||||
void set_loop_analysis(const BytecodeLoopAnalysis* loop_analysis) {
|
||||
loop_analysis_ = loop_analysis;
|
||||
}
|
||||
|
||||
#define DECLARE_VISIT_BYTECODE(name, ...) void Visit##name();
|
||||
BYTECODE_LIST(DECLARE_VISIT_BYTECODE)
|
||||
#undef DECLARE_VISIT_BYTECODE
|
||||
@ -215,6 +228,7 @@ class BytecodeGraphBuilder {
|
||||
const FrameStateFunctionInfo* frame_state_function_info_;
|
||||
const interpreter::BytecodeArrayIterator* bytecode_iterator_;
|
||||
const BytecodeBranchAnalysis* branch_analysis_;
|
||||
const BytecodeLoopAnalysis* loop_analysis_;
|
||||
Environment* environment_;
|
||||
BailoutId osr_ast_id_;
|
||||
|
||||
|
79
src/compiler/bytecode-loop-analysis.cc
Normal file
79
src/compiler/bytecode-loop-analysis.cc
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/compiler/bytecode-loop-analysis.h"
|
||||
|
||||
#include "src/compiler/bytecode-branch-analysis.h"
|
||||
#include "src/interpreter/bytecode-array-iterator.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
BytecodeLoopAnalysis::BytecodeLoopAnalysis(
|
||||
Handle<BytecodeArray> bytecode_array,
|
||||
const BytecodeBranchAnalysis* branch_analysis, Zone* zone)
|
||||
: bytecode_array_(bytecode_array),
|
||||
branch_analysis_(branch_analysis),
|
||||
zone_(zone),
|
||||
backedge_to_header_(zone),
|
||||
loop_header_to_parent_(zone) {}
|
||||
|
||||
void BytecodeLoopAnalysis::Analyze() {
|
||||
current_loop_offset_ = -1;
|
||||
interpreter::BytecodeArrayIterator iterator(bytecode_array());
|
||||
while (!iterator.done()) {
|
||||
interpreter::Bytecode bytecode = iterator.current_bytecode();
|
||||
int current_offset = iterator.current_offset();
|
||||
if (branch_analysis_->backward_branches_target(current_offset)) {
|
||||
AddLoopEntry(current_offset);
|
||||
} else if (interpreter::Bytecodes::IsJump(bytecode)) {
|
||||
AddBranch(current_offset, iterator.GetJumpTargetOffset());
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeLoopAnalysis::AddLoopEntry(int entry_offset) {
|
||||
loop_header_to_parent_[entry_offset] = current_loop_offset_;
|
||||
current_loop_offset_ = entry_offset;
|
||||
}
|
||||
|
||||
void BytecodeLoopAnalysis::AddBranch(int origin_offset, int target_offset) {
|
||||
// If this is a backedge, record it and update the current loop to the parent.
|
||||
if (target_offset < origin_offset) {
|
||||
backedge_to_header_[origin_offset] = target_offset;
|
||||
// Check that we are finishing the current loop. This assumes that
|
||||
// there is one backedge for each loop.
|
||||
DCHECK_EQ(target_offset, current_loop_offset_);
|
||||
current_loop_offset_ = loop_header_to_parent_[target_offset];
|
||||
}
|
||||
}
|
||||
|
||||
int BytecodeLoopAnalysis::GetLoopOffsetFor(int offset) const {
|
||||
auto next_backedge = backedge_to_header_.lower_bound(offset);
|
||||
// If there is no next backedge => offset is not in a loop.
|
||||
if (next_backedge == backedge_to_header_.end()) {
|
||||
return -1;
|
||||
}
|
||||
// If the header preceeds the offset, it is the backedge of the containing
|
||||
// loop.
|
||||
if (next_backedge->second <= offset) {
|
||||
return next_backedge->second;
|
||||
}
|
||||
// Otherwise there is a nested loop after this offset. We just return the
|
||||
// parent of the next nested loop.
|
||||
return loop_header_to_parent_.upper_bound(offset)->second;
|
||||
}
|
||||
|
||||
int BytecodeLoopAnalysis::GetParentLoopFor(int header_offset) const {
|
||||
auto parent = loop_header_to_parent_.find(header_offset);
|
||||
DCHECK(parent != loop_header_to_parent_.end());
|
||||
return parent->second;
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
67
src/compiler/bytecode-loop-analysis.h
Normal file
67
src/compiler/bytecode-loop-analysis.h
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_COMPILER_BYTECODE_LOOP_ANALYSIS_H_
|
||||
#define V8_COMPILER_BYTECODE_LOOP_ANALYSIS_H_
|
||||
|
||||
#include "src/handles.h"
|
||||
#include "src/zone-containers.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class BytecodeArray;
|
||||
|
||||
namespace compiler {
|
||||
|
||||
class BytecodeBranchAnalysis;
|
||||
|
||||
class BytecodeLoopAnalysis BASE_EMBEDDED {
|
||||
public:
|
||||
BytecodeLoopAnalysis(Handle<BytecodeArray> bytecode_array,
|
||||
const BytecodeBranchAnalysis* branch_analysis,
|
||||
Zone* zone);
|
||||
|
||||
// Analyze the bytecodes to find the branch sites and their
|
||||
// targets. No other methods in this class return valid information
|
||||
// until this has been called.
|
||||
void Analyze();
|
||||
|
||||
// Get the loop header offset of the containing loop for arbitrary
|
||||
// {offset}, or -1 if the {offset} is not inside any loop.
|
||||
int GetLoopOffsetFor(int offset) const;
|
||||
// Gets the loop header offset of the parent loop of the loop header
|
||||
// at {header_offset}, or -1 for outer-most loops.
|
||||
int GetParentLoopFor(int header_offset) const;
|
||||
|
||||
private:
|
||||
void AddLoopEntry(int entry_offset);
|
||||
void AddBranch(int origin_offset, int target_offset);
|
||||
|
||||
Zone* zone() const { return zone_; }
|
||||
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
|
||||
|
||||
Handle<BytecodeArray> bytecode_array_;
|
||||
const BytecodeBranchAnalysis* branch_analysis_;
|
||||
Zone* zone_;
|
||||
|
||||
int current_loop_offset_;
|
||||
|
||||
// Map from the offset of a backedge jump to the offset of the corresponding
|
||||
// loop header. Since we assume exactly one backedge per loop, the map will
|
||||
// have as many entries as there are loops.
|
||||
ZoneMap<int, int> backedge_to_header_;
|
||||
// Map from the offset of a loop header to the offset of its parent's loop
|
||||
// header. This map will have as many entries as there are loops in the
|
||||
// function.
|
||||
ZoneMap<int, int> loop_header_to_parent_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BytecodeLoopAnalysis);
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_COMPILER_BYTECODE_LOOP_ANALYSIS_H_
|
@ -861,7 +861,16 @@ void CodeGenerator::AddTranslationForOperand(Translation* translation,
|
||||
Handle<Object> constant_object;
|
||||
switch (constant.type()) {
|
||||
case Constant::kInt32:
|
||||
DCHECK(type == MachineType::Int32() || type == MachineType::Uint32() ||
|
||||
if (type.representation() == MachineRepresentation::kTagged) {
|
||||
// When pointers are 4 bytes, we can use int32 constants to represent
|
||||
// Smis.
|
||||
DCHECK_EQ(4, kPointerSize);
|
||||
constant_object =
|
||||
handle(reinterpret_cast<Smi*>(constant.ToInt32()), isolate());
|
||||
DCHECK(constant_object->IsSmi());
|
||||
} else {
|
||||
DCHECK(type == MachineType::Int32() ||
|
||||
type == MachineType::Uint32() ||
|
||||
type.representation() == MachineRepresentation::kBit ||
|
||||
type.representation() == MachineRepresentation::kNone);
|
||||
DCHECK(type.representation() != MachineRepresentation::kNone ||
|
||||
@ -869,6 +878,16 @@ void CodeGenerator::AddTranslationForOperand(Translation* translation,
|
||||
|
||||
constant_object =
|
||||
isolate()->factory()->NewNumberFromInt(constant.ToInt32());
|
||||
}
|
||||
break;
|
||||
case Constant::kInt64:
|
||||
// When pointers are 8 bytes, we can use int64 constants to represent
|
||||
// Smis.
|
||||
DCHECK_EQ(type.representation(), MachineRepresentation::kTagged);
|
||||
DCHECK_EQ(8, kPointerSize);
|
||||
constant_object =
|
||||
handle(reinterpret_cast<Smi*>(constant.ToInt64()), isolate());
|
||||
DCHECK(constant_object->IsSmi());
|
||||
break;
|
||||
case Constant::kFloat32:
|
||||
DCHECK(type.representation() == MachineRepresentation::kFloat32 ||
|
||||
|
@ -352,6 +352,7 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
|
||||
MachineRepresentation rep) {
|
||||
switch (input->opcode()) {
|
||||
case IrOpcode::kInt32Constant:
|
||||
case IrOpcode::kInt64Constant:
|
||||
case IrOpcode::kNumberConstant:
|
||||
case IrOpcode::kFloat32Constant:
|
||||
case IrOpcode::kFloat64Constant:
|
||||
|
@ -560,6 +560,8 @@
|
||||
'compiler/bytecode-branch-analysis.h',
|
||||
'compiler/bytecode-graph-builder.cc',
|
||||
'compiler/bytecode-graph-builder.h',
|
||||
'compiler/bytecode-loop-analysis.cc',
|
||||
'compiler/bytecode-loop-analysis.h',
|
||||
'compiler/c-linkage.cc',
|
||||
'compiler/checkpoint-elimination.cc',
|
||||
'compiler/checkpoint-elimination.h',
|
||||
|
Loading…
Reference in New Issue
Block a user