blob: 3483e7967a431eead71ded7f5828d20c03bef26e [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <tuple>
#include "src/v8.h"
#include "src/api-inl.h"
#include "src/base/overflowing-math.h"
#include "src/compiler.h"
#include "src/execution.h"
#include "src/handles.h"
#include "src/hash-seed-inl.h"
#include "src/heap/heap-inl.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-flags.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/interpreter.h"
#include "src/objects-inl.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/smi.h"
#include "test/cctest/cctest.h"
#include "test/cctest/interpreter/interpreter-tester.h"
#include "test/cctest/test-feedback-vector.h"
namespace v8 {
namespace internal {
namespace interpreter {
static int GetIndex(FeedbackSlot slot) {
return FeedbackVector::GetIndex(slot);
}
using ToBooleanMode = BytecodeArrayBuilder::ToBooleanMode;
TEST(InterpreterReturn) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Handle<Object> undefined_value = isolate->factory()->undefined_value();
BytecodeArrayBuilder builder(zone, 1, 0);
builder.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(undefined_value));
}
TEST(InterpreterLoadUndefined) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Handle<Object> undefined_value = isolate->factory()->undefined_value();
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadUndefined().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(undefined_value));
}
TEST(InterpreterLoadNull) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Handle<Object> null_value = isolate->factory()->null_value();
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadNull().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(null_value));
}
TEST(InterpreterLoadTheHole) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Handle<Object> the_hole_value = isolate->factory()->the_hole_value();
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadTheHole().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(the_hole_value));
}
TEST(InterpreterLoadTrue) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Handle<Object> true_value = isolate->factory()->true_value();
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadTrue().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(true_value));
}
TEST(InterpreterLoadFalse) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Handle<Object> false_value = isolate->factory()->false_value();
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadFalse().Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(false_value));
}
TEST(InterpreterLoadLiteral) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
// Small Smis.
for (int i = -128; i < 128; i++) {
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadLiteral(Smi::FromInt(i)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(i));
}
// Large Smis.
{
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadLiteral(Smi::FromInt(0x12345678)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0x12345678));
}
// Heap numbers.
{
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadLiteral(-2.1e19).Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(i::HeapNumber::cast(*return_val)->value(), -2.1e19);
}
// Strings.
{
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
BytecodeArrayBuilder builder(zone, 1, 0);
const AstRawString* raw_string = ast_factory.GetOneByteString("String");
builder.LoadLiteral(raw_string).Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(i::String::cast(*return_val)->Equals(*raw_string->string()));
}
}
TEST(InterpreterLoadStoreRegisters) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Handle<Object> true_value = isolate->factory()->true_value();
for (int i = 0; i <= kMaxInt8; i++) {
BytecodeArrayBuilder builder(zone, 1, i + 1);
Register reg(i);
builder.LoadTrue()
.StoreAccumulatorInRegister(reg)
.LoadFalse()
.LoadAccumulatorWithRegister(reg)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(true_value));
}
}
static const Token::Value kShiftOperators[] = {
Token::Value::SHL, Token::Value::SAR, Token::Value::SHR};
static const Token::Value kArithmeticOperators[] = {
Token::Value::BIT_OR, Token::Value::BIT_XOR, Token::Value::BIT_AND,
Token::Value::SHL, Token::Value::SAR, Token::Value::SHR,
Token::Value::ADD, Token::Value::SUB, Token::Value::MUL,
Token::Value::DIV, Token::Value::MOD};
static double BinaryOpC(Token::Value op, double lhs, double rhs) {
switch (op) {
case Token::Value::ADD:
return lhs + rhs;
case Token::Value::SUB:
return lhs - rhs;
case Token::Value::MUL:
return lhs * rhs;
case Token::Value::DIV:
return base::Divide(lhs, rhs);
case Token::Value::MOD:
return Modulo(lhs, rhs);
case Token::Value::BIT_OR:
return (v8::internal::DoubleToInt32(lhs) |
v8::internal::DoubleToInt32(rhs));
case Token::Value::BIT_XOR:
return (v8::internal::DoubleToInt32(lhs) ^
v8::internal::DoubleToInt32(rhs));
case Token::Value::BIT_AND:
return (v8::internal::DoubleToInt32(lhs) &
v8::internal::DoubleToInt32(rhs));
case Token::Value::SHL: {
return base::ShlWithWraparound(DoubleToInt32(lhs), DoubleToInt32(rhs));
}
case Token::Value::SAR: {
int32_t val = v8::internal::DoubleToInt32(lhs);
uint32_t count = v8::internal::DoubleToUint32(rhs) & 0x1F;
int32_t result = val >> count;
return result;
}
case Token::Value::SHR: {
uint32_t val = v8::internal::DoubleToUint32(lhs);
uint32_t count = v8::internal::DoubleToUint32(rhs) & 0x1F;
uint32_t result = val >> count;
return result;
}
default:
UNREACHABLE();
}
}
TEST(InterpreterShiftOpsSmi) {
int lhs_inputs[] = {0, -17, -182, 1073741823, -1};
int rhs_inputs[] = {5, 2, 1, -1, -2, 0, 31, 32, -32, 64, 37};
for (size_t l = 0; l < arraysize(lhs_inputs); l++) {
for (size_t r = 0; r < arraysize(rhs_inputs); r++) {
for (size_t o = 0; o < arraysize(kShiftOperators); o++) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Factory* factory = isolate->factory();
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0);
int lhs = lhs_inputs[l];
int rhs = rhs_inputs[r];
builder.LoadLiteral(Smi::FromInt(lhs))
.StoreAccumulatorInRegister(reg)
.LoadLiteral(Smi::FromInt(rhs))
.BinaryOperation(kShiftOperators[o], reg, GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
Handle<Object> expected_value =
factory->NewNumber(BinaryOpC(kShiftOperators[o], lhs, rhs));
CHECK(return_value->SameValue(*expected_value));
}
}
}
}
TEST(InterpreterBinaryOpsSmi) {
int lhs_inputs[] = {3266, 1024, 0, -17, -18000};
int rhs_inputs[] = {3266, 5, 4, 3, 2, 1, -1, -2};
for (size_t l = 0; l < arraysize(lhs_inputs); l++) {
for (size_t r = 0; r < arraysize(rhs_inputs); r++) {
for (size_t o = 0; o < arraysize(kArithmeticOperators); o++) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Factory* factory = isolate->factory();
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0);
int lhs = lhs_inputs[l];
int rhs = rhs_inputs[r];
builder.LoadLiteral(Smi::FromInt(lhs))
.StoreAccumulatorInRegister(reg)
.LoadLiteral(Smi::FromInt(rhs))
.BinaryOperation(kArithmeticOperators[o], reg, GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
Handle<Object> expected_value =
factory->NewNumber(BinaryOpC(kArithmeticOperators[o], lhs, rhs));
CHECK(return_value->SameValue(*expected_value));
}
}
}
}
TEST(InterpreterBinaryOpsHeapNumber) {
double lhs_inputs[] = {3266.101, 1024.12, 0.01, -17.99, -18000.833, 9.1e17};
double rhs_inputs[] = {3266.101, 5.999, 4.778, 3.331, 2.643,
1.1, -1.8, -2.9, 8.3e-27};
for (size_t l = 0; l < arraysize(lhs_inputs); l++) {
for (size_t r = 0; r < arraysize(rhs_inputs); r++) {
for (size_t o = 0; o < arraysize(kArithmeticOperators); o++) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Factory* factory = isolate->factory();
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0);
double lhs = lhs_inputs[l];
double rhs = rhs_inputs[r];
builder.LoadLiteral(lhs)
.StoreAccumulatorInRegister(reg)
.LoadLiteral(rhs)
.BinaryOperation(kArithmeticOperators[o], reg, GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
Handle<Object> expected_value =
factory->NewNumber(BinaryOpC(kArithmeticOperators[o], lhs, rhs));
CHECK(return_value->SameValue(*expected_value));
}
}
}
}
TEST(InterpreterBinaryOpsBigInt) {
// This test only checks that the recorded type feedback is kBigInt.
AstBigInt inputs[] = {AstBigInt("1"), AstBigInt("-42"), AstBigInt("0xFFFF")};
for (size_t l = 0; l < arraysize(inputs); l++) {
for (size_t r = 0; r < arraysize(inputs); r++) {
for (size_t o = 0; o < arraysize(kArithmeticOperators); o++) {
// Skip over unsigned right shift.
if (kArithmeticOperators[o] == Token::Value::SHR) continue;
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0);
auto lhs = inputs[l];
auto rhs = inputs[r];
builder.LoadLiteral(lhs)
.StoreAccumulatorInRegister(reg)
.LoadLiteral(rhs)
.BinaryOperation(kArithmeticOperators[o], reg, GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBigInt());
if (tester.HasFeedbackMetadata()) {
MaybeObject feedback = callable.vector()->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kBigInt, feedback->ToSmi().value());
}
}
}
}
}
namespace {
struct LiteralForTest {
enum Type { kString, kHeapNumber, kSmi, kTrue, kFalse, kUndefined, kNull };
explicit LiteralForTest(const AstRawString* string)
: type(kString), string(string) {}
explicit LiteralForTest(double number) : type(kHeapNumber), number(number) {}
explicit LiteralForTest(int smi) : type(kSmi), smi(smi) {}
explicit LiteralForTest(Type type) : type(type) {}
Type type;
union {
const AstRawString* string;
double number;
int smi;
};
};
void LoadLiteralForTest(BytecodeArrayBuilder* builder,
const LiteralForTest& value) {
switch (value.type) {
case LiteralForTest::kString:
builder->LoadLiteral(value.string);
return;
case LiteralForTest::kHeapNumber:
builder->LoadLiteral(value.number);
return;
case LiteralForTest::kSmi:
builder->LoadLiteral(Smi::FromInt(value.smi));
return;
case LiteralForTest::kTrue:
builder->LoadTrue();
return;
case LiteralForTest::kFalse:
builder->LoadFalse();
return;
case LiteralForTest::kUndefined:
builder->LoadUndefined();
return;
case LiteralForTest::kNull:
builder->LoadNull();
return;
}
UNREACHABLE();
}
} // anonymous namespace
TEST(InterpreterStringAdd) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Factory* factory = isolate->factory();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
struct TestCase {
const AstRawString* lhs;
LiteralForTest rhs;
Handle<Object> expected_value;
int32_t expected_feedback;
} test_cases[] = {
{ast_factory.GetOneByteString("a"),
LiteralForTest(ast_factory.GetOneByteString("b")),
factory->NewStringFromStaticChars("ab"),
BinaryOperationFeedback::kString},
{ast_factory.GetOneByteString("aaaaaa"),
LiteralForTest(ast_factory.GetOneByteString("b")),
factory->NewStringFromStaticChars("aaaaaab"),
BinaryOperationFeedback::kString},
{ast_factory.GetOneByteString("aaa"),
LiteralForTest(ast_factory.GetOneByteString("bbbbb")),
factory->NewStringFromStaticChars("aaabbbbb"),
BinaryOperationFeedback::kString},
{ast_factory.GetOneByteString(""),
LiteralForTest(ast_factory.GetOneByteString("b")),
factory->NewStringFromStaticChars("b"),
BinaryOperationFeedback::kString},
{ast_factory.GetOneByteString("a"),
LiteralForTest(ast_factory.GetOneByteString("")),
factory->NewStringFromStaticChars("a"),
BinaryOperationFeedback::kString},
{ast_factory.GetOneByteString("1.11"), LiteralForTest(2.5),
factory->NewStringFromStaticChars("1.112.5"),
BinaryOperationFeedback::kAny},
{ast_factory.GetOneByteString("-1.11"), LiteralForTest(2.56),
factory->NewStringFromStaticChars("-1.112.56"),
BinaryOperationFeedback::kAny},
{ast_factory.GetOneByteString(""), LiteralForTest(2.5),
factory->NewStringFromStaticChars("2.5"), BinaryOperationFeedback::kAny},
};
for (size_t i = 0; i < arraysize(test_cases); i++) {
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0);
builder.LoadLiteral(test_cases[i].lhs).StoreAccumulatorInRegister(reg);
LoadLiteralForTest(&builder, test_cases[i].rhs);
builder.BinaryOperation(Token::Value::ADD, reg, GetIndex(slot)).Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*test_cases[i].expected_value));
if (tester.HasFeedbackMetadata()) {
MaybeObject feedback = callable.vector()->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(test_cases[i].expected_feedback, feedback->ToSmi().value());
}
}
}
TEST(InterpreterParameter1) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadAccumulatorWithRegister(builder.Receiver()).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<Handle<Object>>();
// Check for heap objects.
Handle<Object> true_value = isolate->factory()->true_value();
Handle<Object> return_val = callable(true_value).ToHandleChecked();
CHECK(return_val.is_identical_to(true_value));
// Check for Smis.
return_val = callable(Handle<Smi>(Smi::FromInt(3), handles.main_isolate()))
.ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(3));
}
TEST(InterpreterParameter8) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 8, 0, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot1 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot2 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot3 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot4 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot5 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot6 = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
builder.LoadAccumulatorWithRegister(builder.Receiver())
.BinaryOperation(Token::Value::ADD, builder.Parameter(0), GetIndex(slot))
.BinaryOperation(Token::Value::ADD, builder.Parameter(1), GetIndex(slot1))
.BinaryOperation(Token::Value::ADD, builder.Parameter(2), GetIndex(slot2))
.BinaryOperation(Token::Value::ADD, builder.Parameter(3), GetIndex(slot3))
.BinaryOperation(Token::Value::ADD, builder.Parameter(4), GetIndex(slot4))
.BinaryOperation(Token::Value::ADD, builder.Parameter(5), GetIndex(slot5))
.BinaryOperation(Token::Value::ADD, builder.Parameter(6), GetIndex(slot6))
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
typedef Handle<Object> H;
auto callable = tester.GetCallable<H, H, H, H, H, H, H, H>();
Handle<Smi> arg1 = Handle<Smi>(Smi::FromInt(1), handles.main_isolate());
Handle<Smi> arg2 = Handle<Smi>(Smi::FromInt(2), handles.main_isolate());
Handle<Smi> arg3 = Handle<Smi>(Smi::FromInt(3), handles.main_isolate());
Handle<Smi> arg4 = Handle<Smi>(Smi::FromInt(4), handles.main_isolate());
Handle<Smi> arg5 = Handle<Smi>(Smi::FromInt(5), handles.main_isolate());
Handle<Smi> arg6 = Handle<Smi>(Smi::FromInt(6), handles.main_isolate());
Handle<Smi> arg7 = Handle<Smi>(Smi::FromInt(7), handles.main_isolate());
Handle<Smi> arg8 = Handle<Smi>(Smi::FromInt(8), handles.main_isolate());
// Check for Smis.
Handle<Object> return_val =
callable(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
.ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(36));
}
TEST(InterpreterBinaryOpTypeFeedback) {
if (FLAG_lite_mode) return;
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
struct BinaryOpExpectation {
Token::Value op;
LiteralForTest arg1;
LiteralForTest arg2;
Handle<Object> result;
int32_t feedback;
};
BinaryOpExpectation const kTestCases[] = {
// ADD
{Token::Value::ADD, LiteralForTest(2), LiteralForTest(3),
Handle<Smi>(Smi::FromInt(5), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::ADD, LiteralForTest(Smi::kMaxValue), LiteralForTest(1),
isolate->factory()->NewHeapNumber(Smi::kMaxValue + 1.0),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, LiteralForTest(3.1415), LiteralForTest(3),
isolate->factory()->NewHeapNumber(3.1415 + 3),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, LiteralForTest(3.1415), LiteralForTest(1.4142),
isolate->factory()->NewHeapNumber(3.1415 + 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, LiteralForTest(ast_factory.GetOneByteString("foo")),
LiteralForTest(ast_factory.GetOneByteString("bar")),
isolate->factory()->NewStringFromAsciiChecked("foobar"),
BinaryOperationFeedback::kString},
{Token::Value::ADD, LiteralForTest(2),
LiteralForTest(ast_factory.GetOneByteString("2")),
isolate->factory()->NewStringFromAsciiChecked("22"),
BinaryOperationFeedback::kAny},
// SUB
{Token::Value::SUB, LiteralForTest(2), LiteralForTest(3),
Handle<Smi>(Smi::FromInt(-1), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SUB, LiteralForTest(Smi::kMinValue), LiteralForTest(1),
isolate->factory()->NewHeapNumber(Smi::kMinValue - 1.0),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, LiteralForTest(3.1415), LiteralForTest(3),
isolate->factory()->NewHeapNumber(3.1415 - 3),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, LiteralForTest(3.1415), LiteralForTest(1.4142),
isolate->factory()->NewHeapNumber(3.1415 - 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, LiteralForTest(2),
LiteralForTest(ast_factory.GetOneByteString("1")),
Handle<Smi>(Smi::FromInt(1), isolate), BinaryOperationFeedback::kAny},
// MUL
{Token::Value::MUL, LiteralForTest(2), LiteralForTest(3),
Handle<Smi>(Smi::FromInt(6), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::MUL, LiteralForTest(Smi::kMinValue), LiteralForTest(2),
isolate->factory()->NewHeapNumber(Smi::kMinValue * 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, LiteralForTest(3.1415), LiteralForTest(3),
isolate->factory()->NewHeapNumber(3 * 3.1415),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, LiteralForTest(3.1415), LiteralForTest(1.4142),
isolate->factory()->NewHeapNumber(3.1415 * 1.4142),
BinaryOperationFeedback::kNumber},
{Token::Value::MUL, LiteralForTest(2),
LiteralForTest(ast_factory.GetOneByteString("1")),
Handle<Smi>(Smi::FromInt(2), isolate), BinaryOperationFeedback::kAny},
// DIV
{Token::Value::DIV, LiteralForTest(6), LiteralForTest(3),
Handle<Smi>(Smi::FromInt(2), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::DIV, LiteralForTest(3), LiteralForTest(2),
isolate->factory()->NewHeapNumber(3.0 / 2.0),
BinaryOperationFeedback::kSignedSmallInputs},
{Token::Value::DIV, LiteralForTest(3.1415), LiteralForTest(3),
isolate->factory()->NewHeapNumber(3.1415 / 3),
BinaryOperationFeedback::kNumber},
{Token::Value::DIV, LiteralForTest(3.1415),
LiteralForTest(-std::numeric_limits<double>::infinity()),
isolate->factory()->NewHeapNumber(-0.0),
BinaryOperationFeedback::kNumber},
{Token::Value::DIV, LiteralForTest(2),
LiteralForTest(ast_factory.GetOneByteString("1")),
Handle<Smi>(Smi::FromInt(2), isolate), BinaryOperationFeedback::kAny},
// MOD
{Token::Value::MOD, LiteralForTest(5), LiteralForTest(3),
Handle<Smi>(Smi::FromInt(2), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::MOD, LiteralForTest(-4), LiteralForTest(2),
isolate->factory()->NewHeapNumber(-0.0),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, LiteralForTest(3.1415), LiteralForTest(3),
isolate->factory()->NewHeapNumber(fmod(3.1415, 3.0)),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, LiteralForTest(-3.1415), LiteralForTest(-1.4142),
isolate->factory()->NewHeapNumber(fmod(-3.1415, -1.4142)),
BinaryOperationFeedback::kNumber},
{Token::Value::MOD, LiteralForTest(3),
LiteralForTest(ast_factory.GetOneByteString("-2")),
Handle<Smi>(Smi::FromInt(1), isolate), BinaryOperationFeedback::kAny}};
for (const BinaryOpExpectation& test_case : kTestCases) {
i::FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
i::FeedbackSlot slot0 = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
i::NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0);
LoadLiteralForTest(&builder, test_case.arg1);
builder.StoreAccumulatorInRegister(reg);
LoadLiteralForTest(&builder, test_case.arg2);
builder.BinaryOperation(test_case.op, reg, GetIndex(slot0)).Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
MaybeObject feedback0 = callable.vector()->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(test_case.feedback, feedback0->ToSmi().value());
CHECK(Object::Equals(isolate, test_case.result, return_val).ToChecked());
}
}
TEST(InterpreterBinaryOpSmiTypeFeedback) {
if (FLAG_lite_mode) return;
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
struct BinaryOpExpectation {
Token::Value op;
LiteralForTest arg1;
int32_t arg2;
Handle<Object> result;
int32_t feedback;
};
BinaryOpExpectation const kTestCases[] = {
// ADD
{Token::Value::ADD, LiteralForTest(2), 42,
Handle<Smi>(Smi::FromInt(44), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::ADD, LiteralForTest(2), Smi::kMaxValue,
isolate->factory()->NewHeapNumber(Smi::kMaxValue + 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, LiteralForTest(3.1415), 2,
isolate->factory()->NewHeapNumber(3.1415 + 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::ADD, LiteralForTest(ast_factory.GetOneByteString("2")), 2,
isolate->factory()->NewStringFromAsciiChecked("22"),
BinaryOperationFeedback::kAny},
// SUB
{Token::Value::SUB, LiteralForTest(2), 42,
Handle<Smi>(Smi::FromInt(-40), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SUB, LiteralForTest(Smi::kMinValue), 1,
isolate->factory()->NewHeapNumber(Smi::kMinValue - 1.0),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, LiteralForTest(3.1415), 2,
isolate->factory()->NewHeapNumber(3.1415 - 2.0),
BinaryOperationFeedback::kNumber},
{Token::Value::SUB, LiteralForTest(ast_factory.GetOneByteString("2")), 2,
Handle<Smi>(Smi::zero(), isolate), BinaryOperationFeedback::kAny},
// BIT_OR
{Token::Value::BIT_OR, LiteralForTest(4), 1,
Handle<Smi>(Smi::FromInt(5), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::BIT_OR, LiteralForTest(3.1415), 8,
Handle<Smi>(Smi::FromInt(11), isolate),
BinaryOperationFeedback::kNumber},
{Token::Value::BIT_OR, LiteralForTest(ast_factory.GetOneByteString("2")),
1, Handle<Smi>(Smi::FromInt(3), isolate), BinaryOperationFeedback::kAny},
// BIT_AND
{Token::Value::BIT_AND, LiteralForTest(3), 1,
Handle<Smi>(Smi::FromInt(1), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::BIT_AND, LiteralForTest(3.1415), 2,
Handle<Smi>(Smi::FromInt(2), isolate), BinaryOperationFeedback::kNumber},
{Token::Value::BIT_AND, LiteralForTest(ast_factory.GetOneByteString("2")),
1, Handle<Smi>(Smi::zero(), isolate), BinaryOperationFeedback::kAny},
// SHL
{Token::Value::SHL, LiteralForTest(3), 1,
Handle<Smi>(Smi::FromInt(6), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SHL, LiteralForTest(3.1415), 2,
Handle<Smi>(Smi::FromInt(12), isolate),
BinaryOperationFeedback::kNumber},
{Token::Value::SHL, LiteralForTest(ast_factory.GetOneByteString("2")), 1,
Handle<Smi>(Smi::FromInt(4), isolate), BinaryOperationFeedback::kAny},
// SAR
{Token::Value::SAR, LiteralForTest(3), 1,
Handle<Smi>(Smi::FromInt(1), isolate),
BinaryOperationFeedback::kSignedSmall},
{Token::Value::SAR, LiteralForTest(3.1415), 2,
Handle<Smi>(Smi::zero(), isolate), BinaryOperationFeedback::kNumber},
{Token::Value::SAR, LiteralForTest(ast_factory.GetOneByteString("2")), 1,
Handle<Smi>(Smi::FromInt(1), isolate), BinaryOperationFeedback::kAny}};
for (const BinaryOpExpectation& test_case : kTestCases) {
i::FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
i::FeedbackSlot slot0 = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
i::NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0);
LoadLiteralForTest(&builder, test_case.arg1);
builder.StoreAccumulatorInRegister(reg)
.LoadLiteral(Smi::FromInt(test_case.arg2))
.BinaryOperation(test_case.op, reg, GetIndex(slot0))
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
MaybeObject feedback0 = callable.vector()->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(test_case.feedback, feedback0->ToSmi().value());
CHECK(Object::Equals(isolate, test_case.result, return_val).ToChecked());
}
}
TEST(InterpreterUnaryOpFeedback) {
if (FLAG_lite_mode) return;
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Handle<Smi> smi_one = Handle<Smi>(Smi::FromInt(1), isolate);
Handle<Smi> smi_max = Handle<Smi>(Smi::FromInt(Smi::kMaxValue), isolate);
Handle<Smi> smi_min = Handle<Smi>(Smi::FromInt(Smi::kMinValue), isolate);
Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(2.1);
Handle<BigInt> bigint =
BigInt::FromNumber(isolate, smi_max).ToHandleChecked();
Handle<String> str = isolate->factory()->NewStringFromAsciiChecked("42");
struct TestCase {
Token::Value op;
Handle<Smi> smi_feedback_value;
Handle<Smi> smi_to_number_feedback_value;
Handle<HeapNumber> number_feedback_value;
Handle<BigInt> bigint_feedback_value;
Handle<Object> any_feedback_value;
};
TestCase const kTestCases[] = {
// Testing ADD and BIT_NOT would require generalizing the test setup.
{Token::Value::SUB, smi_one, smi_min, number, bigint, str},
{Token::Value::INC, smi_one, smi_max, number, bigint, str},
{Token::Value::DEC, smi_one, smi_min, number, bigint, str}};
for (TestCase const& test_case : kTestCases) {
i::FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 5, 0, &feedback_spec);
i::FeedbackSlot slot0 = feedback_spec.AddBinaryOpICSlot();
i::FeedbackSlot slot1 = feedback_spec.AddBinaryOpICSlot();
i::FeedbackSlot slot2 = feedback_spec.AddBinaryOpICSlot();
i::FeedbackSlot slot3 = feedback_spec.AddBinaryOpICSlot();
i::FeedbackSlot slot4 = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
i::NewFeedbackMetadata(isolate, &feedback_spec);
builder.LoadAccumulatorWithRegister(builder.Receiver())
.UnaryOperation(test_case.op, GetIndex(slot0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.UnaryOperation(test_case.op, GetIndex(slot1))
.LoadAccumulatorWithRegister(builder.Parameter(1))
.UnaryOperation(test_case.op, GetIndex(slot2))
.LoadAccumulatorWithRegister(builder.Parameter(2))
.UnaryOperation(test_case.op, GetIndex(slot3))
.LoadAccumulatorWithRegister(builder.Parameter(3))
.UnaryOperation(test_case.op, GetIndex(slot4))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
typedef Handle<Object> H;
auto callable = tester.GetCallable<H, H, H, H, H>();
Handle<Object> return_val =
callable(test_case.smi_feedback_value,
test_case.smi_to_number_feedback_value,
test_case.number_feedback_value,
test_case.bigint_feedback_value, test_case.any_feedback_value)
.ToHandleChecked();
USE(return_val);
MaybeObject feedback0 = callable.vector()->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kSignedSmall, feedback0->ToSmi().value());
MaybeObject feedback1 = callable.vector()->Get(slot1);
CHECK(feedback1->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber, feedback1->ToSmi().value());
MaybeObject feedback2 = callable.vector()->Get(slot2);
CHECK(feedback2->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber, feedback2->ToSmi().value());
MaybeObject feedback3 = callable.vector()->Get(slot3);
CHECK(feedback3->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kBigInt, feedback3->ToSmi().value());
MaybeObject feedback4 = callable.vector()->Get(slot4);
CHECK(feedback4->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kAny, feedback4->ToSmi().value());
}
}
TEST(InterpreterBitwiseTypeFeedback) {
if (FLAG_lite_mode) return;
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
const Token::Value kBitwiseBinaryOperators[] = {
Token::Value::BIT_OR, Token::Value::BIT_XOR, Token::Value::BIT_AND,
Token::Value::SHL, Token::Value::SHR, Token::Value::SAR};
for (Token::Value op : kBitwiseBinaryOperators) {
i::FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 4, 0, &feedback_spec);
i::FeedbackSlot slot0 = feedback_spec.AddBinaryOpICSlot();
i::FeedbackSlot slot1 = feedback_spec.AddBinaryOpICSlot();
i::FeedbackSlot slot2 = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
i::NewFeedbackMetadata(isolate, &feedback_spec);
builder.LoadAccumulatorWithRegister(builder.Receiver())
.BinaryOperation(op, builder.Parameter(0), GetIndex(slot0))
.BinaryOperation(op, builder.Parameter(1), GetIndex(slot1))
.BinaryOperation(op, builder.Parameter(2), GetIndex(slot2))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
typedef Handle<Object> H;
auto callable = tester.GetCallable<H, H, H, H>();
Handle<Smi> arg1 = Handle<Smi>(Smi::FromInt(2), isolate);
Handle<Smi> arg2 = Handle<Smi>(Smi::FromInt(2), isolate);
Handle<HeapNumber> arg3 = isolate->factory()->NewHeapNumber(2.2);
Handle<String> arg4 = isolate->factory()->NewStringFromAsciiChecked("2");
Handle<Object> return_val =
callable(arg1, arg2, arg3, arg4).ToHandleChecked();
USE(return_val);
MaybeObject feedback0 = callable.vector()->Get(slot0);
CHECK(feedback0->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kSignedSmall, feedback0->ToSmi().value());
MaybeObject feedback1 = callable.vector()->Get(slot1);
CHECK(feedback1->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kNumber, feedback1->ToSmi().value());
MaybeObject feedback2 = callable.vector()->Get(slot2);
CHECK(feedback2->IsSmi());
CHECK_EQ(BinaryOperationFeedback::kAny, feedback2->ToSmi().value());
}
}
TEST(InterpreterParameter1Assign) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadLiteral(Smi::FromInt(5))
.StoreAccumulatorInRegister(builder.Receiver())
.LoadAccumulatorWithRegister(builder.Receiver())
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(Handle<Smi>(Smi::FromInt(3), handles.main_isolate()))
.ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(5));
}
TEST(InterpreterLoadGlobal) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
// Test loading a global.
std::string source(
"var global = 321;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return global;\n"
"}");
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(321));
}
TEST(InterpreterStoreGlobal) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
// Test storing to a global.
std::string source(
"var global = 321;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" global = 999;\n"
"}");
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
callable().ToHandleChecked();
Handle<i::String> name = factory->InternalizeUtf8String("global");
Handle<i::Object> global_obj =
Object::GetProperty(isolate, isolate->global_object(), name)
.ToHandleChecked();
CHECK_EQ(Smi::cast(*global_obj), Smi::FromInt(999));
}
TEST(InterpreterCallGlobal) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
// Test calling a global function.
std::string source(
"function g_add(a, b) { return a + b; }\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return g_add(5, 10);\n"
"}");
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(15));
}
TEST(InterpreterLoadUnallocated) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
// Test loading an unallocated global.
std::string source(
"unallocated = 123;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return unallocated;\n"
"}");
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
}
TEST(InterpreterStoreUnallocated) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
// Test storing to an unallocated global.
std::string source(
"unallocated = 321;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" unallocated = 999;\n"
"}");
InterpreterTester tester(isolate, source.c_str());
auto callable = tester.GetCallable<>();
callable().ToHandleChecked();
Handle<i::String> name = factory->InternalizeUtf8String("unallocated");
Handle<i::Object> global_obj =
Object::GetProperty(isolate, isolate->global_object(), name)
.ToHandleChecked();
CHECK_EQ(Smi::cast(*global_obj), Smi::FromInt(999));
}
TEST(InterpreterLoadNamedProperty) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
FeedbackSlot slot = feedback_spec.AddLoadICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
const AstRawString* name = ast_factory.GetOneByteString("val");
BytecodeArrayBuilder builder(zone, 1, 0, &feedback_spec);
builder.LoadNamedProperty(builder.Receiver(), name, GetIndex(slot)).Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to monomorphic IC.
return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to polymorphic IC.
Handle<Object> object2 =
InterpreterTester::NewObject("({ val : 456, other : 123 })");
return_val = callable(object2).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(456));
// Test transition to megamorphic IC.
Handle<Object> object3 =
InterpreterTester::NewObject("({ val : 789, val2 : 123 })");
callable(object3).ToHandleChecked();
Handle<Object> object4 =
InterpreterTester::NewObject("({ val : 789, val3 : 123 })");
callable(object4).ToHandleChecked();
Handle<Object> object5 =
InterpreterTester::NewObject("({ val : 789, val4 : 123 })");
return_val = callable(object5).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}
TEST(InterpreterLoadKeyedProperty) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
FeedbackSlot slot = feedback_spec.AddKeyedLoadICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
const AstRawString* key = ast_factory.GetOneByteString("key");
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
builder.LoadLiteral(key)
.LoadKeyedProperty(builder.Receiver(), GetIndex(slot))
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject("({ key : 123 })");
// Test IC miss.
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to monomorphic IC.
return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to megamorphic IC.
Handle<Object> object3 =
InterpreterTester::NewObject("({ key : 789, val2 : 123 })");
return_val = callable(object3).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}
TEST(InterpreterStoreNamedProperty) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
FeedbackSlot slot = feedback_spec.AddStoreICSlot(LanguageMode::kStrict);
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
const AstRawString* name = ast_factory.GetOneByteString("val");
BytecodeArrayBuilder builder(zone, 1, 0, &feedback_spec);
builder.LoadLiteral(Smi::FromInt(999))
.StoreNamedProperty(builder.Receiver(), name, GetIndex(slot),
LanguageMode::kStrict)
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> result;
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name->string())
.ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to monomorphic IC.
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name->string())
.ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to polymorphic IC.
Handle<Object> object2 =
InterpreterTester::NewObject("({ val : 456, other : 123 })");
callable(object2).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object2, name->string())
.ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to megamorphic IC.
Handle<Object> object3 =
InterpreterTester::NewObject("({ val : 789, val2 : 123 })");
callable(object3).ToHandleChecked();
Handle<Object> object4 =
InterpreterTester::NewObject("({ val : 789, val3 : 123 })");
callable(object4).ToHandleChecked();
Handle<Object> object5 =
InterpreterTester::NewObject("({ val : 789, val4 : 123 })");
callable(object5).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object5, name->string())
.ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
}
TEST(InterpreterStoreKeyedProperty) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
FeedbackSlot slot = feedback_spec.AddKeyedStoreICSlot(LanguageMode::kSloppy);
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
const AstRawString* name = ast_factory.GetOneByteString("val");
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
builder.LoadLiteral(name)
.StoreAccumulatorInRegister(Register(0))
.LoadLiteral(Smi::FromInt(999))
.StoreKeyedProperty(builder.Receiver(), Register(0), GetIndex(slot),
i::LanguageMode::kSloppy)
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> result;
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name->string())
.ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to monomorphic IC.
callable(object).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object, name->string())
.ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to megamorphic IC.
Handle<Object> object2 =
InterpreterTester::NewObject("({ val : 456, other : 123 })");
callable(object2).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object2, name->string())
.ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
}
TEST(InterpreterCall) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Factory* factory = isolate->factory();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
FeedbackSlot slot = feedback_spec.AddLoadICSlot();
FeedbackSlot call_slot = feedback_spec.AddCallICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
int slot_index = GetIndex(slot);
int call_slot_index = -1;
call_slot_index = GetIndex(call_slot);
const AstRawString* name = ast_factory.GetOneByteString("func");
// Check with no args.
{
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
Register reg = builder.register_allocator()->NewRegister();
RegisterList args = builder.register_allocator()->NewRegisterList(1);
builder.LoadNamedProperty(builder.Receiver(), name, slot_index)
.StoreAccumulatorInRegister(reg)
.MoveRegister(builder.Receiver(), args[0]);
builder.CallProperty(reg, args, call_slot_index);
builder.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() { this.func = function() { return 0x265; }})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0x265));
}
// Check that receiver is passed properly.
{
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
Register reg = builder.register_allocator()->NewRegister();
RegisterList args = builder.register_allocator()->NewRegisterList(1);
builder.LoadNamedProperty(builder.Receiver(), name, slot_index)
.StoreAccumulatorInRegister(reg)
.MoveRegister(builder.Receiver(), args[0]);
builder.CallProperty(reg, args, call_slot_index);
builder.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() {"
" this.val = 1234;"
" this.func = function() { return this.val; };"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(1234));
}
// Check with two parameters (+ receiver).
{
BytecodeArrayBuilder builder(zone, 1, 4, &feedback_spec);
Register reg = builder.register_allocator()->NewRegister();
RegisterList args = builder.register_allocator()->NewRegisterList(3);
builder.LoadNamedProperty(builder.Receiver(), name, slot_index)
.StoreAccumulatorInRegister(reg)
.LoadAccumulatorWithRegister(builder.Receiver())
.StoreAccumulatorInRegister(args[0])
.LoadLiteral(Smi::FromInt(51))
.StoreAccumulatorInRegister(args[1])
.LoadLiteral(Smi::FromInt(11))
.StoreAccumulatorInRegister(args[2]);
builder.CallProperty(reg, args, call_slot_index);
builder.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() { "
" this.func = function(a, b) { return a - b; }"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK(return_val->SameValue(Smi::FromInt(40)));
}
// Check with 10 parameters (+ receiver).
{
BytecodeArrayBuilder builder(zone, 1, 12, &feedback_spec);
Register reg = builder.register_allocator()->NewRegister();
RegisterList args = builder.register_allocator()->NewRegisterList(11);
builder.LoadNamedProperty(builder.Receiver(), name, slot_index)
.StoreAccumulatorInRegister(reg)
.LoadAccumulatorWithRegister(builder.Receiver())
.StoreAccumulatorInRegister(args[0])
.LoadLiteral(ast_factory.GetOneByteString("a"))
.StoreAccumulatorInRegister(args[1])
.LoadLiteral(ast_factory.GetOneByteString("b"))
.StoreAccumulatorInRegister(args[2])
.LoadLiteral(ast_factory.GetOneByteString("c"))
.StoreAccumulatorInRegister(args[3])
.LoadLiteral(ast_factory.GetOneByteString("d"))
.StoreAccumulatorInRegister(args[4])
.LoadLiteral(ast_factory.GetOneByteString("e"))
.StoreAccumulatorInRegister(args[5])
.LoadLiteral(ast_factory.GetOneByteString("f"))
.StoreAccumulatorInRegister(args[6])
.LoadLiteral(ast_factory.GetOneByteString("g"))
.StoreAccumulatorInRegister(args[7])
.LoadLiteral(ast_factory.GetOneByteString("h"))
.StoreAccumulatorInRegister(args[8])
.LoadLiteral(ast_factory.GetOneByteString("i"))
.StoreAccumulatorInRegister(args[9])
.LoadLiteral(ast_factory.GetOneByteString("j"))
.StoreAccumulatorInRegister(args[10]);
builder.CallProperty(reg, args, call_slot_index);
builder.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() { "
" this.prefix = \"prefix_\";"
" this.func = function(a, b, c, d, e, f, g, h, i, j) {"
" return this.prefix + a + b + c + d + e + f + g + h + i + j;"
" }"
"})()");
Handle<Object> return_val = callable(object).ToHandleChecked();
Handle<i::String> expected =
factory->NewStringFromAsciiChecked("prefix_abcdefghij");
CHECK(i::String::cast(*return_val)->Equals(*expected));
}
}
static BytecodeArrayBuilder& SetRegister(BytecodeArrayBuilder& builder,
Register reg, int value,
Register scratch) {
return builder.StoreAccumulatorInRegister(scratch)
.LoadLiteral(Smi::FromInt(value))
.StoreAccumulatorInRegister(reg)
.LoadAccumulatorWithRegister(scratch);
}
static BytecodeArrayBuilder& IncrementRegister(BytecodeArrayBuilder& builder,
Register reg, int value,
Register scratch,
int slot_index) {
return builder.StoreAccumulatorInRegister(scratch)
.LoadLiteral(Smi::FromInt(value))
.BinaryOperation(Token::Value::ADD, reg, slot_index)
.StoreAccumulatorInRegister(reg)
.LoadAccumulatorWithRegister(scratch);
}
TEST(InterpreterJumps) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 2, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot1 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot2 = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0), scratch(1);
BytecodeLoopHeader loop_header;
BytecodeLabel label[2];
builder.LoadLiteral(Smi::zero())
.StoreAccumulatorInRegister(reg)
.Jump(&label[0]);
SetRegister(builder, reg, 1024, scratch).Bind(&loop_header);
IncrementRegister(builder, reg, 1, scratch, GetIndex(slot)).Jump(&label[1]);
SetRegister(builder, reg, 2048, scratch).Bind(&label[0]);
IncrementRegister(builder, reg, 2, scratch, GetIndex(slot1))
.JumpLoop(&loop_header, 0);
SetRegister(builder, reg, 4096, scratch).Bind(&label[1]);
IncrementRegister(builder, reg, 4, scratch, GetIndex(slot2))
.LoadAccumulatorWithRegister(reg)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Smi::ToInt(*return_value), 7);
}
TEST(InterpreterConditionalJumps) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 2, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot1 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot2 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot3 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot4 = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0), scratch(1);
BytecodeLabel label[2];
BytecodeLabel done, done1;
builder.LoadLiteral(Smi::zero())
.StoreAccumulatorInRegister(reg)
.LoadFalse()
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &label[0]);
IncrementRegister(builder, reg, 1024, scratch, GetIndex(slot))
.Bind(&label[0])
.LoadTrue()
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &done);
IncrementRegister(builder, reg, 1, scratch, GetIndex(slot1))
.LoadTrue()
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &label[1]);
IncrementRegister(builder, reg, 2048, scratch, GetIndex(slot2))
.Bind(&label[1]);
IncrementRegister(builder, reg, 2, scratch, GetIndex(slot3))
.LoadFalse()
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &done1);
IncrementRegister(builder, reg, 4, scratch, GetIndex(slot4))
.LoadAccumulatorWithRegister(reg)
.Bind(&done)
.Bind(&done1)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Smi::ToInt(*return_value), 7);
}
TEST(InterpreterConditionalJumps2) {
// TODO(oth): Add tests for all conditional jumps near and far.
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 2, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot1 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot2 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot3 = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot4 = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0), scratch(1);
BytecodeLabel label[2];
BytecodeLabel done, done1;
builder.LoadLiteral(Smi::zero())
.StoreAccumulatorInRegister(reg)
.LoadFalse()
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &label[0]);
IncrementRegister(builder, reg, 1024, scratch, GetIndex(slot))
.Bind(&label[0])
.LoadTrue()
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &done);
IncrementRegister(builder, reg, 1, scratch, GetIndex(slot1))
.LoadTrue()
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &label[1]);
IncrementRegister(builder, reg, 2048, scratch, GetIndex(slot2))
.Bind(&label[1]);
IncrementRegister(builder, reg, 2, scratch, GetIndex(slot3))
.LoadFalse()
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &done1);
IncrementRegister(builder, reg, 4, scratch, GetIndex(slot4))
.LoadAccumulatorWithRegister(reg)
.Bind(&done)
.Bind(&done1)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Smi::ToInt(*return_value), 7);
}
TEST(InterpreterJumpConstantWith16BitOperand) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 257, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddBinaryOpICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register reg(0), scratch(256);
BytecodeLabel done, fake;
builder.LoadLiteral(Smi::zero());
builder.StoreAccumulatorInRegister(reg);
// Conditional jump to the fake label, to force both basic blocks to be live.
builder.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &fake);
// Consume all 8-bit operands
for (int i = 1; i <= 256; i++) {
builder.LoadLiteral(i + 0.5);
builder.BinaryOperation(Token::Value::ADD, reg, GetIndex(slot));
builder.StoreAccumulatorInRegister(reg);
}
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::zero()); // 1-byte
builder.BinaryOperation(Token::Value::ADD, scratch,
GetIndex(slot)); // 6-bytes
builder.StoreAccumulatorInRegister(scratch); // 4-bytes
builder.MoveRegister(scratch, reg); // 6-bytes
}
builder.Bind(&done);
builder.LoadAccumulatorWithRegister(reg);
builder.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
BytecodeArrayIterator iterator(bytecode_array);
bool found_16bit_constant_jump = false;
while (!iterator.done()) {
if (iterator.current_bytecode() == Bytecode::kJumpConstant &&
iterator.current_operand_scale() == OperandScale::kDouble) {
found_16bit_constant_jump = true;
break;
}
iterator.Advance();
}
CHECK(found_16bit_constant_jump);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Handle<HeapNumber>::cast(return_value)->value(),
256.0 / 2 * (1.5 + 256.5));
}
TEST(InterpreterJumpWith32BitOperand) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
BytecodeArrayBuilder builder(zone, 1, 1);
Register reg(0);
BytecodeLabel done;
builder.LoadLiteral(Smi::zero());
builder.StoreAccumulatorInRegister(reg);
// Consume all 16-bit constant pool entries. Make sure to use doubles so that
// the jump can't re-use an integer.
for (int i = 1; i <= 65536; i++) {
builder.LoadLiteral(i + 0.5);
}
builder.Jump(&done);
builder.LoadLiteral(Smi::zero());
builder.Bind(&done);
builder.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
BytecodeArrayIterator iterator(bytecode_array);
bool found_32bit_jump = false;
while (!iterator.done()) {
if (iterator.current_bytecode() == Bytecode::kJump &&
iterator.current_operand_scale() == OperandScale::kQuadruple) {
found_32bit_jump = true;
break;
}
iterator.Advance();
}
CHECK(found_32bit_jump);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK_EQ(Handle<HeapNumber>::cast(return_value)->value(), 65536.5);
}
static const Token::Value kComparisonTypes[] = {
Token::Value::EQ, Token::Value::EQ_STRICT, Token::Value::LT,
Token::Value::LTE, Token::Value::GT, Token::Value::GTE};
template <typename T>
bool CompareC(Token::Value op, T lhs, T rhs, bool types_differed = false) {
switch (op) {
case Token::Value::EQ:
return lhs == rhs;
case Token::Value::NE:
return lhs != rhs;
case Token::Value::EQ_STRICT:
return (lhs == rhs) && !types_differed;
case Token::Value::NE_STRICT:
return (lhs != rhs) || types_differed;
case Token::Value::LT:
return lhs < rhs;
case Token::Value::LTE:
return lhs <= rhs;
case Token::Value::GT:
return lhs > rhs;
case Token::Value::GTE:
return lhs >= rhs;
default:
UNREACHABLE();
}
}
TEST(InterpreterSmiComparisons) {
// NB Constants cover 31-bit space.
int inputs[] = {v8::internal::kMinInt / 2,
v8::internal::kMinInt / 4,
-108733832,
-999,
-42,
-2,
-1,
0,
+1,
+2,
42,
12345678,
v8::internal::kMaxInt / 4,
v8::internal::kMaxInt / 2};
for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
Token::Value comparison = kComparisonTypes[c];
for (size_t i = 0; i < arraysize(inputs); i++) {
for (size_t j = 0; j < arraysize(inputs); j++) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddCompareICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register r0(0);
builder.LoadLiteral(Smi::FromInt(inputs[i]))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(Smi::FromInt(inputs[j]))
.CompareOperation(comparison, r0, GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate),
CompareC(comparison, inputs[i], inputs[j]));
if (tester.HasFeedbackMetadata()) {
MaybeObject feedback = callable.vector()->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(CompareOperationFeedback::kSignedSmall,
feedback->ToSmi().value());
}
}
}
}
}
TEST(InterpreterHeapNumberComparisons) {
double inputs[] = {std::numeric_limits<double>::min(),
std::numeric_limits<double>::max(),
-0.001,
0.01,
0.1000001,
1e99,
-1e-99};
for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
Token::Value comparison = kComparisonTypes[c];
for (size_t i = 0; i < arraysize(inputs); i++) {
for (size_t j = 0; j < arraysize(inputs); j++) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddCompareICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register r0(0);
builder.LoadLiteral(inputs[i])
.StoreAccumulatorInRegister(r0)
.LoadLiteral(inputs[j])
.CompareOperation(comparison, r0, GetIndex(slot))
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate),
CompareC(comparison, inputs[i], inputs[j]));
if (tester.HasFeedbackMetadata()) {
MaybeObject feedback = callable.vector()->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(CompareOperationFeedback::kNumber,
feedback->ToSmi().value());
}
}
}
}
}
TEST(InterpreterBigIntComparisons) {
// This test only checks that the recorded type feedback is kBigInt.
AstBigInt inputs[] = {AstBigInt("0"), AstBigInt("-42"),
AstBigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")};
for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
Token::Value comparison = kComparisonTypes[c];
for (size_t i = 0; i < arraysize(inputs); i++) {
for (size_t j = 0; j < arraysize(inputs); j++) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
FeedbackSlot slot = feedback_spec.AddCompareICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
Register r0(0);
builder.LoadLiteral(inputs[i])
.StoreAccumulatorInRegister(r0)
.LoadLiteral(inputs[j])
.CompareOperation(comparison, r0, GetIndex(slot))
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
if (tester.HasFeedbackMetadata()) {
MaybeObject feedback = callable.vector()->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(CompareOperationFeedback::kBigInt,
feedback->ToSmi().value());
}
}
}
}
}
TEST(InterpreterStringComparisons) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
std::string inputs[] = {"A", "abc", "z", "", "Foo!", "Foo"};
for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
Token::Value comparison = kComparisonTypes[c];
for (size_t i = 0; i < arraysize(inputs); i++) {
for (size_t j = 0; j < arraysize(inputs); j++) {
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
CanonicalHandleScope canonical(isolate);
const char* lhs = inputs[i].c_str();
const char* rhs = inputs[j].c_str();
FeedbackVectorSpec feedback_spec(zone);
FeedbackSlot slot = feedback_spec.AddCompareICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
Register r0(0);
builder.LoadLiteral(ast_factory.GetOneByteString(lhs))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(ast_factory.GetOneByteString(rhs))
.CompareOperation(comparison, r0, GetIndex(slot))
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate),
CompareC(comparison, inputs[i], inputs[j]));
if (tester.HasFeedbackMetadata()) {
MaybeObject feedback = callable.vector()->Get(slot);
CHECK(feedback->IsSmi());
int const expected_feedback =
Token::IsOrderedRelationalCompareOp(comparison)
? CompareOperationFeedback::kString
: CompareOperationFeedback::kInternalizedString;
CHECK_EQ(expected_feedback, feedback->ToSmi().value());
}
}
}
}
}
static void LoadStringAndAddSpace(BytecodeArrayBuilder* builder,
AstValueFactory* ast_factory,
const char* cstr,
FeedbackSlot string_add_slot) {
Register string_reg = builder->register_allocator()->NewRegister();
(*builder)
.LoadLiteral(ast_factory->GetOneByteString(cstr))
.StoreAccumulatorInRegister(string_reg)
.LoadLiteral(ast_factory->GetOneByteString(" "))
.BinaryOperation(Token::Value::ADD, string_reg,
GetIndex(string_add_slot));
}
TEST(InterpreterMixedComparisons) {
// This test compares a HeapNumber with a String. The latter is
// convertible to a HeapNumber so comparison will be between numeric
// values except for the strict comparisons where no conversion is
// performed.
const char* inputs[] = {"-1.77", "-40.333", "0.01", "55.77e50", "2.01"};
enum WhichSideString { kLhsIsString, kRhsIsString };
enum StringType { kInternalizedStringConstant, kComputedString };
for (size_t c = 0; c < arraysize(kComparisonTypes); c++) {
Token::Value comparison = kComparisonTypes[c];
for (size_t i = 0; i < arraysize(inputs); i++) {
for (size_t j = 0; j < arraysize(inputs); j++) {
// We test the case where either the lhs or the rhs is a string...
for (WhichSideString which_side : {kLhsIsString, kRhsIsString}) {
// ... and the case when the string is internalized or computed.
for (StringType string_type :
{kInternalizedStringConstant, kComputedString}) {
const char* lhs_cstr = inputs[i];
const char* rhs_cstr = inputs[j];
double lhs = StringToDouble(lhs_cstr, ConversionFlags::NO_FLAGS);
double rhs = StringToDouble(rhs_cstr, ConversionFlags::NO_FLAGS);
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 0, &feedback_spec);
FeedbackSlot string_add_slot = feedback_spec.AddBinaryOpICSlot();
FeedbackSlot slot = feedback_spec.AddCompareICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
// lhs is in a register, rhs is in the accumulator.
Register lhs_reg = builder.register_allocator()->NewRegister();
if (which_side == kRhsIsString) {
// Comparison with HeapNumber on the lhs and String on the rhs.
builder.LoadLiteral(lhs).StoreAccumulatorInRegister(lhs_reg);
if (string_type == kInternalizedStringConstant) {
// rhs string is internalized.
builder.LoadLiteral(ast_factory.GetOneByteString(rhs_cstr));
} else {
CHECK_EQ(string_type, kComputedString);
// rhs string is not internalized (append a space to the end).
LoadStringAndAddSpace(&builder, &ast_factory, rhs_cstr,
string_add_slot);
}
break;
} else {
CHECK_EQ(which_side, kLhsIsString);
// Comparison with String on the lhs and HeapNumber on the rhs.
if (string_type == kInternalizedStringConstant) {
// lhs string is internalized
builder.LoadLiteral(ast_factory.GetOneByteString(lhs_cstr));
} else {
CHECK_EQ(string_type, kComputedString);
// lhs string is not internalized (append a space to the end).
LoadStringAndAddSpace(&builder, &ast_factory, lhs_cstr,
string_add_slot);
}
builder.StoreAccumulatorInRegister(lhs_reg);
builder.LoadLiteral(rhs);
}
builder.CompareOperation(comparison, lhs_reg, GetIndex(slot))
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array =
builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate),
CompareC(comparison, lhs, rhs, true));
if (tester.HasFeedbackMetadata()) {
MaybeObject feedback = callable.vector()->Get(slot);
CHECK(feedback->IsSmi());
// Comparison with a number and string collects kAny feedback.
CHECK_EQ(CompareOperationFeedback::kAny,
feedback->ToSmi().value());
}
}
}
}
}
}
}
TEST(InterpreterStrictNotEqual) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
const char* code_snippet =
"function f(lhs, rhs) {\n"
" return lhs !== rhs;\n"
"}\n"
"f(0, 0);\n";
InterpreterTester tester(isolate, code_snippet);
auto callable = tester.GetCallable<Handle<Object>, Handle<Object>>();
// Test passing different types.
const char* inputs[] = {"-1.77", "-40.333", "0.01", "55.77e5", "2.01"};
for (size_t i = 0; i < arraysize(inputs); i++) {
for (size_t j = 0; j < arraysize(inputs); j++) {
double lhs = StringToDouble(inputs[i], ConversionFlags::NO_FLAGS);
double rhs = StringToDouble(inputs[j], ConversionFlags::NO_FLAGS);
Handle<Object> lhs_obj = factory->NewNumber(lhs);
Handle<Object> rhs_obj = factory->NewStringFromAsciiChecked(inputs[j]);
Handle<Object> return_value =
callable(lhs_obj, rhs_obj).ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate),
CompareC(Token::Value::NE_STRICT, lhs, rhs, true));
}
}
// Test passing string types.
const char* inputs_str[] = {"A", "abc", "z", "", "Foo!", "Foo"};
for (size_t i = 0; i < arraysize(inputs_str); i++) {
for (size_t j = 0; j < arraysize(inputs_str); j++) {
Handle<Object> lhs_obj =
factory->NewStringFromAsciiChecked(inputs_str[i]);
Handle<Object> rhs_obj =
factory->NewStringFromAsciiChecked(inputs_str[j]);
Handle<Object> return_value =
callable(lhs_obj, rhs_obj).ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate),
CompareC(Token::Value::NE_STRICT, inputs_str[i], inputs_str[j]));
}
}
// Test passing doubles.
double inputs_number[] = {std::numeric_limits<double>::min(),
std::numeric_limits<double>::max(),
-0.001,
0.01,
0.1000001,
1e99,
-1e-99};
for (size_t i = 0; i < arraysize(inputs_number); i++) {
for (size_t j = 0; j < arraysize(inputs_number); j++) {
Handle<Object> lhs_obj = factory->NewNumber(inputs_number[i]);
Handle<Object> rhs_obj = factory->NewNumber(inputs_number[j]);
Handle<Object> return_value =
callable(lhs_obj, rhs_obj).ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate),
CompareC(Token::Value::NE_STRICT, inputs_number[i],
inputs_number[j]));
}
}
}
TEST(InterpreterCompareTypeOf) {
typedef TestTypeOfFlags::LiteralFlag LiteralFlag;
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Factory* factory = isolate->factory();
Zone* zone = handles.main_zone();
std::pair<Handle<Object>, LiteralFlag> inputs[] = {
{handle(Smi::FromInt(24), isolate), LiteralFlag::kNumber},
{factory->NewNumber(2.5), LiteralFlag::kNumber},
{factory->NewStringFromAsciiChecked("foo"), LiteralFlag::kString},
{factory
->NewConsString(factory->NewStringFromAsciiChecked("foo"),
factory->NewStringFromAsciiChecked("bar"))
.ToHandleChecked(),
LiteralFlag::kString},
{factory->prototype_string(), LiteralFlag::kString},
{factory->NewSymbol(), LiteralFlag::kSymbol},
{factory->true_value(), LiteralFlag::kBoolean},
{factory->false_value(), LiteralFlag::kBoolean},
{factory->undefined_value(), LiteralFlag::kUndefined},
{InterpreterTester::NewObject(
"(function() { return function() {}; })();"),
LiteralFlag::kFunction},
{InterpreterTester::NewObject("new Object();"), LiteralFlag::kObject},
{factory->null_value(), LiteralFlag::kObject},
};
const LiteralFlag kLiterals[] = {
#define LITERAL_FLAG(name, _) LiteralFlag::k##name,
TYPEOF_LITERAL_LIST(LITERAL_FLAG)
#undef LITERAL_FLAG
};
for (size_t l = 0; l < arraysize(kLiterals); l++) {
LiteralFlag literal_flag = kLiterals[l];
if (literal_flag == LiteralFlag::kOther) continue;
BytecodeArrayBuilder builder(zone, 1, 0);
builder.LoadAccumulatorWithRegister(builder.Receiver())
.CompareTypeOf(kLiterals[l])
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<Handle<Object>>();
for (size_t i = 0; i < arraysize(inputs); i++) {
Handle<Object> return_value = callable(inputs[i].first).ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate),
inputs[i].second == literal_flag);
}
}
}
TEST(InterpreterInstanceOf) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Factory* factory = isolate->factory();
Handle<i::String> name = factory->NewStringFromAsciiChecked("cons");
Handle<i::JSFunction> func = factory->NewFunctionForTest(name);
Handle<i::JSObject> instance = factory->NewJSObject(func);
Handle<i::Object> other = factory->NewNumber(3.3333);
Handle<i::Object> cases[] = {Handle<i::Object>::cast(instance), other};
for (size_t i = 0; i < arraysize(cases); i++) {
bool expected_value = (i == 0);
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
Register r0(0);
size_t case_entry = builder.AllocateDeferredConstantPoolEntry();
builder.SetDeferredConstantPoolEntry(case_entry, cases[i]);
builder.LoadConstantPoolEntry(case_entry).StoreAccumulatorInRegister(r0);
FeedbackSlot slot = feedback_spec.AddInstanceOfSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
size_t func_entry = builder.AllocateDeferredConstantPoolEntry();
builder.SetDeferredConstantPoolEntry(func_entry, func);
builder.LoadConstantPoolEntry(func_entry)
.CompareOperation(Token::Value::INSTANCEOF, r0, GetIndex(slot))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());
CHECK_EQ(return_value->BooleanValue(isolate), expected_value);
}
}
TEST(InterpreterTestIn) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
Zone* zone = handles.main_zone();
Factory* factory = isolate->factory();
AstValueFactory ast_factory(zone, isolate->ast_string_constants(),
HashSeed(isolate));
// Allocate an array
Handle<i::JSArray> array =
factory->NewJSArray(0, i::ElementsKind::PACKED_SMI_ELEMENTS);
// Check for these properties on the array object
const char* properties[] = {"length", "fuzzle", "x", "0"};
for (size_t i = 0; i < arraysize(properties); i++) {
bool expected_value = (i == 0);
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
Register r0(0);
builder.LoadLiteral(ast_factory.GetOneByteString(properties[i]))
.StoreAccumulatorInRegister(r0);
FeedbackSlot slot = feedback_spec.AddKeyedHasICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
size_t array_entry = builder.AllocateDeferredConstantPoolEntry();
builder.SetDeferredConstantPoolEntry(array_entry,<