DCHECK_NOT_NULL(expr->class_variable_proxy());
Variable* var = expr->class_variable_proxy()->var();
FrameStateBeforeAndAfter states(this, BailoutId::None());
- BuildVariableAssignment(states, var, literal, Token::INIT_CONST,
- BailoutId::None());
+ BuildVariableAssignment(var, literal, Token::INIT_CONST, BailoutId::None(),
+ states);
}
ast_context()->ProduceValue(literal);
void AstGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
VectorSlotPair pair = CreateVectorSlotPair(expr->VariableFeedbackSlot());
FrameStateBeforeAndAfter states(this, BeforeId(expr));
- Node* value = BuildVariableLoad(states, expr->var(), expr->id(), pair,
+ Node* value = BuildVariableLoad(expr->var(), expr->id(), states, pair,
ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
}
case VARIABLE: {
Variable* var = expr->AsVariableProxy()->var();
FrameStateBeforeAndAfter states(this, BailoutId::None());
- BuildVariableAssignment(states, var, value, Token::ASSIGN, bailout_id);
+ BuildVariableAssignment(var, value, Token::ASSIGN, bailout_id, states);
break;
}
case NAMED_PROPERTY: {
CreateVectorSlotPair(proxy->VariableFeedbackSlot());
FrameStateBeforeAndAfter states(this, BeforeId(proxy));
old_value =
- BuildVariableLoad(states, proxy->var(), expr->target()->id(), pair,
+ BuildVariableLoad(proxy->var(), expr->target()->id(), states, pair,
OutputFrameStateCombine::Push());
break;
}
switch (assign_type) {
case VARIABLE: {
Variable* variable = expr->target()->AsVariableProxy()->var();
- BuildVariableAssignment(store_states, variable, value, expr->op(),
- expr->id(), ast_context()->GetStateCombine());
+ BuildVariableAssignment(variable, value, expr->op(), expr->id(),
+ store_states, ast_context()->GetStateCombine());
break;
}
case NAMED_PROPERTY: {
VectorSlotPair pair = CreateVectorSlotPair(proxy->VariableFeedbackSlot());
FrameStateBeforeAndAfter states(this, BeforeId(proxy));
callee_value =
- BuildVariableLoad(states, proxy->var(), expr->expression()->id(),
+ BuildVariableLoad(proxy->var(), expr->expression()->id(), states,
pair, OutputFrameStateCombine::Push());
receiver_value = jsgraph()->UndefinedConstant();
break;
VectorSlotPair pair = CreateVectorSlotPair(proxy->VariableFeedbackSlot());
FrameStateBeforeAndAfter states(this, BeforeId(proxy));
old_value =
- BuildVariableLoad(states, proxy->var(), expr->expression()->id(),
+ BuildVariableLoad(proxy->var(), expr->expression()->id(), states,
pair, OutputFrameStateCombine::Push());
stack_depth = 0;
break;
case VARIABLE: {
Variable* variable = expr->expression()->AsVariableProxy()->var();
environment()->Push(value);
- BuildVariableAssignment(store_states, variable, value, expr->op(),
- expr->AssignmentId());
+ BuildVariableAssignment(variable, value, expr->op(), expr->AssignmentId(),
+ store_states);
environment()->Pop();
break;
}
VectorSlotPair pair = CreateVectorSlotPair(proxy->VariableFeedbackSlot());
FrameStateBeforeAndAfter states(this, BeforeId(proxy));
operand =
- BuildVariableLoad(states, proxy->var(), expr->expression()->id(), pair,
+ BuildVariableLoad(proxy->var(), expr->expression()->id(), states, pair,
OutputFrameStateCombine::Push(), NOT_CONTEXTUAL);
} else {
VisitForValue(expr->expression());
}
+uint32_t AstGraphBuilder::ComputeBitsetForDynamicGlobal(Variable* variable) {
+ DCHECK_EQ(DYNAMIC_GLOBAL, variable->mode());
+ EnumSet<int, uint32_t> check_depths;
+ for (Scope* s = current_scope(); s != nullptr; s = s->outer_scope()) {
+ if (s->num_heap_slots() <= 0) continue;
+ // TODO(mstarzinger): Be smarter about which checks to require!
+ int depth = current_scope()->ContextChainLength(s);
+ if (depth > DynamicGlobalAccess::kMaxCheckDepth) {
+ return DynamicGlobalAccess::kFullCheckRequired;
+ }
+ check_depths.Add(depth);
+ }
+ return check_depths.ToIntegral();
+}
+
+
+uint32_t AstGraphBuilder::ComputeBitsetForDynamicContext(Variable* variable) {
+ DCHECK_EQ(DYNAMIC_LOCAL, variable->mode());
+ EnumSet<int, uint32_t> check_depths;
+ for (Scope* s = current_scope(); s != nullptr; s = s->outer_scope()) {
+ if (s->num_heap_slots() <= 0) continue;
+ if (!s->calls_sloppy_eval()) continue;
+ int depth = current_scope()->ContextChainLength(s);
+ if (depth > DynamicContextAccess::kMaxCheckDepth) {
+ return DynamicContextAccess::kFullCheckRequired;
+ }
+ check_depths.Add(depth);
+ if (s == variable->scope()) break;
+ }
+ return check_depths.ToIntegral();
+}
+
+
Node* AstGraphBuilder::ProcessArguments(const Operator* op, int arity) {
DCHECK(environment()->stack_height() >= arity);
Node** all = info()->zone()->NewArray<Node*>(arity);
DCHECK(arguments->IsContextSlot() || arguments->IsStackAllocated());
// This should never lazy deopt, so it is fine to send invalid bailout id.
FrameStateBeforeAndAfter states(this, BailoutId::None());
- BuildVariableAssignment(states, arguments, object, Token::ASSIGN,
- BailoutId::None());
+ BuildVariableAssignment(arguments, object, Token::ASSIGN, BailoutId::None(),
+ states);
return object;
}
DCHECK(rest->IsContextSlot() || rest->IsStackAllocated());
// This should never lazy deopt, so it is fine to send invalid bailout id.
FrameStateBeforeAndAfter states(this, BailoutId::None());
- BuildVariableAssignment(states, rest, object, Token::ASSIGN,
- BailoutId::None());
+ BuildVariableAssignment(rest, object, Token::ASSIGN, BailoutId::None(),
+ states);
return object;
}
}
-Node* AstGraphBuilder::BuildVariableLoad(FrameStateBeforeAndAfter& states,
- Variable* variable,
+Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
BailoutId bailout_id,
+ FrameStateBeforeAndAfter& states,
const VectorSlotPair& feedback,
OutputFrameStateCombine combine,
ContextualMode contextual_mode) {
// Global var, const, or let variable.
Node* global = BuildLoadGlobalObject();
Handle<Name> name = variable->name();
- Node* node = BuildNamedLoad(global, name, feedback, contextual_mode);
- states.AddToNode(node, bailout_id, combine);
- return node;
+ Node* value = BuildNamedLoad(global, name, feedback, contextual_mode);
+ states.AddToNode(value, bailout_id, combine);
+ return value;
}
case Variable::PARAMETER:
case Variable::LOCAL: {
}
case Variable::LOOKUP: {
// Dynamic lookup of context variable (anywhere in the chain).
- Node* name = jsgraph()->Constant(variable->name());
- Runtime::FunctionId function_id =
- (contextual_mode == CONTEXTUAL)
- ? Runtime::kLoadLookupSlot
- : Runtime::kLoadLookupSlotNoReferenceError;
- const Operator* op = javascript()->CallRuntime(function_id, 2);
- Node* pair = NewNode(op, current_context(), name);
- PrepareFrameState(pair, bailout_id, OutputFrameStateCombine::Push(1));
- return NewNode(common()->Projection(0), pair);
+ Node* value = jsgraph()->TheHoleConstant();
+ Handle<String> name = variable->name();
+ if (mode == DYNAMIC_GLOBAL) {
+ uint32_t check_bitset = ComputeBitsetForDynamicGlobal(variable);
+ const Operator* op = javascript()->LoadDynamicGlobal(name, check_bitset,
+ contextual_mode);
+ value = NewNode(op, current_context());
+ } else if (mode == DYNAMIC_LOCAL) {
+ Variable* local = variable->local_if_not_shadowed();
+ DCHECK(local->location() == Variable::CONTEXT); // Must be context.
+ int depth = current_scope()->ContextChainLength(local->scope());
+ uint32_t check_bitset = ComputeBitsetForDynamicContext(variable);
+ const Operator* op = javascript()->LoadDynamicContext(
+ name, check_bitset, depth, local->index());
+ value = NewNode(op, current_context());
+ // TODO(mstarzinger): Hole checks are missing here when optimized.
+ } else if (mode == DYNAMIC) {
+ uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired;
+ const Operator* op = javascript()->LoadDynamicGlobal(name, check_bitset,
+ contextual_mode);
+ value = NewNode(op, current_context());
+ }
+ PrepareFrameState(value, bailout_id, combine);
+ return value;
}
}
UNREACHABLE();
Node* AstGraphBuilder::BuildVariableAssignment(
- FrameStateBeforeAndAfter& states, Variable* variable, Node* value,
- Token::Value op, BailoutId bailout_id, OutputFrameStateCombine combine) {
+ Variable* variable, Node* value, Token::Value op, BailoutId bailout_id,
+ FrameStateBeforeAndAfter& states, OutputFrameStateCombine combine) {
Node* the_hole = jsgraph()->TheHoleConstant();
VariableMode mode = variable->mode();
switch (variable->location()) {
void UpdateControlDependencyToLeaveFunction(Node* exit);
// Builds deoptimization for a given node.
- void PrepareFrameState(
- Node* node, BailoutId ast_id,
- OutputFrameStateCombine combine = OutputFrameStateCombine::Ignore());
+ void PrepareFrameState(Node* node, BailoutId ast_id,
+ OutputFrameStateCombine framestate_combine =
+ OutputFrameStateCombine::Ignore());
BitVector* GetVariablesAssignedInLoop(IterationStatement* stmt);
// Named and keyed loads require a VectorSlotPair for successful lowering.
VectorSlotPair CreateVectorSlotPair(FeedbackVectorICSlot slot) const;
+ // Determine which contexts need to be checked for extension objects that
+ // might shadow the optimistic declaration of dynamic lookup variables.
+ uint32_t ComputeBitsetForDynamicGlobal(Variable* variable);
+ uint32_t ComputeBitsetForDynamicContext(Variable* variable);
+
// ===========================================================================
// The following build methods all generate graph fragments and return one
// resulting node. The operand stack height remains the same, variables and
Node* BuildRestArgumentsArray(Variable* rest, int index);
// Builders for variable load and assignment.
- Node* BuildVariableAssignment(
- FrameStateBeforeAndAfter& states, Variable* var, Node* value,
- Token::Value op, BailoutId bailout_id,
- OutputFrameStateCombine combine = OutputFrameStateCombine::Ignore());
- Node* BuildVariableDelete(Variable* var, BailoutId bailout_id,
- OutputFrameStateCombine combine);
- Node* BuildVariableLoad(FrameStateBeforeAndAfter& states, Variable* var,
- BailoutId bailout_id, const VectorSlotPair& feedback,
- OutputFrameStateCombine combine,
+ Node* BuildVariableAssignment(Variable* variable, Node* value,
+ Token::Value op, BailoutId bailout_id,
+ FrameStateBeforeAndAfter& states,
+ OutputFrameStateCombine framestate_combine =
+ OutputFrameStateCombine::Ignore());
+ Node* BuildVariableDelete(Variable* variable, BailoutId bailout_id,
+ OutputFrameStateCombine framestate_combine);
+ Node* BuildVariableLoad(Variable* variable, BailoutId bailout_id,
+ FrameStateBeforeAndAfter& states,
+ const VectorSlotPair& feedback,
+ OutputFrameStateCombine framestate_combine,
ContextualMode mode = CONTEXTUAL);
// Builders for property loads and stores.
}
+void JSGenericLowering::LowerJSLoadDynamicGlobal(Node* node) {
+ const DynamicGlobalAccess& access = DynamicGlobalAccessOf(node->op());
+ Runtime::FunctionId function_id =
+ (access.mode() == CONTEXTUAL) ? Runtime::kLoadLookupSlot
+ : Runtime::kLoadLookupSlotNoReferenceError;
+ Node* projection = graph()->NewNode(common()->Projection(0), node);
+ NodeProperties::ReplaceWithValue(node, projection, node, node);
+ node->InsertInput(zone(), 1, jsgraph()->Constant(access.name()));
+ ReplaceWithRuntimeCall(node, function_id);
+ projection->ReplaceInput(0, node);
+}
+
+
+void JSGenericLowering::LowerJSLoadDynamicContext(Node* node) {
+ const DynamicContextAccess& access = DynamicContextAccessOf(node->op());
+ Node* projection = graph()->NewNode(common()->Projection(0), node);
+ NodeProperties::ReplaceWithValue(node, projection, node, node);
+ node->InsertInput(zone(), 1, jsgraph()->Constant(access.name()));
+ ReplaceWithRuntimeCall(node, Runtime::kLoadLookupSlot);
+ projection->ReplaceInput(0, node);
+}
+
+
void JSGenericLowering::LowerJSCreateClosure(Node* node) {
CreateClosureParameters p = CreateClosureParametersOf(node->op());
node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.shared_info()));
}
+DynamicGlobalAccess::DynamicGlobalAccess(const Handle<String>& name,
+ uint32_t check_bitset,
+ ContextualMode mode)
+ : name_(name), check_bitset_(check_bitset), mode_(mode) {
+ DCHECK(check_bitset == kFullCheckRequired || check_bitset < 0x80000000U);
+}
+
+
+bool operator==(DynamicGlobalAccess const& lhs,
+ DynamicGlobalAccess const& rhs) {
+ UNIMPLEMENTED();
+ return true;
+}
+
+
+bool operator!=(DynamicGlobalAccess const& lhs,
+ DynamicGlobalAccess const& rhs) {
+ return !(lhs == rhs);
+}
+
+
+size_t hash_value(DynamicGlobalAccess const& access) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+std::ostream& operator<<(std::ostream& os, DynamicGlobalAccess const& access) {
+ return os << Brief(*access.name()) << ", " << access.check_bitset() << ", "
+ << access.mode();
+}
+
+
+DynamicGlobalAccess const& DynamicGlobalAccessOf(Operator const* op) {
+ DCHECK_EQ(IrOpcode::kJSLoadDynamicGlobal, op->opcode());
+ return OpParameter<DynamicGlobalAccess>(op);
+}
+
+
+DynamicContextAccess::DynamicContextAccess(const Handle<String>& name,
+ uint32_t check_bitset,
+ const ContextAccess& context_access)
+ : name_(name),
+ check_bitset_(check_bitset),
+ context_access_(context_access) {
+ DCHECK(check_bitset == kFullCheckRequired || check_bitset < 0x80000000U);
+}
+
+
+bool operator==(DynamicContextAccess const& lhs,
+ DynamicContextAccess const& rhs) {
+ UNIMPLEMENTED();
+ return true;
+}
+
+
+bool operator!=(DynamicContextAccess const& lhs,
+ DynamicContextAccess const& rhs) {
+ return !(lhs == rhs);
+}
+
+
+size_t hash_value(DynamicContextAccess const& access) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+std::ostream& operator<<(std::ostream& os, DynamicContextAccess const& access) {
+ return os << Brief(*access.name()) << ", " << access.check_bitset() << ", "
+ << access.context_access();
+}
+
+
+DynamicContextAccess const& DynamicContextAccessOf(Operator const* op) {
+ DCHECK_EQ(IrOpcode::kJSLoadDynamicContext, op->opcode());
+ return OpParameter<DynamicContextAccess>(op);
+}
+
+
bool operator==(VectorSlotPair const& lhs, VectorSlotPair const& rhs) {
return lhs.slot().ToInt() == rhs.slot().ToInt() &&
lhs.vector().is_identical_to(rhs.vector());
}
+const Operator* JSOperatorBuilder::LoadDynamicGlobal(const Handle<String>& name,
+ uint32_t check_bitset,
+ ContextualMode mode) {
+ DynamicGlobalAccess access(name, check_bitset, mode);
+ return new (zone()) Operator1<DynamicGlobalAccess>( // --
+ IrOpcode::kJSLoadDynamicGlobal, Operator::kNoProperties, // opcode
+ "JSLoadDynamicGlobal", // name
+ 1, 1, 1, 1, 1, 2, // counts
+ access); // parameter
+}
+
+
+const Operator* JSOperatorBuilder::LoadDynamicContext(
+ const Handle<String>& name, uint32_t check_bitset, size_t depth,
+ size_t index) {
+ ContextAccess context_access(depth, index, false);
+ DynamicContextAccess access(name, check_bitset, context_access);
+ return new (zone()) Operator1<DynamicContextAccess>( // --
+ IrOpcode::kJSLoadDynamicContext, Operator::kNoProperties, // opcode
+ "JSLoadDynamicContext", // name
+ 1, 1, 1, 1, 1, 2, // counts
+ access); // parameter
+}
+
+
const Operator* JSOperatorBuilder::CreateClosure(
Handle<SharedFunctionInfo> shared_info, PretenureFlag pretenure) {
CreateClosureParameters parameters(shared_info, pretenure);
ContextAccess const& ContextAccessOf(Operator const*);
+// Defines the name for a dynamic variable lookup. The {check_bitset} allows to
+// inline checks whether the lookup yields in a global variable. This is used as
+// a parameter by JSLoadDynamicGlobal and JSStoreDynamicGlobal operators.
+class DynamicGlobalAccess final {
+ public:
+ DynamicGlobalAccess(const Handle<String>& name, uint32_t check_bitset,
+ ContextualMode mode);
+
+ const Handle<String>& name() const { return name_; }
+ uint32_t check_bitset() const { return check_bitset_; }
+ ContextualMode mode() const { return mode_; }
+
+ // Indicates that an inline check is disabled.
+ bool RequiresFullCheck() const {
+ return check_bitset() == kFullCheckRequired;
+ }
+
+ // Limit of context chain length to which inline check is possible.
+ static const int kMaxCheckDepth = 30;
+
+ // Sentinel for {check_bitset} disabling inline checks.
+ static const uint32_t kFullCheckRequired = -1;
+
+ private:
+ const Handle<String> name_;
+ const uint32_t check_bitset_;
+ const ContextualMode mode_;
+};
+
+size_t hash_value(DynamicGlobalAccess const&);
+
+bool operator==(DynamicGlobalAccess const&, DynamicGlobalAccess const&);
+bool operator!=(DynamicGlobalAccess const&, DynamicGlobalAccess const&);
+
+std::ostream& operator<<(std::ostream&, DynamicGlobalAccess const&);
+
+DynamicGlobalAccess const& DynamicGlobalAccessOf(Operator const*);
+
+
+// Defines the name for a dynamic variable lookup. The {check_bitset} allows to
+// inline checks whether the lookup yields in a context variable. This is used
+// as a parameter by JSLoadDynamicContext and JSStoreDynamicContext operators.
+class DynamicContextAccess final {
+ public:
+ DynamicContextAccess(const Handle<String>& name, uint32_t check_bitset,
+ const ContextAccess& context_access);
+
+ const Handle<String>& name() const { return name_; }
+ uint32_t check_bitset() const { return check_bitset_; }
+ const ContextAccess& context_access() const { return context_access_; }
+
+ // Indicates that an inline check is disabled.
+ bool RequiresFullCheck() const {
+ return check_bitset() == kFullCheckRequired;
+ }
+
+ // Limit of context chain length to which inline check is possible.
+ static const int kMaxCheckDepth = 30;
+
+ // Sentinel for {check_bitset} disabling inline checks.
+ static const uint32_t kFullCheckRequired = -1;
+
+ private:
+ const Handle<String> name_;
+ const uint32_t check_bitset_;
+ const ContextAccess context_access_;
+};
+
+size_t hash_value(DynamicContextAccess const&);
+
+bool operator==(DynamicContextAccess const&, DynamicContextAccess const&);
+bool operator!=(DynamicContextAccess const&, DynamicContextAccess const&);
+
+std::ostream& operator<<(std::ostream&, DynamicContextAccess const&);
+
+DynamicContextAccess const& DynamicContextAccessOf(Operator const*);
+
+
class VectorSlotPair {
public:
VectorSlotPair(Handle<TypeFeedbackVector> vector, FeedbackVectorICSlot slot)
const Operator* LoadContext(size_t depth, size_t index, bool immutable);
const Operator* StoreContext(size_t depth, size_t index);
+ const Operator* LoadDynamicGlobal(const Handle<String>& name,
+ uint32_t check_bitset, ContextualMode mode);
+ const Operator* LoadDynamicContext(const Handle<String>& name,
+ uint32_t check_bitset, size_t depth,
+ size_t index);
+
const Operator* TypeOf();
const Operator* InstanceOf();
#define JS_CONTEXT_OP_LIST(V) \
V(JSLoadContext) \
V(JSStoreContext) \
+ V(JSLoadDynamicGlobal) \
+ V(JSLoadDynamicContext) \
V(JSCreateFunctionContext) \
V(JSCreateCatchContext) \
V(JSCreateWithContext) \
case IrOpcode::kJSCreateLiteralObject:
// Context operations
+ case IrOpcode::kJSLoadDynamicGlobal:
+ case IrOpcode::kJSLoadDynamicContext:
case IrOpcode::kJSCreateScriptContext:
case IrOpcode::kJSCreateWithContext:
}
+Bounds Typer::Visitor::TypeJSLoadDynamicGlobal(Node* node) {
+ return Bounds::Unbounded(zone());
+}
+
+
+Bounds Typer::Visitor::TypeJSLoadDynamicContext(Node* node) {
+ return Bounds::Unbounded(zone());
+}
+
+
Bounds Typer::Visitor::WrapContextBoundsForInput(Node* node) {
Bounds outer = BoundsOrNone(NodeProperties::GetContextInput(node));
if (outer.upper->Is(Type::None())) {
break;
case IrOpcode::kJSLoadContext:
+ case IrOpcode::kJSLoadDynamicGlobal:
+ case IrOpcode::kJSLoadDynamicContext:
// Type can be anything.
CheckUpperIs(node, Type::Any());
break;