Commit c0185b7d authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for new local function context creation.

Adds support for creation of new local function contexts (or script context for
top-level code). As part of this, also adds support for context push/pop
operations using a ContextScope object in BytecodeGenerator. Adds the following
bytecodes:
 - PushContext
 - PopContext

Support for inner contexts and loading from / storing to context allocated
variables will come in a future CL.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#31238}
parent 010897c1
......@@ -321,6 +321,18 @@ void BytecodeGraphBuilder::VisitCreateClosure(
}
void BytecodeGraphBuilder::VisitPushContext(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitPopContext(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitCall(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
......
......@@ -35,6 +35,8 @@ InterpreterAssembler::InterpreterAssembler(Isolate* isolate, Zone* zone,
end_nodes_(zone),
accumulator_(
raw_assembler_->Parameter(Linkage::kInterpreterAccumulatorParameter)),
context_(
raw_assembler_->Parameter(Linkage::kInterpreterContextParameter)),
code_generated_(false) {}
......@@ -66,19 +68,16 @@ Handle<Code> InterpreterAssembler::GenerateCode() {
}
Node* InterpreterAssembler::GetAccumulator() {
return accumulator_;
}
Node* InterpreterAssembler::GetAccumulator() { return accumulator_; }
void InterpreterAssembler::SetAccumulator(Node* value) {
accumulator_ = value;
}
void InterpreterAssembler::SetAccumulator(Node* value) { accumulator_ = value; }
Node* InterpreterAssembler::ContextTaggedPointer() {
return raw_assembler_->Parameter(Linkage::kInterpreterContextParameter);
}
Node* InterpreterAssembler::GetContext() { return context_; }
void InterpreterAssembler::SetContext(Node* value) { context_ = value; }
Node* InterpreterAssembler::RegisterFileRawPointer() {
......@@ -339,7 +338,7 @@ Node* InterpreterAssembler::CallJS(Node* function, Node* first_arg,
args[0] = arg_count;
args[1] = first_arg;
args[2] = function;
args[3] = ContextTaggedPointer();
args[3] = GetContext();
return CallN(descriptor, code_target, args);
}
......@@ -361,7 +360,7 @@ Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
args[1] = arg2;
args[2] = arg3;
args[3] = arg4;
args[4] = ContextTaggedPointer();
args[4] = GetContext();
return CallIC(descriptor, target, args);
}
......@@ -375,7 +374,7 @@ Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
args[2] = arg3;
args[3] = arg4;
args[4] = arg5;
args[5] = ContextTaggedPointer();
args[5] = GetContext();
return CallIC(descriptor, target, args);
}
......@@ -401,7 +400,7 @@ Node* InterpreterAssembler::CallRuntime(Node* function_id, Node* first_arg,
args[0] = arg_count;
args[1] = first_arg;
args[2] = function_entry;
args[3] = ContextTaggedPointer();
args[3] = GetContext();
return CallN(descriptor, code_target, args);
}
......@@ -409,15 +408,13 @@ Node* InterpreterAssembler::CallRuntime(Node* function_id, Node* first_arg,
Node* InterpreterAssembler::CallRuntime(Runtime::FunctionId function_id,
Node* arg1) {
return raw_assembler_->CallRuntime1(function_id, arg1,
ContextTaggedPointer());
return raw_assembler_->CallRuntime1(function_id, arg1, GetContext());
}
Node* InterpreterAssembler::CallRuntime(Runtime::FunctionId function_id,
Node* arg1, Node* arg2) {
return raw_assembler_->CallRuntime2(function_id, arg1, arg2,
ContextTaggedPointer());
return raw_assembler_->CallRuntime2(function_id, arg1, arg2, GetContext());
}
......@@ -436,7 +433,7 @@ void InterpreterAssembler::Return() {
BytecodeOffset(),
BytecodeArrayTaggedPointer(),
DispatchTableRawPointer(),
ContextTaggedPointer() };
GetContext() };
Node* tail_call = raw_assembler_->TailCallN(
call_descriptor(), exit_trampoline_code_object, args);
// This should always be the end node.
......@@ -496,7 +493,7 @@ void InterpreterAssembler::DispatchTo(Node* new_bytecode_offset) {
new_bytecode_offset,
BytecodeArrayTaggedPointer(),
DispatchTableRawPointer(),
ContextTaggedPointer() };
GetContext() };
Node* tail_call =
raw_assembler_->TailCallN(call_descriptor(), target_code_object, args);
// This should always be the end node.
......
......@@ -60,6 +60,10 @@ class InterpreterAssembler {
Node* GetAccumulator();
void SetAccumulator(Node* value);
// Context.
Node* GetContext();
void SetContext(Node* value);
// Loads from and stores to the interpreter register file.
Node* LoadRegister(Node* reg_index);
Node* StoreRegister(Node* value, Node* reg_index);
......@@ -143,8 +147,6 @@ class InterpreterAssembler {
Node* BytecodeOffset();
// Returns a raw pointer to first entry in the interpreter dispatch table.
Node* DispatchTableRawPointer();
// Returns a tagged pointer to the current context.
Node* ContextTaggedPointer();
// Returns the offset of register |index| relative to RegisterFilePointer().
Node* RegisterFrameOffset(Node* index);
......@@ -182,6 +184,7 @@ class InterpreterAssembler {
base::SmartPointer<RawMachineAssembler> raw_assembler_;
ZoneVector<Node*> end_nodes_;
Node* accumulator_;
Node* context_;
bool code_generated_;
DISALLOW_COPY_AND_ASSIGN(InterpreterAssembler);
......
......@@ -420,16 +420,18 @@ CallDescriptor* Linkage::GetInterpreterDispatchDescriptor(Zone* zone) {
STATIC_ASSERT(4 == Linkage::kInterpreterDispatchTableParameter);
types.AddParam(kMachPtr);
#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X87)
// TODO(rmcilroy): Make the context param the one spilled to the stack once
// Turbofan supports modified stack arguments in tail calls.
locations.AddParam(
LinkageLocation::ForCallerFrameSlot(kInterpreterDispatchTableSpillSlot));
#else
locations.AddParam(regloc(kInterpreterDispatchTableRegister));
#endif
STATIC_ASSERT(5 == Linkage::kInterpreterContextParameter);
types.AddParam(kMachAnyTagged);
#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X87)
locations.AddParam(
LinkageLocation::ForCallerFrameSlot(kInterpreterContextSpillSlot));
#else
locations.AddParam(regloc(kContextRegister));
#endif
LinkageLocation target_loc = LinkageLocation::ForAnyRegister();
return new (zone) CallDescriptor( // --
......
......@@ -680,24 +680,25 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
// Since the dispatch table root might be set after builtins are generated,
// load directly from the roots table.
__ LoadRoot(kInterpreterDispatchTableRegister,
Heap::kInterpreterTableRootIndex);
__ add(kInterpreterDispatchTableRegister,
Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
__ LoadRoot(ebx, Heap::kInterpreterTableRootIndex);
__ add(ebx, Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
// Push context as a stack located parameter to the bytecode handler.
DCHECK_EQ(-1, kInterpreterContextSpillSlot);
__ push(esi);
DCHECK_EQ(-1, kInterpreterDispatchTableSpillSlot);
__ push(ebx);
// Dispatch to the first bytecode handler for the function.
__ movzx_b(esi, Operand(kInterpreterBytecodeArrayRegister,
__ movzx_b(eax, Operand(kInterpreterBytecodeArrayRegister,
kInterpreterBytecodeOffsetRegister, times_1, 0));
__ mov(esi, Operand(kInterpreterDispatchTableRegister, esi,
times_pointer_size, 0));
__ mov(ebx, Operand(ebx, eax, times_pointer_size, 0));
// Restore undefined_value in accumulator (eax)
// TODO(rmcilroy): Remove this once we move the dispatch table back into a
// register.
__ mov(eax, Immediate(masm->isolate()->factory()->undefined_value()));
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
// and header removal.
__ add(esi, Immediate(Code::kHeaderSize - kHeapObjectTag));
__ call(esi);
__ add(ebx, Immediate(Code::kHeaderSize - kHeapObjectTag));
__ call(ebx);
}
......
......@@ -22,12 +22,11 @@ const Register kInterpreterAccumulatorRegister = {Register::kCode_eax};
const Register kInterpreterRegisterFileRegister = {Register::kCode_edx};
const Register kInterpreterBytecodeOffsetRegister = {Register::kCode_ecx};
const Register kInterpreterBytecodeArrayRegister = {Register::kCode_edi};
const Register kInterpreterDispatchTableRegister = {Register::kCode_ebx};
const Register kRuntimeCallFunctionRegister = {Register::kCode_ebx};
const Register kRuntimeCallArgCountRegister = {Register::kCode_eax};
// Spill slots used by interpreter dispatch calling convention.
const int kInterpreterContextSpillSlot = -1;
const int kInterpreterDispatchTableSpillSlot = -1;
// Convenience for platform-independent signatures. We do not normally
// distinguish memory operands from other operands on ia32.
......
......@@ -20,12 +20,14 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
constants_(zone),
parameter_count_(-1),
local_register_count_(-1),
context_register_count_(-1),
temporary_register_count_(0),
temporary_register_next_(0) {}
void BytecodeArrayBuilder::set_locals_count(int number_of_locals) {
local_register_count_ = number_of_locals;
DCHECK_LE(context_register_count_, 0);
temporary_register_next_ = local_register_count_;
}
......@@ -41,7 +43,26 @@ void BytecodeArrayBuilder::set_parameter_count(int number_of_parameters) {
int BytecodeArrayBuilder::parameter_count() const { return parameter_count_; }
Register BytecodeArrayBuilder::Parameter(int parameter_index) {
void BytecodeArrayBuilder::set_context_count(int number_of_contexts) {
context_register_count_ = number_of_contexts;
DCHECK_GE(local_register_count_, 0);
temporary_register_next_ = local_register_count_ + context_register_count_;
}
Register BytecodeArrayBuilder::first_context_register() const {
DCHECK_GT(context_register_count_, 0);
return Register(local_register_count_);
}
Register BytecodeArrayBuilder::last_context_register() const {
DCHECK_GT(context_register_count_, 0);
return Register(local_register_count_ + context_register_count_ - 1);
}
Register BytecodeArrayBuilder::Parameter(int parameter_index) const {
DCHECK_GE(parameter_index, 0);
DCHECK_LT(parameter_index, parameter_count_);
return Register::FromParameterIndex(parameter_index, parameter_count_);
......@@ -330,6 +351,18 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
Output(Bytecode::kPushContext, context.ToOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
Output(Bytecode::kPopContext, context.ToOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() {
if (LastBytecodeInSameBlock()) {
// If the previous bytecode puts a boolean in the accumulator
......@@ -579,7 +612,7 @@ 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_function_context()) {
if (reg.is_function_context() || reg.is_function_closure()) {
return true;
} else if (reg.is_parameter()) {
int parameter_index = reg.ToParameterIndex(parameter_count_);
......
......@@ -36,7 +36,14 @@ class BytecodeArrayBuilder {
void set_locals_count(int number_of_locals);
int locals_count() const;
Register Parameter(int parameter_index);
// Set number of contexts required for bytecode array.
void set_context_count(int number_of_contexts);
int context_count() const;
Register first_context_register() const;
Register last_context_register() const;
Register Parameter(int parameter_index) const;
// Constant loads to accumulator.
BytecodeArrayBuilder& LoadLiteral(v8::internal::Smi* value);
......@@ -75,6 +82,12 @@ class BytecodeArrayBuilder {
// Create a new closure for the SharedFunctionInfo in the accumulator.
BytecodeArrayBuilder& CreateClosure(PretenureFlag tenured);
// Push the context in accumulator as the new context, and store in register
// |context|.
BytecodeArrayBuilder& PushContext(Register context);
// Pop the current context and replace with |context|.
BytecodeArrayBuilder& PopContext(Register context);
// Call a JS function. The JSFunction or Callable to be called should be in
// |callable|, the receiver should be in |receiver| and all subsequent
// arguments should be in registers <receiver + 1> to
......@@ -175,6 +188,7 @@ class BytecodeArrayBuilder {
int parameter_count_;
int local_register_count_;
int context_register_count_;
int temporary_register_count_;
int temporary_register_next_;
......
......@@ -17,6 +17,47 @@ namespace internal {
namespace interpreter {
// Scoped class tracking context objects created by the visitor. Represents
// mutations of the context chain within the function body, allowing pushing and
// popping of the current {context_register} during visitation.
class BytecodeGenerator::ContextScope BASE_EMBEDDED {
public:
explicit ContextScope(BytecodeGenerator* generator,
bool is_function_context = false)
: generator_(generator),
outer_(generator_->current_context()),
is_function_context_(is_function_context) {
DCHECK(!is_function_context ||
outer_.index() == Register::function_context().index());
Register new_context_reg = NewContextRegister();
generator_->builder()->PushContext(new_context_reg);
generator_->set_current_context(new_context_reg);
}
~ContextScope() {
if (!is_function_context_) {
generator_->builder()->PopContext(outer_);
}
generator_->set_current_context(outer_);
}
private:
Register NewContextRegister() const {
if (outer_.index() == Register::function_context().index()) {
return generator_->builder()->first_context_register();
} else {
DCHECK_LT(outer_.index(),
generator_->builder()->last_context_register().index());
return Register(outer_.index() + 1);
}
}
BytecodeGenerator* generator_;
Register outer_;
bool is_function_context_;
};
// Scoped class for tracking control statements entered by the
// visitor. The pattern derives AstGraphBuilder::ControlScope.
class BytecodeGenerator::ControlScope BASE_EMBEDDED {
......@@ -109,21 +150,31 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
builder()->set_parameter_count(info->num_parameters_including_this());
builder()->set_locals_count(scope()->num_stack_slots());
// Visit implicit declaration of the function name.
if (scope()->is_function_scope() && scope()->function() != NULL) {
VisitVariableDeclaration(scope()->function());
// TODO(rmcilroy): Set correct context count.
builder()->set_context_count(info->num_heap_slots() > 0 ? 1 : 0);
// Build function context only if there are context allocated variables.
if (info->num_heap_slots() > 0) {
// Push a new inner context scope for the function.
VisitNewLocalFunctionContext();
ContextScope top_context(this, true);
MakeBytecodeBody();
} else {
MakeBytecodeBody();
}
set_scope(nullptr);
set_info(nullptr);
return builder_.ToBytecodeArray();
}
void BytecodeGenerator::MakeBytecodeBody() {
// Visit declarations within the function scope.
VisitDeclarations(scope()->declarations());
// Visit statements in the function body.
VisitStatements(info->literal()->body());
set_scope(nullptr);
set_info(nullptr);
return builder_.ToBytecodeArray();
VisitStatements(info()->literal()->body());
}
......@@ -219,7 +270,7 @@ void BytecodeGenerator::VisitDeclarations(
DeclareGlobalsNativeFlag::encode(info()->is_native()) |
DeclareGlobalsLanguageMode::encode(language_mode());
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
Register pairs = temporary_register_scope.NewRegister();
builder()->LoadLiteral(data);
builder()->StoreAccumulatorInRegister(pairs);
......@@ -484,7 +535,7 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
break;
}
case VariableLocation::UNALLOCATED: {
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
Register obj = temporary_register_scope.NewRegister();
builder()->LoadContextSlot(current_context(),
Context::GLOBAL_OBJECT_INDEX);
......@@ -526,7 +577,7 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
break;
}
case VariableLocation::UNALLOCATED: {
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
Register value = temporary_register_scope.NewRegister();
Register obj = temporary_register_scope.NewRegister();
Register name = temporary_register_scope.NewRegister();
......@@ -552,7 +603,7 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
void BytecodeGenerator::VisitAssignment(Assignment* expr) {
DCHECK(expr->target()->IsValidReferenceExpression());
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
Register object, key;
// Left-hand side can only be a property, a global or a variable slot.
......@@ -646,7 +697,7 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) {
void BytecodeGenerator::VisitProperty(Property* expr) {
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
Register obj = temporary_register_scope.NewRegister();
Visit(expr->obj());
builder()->StoreAccumulatorInRegister(obj);
......@@ -660,7 +711,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
// Prepare the callee and the receiver to the function call. This depends on
// the semantics of the underlying call type.
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
Register callee = temporary_register_scope.NewRegister();
Register receiver = temporary_register_scope.NewRegister();
......@@ -724,7 +775,7 @@ void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
// Evaluate all arguments to the runtime call.
ZoneList<Expression*>* args = expr->arguments();
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
// Ensure we always have a valid first_arg register even if there are no
// arguments to pass.
Register first_arg = temporary_register_scope.NewRegister();
......@@ -805,7 +856,7 @@ void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
Expression* left = expr->left();
Expression* right = expr->right();
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
Register temporary = temporary_register_scope.NewRegister();
Visit(left);
......@@ -839,12 +890,47 @@ void BytecodeGenerator::VisitSuperPropertyReference(
}
void BytecodeGenerator::VisitNewLocalFunctionContext() {
Scope* scope = this->scope();
// Allocate a new local context.
if (scope->is_script_scope()) {
TemporaryRegisterScope temporary_register_scope(builder());
Register closure = temporary_register_scope.NewRegister();
Register scope_info = temporary_register_scope.NewRegister();
DCHECK_EQ(closure.index() + 1, scope_info.index());
builder()
->LoadAccumulatorWithRegister(Register::function_closure())
.StoreAccumulatorInRegister(closure)
.LoadLiteral(scope->GetScopeInfo(isolate()))
.StoreAccumulatorInRegister(scope_info)
.CallRuntime(Runtime::kNewScriptContext, closure, 2);
} else {
builder()->CallRuntime(Runtime::kNewFunctionContext,
Register::function_closure(), 1);
}
if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) {
UNIMPLEMENTED();
}
// Copy parameters into context if necessary.
int num_parameters = scope->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* variable = scope->parameter(i);
if (variable->IsContextSlot()) {
UNIMPLEMENTED();
}
}
}
void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* binop) {
Token::Value op = binop->op();
Expression* left = binop->left();
Expression* right = binop->right();
TemporaryRegisterScope temporary_register_scope(&builder_);
TemporaryRegisterScope temporary_register_scope(builder());
Register temporary = temporary_register_scope.NewRegister();
Visit(left);
......@@ -868,9 +954,6 @@ 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
......@@ -28,15 +28,19 @@ class BytecodeGenerator : public AstVisitor {
void VisitDeclarations(ZoneList<Declaration*>* declarations) override;
private:
class ContextScope;
class ControlScope;
class ControlScopeForIteration;
void MakeBytecodeBody();
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitPropertyLoad(Register obj, Property* expr);
void VisitVariableLoad(Variable* variable, FeedbackVectorSlot slot);
void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot);
void VisitNewLocalFunctionContext();
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
......@@ -44,18 +48,23 @@ class BytecodeGenerator : public AstVisitor {
void VisitNot(UnaryOperation* expr);
inline BytecodeArrayBuilder* builder() { return &builder_; }
inline Scope* scope() const { return scope_; }
inline void set_scope(Scope* scope) { scope_ = scope; }
inline ControlScope* control_scope() const { return control_scope_; }
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;
inline ControlScope* control_scope() const { return control_scope_; }
inline void set_control_scope(ControlScope* scope) { control_scope_ = scope; }
inline Register current_context() const { return current_context_; }
inline void set_current_context(Register context) {
current_context_ = context;
}
ZoneVector<Handle<Object>>* globals() { return &globals_; }
inline LanguageMode language_mode() const;
Strength language_mode_strength() const;
int feedback_index(FeedbackVectorSlot slot) const;
Register current_context() const;
BytecodeArrayBuilder builder_;
CompilationInfo* info_;
......@@ -63,7 +72,6 @@ class BytecodeGenerator : public AstVisitor {
ZoneVector<Handle<Object>> globals_;
ControlScope* control_scope_;
// TODO(rmcilroy): Encapsulate this in an environment object.
Register current_context_;
};
......
......@@ -225,7 +225,11 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start,
break;
case interpreter::OperandType::kReg8: {
Register reg = Register::FromOperand(*operand_start);
if (reg.is_parameter()) {
if (reg.is_function_context()) {
os << "<context>";
} else if (reg.is_function_closure()) {
os << "<closure>";
} else if (reg.is_parameter()) {
int parameter_index = reg.ToParameterIndex(parameter_count);
if (parameter_index == 0) {
os << "<this>";
......@@ -266,6 +270,8 @@ std::ostream& operator<<(std::ostream& os, const OperandSize& operand_size) {
static const int kLastParamRegisterIndex =
-InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
static const int kFunctionClosureRegisterIndex =
-InterpreterFrameConstants::kFunctionFromRegisterPointer / kPointerSize;
static const int kFunctionContextRegisterIndex =
-InterpreterFrameConstants::kContextFromRegisterPointer / kPointerSize;
......@@ -293,6 +299,16 @@ int Register::ToParameterIndex(int parameter_count) const {
}
Register Register::function_closure() {
return Register(kFunctionClosureRegisterIndex);
}
bool Register::is_function_closure() const {
return index() == kFunctionClosureRegisterIndex;
}
Register Register::function_context() {
return Register(kFunctionContextRegisterIndex);
}
......
......@@ -68,6 +68,10 @@ namespace interpreter {
V(KeyedStoreICStrict, OperandType::kReg8, OperandType::kReg8, \
OperandType::kIdx8) \
\
/* Context operations */ \
V(PushContext, OperandType::kReg8) \
V(PopContext, OperandType::kReg8) \
\
/* Binary Operators */ \
V(Add, OperandType::kReg8) \
V(Sub, OperandType::kReg8) \
......@@ -176,6 +180,10 @@ class Register {
int ToParameterIndex(int parameter_count) const;
static int MaxParameterIndex();
// Returns the register for the function's closure object.
static Register function_closure();
bool is_function_closure() const;
// Returns the register for the function's outer context.
static Register function_context();
bool is_function_context() const;
......
......@@ -347,6 +347,29 @@ void Interpreter::DoKeyedStoreICStrict(
}
// PushContext <context>
//
// Pushes the accumulator as the current context, and saves it in <context>
void Interpreter::DoPushContext(compiler::InterpreterAssembler* assembler) {
Node* reg_index = __ BytecodeOperandReg8(0);
Node* context = __ GetAccumulator();
__ SetContext(context);
__ StoreRegister(context, reg_index);
__ Dispatch();
}
// PopContext <context>
//
// Pops the current context and sets <context> as the new context.
void Interpreter::DoPopContext(compiler::InterpreterAssembler* assembler) {
Node* reg_index = __ BytecodeOperandReg8(0);
Node* context = __ LoadRegister(reg_index);
__ SetContext(context);
__ Dispatch();
}
void Interpreter::DoBinaryOp(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy): Call ICs which back-patch bytecode with type specialized
......
......@@ -665,24 +665,25 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
// Since the dispatch table root might be set after builtins are generated,
// load directly from the roots table.
__ LoadRoot(kInterpreterDispatchTableRegister,
Heap::kInterpreterTableRootIndex);
__ add(kInterpreterDispatchTableRegister,
Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
__ LoadRoot(ebx, Heap::kInterpreterTableRootIndex);
__ add(ebx, Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
// Push context as a stack located parameter to the bytecode handler.
DCHECK_EQ(-1, kInterpreterContextSpillSlot);
__ push(esi);
DCHECK_EQ(-1, kInterpreterDispatchTableSpillSlot);
__ push(ebx);
// Dispatch to the first bytecode handler for the function.
__ movzx_b(esi, Operand(kInterpreterBytecodeArrayRegister,
__ movzx_b(eax, Operand(kInterpreterBytecodeArrayRegister,
kInterpreterBytecodeOffsetRegister, times_1, 0));
__ mov(esi, Operand(kInterpreterDispatchTableRegister, esi,
times_pointer_size, 0));
__ mov(ebx, Operand(ebx, eax, times_pointer_size, 0));
// Restore undefined_value in accumulator (eax)
// TODO(rmcilroy): Remove this once we move the dispatch table back into a
// register.
__ mov(eax, Immediate(masm->isolate()->factory()->undefined_value()));
// TODO(rmcilroy): Make dispatch table point to code entrys to avoid untagging
// and header removal.
__ add(esi, Immediate(Code::kHeaderSize - kHeapObjectTag));
__ call(esi);
__ add(ebx, Immediate(Code::kHeaderSize - kHeapObjectTag));
__ call(ebx);
}
......
......@@ -22,12 +22,11 @@ const Register kInterpreterAccumulatorRegister = {kRegister_eax_Code};
const Register kInterpreterRegisterFileRegister = {kRegister_edx_Code};
const Register kInterpreterBytecodeOffsetRegister = {kRegister_ecx_Code};
const Register kInterpreterBytecodeArrayRegister = {kRegister_edi_Code};
const Register kInterpreterDispatchTableRegister = {kRegister_ebx_Code};
const Register kRuntimeCallFunctionRegister = {kRegister_ebx_Code};
const Register kRuntimeCallArgCountRegister = {kRegister_eax_Code};
// Spill slots used by interpreter dispatch calling convention.
const int kInterpreterContextSpillSlot = -1;
const int kInterpreterDispatchTableSpillSlot = -1;
// Convenience for platform-independent signatures. We do not normally
// distinguish memory operands from other operands on ia32.
......
......@@ -1102,7 +1102,7 @@ TEST(LoadUnallocated) {
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);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
......@@ -1159,7 +1159,7 @@ TEST(StoreUnallocated) {
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);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
......@@ -1211,7 +1211,7 @@ TEST(CallRuntime) {
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);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
......@@ -1393,39 +1393,108 @@ TEST(DeclareGlobals) {
ExpectedSnippet<int> snippets[] = {
{"var a = 1;",
4 * kPointerSize,
5 * kPointerSize,
1,
30,
45,
{
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) //
B(Ldar), R(Register::function_closure().index()), //
B(Star), R(2), //
B(LdaConstant), U8(0), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kNewScriptContext), R(2), U8(2), //
B(PushContext), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(LdaZero), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kDeclareGlobals), R(2), U8(2), //
B(LdaConstant), U8(2), //
B(Star), R(2), //
B(LdaZero), //
B(Star), R(3), //
B(LdaSmi8), U8(1), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kInitializeVarGlobal), R(2), //
U8(3), //
B(LdaUndefined), //
B(Return) //
},
-1},
{"function f() {}",
2 * kPointerSize,
3 * kPointerSize,
1,
14,
29,
{
B(Ldar), R(Register::function_closure().index()), //
B(Star), R(1), //
B(LdaConstant), U8(0), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kNewScriptContext), R(1), U8(2), //
B(PushContext), R(0), //
B(LdaConstant), U8(1), //
B(Star), R(1), //
B(LdaZero), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kDeclareGlobals), R(1), U8(2), //
B(LdaUndefined), //
B(Return) //
},
-1},
{"var a = 1;\na=2;",
5 * kPointerSize,
1,
52,
{
B(Ldar), R(Register::function_closure().index()), //
B(Star), R(2), //
B(LdaConstant), U8(0), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kNewScriptContext), R(2), U8(2), //
B(PushContext), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(LdaZero), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kDeclareGlobals), R(2), U8(2), //
B(LdaConstant), U8(2), //
B(Star), R(2), //
B(LdaZero), //
B(Star), R(3), //
B(LdaSmi8), U8(1), //
B(Star), R(4), //
B(CallRuntime), U16(Runtime::kInitializeVarGlobal), R(2), //
U8(3), //
B(LdaSmi8), U8(2), //
B(StaGlobal), _, //
B(Star), R(0), //
B(Ldar), R(0), //
B(Return) //
},
-1},
{"function f() {}\nf();",
4 * kPointerSize,
1,
43,
{
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) //
B(Ldar), R(Register::function_closure().index()), //
B(Star), R(2), //
B(LdaConstant), U8(0), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kNewScriptContext), R(2), U8(2), //
B(PushContext), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(2), //
B(LdaZero), //
B(Star), R(3), //
B(CallRuntime), U16(Runtime::kDeclareGlobals), R(2), U8(2), //
B(LdaUndefined), //
B(Star), R(3), //
B(LdaGlobal), _, //
B(Star), R(2), //
B(Call), R(2), R(3), U8(0), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Return) //
},
-1},
};
......
......@@ -44,7 +44,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.LoadGlobal(1);
builder.StoreGlobal(1, LanguageMode::SLOPPY);
// Emit context load operations.
// Emit context operations.
builder.PushContext(reg);
builder.PopContext(reg);
builder.LoadContextSlot(reg, 1);
// Emit load / store property operations.
......
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