Commit 565f0d73 authored by oth's avatar oth Committed by Commit bot

[Interpreter] Unary operators - typeof, void, and logical not.

Implementations and tests for typeof, void, and logical not.

Add missing string type to Object::TypeOf.

BUG=v8:4280
LOG=NO

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

Cr-Commit-Position: refs/heads/master@{#31124}
parent 6ff9516b
......@@ -338,6 +338,18 @@ void BytecodeGraphBuilder::VisitMod(
}
void BytecodeGraphBuilder::VisitLogicalNot(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitTypeOf(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitTestEqual(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
......
......@@ -134,6 +134,18 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op,
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() {
Output(Bytecode::kLogicalNot);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() {
Output(Bytecode::kTypeOf);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(
Token::Value op, Register reg, LanguageMode language_mode) {
if (!is_sloppy(language_mode)) {
......@@ -299,6 +311,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() {
UNREACHABLE();
case Bytecode::kLdaTrue:
case Bytecode::kLdaFalse:
case Bytecode::kLogicalNot:
case Bytecode::kTestEqual:
case Bytecode::kTestNotEqual:
case Bytecode::kTestEqualStrict:
......
......@@ -84,6 +84,10 @@ class BytecodeArrayBuilder {
// Operators (register == lhs, accumulator = rhs).
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
// Unary Operators.
BytecodeArrayBuilder& LogicalNot();
BytecodeArrayBuilder& TypeOf();
// Tests.
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
LanguageMode language_mode);
......
......@@ -619,8 +619,41 @@ void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
}
void BytecodeGenerator::VisitVoid(UnaryOperation* expr) {
Visit(expr->expression());
builder()->LoadUndefined();
}
void BytecodeGenerator::VisitTypeOf(UnaryOperation* expr) {
Visit(expr->expression());
builder()->TypeOf();
}
void BytecodeGenerator::VisitNot(UnaryOperation* expr) {
Visit(expr->expression());
builder()->LogicalNot();
}
void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
UNIMPLEMENTED();
switch (expr->op()) {
case Token::Value::NOT:
VisitNot(expr);
break;
case Token::Value::TYPEOF:
VisitTypeOf(expr);
break;
case Token::Value::VOID:
VisitVoid(expr);
break;
case Token::Value::BIT_NOT:
case Token::Value::DELETE:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
}
......
......@@ -34,6 +34,11 @@ class BytecodeGenerator : public AstVisitor {
void VisitPropertyLoad(Register obj, Property* expr);
void VisitVariableLoad(Variable* variable);
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
void VisitTypeOf(UnaryOperation* expr);
void VisitNot(UnaryOperation* expr);
inline BytecodeArrayBuilder* builder() { return &builder_; }
inline Scope* scope() const { return scope_; }
inline void set_scope(Scope* scope) { scope_ = scope; }
......
......@@ -65,6 +65,10 @@ namespace interpreter {
V(Div, OperandType::kReg8) \
V(Mod, OperandType::kReg8) \
\
/* Unary Operators */ \
V(LogicalNot, OperandType::kNone) \
V(TypeOf, OperandType::kNone) \
\
/* Call operations. */ \
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
V(CallRuntime, OperandType::kIdx16, OperandType::kReg8, \
......
......@@ -334,10 +334,34 @@ void Interpreter::DoMod(compiler::InterpreterAssembler* assembler) {
}
// LogicalNot
//
// Perform logical-not on the accumulator, first casting the
// accumulator to a boolean value if required.
void Interpreter::DoLogicalNot(compiler::InterpreterAssembler* assembler) {
Node* accumulator = __ GetAccumulator();
Node* result = __ CallRuntime(Runtime::kInterpreterLogicalNot, accumulator);
__ SetAccumulator(result);
__ Dispatch();
}
// TypeOf
//
// Load the accumulator with the string representating type of the
// object in the accumulator.
void Interpreter::DoTypeOf(compiler::InterpreterAssembler* assembler) {
Node* accumulator = __ GetAccumulator();
Node* result = __ CallRuntime(Runtime::kInterpreterTypeOf, accumulator);
__ SetAccumulator(result);
__ Dispatch();
}
// Call <callable> <receiver> <arg_count>
//
// Call a JSfunction or Callable in |callable| with receiver and |arg_count|
// arguments in subsequent registers.
// Call a JSfunction or Callable in |callable| with the |receiver| and
// |arg_count| arguments in subsequent registers.
void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
Node* function_reg = __ BytecodeOperandReg8(0);
Node* function = __ LoadRegister(function_reg);
......@@ -352,8 +376,9 @@ void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
// CallRuntime <function_id> <first_arg> <arg_count>
//
// Call the runtime function |function_id| with first argument in register
// |first_arg| and |arg_count| arguments in subsequent registers.
// Call the runtime function |function_id| with the first argument in
// register |first_arg| and |arg_count| arguments in subsequent
// registers.
void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) {
Node* function_id = __ BytecodeOperandIdx16(0);
Node* first_arg_reg = __ BytecodeOperandReg8(1);
......@@ -457,15 +482,16 @@ void Interpreter::DoTestInstanceOf(compiler::InterpreterAssembler* assembler) {
//
// Cast the object referenced by the accumulator to a boolean.
void Interpreter::DoToBoolean(compiler::InterpreterAssembler* assembler) {
// TODO(oth): The next CL for test operations has interpreter specific
// runtime calls. This looks like another candidate.
Node* accumulator = __ GetAccumulator();
Node* result = __ CallRuntime(Runtime::kInterpreterToBoolean, accumulator);
__ SetAccumulator(result);
__ Dispatch();
}
// Jump <imm8>
//
// Jump by number of bytes represented by an immediate operand.
// Jump by number of bytes represented by the immediate operand |imm8|.
void Interpreter::DoJump(compiler::InterpreterAssembler* assembler) {
Node* relative_jump = __ BytecodeOperandImm8(0);
__ Jump(relative_jump);
......@@ -539,7 +565,7 @@ void Interpreter::DoJumpIfFalseConstant(
// Return
//
// Return the value in register 0.
// Return the value in the accumulator.
void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) {
__ Return();
}
......
......@@ -381,6 +381,7 @@ Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) {
return isolate->factory()->undefined_string();
}
if (object->IsBoolean()) return isolate->factory()->boolean_string();
if (object->IsString()) return isolate->factory()->string_string();
if (object->IsSymbol()) return isolate->factory()->symbol_string();
#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \
if (object->Is##Type()) return isolate->factory()->type##_string();
......
......@@ -96,7 +96,7 @@ RUNTIME_FUNCTION(Runtime_InterpreterGreaterThanOrEqual) {
RUNTIME_FUNCTION(Runtime_InterpreterStrictEquals) {
SealHandleScope scope(isolate);
SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_CHECKED(Object, x, 0);
CONVERT_ARG_CHECKED(Object, y, 1);
......@@ -105,7 +105,7 @@ RUNTIME_FUNCTION(Runtime_InterpreterStrictEquals) {
RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
SealHandleScope scope(isolate);
SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_CHECKED(Object, x, 0);
CONVERT_ARG_CHECKED(Object, y, 1);
......@@ -114,12 +114,28 @@ RUNTIME_FUNCTION(Runtime_InterpreterStrictNotEquals) {
RUNTIME_FUNCTION(Runtime_InterpreterToBoolean) {
SealHandleScope scope(isolate);
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(Object, x, 0);
return isolate->heap()->ToBoolean(x->BooleanValue());
}
RUNTIME_FUNCTION(Runtime_InterpreterLogicalNot) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(Object, x, 0);
return isolate->heap()->ToBoolean(!x->BooleanValue());
}
RUNTIME_FUNCTION(Runtime_InterpreterTypeOf) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, x, 0);
return Object::cast(*Object::TypeOf(isolate, x));
}
} // namespace internal
} // namespace v8
......@@ -225,7 +225,9 @@ namespace internal {
F(InterpreterGreaterThan, 2, 1) \
F(InterpreterLessThanOrEqual, 2, 1) \
F(InterpreterGreaterThanOrEqual, 2, 1) \
F(InterpreterToBoolean, 1, 1)
F(InterpreterToBoolean, 1, 1) \
F(InterpreterLogicalNot, 1, 1) \
F(InterpreterTypeOf, 1, 1)
#define FOR_EACH_INTRINSIC_FUNCTION(F) \
......
......@@ -1260,6 +1260,124 @@ TEST(BasicLoops) {
}
}
TEST(UnaryOperators) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<int> snippets[] = {
{"var x = 0;"
"while (x != 10) {"
" x = x + 10;"
"}"
"return x;",
2 * kPointerSize,
1,
29,
{
B(LdaZero), //
B(Star), R(0), //
B(Jump), U8(12), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(10), //
B(Add), R(1), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(10), //
B(TestEqual), R(1), //
B(LogicalNot), //
B(JumpIfTrue), U8(-19), //
B(Ldar), R(0), //
B(Return), //
},
0},
{"var x = false;"
"do {"
" x = !x;"
"} while(x == false);"
"return x;",
2 * kPointerSize,
1,
20,
{
B(LdaFalse), //
B(Star), R(0), //
B(Ldar), R(0), //
B(LogicalNot), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaFalse), //
B(TestEqual), R(1), //
B(JumpIfTrue), U8(-12), //
B(Ldar), R(0), //
B(Return), //
},
0},
{"var x = 101;"
"return void(x * 3);",
2 * kPointerSize,
1,
14,
{
B(LdaSmi8), U8(101), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Star), R(1), //
B(LdaSmi8), U8(3), //
B(Mul), R(1), //
B(LdaUndefined), //
B(Return), //
},
0},
{"var x = 1234;"
"var y = void (x * x - 1);"
"return y;",
4 * kPointerSize,
1,
24,
{
B(LdaConstant), U8(0), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Star), R(3), //
B(Ldar), R(0), //
B(Mul), R(3), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Sub), R(2), //
B(LdaUndefined), //
B(Star), R(1), //
B(Ldar), R(1), //
B(Return), //
},
1,
{1234}},
{"var x = 13;"
"return typeof(x);",
1 * kPointerSize,
1,
8,
{
B(LdaSmi8), U8(13), //
B(Star), R(0), //
B(Ldar), R(0), //
B(TypeOf), //
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
......@@ -1291,6 +1291,164 @@ TEST(InterpreterTestIn) {
}
TEST(InterpreterUnaryNot) {
HandleAndZoneScope handles;
for (size_t i = 1; i < 10; i++) {
bool expected_value = ((i & 1) == 1);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
Register r0(0);
builder.set_locals_count(0);
builder.set_parameter_count(0);
builder.EnterBlock();
builder.LoadFalse();
for (size_t j = 0; j < i; j++) {
builder.LogicalNot();
}
builder.LeaveBlock().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(), expected_value);
}
}
static void LoadAny(BytecodeArrayBuilder* builder,
v8::internal::Factory* factory, Handle<Object> obj) {
if (obj->IsOddball()) {
if (obj->SameValue(*factory->true_value())) {
builder->LoadTrue();
} else if (obj->SameValue(*factory->false_value())) {
builder->LoadFalse();
} else if (obj->SameValue(*factory->the_hole_value())) {
builder->LoadTheHole();
} else if (obj->SameValue(*factory->null_value())) {
builder->LoadNull();
} else if (obj->SameValue(*factory->undefined_value())) {
builder->LoadUndefined();
} else {
UNREACHABLE();
}
} else if (obj->IsSmi()) {
builder->LoadLiteral(*Handle<Smi>::cast(obj));
} else {
builder->LoadLiteral(obj);
}
}
TEST(InterpreterToBoolean) {
HandleAndZoneScope handles;
i::Factory* factory = handles.main_isolate()->factory();
std::pair<Handle<Object>, bool> object_type_tuples[] = {
std::make_pair(factory->undefined_value(), false),
std::make_pair(factory->null_value(), false),
std::make_pair(factory->false_value(), false),
std::make_pair(factory->true_value(), true),
std::make_pair(factory->NewNumber(9.1), true),
std::make_pair(factory->NewNumberFromInt(0), false),
std::make_pair(
Handle<Object>::cast(factory->NewStringFromStaticChars("hello")),
true),
std::make_pair(
Handle<Object>::cast(factory->NewStringFromStaticChars("")), false),
};
for (size_t i = 0; i < arraysize(object_type_tuples); i++) {
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
Register r0(0);
builder.set_locals_count(0);
builder.set_parameter_count(0);
builder.EnterBlock();
LoadAny(&builder, factory, object_type_tuples[i].first);
builder.CastAccumulatorToBoolean();
builder.LeaveBlock().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(), object_type_tuples[i].second);
}
}
TEST(InterpreterUnaryNotNonBoolean) {
HandleAndZoneScope handles;
i::Factory* factory = handles.main_isolate()->factory();
std::pair<Handle<Object>, bool> object_type_tuples[] = {
std::make_pair(factory->undefined_value(), true),
std::make_pair(factory->null_value(), true),
std::make_pair(factory->false_value(), true),
std::make_pair(factory->true_value(), false),
std::make_pair(factory->NewNumber(9.1), false),
std::make_pair(factory->NewNumberFromInt(0), true),
std::make_pair(
Handle<Object>::cast(factory->NewStringFromStaticChars("hello")),
false),
std::make_pair(
Handle<Object>::cast(factory->NewStringFromStaticChars("")), true),
};
for (size_t i = 0; i < arraysize(object_type_tuples); i++) {
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
Register r0(0);
builder.set_locals_count(0);
builder.set_parameter_count(0);
builder.EnterBlock();
LoadAny(&builder, factory, object_type_tuples[i].first);
builder.LogicalNot();
builder.LeaveBlock().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(), object_type_tuples[i].second);
}
}
TEST(InterpreterTypeOf) {
HandleAndZoneScope handles;
i::Factory* factory = handles.main_isolate()->factory();
std::pair<Handle<Object>, const char*> object_type_tuples[] = {
std::make_pair(factory->undefined_value(), "undefined"),
std::make_pair(factory->null_value(), "object"),
std::make_pair(factory->true_value(), "boolean"),
std::make_pair(factory->false_value(), "boolean"),
std::make_pair(factory->NewNumber(9.1), "number"),
std::make_pair(factory->NewNumberFromInt(7771), "number"),
std::make_pair(
Handle<Object>::cast(factory->NewStringFromStaticChars("hello")),
"string"),
};
for (size_t i = 0; i < arraysize(object_type_tuples); i++) {
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
Register r0(0);
builder.set_locals_count(0);
builder.set_parameter_count(0);
builder.EnterBlock();
LoadAny(&builder, factory, object_type_tuples[i].first);
builder.TypeOf();
builder.LeaveBlock().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<v8::internal::String> return_value =
Handle<v8::internal::String>::cast(callable().ToHandleChecked());
auto actual = return_value->ToCString();
CHECK_EQ(strcmp(&actual[0], object_type_tuples[i].second), 0);
}
}
TEST(InterpreterCallRuntime) {
HandleAndZoneScope handles;
......
......@@ -60,6 +60,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.BinaryOperation(Token::Value::DIV, reg)
.BinaryOperation(Token::Value::MOD, reg);
// Emit unary operator invocations.
builder.LogicalNot().TypeOf();
// Emit test operator invocations.
builder.CompareOperation(Token::Value::EQ, reg, LanguageMode::SLOPPY)
.CompareOperation(Token::Value::NE, reg, LanguageMode::SLOPPY)
......
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