[Interpreter] Add support for loading globals in the interpreter.

Adds LdaGlobal bytecode and augments BytecodeGenerator to load globals for
global variables and function calls.

Modified TestBytecodeGenerator to add the ability to specify that a bytecode
operand has an unknown value (used so we don't need to figure out the slot
index of a global). Also added a helper which checks equality of BytecodeArray
with the expected snipptets.

Modified TestInterpreter to allow it to take snippets of JS and have the
BytecodeGenerator generate the bytecode rather than having to build a
BytecodeArray manually. This is used to enable the global tests.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#30910}
This commit is contained in:
rmcilroy 2015-09-24 04:48:22 -07:00 committed by Commit bot
parent 809f6b15be
commit 8087c49dc7
14 changed files with 370 additions and 140 deletions

View File

@ -249,6 +249,12 @@ void BytecodeGraphBuilder::VisitStar(
}
void BytecodeGraphBuilder::VisitLdaGlobal(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitLoadIC(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();

View File

@ -315,6 +315,13 @@ Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
}
Node* InterpreterAssembler::CallRuntime(Runtime::FunctionId function_id,
Node* arg1) {
return raw_assembler_->CallRuntime1(function_id, arg1,
ContextTaggedPointer());
}
Node* InterpreterAssembler::CallRuntime(Runtime::FunctionId function_id,
Node* arg1, Node* arg2) {
return raw_assembler_->CallRuntime2(function_id, arg1, arg2,

View File

@ -104,6 +104,7 @@ class InterpreterAssembler {
Node* arg2, Node* arg3, Node* arg4, Node* arg5);
// Call runtime function.
Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1);
Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1, Node* arg2);
// Returns from the function.

View File

@ -152,6 +152,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(int slot_index) {
DCHECK(slot_index >= 0);
if (FitsInByteOperand(slot_index)) {
Output(Bytecode::kLdaGlobal, static_cast<uint8_t>(slot_index));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
Register object, int feedback_slot, LanguageMode language_mode) {
if (!is_sloppy(language_mode)) {

View File

@ -49,6 +49,9 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& LoadTrue();
BytecodeArrayBuilder& LoadFalse();
// Global loads to accumulator.
BytecodeArrayBuilder& LoadGlobal(int slot_index);
// Register-accumulator transfers.
BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg);
BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg);

View File

@ -32,8 +32,8 @@ Bytecode BytecodeArrayIterator::current_bytecode() const {
}
uint8_t BytecodeArrayIterator::GetOperand(int operand_index,
OperandType operand_type) const {
uint8_t BytecodeArrayIterator::GetRawOperand(int operand_index,
OperandType operand_type) const {
DCHECK_GE(operand_index, 0);
DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
DCHECK_EQ(operand_type,
@ -44,19 +44,19 @@ uint8_t BytecodeArrayIterator::GetOperand(int operand_index,
int8_t BytecodeArrayIterator::GetSmi8Operand(int operand_index) const {
uint8_t operand = GetOperand(operand_index, OperandType::kImm8);
uint8_t operand = GetRawOperand(operand_index, OperandType::kImm8);
return static_cast<int8_t>(operand);
}
int BytecodeArrayIterator::GetIndexOperand(int operand_index) const {
uint8_t operand = GetOperand(operand_index, OperandType::kIdx);
uint8_t operand = GetRawOperand(operand_index, OperandType::kIdx);
return static_cast<int>(operand);
}
Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
uint8_t operand = GetOperand(operand_index, OperandType::kReg);
uint8_t operand = GetRawOperand(operand_index, OperandType::kReg);
return Register::FromOperand(operand);
}

View File

@ -29,9 +29,11 @@ class BytecodeArrayIterator {
Register GetRegisterOperand(int operand_index) const;
Handle<Object> GetConstantForIndexOperand(int operand_index) const;
private:
uint8_t GetOperand(int operand_index, OperandType operand_type) const;
// Get the raw byte for the given operand. Note: you should prefer using the
// typed versions above which cast the return to an appropriate type.
uint8_t GetRawOperand(int operand_index, OperandType operand_type) const;
private:
Handle<BytecodeArray> bytecode_array_;
int bytecode_offset_;

View File

@ -251,7 +251,11 @@ void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
Variable* variable = proxy->var();
VisitVariableLoad(proxy->var());
}
void BytecodeGenerator::VisitVariableLoad(Variable* variable) {
switch (variable->location()) {
case VariableLocation::LOCAL: {
Register source(variable->index());
@ -265,7 +269,15 @@ void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
builder().LoadAccumulatorWithRegister(source);
break;
}
case VariableLocation::GLOBAL:
case VariableLocation::GLOBAL: {
// Global var, const, or let variable.
// TODO(rmcilroy): If context chain depth is short enough, do this using
// a generic version of LoadGlobalViaContextStub rather than calling the
// runtime.
DCHECK(variable->IsStaticGlobalObjectProperty());
builder().LoadGlobal(variable->index());
break;
}
case VariableLocation::UNALLOCATED:
case VariableLocation::CONTEXT:
case VariableLocation::LOOKUP:
@ -403,7 +415,15 @@ void BytecodeGenerator::VisitCall(Call* expr) {
builder().StoreAccumulatorInRegister(callee);
break;
}
case Call::GLOBAL_CALL:
case Call::GLOBAL_CALL: {
// Receiver is undefined for global calls.
builder().LoadUndefined().StoreAccumulatorInRegister(receiver);
// Load callee as a global variable.
VariableProxy* proxy = callee_expr->AsVariableProxy();
VisitVariableLoad(proxy->var());
builder().StoreAccumulatorInRegister(callee);
break;
}
case Call::LOOKUP_SLOT_CALL:
case Call::SUPER_CALL:
case Call::POSSIBLY_EVAL_CALL:

View File

@ -29,6 +29,7 @@ class BytecodeGenerator : public AstVisitor {
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitPropertyLoad(Register obj, Property* expr);
void VisitVariableLoad(Variable* variable);
inline BytecodeArrayBuilder& builder() { return builder_; }
inline Scope* scope() const { return scope_; }

View File

@ -36,6 +36,9 @@ namespace interpreter {
V(LdaTrue, OperandType::kNone) \
V(LdaFalse, OperandType::kNone) \
\
/* Load globals */ \
V(LdaGlobal, OperandType::kIdx) \
\
/* Register-accumulator transfers */ \
V(Ldar, OperandType::kReg) \
V(Star, OperandType::kReg) \

View File

@ -191,6 +191,18 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) {
}
// LdaGlobal <slot_index>
//
// Load the global at |slot_index| into the accumulator.
void Interpreter::DoLdaGlobal(compiler::InterpreterAssembler* assembler) {
Node* slot_index = __ BytecodeOperandIdx(0);
Node* smi_slot_index = __ SmiTag(slot_index);
Node* result = __ CallRuntime(Runtime::kLoadGlobalViaContext, smi_slot_index);
__ SetAccumulator(result);
__ Dispatch();
}
void Interpreter::DoPropertyLoadIC(Callable ic,
compiler::InterpreterAssembler* assembler) {
Node* code_target = __ HeapConstant(ic.code());

View File

@ -5,6 +5,7 @@
#include "src/v8.h"
#include "src/compiler.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/interpreter.h"
#include "test/cctest/cctest.h"
@ -57,6 +58,13 @@ class BytecodeGeneratorHelper {
};
// Helper macros for handcrafting bytecode sequences.
#define B(x) static_cast<uint8_t>(Bytecode::k##x)
#define U8(x) static_cast<uint8_t>((x) & 0xff)
#define R(x) static_cast<uint8_t>(-(x) & 0xff)
#define _ static_cast<uint8_t>(0x5a)
// Structure for containing expected bytecode snippets.
template<typename T>
struct ExpectedSnippet {
@ -70,17 +78,82 @@ struct ExpectedSnippet {
};
// Helper macros for handcrafting bytecode sequences.
#define B(x) static_cast<uint8_t>(Bytecode::k##x)
#define U8(x) static_cast<uint8_t>((x) & 0xff)
#define R(x) static_cast<uint8_t>(-(x) & 0xff)
static void CheckConstant(int expected, Object* actual) {
CHECK_EQ(expected, Smi::cast(actual)->value());
}
static void CheckConstant(double expected, Object* actual) {
CHECK_EQ(expected, HeapNumber::cast(actual)->value());
}
static void CheckConstant(const char* expected, Object* actual) {
Handle<String> expected_string =
CcTest::i_isolate()->factory()->NewStringFromAsciiChecked(expected);
CHECK(String::cast(actual)->Equals(*expected_string));
}
template <typename T>
static void CheckBytecodeArrayEqual(struct ExpectedSnippet<T> expected,
Handle<BytecodeArray> actual,
bool has_unknown = false) {
CHECK_EQ(actual->frame_size(), expected.frame_size);
CHECK_EQ(actual->parameter_count(), expected.parameter_count);
CHECK_EQ(actual->length(), expected.bytecode_length);
if (expected.constant_count == 0) {
CHECK_EQ(actual->constant_pool(), CcTest::heap()->empty_fixed_array());
} else {
CHECK_EQ(actual->constant_pool()->length(), expected.constant_count);
for (int i = 0; i < expected.constant_count; i++) {
CheckConstant(expected.constants[i], actual->constant_pool()->get(i));
}
}
BytecodeArrayIterator iterator(actual);
int i = 0;
while (!iterator.done()) {
int bytecode_index = i++;
Bytecode bytecode = iterator.current_bytecode();
if (Bytecodes::ToByte(bytecode) != expected.bytecode[bytecode_index]) {
std::ostringstream stream;
stream << "Check failed: expected bytecode [" << bytecode_index
<< "] to be " << Bytecodes::ToString(static_cast<Bytecode>(
expected.bytecode[bytecode_index]))
<< " but got " << Bytecodes::ToString(bytecode);
FATAL(stream.str().c_str());
}
for (int j = 0; j < Bytecodes::NumberOfOperands(bytecode); ++j, ++i) {
uint8_t raw_operand =
iterator.GetRawOperand(j, Bytecodes::GetOperandType(bytecode, j));
if (has_unknown) {
// Check actual bytecode array doesn't have the same byte as the
// one we use to specify an unknown byte.
CHECK_NE(raw_operand, _);
if (expected.bytecode[i] == _) {
continue;
}
}
if (raw_operand != expected.bytecode[i]) {
std::ostringstream stream;
stream << "Check failed: expected operand [" << j << "] for bytecode ["
<< bytecode_index << "] to be "
<< static_cast<unsigned int>(expected.bytecode[i]) << " but got "
<< static_cast<unsigned int>(raw_operand);
FATAL(stream.str().c_str());
}
}
iterator.Advance();
}
}
TEST(PrimitiveReturnStatements) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<void*> snippets[] = {
ExpectedSnippet<int> snippets[] = {
{"", 0, 1, 2, {B(LdaUndefined), B(Return)}, 0},
{"return;", 0, 1, 2, {B(LdaUndefined), B(Return)}, 0},
{"return null;", 0, 1, 2, {B(LdaNull), B(Return)}, 0},
@ -95,14 +168,9 @@ TEST(PrimitiveReturnStatements) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool(), CcTest::heap()->empty_fixed_array());
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
@ -111,7 +179,7 @@ TEST(PrimitiveExpressions) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<void*> snippets[] = {
ExpectedSnippet<int> snippets[] = {
{"var x = 0; return x;",
kPointerSize,
1,
@ -142,14 +210,9 @@ TEST(PrimitiveExpressions) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool(), CcTest::heap()->empty_fixed_array());
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
@ -158,7 +221,7 @@ TEST(Parameters) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<void*> snippets[] = {
ExpectedSnippet<int> snippets[] = {
{"function f() { return this; }",
0, 1, 3, {B(Ldar), R(helper.kLastParamIndex), B(Return)}, 0},
{"function f(arg1) { return arg1; }",
@ -173,14 +236,9 @@ TEST(Parameters) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunction(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool(), CcTest::heap()->empty_fixed_array());
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
@ -219,18 +277,9 @@ TEST(Constants) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
CHECK_EQ(Smi::cast(ba->constant_pool()->get(j))->value(),
snippets[i].constants[j]);
}
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
@ -268,18 +317,9 @@ TEST(Constants) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
CHECK_EQ(HeapNumber::cast(ba->constant_pool()->get(j))->value(),
snippets[i].constants[j]);
}
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
@ -315,19 +355,9 @@ TEST(Constants) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
Handle<String> expected = helper.factory()->NewStringFromAsciiChecked(
snippets[i].constants[j]);
CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected));
}
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
}
@ -405,19 +435,9 @@ TEST(PropertyLoads) {
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
Handle<String> expected = helper.factory()->NewStringFromAsciiChecked(
snippets[i].constants[j]);
CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected));
}
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
@ -509,19 +529,9 @@ TEST(PropertyStores) {
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
Handle<String> expected =
helper.factory()->NewStringFromAsciiChecked(snippets[i].constants[j]);
CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected));
}
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
@ -592,19 +602,84 @@ TEST(PropertyCall) {
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
Handle<String> expected =
helper.factory()->NewStringFromAsciiChecked(snippets[i].constants[j]);
CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected));
}
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(LoadGlobal) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<const char*> snippets[] = {
{"var a = 1;\nfunction f() { return a; }\nf()",
0, 1, 3,
{
B(LdaGlobal), _,
B(Return)
},
},
{"function t() { }\nfunction f() { return t; }\nf()",
0, 1, 3,
{
B(LdaGlobal), _,
B(Return)
},
},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
bytecode_array->Print();
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
TEST(CallGlobal) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<const char*> snippets[] = {
{"function t() { }\nfunction f() { return t(); }\nf()",
2 * kPointerSize, 1, 12,
{
B(LdaUndefined),
B(Star), R(1),
B(LdaGlobal), _,
B(Star), R(0),
B(Call), R(0), R(1), U8(0),
B(Return)
},
},
{"function t(a, b, c) { }\nfunction f() { return t(1, 2, 3); }\nf()",
5 * kPointerSize, 1, 24,
{
B(LdaUndefined),
B(Star), R(1),
B(LdaGlobal), _,
B(Star), R(0),
B(LdaSmi8), U8(1),
B(Star), R(2),
B(LdaSmi8), U8(2),
B(Star), R(3),
B(LdaSmi8), U8(3),
B(Star), R(4),
B(Call), R(0), R(1), U8(3),
B(Return)
},
},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}

View File

@ -50,19 +50,41 @@ class InterpreterCallable {
};
static const char* kFunctionName = "f";
class InterpreterTester {
public:
InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector =
MaybeHandle<TypeFeedbackVector>())
InterpreterTester(Isolate* isolate, const char* source,
MaybeHandle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector)
: isolate_(isolate),
source_(source),
bytecode_(bytecode),
feedback_vector_(feedback_vector) {
i::FLAG_vector_stores = true;
i::FLAG_ignition = true;
i::FLAG_always_opt = false;
// Set ignition filter flag via SetFlagsFromString to avoid double-free
// (or potential leak with StrDup() based on ownership confusion).
ScopedVector<char> ignition_filter(64);
SNPrintF(ignition_filter, "--ignition-filter=%s", kFunctionName);
FlagList::SetFlagsFromString(ignition_filter.start(),
ignition_filter.length());
// Ensure handler table is generated.
isolate->interpreter()->Initialize();
}
InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector =
MaybeHandle<TypeFeedbackVector>())
: InterpreterTester(isolate, nullptr, bytecode, feedback_vector) {}
InterpreterTester(Isolate* isolate, const char* source)
: InterpreterTester(isolate, source, MaybeHandle<BytecodeArray>(),
MaybeHandle<TypeFeedbackVector>()) {}
virtual ~InterpreterTester() {}
template <class... A>
@ -70,28 +92,49 @@ class InterpreterTester {
return InterpreterCallable<A...>(isolate_, GetBytecodeFunction<A...>());
}
Handle<Object> NewObject(const char* script) {
static Handle<Object> NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
static Handle<String> GetName(Isolate* isolate, const char* name) {
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(name);
return isolate->factory()->string_table()->LookupString(isolate, result);
}
static std::string function_name() {
return std::string(kFunctionName);
}
private:
Isolate* isolate_;
Handle<BytecodeArray> bytecode_;
const char* source_;
MaybeHandle<BytecodeArray> bytecode_;
MaybeHandle<TypeFeedbackVector> feedback_vector_;
template <class... A>
Handle<JSFunction> GetBytecodeFunction() {
int arg_count = sizeof...(A);
std::string function_text("(function(");
for (int i = 0; i < arg_count; i++) {
function_text += i == 0 ? "a" : ", a";
Handle<JSFunction> function;
if (source_) {
CompileRun(source_);
Local<Function> api_function =
Local<Function>::Cast(CcTest::global()->Get(v8_str(kFunctionName)));
function = v8::Utils::OpenHandle(*api_function);
} else {
int arg_count = sizeof...(A);
std::string source("(function " + function_name() + "(");
for (int i = 0; i < arg_count; i++) {
source += i == 0 ? "a" : ", a";
}
source += "){})";
function = v8::Utils::OpenHandle(
*v8::Handle<v8::Function>::Cast(CompileRun(source.c_str())));
function->ReplaceCode(
*isolate_->builtins()->InterpreterEntryTrampoline());
}
function_text += "){})";
Handle<JSFunction> function = v8::Utils::OpenHandle(
*v8::Handle<v8::Function>::Cast(CompileRun(function_text.c_str())));
function->ReplaceCode(*isolate_->builtins()->InterpreterEntryTrampoline());
function->shared()->set_function_data(*bytecode_);
if (!bytecode_.is_null()) {
function->shared()->set_function_data(*bytecode_.ToHandleChecked());
}
if (!feedback_vector_.is_null()) {
function->shared()->set_feedback_vector(
*feedback_vector_.ToHandleChecked());
@ -470,6 +513,40 @@ TEST(InterpreterParameter8) {
}
TEST(InterpreterLoadGlobal) {
HandleAndZoneScope handles;
// Test loading a global.
std::string source(
"var global = 321;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return global;\n"
"}");
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(321));
}
TEST(InterpreterCallGlobal) {
HandleAndZoneScope handles;
// 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(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(15));
}
TEST(InterpreterLoadNamedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
@ -495,7 +572,7 @@ TEST(InterpreterLoadNamedProperty) {
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject("({ val : 123 })");
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));
@ -505,16 +582,20 @@ TEST(InterpreterLoadNamedProperty) {
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to polymorphic IC.
Handle<Object> object2 = tester.NewObject("({ val : 456, other : 123 })");
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 = tester.NewObject("({ val : 789, val2 : 123 })");
Handle<Object> object3 =
InterpreterTester::NewObject("({ val : 789, val2 : 123 })");
callable(object3).ToHandleChecked();
Handle<Object> object4 = tester.NewObject("({ val : 789, val3 : 123 })");
Handle<Object> object4 =
InterpreterTester::NewObject("({ val : 789, val3 : 123 })");
callable(object4).ToHandleChecked();
Handle<Object> object5 = tester.NewObject("({ val : 789, val4 : 123 })");
Handle<Object> object5 =
InterpreterTester::NewObject("({ val : 789, val4 : 123 })");
return_val = callable(object5).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}
@ -545,7 +626,7 @@ TEST(InterpreterLoadKeyedProperty) {
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject("({ key : 123 })");
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));
@ -555,7 +636,8 @@ TEST(InterpreterLoadKeyedProperty) {
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to megamorphic IC.
Handle<Object> object3 = tester.NewObject("({ key : 789, val2 : 123 })");
Handle<Object> object3 =
InterpreterTester::NewObject("({ key : 789, val2 : 123 })");
return_val = callable(object3).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}
@ -587,7 +669,7 @@ TEST(InterpreterStoreNamedProperty) {
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject("({ val : 123 })");
Handle<Object> object = InterpreterTester::NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> result;
callable(object).ToHandleChecked();
@ -600,17 +682,21 @@ TEST(InterpreterStoreNamedProperty) {
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to polymorphic IC.
Handle<Object> object2 = tester.NewObject("({ val : 456, other : 123 })");
Handle<Object> object2 =
InterpreterTester::NewObject("({ val : 456, other : 123 })");
callable(object2).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to megamorphic IC.
Handle<Object> object3 = tester.NewObject("({ val : 789, val2 : 123 })");
Handle<Object> object3 =
InterpreterTester::NewObject("({ val : 789, val2 : 123 })");
callable(object3).ToHandleChecked();
Handle<Object> object4 = tester.NewObject("({ val : 789, val3 : 123 })");
Handle<Object> object4 =
InterpreterTester::NewObject("({ val : 789, val3 : 123 })");
callable(object4).ToHandleChecked();
Handle<Object> object5 = tester.NewObject("({ val : 789, val4 : 123 })");
Handle<Object> object5 =
InterpreterTester::NewObject("({ val : 789, val4 : 123 })");
callable(object5).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object5, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
@ -643,7 +729,7 @@ TEST(InterpreterStoreKeyedProperty) {
InterpreterTester tester(isolate, bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject("({ val : 123 })");
Handle<Object> object = InterpreterTester::NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> result;
callable(object).ToHandleChecked();
@ -656,7 +742,8 @@ TEST(InterpreterStoreKeyedProperty) {
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
// Test transition to megamorphic IC.
Handle<Object> object2 = tester.NewObject("({ val : 456, other : 123 })");
Handle<Object> object2 =
InterpreterTester::NewObject("({ val : 456, other : 123 })");
callable(object2).ToHandleChecked();
CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result));
CHECK_EQ(Smi::cast(*result), Smi::FromInt(999));
@ -692,7 +779,7 @@ TEST(InterpreterCall) {
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject(
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));
@ -714,7 +801,7 @@ TEST(InterpreterCall) {
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject(
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() {"
" this.val = 1234;"
" this.func = function() { return this.val; };"
@ -745,7 +832,7 @@ TEST(InterpreterCall) {
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject(
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() { "
" this.func = function(a, b) { return a - b; }"
"})()");
@ -791,7 +878,7 @@ TEST(InterpreterCall) {
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject(
Handle<Object> object = InterpreterTester::NewObject(
"new (function Obj() { "
" this.prefix = \"prefix_\";"
" this.func = function(a, b, c, d, e, f, g, h, i, j) {"

View File

@ -39,6 +39,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
Register reg(0);
builder.LoadAccumulatorWithRegister(reg).StoreAccumulatorInRegister(reg);
// Emit global load operations.
builder.LoadGlobal(1);
// Emit load / store property operations.
builder.LoadNamedProperty(reg, 0, LanguageMode::SLOPPY)
.LoadKeyedProperty(reg, 0, LanguageMode::SLOPPY)