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

[Interpreter] Enable assignments in expressions.

This change introduces register re-mapping to avoid assignment hazards
in binary expressions. Expressions that cause problems typically have
the form y = x + (x = 4);. The problem occurs because the lhs value
evaluates to the register holding x. The rhs updates that register and
then applying the operation would use the new value as the lhs.

By tracking loads and stores in binary expressions the generator is now
able to detect when condition occurs and uses a temporary register for
the rhs value. When the binary expression evaluation is complete the
variable is updated with the latest temporary.

A new bytecode Mov performs this update without touching the
accumulator.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#32141}
parent 0ec6db47
......@@ -330,6 +330,13 @@ void BytecodeGraphBuilder::VisitStar(
}
void BytecodeGraphBuilder::VisitMov(
const interpreter::BytecodeArrayIterator& iterator) {
Node* value = environment()->LookupRegister(iterator.GetRegisterOperand(0));
environment()->BindRegister(iterator.GetRegisterOperand(1), value);
}
void BytecodeGraphBuilder::BuildLoadGlobal(
const interpreter::BytecodeArrayIterator& iterator,
TypeofMode typeof_mode) {
......
......@@ -288,6 +288,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
Register reg) {
// TODO(oth): Avoid storing the accumulator in the register if the
// previous bytecode loaded the accumulator with the same register.
//
// TODO(oth): If the previous bytecode is a MOV into this register,
// the previous instruction can be removed. The logic for determining
// these redundant MOVs appears complex.
Output(Bytecode::kStar, reg.ToOperand());
if (!IsRegisterInAccumulator(reg)) {
Output(Bytecode::kStar, reg.ToOperand());
}
......@@ -295,6 +302,14 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from,
Register to) {
DCHECK(from != to);
Output(Bytecode::kMov, from.ToOperand(), to.ToOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(
size_t name_index, int feedback_slot, LanguageMode language_mode,
TypeofMode typeof_mode) {
......
......@@ -97,6 +97,9 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg);
BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg);
// Register-register transfer.
BytecodeArrayBuilder& MoveRegister(Register from, Register to);
// Named load property.
BytecodeArrayBuilder& LoadNamedProperty(Register object, size_t name_index,
int feedback_slot,
......
......@@ -297,6 +297,120 @@ class BytecodeGenerator::RegisterResultScope final
};
BytecodeGenerator::AssignmentHazardHelper::AssignmentHazardHelper(
BytecodeGenerator* generator)
: generator_(generator),
alias_mappings_(generator->zone()),
aliased_locals_and_parameters_(generator->zone()),
execution_result_(nullptr),
scope_depth_(0) {}
void BytecodeGenerator::AssignmentHazardHelper::EnterScope() {
DCHECK_GE(scope_depth_, 0);
if (scope_depth_++ == 0) {
execution_result_ = generator_->execution_result();
}
}
void BytecodeGenerator::AssignmentHazardHelper::LeaveScope() {
DCHECK_GT(scope_depth_, 0);
if (--scope_depth_ == 0) {
DCHECK_EQ(execution_result_, generator_->execution_result());
RestoreAliasedLocalsAndParameters();
}
}
// Returns a register that a load instruction should use when
// loading from |reg|. This allows an alias for a modified version
// of |reg| to be used within a hazard regions.
MUST_USE_RESULT Register
BytecodeGenerator::AssignmentHazardHelper::GetRegisterForLoad(Register reg) {
if (scope_depth_ == 0) {
return reg;
} else {
// A load from |reg| is to be issued. The register is placed in
// the mappings table initially mapping to itself. Future stores
// will update the mapping with temporaries thus preserving the
// original register's value.
//
// NB This insert only updates the table if no mapping exists
// already (std::map::insert semantics).
auto insert_result =
alias_mappings_.insert(std::make_pair(reg.index(), reg.index()));
auto mapping = insert_result.first;
// Return the current alias for reg.
return Register(mapping->second);
}
}
// Returns a register that a store instruction should use when
// loading from |reg|. This allows an alias for a modified version
// of |reg| to be used within hazard regions.
MUST_USE_RESULT Register
BytecodeGenerator::AssignmentHazardHelper::GetRegisterForStore(Register reg) {
if (scope_depth_ == 0 ||
alias_mappings_.find(reg.index()) == alias_mappings_.end()) {
// If not in a hazard region or a load for this register has not
// occurred no mapping is necessary.
return reg;
} else {
// Storing to a register with 1 or more loads issued. The
// register is mapped to a temporary alias so we don't overwrite
// the lhs value, e.g. y = x + (x = 1); has a register for x on
// the lhs and needs a new register x' for the upcoming store on
// the rhs as the original x is an input to the add operation.
Register alias = execution_result_->NewRegister();
alias_mappings_[reg.index()] = alias.index();
if (generator_->builder()->RegisterIsParameterOrLocal(reg)) {
// Keep track of registers that need to be restored on exit
// from the assignment hazard region.
aliased_locals_and_parameters_.insert(reg.index());
}
return alias;
}
}
void BytecodeGenerator::AssignmentHazardHelper::
RestoreAliasedLocalsAndParameters() {
DCHECK(scope_depth_ == 0);
// Move temporary registers holding values for locals and
// parameters back into their local and parameter registers.
for (auto reg = aliased_locals_and_parameters_.begin();
reg != aliased_locals_and_parameters_.end(); reg++) {
auto mapping = alias_mappings_.find(*reg);
if (mapping != alias_mappings_.end()) {
generator_->builder()->MoveRegister(Register(mapping->second),
Register(*reg));
}
}
alias_mappings_.clear();
aliased_locals_and_parameters_.clear();
}
class BytecodeGenerator::AssignmentHazardScope final {
public:
explicit AssignmentHazardScope(BytecodeGenerator* generator)
: generator_(generator) {
generator_->assignment_hazard_helper()->EnterScope();
}
~AssignmentHazardScope() {
generator_->assignment_hazard_helper()->LeaveScope();
}
private:
BytecodeGenerator* generator_;
DISALLOW_COPY_AND_ASSIGN(AssignmentHazardScope);
};
BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
: isolate_(isolate),
zone_(zone),
......@@ -307,15 +421,11 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
execution_control_(nullptr),
execution_context_(nullptr),
execution_result_(nullptr),
binary_expression_depth_(0),
binary_expression_hazard_set_(zone) {
assignment_hazard_helper_(this) {
InitializeAstVisitor(isolate);
}
BytecodeGenerator::~BytecodeGenerator() {}
Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
set_info(info);
set_scope(info->scope());
......@@ -506,8 +616,7 @@ void BytecodeGenerator::VisitDeclarations(
void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
// TODO(rmcilroy): Replace this with a StatementResultScope when it exists.
EffectResultScope effect_scope(this);
EffectResultScope statement_result_scope(this);
VisitForEffect(stmt->expression());
}
......@@ -559,7 +668,7 @@ void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
EffectResultScope effect_scope(this);
EffectResultScope statement_result_scope(this);
VisitForAccumulatorValue(stmt->expression());
builder()->Return();
}
......@@ -628,7 +737,6 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
LoopBuilder loop_builder(builder());
ControlScopeForIteration execution_control(this, stmt, &loop_builder);
BytecodeLabel body_label, condition_label, done_label;
if (stmt->cond()->ToBooleanIsFalse()) {
Visit(stmt->body());
// Bind condition_label and done_label for processing continue and break.
......@@ -764,11 +872,7 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr,
void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// TODO(oth): For now we need a parent scope for paths that end up
// in VisitLiteral which can allocate in the parent scope. A future
// CL in preparation will add a StatementResultScope that will
// remove the need for this EffectResultScope.
EffectResultScope result_scope(this);
EffectResultScope statement_result_scope(this);
if (stmt->subject()->IsNullLiteral() ||
stmt->subject()->IsUndefinedLiteral(isolate())) {
......@@ -1198,6 +1302,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
switch (variable->location()) {
case VariableLocation::LOCAL: {
Register source(Register(variable->index()));
source = assignment_hazard_helper()->GetRegisterForLoad(source);
execution_result()->SetResultInRegister(source);
break;
}
......@@ -1205,6 +1310,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
// The parameter indices are shifted by 1 (receiver is variable
// index -1 but is parameter index 0 in BytecodeArrayBuilder).
Register source = builder()->Parameter(variable->index() + 1);
source = assignment_hazard_helper()->GetRegisterForLoad(source);
execution_result()->SetResultInRegister(source);
break;
}
......@@ -1269,16 +1375,19 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
case VariableLocation::LOCAL: {
// TODO(rmcilroy): support const mode initialization.
Register destination(variable->index());
destination =
assignment_hazard_helper()->GetRegisterForStore(destination);
builder()->StoreAccumulatorInRegister(destination);
RecordStoreToRegister(destination);
break;
}
case VariableLocation::PARAMETER: {
// The parameter indices are shifted by 1 (receiver is variable
// index -1 but is parameter index 0 in BytecodeArrayBuilder).
Register destination(builder()->Parameter(variable->index() + 1));
destination =
assignment_hazard_helper()->GetRegisterForStore(destination);
builder()->StoreAccumulatorInRegister(destination);
RecordStoreToRegister(destination);
break;
}
case VariableLocation::GLOBAL:
......@@ -1826,37 +1935,33 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) {
void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// TODO(oth): Remove PrepareForBinaryExpression/CompleteBinaryExpression
// once we have StatementScope that tracks hazardous loads/stores.
PrepareForBinaryExpression();
// The evaluation of binary comparison expressions has an assignment
// hazard because the lhs may be a variable that evaluates to a
// local or parameter and the rhs may modify that, e.g. y = x + (x = 1)
// To get a correct result the generator treats the inner assigment
// as being made to a temporary x' that is spilled on exit of the
// assignment hazard.
AssignmentHazardScope assignment_hazard_scope(this);
Register lhs = VisitForRegisterValue(expr->left());
if (builder()->RegisterIsParameterOrLocal(lhs)) {
// Result was returned in an existing local or parameter. See if
// it needs to be moved to a temporary.
// TODO(oth) LoadFromAliasedRegister call into VisitVariableLoad().
lhs = LoadFromAliasedRegister(lhs);
}
VisitForAccumulatorValue(expr->right());
builder()->CompareOperation(expr->op(), lhs, language_mode_strength());
CompleteBinaryExpression();
execution_result()->SetResultInAccumulator();
}
void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
// TODO(oth): Remove PrepareForBinaryExpression/CompleteBinaryExpression
// once we have StatementScope that tracks hazardous loads/stores.
PrepareForBinaryExpression();
// The evaluation of binary arithmetic expressions has an assignment
// hazard because the lhs may be a variable that evaluates to a
// local or parameter and the rhs may modify that, e.g. y = x + (x = 1)
// To get a correct result the generator treats the inner assigment
// as being made to a temporary x' that is spilled on exit of the
// assignment hazard.
AssignmentHazardScope assignment_hazard_scope(this);
Register lhs = VisitForRegisterValue(expr->left());
if (builder()->RegisterIsParameterOrLocal(lhs)) {
// Result was returned in an existing local or parameter. See if
// it needs to be moved to a temporary.
// TODO(oth) LoadFromAliasedRegister call into VisitVariableLoad().
lhs = LoadFromAliasedRegister(lhs);
}
VisitForAccumulatorValue(expr->right());
builder()->BinaryOperation(expr->op(), lhs, language_mode_strength());
CompleteBinaryExpression();
execution_result()->SetResultInAccumulator();
}
......@@ -2074,13 +2179,6 @@ void BytecodeGenerator::VisitFunctionClosureForContext() {
}
void BytecodeGenerator::PrepareForBinaryExpression() {
if (binary_expression_depth_++ == 0) {
binary_expression_hazard_set_.clear();
}
}
// Visits the expression |expr| and places the result in the accumulator.
void BytecodeGenerator::VisitForAccumulatorValue(Expression* expr) {
AccumulatorResultScope accumulator_scope(this);
......@@ -2104,35 +2202,6 @@ Register BytecodeGenerator::VisitForRegisterValue(Expression* expr) {
}
Register BytecodeGenerator::LoadFromAliasedRegister(Register reg) {
// TODO(oth): Follow on CL to load from re-map here.
DCHECK(builder()->RegisterIsParameterOrLocal(reg));
if (binary_expression_depth_ > 0) {
binary_expression_hazard_set_.insert(reg.index());
}
return reg;
}
void BytecodeGenerator::RecordStoreToRegister(Register reg) {
DCHECK(builder()->RegisterIsParameterOrLocal(reg));
if (binary_expression_depth_ > 0) {
// TODO(oth): a store to a register that's be loaded needs to be
// remapped.
DCHECK(binary_expression_hazard_set_.find(reg.index()) ==
binary_expression_hazard_set_.end());
}
}
void BytecodeGenerator::CompleteBinaryExpression() {
DCHECK(binary_expression_depth_ > 0);
binary_expression_depth_ -= 1;
// TODO(oth): spill remapped registers into origins.
// TODO(oth): make statement/top-level.
}
Register BytecodeGenerator::NextContextRegister() const {
if (execution_context() == nullptr) {
// Return the incoming function context for the outermost execution context.
......
......@@ -13,10 +13,9 @@ namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeGenerator : public AstVisitor {
class BytecodeGenerator final : public AstVisitor {
public:
BytecodeGenerator(Isolate* isolate, Zone* zone);
virtual ~BytecodeGenerator();
Handle<BytecodeArray> MakeBytecode(CompilationInfo* info);
......@@ -36,6 +35,37 @@ class BytecodeGenerator : public AstVisitor {
class EffectResultScope;
class AccumulatorResultScope;
class RegisterResultScope;
class AssignmentHazardScope;
// Helper class that aliases locals and parameters when assignment
// hazards occur in binary expressions. For y = x + (x = 1) has an
// assignment hazard because the lhs evaluates to the register
// holding x and the rhs (x = 1) potentially updates x. When this
// hazard is detected, the rhs uses a temporary to hold the newer
// value of x while preserving the lhs for the binary expresion
// evaluation. The newer value is spilled to x at the end of the
// binary expression evaluation.
class AssignmentHazardHelper final {
public:
explicit AssignmentHazardHelper(BytecodeGenerator* generator);
MUST_USE_RESULT Register GetRegisterForLoad(Register reg);
MUST_USE_RESULT Register GetRegisterForStore(Register reg);
private:
friend class AssignmentHazardScope;
void EnterScope();
void LeaveScope();
void RestoreAliasedLocalsAndParameters();
BytecodeGenerator* generator_;
ZoneMap<int, int> alias_mappings_;
ZoneSet<int> aliased_locals_and_parameters_;
ExpressionResultScope* execution_result_;
int scope_depth_;
DISALLOW_COPY_AND_ASSIGN(AssignmentHazardHelper);
};
void MakeBytecodeBody();
Register NextContextRegister() const;
......@@ -54,6 +84,9 @@ class BytecodeGenerator : public AstVisitor {
void VisitNot(UnaryOperation* expr);
void VisitDelete(UnaryOperation* expr);
// Used by flow control routines to evaluate loop condition.
void VisitCondition(Expression* expr);
// Helper visitors which perform common operations.
Register VisitArguments(ZoneList<Expression*>* arguments);
......@@ -84,17 +117,12 @@ class BytecodeGenerator : public AstVisitor {
Register value_out);
void VisitForInAssignment(Expression* expr, FeedbackVectorSlot slot);
// Visitors for obtaining expression result in the accumulator, in a
// register, or just getting the effect.
void VisitForAccumulatorValue(Expression* expression);
MUST_USE_RESULT Register VisitForRegisterValue(Expression* expression);
void VisitForEffect(Expression* node);
// Methods marking the start and end of binary expressions.
void PrepareForBinaryExpression();
void CompleteBinaryExpression();
// Methods for tracking and remapping register.
void RecordStoreToRegister(Register reg);
Register LoadFromAliasedRegister(Register reg);
......@@ -121,6 +149,9 @@ class BytecodeGenerator : public AstVisitor {
execution_result_ = execution_result;
}
ExpressionResultScope* execution_result() const { return execution_result_; }
inline AssignmentHazardHelper* assignment_hazard_helper() {
return &assignment_hazard_helper_;
}
ZoneVector<Handle<Object>>* globals() { return &globals_; }
inline LanguageMode language_mode() const;
......@@ -136,9 +167,7 @@ class BytecodeGenerator : public AstVisitor {
ControlScope* execution_control_;
ContextScope* execution_context_;
ExpressionResultScope* execution_result_;
int binary_expression_depth_;
ZoneSet<int> binary_expression_hazard_set_;
AssignmentHazardHelper assignment_hazard_helper_;
};
} // namespace interpreter
......
......@@ -70,6 +70,9 @@ namespace interpreter {
V(Ldar, OperandType::kReg8) \
V(Star, OperandType::kReg8) \
\
/* Register-register transfers */ \
V(Mov, OperandType::kReg8, OperandType::kReg8) \
\
/* LoadIC operations */ \
V(LoadICSloppy, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \
V(LoadICStrict, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \
......
......@@ -200,6 +200,18 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) {
}
// Mov <src> <dst>
//
// Stores the value of register <src> to register <dst>.
void Interpreter::DoMov(compiler::InterpreterAssembler* assembler) {
Node* src_index = __ BytecodeOperandReg(0);
Node* src_value = __ LoadRegister(src_index);
Node* dst_index = __ BytecodeOperandReg(1);
__ StoreRegister(src_value, dst_index);
__ Dispatch();
}
void Interpreter::DoLoadGlobal(Callable ic,
compiler::InterpreterAssembler* assembler) {
// Get the global object.
......@@ -216,7 +228,6 @@ void Interpreter::DoLoadGlobal(Callable ic,
Node* result = __ CallIC(ic.descriptor(), code_target, global, name, smi_slot,
type_feedback_vector);
__ SetAccumulator(result);
__ Dispatch();
}
......
......@@ -5264,6 +5264,225 @@ TEST(RemoveRedundantLdar) {
}
}
TEST(AssignmentsInBinaryExpression) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<const char*> snippets[] = {
{"var x = 0, y = 1;\n"
"return (x = 2, y = 3, x = 4, y = 5)",
2 * kPointerSize,
1,
24,
{
B(LdaZero), B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(LdaSmi8), U8(2), //
B(Star), R(0), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(LdaSmi8), U8(4), //
B(Star), R(0), //
B(LdaSmi8), U8(5), //
B(Star), R(1), //
B(Return), //
},
0},
{"var x = 55;\n"
"var y = (x = 100);\n"
"return y",
2 * kPointerSize,
1,
11,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(100), //
B(Star), R(0), //
B(Star), R(1), //
B(Return), //
},
0},
{"var x = 55;\n"
"x = x + (x = 100) + (x = 101);\n"
"return x;",
4 * kPointerSize,
1,
24,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(100), //
B(Star), R(1), //
B(Add), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(101), //
B(Star), R(3), //
B(Add), R(2), //
B(Mov), R(3), R(0), //
B(Star), R(0), //
B(Return), //
},
0},
{"var x = 55;\n"
"x = (x = 56) - x + (x = 57);\n"
"x++;\n"
"return x;",
3 * kPointerSize,
1,
34,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(56), //
B(Star), R(0), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Sub), R(1), //
B(Star), R(2), //
B(LdaSmi8), U8(57), //
B(Star), R(1), //
B(Add), R(2), //
B(Mov), R(1), R(0), //
B(Star), R(0), //
B(ToNumber), //
B(Star), R(1), //
B(Inc), //
B(Star), R(0), //
B(Return), //
},
0},
{"var x = 55;\n"
"var y = x + (x = 1) + (x = 2) + (x = 3);\n"
"return y;",
6 * kPointerSize,
1,
32,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(2), //
B(Add), R(0), //
B(Star), R(3), //
B(LdaSmi8), U8(2), //
B(Star), R(4), //
B(Add), R(3), //
B(Star), R(5), //
B(LdaSmi8), U8(3), //
B(Star), R(3), //
B(Add), R(5), //
B(Mov), R(3), R(0), //
B(Star), R(1), //
B(Return), //
},
0},
{"var x = 55;\n"
"var x = x + (x = 1) + (x = 2) + (x = 3);\n"
"return x;",
5 * kPointerSize,
1,
32,
{
B(LdaSmi8), U8(55), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Add), R(0), //
B(Star), R(2), //
B(LdaSmi8), U8(2), //
B(Star), R(3), //
B(Add), R(2), //
B(Star), R(4), //
B(LdaSmi8), U8(3), //
B(Star), R(2), //
B(Add), R(4), //
B(Mov), R(2), R(0), //
B(Star), R(0), //
B(Return), //
},
0},
{"var x = 10, y = 20;\n"
"return x + (x = 1) + (x + 1) * (y = 2) + (y = 3) + (x = 4) + (y = 5) + "
"y;\n",
6 * kPointerSize,
1,
64,
{
B(LdaSmi8), U8(10), //
B(Star), R(0), //
B(LdaSmi8), U8(20), //
B(Star), R(1), //
B(LdaSmi8), U8(1), //
B(Star), R(2), //
B(Add), R(0), //
B(Star), R(3), //
B(LdaSmi8), U8(1), //
B(Add), R(2), //
B(Star), R(4), //
B(LdaSmi8), U8(2), //
B(Star), R(1), //
B(Mul), R(4), //
B(Add), R(3), //
B(Star), R(4), //
B(LdaSmi8), U8(3), //
B(Star), R(1), //
B(Add), R(4), //
B(Star), R(3), //
B(LdaSmi8), U8(4), //
B(Star), R(4), //
B(Add), R(3), //
B(Star), R(5), //
B(LdaSmi8), U8(5), //
B(Star), R(1), //
B(Add), R(5), //
B(Star), R(3), //
B(Ldar), R(1), //
B(Add), R(3), //
B(Mov), R(4), R(0), //
B(Return), //
},
0},
{"var x = 17;\n"
"return 1 + x + (x++) + (++x);\n",
5 * kPointerSize,
1,
40,
{
B(LdaSmi8), U8(17), //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Add), R(1), //
B(Star), R(2), //
B(Ldar), R(0), //
B(ToNumber), //
B(Star), R(1), //
B(Inc), //
B(Star), R(3), //
B(Ldar), R(1), //
B(Add), R(2), //
B(Star), R(4), //
B(Ldar), R(3), //
B(ToNumber), //
B(Inc), //
B(Star), R(1), //
B(Add), R(4), //
B(Mov), R(1), R(0), //
B(Return), //
},
0}};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -2617,7 +2617,6 @@ TEST(InterpreterBasicLoops) {
TEST(InterpreterForIn) {
HandleAndZoneScope handles;
// TODO(oth): Add a test here for delete mid-loop when delete is ready.
std::pair<const char*, int> for_in_samples[] = {
{"function f() {\n"
" var r = -1;\n"
......@@ -2950,6 +2949,148 @@ TEST(InterpreterNewTarget) {
CHECK(new_target_name->SameValue(*factory->NewStringFromStaticChars("f")));
}
TEST(InterpreterAssignmentInExpressions) {
HandleAndZoneScope handles;
std::pair<const char*, int> samples[] = {
{"function f() {\n"
" var x = 7;\n"
" var y = x + (x = 1) + (x = 2);\n"
" return y;\n"
"}",
10},
{"function f() {\n"
" var x = 7;\n"
" var y = x + (x = 1) + (x = 2);\n"
" return x;\n"
"}",
2},
{"function f() {\n"
" var x = 55;\n"
" x = x + (x = 100) + (x = 101);\n"
" return x;\n"
"}",
256},
{"function f() {\n"
" var x = 7;\n"
" return ++x + x + x++;\n"
"}",
24},
{"function f() {\n"
" var x = 7;\n"
" var y = 1 + ++x + x + x++;\n"
" return x;\n"
"}",
9},
{"function f() {\n"
" var x = 7;\n"
" var y = ++x + x + x++;\n"
" return x;\n"
"}",
9},
{"function f() {\n"
" var x = 7, y = 100, z = 1000;\n"
" return x + (x += 3) + y + (y *= 10) + (z *= 7) + z;\n"
"}",
15117},
{"function f() {\n"
" var inner = function (x) { return x + (x = 2) + (x = 4) + x; };\n"
" return inner(1);\n"
"}",
11},
{"function f() {\n"
" var x = 1, y = 2;\n"
" x = x + (x = 3) + y + (y = 4), y = y + (y = 5) + y + x;\n"
" return x + y;\n"
"}",
10 + 24},
{"function f() {\n"
" var x = 0;\n"
" var y = x | (x = 1) | (x = 2);\n"
" return x;\n"
"}",
2},
{"function f() {\n"
" var x = 0;\n"
" var y = x || (x = 1);\n"
" return x;\n"
"}",
1},
{"function f() {\n"
" var x = 1;\n"
" var y = x && (x = 2) && (x = 3);\n"
" return x;\n"
"}",
3},
{"function f() {\n"
" var x = 1;\n"
" var y = x || (x = 2);\n"
" return x;\n"
"}",
1},
{"function f() {\n"
" var x = 1;\n"
" x = (x << (x = 3)) | (x = 16);\n"
" return x;\n"
"}",
24},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" var u = r + s + t + (r = 10) + (s = 20) +"
" (t = (r + s)) + r + s + t;\n"
" return r + s + t + u;\n"
"}",
211},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return r > (3 * s * (s = 1)) ? (t + (t += 1)) : (r + (r = 4));\n"
"}",
11},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return r > (3 * s * (s = 0)) ? (t + (t += 1)) : (r + (r = 4));\n"
"}",
27},
{"function f() {\n"
" var r = 7;\n"
" var s = 11;\n"
" var t = 13;\n"
" return (r + (r = 5)) > s ? r : t;\n"
"}",
5},
{"function f(a) {\n"
" return a + (arguments[0] = 10);\n"
"}",
50},
{"function f(a) {\n"
" return a + (arguments[0] = 10) + a;\n"
"}",
60},
{"function f(a) {\n"
" return a + (arguments[0] = 10) + arguments[0];\n"
"}",
60},
};
const int arg_value = 40;
for (size_t i = 0; i < arraysize(samples); i++) {
InterpreterTester tester(handles.main_isolate(), samples[i].first);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(handle(Smi::FromInt(arg_value), handles.main_isolate()))
.ToHandleChecked();
CHECK_EQ(Handle<Smi>::cast(return_val)->value(), samples[i].second);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -22,12 +22,12 @@ class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_locals_count(1);
builder.set_locals_count(2);
builder.set_context_count(1);
builder.set_parameter_count(0);
CHECK_EQ(builder.locals_count(), 1);
CHECK_EQ(builder.locals_count(), 2);
CHECK_EQ(builder.context_count(), 1);
CHECK_EQ(builder.fixed_register_count(), 2);
CHECK_EQ(builder.fixed_register_count(), 3);
// Emit constant loads.
builder.LoadLiteral(Smi::FromInt(0))
......@@ -46,6 +46,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.LoadNull()
.StoreAccumulatorInRegister(reg);
// Emit register-register transfer.
Register other(1);
builder.MoveRegister(reg, other);
// Emit global load / store operations.
builder.LoadGlobal(0, 1, LanguageMode::SLOPPY, TypeofMode::NOT_INSIDE_TYPEOF)
.LoadGlobal(0, 1, LanguageMode::STRICT, TypeofMode::NOT_INSIDE_TYPEOF)
......
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