[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:
parent
809f6b15be
commit
8087c49dc7
@ -249,6 +249,12 @@ void BytecodeGraphBuilder::VisitStar(
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::VisitLdaGlobal(
|
||||
const interpreter::BytecodeArrayIterator& iterator) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
void BytecodeGraphBuilder::VisitLoadIC(
|
||||
const interpreter::BytecodeArrayIterator& iterator) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
|
@ -32,7 +32,7 @@ Bytecode BytecodeArrayIterator::current_bytecode() const {
|
||||
}
|
||||
|
||||
|
||||
uint8_t BytecodeArrayIterator::GetOperand(int operand_index,
|
||||
uint8_t BytecodeArrayIterator::GetRawOperand(int operand_index,
|
||||
OperandType operand_type) const {
|
||||
DCHECK_GE(operand_index, 0);
|
||||
DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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_; }
|
||||
|
@ -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) \
|
||||
|
@ -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());
|
||||
|
@ -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,20 +602,85 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace interpreter
|
||||
|
@ -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() {
|
||||
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 function_text("(function(");
|
||||
std::string source("(function " + function_name() + "(");
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
function_text += i == 0 ? "a" : ", a";
|
||||
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) {"
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user