From 0030805643c58dd534e772e8603857372e095de7 Mon Sep 17 00:00:00 2001 From: rmcilroy Date: Thu, 22 Oct 2015 13:40:20 -0700 Subject: [PATCH] [Interpreter] Add support for for count operations. Adds support for count operations to the interpreter. Deals with count operations on locals, globals, context allocated variables and named and keyed properties. Adds the following bytecodes: ToNumber Inc Dec BUG=v8:4280 LOG=N TBR=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/1416623003 Cr-Commit-Position: refs/heads/master@{#31484} --- src/compiler/bytecode-graph-builder.cc | 18 ++ src/interpreter/bytecode-array-builder.cc | 33 ++ src/interpreter/bytecode-array-builder.h | 5 + src/interpreter/bytecode-generator.cc | 109 ++++++- src/interpreter/bytecode-generator.h | 19 +- src/interpreter/bytecodes.h | 3 + src/interpreter/interpreter.cc | 37 +++ src/interpreter/interpreter.h | 4 + .../interpreter/test-bytecode-generator.cc | 289 ++++++++++++++++++ test/cctest/interpreter/test-interpreter.cc | 136 +++++++-- .../bytecode-array-builder-unittest.cc | 7 +- 11 files changed, 620 insertions(+), 40 deletions(-) diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc index 9df0f1e7af..0a544ed6f9 100644 --- a/src/compiler/bytecode-graph-builder.cc +++ b/src/compiler/bytecode-graph-builder.cc @@ -476,6 +476,18 @@ void BytecodeGraphBuilder::VisitShiftRightLogical( } +void BytecodeGraphBuilder::VisitInc( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + +void BytecodeGraphBuilder::VisitDec( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + void BytecodeGraphBuilder::VisitLogicalNot( const interpreter::BytecodeArrayIterator& iterator) { UNIMPLEMENTED(); @@ -560,6 +572,12 @@ void BytecodeGraphBuilder::VisitToName( } +void BytecodeGraphBuilder::VisitToNumber( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + void BytecodeGraphBuilder::VisitJump( const interpreter::BytecodeArrayIterator& iterator) { UNIMPLEMENTED(); diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc index a41d332a30..7fb5e949a1 100644 --- a/src/interpreter/bytecode-array-builder.cc +++ b/src/interpreter/bytecode-array-builder.cc @@ -172,6 +172,17 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op, } +BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op, + Strength strength) { + if (is_strong(strength)) { + UNIMPLEMENTED(); + } + + Output(BytecodeForCountOperation(op)); + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() { Output(Bytecode::kLogicalNot); return *this; @@ -465,6 +476,14 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() { } +BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToNumber() { + // TODO(rmcilroy): consider omitting if the preceeding bytecode always returns + // a number. + Output(Bytecode::kToNumber); + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) { if (label->is_forward_target()) { // An earlier jump instruction refers to this label. Update it's location. @@ -826,6 +845,20 @@ Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) { } +// static +Bytecode BytecodeArrayBuilder::BytecodeForCountOperation(Token::Value op) { + switch (op) { + case Token::Value::ADD: + return Bytecode::kInc; + case Token::Value::SUB: + return Bytecode::kDec; + default: + UNREACHABLE(); + return static_cast(-1); + } +} + + // static Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) { switch (op) { diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h index 1f0badb10e..e4a7778117 100644 --- a/src/interpreter/bytecode-array-builder.h +++ b/src/interpreter/bytecode-array-builder.h @@ -146,6 +146,9 @@ class BytecodeArrayBuilder { BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg, Strength strength); + // Count Operators (value stored in accumulator). + BytecodeArrayBuilder& CountOperation(Token::Value op, Strength strength); + // Unary Operators. BytecodeArrayBuilder& LogicalNot(); BytecodeArrayBuilder& TypeOf(); @@ -157,6 +160,7 @@ class BytecodeArrayBuilder { // Casts. BytecodeArrayBuilder& CastAccumulatorToBoolean(); BytecodeArrayBuilder& CastAccumulatorToName(); + BytecodeArrayBuilder& CastAccumulatorToNumber(); // Flow Control. BytecodeArrayBuilder& Bind(BytecodeLabel* label); @@ -186,6 +190,7 @@ class BytecodeArrayBuilder { Isolate* isolate() const { return isolate_; } static Bytecode BytecodeForBinaryOperation(Token::Value op); + static Bytecode BytecodeForCountOperation(Token::Value op); static Bytecode BytecodeForCompareOperation(Token::Value op); static Bytecode BytecodeForLoadIC(LanguageMode language_mode); static Bytecode BytecodeForKeyedLoadIC(LanguageMode language_mode); diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index b1cac30d36..ced5210bf6 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -960,6 +960,21 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable, } +void BytecodeGenerator::VisitVariableLoadForAccumulatorValue( + Variable* variable, FeedbackVectorSlot slot) { + AccumulatorResultScope accumulator_result(this); + VisitVariableLoad(variable, slot); +} + + +Register BytecodeGenerator::VisitVariableLoadForRegisterValue( + Variable* variable, FeedbackVectorSlot slot) { + RegisterResultScope register_scope(this); + VisitVariableLoad(variable, slot); + return register_scope.ResultRegister(); +} + + void BytecodeGenerator::VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot) { switch (variable->location()) { @@ -1174,10 +1189,8 @@ void BytecodeGenerator::VisitCall(Call* expr) { builder()->LoadUndefined().StoreAccumulatorInRegister(receiver); // Load callee as a global variable. VariableProxy* proxy = callee_expr->AsVariableProxy(); - // Result scope for VisitVariableLoad to avoid using our temporaries - // and double setting the result in our result_scope() and. - AccumulatorResultScope accumulator_execution_result(this); - VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot()); + VisitVariableLoadForAccumulatorValue(proxy->var(), + proxy->VariableFeedbackSlot()); builder()->StoreAccumulatorInRegister(callee); break; } @@ -1295,7 +1308,93 @@ void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { void BytecodeGenerator::VisitCountOperation(CountOperation* expr) { - UNIMPLEMENTED(); + DCHECK(expr->expression()->IsValidReferenceExpressionOrThis()); + + // Left-hand side can only be a property, a global or a variable slot. + Property* property = expr->expression()->AsProperty(); + LhsKind assign_type = Property::GetAssignType(property); + + // TODO(rmcilroy): Set is_postfix to false if visiting for effect. + bool is_postfix = expr->is_postfix(); + + // Evaluate LHS expression and get old value. + Register obj, key, old_value; + size_t name_index = kMaxUInt32; + switch (assign_type) { + case VARIABLE: { + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + VisitVariableLoadForAccumulatorValue(proxy->var(), + proxy->VariableFeedbackSlot()); + break; + } + case NAMED_PROPERTY: { + FeedbackVectorSlot slot = property->PropertyFeedbackSlot(); + obj = VisitForRegisterValue(property->obj()); + name_index = builder()->GetConstantPoolEntry( + property->key()->AsLiteral()->AsPropertyName()); + builder()->LoadNamedProperty(obj, name_index, feedback_index(slot), + language_mode()); + break; + } + case KEYED_PROPERTY: { + FeedbackVectorSlot slot = property->PropertyFeedbackSlot(); + obj = VisitForRegisterValue(property->obj()); + // Use visit for accumulator here since we need the key in the accumulator + // for the LoadKeyedProperty. + key = execution_result()->NewRegister(); + VisitForAccumulatorValue(property->key()); + builder()->StoreAccumulatorInRegister(key).LoadKeyedProperty( + obj, feedback_index(slot), language_mode()); + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNIMPLEMENTED(); + } + + // Convert old value into a number. + if (!is_strong(language_mode())) { + builder()->CastAccumulatorToNumber(); + } + + // Save result for postfix expressions. + if (is_postfix) { + old_value = execution_result()->NewRegister(); + builder()->StoreAccumulatorInRegister(old_value); + } + + // Perform +1/-1 operation. + builder()->CountOperation(expr->binary_op(), language_mode_strength()); + + // Store the value. + FeedbackVectorSlot feedback_slot = expr->CountSlot(); + switch (assign_type) { + case VARIABLE: { + Variable* variable = expr->expression()->AsVariableProxy()->var(); + VisitVariableAssignment(variable, feedback_slot); + break; + } + case NAMED_PROPERTY: { + builder()->StoreNamedProperty( + obj, name_index, feedback_index(feedback_slot), language_mode()); + break; + } + case KEYED_PROPERTY: { + builder()->StoreKeyedProperty(obj, key, feedback_index(feedback_slot), + language_mode()); + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNIMPLEMENTED(); + } + + // Restore old value for postfix expressions. + if (is_postfix) { + execution_result()->SetResultInRegister(old_value); + } else { + execution_result()->SetResultInAccumulator(); + } } diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h index 5269b74d1c..2d4b18c36d 100644 --- a/src/interpreter/bytecode-generator.h +++ b/src/interpreter/bytecode-generator.h @@ -41,17 +41,28 @@ class BytecodeGenerator : public AstVisitor { DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); - Register VisitArguments(ZoneList* arguments); - + // Dispatched from VisitBinaryOperation. void VisitArithmeticExpression(BinaryOperation* binop); void VisitCommaExpression(BinaryOperation* binop); void VisitLogicalOrExpression(BinaryOperation* binop); void VisitLogicalAndExpression(BinaryOperation* binop); + // Dispatched from VisitUnaryOperation. + void VisitVoid(UnaryOperation* expr); + void VisitTypeOf(UnaryOperation* expr); + void VisitNot(UnaryOperation* expr); + + // Helper visitors which perform common operations. + Register VisitArguments(ZoneList* arguments); + void VisitPropertyLoad(Register obj, Property* expr); void VisitPropertyLoadForAccumulator(Register obj, Property* expr); void VisitVariableLoad(Variable* variable, FeedbackVectorSlot slot); + void VisitVariableLoadForAccumulatorValue(Variable* variable, + FeedbackVectorSlot slot); + MUST_USE_RESULT Register VisitVariableLoadForRegisterValue( + Variable* variable, FeedbackVectorSlot slot); void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot); void VisitNewLocalFunctionContext(); @@ -64,10 +75,6 @@ class BytecodeGenerator : public AstVisitor { ObjectLiteralProperty* property, Register value_out); - // Dispatched from VisitUnaryOperation. - void VisitVoid(UnaryOperation* expr); - void VisitTypeOf(UnaryOperation* expr); - void VisitNot(UnaryOperation* expr); // Visitors for obtaining expression result in the accumulator, in a // register, or just getting the effect. diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index c113df8bca..263e0ce490 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -87,6 +87,8 @@ namespace interpreter { V(ShiftRightLogical, OperandType::kReg8) \ \ /* Unary Operators */ \ + V(Inc, OperandType::kNone) \ + V(Dec, OperandType::kNone) \ V(LogicalNot, OperandType::kNone) \ V(TypeOf, OperandType::kNone) \ \ @@ -113,6 +115,7 @@ namespace interpreter { /* Cast operators */ \ V(ToBoolean, OperandType::kNone) \ V(ToName, OperandType::kNone) \ + V(ToNumber, OperandType::kNone) \ \ /* Literals */ \ V(CreateRegExpLiteral, OperandType::kIdx8, OperandType::kReg8) \ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 7f9bf81298..5778358196 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -591,6 +591,32 @@ void Interpreter::DoShiftRightLogical( } +void Interpreter::DoCountOp(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler) { + Node* value = __ GetAccumulator(); + Node* one = __ NumberConstant(1); + Node* result = __ CallRuntime(function_id, value, one); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// Inc +// +// Increments value in the accumulator by one. +void Interpreter::DoInc(compiler::InterpreterAssembler* assembler) { + DoCountOp(Runtime::kAdd, assembler); +} + + +// Dec +// +// Decrements value in the accumulator by one. +void Interpreter::DoDec(compiler::InterpreterAssembler* assembler) { + DoCountOp(Runtime::kSubtract, assembler); +} + + // LogicalNot // // Perform logical-not on the accumulator, first casting the @@ -776,6 +802,17 @@ void Interpreter::DoToName(compiler::InterpreterAssembler* assembler) { } +// ToNumber +// +// Cast the object referenced by the accumulator to a number. +void Interpreter::DoToNumber(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kToNumber, accumulator); + __ SetAccumulator(result); + __ Dispatch(); +} + + // Jump // // Jump by number of bytes represented by the immediate operand |imm8|. diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index cb68bf671b..6bd15078be 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -54,6 +54,10 @@ class Interpreter { void DoBinaryOp(Runtime::FunctionId function_id, compiler::InterpreterAssembler* assembler); + // Generates code to perform the count operations via |function_id|. + void DoCountOp(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler); + // Generates code to perform the comparison operation associated with // |compare_op|. void DoCompareOp(Token::Value compare_op, diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc index 17a3766a12..65c7ec350a 100644 --- a/test/cctest/interpreter/test-bytecode-generator.cc +++ b/test/cctest/interpreter/test-bytecode-generator.cc @@ -2916,6 +2916,295 @@ TEST(ContextParameters) { } } + +TEST(CountOperators) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); + + Handle vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + int closure = Register::function_closure().index(); + int first_context_slot = Context::MIN_CONTEXT_SLOTS; + + int object_literal_flags = + ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos; + + ExpectedSnippet snippets[] = { + {"var a = 1; return ++a;", + 1 * kPointerSize, + 1, + 11, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(ToNumber), // + B(Inc), // + B(Star), R(0), // + B(Return), // + }}, + {"var a = 1; return a++;", + 2 * kPointerSize, + 1, + 15, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(ToNumber), // + B(Star), R(1), // + B(Inc), // + B(Star), R(0), // + B(Ldar), R(1), // + B(Return), // + }}, + {"var a = 1; return --a;", + 1 * kPointerSize, + 1, + 11, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(ToNumber), // + B(Dec), // + B(Star), R(0), // + B(Return), // + }}, + {"var a = 1; return a--;", + 2 * kPointerSize, + 1, + 15, + { + B(LdaSmi8), U8(1), // + B(Star), R(0), // + B(Ldar), R(0), // + B(ToNumber), // + B(Star), R(1), // + B(Dec), // + B(Star), R(0), // + B(Ldar), R(1), // + B(Return), // + }}, + {"var a = { val: 1 }; return a.val++;", + 2 * kPointerSize, + 1, + 22, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(0), // + B(LoadICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Star), R(1), // + B(Inc), // + B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot2)), // + B(Ldar), R(1), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var a = { val: 1 }; return --a.val;", + 1 * kPointerSize, + 1, + 18, + { + B(LdaConstant), U8(0), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(0), // + B(LoadICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Dec), // + B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 2, + {InstanceType::FIXED_ARRAY_TYPE, + InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}, + {"var name = 'var'; var a = { val: 1 }; return a[name]--;", + 4 * kPointerSize, + 1, + 29, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(Star), R(2), // + B(KeyedLoadICSloppy), R(1), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Star), R(3), // + B(Dec), // + B(KeyedStoreICSloppy), R(1), R(2), U8(vector->GetIndex(slot2)), // + B(Ldar), R(3), // + B(Return), // + }, + 2, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE}}, + {"var name = 'var'; var a = { val: 1 }; return ++a[name];", + 3 * kPointerSize, + 1, + 25, + { + B(LdaConstant), U8(0), // + B(Star), R(0), // + B(LdaConstant), U8(1), // + B(CreateObjectLiteral), U8(0), U8(object_literal_flags), // + B(Star), R(1), // + B(Ldar), R(0), // + B(Star), R(2), // + B(KeyedLoadICSloppy), R(1), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Inc), // + B(KeyedStoreICSloppy), R(1), R(2), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 2, + {InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE, + InstanceType::FIXED_ARRAY_TYPE}}, + {"var a = 1; var b = function() { return a }; return ++a;", + 2 * kPointerSize, + 1, + 27, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), // + U8(1), // + B(PushContext), R(1), // + B(LdaSmi8), U8(1), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(LdaContextSlot), R(1), U8(first_context_slot), // + B(ToNumber), // + B(Inc), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"var a = 1; var b = function() { return a }; return a--;", + 3 * kPointerSize, + 1, + 31, + { + B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), // + U8(1), // + B(PushContext), R(1), // + B(LdaSmi8), U8(1), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(LdaContextSlot), R(1), U8(first_context_slot), // + B(ToNumber), // + B(Star), R(2), // + B(Dec), // + B(StaContextSlot), R(1), U8(first_context_slot), // + B(Ldar), R(2), // + B(Return), // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(GlobalCountOperators) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + Zone zone; + + FeedbackVectorSpec feedback_spec(&zone); + FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot(); + FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot(); + + Handle vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet snippets[] = { + {"var global = 1;\nfunction f() { return ++global; }\nf()", + 0, + 1, + 9, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Inc), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 1, + {"global"}}, + {"var global = 1;\nfunction f() { return global--; }\nf()", + 1 * kPointerSize, + 1, + 13, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Star), R(0), // + B(Dec), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Ldar), R(0), // + B(Return), + }, + 1, + {"global"}}, + {"unallocated = 1;\nfunction f() { 'use strict'; return --unallocated; }" + "f()", + 0, + 1, + 9, + { + B(LdaGlobalStrict), U8(0), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Dec), // + B(StaGlobalStrict), U8(0), U8(vector->GetIndex(slot2)), // + B(Return), // + }, + 1, + {"unallocated"}}, + {"unallocated = 1;\nfunction f() { return unallocated++; }\nf()", + 1 * kPointerSize, + 1, + 13, + { + B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), // + B(ToNumber), // + B(Star), R(0), // + B(Inc), // + B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), // + B(Ldar), R(0), // + B(Return), + }, + 1, + {"unallocated"}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array, true); + } +} + } // namespace interpreter } // namespace internal } // namespace v8 diff --git a/test/cctest/interpreter/test-interpreter.cc b/test/cctest/interpreter/test-interpreter.cc index 8128be5ddf..63df7f7369 100644 --- a/test/cctest/interpreter/test-interpreter.cc +++ b/test/cctest/interpreter/test-interpreter.cc @@ -1724,11 +1724,11 @@ TEST(InterpreterArrayLiterals) { std::make_pair("return [][0];\n", factory->undefined_value()), std::make_pair("return [1, 3, 2][1];\n", - Handle(Smi::FromInt(3), isolate)), + handle(Smi::FromInt(3), isolate)), std::make_pair("return ['a', 'b', 'c'][2];\n", factory->NewStringFromStaticChars("c")), std::make_pair("var a = 100; return [a, a + 1, a + 2, a + 3][2];\n", - Handle(Smi::FromInt(102), isolate)), + handle(Smi::FromInt(102), isolate)), std::make_pair("return [[1, 2, 3], ['a', 'b', 'c']][1][0];\n", factory->NewStringFromStaticChars("a")), std::make_pair("var t = 't'; return [[t, t + 'est'], [1 + t]][0][1];\n", @@ -1757,26 +1757,26 @@ TEST(InterpreterObjectLiterals) { std::make_pair("return { name: 'string', val: 9.2 }.name;", factory->NewStringFromStaticChars("string")), std::make_pair("var a = 15; return { name: 'string', val: a }.val;", - Handle(Smi::FromInt(15), isolate)), + handle(Smi::FromInt(15), isolate)), std::make_pair("var a = 5; return { val: a, val: a + 1 }.val;", - Handle(Smi::FromInt(6), isolate)), + handle(Smi::FromInt(6), isolate)), std::make_pair("return { func: function() { return 'test' } }.func();", factory->NewStringFromStaticChars("test")), std::make_pair("return { func(a) { return a + 'st'; } }.func('te');", factory->NewStringFromStaticChars("test")), std::make_pair("return { get a() { return 22; } }.a;", - Handle(Smi::FromInt(22), isolate)), + handle(Smi::FromInt(22), isolate)), std::make_pair("var a = { get b() { return this.x + 't'; },\n" " set b(val) { this.x = val + 's' } };\n" "a.b = 'te';\n" "return a.b;", factory->NewStringFromStaticChars("test")), std::make_pair("var a = 123; return { 1: a }[1];", - Handle(Smi::FromInt(123), isolate)), + handle(Smi::FromInt(123), isolate)), std::make_pair("return Object.getPrototypeOf({ __proto__: null });", factory->null_value()), std::make_pair("var a = 'test'; return { [a]: 1 }.test;", - Handle(Smi::FromInt(1), isolate)), + handle(Smi::FromInt(1), isolate)), std::make_pair("var a = 'test'; return { b: a, [a]: a + 'ing' }['test']", factory->NewStringFromStaticChars("testing")), std::make_pair("var a = 'proto_str';\n" @@ -1785,7 +1785,7 @@ TEST(InterpreterObjectLiterals) { factory->NewStringFromStaticChars("proto_str")), std::make_pair("var n = 'name';\n" "return { [n]: 'val', get a() { return 987 } }['a'];", - Handle(Smi::FromInt(987), isolate)), + handle(Smi::FromInt(987), isolate)), }; for (size_t i = 0; i < arraysize(literals); i++) { @@ -1864,19 +1864,19 @@ TEST(InterpreterContextVariables) { std::pair> context_vars[5] = { std::make_pair("var a; (function() { a = 1; })(); return a;", - Handle(Smi::FromInt(1), isolate)), + handle(Smi::FromInt(1), isolate)), std::make_pair("var a = 10; (function() { a; })(); return a;", - Handle(Smi::FromInt(10), isolate)), + handle(Smi::FromInt(10), isolate)), std::make_pair("var a = 20; var b = 30;\n" "return (function() { return a + b; })();", - Handle(Smi::FromInt(50), isolate)), + handle(Smi::FromInt(50), isolate)), std::make_pair("'use strict'; let a = 1;\n" "{ let b = 2; return (function() { return a + b; })(); }", - Handle(Smi::FromInt(3), isolate)), + handle(Smi::FromInt(3), isolate)), std::make_pair("'use strict'; let a = 10;\n" "{ let b = 20; var c = function() { [a, b] };\n" " return a + b; }", - Handle(Smi::FromInt(30), isolate)), + handle(Smi::FromInt(30), isolate)), }; for (size_t i = 0; i < arraysize(context_vars); i++) { @@ -1896,11 +1896,11 @@ TEST(InterpreterContextParameters) { std::pair> context_params[3] = { std::make_pair("return (function() { return arg1; })();", - Handle(Smi::FromInt(1), isolate)), + handle(Smi::FromInt(1), isolate)), std::make_pair("(function() { arg1 = 4; })(); return arg1;", - Handle(Smi::FromInt(4), isolate)), + handle(Smi::FromInt(4), isolate)), std::make_pair("(function() { arg3 = arg2 - arg1; })(); return arg3;", - Handle(Smi::FromInt(1), isolate)), + handle(Smi::FromInt(1), isolate)), }; for (size_t i = 0; i < arraysize(context_params); i++) { @@ -1910,9 +1910,9 @@ TEST(InterpreterContextParameters) { auto callable = tester.GetCallable, Handle, Handle>(); - Handle a1 = Handle(Smi::FromInt(1), isolate); - Handle a2 = Handle(Smi::FromInt(2), isolate); - Handle a3 = Handle(Smi::FromInt(3), isolate); + Handle a1 = handle(Smi::FromInt(1), isolate); + Handle a2 = handle(Smi::FromInt(2), isolate); + Handle a3 = handle(Smi::FromInt(3), isolate); Handle return_value = callable(a1, a2, a3).ToHandleChecked(); CHECK(return_value->SameValue(*context_params[i].second)); } @@ -1927,14 +1927,14 @@ TEST(InterpreterComma) { std::pair> literals[6] = { std::make_pair("var a; return 0, a;\n", factory->undefined_value()), std::make_pair("return 'a', 2.2, 3;\n", - Handle(Smi::FromInt(3), isolate)), + handle(Smi::FromInt(3), isolate)), std::make_pair("return 'a', 'b', 'c';\n", factory->NewStringFromStaticChars("c")), std::make_pair("return 3.2, 2.3, 4.5;\n", factory->NewNumber(4.5)), std::make_pair("var a = 10; return b = a, b = b+1;\n", - Handle(Smi::FromInt(11), isolate)), + handle(Smi::FromInt(11), isolate)), std::make_pair("var a = 10; return b = a, b = b+1, b + 10;\n", - Handle(Smi::FromInt(21), isolate))}; + handle(Smi::FromInt(21), isolate))}; for (size_t i = 0; i < arraysize(literals); i++) { std::string source(InterpreterTester::SourceForBody(literals[i].first)); @@ -1955,7 +1955,7 @@ TEST(InterpreterLogicalOr) { std::pair> literals[5] = { std::make_pair("var a, b; return a || b;\n", factory->undefined_value()), std::make_pair("var a, b = 10; return a || b;\n", - Handle(Smi::FromInt(10), isolate)), + handle(Smi::FromInt(10), isolate)), std::make_pair("var a = '0', b = 10; return a || b;\n", factory->NewStringFromStaticChars("0")), std::make_pair("return 0 || 3.2;\n", factory->NewNumber(3.2)), @@ -1982,17 +1982,17 @@ TEST(InterpreterLogicalAnd) { std::make_pair("var a, b = 10; return a && b;\n", factory->undefined_value()), std::make_pair("var a = 0, b = 10; return a && b / a;\n", - Handle(Smi::FromInt(0), isolate)), + handle(Smi::FromInt(0), isolate)), std::make_pair("var a = '0', b = 10; return a && b;\n", - Handle(Smi::FromInt(10), isolate)), + handle(Smi::FromInt(10), isolate)), std::make_pair("return 0.0 && 3.2;\n", - Handle(Smi::FromInt(0), isolate)), + handle(Smi::FromInt(0), isolate)), std::make_pair("return 'a' && 'b';\n", factory->NewStringFromStaticChars("b")), std::make_pair("return 'a' && 0 || 'b', 'c';\n", factory->NewStringFromStaticChars("c")), std::make_pair("var x = 1, y = 3; return x && 0 + 1 || y;\n", - Handle(Smi::FromInt(1), isolate))}; + handle(Smi::FromInt(1), isolate))}; for (size_t i = 0; i < arraysize(literals); i++) { std::string source(InterpreterTester::SourceForBody(literals[i].first)); @@ -2043,7 +2043,7 @@ TEST(InterpreterThrow) { std::make_pair("throw undefined;\n", factory->undefined_value()), std::make_pair("throw 1;\n", - Handle(Smi::FromInt(1), isolate)), + handle(Smi::FromInt(1), isolate)), std::make_pair("throw 'Error';\n", factory->NewStringFromStaticChars("Error")), std::make_pair("var a = true; if (a) { throw 'Error'; }\n", @@ -2065,3 +2065,83 @@ TEST(InterpreterThrow) { CHECK(thrown_obj->SameValue(*throws[i].second)); } } + + +TEST(InterpreterCountOperators) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + i::Factory* factory = isolate->factory(); + + std::pair> count_ops[16] = { + std::make_pair("var a = 1; return ++a;", + handle(Smi::FromInt(2), isolate)), + std::make_pair("var a = 1; return a++;", + handle(Smi::FromInt(1), isolate)), + std::make_pair("var a = 5; return --a;", + handle(Smi::FromInt(4), isolate)), + std::make_pair("var a = 5; return a--;", + handle(Smi::FromInt(5), isolate)), + std::make_pair("var a = 5.2; return --a;", + factory->NewHeapNumber(4.2)), + std::make_pair("var a = 'string'; return ++a;", + factory->nan_value()), + std::make_pair("var a = 'string'; return a--;", + factory->nan_value()), + std::make_pair("var a = true; return ++a;", + handle(Smi::FromInt(2), isolate)), + std::make_pair("var a = false; return a--;", + handle(Smi::FromInt(0), isolate)), + std::make_pair("var a = { val: 11 }; return ++a.val;", + handle(Smi::FromInt(12), isolate)), + std::make_pair("var a = { val: 11 }; return a.val--;", + handle(Smi::FromInt(11), isolate)), + std::make_pair("var a = { val: 11 }; return ++a.val;", + handle(Smi::FromInt(12), isolate)), + std::make_pair("var name = 'val'; var a = { val: 22 }; return --a[name];", + handle(Smi::FromInt(21), isolate)), + std::make_pair("var name = 'val'; var a = { val: 22 }; return a[name]++;", + handle(Smi::FromInt(22), isolate)), + std::make_pair("var a = 1; (function() { a = 2 })(); return ++a;", + handle(Smi::FromInt(3), isolate)), + std::make_pair("var a = 1; (function() { a = 2 })(); return a--;", + handle(Smi::FromInt(2), isolate)), + }; + + for (size_t i = 0; i < arraysize(count_ops); i++) { + std::string source(InterpreterTester::SourceForBody(count_ops[i].first)); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*count_ops[i].second)); + } +} + + +TEST(InterpreterGlobalCountOperators) { + HandleAndZoneScope handles; + i::Isolate* isolate = handles.main_isolate(); + + std::pair> count_ops[6] = { + std::make_pair("var global = 100;function f(){ return ++global; }", + handle(Smi::FromInt(101), isolate)), + std::make_pair("var global = 100; function f(){ return --global; }", + handle(Smi::FromInt(99), isolate)), + std::make_pair("var global = 100; function f(){ return global++; }", + handle(Smi::FromInt(100), isolate)), + std::make_pair("unallocated = 200; function f(){ return ++unallocated; }", + handle(Smi::FromInt(201), isolate)), + std::make_pair("unallocated = 200; function f(){ return --unallocated; }", + handle(Smi::FromInt(199), isolate)), + std::make_pair("unallocated = 200; function f(){ return unallocated++; }", + handle(Smi::FromInt(200), isolate)), + }; + + for (size_t i = 0; i < arraysize(count_ops); i++) { + InterpreterTester tester(handles.main_isolate(), count_ops[i].first); + auto callable = tester.GetCallable<>(); + + Handle return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*count_ops[i].second)); + } +} diff --git a/test/unittests/interpreter/bytecode-array-builder-unittest.cc b/test/unittests/interpreter/bytecode-array-builder-unittest.cc index 6bd2d943eb..7d3601a11c 100644 --- a/test/unittests/interpreter/bytecode-array-builder-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-builder-unittest.cc @@ -94,9 +94,14 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { .BinaryOperation(Token::Value::SAR, reg, Strength::WEAK) .BinaryOperation(Token::Value::SHR, reg, Strength::WEAK); + // Emit count operatior invocations + builder.CountOperation(Token::Value::ADD, Strength::WEAK) + .CountOperation(Token::Value::SUB, Strength::WEAK); + // Emit unary operator invocations. builder.LogicalNot().TypeOf(); + // Emit new. builder.New(reg, reg, 0); @@ -113,7 +118,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { .CompareOperation(Token::Value::IN, reg, Strength::WEAK); // Emit cast operator invocations. - builder.LoadNull() + builder.CastAccumulatorToNumber() .CastAccumulatorToBoolean() .CastAccumulatorToName();