Commit 76368d08 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add a simple dead-code elimination bytecode optimizer.

Adds back simple dead code elimination to the bytecode pipeline.

BUG=v8:4280,chromium:616064

Review-Url: https://codereview.chromium.org/2038083002
Cr-Commit-Position: refs/heads/master@{#37147}
parent f5679307
......@@ -1222,6 +1222,8 @@ v8_source_set("v8_base") {
"src/interpreter/bytecode-array-iterator.h",
"src/interpreter/bytecode-array-writer.cc",
"src/interpreter/bytecode-array-writer.h",
"src/interpreter/bytecode-dead-code-optimizer.cc",
"src/interpreter/bytecode-dead-code-optimizer.h",
"src/interpreter/bytecode-generator.cc",
"src/interpreter/bytecode-generator.h",
"src/interpreter/bytecode-label.h",
......
......@@ -306,6 +306,8 @@ DEFINE_BOOL(ignition_eager, false, "eagerly compile and parse with ignition")
DEFINE_BOOL(ignition_generators, true,
"enable experimental ignition support for generators")
DEFINE_STRING(ignition_filter, "*", "filter for ignition interpreter")
DEFINE_BOOL(ignition_deadcode, true,
"use ignition dead code elimination optimizer")
DEFINE_BOOL(ignition_peephole, true, "use ignition peephole optimizer")
DEFINE_BOOL(ignition_reo, true, "use ignition register equivalence optimizer")
DEFINE_BOOL(ignition_filter_expression_positions, true,
......
......@@ -6,6 +6,7 @@
#include "src/compiler.h"
#include "src/interpreter/bytecode-array-writer.h"
#include "src/interpreter/bytecode-dead-code-optimizer.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-peephole-optimizer.h"
#include "src/interpreter/bytecode-register-optimizer.h"
......@@ -35,6 +36,10 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone,
DCHECK_GE(context_register_count_, 0);
DCHECK_GE(local_register_count_, 0);
if (FLAG_ignition_deadcode) {
pipeline_ = new (zone) BytecodeDeadCodeOptimizer(pipeline_);
}
if (FLAG_ignition_peephole) {
pipeline_ = new (zone)
BytecodePeepholeOptimizer(&constant_array_builder_, pipeline_);
......
// 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/interpreter/bytecode-dead-code-optimizer.h"
namespace v8 {
namespace internal {
namespace interpreter {
BytecodeDeadCodeOptimizer::BytecodeDeadCodeOptimizer(
BytecodePipelineStage* next_stage)
: next_stage_(next_stage), exit_seen_in_block_(false) {}
// override
Handle<BytecodeArray> BytecodeDeadCodeOptimizer::ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) {
return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count,
handler_table);
}
// override
void BytecodeDeadCodeOptimizer::Write(BytecodeNode* node) {
// Don't emit dead code.
if (exit_seen_in_block_) return;
switch (node->bytecode()) {
case Bytecode::kReturn:
case Bytecode::kThrow:
case Bytecode::kReThrow:
exit_seen_in_block_ = true;
break;
default:
break;
}
next_stage_->Write(node);
}
// override
void BytecodeDeadCodeOptimizer::WriteJump(BytecodeNode* node,
BytecodeLabel* label) {
// Don't emit dead code.
// TODO(rmcilroy): For forward jumps we could mark the label as dead, thereby
// avoiding emitting dead code when we bind the label.
if (exit_seen_in_block_) return;
switch (node->bytecode()) {
case Bytecode::kJump:
case Bytecode::kJumpConstant:
exit_seen_in_block_ = true;
break;
default:
break;
}
next_stage_->WriteJump(node, label);
}
// override
void BytecodeDeadCodeOptimizer::BindLabel(BytecodeLabel* label) {
next_stage_->BindLabel(label);
exit_seen_in_block_ = false;
}
// override
void BytecodeDeadCodeOptimizer::BindLabel(const BytecodeLabel& target,
BytecodeLabel* label) {
next_stage_->BindLabel(target, label);
// exit_seen_in_block_ was reset when target was bound, so shouldn't be
// changed here.
}
} // namespace interpreter
} // namespace internal
} // namespace v8
// 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_INTERPRETER_BYTECODE_DEAD_CODE_OPTIMIZER_H_
#define V8_INTERPRETER_BYTECODE_DEAD_CODE_OPTIMIZER_H_
#include "src/interpreter/bytecode-pipeline.h"
namespace v8 {
namespace internal {
namespace interpreter {
// An optimization stage for eliminating obviously dead code in bytecode
// generation.
class BytecodeDeadCodeOptimizer final : public BytecodePipelineStage,
public ZoneObject {
public:
explicit BytecodeDeadCodeOptimizer(BytecodePipelineStage* next_stage);
// BytecodePipelineStage interface.
void Write(BytecodeNode* node) override;
void WriteJump(BytecodeNode* node, BytecodeLabel* label) override;
void BindLabel(BytecodeLabel* label) override;
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override;
Handle<BytecodeArray> ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handler_table) override;
private:
BytecodePipelineStage* next_stage_;
bool exit_seen_in_block_;
DISALLOW_COPY_AND_ASSIGN(BytecodeDeadCodeOptimizer);
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_DEAD_CODE_OPTIMIZER_H_
......@@ -898,7 +898,11 @@
'interpreter/bytecode-array-iterator.h',
'interpreter/bytecode-array-writer.cc',
'interpreter/bytecode-array-writer.h',
'interpreter/bytecode-dead-code-optimizer.cc',
'interpreter/bytecode-dead-code-optimizer.h',
'interpreter/bytecode-label.h',
'interpreter/bytecode-generator.cc',
'interpreter/bytecode-generator.h',
'interpreter/bytecode-peephole-optimizer.cc',
'interpreter/bytecode-peephole-optimizer.h',
'interpreter/bytecode-pipeline.cc',
......@@ -907,8 +911,6 @@
'interpreter/bytecode-register-allocator.h',
'interpreter/bytecode-register-optimizer.cc',
'interpreter/bytecode-register-optimizer.h',
'interpreter/bytecode-generator.cc',
'interpreter/bytecode-generator.h',
'interpreter/bytecode-traits.h',
'interpreter/constant-array-builder.cc',
'interpreter/constant-array-builder.h',
......
......@@ -105,7 +105,7 @@ snippet: "
"
frame size: 4
parameter count: 1
bytecode array length: 48
bytecode array length: 46
bytecodes: [
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), U8(1),
B(PushContext), R(0),
......@@ -126,7 +126,6 @@ bytecodes: [
/* 72 S> */ B(CreateClosure), U8(1), U8(0),
B(PopContext), R(0),
/* 104 S> */ B(Return),
B(PopContext), R(1),
]
constant pool: [
InstanceType::FIXED_ARRAY_TYPE,
......
......@@ -54,7 +54,7 @@ snippet: "
"
frame size: 2
parameter count: 1
bytecode array length: 24
bytecode array length: 17
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 34 E> */ B(StackCheck),
......@@ -63,11 +63,8 @@ bytecodes: [
/* 69 S> */ B(Inc),
B(Star), R(1),
B(Star), R(0),
/* 74 S> */ B(Jump), U8(11),
B(Mov), R(0), R(1),
/* 84 S> */ B(LdaSmi), U8(20),
B(Star), R(1),
B(Jump), U8(-19),
/* 74 S> */ B(Jump), U8(4),
B(Jump), U8(-12),
B(LdaUndefined),
/* 94 S> */ B(Return),
]
......
......@@ -150,15 +150,14 @@ snippet: "
"
frame size: 0
parameter count: 2
bytecode array length: 20
bytecode array length: 18
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 18 S> */ B(LdaZero),
/* 24 E> */ B(TestLessThanOrEqual), R(arg0),
B(JumpIfFalse), U8(9),
B(JumpIfFalse), U8(7),
/* 36 S> */ B(Wide), B(LdaSmi), U16(200),
/* 80 S> */ B(Return),
B(Jump), U8(7),
/* 63 S> */ B(Wide), B(LdaSmi), U16(-200),
/* 80 S> */ B(Return),
B(LdaUndefined),
......@@ -262,7 +261,7 @@ snippet: "
"
frame size: 2
parameter count: 2
bytecode array length: 411
bytecode array length: 409
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 24 S> */ B(LdaZero),
......@@ -402,7 +401,6 @@ bytecodes: [
/* 1073 S> */ B(Mov), R(1), R(0),
/* 1081 S> */ B(Wide), B(LdaSmi), U16(200),
/* 1117 S> */ B(Return),
B(Jump), U8(7),
/* 1102 S> */ B(Wide), B(LdaSmi), U16(-200),
/* 1117 S> */ B(Return),
B(LdaUndefined),
......@@ -490,7 +488,7 @@ snippet: "
"
frame size: 2
parameter count: 1
bytecode array length: 409
bytecode array length: 407
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 25 S> */ B(LdaZero),
......@@ -629,7 +627,6 @@ bytecodes: [
/* 1067 S> */ B(Mov), R(1), R(0),
/* 1076 S> */ B(Wide), B(LdaSmi), U16(200),
/* 1112 S> */ B(Return),
B(Jump), U8(7),
/* 1097 S> */ B(Wide), B(LdaSmi), U16(-200),
/* 1112 S> */ B(Return),
B(LdaUndefined),
......@@ -723,15 +720,14 @@ snippet: "
"
frame size: 1
parameter count: 1
bytecode array length: 16
bytecode array length: 14
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 25 S> */ B(LdaZero),
B(Star), R(0),
/* 30 S> */ B(JumpIfToBooleanFalse), U8(7),
/* 30 S> */ B(JumpIfToBooleanFalse), U8(5),
/* 43 S> */ B(LdaSmi), U8(20),
/* 85 S> */ B(Return),
B(Jump), U8(5),
/* 69 S> */ B(LdaSmi), U8(-20),
/* 85 S> */ B(Return),
B(LdaUndefined),
......
......@@ -13,13 +13,11 @@ snippet: "
"
frame size: 0
parameter count: 1
bytecode array length: 6
bytecode array length: 4
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 34 S> */ B(LdaSmi), U8(1),
/* 34 E> */ B(Throw),
B(LdaUndefined),
/* 43 S> */ B(Return),
]
constant pool: [
]
......@@ -32,13 +30,11 @@ snippet: "
"
frame size: 0
parameter count: 1
bytecode array length: 6
bytecode array length: 4
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 34 S> */ B(LdaConstant), U8(0),
/* 34 E> */ B(Throw),
B(LdaUndefined),
/* 49 S> */ B(Return),
]
constant pool: [
"Error",
......
......@@ -13,13 +13,13 @@ snippet: "
"
frame size: 5
parameter count: 1
bytecode array length: 43
bytecode array length: 41
bytecodes: [
/* 30 E> */ B(StackCheck),
B(Mov), R(context), R(1),
/* 40 S> */ B(LdaSmi), U8(1),
/* 75 S> */ B(Return),
B(Jump), U8(34),
B(Jump), U8(32),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(2),
......@@ -32,7 +32,6 @@ bytecodes: [
/* 63 S> */ B(LdaSmi), U8(2),
B(PopContext), R(0),
/* 75 S> */ B(Return),
B(PopContext), R(0),
B(LdaUndefined),
/* 75 S> */ B(Return),
]
......
......@@ -13,7 +13,7 @@ snippet: "
"
frame size: 4
parameter count: 1
bytecode array length: 27
bytecode array length: 25
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 34 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(1),
......@@ -26,7 +26,6 @@ bytecodes: [
/* 50 S> */ B(LdaLookupSlot), U8(1),
B(PopContext), R(0),
/* 62 S> */ B(Return),
B(PopContext), R(0),
]
constant pool: [
InstanceType::FIXED_ARRAY_TYPE,
......
......@@ -1053,7 +1053,7 @@ TEST(InterpreterJumpConstantWith16BitOperand) {
0, 257);
Register reg(0), scratch(256);
BytecodeLabel done;
BytecodeLabel done, fake;
builder.LoadLiteral(Smi::FromInt(0));
builder.StoreAccumulatorInRegister(reg);
......@@ -1066,6 +1066,7 @@ TEST(InterpreterJumpConstantWith16BitOperand) {
builder.Jump(&done);
// Emit more than 16-bit immediate operands worth of code to jump over.
builder.Bind(&fake);
for (int i = 0; i < 6600; i++) {
builder.LoadLiteral(Smi::FromInt(0)); // 1-byte
builder.BinaryOperation(Token::Value::ADD, scratch); // 4-bytes
......
......@@ -19,11 +19,12 @@ namespace interpreter {
// Flags enabling optimizations that change generated bytecode array.
// Format is <command-line flag> <flag name> <bit index>
#define OPTIMIZATION_FLAGS(V) \
V(FLAG_ignition_reo, kUseReo, 0) \
V(FLAG_ignition_peephole, kUsePeephole, 1) \
V(FLAG_ignition_filter_expression_positions, \
kUseUseFilterExpressionPositions, 2)
#define OPTIMIZATION_FLAGS(V) \
V(FLAG_ignition_reo, kUseReo, 0) \
V(FLAG_ignition_peephole, kUsePeephole, 1) \
V(FLAG_ignition_filter_expression_positions, kUseFilterExpressionPositions, \
2) \
V(FLAG_ignition_deadcode, kUseDeadCode, 3)
#define DECLARE_BIT(_, Name, BitIndex) static const int Name = 1 << BitIndex;
OPTIMIZATION_FLAGS(DECLARE_BIT)
......@@ -32,17 +33,22 @@ OPTIMIZATION_FLAGS(DECLARE_BIT)
// Test cases source positions are checked for. Please ensure all
// combinations of flags are present here. This is done manually
// because it provides easier to comprehend failure case for humans.
#define TEST_CASES(V) \
V(UsingReo, kUseReo) \
V(UsingPeephole, kUsePeephole) \
V(UsingReoAndPeephole, kUseReo | kUsePeephole) \
V(UsingUseFilterExpressionPositions, kUseUseFilterExpressionPositions) \
V(UsingReoAndUseFilterExpressionPositions, \
kUseReo | kUseUseFilterExpressionPositions) \
V(UsingPeepholeAndUseFilterExpressionPositions, \
kUsePeephole | kUseUseFilterExpressionPositions) \
V(UsingAllOptimizations, \
kUseReo | kUsePeephole | kUseUseFilterExpressionPositions)
#define TEST_CASES(V) \
V(UsingReo, kUseReo) \
V(UsingPeephole, kUsePeephole) \
V(UsingDeadCode, kUseDeadCode) \
V(UsingFilterExpressionPositions, kUseFilterExpressionPositions) \
V(UsingReoAndPeephole, kUseReo | kUsePeephole) \
V(UsingReoAndFilterExpressionPositions, \
kUseReo | kUseFilterExpressionPositions) \
V(UsingReoAndDeadCode, kUseReo | kUseDeadCode) \
V(UsingPeepholeAndFilterExpressionPositions, \
kUsePeephole | kUseFilterExpressionPositions) \
V(UsingPeepholeAndDeadCode, kUsePeephole | kUseDeadCode) \
V(UsingFilterExpressionPositionsAndDeadCode, \
kUseFilterExpressionPositions | kUseDeadCode) \
V(UsingAllOptimizations, \
kUseReo | kUsePeephole | kUseFilterExpressionPositions | kUseDeadCode)
struct TestCaseData {
TestCaseData(const char* const script,
......@@ -142,9 +148,7 @@ class OptimizedBytecodeSourcePositionTester final {
const char* function_body,
const char* function_decl_params,
const char* function_args);
static std::string MakeFunctionName(int optimization_bitmap);
static std::string MakeScript(const char* function_name,
const char* function_body,
static std::string MakeScript(const char* function_body,
const char* function_decl_params,
const char* function_args);
......@@ -160,40 +164,31 @@ class OptimizedBytecodeSourcePositionTester final {
bool saved_flag_always_opt_;
};
// static
std::string OptimizedBytecodeSourcePositionTester::MakeFunctionName(
int optimization_bitmap) {
std::ostringstream os;
os << "test_function_" << optimization_bitmap;
return os.str();
}
// static
std::string OptimizedBytecodeSourcePositionTester::MakeScript(
const char* function_name, const char* function_body,
const char* function_decl_params, const char* function_args) {
const char* function_body, const char* function_decl_params,
const char* function_args) {
std::ostringstream os;
os << "function " << function_name << "(" << function_decl_params << ") {";
os << "function test_function"
<< "(" << function_decl_params << ") {";
os << function_body;
os << "}";
os << function_name << "(" << function_args << ");";
os << "test_function(" << function_args << ");";
return os.str();
}
Handle<BytecodeArray> OptimizedBytecodeSourcePositionTester::MakeBytecode(
int optimization_bitmap, const char* function_body,
const char* function_decl_params, const char* function_args) {
std::string function_name = MakeFunctionName(optimization_bitmap);
std::string script = MakeScript(function_name.c_str(), function_body,
function_decl_params, function_args);
std::string script =
MakeScript(function_body, function_decl_params, function_args);
SetOptimizationFlags(optimization_bitmap);
CompileRun(script.c_str());
Local<Function> api_function =
Local<Function>::Cast(CcTest::global()
->Get(CcTest::isolate()->GetCurrentContext(),
v8_str(function_name.c_str()))
.ToLocalChecked());
Local<Function> api_function = Local<Function>::Cast(
CcTest::global()
->Get(CcTest::isolate()->GetCurrentContext(), v8_str("test_function"))
.ToLocalChecked());
Handle<JSFunction> function =
Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function));
return handle(function->shared()->bytecode_array());
......
......@@ -90,6 +90,7 @@ v8_executable("unittests") {
"interpreter/bytecode-array-builder-unittest.cc",
"interpreter/bytecode-array-iterator-unittest.cc",
"interpreter/bytecode-array-writer-unittest.cc",
"interpreter/bytecode-dead-code-optimizer-unittest.cc",
"interpreter/bytecode-peephole-optimizer-unittest.cc",
"interpreter/bytecode-pipeline-unittest.cc",
"interpreter/bytecode-register-allocator-unittest.cc",
......
......@@ -171,26 +171,34 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Emit control flow. Return must be the last instruction.
BytecodeLabel start;
builder.Bind(&start);
// Short jumps with Imm8 operands
builder.Jump(&start)
.JumpIfNull(&start)
.JumpIfUndefined(&start)
.JumpIfNotHole(&start);
{
// Short jumps with Imm8 operands
BytecodeLabel after_jump;
builder.Jump(&start)
.Bind(&after_jump)
.JumpIfNull(&start)
.JumpIfUndefined(&start)
.JumpIfNotHole(&start);
}
// Longer jumps with constant operands
BytecodeLabel end[8];
builder.Jump(&end[0])
.LoadTrue()
.JumpIfTrue(&end[1])
.LoadTrue()
.JumpIfFalse(&end[2])
.LoadLiteral(Smi::FromInt(0))
.JumpIfTrue(&end[3])
.LoadLiteral(Smi::FromInt(0))
.JumpIfFalse(&end[4])
.JumpIfNull(&end[5])
.JumpIfUndefined(&end[6])
.JumpIfNotHole(&end[7]);
{
BytecodeLabel after_jump;
builder.Jump(&end[0])
.Bind(&after_jump)
.LoadTrue()
.JumpIfTrue(&end[1])
.LoadTrue()
.JumpIfFalse(&end[2])
.LoadLiteral(Smi::FromInt(0))
.JumpIfTrue(&end[3])
.LoadLiteral(Smi::FromInt(0))
.JumpIfFalse(&end[4])
.JumpIfNull(&end[5])
.JumpIfUndefined(&end[6])
.JumpIfNotHole(&end[7]);
}
// Perform an operation that returns boolean value to
// generate JumpIfTrue/False
......@@ -209,20 +217,26 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.LoadTrue();
}
// Longer jumps requiring Constant operand
builder.Jump(&start).JumpIfNull(&start).JumpIfUndefined(&start).JumpIfNotHole(
&start);
// Perform an operation that returns boolean value to
// generate JumpIfTrue/False
builder.CompareOperation(Token::Value::EQ, reg)
.JumpIfTrue(&start)
.CompareOperation(Token::Value::EQ, reg)
.JumpIfFalse(&start);
// Perform an operation that returns a non-boolean operation to
// generate JumpIfToBooleanTrue/False.
builder.BinaryOperation(Token::Value::ADD, reg)
.JumpIfTrue(&start)
.BinaryOperation(Token::Value::ADD, reg)
.JumpIfFalse(&start);
{
BytecodeLabel after_jump;
builder.Jump(&start)
.Bind(&after_jump)
.JumpIfNull(&start)
.JumpIfUndefined(&start)
.JumpIfNotHole(&start);
// Perform an operation that returns boolean value to
// generate JumpIfTrue/False
builder.CompareOperation(Token::Value::EQ, reg)
.JumpIfTrue(&start)
.CompareOperation(Token::Value::EQ, reg)
.JumpIfFalse(&start);
// Perform an operation that returns a non-boolean operation to
// generate JumpIfToBooleanTrue/False.
builder.BinaryOperation(Token::Value::ADD, reg)
.JumpIfTrue(&start)
.BinaryOperation(Token::Value::ADD, reg)
.JumpIfFalse(&start);
}
// Emit stack check bytecode.
builder.StackCheck(0);
......@@ -230,9 +244,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Emit throw and re-throw in it's own basic block so that the rest of the
// code isn't omitted due to being dead.
BytecodeLabel after_throw;
builder.Jump(&after_throw).Throw().Bind(&after_throw);
builder.Throw().Bind(&after_throw);
BytecodeLabel after_rethrow;
builder.Jump(&after_rethrow).ReThrow().Bind(&after_rethrow);
builder.ReThrow().Bind(&after_rethrow);
builder.ForInPrepare(reg)
.ForInDone(reg, reg)
......@@ -304,8 +318,14 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CreateObjectLiteral(factory->NewFixedArray(2), 0, 0);
// Longer jumps requiring ConstantWide operand
builder.Jump(&start).JumpIfNull(&start).JumpIfUndefined(&start).JumpIfNotHole(
&start);
{
BytecodeLabel after_jump;
builder.Jump(&start)
.Bind(&after_jump)
.JumpIfNull(&start)
.JumpIfUndefined(&start)
.JumpIfNotHole(&start);
}
// Perform an operation that returns boolean value to
// generate JumpIfTrue/False
......@@ -506,8 +526,10 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
Register reg(0);
BytecodeLabel far0, far1, far2, far3, far4;
BytecodeLabel near0, near1, near2, near3, near4;
BytecodeLabel after_jump0, after_jump1;
builder.Jump(&near0)
.Bind(&after_jump0)
.CompareOperation(Token::Value::EQ, reg)
.JumpIfTrue(&near1)
.CompareOperation(Token::Value::EQ, reg)
......@@ -522,6 +544,7 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
.Bind(&near3)
.Bind(&near4)
.Jump(&far0)
.Bind(&after_jump1)
.CompareOperation(Token::Value::EQ, reg)
.JumpIfTrue(&far1)
.CompareOperation(Token::Value::EQ, reg)
......@@ -637,7 +660,8 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
.BinaryOperation(Token::Value::ADD, reg)
.JumpIfFalse(&label4);
for (int i = 0; i < 63; i++) {
builder.Jump(&label4);
BytecodeLabel after_jump;
builder.Jump(&label4).Bind(&after_jump);
}
// Add padding to force wide backwards jumps.
......@@ -650,6 +674,8 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
builder.CompareOperation(Token::Value::EQ, reg).JumpIfFalse(&label2);
builder.CompareOperation(Token::Value::EQ, reg).JumpIfTrue(&label1);
builder.Jump(&label0);
BytecodeLabel end;
builder.Bind(&end);
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray();
......@@ -735,9 +761,15 @@ TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
// Labels can only have 1 forward reference, but
// can be referred to mulitple times once bound.
BytecodeLabel label;
builder.Jump(&label).Bind(&label).Jump(&label).Jump(&label).Return();
BytecodeLabel label, after_jump0, after_jump1;
builder.Jump(&label)
.Bind(&label)
.Jump(&label)
.Bind(&after_jump0)
.Jump(&label)
.Bind(&after_jump1)
.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray();
BytecodeArrayIterator iterator(array);
......@@ -761,8 +793,13 @@ TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
for (int i = 0; i < kRepeats; i++) {
BytecodeLabel label;
builder.Jump(&label).Bind(&label).Jump(&label).Jump(&label);
BytecodeLabel label, after_jump0, after_jump1;
builder.Jump(&label)
.Bind(&label)
.Jump(&label)
.Bind(&after_jump0)
.Jump(&label)
.Bind(&after_jump1);
}
builder.Return();
......
// 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/v8.h"
#include "src/interpreter/bytecode-dead-code-optimizer.h"
#include "src/interpreter/bytecode-label.h"
#include "src/objects.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeDeadCodeOptimizerTest : public BytecodePipelineStage,
public TestWithIsolateAndZone {
public:
BytecodeDeadCodeOptimizerTest() : dead_code_optimizer_(this) {}
~BytecodeDeadCodeOptimizerTest() override {}
void Write(BytecodeNode* node) override {
write_count_++;
last_written_.Clone(node);
}
void WriteJump(BytecodeNode* node, BytecodeLabel* label) override {
write_count_++;
last_written_.Clone(node);
}
void BindLabel(BytecodeLabel* label) override {}
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override {}
Handle<BytecodeArray> ToBytecodeArray(
int fixed_register_count, int parameter_count,
Handle<FixedArray> handle_table) override {
return Handle<BytecodeArray>();
}
BytecodeDeadCodeOptimizer* optimizer() { return &dead_code_optimizer_; }
int write_count() const { return write_count_; }
const BytecodeNode& last_written() const { return last_written_; }
private:
BytecodeDeadCodeOptimizer dead_code_optimizer_;
int write_count_ = 0;
BytecodeNode last_written_;
};
TEST_F(BytecodeDeadCodeOptimizerTest, LiveCodeKept) {
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
optimizer()->Write(&add);
CHECK_EQ(write_count(), 1);
CHECK_EQ(add, last_written());
BytecodeLabel target;
BytecodeNode jump(Bytecode::kJump, 0);
optimizer()->WriteJump(&jump, &target);
CHECK_EQ(write_count(), 2);
CHECK_EQ(jump, last_written());
}
TEST_F(BytecodeDeadCodeOptimizerTest, DeadCodeAfterReturnEliminated) {
BytecodeNode ret(Bytecode::kReturn);
optimizer()->Write(&ret);
CHECK_EQ(write_count(), 1);
CHECK_EQ(ret, last_written());
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
optimizer()->Write(&add);
CHECK_EQ(write_count(), 1);
CHECK_EQ(ret, last_written());
}
TEST_F(BytecodeDeadCodeOptimizerTest, DeadCodeAfterThrowEliminated) {
BytecodeNode thrw(Bytecode::kThrow);
optimizer()->Write(&thrw);
CHECK_EQ(write_count(), 1);
CHECK_EQ(thrw, last_written());
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
optimizer()->Write(&add);
CHECK_EQ(write_count(), 1);
CHECK_EQ(thrw, last_written());
}
TEST_F(BytecodeDeadCodeOptimizerTest, DeadCodeAfterReThrowEliminated) {
BytecodeNode rethrow(Bytecode::kReThrow);
optimizer()->Write(&rethrow);
CHECK_EQ(write_count(), 1);
CHECK_EQ(rethrow, last_written());
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
optimizer()->Write(&add);
CHECK_EQ(write_count(), 1);
CHECK_EQ(rethrow, last_written());
}
TEST_F(BytecodeDeadCodeOptimizerTest, DeadCodeAfterJumpEliminated) {
BytecodeLabel target;
BytecodeNode jump(Bytecode::kJump, 0);
optimizer()->WriteJump(&jump, &target);
CHECK_EQ(write_count(), 1);
CHECK_EQ(jump, last_written());
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
optimizer()->Write(&add);
CHECK_EQ(write_count(), 1);
CHECK_EQ(jump, last_written());
}
TEST_F(BytecodeDeadCodeOptimizerTest, DeadCodeStillDeadAfterConditinalJump) {
BytecodeNode ret(Bytecode::kReturn);
optimizer()->Write(&ret);
CHECK_EQ(write_count(), 1);
CHECK_EQ(ret, last_written());
BytecodeLabel target;
BytecodeNode jump(Bytecode::kJumpIfTrue, 0);
optimizer()->WriteJump(&jump, &target);
CHECK_EQ(write_count(), 1);
CHECK_EQ(ret, last_written());
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
optimizer()->Write(&add);
CHECK_EQ(write_count(), 1);
CHECK_EQ(ret, last_written());
}
TEST_F(BytecodeDeadCodeOptimizerTest, CodeLiveAfterLabelBind) {
BytecodeNode ret(Bytecode::kReturn);
optimizer()->Write(&ret);
CHECK_EQ(write_count(), 1);
CHECK_EQ(ret, last_written());
BytecodeLabel target;
optimizer()->BindLabel(&target);
BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
optimizer()->Write(&add);
CHECK_EQ(write_count(), 2);
CHECK_EQ(add, last_written());
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -100,6 +100,7 @@
'interpreter/bytecode-array-builder-unittest.cc',
'interpreter/bytecode-array-iterator-unittest.cc',
'interpreter/bytecode-array-writer-unittest.cc',
'interpreter/bytecode-dead-code-optimizer-unittest.cc',
'interpreter/bytecode-peephole-optimizer-unittest.cc',
'interpreter/bytecode-pipeline-unittest.cc',
'interpreter/bytecode-register-allocator-unittest.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