Commit 2267ccb1 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce a dedicated CheckBounds operator.

This CheckBounds simplified operator is similar to the HBoundsCheck in
Crankshaft, and is hooked up to the new type feedback support in the
SimplifiedLowering. We use it to check the index bounds for keyed
property accesses.

Note to perf sheriffs: This will tank quite a few benchmarks, as the
operator makes some redundant branch elimination ineffective for
certain patterns of keyed accesses. This does require more serious
redundancy elimination, which we will do in a separate CL. So ignore
any regressions from this CL, we know there will be a few.

R=jarin@chromium.org
BUG=v8:4470,v8:5100

Committed: https://crrev.com/85e5567dae66a918500ae94c5568221137a0f5d4
Review-Url: https://codereview.chromium.org/2035893004
Cr-Original-Commit-Position: refs/heads/master@{#36947}
Cr-Commit-Position: refs/heads/master@{#37003}
parent c170a4c4
...@@ -416,6 +416,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -416,6 +416,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kTruncateTaggedToFloat64: case IrOpcode::kTruncateTaggedToFloat64:
state = LowerTruncateTaggedToFloat64(node, *effect, *control); state = LowerTruncateTaggedToFloat64(node, *effect, *control);
break; break;
case IrOpcode::kCheckBounds:
state = LowerCheckBounds(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckedUint32ToInt32: case IrOpcode::kCheckedUint32ToInt32:
state = LowerCheckedUint32ToInt32(node, frame_state, *effect, *control); state = LowerCheckedUint32ToInt32(node, frame_state, *effect, *control);
break; break;
...@@ -764,6 +767,22 @@ EffectControlLinearizer::LowerTruncateTaggedToFloat64(Node* node, Node* effect, ...@@ -764,6 +767,22 @@ EffectControlLinearizer::LowerTruncateTaggedToFloat64(Node* node, Node* effect,
return ValueEffectControl(value, effect, control); return ValueEffectControl(value, effect, control);
} }
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckBounds(Node* node, Node* frame_state,
Node* effect, Node* control) {
Node* index = node->InputAt(0);
Node* limit = node->InputAt(1);
Node* check = graph()->NewNode(machine()->Uint32LessThan(), index, limit);
control = effect = graph()->NewNode(common()->DeoptimizeUnless(), check,
frame_state, effect, control);
// Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0);
return ValueEffectControl(index, effect, control);
}
EffectControlLinearizer::ValueEffectControl EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckedUint32ToInt32(Node* node, EffectControlLinearizer::LowerCheckedUint32ToInt32(Node* node,
Node* frame_state, Node* frame_state,
...@@ -852,26 +871,36 @@ EffectControlLinearizer::LowerCheckedTaggedToInt32(Node* node, ...@@ -852,26 +871,36 @@ EffectControlLinearizer::LowerCheckedTaggedToInt32(Node* node,
// In the non-Smi case, check the heap numberness, load the number and convert // In the non-Smi case, check the heap numberness, load the number and convert
// to int32. // to int32.
// TODO(jarin) Propagate/handle possible truncations here.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch); Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
ValueEffectControl number_state = BuildCheckedHeapNumberOrOddballToFloat64( Node* efalse = effect;
value, frame_state, effect, if_false); Node* vfalse;
number_state = {
BuildCheckedFloat64ToInt32(number_state.value, frame_state, Node* value_map = efalse =
number_state.effect, number_state.control); graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
value, efalse, if_false);
Node* check = graph()->NewNode(machine()->WordEqual(), value_map,
jsgraph()->HeapNumberMapConstant());
if_false = efalse = graph()->NewNode(common()->DeoptimizeUnless(), check,
frame_state, efalse, if_false);
vfalse = efalse = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), value,
efalse, if_false);
ValueEffectControl state =
BuildCheckedFloat64ToInt32(vfalse, frame_state, efalse, if_false);
if_false = state.control;
efalse = state.effect;
vfalse = state.value;
}
Node* merge = control = graph()->NewNode(common()->Merge(2), if_true, if_false);
graph()->NewNode(common()->Merge(2), if_true, number_state.control); effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), etrue, value = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
number_state.effect, merge); vtrue, vfalse, control);
Node* result =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), vtrue,
number_state.value, merge);
// Make sure the lowered node does not appear in any use lists. // Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0); node->TrimInputCount(0);
return ValueEffectControl(result, effect_phi, merge); return ValueEffectControl(value, effect, control);
} }
EffectControlLinearizer::ValueEffectControl EffectControlLinearizer::ValueEffectControl
......
...@@ -62,6 +62,8 @@ class EffectControlLinearizer { ...@@ -62,6 +62,8 @@ class EffectControlLinearizer {
Node* control); Node* control);
ValueEffectControl LowerChangeTaggedToUint32(Node* node, Node* effect, ValueEffectControl LowerChangeTaggedToUint32(Node* node, Node* effect,
Node* control); Node* control);
ValueEffectControl LowerCheckBounds(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckedUint32ToInt32(Node* node, Node* frame_state, ValueEffectControl LowerCheckedUint32ToInt32(Node* node, Node* frame_state,
Node* effect, Node* control); Node* effect, Node* control);
ValueEffectControl LowerCheckedFloat64ToInt32(Node* node, Node* frame_state, ValueEffectControl LowerCheckedFloat64ToInt32(Node* node, Node* frame_state,
......
...@@ -634,6 +634,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -634,6 +634,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
receiver, jsgraph()->HeapConstant(transition_target), context, receiver, jsgraph()->HeapConstant(transition_target), context,
frame_state, transition_effect, transition_control); frame_state, transition_effect, transition_control);
} }
this_controls.push_back(transition_control); this_controls.push_back(transition_control);
this_effects.push_back(transition_effect); this_effects.push_back(transition_effect);
} }
...@@ -670,30 +671,6 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -670,30 +671,6 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
AssumePrototypesStable(receiver_type, native_context, holder); AssumePrototypesStable(receiver_type, native_context, holder);
} }
// Check that the {index} is actually a Number.
if (!NumberMatcher(this_index).HasValue()) {
Node* check =
graph()->NewNode(simplified()->ObjectIsNumber(), this_index);
this_control = this_effect =
graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state,
this_effect, this_control);
this_index = graph()->NewNode(simplified()->TypeGuard(Type::Number()),
this_index, this_control);
}
// Convert the {index} to an unsigned32 value and check if the result is
// equal to the original {index}.
if (!NumberMatcher(this_index).IsInRange(0.0, kMaxUInt32)) {
Node* this_index32 =
graph()->NewNode(simplified()->NumberToUint32(), this_index);
Node* check = graph()->NewNode(simplified()->NumberEqual(), this_index32,
this_index);
this_control = this_effect =
graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state,
this_effect, this_control);
this_index = this_index32;
}
// TODO(bmeurer): We currently specialize based on elements kind. We should // TODO(bmeurer): We currently specialize based on elements kind. We should
// also be able to properly support strings and other JSObjects here. // also be able to properly support strings and other JSObjects here.
ElementsKind elements_kind = access_info.elements_kind(); ElementsKind elements_kind = access_info.elements_kind();
...@@ -729,10 +706,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -729,10 +706,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
this_elements, this_effect, this_control); this_elements, this_effect, this_control);
// Check that the {index} is in the valid range for the {receiver}. // Check that the {index} is in the valid range for the {receiver}.
Node* check = graph()->NewNode(simplified()->NumberLessThan(), this_index, this_index = this_effect =
this_length); graph()->NewNode(simplified()->CheckBounds(), this_index, this_length,
this_control = this_effect =
graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state,
this_effect, this_control); this_effect, this_control);
// Compute the element access. // Compute the element access.
......
...@@ -220,6 +220,7 @@ ...@@ -220,6 +220,7 @@
V(ChangeFloat64ToTagged) \ V(ChangeFloat64ToTagged) \
V(ChangeTaggedToBit) \ V(ChangeTaggedToBit) \
V(ChangeBitToTagged) \ V(ChangeBitToTagged) \
V(CheckBounds) \
V(CheckedUint32ToInt32) \ V(CheckedUint32ToInt32) \
V(CheckedFloat64ToInt32) \ V(CheckedFloat64ToInt32) \
V(CheckedTaggedToInt32) \ V(CheckedTaggedToInt32) \
......
...@@ -1533,6 +1533,13 @@ class RepresentationSelector { ...@@ -1533,6 +1533,13 @@ class RepresentationSelector {
} }
return; return;
} }
case IrOpcode::kCheckBounds: {
VisitBinop(node, UseInfo::CheckedSigned32AsWord32(),
UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
return;
}
case IrOpcode::kAllocate: { case IrOpcode::kAllocate: {
ProcessInput(node, 0, UseInfo::TruncatingWord32()); ProcessInput(node, 0, UseInfo::TruncatingWord32());
ProcessRemainingInputs(node, 1); ProcessRemainingInputs(node, 1);
......
...@@ -423,6 +423,12 @@ const Operator* SimplifiedOperatorBuilder::ReferenceEqual(Type* type) { ...@@ -423,6 +423,12 @@ const Operator* SimplifiedOperatorBuilder::ReferenceEqual(Type* type) {
"ReferenceEqual", 2, 0, 0, 1, 0, 0); "ReferenceEqual", 2, 0, 0, 1, 0, 0);
} }
const Operator* SimplifiedOperatorBuilder::CheckBounds() {
// TODO(bmeurer): Cache this operator. Make it pure!
return new (zone()) Operator(IrOpcode::kCheckBounds, Operator::kEliminatable,
"CheckBounds", 2, 1, 1, 1, 1, 0);
}
const Operator* SimplifiedOperatorBuilder::TypeGuard(Type* type) { const Operator* SimplifiedOperatorBuilder::TypeGuard(Type* type) {
class TypeGuardOperator final : public Operator1<Type*> { class TypeGuardOperator final : public Operator1<Type*> {
public: public:
......
...@@ -217,6 +217,8 @@ class SimplifiedOperatorBuilder final : public ZoneObject { ...@@ -217,6 +217,8 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* TruncateTaggedToWord32(); const Operator* TruncateTaggedToWord32();
const Operator* TruncateTaggedToFloat64(); const Operator* TruncateTaggedToFloat64();
const Operator* CheckBounds();
const Operator* CheckedUint32ToInt32(); const Operator* CheckedUint32ToInt32();
const Operator* CheckedFloat64ToInt32(); const Operator* CheckedFloat64ToInt32();
const Operator* CheckedTaggedToInt32(); const Operator* CheckedTaggedToInt32();
......
...@@ -1945,6 +1945,11 @@ Type* Typer::Visitor::TypeChangeBitToTagged(Node* node) { ...@@ -1945,6 +1945,11 @@ Type* Typer::Visitor::TypeChangeBitToTagged(Node* node) {
return ChangeRepresentation(arg, Type::TaggedPointer(), zone()); return ChangeRepresentation(arg, Type::TaggedPointer(), zone());
} }
Type* Typer::Visitor::TypeCheckBounds(Node* node) {
// TODO(bmeurer): We could do better here based on the limit.
return Type::Unsigned31();
}
Type* Typer::Visitor::TypeCheckedUint32ToInt32(Node* node) { Type* Typer::Visitor::TypeCheckedUint32ToInt32(Node* node) {
return Type::Signed32(); return Type::Signed32();
} }
......
...@@ -924,6 +924,12 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -924,6 +924,12 @@ void Verifier::Visitor::Check(Node* node) {
break; break;
} }
case IrOpcode::kCheckBounds:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Unsigned31());
CheckUpperIs(node, Type::Unsigned31());
break;
case IrOpcode::kCheckedUint32ToInt32: case IrOpcode::kCheckedUint32ToInt32:
case IrOpcode::kCheckedFloat64ToInt32: case IrOpcode::kCheckedFloat64ToInt32:
case IrOpcode::kCheckedTaggedToInt32: case IrOpcode::kCheckedTaggedToInt32:
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
var a = [0, 1];
a["true"] = "true";
a["false"] = "false";
a["null"] = "null";
a["undefined"] = "undefined";
// Ensure we don't accidentially truncate true when used to index arrays.
(function() {
function f(x) { return a[x]; }
assertEquals(0, f(0));
assertEquals(0, f(0));
%OptimizeFunctionOnNextCall(f);
assertEquals("true", f(true));
})();
// Ensure we don't accidentially truncate false when used to index arrays.
(function() {
function f( x) { return a[x]; }
assertEquals(0, f(0));
assertEquals(0, f(0));
%OptimizeFunctionOnNextCall(f);
assertEquals("false", f(false));
})();
// Ensure we don't accidentially truncate null when used to index arrays.
(function() {
function f( x) { return a[x]; }
assertEquals(0, f(0));
assertEquals(0, f(0));
%OptimizeFunctionOnNextCall(f);
assertEquals("null", f(null));
})();
// Ensure we don't accidentially truncate undefined when used to index arrays.
(function() {
function f( x) { return a[x]; }
assertEquals(0, f(0));
assertEquals(0, f(0));
%OptimizeFunctionOnNextCall(f);
assertEquals("undefined", f(undefined));
})();
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