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());
......
......@@ -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