Commit d3168202 authored by oth's avatar oth Committed by Commit bot

[Interpreter] Local flow control in the bytecode graph builder.

This change adds support for local control flow when building graphs
from bytecode. The change ensures loop emitted from the bytecode
generator are in natural order so the only back branches are for loops.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#32911}
parent 6540e736
......@@ -696,6 +696,8 @@ source_set("v8_base") {
"src/compiler/basic-block-instrumentor.h",
"src/compiler/branch-elimination.cc",
"src/compiler/branch-elimination.h",
"src/compiler/bytecode-branch-analysis.cc",
"src/compiler/bytecode-branch-analysis.h",
"src/compiler/bytecode-graph-builder.cc",
"src/compiler/bytecode-graph-builder.h",
"src/compiler/change-lowering.cc",
......
// Copyright 2015 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-branch-analysis.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
// The class contains all of the sites that contain
// branches to a particular target (bytecode offset).
class BytecodeBranchInfo final : public ZoneObject {
public:
explicit BytecodeBranchInfo(Zone* zone)
: back_edge_offsets_(zone), fore_edge_offsets_(zone) {}
void AddBranch(int source_offset, int target_offset);
// The offsets of bytecodes that refer to this bytecode as
// a back-edge predecessor.
const ZoneVector<int>* back_edge_offsets() { return &back_edge_offsets_; }
// The offsets of bytecodes that refer to this bytecode as
// a forwards-edge predecessor.
const ZoneVector<int>* fore_edge_offsets() { return &fore_edge_offsets_; }
private:
ZoneVector<int> back_edge_offsets_;
ZoneVector<int> fore_edge_offsets_;
DISALLOW_COPY_AND_ASSIGN(BytecodeBranchInfo);
};
void BytecodeBranchInfo::AddBranch(int source_offset, int target_offset) {
if (source_offset < target_offset) {
fore_edge_offsets_.push_back(source_offset);
} else {
back_edge_offsets_.push_back(source_offset);
}
}
BytecodeBranchAnalysis::BytecodeBranchAnalysis(
Handle<BytecodeArray> bytecode_array, Zone* zone)
: branch_infos_(zone),
bytecode_array_(bytecode_array),
reachable_(bytecode_array->length(), zone),
zone_(zone) {}
void BytecodeBranchAnalysis::Analyze() {
interpreter::BytecodeArrayIterator iterator(bytecode_array());
bool reachable = true;
while (!iterator.done()) {
interpreter::Bytecode bytecode = iterator.current_bytecode();
int current_offset = iterator.current_offset();
// All bytecode basic blocks are generated to be forward reachable
// and may also be backward reachable. Hence if there's a forward
// branch targetting here the code becomes reachable.
reachable = reachable || forward_branches_target(current_offset);
if (reachable) {
reachable_.Add(current_offset);
if (interpreter::Bytecodes::IsConditionalJump(bytecode)) {
// Only the branch is recorded, the forward path falls through
// and is handled as normal bytecode data flow.
AddBranch(current_offset, iterator.GetJumpTargetOffset());
} else if (interpreter::Bytecodes::IsJump(bytecode)) {
// Unless the branch targets the next bytecode it's not
// reachable. If it targets the next bytecode the check at the
// start of the loop will set the reachable flag.
AddBranch(current_offset, iterator.GetJumpTargetOffset());
reachable = false;
} else if (interpreter::Bytecodes::IsJumpOrReturn(bytecode)) {
DCHECK_EQ(bytecode, interpreter::Bytecode::kReturn);
reachable = false;
}
}
iterator.Advance();
}
}
const ZoneVector<int>* BytecodeBranchAnalysis::BackwardBranchesTargetting(
int offset) const {
auto iterator = branch_infos_.find(offset);
if (branch_infos_.end() != iterator) {
return iterator->second->back_edge_offsets();
} else {
return nullptr;
}
}
const ZoneVector<int>* BytecodeBranchAnalysis::ForwardBranchesTargetting(
int offset) const {
auto iterator = branch_infos_.find(offset);
if (branch_infos_.end() != iterator) {
return iterator->second->fore_edge_offsets();
} else {
return nullptr;
}
}
void BytecodeBranchAnalysis::AddBranch(int source_offset, int target_offset) {
BytecodeBranchInfo* branch_info = nullptr;
auto iterator = branch_infos_.find(target_offset);
if (branch_infos_.end() == iterator) {
branch_info = new (zone()) BytecodeBranchInfo(zone());
branch_infos_.insert(std::make_pair(target_offset, branch_info));
} else {
branch_info = iterator->second;
}
branch_info->AddBranch(source_offset, target_offset);
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2015 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_BRANCH_ANALYSIS_H_
#define V8_COMPILER_BYTECODE_BRANCH_ANALYSIS_H_
#include "src/bit-vector.h"
#include "src/handles.h"
#include "src/zone-containers.h"
namespace v8 {
namespace internal {
class BytecodeArray;
namespace compiler {
class BytecodeBranchInfo;
// A class for identifying the branch targets and their branch sites
// within a bytecode array and also identifying which bytecodes are
// reachable. This information can be used to construct the local
// control flow logic for high-level IR graphs built from bytecode.
//
// NB This class relies on the only backwards branches in bytecode
// being jumps back to loop headers.
class BytecodeBranchAnalysis BASE_EMBEDDED {
public:
BytecodeBranchAnalysis(Handle<BytecodeArray> bytecode_array, 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();
// Offsets of bytecodes having a backward branch to the bytecode at |offset|.
const ZoneVector<int>* BackwardBranchesTargetting(int offset) const;
// Offsets of bytecodes having a forward branch to the bytecode at |offset|.
const ZoneVector<int>* ForwardBranchesTargetting(int offset) const;
// Returns true if the bytecode at |offset| is reachable.
bool is_reachable(int offset) const { return reachable_.Contains(offset); }
// Returns true if there are any forward branches to the bytecode at
// |offset|.
bool forward_branches_target(int offset) const {
const ZoneVector<int>* sites = ForwardBranchesTargetting(offset);
return sites != nullptr && sites->size() > 0;
}
// Returns true if there are any backward branches to the bytecode
// at |offset|.
bool backward_branches_target(int offset) const {
const ZoneVector<int>* sites = BackwardBranchesTargetting(offset);
return sites != nullptr && sites->size() > 0;
}
private:
void AddBranch(int origin_offset, int target_offset);
Zone* zone() const { return zone_; }
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
ZoneMap<int, BytecodeBranchInfo*> branch_infos_;
Handle<BytecodeArray> bytecode_array_;
BitVector reachable_;
Zone* zone_;
DISALLOW_COPY_AND_ASSIGN(BytecodeBranchAnalysis);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_BYTECODE_BRANCH_ANALYSIS_H_
This diff is collapsed.
......@@ -6,6 +6,7 @@
#define V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_
#include "src/compiler.h"
#include "src/compiler/bytecode-branch-analysis.h"
#include "src/compiler/js-graph.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecodes.h"
......@@ -89,15 +90,34 @@ class BytecodeGraphBuilder {
return MakeNode(op, arraysize(buffer), buffer, false);
}
Node* MakeNode(const Operator* op, int value_input_count, Node** value_inputs,
bool incomplete);
// Helpers to create new control nodes.
Node* NewIfTrue() { return NewNode(common()->IfTrue()); }
Node* NewIfFalse() { return NewNode(common()->IfFalse()); }
Node* NewMerge() { return NewNode(common()->Merge(1), true); }
Node* NewLoop() { return NewNode(common()->Loop(1), true); }
Node* NewBranch(Node* condition, BranchHint hint = BranchHint::kNone) {
return NewNode(common()->Branch(hint), condition);
}
// Creates a new Phi node having {count} input values.
Node* NewPhi(int count, Node* input, Node* control);
Node* NewEffectPhi(int count, Node* input, Node* control);
// Helpers for merging control, effect or value dependencies.
Node* MergeControl(Node* control, Node* other);
Node* MergeEffect(Node* effect, Node* other_effect, Node* control);
Node* MergeValue(Node* value, Node* other_value, Node* control);
Node** EnsureInputBufferSize(int size);
// The main node creation chokepoint. Adds context, frame state, effect,
// and control dependencies depending on the operator.
Node* MakeNode(const Operator* op, int value_input_count, Node** value_inputs,
bool incomplete);
// Helper to indicate a node exits the function body.
void UpdateControlDependencyToLeaveFunction(Node* exit);
Node** EnsureInputBufferSize(int size);
Node* ProcessCallArguments(const Operator* call_op, Node* callee,
interpreter::Register receiver, size_t arity);
Node* ProcessCallNewArguments(const Operator* call_new_op,
......@@ -130,6 +150,21 @@ class BytecodeGraphBuilder {
void BuildCastOperator(const Operator* js_op,
const interpreter::BytecodeArrayIterator& iterator);
// Control flow plumbing.
void BuildJump(int source_offset, int target_offset);
void BuildJump();
void BuildConditionalJump(Node* condition);
// Helpers for building conditions for conditional jumps.
Node* BuildCondition(Node* comperand);
Node* BuildToBooleanCondition(Node* comperand);
// Constructing merge and loop headers.
void MergeEnvironmentsOfBackwardBranches(int source_offset,
int target_offset);
void MergeEnvironmentsOfForwardBranches(int source_offset);
void BuildLoopHeaderForBackwardBranches(int source_offset);
// Growth increment for the temporary buffer used to construct input lists to
// new nodes.
static const int kInputBufferSizeIncrement = 64;
......@@ -150,6 +185,23 @@ class BytecodeGraphBuilder {
return info()->language_mode();
}
const interpreter::BytecodeArrayIterator* bytecode_iterator() const {
return bytecode_iterator_;
}
void set_bytecode_iterator(
const interpreter::BytecodeArrayIterator* bytecode_iterator) {
bytecode_iterator_ = bytecode_iterator;
}
const BytecodeBranchAnalysis* branch_analysis() const {
return branch_analysis_;
}
void set_branch_analysis(const BytecodeBranchAnalysis* branch_analysis) {
branch_analysis_ = branch_analysis;
}
#define DECLARE_VISIT_BYTECODE(name, ...) \
void Visit##name(const interpreter::BytecodeArrayIterator& iterator);
BYTECODE_LIST(DECLARE_VISIT_BYTECODE)
......@@ -159,8 +211,18 @@ class BytecodeGraphBuilder {
CompilationInfo* info_;
JSGraph* jsgraph_;
Handle<BytecodeArray> bytecode_array_;
const interpreter::BytecodeArrayIterator* bytecode_iterator_;
const BytecodeBranchAnalysis* branch_analysis_;
Environment* environment_;
// Merge environments are snapshots of the environment at a particular
// bytecode offset to be merged into a later environment.
ZoneMap<int, Environment*> merge_environments_;
// Loop header environments are environments created for bytecodes
// where it is known there are back branches, ie a loop header.
ZoneMap<int, Environment*> loop_header_environments_;
// Temporary storage for building node input lists.
int input_buffer_size_;
Node** input_buffer_;
......@@ -212,9 +274,15 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
Node* Context() const { return context_; }
void SetContext(Node* new_context) { context_ = new_context; }
Environment* CopyForConditional() const;
Environment* CopyForLoop();
void Merge(Environment* other);
private:
int RegisterToValuesIndex(interpreter::Register the_register) const;
explicit Environment(const Environment* copy);
void PrepareForLoop();
int RegisterToValuesIndex(interpreter::Register the_register) const;
Zone* zone() const { return builder_->local_zone(); }
Graph* graph() const { return builder_->graph(); }
CommonOperatorBuilder* common() const { return builder_->common(); }
......@@ -235,7 +303,6 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
int register_base_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -304,22 +304,24 @@ class BytecodeLabel final {
public:
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
INLINE(bool is_bound() const) { return bound_; }
bool is_bound() const { return bound_; }
size_t offset() const { return offset_; }
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
INLINE(void bind_to(size_t offset)) {
void bind_to(size_t offset) {
DCHECK(!bound_ && offset != kInvalidOffset);
offset_ = offset;
bound_ = true;
}
INLINE(void set_referrer(size_t offset)) {
void set_referrer(size_t offset) {
DCHECK(!bound_ && offset != kInvalidOffset && offset_ == kInvalidOffset);
offset_ = offset;
}
INLINE(size_t offset() const) { return offset_; }
INLINE(bool is_forward_target() const) {
bool is_forward_target() const {
return offset() != kInvalidOffset && !is_bound();
}
......
......@@ -95,6 +95,21 @@ Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
return FixedArray::get(constants, GetIndexOperand(operand_index));
}
int BytecodeArrayIterator::GetJumpTargetOffset() const {
Bytecode bytecode = current_bytecode();
if (interpreter::Bytecodes::IsJumpImmediate(bytecode)) {
int relative_offset = GetImmediateOperand(0);
return current_offset() + relative_offset;
} else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
Smi* smi = Smi::cast(*GetConstantForIndexOperand(0));
return current_offset() + smi->value();
} else {
UNREACHABLE();
return kMinInt;
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -35,6 +35,11 @@ class BytecodeArrayIterator {
// typed versions above which cast the return to an appropriate type.
uint32_t GetRawOperand(int operand_index, OperandType operand_type) const;
// Returns the absolute offset of the branch target at the current
// bytecode. It is an error to call this method if the bytecode is
// not for a jump or conditional jump.
int GetJumpTargetOffset() const;
private:
Handle<BytecodeArray> bytecode_array_;
int bytecode_offset_;
......
......@@ -628,14 +628,17 @@ void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
BytecodeLabel else_label, end_label;
if (stmt->condition()->ToBooleanIsTrue()) {
// Generate only then block.
// Generate then block unconditionally as always true.
Visit(stmt->then_statement());
} else if (stmt->condition()->ToBooleanIsFalse()) {
// Generate only else block if it exists.
// Generate else block unconditionally if it exists.
if (stmt->HasElseStatement()) {
Visit(stmt->else_statement());
}
} else {
// TODO(oth): If then statement is BreakStatement or
// ContinueStatement we can reduce number of generated
// jump/jump_ifs here. See BasicLoops test.
VisitForAccumulatorValue(stmt->condition());
builder()->JumpIfFalse(&else_label);
Visit(stmt->then_statement());
......@@ -736,95 +739,70 @@ void BytecodeGenerator::VisitCaseClause(CaseClause* clause) {
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
LoopBuilder loop_builder(builder());
ControlScopeForIteration execution_control(this, stmt, &loop_builder);
BytecodeLabel body_label, condition_label, done_label;
loop_builder.LoopHeader();
if (stmt->cond()->ToBooleanIsFalse()) {
Visit(stmt->body());
// Bind condition_label and done_label for processing continue and break.
builder()->Bind(&condition_label);
builder()->Bind(&done_label);
loop_builder.Condition();
} else if (stmt->cond()->ToBooleanIsTrue()) {
loop_builder.Condition();
Visit(stmt->body());
loop_builder.JumpToHeader();
} else {
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&condition_label);
if (stmt->cond()->ToBooleanIsTrue()) {
builder()->Jump(&body_label);
} else {
VisitForAccumulatorValue(stmt->cond());
builder()->JumpIfTrue(&body_label);
}
builder()->Bind(&done_label);
loop_builder.Condition();
VisitForAccumulatorValue(stmt->cond());
loop_builder.JumpToHeaderIfTrue();
}
loop_builder.SetBreakTarget(done_label);
loop_builder.SetContinueTarget(condition_label);
loop_builder.LoopEnd();
}
void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
LoopBuilder loop_builder(builder());
ControlScopeForIteration execution_control(this, stmt, &loop_builder);
BytecodeLabel body_label, condition_label, done_label;
if (stmt->cond()->ToBooleanIsFalse()) {
// If the condition is false there is no need to generate the loop.
return;
}
LoopBuilder loop_builder(builder());
ControlScopeForIteration execution_control(this, stmt, &loop_builder);
loop_builder.LoopHeader();
loop_builder.Condition();
if (!stmt->cond()->ToBooleanIsTrue()) {
builder()->Jump(&condition_label);
}
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&condition_label);
if (stmt->cond()->ToBooleanIsTrue()) {
builder()->Jump(&body_label);
} else {
VisitForAccumulatorValue(stmt->cond());
builder()->JumpIfTrue(&body_label);
loop_builder.BreakIfFalse();
}
builder()->Bind(&done_label);
loop_builder.SetBreakTarget(done_label);
loop_builder.SetContinueTarget(condition_label);
Visit(stmt->body());
loop_builder.JumpToHeader();
loop_builder.LoopEnd();
}
void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
LoopBuilder loop_builder(builder());
ControlScopeForIteration execution_control(this, stmt, &loop_builder);
if (stmt->init() != nullptr) {
Visit(stmt->init());
}
if (stmt->cond() && stmt->cond()->ToBooleanIsFalse()) {
// If the condition is known to be false there is no need to generate
// body, next or condition blocks. Init block should be generated.
return;
}
BytecodeLabel body_label, condition_label, next_label, done_label;
LoopBuilder loop_builder(builder());
ControlScopeForIteration execution_control(this, stmt, &loop_builder);
loop_builder.LoopHeader();
loop_builder.Condition();
if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) {
builder()->Jump(&condition_label);
VisitForAccumulatorValue(stmt->cond());
loop_builder.BreakIfFalse();
}
builder()->Bind(&body_label);
Visit(stmt->body());
builder()->Bind(&next_label);
if (stmt->next() != nullptr) {
loop_builder.Next();
Visit(stmt->next());
}
if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) {
builder()->Bind(&condition_label);
VisitForAccumulatorValue(stmt->cond());
builder()->JumpIfTrue(&body_label);
} else {
builder()->Jump(&body_label);
}
builder()->Bind(&done_label);
loop_builder.SetBreakTarget(done_label);
loop_builder.SetContinueTarget(next_label);
loop_builder.JumpToHeader();
loop_builder.LoopEnd();
}
......@@ -898,42 +876,25 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
Register for_in_state = execution_result()->NewRegister();
builder()->StoreAccumulatorInRegister(for_in_state);
// The loop.
BytecodeLabel condition_label, break_label, continue_label;
// Check loop termination (accumulator holds index).
Register index = receiver; // Re-using register as receiver no longer used.
builder()->LoadLiteral(Smi::FromInt(0));
// Check loop termination (accumulator holds index).
builder()
->Bind(&condition_label)
.StoreAccumulatorInRegister(index)
.ForInDone(for_in_state);
loop_builder.LoopHeader();
loop_builder.Condition();
builder()->StoreAccumulatorInRegister(index).ForInDone(for_in_state);
loop_builder.BreakIfTrue();
// Get the next item.
builder()->ForInNext(for_in_state, index);
// Start again if the item, currently in the accumulator, is undefined.
loop_builder.ContinueIfUndefined();
// Store the value in the each variable.
VisitForInAssignment(stmt->each(), stmt->EachFeedbackSlot());
// NB the user's loop variable will be assigned the value of each so
// even an empty body will have this assignment.
Visit(stmt->body());
// Increment the index and start loop again.
builder()
->Bind(&continue_label)
.LoadAccumulatorWithRegister(index)
.CountOperation(Token::Value::ADD, language_mode_strength())
.Jump(&condition_label);
// End of the loop.
builder()->Bind(&break_label);
loop_builder.SetBreakTarget(break_label);
loop_builder.SetContinueTarget(continue_label);
// TODO(oth): replace CountOperation here with ForInStep.
loop_builder.Next();
builder()->LoadAccumulatorWithRegister(index).CountOperation(
Token::Value::ADD, language_mode_strength());
loop_builder.JumpToHeader();
loop_builder.LoopEnd();
}
......
......@@ -159,8 +159,8 @@ OperandSize Bytecodes::SizeOfOperand(OperandType operand_type) {
// static
bool Bytecodes::IsJump(Bytecode bytecode) {
return bytecode == Bytecode::kJump || bytecode == Bytecode::kJumpIfTrue ||
bool Bytecodes::IsConditionalJumpImmediate(Bytecode bytecode) {
return bytecode == Bytecode::kJumpIfTrue ||
bytecode == Bytecode::kJumpIfFalse ||
bytecode == Bytecode::kJumpIfToBooleanTrue ||
bytecode == Bytecode::kJumpIfToBooleanFalse ||
......@@ -170,17 +170,48 @@ bool Bytecodes::IsJump(Bytecode bytecode) {
// static
bool Bytecodes::IsJumpConstant(Bytecode bytecode) {
return bytecode == Bytecode::kJumpConstant ||
bytecode == Bytecode::kJumpIfTrueConstant ||
bool Bytecodes::IsConditionalJumpConstant(Bytecode bytecode) {
return bytecode == Bytecode::kJumpIfTrueConstant ||
bytecode == Bytecode::kJumpIfFalseConstant ||
bytecode == Bytecode::kJumpIfToBooleanTrueConstant ||
bytecode == Bytecode::kJumpIfToBooleanFalseConstant ||
bytecode == Bytecode::kJumpIfNull ||
bytecode == Bytecode::kJumpIfNullConstant ||
bytecode == Bytecode::kJumpIfUndefinedConstant;
}
// static
bool Bytecodes::IsConditionalJump(Bytecode bytecode) {
return IsConditionalJumpImmediate(bytecode) ||
IsConditionalJumpConstant(bytecode);
}
// static
bool Bytecodes::IsJumpImmediate(Bytecode bytecode) {
return bytecode == Bytecode::kJump || IsConditionalJumpImmediate(bytecode);
}
// static
bool Bytecodes::IsJumpConstant(Bytecode bytecode) {
return bytecode == Bytecode::kJumpConstant ||
IsConditionalJumpConstant(bytecode);
}
// static
bool Bytecodes::IsJump(Bytecode bytecode) {
return IsJumpImmediate(bytecode) || IsJumpConstant(bytecode);
}
// static
bool Bytecodes::IsJumpOrReturn(Bytecode bytecode) {
return bytecode == Bytecode::kReturn || IsJump(bytecode);
}
// static
std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
int parameter_count) {
......
......@@ -340,14 +340,33 @@ class Bytecodes {
// Returns the size of |operand|.
static OperandSize SizeOfOperand(OperandType operand);
// Return true if the bytecode is a conditional jump taking
// an immediate byte operand (OperandType::kImm8).
static bool IsConditionalJumpImmediate(Bytecode bytecode);
// Return true if the bytecode is a conditional jump taking
// a constant pool entry (OperandType::kIdx).
static bool IsConditionalJumpConstant(Bytecode bytecode);
// Return true if the bytecode is a conditional jump taking
// any kind of operand.
static bool IsConditionalJump(Bytecode bytecode);
// Return true if the bytecode is a jump or a conditional jump taking
// an immediate byte operand (OperandType::kImm8).
static bool IsJump(Bytecode bytecode);
static bool IsJumpImmediate(Bytecode bytecode);
// Return true if the bytecode is a jump or conditional jump taking a
// constant pool entry (OperandType::kIdx).
static bool IsJumpConstant(Bytecode bytecode);
// Return true if the bytecode is a jump or conditional jump taking
// any kind of operand.
static bool IsJump(Bytecode bytecode);
// Return true if the bytecode is a conditional jump, a jump, or a return.
static bool IsJumpOrReturn(Bytecode bytecode);
// Decode a single bytecode and operands to |os|.
static std::ostream& Decode(std::ostream& os, const uint8_t* bytecode_start,
int number_of_parameters);
......
......@@ -32,6 +32,13 @@ void BreakableControlFlowBuilder::EmitJumpIfTrue(
}
void BreakableControlFlowBuilder::EmitJumpIfFalse(
ZoneVector<BytecodeLabel>* sites) {
sites->push_back(BytecodeLabel());
builder()->JumpIfFalse(&sites->back());
}
void BreakableControlFlowBuilder::EmitJumpIfUndefined(
ZoneVector<BytecodeLabel>* sites) {
sites->push_back(BytecodeLabel());
......@@ -58,6 +65,12 @@ void BreakableControlFlowBuilder::EmitJumpIfTrue(
}
void BreakableControlFlowBuilder::EmitJumpIfFalse(
ZoneVector<BytecodeLabel>* sites, int index) {
builder()->JumpIfFalse(&sites->at(index));
}
void BreakableControlFlowBuilder::BindLabels(const BytecodeLabel& target,
ZoneVector<BytecodeLabel>* sites) {
for (size_t i = 0; i < sites->size(); i++) {
......@@ -71,6 +84,24 @@ void BreakableControlFlowBuilder::BindLabels(const BytecodeLabel& target,
LoopBuilder::~LoopBuilder() { DCHECK(continue_sites_.empty()); }
void LoopBuilder::LoopEnd() {
// Loop must have closed form, i.e. all loop elements are within the loop,
// the loop header precedes the body and next elements in the loop.
DCHECK(loop_header_.is_bound());
builder()->Bind(&loop_end_);
SetBreakTarget(loop_end_);
if (next_.is_bound()) {
DCHECK(!condition_.is_bound() || next_.offset() >= condition_.offset());
SetContinueTarget(next_);
} else {
DCHECK(condition_.is_bound());
DCHECK_GE(condition_.offset(), loop_header_.offset());
DCHECK_LE(condition_.offset(), loop_end_.offset());
SetContinueTarget(condition_);
}
}
void LoopBuilder::SetContinueTarget(const BytecodeLabel& target) {
BindLabels(target, &continue_sites_);
}
......
......@@ -44,6 +44,7 @@ class BreakableControlFlowBuilder : public ControlFlowBuilder {
// SetBreakTarget is called.
void Break() { EmitJump(&break_sites_); }
void BreakIfTrue() { EmitJumpIfTrue(&break_sites_); }
void BreakIfFalse() { EmitJumpIfFalse(&break_sites_); }
void BreakIfUndefined() { EmitJumpIfUndefined(&break_sites_); }
void BreakIfNull() { EmitJumpIfNull(&break_sites_); }
......@@ -52,6 +53,8 @@ class BreakableControlFlowBuilder : public ControlFlowBuilder {
void EmitJump(ZoneVector<BytecodeLabel>* labels, int index);
void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels);
void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels, int index);
void EmitJumpIfFalse(ZoneVector<BytecodeLabel>* labels);
void EmitJumpIfFalse(ZoneVector<BytecodeLabel>* labels, int index);
void EmitJumpIfUndefined(ZoneVector<BytecodeLabel>* labels);
void EmitJumpIfNull(ZoneVector<BytecodeLabel>* labels);
......@@ -64,7 +67,6 @@ class BreakableControlFlowBuilder : public ControlFlowBuilder {
// A class to help with co-ordinating break and continue statements with
// their loop.
// TODO(oth): add support for TF branch/merge info.
class LoopBuilder final : public BreakableControlFlowBuilder {
public:
explicit LoopBuilder(BytecodeArrayBuilder* builder)
......@@ -72,9 +74,12 @@ class LoopBuilder final : public BreakableControlFlowBuilder {
continue_sites_(builder->zone()) {}
~LoopBuilder();
// This methods should be called by the LoopBuilder owner before
// destruction to update sites that emit jumps for continue.
void SetContinueTarget(const BytecodeLabel& continue_target);
void LoopHeader() { builder()->Bind(&loop_header_); }
void Condition() { builder()->Bind(&condition_); }
void Next() { builder()->Bind(&next_); }
void JumpToHeader() { builder()->Jump(&loop_header_); }
void JumpToHeaderIfTrue() { builder()->JumpIfTrue(&loop_header_); }
void LoopEnd();
// This method is called when visiting continue statements in the AST.
// Inserts a jump to a unbound label that is patched when the corresponding
......@@ -85,6 +90,13 @@ class LoopBuilder final : public BreakableControlFlowBuilder {
void ContinueIfNull() { EmitJumpIfNull(&continue_sites_); }
private:
void SetContinueTarget(const BytecodeLabel& continue_target);
BytecodeLabel loop_header_;
BytecodeLabel condition_;
BytecodeLabel next_;
BytecodeLabel loop_end_;
// Unbound labels that identify jumps for continue statements in the code.
ZoneVector<BytecodeLabel> continue_sites_;
};
......
......@@ -474,6 +474,8 @@
'../../src/compiler/basic-block-instrumentor.h',
'../../src/compiler/branch-elimination.cc',
'../../src/compiler/branch-elimination.h',
'../../src/compiler/bytecode-branch-analysis.cc',
'../../src/compiler/bytecode-branch-analysis.h',
'../../src/compiler/bytecode-graph-builder.cc',
'../../src/compiler/bytecode-graph-builder.h',
'../../src/compiler/change-lowering.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