[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:
jarin 2016-08-08 03:01:09 -07:00 committed by Commit bot
parent b79d7b0486
commit c38f1011e8
8 changed files with 245 additions and 6 deletions

View File

@ -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",

View File

@ -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());
}

View File

@ -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_;

View 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

View 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_

View File

@ -861,14 +861,33 @@ void CodeGenerator::AddTranslationForOperand(Translation* translation,
Handle<Object> constant_object;
switch (constant.type()) {
case Constant::kInt32:
DCHECK(type == MachineType::Int32() || type == MachineType::Uint32() ||
type.representation() == MachineRepresentation::kBit ||
type.representation() == MachineRepresentation::kNone);
DCHECK(type.representation() != MachineRepresentation::kNone ||
constant.ToInt32() == FrameStateDescriptor::kImpossibleValue);
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 ||
constant.ToInt32() == FrameStateDescriptor::kImpossibleValue);
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 =
isolate()->factory()->NewNumberFromInt(constant.ToInt32());
handle(reinterpret_cast<Smi*>(constant.ToInt64()), isolate());
DCHECK(constant_object->IsSmi());
break;
case Constant::kFloat32:
DCHECK(type.representation() == MachineRepresentation::kFloat32 ||

View File

@ -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:

View File

@ -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',