uint32_t AstGraphBuilder::ComputeBitsetForDynamicGlobal(Variable* variable) {
DCHECK_EQ(DYNAMIC_GLOBAL, variable->mode());
+ bool found_eval_scope = false;
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!
+ // TODO(mstarzinger): If we have reached an eval scope, we check all
+ // extensions from this point. Replicated from full-codegen, figure out
+ // whether this is still needed. If not, drop {found_eval_scope} below.
+ if (s->is_eval_scope()) found_eval_scope = true;
+ if (!s->calls_sloppy_eval() && !found_eval_scope) continue;
int depth = current_scope()->ContextChainLength(s);
if (depth > DynamicGlobalAccess::kMaxCheckDepth) {
return DynamicGlobalAccess::kFullCheckRequired;
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);
+ const Operator* op = javascript()->LoadDynamicGlobal(
+ name, check_bitset, feedback, contextual_mode);
value = NewNode(op, current_context());
+ states.AddToNode(value, bailout_id, combine);
} else if (mode == DYNAMIC_LOCAL) {
Variable* local = variable->local_if_not_shadowed();
DCHECK(local->location() == Variable::CONTEXT); // Must be context.
const Operator* op = javascript()->LoadDynamicContext(
name, check_bitset, depth, local->index());
value = NewNode(op, current_context());
+ PrepareFrameState(value, bailout_id, combine);
// 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);
+ const Operator* op = javascript()->LoadDynamicGlobal(
+ name, check_bitset, feedback, contextual_mode);
value = NewNode(op, current_context());
+ states.AddToNode(value, bailout_id, combine);
}
- PrepareFrameState(value, bailout_id, combine);
return value;
}
}
: Runtime::kLoadLookupSlotNoReferenceError;
Node* projection = graph()->NewNode(common()->Projection(0), node);
NodeProperties::ReplaceWithValue(node, projection, node, node);
+ node->RemoveInput(NodeProperties::FirstFrameStateIndex(node) + 1);
node->InsertInput(zone(), 1, jsgraph()->Constant(access.name()));
ReplaceWithRuntimeCall(node, function_id);
projection->ReplaceInput(0, node);
DynamicGlobalAccess::DynamicGlobalAccess(const Handle<String>& name,
uint32_t check_bitset,
+ const VectorSlotPair& feedback,
ContextualMode mode)
- : name_(name), check_bitset_(check_bitset), mode_(mode) {
+ : name_(name),
+ check_bitset_(check_bitset),
+ feedback_(feedback),
+ mode_(mode) {
DCHECK(check_bitset == kFullCheckRequired || check_bitset < 0x80000000U);
}
}
-const Operator* JSOperatorBuilder::LoadDynamicGlobal(const Handle<String>& name,
- uint32_t check_bitset,
- ContextualMode mode) {
- DynamicGlobalAccess access(name, check_bitset, mode);
+const Operator* JSOperatorBuilder::LoadDynamicGlobal(
+ const Handle<String>& name, uint32_t check_bitset,
+ const VectorSlotPair& feedback, ContextualMode mode) {
+ DynamicGlobalAccess access(name, check_bitset, feedback, mode);
return new (zone()) Operator1<DynamicGlobalAccess>( // --
IrOpcode::kJSLoadDynamicGlobal, Operator::kNoProperties, // opcode
"JSLoadDynamicGlobal", // name
ContextAccess const& ContextAccessOf(Operator const*);
+class VectorSlotPair {
+ public:
+ VectorSlotPair(Handle<TypeFeedbackVector> vector, FeedbackVectorICSlot slot)
+ : vector_(vector), slot_(slot) {}
+
+ Handle<TypeFeedbackVector> vector() const { return vector_; }
+ FeedbackVectorICSlot slot() const { return slot_; }
+
+ int index() const { return vector_->GetIndex(slot_); }
+
+ private:
+ const Handle<TypeFeedbackVector> vector_;
+ const FeedbackVectorICSlot slot_;
+};
+
+
+bool operator==(VectorSlotPair const& lhs, VectorSlotPair const& rhs);
+
+
// 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 VectorSlotPair& feedback, ContextualMode mode);
const Handle<String>& name() const { return name_; }
uint32_t check_bitset() const { return check_bitset_; }
+ const VectorSlotPair& feedback() const { return feedback_; }
ContextualMode mode() const { return mode_; }
// Indicates that an inline check is disabled.
private:
const Handle<String> name_;
const uint32_t check_bitset_;
+ const VectorSlotPair feedback_;
const ContextualMode mode_;
};
DynamicContextAccess const& DynamicContextAccessOf(Operator const*);
-class VectorSlotPair {
- public:
- VectorSlotPair(Handle<TypeFeedbackVector> vector, FeedbackVectorICSlot slot)
- : vector_(vector), slot_(slot) {}
-
- Handle<TypeFeedbackVector> vector() const { return vector_; }
- FeedbackVectorICSlot slot() const { return slot_; }
-
- int index() const { return vector_->GetIndex(slot_); }
-
- private:
- const Handle<TypeFeedbackVector> vector_;
- const FeedbackVectorICSlot slot_;
-};
-
-
-bool operator==(VectorSlotPair const& lhs, VectorSlotPair const& rhs);
-
-
// Defines the property being loaded from an object by a named load. This is
// used as a parameter by JSLoadNamed operators.
class LoadNamedParameters final {
const Operator* StoreContext(size_t depth, size_t index);
const Operator* LoadDynamicGlobal(const Handle<String>& name,
- uint32_t check_bitset, ContextualMode mode);
+ uint32_t check_bitset,
+ const VectorSlotPair& feedback,
+ ContextualMode mode);
const Operator* LoadDynamicContext(const Handle<String>& name,
uint32_t check_bitset, size_t depth,
size_t index);
}
+Reduction JSTypedLowering::ReduceJSLoadDynamicGlobal(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSLoadDynamicGlobal, node->opcode());
+ DynamicGlobalAccess const& access = DynamicGlobalAccessOf(node->op());
+ Node* const context = NodeProperties::GetContextInput(node);
+ Node* const state1 = NodeProperties::GetFrameStateInput(node, 0);
+ Node* const state2 = NodeProperties::GetFrameStateInput(node, 1);
+ Node* const effect = NodeProperties::GetEffectInput(node);
+ Node* const control = NodeProperties::GetControlInput(node);
+ if (access.RequiresFullCheck()) return NoChange();
+
+ // Perform checks whether the fast mode applies, by looking for any extension
+ // object which might shadow the optimistic declaration.
+ uint32_t bitset = access.check_bitset();
+ Node* check_true = control;
+ Node* check_false = graph()->NewNode(common()->Merge(0));
+ for (int depth = 0; bitset != 0; bitset >>= 1, depth++) {
+ if ((bitset & 1) == 0) continue;
+ Node* load = graph()->NewNode(
+ javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
+ context, context, effect);
+ Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), load);
+ Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check,
+ check_true);
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+ check_false->set_op(common()->Merge(check_false->InputCount() + 1));
+ check_false->AppendInput(graph()->zone(), if_false);
+ check_true = if_true;
+ }
+
+ // Fast case, because variable is not shadowed. Perform global object load.
+ Unique<Name> name = Unique<Name>::CreateUninitialized(access.name());
+ Node* global = graph()->NewNode(
+ javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true), context,
+ context, effect);
+ Node* fast = graph()->NewNode(
+ javascript()->LoadNamed(name, access.feedback(), access.mode()), global,
+ context, state1, state2, global, check_true);
+
+ // Slow case, because variable potentially shadowed. Perform dynamic lookup.
+ uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired;
+ Node* slow = graph()->NewNode(
+ javascript()->LoadDynamicGlobal(access.name(), check_bitset,
+ access.feedback(), access.mode()),
+ context, context, state1, state2, effect, check_false);
+
+ // Replace value, effect and control uses accordingly.
+ Node* new_control =
+ graph()->NewNode(common()->Merge(2), check_true, check_false);
+ Node* new_effect =
+ graph()->NewNode(common()->EffectPhi(2), fast, slow, new_control);
+ Node* new_value = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), fast,
+ slow, new_control);
+ ReplaceWithValue(node, new_value, new_effect, new_control);
+ return Changed(new_value);
+}
+
+
Reduction JSTypedLowering::ReduceJSCreateClosure(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode());
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
return ReduceJSLoadContext(node);
case IrOpcode::kJSStoreContext:
return ReduceJSStoreContext(node);
+ case IrOpcode::kJSLoadDynamicGlobal:
+ return ReduceJSLoadDynamicGlobal(node);
case IrOpcode::kJSCreateClosure:
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateLiteralArray:
Reduction ReduceJSStoreProperty(Node* node);
Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSStoreContext(Node* node);
+ Reduction ReduceJSLoadDynamicGlobal(Node* node);
Reduction ReduceJSEqual(Node* node, bool invert);
Reduction ReduceJSStrictEqual(Node* node, bool invert);
Reduction ReduceJSUnaryNot(Node* node);
case IrOpcode::kJSCreateLiteralObject:
// Context operations
- case IrOpcode::kJSLoadDynamicGlobal:
case IrOpcode::kJSLoadDynamicContext:
case IrOpcode::kJSCreateScriptContext:
case IrOpcode::kJSCreateWithContext:
return 1;
// We record the frame state immediately before and immediately after
- // every property access.
+ // every property or global variable access.
case IrOpcode::kJSLoadNamed:
case IrOpcode::kJSStoreNamed:
case IrOpcode::kJSLoadProperty:
case IrOpcode::kJSStoreProperty:
+ case IrOpcode::kJSLoadDynamicGlobal:
return 2;
// Binary operators that can deopt in the middle the operation (e.g.,
}
}
+
+// -----------------------------------------------------------------------------
+// JSLoadDynamicGlobal
+
+
+TEST_F(JSTypedLoweringTest, JSLoadDynamicGlobal) {
+ Node* const context = Parameter(Type::Any());
+ Node* const frame_state = EmptyFrameState();
+ Node* const effect = graph()->start();
+ Node* const control = graph()->start();
+ Handle<String> name = factory()->object_string();
+ VectorSlotPair feedback(Handle<TypeFeedbackVector>::null(),
+ FeedbackVectorICSlot::Invalid());
+ for (int i = 0; i < DynamicGlobalAccess::kMaxCheckDepth; ++i) {
+ uint32_t bitset = 1 << i; // Only single check.
+ Reduction r = Reduce(graph()->NewNode(
+ javascript()->LoadDynamicGlobal(name, bitset, feedback, NOT_CONTEXTUAL),
+ context, context, frame_state, frame_state, effect, control));
+ ASSERT_TRUE(r.Changed());
+ EXPECT_THAT(
+ r.replacement(),
+ IsPhi(kMachAnyTagged, _, _,
+ IsMerge(IsIfTrue(IsBranch(
+ IsObjectIsSmi(IsLoadContext(
+ ContextAccess(i, Context::EXTENSION_INDEX, false),
+ context)),
+ control)),
+ _)));
+ }
+}
+
#if V8_TURBOFAN_TARGET
// -----------------------------------------------------------------------------
#include <vector>
#include "src/assembler.h"
+#include "src/compiler/js-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/unique.h"
};
-class IsToNumberMatcher final : public NodeMatcher {
+class IsStoreMatcher final : public NodeMatcher {
public:
- IsToNumberMatcher(const Matcher<Node*>& base_matcher,
- const Matcher<Node*>& context_matcher,
- const Matcher<Node*>& effect_matcher,
- const Matcher<Node*>& control_matcher)
- : NodeMatcher(IrOpcode::kJSToNumber),
+ IsStoreMatcher(const Matcher<StoreRepresentation>& rep_matcher,
+ const Matcher<Node*>& base_matcher,
+ const Matcher<Node*>& index_matcher,
+ const Matcher<Node*>& value_matcher,
+ const Matcher<Node*>& effect_matcher,
+ const Matcher<Node*>& control_matcher)
+ : NodeMatcher(IrOpcode::kStore),
+ rep_matcher_(rep_matcher),
base_matcher_(base_matcher),
- context_matcher_(context_matcher),
+ index_matcher_(index_matcher),
+ value_matcher_(value_matcher),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
void DescribeTo(std::ostream* os) const final {
NodeMatcher::DescribeTo(os);
- *os << " whose base (";
+ *os << " whose rep (";
+ rep_matcher_.DescribeTo(os);
+ *os << "), base (";
base_matcher_.DescribeTo(os);
- *os << "), context (";
- context_matcher_.DescribeTo(os);
+ *os << "), index (";
+ index_matcher_.DescribeTo(os);
+ *os << "), value (";
+ value_matcher_.DescribeTo(os);
*os << "), effect (";
effect_matcher_.DescribeTo(os);
*os << ") and control (";
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (NodeMatcher::MatchAndExplain(node, listener) &&
+ PrintMatchAndExplain(OpParameter<StoreRepresentation>(node), "rep",
+ rep_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "base",
base_matcher_, listener) &&
- PrintMatchAndExplain(NodeProperties::GetContextInput(node),
- "context", context_matcher_, listener) &&
+ PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
+ "index", index_matcher_, listener) &&
+ PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2),
+ "value", value_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
}
private:
+ const Matcher<StoreRepresentation> rep_matcher_;
const Matcher<Node*> base_matcher_;
- const Matcher<Node*> context_matcher_;
+ const Matcher<Node*> index_matcher_;
+ const Matcher<Node*> value_matcher_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
-class IsStoreMatcher final : public NodeMatcher {
+class IsToNumberMatcher final : public NodeMatcher {
public:
- IsStoreMatcher(const Matcher<StoreRepresentation>& rep_matcher,
- const Matcher<Node*>& base_matcher,
- const Matcher<Node*>& index_matcher,
- const Matcher<Node*>& value_matcher,
- const Matcher<Node*>& effect_matcher,
- const Matcher<Node*>& control_matcher)
- : NodeMatcher(IrOpcode::kStore),
- rep_matcher_(rep_matcher),
+ IsToNumberMatcher(const Matcher<Node*>& base_matcher,
+ const Matcher<Node*>& context_matcher,
+ const Matcher<Node*>& effect_matcher,
+ const Matcher<Node*>& control_matcher)
+ : NodeMatcher(IrOpcode::kJSToNumber),
base_matcher_(base_matcher),
- index_matcher_(index_matcher),
- value_matcher_(value_matcher),
+ context_matcher_(context_matcher),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
void DescribeTo(std::ostream* os) const final {
NodeMatcher::DescribeTo(os);
- *os << " whose rep (";
- rep_matcher_.DescribeTo(os);
- *os << "), base (";
+ *os << " whose base (";
base_matcher_.DescribeTo(os);
- *os << "), index (";
- index_matcher_.DescribeTo(os);
- *os << "), value (";
- value_matcher_.DescribeTo(os);
+ *os << "), context (";
+ context_matcher_.DescribeTo(os);
*os << "), effect (";
effect_matcher_.DescribeTo(os);
*os << ") and control (";
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (NodeMatcher::MatchAndExplain(node, listener) &&
- PrintMatchAndExplain(OpParameter<StoreRepresentation>(node), "rep",
- rep_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "base",
base_matcher_, listener) &&
- PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
- "index", index_matcher_, listener) &&
- PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2),
- "value", value_matcher_, listener) &&
+ PrintMatchAndExplain(NodeProperties::GetContextInput(node),
+ "context", context_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
}
private:
- const Matcher<StoreRepresentation> rep_matcher_;
const Matcher<Node*> base_matcher_;
- const Matcher<Node*> index_matcher_;
- const Matcher<Node*> value_matcher_;
+ const Matcher<Node*> context_matcher_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
+class IsLoadContextMatcher final : public NodeMatcher {
+ public:
+ IsLoadContextMatcher(const Matcher<ContextAccess>& access_matcher,
+ const Matcher<Node*>& context_matcher)
+ : NodeMatcher(IrOpcode::kJSLoadContext),
+ access_matcher_(access_matcher),
+ context_matcher_(context_matcher) {}
+
+ bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
+ return (NodeMatcher::MatchAndExplain(node, listener) &&
+ PrintMatchAndExplain(OpParameter<ContextAccess>(node), "access",
+ access_matcher_, listener) &&
+ PrintMatchAndExplain(NodeProperties::GetContextInput(node),
+ "context", context_matcher_, listener));
+ }
+
+ private:
+ const Matcher<ContextAccess> access_matcher_;
+ const Matcher<Node*> context_matcher_;
+};
+
+
class IsBinopMatcher final : public NodeMatcher {
public:
IsBinopMatcher(IrOpcode::Value opcode, const Matcher<Node*>& lhs_matcher,
}
-Matcher<Node*> IsToNumber(const Matcher<Node*>& base_matcher,
- const Matcher<Node*>& context_matcher,
- const Matcher<Node*>& effect_matcher,
- const Matcher<Node*>& control_matcher) {
- return MakeMatcher(new IsToNumberMatcher(base_matcher, context_matcher,
- effect_matcher, control_matcher));
-}
-
-
Matcher<Node*> IsStore(const Matcher<StoreRepresentation>& rep_matcher,
const Matcher<Node*>& base_matcher,
const Matcher<Node*>& index_matcher,
}
+Matcher<Node*> IsToNumber(const Matcher<Node*>& base_matcher,
+ const Matcher<Node*>& context_matcher,
+ const Matcher<Node*>& effect_matcher,
+ const Matcher<Node*>& control_matcher) {
+ return MakeMatcher(new IsToNumberMatcher(base_matcher, context_matcher,
+ effect_matcher, control_matcher));
+}
+
+
+Matcher<Node*> IsLoadContext(const Matcher<ContextAccess>& access_matcher,
+ const Matcher<Node*>& context_matcher) {
+ return MakeMatcher(new IsLoadContextMatcher(access_matcher, context_matcher));
+}
+
+
#define IS_BINOP_MATCHER(Name) \
Matcher<Node*> Is##Name(const Matcher<Node*>& lhs_matcher, \
const Matcher<Node*>& rhs_matcher) { \
// Forward declarations.
class BufferAccess;
class CallDescriptor;
+class ContextAccess;
struct ElementAccess;
struct FieldAccess;
class Node;
const Matcher<Node*>& context_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
+Matcher<Node*> IsLoadContext(const Matcher<ContextAccess>& access_matcher,
+ const Matcher<Node*>& context_matcher);
Matcher<Node*> IsNumberToInt32(const Matcher<Node*>& input_matcher);
Matcher<Node*> IsNumberToUint32(const Matcher<Node*>& input_matcher);