Commit 440d099c authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[turbofan] Optimized lowering of DYNAMIC_GLOBAL lookup slot loads.

This adds handling of JSLoadDynamicGlobal nodes to JSTypedLowering to
perform extension checks and an inline fast path. The fast path is a
global variable load from the global object.

R=bmeurer@chromium.org
BUG=v8:4131
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#28750}
parent 450002f3
......@@ -2748,10 +2748,15 @@ VectorSlotPair AstGraphBuilder::CreateVectorSlotPair(
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;
......@@ -3021,9 +3026,10 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
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.
......@@ -3032,14 +3038,15 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
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;
}
}
......
......@@ -454,6 +454,7 @@ void JSGenericLowering::LowerJSLoadDynamicGlobal(Node* node) {
: 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);
......
......@@ -93,8 +93,12 @@ ContextAccess const& ContextAccessOf(Operator const* op) {
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);
}
......@@ -520,10 +524,10 @@ const Operator* JSOperatorBuilder::StoreContext(size_t depth, size_t index) {
}
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
......
......@@ -112,16 +112,36 @@ std::ostream& operator<<(std::ostream&, ContextAccess const&);
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.
......@@ -138,6 +158,7 @@ class DynamicGlobalAccess final {
private:
const Handle<String> name_;
const uint32_t check_bitset_;
const VectorSlotPair feedback_;
const ContextualMode mode_;
};
......@@ -190,25 +211,6 @@ std::ostream& operator<<(std::ostream&, DynamicContextAccess const&);
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 {
......@@ -376,7 +378,9 @@ class JSOperatorBuilder final : public ZoneObject {
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);
......
......@@ -924,6 +924,64 @@ Reduction JSTypedLowering::ReduceJSStoreContext(Node* node) {
}
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());
......@@ -1429,6 +1487,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSLoadContext(node);
case IrOpcode::kJSStoreContext:
return ReduceJSStoreContext(node);
case IrOpcode::kJSLoadDynamicGlobal:
return ReduceJSLoadDynamicGlobal(node);
case IrOpcode::kJSCreateClosure:
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateLiteralArray:
......
......@@ -45,6 +45,7 @@ class JSTypedLowering final : public AdvancedReducer {
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);
......
......@@ -49,7 +49,6 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
case IrOpcode::kJSCreateLiteralObject:
// Context operations
case IrOpcode::kJSLoadDynamicGlobal:
case IrOpcode::kJSLoadDynamicContext:
case IrOpcode::kJSCreateScriptContext:
case IrOpcode::kJSCreateWithContext:
......@@ -67,11 +66,12 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
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.,
......
......@@ -896,6 +896,37 @@ TEST_F(JSTypedLoweringTest, JSLoadNamedGlobalConstants) {
}
}
// -----------------------------------------------------------------------------
// 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
// -----------------------------------------------------------------------------
......
......@@ -7,6 +7,7 @@
#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"
......@@ -1186,24 +1187,32 @@ class IsLoadMatcher final : public NodeMatcher {
};
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 (";
......@@ -1213,10 +1222,14 @@ class IsToNumberMatcher final : public NodeMatcher {
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),
......@@ -1224,39 +1237,33 @@ class IsToNumberMatcher final : public NodeMatcher {
}
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 (";
......@@ -1266,14 +1273,10 @@ class IsStoreMatcher final : public NodeMatcher {
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),
......@@ -1281,15 +1284,35 @@ class IsStoreMatcher final : public NodeMatcher {
}
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,
......@@ -1727,15 +1750,6 @@ Matcher<Node*> IsLoad(const Matcher<LoadRepresentation>& rep_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,
......@@ -1748,6 +1762,21 @@ Matcher<Node*> IsStore(const Matcher<StoreRepresentation>& rep_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) { \
......
......@@ -27,6 +27,7 @@ namespace compiler {
// Forward declarations.
class BufferAccess;
class CallDescriptor;
class ContextAccess;
struct ElementAccess;
struct FieldAccess;
class Node;
......@@ -264,6 +265,8 @@ Matcher<Node*> IsToNumber(const Matcher<Node*>& base_matcher,
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);
......
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