Commit 688eacda authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for global declarations and load/store of global variables

Implements support for declaring global variables. Also adds support for loading
from and storing to both global and unallocated global variables.  Adds the
following bytecodes:
 - StoreGlobal
 - LoadContextSlot

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#31166}
parent f29705db
......@@ -255,6 +255,18 @@ void BytecodeGraphBuilder::VisitLdaGlobal(
}
void BytecodeGraphBuilder::VisitStaGlobal(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitLdaContextSlot(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitLoadICSloppy(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
......
......@@ -291,14 +291,11 @@ Node* InterpreterAssembler::LoadObjectField(Node* object, int offset) {
}
Node* InterpreterAssembler::LoadContextSlot(Node* context, int slot_index) {
return raw_assembler_->Load(kMachAnyTagged, context,
IntPtrConstant(Context::SlotOffset(slot_index)));
}
Node* InterpreterAssembler::LoadContextSlot(int slot_index) {
return LoadContextSlot(ContextTaggedPointer(), slot_index);
Node* InterpreterAssembler::LoadContextSlot(Node* context, Node* slot_index) {
Node* offset =
IntPtrAdd(WordShl(slot_index, kPointerSizeLog2),
Int32Constant(Context::kHeaderSize - kHeapObjectTag));
return raw_assembler_->Load(kMachAnyTagged, context, offset);
}
......@@ -314,8 +311,7 @@ Node* InterpreterAssembler::LoadTypeFeedbackVector() {
}
Node* InterpreterAssembler::CallN(CallDescriptor* descriptor,
Node* code_target,
Node* InterpreterAssembler::CallN(CallDescriptor* descriptor, Node* code_target,
Node** args) {
Node* stack_pointer_before_call = nullptr;
if (FLAG_debug_code) {
......@@ -508,8 +504,8 @@ void InterpreterAssembler::DispatchTo(Node* new_bytecode_offset) {
}
void InterpreterAssembler::AbortIfWordNotEqual(
Node* lhs, Node* rhs, BailoutReason bailout_reason) {
void InterpreterAssembler::AbortIfWordNotEqual(Node* lhs, Node* rhs,
BailoutReason bailout_reason) {
RawMachineAssembler::Label match, no_match;
Node* condition = raw_assembler_->WordEqual(lhs, rhs);
raw_assembler_->Branch(condition, &match, &no_match);
......
......@@ -90,11 +90,8 @@ class InterpreterAssembler {
// Load a field from an object on the heap.
Node* LoadObjectField(Node* object, int offset);
// Load |slot_index| from a context.
Node* LoadContextSlot(Node* context, int slot_index);
// Load |slot_index| from the current context.
Node* LoadContextSlot(int slot_index);
// Load |slot_index| from |context|.
Node* LoadContextSlot(Node* context, Node* slot_index);
// Load the TypeFeedbackVector for the current function.
Node* LoadTypeFeedbackVector();
......
......@@ -181,6 +181,7 @@ class InterpreterFrameConstants : public AllStatic {
static const int kLastParamFromRegisterPointer =
StandardFrameConstants::kFixedFrameSize + kPointerSize;
static const int kFunctionFromRegisterPointer = kPointerSize;
static const int kContextFromRegisterPointer = 2 * kPointerSize;
};
......
......@@ -241,6 +241,35 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(int slot_index) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal(
int slot_index, LanguageMode language_mode) {
if (!is_sloppy(language_mode)) {
UNIMPLEMENTED();
}
DCHECK(slot_index >= 0);
if (FitsInIdx8Operand(slot_index)) {
Output(Bytecode::kStaGlobal, static_cast<uint8_t>(slot_index));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(Register context,
int slot_index) {
DCHECK(slot_index >= 0);
if (FitsInIdx8Operand(slot_index)) {
Output(Bytecode::kLdaContextSlot, context.ToOperand(),
static_cast<uint8_t>(slot_index));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
Register object, int feedback_slot, LanguageMode language_mode) {
Bytecode bytecode = BytecodeForLoadIC(language_mode);
......@@ -542,7 +571,9 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
return static_cast<uint8_t>(operand_value) == operand_value;
case OperandType::kReg8: {
Register reg = Register::FromOperand(static_cast<uint8_t>(operand_value));
if (reg.is_parameter()) {
if (reg.is_function_context()) {
return true;
} else if (reg.is_parameter()) {
int parameter_index = reg.ToParameterIndex(parameter_count_);
return parameter_index >= 0 && parameter_index < parameter_count_;
} else {
......
......@@ -47,8 +47,12 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& LoadTrue();
BytecodeArrayBuilder& LoadFalse();
// Global loads to accumulator.
// Global loads to accumulator and stores from accumulator.
BytecodeArrayBuilder& LoadGlobal(int slot_index);
BytecodeArrayBuilder& StoreGlobal(int slot_index, LanguageMode language_mode);
// Load the object at |slot_index| in |context| into the accumulator.
BytecodeArrayBuilder& LoadContextSlot(Register context, int slot_index);
// Register-accumulator transfers.
BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg);
......
......@@ -93,7 +93,9 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
: builder_(isolate, zone),
info_(nullptr),
scope_(nullptr),
control_scope_(nullptr) {
globals_(0, zone),
control_scope_(nullptr),
current_context_(Register::function_context()) {
InitializeAstVisitor(isolate, zone);
}
......@@ -145,11 +147,21 @@ void BytecodeGenerator::VisitBlock(Block* node) {
void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
Variable* variable = decl->proxy()->var();
VariableMode mode = decl->mode();
bool hole_init = mode == CONST || mode == CONST_LEGACY || mode == LET;
if (hole_init) {
UNIMPLEMENTED();
}
switch (variable->location()) {
case VariableLocation::GLOBAL:
case VariableLocation::UNALLOCATED:
UNIMPLEMENTED();
case VariableLocation::UNALLOCATED: {
Handle<Oddball> value = variable->binding_needs_init()
? isolate()->factory()->the_hole_value()
: isolate()->factory()->undefined_value();
globals()->push_back(variable->name());
globals()->push_back(value);
break;
}
case VariableLocation::PARAMETER:
case VariableLocation::LOCAL:
// Details stored in scope, i.e. variable index.
......@@ -163,7 +175,24 @@ void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
UNIMPLEMENTED();
Variable* variable = decl->proxy()->var();
switch (variable->location()) {
case VariableLocation::GLOBAL:
case VariableLocation::UNALLOCATED: {
Handle<SharedFunctionInfo> function = Compiler::GetSharedFunctionInfo(
decl->fun(), info()->script(), info());
// Check for stack-overflow exception.
if (function.is_null()) return SetStackOverflow();
globals()->push_back(variable->name());
globals()->push_back(function);
break;
}
case VariableLocation::PARAMETER:
case VariableLocation::LOCAL:
case VariableLocation::CONTEXT:
case VariableLocation::LOOKUP:
UNIMPLEMENTED();
}
}
......@@ -177,6 +206,34 @@ void BytecodeGenerator::VisitExportDeclaration(ExportDeclaration* decl) {
}
void BytecodeGenerator::VisitDeclarations(
ZoneList<Declaration*>* declarations) {
DCHECK(globals()->empty());
AstVisitor::VisitDeclarations(declarations);
if (globals()->empty()) return;
int array_index = 0;
Handle<FixedArray> data = isolate()->factory()->NewFixedArray(
static_cast<int>(globals()->size()), TENURED);
for (Handle<Object> obj : *globals()) data->set(array_index++, *obj);
int encoded_flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) |
DeclareGlobalsNativeFlag::encode(info()->is_native()) |
DeclareGlobalsLanguageMode::encode(language_mode());
TemporaryRegisterScope temporary_register_scope(&builder_);
Register pairs = temporary_register_scope.NewRegister();
builder()->LoadLiteral(data);
builder()->StoreAccumulatorInRegister(pairs);
Register flags = temporary_register_scope.NewRegister();
builder()->LoadLiteral(Smi::FromInt(encoded_flags));
builder()->StoreAccumulatorInRegister(flags);
DCHECK(flags.index() == pairs.index() + 1);
builder()->CallRuntime(Runtime::kDeclareGlobals, pairs, 2);
globals()->clear();
}
void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
Visit(stmt->expression());
}
......@@ -391,11 +448,12 @@ void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) {
VisitVariableLoad(proxy->var());
VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot());
}
void BytecodeGenerator::VisitVariableLoad(Variable* variable) {
void BytecodeGenerator::VisitVariableLoad(Variable* variable,
FeedbackVectorSlot slot) {
switch (variable->location()) {
case VariableLocation::LOCAL: {
Register source(variable->index());
......@@ -418,7 +476,66 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable) {
builder()->LoadGlobal(variable->index());
break;
}
case VariableLocation::UNALLOCATED:
case VariableLocation::UNALLOCATED: {
TemporaryRegisterScope temporary_register_scope(&builder_);
Register obj = temporary_register_scope.NewRegister();
builder()->LoadContextSlot(current_context(),
Context::GLOBAL_OBJECT_INDEX);
builder()->StoreAccumulatorInRegister(obj);
builder()->LoadLiteral(variable->name());
builder()->LoadNamedProperty(obj, feedback_index(slot), language_mode());
break;
}
case VariableLocation::CONTEXT:
case VariableLocation::LOOKUP:
UNIMPLEMENTED();
}
}
void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
FeedbackVectorSlot slot) {
switch (variable->location()) {
case VariableLocation::LOCAL: {
// TODO(rmcilroy): support const mode initialization.
Register destination(variable->index());
builder()->StoreAccumulatorInRegister(destination);
break;
}
case VariableLocation::PARAMETER: {
// The parameter indices are shifted by 1 (receiver is variable
// index -1 but is parameter index 0 in BytecodeArrayBuilder).
Register destination(builder()->Parameter(variable->index() + 1));
builder()->StoreAccumulatorInRegister(destination);
break;
}
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()->StoreGlobal(variable->index(), language_mode());
break;
}
case VariableLocation::UNALLOCATED: {
TemporaryRegisterScope temporary_register_scope(&builder_);
Register value = temporary_register_scope.NewRegister();
Register obj = temporary_register_scope.NewRegister();
Register name = temporary_register_scope.NewRegister();
// TODO(rmcilroy): Investigate whether we can avoid having to stash the
// value in a register.
builder()->StoreAccumulatorInRegister(value);
builder()->LoadContextSlot(current_context(),
Context::GLOBAL_OBJECT_INDEX);
builder()->StoreAccumulatorInRegister(obj);
builder()->LoadLiteral(variable->name());
builder()->StoreAccumulatorInRegister(name);
builder()->LoadAccumulatorWithRegister(value);
builder()->StoreNamedProperty(obj, name, feedback_index(slot),
language_mode());
break;
}
case VariableLocation::CONTEXT:
case VariableLocation::LOOKUP:
UNIMPLEMENTED();
......@@ -474,9 +591,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
switch (assign_type) {
case VARIABLE: {
Variable* variable = expr->target()->AsVariableProxy()->var();
DCHECK(variable->location() == VariableLocation::LOCAL);
Register destination(variable->index());
builder()->StoreAccumulatorInRegister(destination);
VisitVariableAssignment(variable, slot);
break;
}
case NAMED_PROPERTY:
......@@ -560,7 +675,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
builder()->LoadUndefined().StoreAccumulatorInRegister(receiver);
// Load callee as a global variable.
VariableProxy* proxy = callee_expr->AsVariableProxy();
VisitVariableLoad(proxy->var());
VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot());
builder()->StoreAccumulatorInRegister(callee);
break;
}
......@@ -741,6 +856,9 @@ int BytecodeGenerator::feedback_index(FeedbackVectorSlot slot) const {
return info()->feedback_vector()->GetIndex(slot);
}
Register BytecodeGenerator::current_context() const { return current_context_; }
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -24,6 +24,9 @@ class BytecodeGenerator : public AstVisitor {
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
// Visiting function for declarations list is overridden.
void VisitDeclarations(ZoneList<Declaration*>* declarations) override;
private:
class ControlScope;
class ControlScopeForIteration;
......@@ -32,7 +35,8 @@ class BytecodeGenerator : public AstVisitor {
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitPropertyLoad(Register obj, Property* expr);
void VisitVariableLoad(Variable* variable);
void VisitVariableLoad(Variable* variable, FeedbackVectorSlot slot);
void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot);
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
......@@ -46,15 +50,21 @@ class BytecodeGenerator : public AstVisitor {
inline void set_control_scope(ControlScope* scope) { control_scope_ = scope; }
inline CompilationInfo* info() const { return info_; }
inline void set_info(CompilationInfo* info) { info_ = info; }
ZoneVector<Handle<Object>>* globals() { return &globals_; }
LanguageMode language_mode() const;
Strength language_mode_strength() const;
int feedback_index(FeedbackVectorSlot slot) const;
Register current_context() const;
BytecodeArrayBuilder builder_;
CompilationInfo* info_;
Scope* scope_;
ZoneVector<Handle<Object>> globals_;
ControlScope* control_scope_;
// TODO(rmcilroy): Encapsulate this in an environment object.
Register current_context_;
};
} // namespace interpreter
......
......@@ -266,6 +266,8 @@ std::ostream& operator<<(std::ostream& os, const OperandSize& operand_size) {
static const int kLastParamRegisterIndex =
-InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
static const int kFunctionContextRegisterIndex =
-InterpreterFrameConstants::kContextFromRegisterPointer / kPointerSize;
// Registers occupy range 0-127 in 8-bit value leaving 128 unused values.
......@@ -291,6 +293,16 @@ int Register::ToParameterIndex(int parameter_count) const {
}
Register Register::function_context() {
return Register(kFunctionContextRegisterIndex);
}
bool Register::is_function_context() const {
return index() == kFunctionContextRegisterIndex;
}
int Register::MaxParameterIndex() { return kMaxParameterIndex; }
......
......@@ -43,8 +43,12 @@ namespace interpreter {
V(LdaTrue, OperandType::kNone) \
V(LdaFalse, OperandType::kNone) \
\
/* Load globals */ \
/* Globals */ \
V(LdaGlobal, OperandType::kIdx8) \
V(StaGlobal, OperandType::kIdx8) \
\
/* Context operations */ \
V(LdaContextSlot, OperandType::kReg8, OperandType::kIdx8) \
\
/* Register-accumulator transfers */ \
V(Ldar, OperandType::kReg8) \
......@@ -163,6 +167,10 @@ class Register {
int ToParameterIndex(int parameter_count) const;
static int MaxParameterIndex();
// Returns the register for the function's outer context.
static Register function_context();
bool is_function_context() const;
static Register FromOperand(uint8_t operand);
uint8_t ToOperand() const;
......
......@@ -196,6 +196,31 @@ void Interpreter::DoLdaGlobal(compiler::InterpreterAssembler* assembler) {
}
// StaGlobal <slot_index>
//
// Store the global at |slot_index| with the value in the the accumulator.
void Interpreter::DoStaGlobal(compiler::InterpreterAssembler* assembler) {
Node* slot_index = __ BytecodeOperandIdx8(0);
Node* smi_slot_index = __ SmiTag(slot_index);
Node* value = __ GetAccumulator();
__ CallRuntime(Runtime::kStoreGlobalViaContext_Sloppy, smi_slot_index, value);
__ Dispatch();
}
// LdaContextSlot <context> <slot_index>
//
// Load the object in |slot_index| of |context| into the accumulator.
void Interpreter::DoLdaContextSlot(compiler::InterpreterAssembler* assembler) {
Node* reg_index = __ BytecodeOperandReg8(0);
Node* context = __ LoadRegister(reg_index);
Node* slot_index = __ BytecodeOperandIdx8(1);
Node* result = __ LoadContextSlot(context, slot_index);
__ SetAccumulator(result);
__ Dispatch();
}
void Interpreter::DoPropertyLoadIC(Callable ic,
compiler::InterpreterAssembler* assembler) {
Node* code_target = __ HeapConstant(ic.code());
......
......@@ -35,6 +35,16 @@ class BytecodeGeneratorHelper {
Factory* factory() { return CcTest::i_isolate()->factory(); }
Handle<BytecodeArray> MakeTopLevelBytecode(const char* source) {
const char* old_ignition_filter = i::FLAG_ignition_filter;
i::FLAG_ignition_filter = "*";
Local<v8::Script> script = v8_compile(source);
i::FLAG_ignition_filter = old_ignition_filter;
i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*script);
return handle(js_function->shared()->bytecode_array(), CcTest::i_isolate());
}
Handle<BytecodeArray> MakeBytecode(const char* script,
const char* function_name) {
CompileRun(script);
......@@ -118,12 +128,14 @@ static void CheckBytecodeArrayEqual(struct ExpectedSnippet<T> expected,
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));
if (expected.constant_count != -1) {
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));
}
}
}
......@@ -264,7 +276,21 @@ TEST(Parameters) {
{"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return arg4; }",
0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 3), B(Return)}, 0},
{"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return this; }",
0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 7), B(Return)}, 0}
0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 7), B(Return)}, 0},
{"function f(arg1) { arg1 = 1; }",
0, 2, 6,
{B(LdaSmi8), U8(1), //
B(Star), R(helper.kLastParamIndex), //
B(LdaUndefined), //
B(Return)},
0},
{"function f(arg1, arg2, arg3, arg4) { arg2 = 1; }",
0, 5, 6,
{B(LdaSmi8), U8(1), //
B(Star), R(helper.kLastParamIndex - 2), //
B(LdaUndefined), //
B(Return)},
0},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
......@@ -691,7 +717,7 @@ TEST(PropertyStores) {
TEST(PropertyCall) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper; //
BytecodeGeneratorHelper helper;
Zone zone;
FeedbackVectorSpec feedback_spec(&zone);
......@@ -802,6 +828,45 @@ TEST(LoadGlobal) {
}
TEST(StoreGlobal) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<int> snippets[] = {
{
"var a = 1;\nfunction f() { a = 2; }\nf()",
0,
1,
6,
{
B(LdaSmi8), U8(2), //
B(StaGlobal), _, //
B(LdaUndefined), //
B(Return) //
},
},
{
"var a = \"test\"; function f(b) { a = b; }\nf(\"global\")",
0,
2,
6,
{
B(Ldar), R(helper.kLastParamIndex), //
B(StaGlobal), _, //
B(LdaUndefined), //
B(Return) //
},
},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
TEST(CallGlobal) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
......@@ -852,6 +917,110 @@ TEST(CallGlobal) {
}
TEST(LoadUnallocated) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Zone zone;
int context_reg = Register::function_context().index();
int global_index = Context::GLOBAL_OBJECT_INDEX;
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot1 = feedback_spec.AddStoreICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
ExpectedSnippet<const char*> snippets[] = {
{"a = 1;\nfunction f() { return a; }\nf()",
1 * kPointerSize,
1,
11,
{B(LdaContextSlot), R(context_reg), U8(global_index), //
B(Star), R(0), //
B(LdaConstant), U8(0), //
B(LoadICSloppy), R(0), U8(vector->GetIndex(slot1)), //
B(Return)},
1,
{"a"}},
{"function f() { return t; }\nt = 1;\nf()",
1 * kPointerSize,
1,
11,
{B(LdaContextSlot), R(context_reg), U8(global_index), //
B(Star), R(0), //
B(LdaConstant), U8(0), //
B(LoadICSloppy), R(0), U8(vector->GetIndex(slot1)), //
B(Return)},
1,
{"t"}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
TEST(StoreUnallocated) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Zone zone;
int context_reg = Register::function_context().index();
int global_index = Context::GLOBAL_OBJECT_INDEX;
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot1 = feedback_spec.AddStoreICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
ExpectedSnippet<const char*> snippets[] = {
{"a = 1;\nfunction f() { a = 2; }\nf()",
3 * kPointerSize,
1,
21,
{B(LdaSmi8), U8(2), //
B(Star), R(0), //
B(LdaContextSlot), R(context_reg), U8(global_index), //
B(Star), R(1), //
B(LdaConstant), U8(0), //
B(Star), R(2), //
B(Ldar), R(0), //
B(StoreICSloppy), R(1), R(2), U8(vector->GetIndex(slot1)), //
B(LdaUndefined), //
B(Return)},
1,
{"a"}},
{"function f() { t = 4; }\nf()\nt = 1;",
3 * kPointerSize,
1,
21,
{B(LdaSmi8), U8(4), //
B(Star), R(0), //
B(LdaContextSlot), R(context_reg), U8(global_index), //
B(Star), R(1), //
B(LdaConstant), U8(0), //
B(Star), R(2), //
B(Ldar), R(0), //
B(StoreICSloppy), R(1), R(2), U8(vector->GetIndex(slot1)), //
B(LdaUndefined), //
B(Return)},
1,
{"t"}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
TEST(CallRuntime) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
......@@ -1075,6 +1244,57 @@ TEST(IfConditions) {
}
TEST(DeclareGlobals) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<int> snippets[] = {
{"var a = 1;",
4 * kPointerSize,
1,
30,
{
B(LdaConstant), U8(0), //
B(Star), R(1), //
B(LdaZero), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kDeclareGlobals), R(1), U8(2), //
B(LdaConstant), U8(1), //
B(Star), R(1), //
B(LdaZero), //
B(Star), R(2), //
B(LdaSmi8), U8(1), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kInitializeVarGlobal), R(1), //
U8(3), //
B(LdaUndefined), //
B(Return) //
},
-1},
{"function f() {}",
2 * kPointerSize,
1,
14,
{
B(LdaConstant), U8(0), //
B(Star), R(0), //
B(LdaZero), //
B(Star), R(1), //
B(CallRuntime), U16(Runtime::kDeclareGlobals), R(0), U8(2), //
B(LdaUndefined), //
B(Return) //
},
-1},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeTopLevelBytecode(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
TEST(BasicLoops) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
......
......@@ -555,6 +555,27 @@ TEST(InterpreterParameter8) {
}
TEST(InterpreterParameter1Assign) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(Smi::FromInt(5))
.StoreAccumulatorInRegister(builder.Parameter(0))
.LoadAccumulatorWithRegister(builder.Parameter(0))
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_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;
......@@ -572,6 +593,28 @@ TEST(InterpreterLoadGlobal) {
}
TEST(InterpreterStoreGlobal) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::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(handles.main_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->global_object(), name).ToHandleChecked();
CHECK_EQ(Smi::cast(*global_obj), Smi::FromInt(999));
}
TEST(InterpreterCallGlobal) {
HandleAndZoneScope handles;
......@@ -589,6 +632,45 @@ TEST(InterpreterCallGlobal) {
}
TEST(InterpreterLoadUnallocated) {
HandleAndZoneScope handles;
// Test loading an unallocated global.
std::string source(
"unallocated = 123;\n"
"function " + InterpreterTester::function_name() + "() {\n"
" return unallocated;\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(123));
}
TEST(InterpreterStoreUnallocated) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::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(handles.main_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->global_object(), name).ToHandleChecked();
CHECK_EQ(Smi::cast(*global_obj), Smi::FromInt(999));
}
TEST(InterpreterLoadNamedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
......
......@@ -474,18 +474,14 @@ TARGET_TEST_F(InterpreterAssemblerTest, LoadConstantPoolEntry) {
TARGET_TEST_F(InterpreterAssemblerTest, LoadContextSlot) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Node* load_from_current_context = m.LoadContextSlot(22);
Matcher<Node*> load_from_current_context_matcher = m.IsLoad(
kMachAnyTagged, IsParameter(Linkage::kInterpreterContextParameter),
IsIntPtrConstant(Context::SlotOffset(22)));
EXPECT_THAT(load_from_current_context, load_from_current_context_matcher);
// Let's imagine that the loaded context slot is another context.
Node* load_from_any_context =
m.LoadContextSlot(load_from_current_context, 23);
EXPECT_THAT(load_from_any_context,
m.IsLoad(kMachAnyTagged, load_from_current_context_matcher,
IsIntPtrConstant(Context::SlotOffset(23))));
Node* context = m.Int32Constant(1);
Node* slot_index = m.Int32Constant(22);
Node* load_context_slot = m.LoadContextSlot(context, slot_index);
Matcher<Node*> offset =
IsIntPtrAdd(IsWordShl(slot_index, IsInt32Constant(kPointerSizeLog2)),
IsInt32Constant(Context::kHeaderSize - kHeapObjectTag));
EXPECT_THAT(load_context_slot, m.IsLoad(kMachAnyTagged, context, offset));
}
}
......
......@@ -40,8 +40,12 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
Register reg(0);
builder.LoadAccumulatorWithRegister(reg).StoreAccumulatorInRegister(reg);
// Emit global load operations.
// Emit global load / store operations.
builder.LoadGlobal(1);
builder.StoreGlobal(1, LanguageMode::SLOPPY);
// Emit context load operations.
builder.LoadContextSlot(reg, 1);
// Emit load / store property operations.
builder.LoadNamedProperty(reg, 0, LanguageMode::SLOPPY)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment