aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/js-typed-lowering.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/js-typed-lowering.cc')
-rw-r--r--src/compiler/js-typed-lowering.cc582
1 files changed, 398 insertions, 184 deletions
diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc
index dbbeca6e..31accbd8 100644
--- a/src/compiler/js-typed-lowering.cc
+++ b/src/compiler/js-typed-lowering.cc
@@ -16,6 +16,7 @@
#include "src/compiler/operator-properties.h"
#include "src/compiler/type-cache.h"
#include "src/compiler/types.h"
+#include "src/objects-inl.h"
namespace v8 {
namespace internal {
@@ -30,30 +31,6 @@ class JSBinopReduction final {
JSBinopReduction(JSTypedLowering* lowering, Node* node)
: lowering_(lowering), node_(node) {}
- bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
- if (lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) {
- DCHECK_NE(0, node_->op()->ControlOutputCount());
- DCHECK_EQ(1, node_->op()->EffectOutputCount());
- DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node_->op()));
- switch (BinaryOperationHintOf(node_->op())) {
- case BinaryOperationHint::kSignedSmall:
- *hint = NumberOperationHint::kSignedSmall;
- return true;
- case BinaryOperationHint::kSigned32:
- *hint = NumberOperationHint::kSigned32;
- return true;
- case BinaryOperationHint::kNumberOrOddball:
- *hint = NumberOperationHint::kNumberOrOddball;
- return true;
- case BinaryOperationHint::kAny:
- case BinaryOperationHint::kNone:
- case BinaryOperationHint::kString:
- break;
- }
- }
- return false;
- }
-
bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
if (lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) {
DCHECK_EQ(1, node_->op()->EffectOutputCount());
@@ -69,17 +46,51 @@ class JSBinopReduction final {
return true;
case CompareOperationHint::kAny:
case CompareOperationHint::kNone:
+ case CompareOperationHint::kString:
+ case CompareOperationHint::kReceiver:
+ case CompareOperationHint::kInternalizedString:
break;
}
}
return false;
}
+ bool IsInternalizedStringCompareOperation() {
+ if (lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) {
+ DCHECK_EQ(1, node_->op()->EffectOutputCount());
+ return (CompareOperationHintOf(node_->op()) ==
+ CompareOperationHint::kInternalizedString) &&
+ BothInputsMaybe(Type::InternalizedString());
+ }
+ return false;
+ }
+
+ bool IsReceiverCompareOperation() {
+ if (lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) {
+ DCHECK_EQ(1, node_->op()->EffectOutputCount());
+ return (CompareOperationHintOf(node_->op()) ==
+ CompareOperationHint::kReceiver) &&
+ BothInputsMaybe(Type::Receiver());
+ }
+ return false;
+ }
+
+ bool IsStringCompareOperation() {
+ if (lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) {
+ DCHECK_EQ(1, node_->op()->EffectOutputCount());
+ return (CompareOperationHintOf(node_->op()) ==
+ CompareOperationHint::kString) &&
+ BothInputsMaybe(Type::String());
+ }
+ return false;
+ }
+
// Check if a string addition will definitely result in creating a ConsString,
// i.e. if the combined length of the resulting string exceeds the ConsString
// minimum length.
bool ShouldCreateConsString() {
DCHECK_EQ(IrOpcode::kJSAdd, node_->opcode());
+ DCHECK(OneInputIs(Type::String()));
if (BothInputsAre(Type::String()) ||
((lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) &&
BinaryOperationHintOf(node_->op()) == BinaryOperationHint::kString)) {
@@ -103,6 +114,66 @@ class JSBinopReduction final {
return false;
}
+ // Inserts a CheckReceiver for the left input.
+ void CheckLeftInputToReceiver() {
+ Node* left_input = graph()->NewNode(simplified()->CheckReceiver(), left(),
+ effect(), control());
+ node_->ReplaceInput(0, left_input);
+ update_effect(left_input);
+ }
+
+ // Checks that both inputs are Receiver, and if we don't know
+ // statically that one side is already a Receiver, insert a
+ // CheckReceiver node.
+ void CheckInputsToReceiver() {
+ if (!left_type()->Is(Type::Receiver())) {
+ CheckLeftInputToReceiver();
+ }
+ if (!right_type()->Is(Type::Receiver())) {
+ Node* right_input = graph()->NewNode(simplified()->CheckReceiver(),
+ right(), effect(), control());
+ node_->ReplaceInput(1, right_input);
+ update_effect(right_input);
+ }
+ }
+
+ // Checks that both inputs are String, and if we don't know
+ // statically that one side is already a String, insert a
+ // CheckString node.
+ void CheckInputsToString() {
+ if (!left_type()->Is(Type::String())) {
+ Node* left_input = graph()->NewNode(simplified()->CheckString(), left(),
+ effect(), control());
+ node_->ReplaceInput(0, left_input);
+ update_effect(left_input);
+ }
+ if (!right_type()->Is(Type::String())) {
+ Node* right_input = graph()->NewNode(simplified()->CheckString(), right(),
+ effect(), control());
+ node_->ReplaceInput(1, right_input);
+ update_effect(right_input);
+ }
+ }
+
+ // Checks that both inputs are InternalizedString, and if we don't know
+ // statically that one side is already an InternalizedString, insert a
+ // CheckInternalizedString node.
+ void CheckInputsToInternalizedString() {
+ if (!left_type()->Is(Type::UniqueName())) {
+ Node* left_input = graph()->NewNode(
+ simplified()->CheckInternalizedString(), left(), effect(), control());
+ node_->ReplaceInput(0, left_input);
+ update_effect(left_input);
+ }
+ if (!right_type()->Is(Type::UniqueName())) {
+ Node* right_input =
+ graph()->NewNode(simplified()->CheckInternalizedString(), right(),
+ effect(), control());
+ node_->ReplaceInput(1, right_input);
+ update_effect(right_input);
+ }
+ }
+
void ConvertInputsToNumber() {
// To convert the inputs to numbers, we have to provide frame states
// for lazy bailouts in the ToNumber conversions.
@@ -277,30 +348,18 @@ class JSBinopReduction final {
return nullptr;
}
- const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
+ const Operator* NumberOpFromSpeculativeNumberOp() {
switch (node_->opcode()) {
- case IrOpcode::kJSAdd:
- return simplified()->SpeculativeNumberAdd(hint);
- case IrOpcode::kJSSubtract:
- return simplified()->SpeculativeNumberSubtract(hint);
- case IrOpcode::kJSMultiply:
- return simplified()->SpeculativeNumberMultiply(hint);
- case IrOpcode::kJSDivide:
- return simplified()->SpeculativeNumberDivide(hint);
- case IrOpcode::kJSModulus:
- return simplified()->SpeculativeNumberModulus(hint);
- case IrOpcode::kJSBitwiseAnd:
- return simplified()->SpeculativeNumberBitwiseAnd(hint);
- case IrOpcode::kJSBitwiseOr:
- return simplified()->SpeculativeNumberBitwiseOr(hint);
- case IrOpcode::kJSBitwiseXor:
- return simplified()->SpeculativeNumberBitwiseXor(hint);
- case IrOpcode::kJSShiftLeft:
- return simplified()->SpeculativeNumberShiftLeft(hint);
- case IrOpcode::kJSShiftRight:
- return simplified()->SpeculativeNumberShiftRight(hint);
- case IrOpcode::kJSShiftRightLogical:
- return simplified()->SpeculativeNumberShiftRightLogical(hint);
+ case IrOpcode::kSpeculativeNumberAdd:
+ return simplified()->NumberAdd();
+ case IrOpcode::kSpeculativeNumberSubtract:
+ return simplified()->NumberSubtract();
+ case IrOpcode::kSpeculativeNumberMultiply:
+ return simplified()->NumberMultiply();
+ case IrOpcode::kSpeculativeNumberDivide:
+ return simplified()->NumberDivide();
+ case IrOpcode::kSpeculativeNumberModulus:
+ return simplified()->NumberModulus();
default:
break;
}
@@ -316,6 +375,10 @@ class JSBinopReduction final {
bool BothInputsAre(Type* t) { return LeftInputIs(t) && RightInputIs(t); }
+ bool BothInputsMaybe(Type* t) {
+ return left_type()->Maybe(t) && right_type()->Maybe(t);
+ }
+
bool OneInputCannotBe(Type* t) {
return !left_type()->Maybe(t) || !right_type()->Maybe(t);
}
@@ -459,8 +522,13 @@ JSTypedLowering::JSTypedLowering(Editor* editor,
dependencies_(dependencies),
flags_(flags),
jsgraph_(jsgraph),
- the_hole_type_(
- Type::HeapConstant(factory()->the_hole_value(), graph()->zone())),
+ pointer_comparable_type_(Type::Union(
+ Type::Oddball(),
+ Type::Union(
+ Type::SymbolOrReceiver(),
+ Type::HeapConstant(factory()->empty_string(), graph()->zone()),
+ graph()->zone()),
+ graph()->zone())),
type_cache_(TypeCache::Get()) {
for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) {
double min = kMinInt / (1 << k);
@@ -469,20 +537,22 @@ JSTypedLowering::JSTypedLowering(Editor* editor,
}
}
-Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
+Reduction JSTypedLowering::ReduceSpeculativeNumberAdd(Node* node) {
JSBinopReduction r(this, node);
- NumberOperationHint hint;
- if (r.GetBinaryNumberOperationHint(&hint)) {
- if (hint == NumberOperationHint::kNumberOrOddball &&
- r.BothInputsAre(Type::PlainPrimitive()) &&
- r.NeitherInputCanBe(Type::StringOrReceiver())) {
- // JSAdd(x:-string, y:-string) => NumberAdd(ToNumber(x), ToNumber(y))
- r.ConvertInputsToNumber();
- return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
- }
- return r.ChangeToSpeculativeOperator(
- simplified()->SpeculativeNumberAdd(hint), Type::Number());
+ NumberOperationHint hint = NumberOperationHintOf(node->op());
+ if (hint == NumberOperationHint::kNumberOrOddball &&
+ r.BothInputsAre(Type::PlainPrimitive()) &&
+ r.NeitherInputCanBe(Type::StringOrReceiver())) {
+ // SpeculativeNumberAdd(x:-string, y:-string) =>
+ // NumberAdd(ToNumber(x), ToNumber(y))
+ r.ConvertInputsToNumber();
+ return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
}
+ return NoChange();
+}
+
+Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
+ JSBinopReduction r(this, node);
if (r.BothInputsAre(Type::Number())) {
// JSAdd(x:number, y:number) => NumberAdd(x, y)
r.ConvertInputsToNumber();
@@ -505,13 +575,20 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
} else if (!r.RightInputIs(Type::String())) {
flags = STRING_ADD_CONVERT_RIGHT;
}
+ Operator::Properties properties = node->op()->properties();
+ if (r.NeitherInputCanBe(Type::Receiver())) {
+ // Both sides are already strings, so we know that the
+ // string addition will not cause any observable side
+ // effects; it can still throw obviously.
+ properties = Operator::kNoWrite | Operator::kNoDeopt;
+ }
// JSAdd(x:string, y) => CallStub[StringAdd](x, y)
// JSAdd(x, y:string) => CallStub[StringAdd](x, y)
Callable const callable =
CodeFactory::StringAdd(isolate(), flags, NOT_TENURED);
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
- CallDescriptor::kNeedsFrameState, node->op()->properties());
+ CallDescriptor::kNeedsFrameState, properties);
DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
node->InsertInput(graph()->zone(), 0,
jsgraph()->HeapConstant(callable.code()));
@@ -523,16 +600,6 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
Reduction JSTypedLowering::ReduceNumberBinop(Node* node) {
JSBinopReduction r(this, node);
- NumberOperationHint hint;
- if (r.GetBinaryNumberOperationHint(&hint)) {
- if (hint == NumberOperationHint::kNumberOrOddball &&
- r.BothInputsAre(Type::NumberOrOddball())) {
- r.ConvertInputsToNumber();
- return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
- }
- return r.ChangeToSpeculativeOperator(r.SpeculativeNumberOp(hint),
- Type::Number());
- }
if (r.BothInputsAre(Type::PlainPrimitive()) ||
!(flags() & kDeoptimizationEnabled)) {
r.ConvertInputsToNumber();
@@ -541,13 +608,20 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node) {
return NoChange();
}
-Reduction JSTypedLowering::ReduceInt32Binop(Node* node) {
+Reduction JSTypedLowering::ReduceSpeculativeNumberBinop(Node* node) {
JSBinopReduction r(this, node);
- NumberOperationHint hint;
- if (r.GetBinaryNumberOperationHint(&hint)) {
- return r.ChangeToSpeculativeOperator(r.SpeculativeNumberOp(hint),
- Type::Signed32());
+ NumberOperationHint hint = NumberOperationHintOf(node->op());
+ if (hint == NumberOperationHint::kNumberOrOddball &&
+ r.BothInputsAre(Type::NumberOrOddball())) {
+ r.ConvertInputsToNumber();
+ return r.ChangeToPureOperator(r.NumberOpFromSpeculativeNumberOp(),
+ Type::Number());
}
+ return NoChange();
+}
+
+Reduction JSTypedLowering::ReduceInt32Binop(Node* node) {
+ JSBinopReduction r(this, node);
if (r.BothInputsAre(Type::PlainPrimitive()) ||
!(flags() & kDeoptimizationEnabled)) {
r.ConvertInputsToNumber();
@@ -559,12 +633,6 @@ Reduction JSTypedLowering::ReduceInt32Binop(Node* node) {
Reduction JSTypedLowering::ReduceUI32Shift(Node* node, Signedness signedness) {
JSBinopReduction r(this, node);
- NumberOperationHint hint;
- if (r.GetBinaryNumberOperationHint(&hint)) {
- return r.ChangeToSpeculativeOperator(
- r.SpeculativeNumberOp(hint),
- signedness == kUnsigned ? Type::Unsigned32() : Type::Signed32());
- }
if (r.BothInputsAre(Type::PlainPrimitive()) ||
!(flags() & kDeoptimizationEnabled)) {
r.ConvertInputsToNumber();
@@ -746,6 +814,10 @@ Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
r.ConvertInputsToNumber();
less_than = simplified()->NumberLessThan();
less_than_or_equal = simplified()->NumberLessThanOrEqual();
+ } else if (r.IsStringCompareOperation()) {
+ r.CheckInputsToString();
+ less_than = simplified()->StringLessThan();
+ less_than_or_equal = simplified()->StringLessThanOrEqual();
} else {
return NoChange();
}
@@ -787,61 +859,72 @@ Reduction JSTypedLowering::ReduceJSTypeOf(Node* node) {
return Replace(jsgraph()->Constant(f->string_string()));
} else if (type->Is(Type::Symbol())) {
return Replace(jsgraph()->Constant(f->symbol_string()));
- } else if (type->Is(Type::Union(Type::Undefined(), Type::OtherUndetectable(),
- graph()->zone()))) {
+ } else if (type->Is(Type::OtherUndetectableOrUndefined())) {
return Replace(jsgraph()->Constant(f->undefined_string()));
- } else if (type->Is(Type::Null())) {
+ } else if (type->Is(Type::NonCallableOrNull())) {
return Replace(jsgraph()->Constant(f->object_string()));
} else if (type->Is(Type::Function())) {
return Replace(jsgraph()->Constant(f->function_string()));
} else if (type->IsHeapConstant()) {
return Replace(jsgraph()->Constant(
Object::TypeOf(isolate(), type->AsHeapConstant()->Value())));
- } else if (type->IsOtherNumberConstant()) {
- return Replace(jsgraph()->Constant(f->number_string()));
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSEqualTypeOf(Node* node, bool invert) {
+ Node* input;
+ Handle<String> type;
HeapObjectBinopMatcher m(node);
if (m.left().IsJSTypeOf() && m.right().HasValue() &&
m.right().Value()->IsString()) {
- Node* replacement;
- Node* input = m.left().InputAt(0);
- Handle<String> value = Handle<String>::cast(m.right().Value());
- if (String::Equals(value, factory()->boolean_string())) {
- replacement =
- graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
- graph()->NewNode(simplified()->ReferenceEqual(),
- input, jsgraph()->TrueConstant()),
- jsgraph()->TrueConstant(),
- graph()->NewNode(simplified()->ReferenceEqual(),
- input, jsgraph()->FalseConstant()));
- } else if (String::Equals(value, factory()->function_string())) {
- replacement = graph()->NewNode(simplified()->ObjectIsCallable(), input);
- } else if (String::Equals(value, factory()->number_string())) {
- replacement = graph()->NewNode(simplified()->ObjectIsNumber(), input);
- } else if (String::Equals(value, factory()->string_string())) {
- replacement = graph()->NewNode(simplified()->ObjectIsString(), input);
- } else if (String::Equals(value, factory()->undefined_string())) {
- replacement = graph()->NewNode(
- common()->Select(MachineRepresentation::kTagged),
- graph()->NewNode(simplified()->ReferenceEqual(), input,
- jsgraph()->NullConstant()),
- jsgraph()->FalseConstant(),
- graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
- } else {
- return NoChange();
- }
- if (invert) {
- replacement = graph()->NewNode(simplified()->BooleanNot(), replacement);
- }
- ReplaceWithValue(node, replacement);
- return Replace(replacement);
+ input = m.left().InputAt(0);
+ type = Handle<String>::cast(m.right().Value());
+ } else if (m.right().IsJSTypeOf() && m.left().HasValue() &&
+ m.left().Value()->IsString()) {
+ input = m.right().InputAt(0);
+ type = Handle<String>::cast(m.left().Value());
+ } else {
+ return NoChange();
}
- return NoChange();
+ Node* value;
+ if (String::Equals(type, factory()->boolean_string())) {
+ value =
+ graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
+ graph()->NewNode(simplified()->ReferenceEqual(), input,
+ jsgraph()->TrueConstant()),
+ jsgraph()->TrueConstant(),
+ graph()->NewNode(simplified()->ReferenceEqual(), input,
+ jsgraph()->FalseConstant()));
+ } else if (String::Equals(type, factory()->function_string())) {
+ value = graph()->NewNode(simplified()->ObjectIsDetectableCallable(), input);
+ } else if (String::Equals(type, factory()->number_string())) {
+ value = graph()->NewNode(simplified()->ObjectIsNumber(), input);
+ } else if (String::Equals(type, factory()->object_string())) {
+ value = graph()->NewNode(
+ common()->Select(MachineRepresentation::kTagged),
+ graph()->NewNode(simplified()->ObjectIsNonCallable(), input),
+ jsgraph()->TrueConstant(),
+ graph()->NewNode(simplified()->ReferenceEqual(), input,
+ jsgraph()->NullConstant()));
+ } else if (String::Equals(type, factory()->string_string())) {
+ value = graph()->NewNode(simplified()->ObjectIsString(), input);
+ } else if (String::Equals(type, factory()->undefined_string())) {
+ value = graph()->NewNode(
+ common()->Select(MachineRepresentation::kTagged),
+ graph()->NewNode(simplified()->ReferenceEqual(), input,
+ jsgraph()->NullConstant()),
+ jsgraph()->FalseConstant(),
+ graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
+ } else {
+ return NoChange();
+ }
+ if (invert) {
+ value = graph()->NewNode(simplified()->BooleanNot(), value);
+ }
+ ReplaceWithValue(node, value);
+ return Replace(value);
}
Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) {
@@ -850,6 +933,13 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) {
JSBinopReduction r(this, node);
+ if (r.BothInputsAre(Type::UniqueName())) {
+ return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
+ }
+ if (r.IsInternalizedStringCompareOperation()) {
+ r.CheckInputsToInternalizedString();
+ return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
+ }
if (r.BothInputsAre(Type::String())) {
return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
}
@@ -884,6 +974,12 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) {
simplified()->SpeculativeNumberEqual(hint), invert, Type::Boolean());
} else if (r.BothInputsAre(Type::Number())) {
return r.ChangeToPureOperator(simplified()->NumberEqual(), invert);
+ } else if (r.IsReceiverCompareOperation()) {
+ r.CheckInputsToReceiver();
+ return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
+ } else if (r.IsStringCompareOperation()) {
+ r.CheckInputsToString();
+ return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
}
return NoChange();
}
@@ -898,10 +994,10 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
return Replace(replacement);
}
}
- if (r.OneInputCannotBe(Type::NumberOrSimdOrString())) {
+ if (r.OneInputCannotBe(Type::NumberOrString())) {
// For values with canonical representation (i.e. neither String, nor
- // Simd128Value nor Number) an empty type intersection means the values
- // cannot be strictly equal.
+ // Number) an empty type intersection means the values cannot be strictly
+ // equal.
if (!r.left_type()->Maybe(r.right_type())) {
Node* replacement = jsgraph()->BooleanConstant(invert);
ReplaceWithValue(node, replacement);
@@ -912,25 +1008,14 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
Reduction const reduction = ReduceJSEqualTypeOf(node, invert);
if (reduction.Changed()) return reduction;
- if (r.OneInputIs(the_hole_type_)) {
- return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
- }
- if (r.OneInputIs(Type::Undefined())) {
- return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
- }
- if (r.OneInputIs(Type::Null())) {
- return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
- }
- if (r.OneInputIs(Type::Boolean())) {
- return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
- }
- if (r.OneInputIs(Type::Object())) {
+ if (r.BothInputsAre(Type::Unique())) {
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
- if (r.OneInputIs(Type::Receiver())) {
+ if (r.OneInputIs(pointer_comparable_type_)) {
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
- if (r.BothInputsAre(Type::Unique())) {
+ if (r.IsInternalizedStringCompareOperation()) {
+ r.CheckInputsToInternalizedString();
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
if (r.BothInputsAre(Type::String())) {
@@ -946,6 +1031,15 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
simplified()->SpeculativeNumberEqual(hint), invert, Type::Boolean());
} else if (r.BothInputsAre(Type::Number())) {
return r.ChangeToPureOperator(simplified()->NumberEqual(), invert);
+ } else if (r.IsReceiverCompareOperation()) {
+ // For strict equality, it's enough to know that one input is a Receiver,
+ // as a strict equality comparison with a Receiver can only yield true if
+ // both sides refer to the same Receiver than.
+ r.CheckLeftInputToReceiver();
+ return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
+ } else if (r.IsStringCompareOperation()) {
+ r.CheckInputsToString();
+ return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
}
return NoChange();
}
@@ -958,7 +1052,6 @@ Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
return Replace(input);
} else if (input_type->Is(Type::OrderedNumber())) {
// JSToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x,#0))
- RelaxEffectsAndControls(node);
node->ReplaceInput(0, graph()->NewNode(simplified()->NumberEqual(), input,
jsgraph()->ZeroConstant()));
node->TrimInputCount(1);
@@ -966,10 +1059,33 @@ Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
return Changed(node);
} else if (input_type->Is(Type::Number())) {
// JSToBoolean(x:number) => NumberToBoolean(x)
- RelaxEffectsAndControls(node);
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->NumberToBoolean());
return Changed(node);
+ } else if (input_type->Is(Type::DetectableReceiverOrNull())) {
+ // JSToBoolean(x:detectable receiver \/ null)
+ // => BooleanNot(ReferenceEqual(x,#null))
+ node->ReplaceInput(0, graph()->NewNode(simplified()->ReferenceEqual(),
+ input, jsgraph()->NullConstant()));
+ node->TrimInputCount(1);
+ NodeProperties::ChangeOp(node, simplified()->BooleanNot());
+ return Changed(node);
+ } else if (input_type->Is(Type::ReceiverOrNullOrUndefined())) {
+ // JSToBoolean(x:receiver \/ null \/ undefined)
+ // => BooleanNot(ObjectIsUndetectable(x))
+ node->ReplaceInput(
+ 0, graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
+ node->TrimInputCount(1);
+ NodeProperties::ChangeOp(node, simplified()->BooleanNot());
+ return Changed(node);
+ } else if (input_type->Is(Type::String())) {
+ // JSToBoolean(x:string) => BooleanNot(ReferenceEqual(x,""))
+ node->ReplaceInput(0,
+ graph()->NewNode(simplified()->ReferenceEqual(), input,
+ jsgraph()->EmptyStringConstant()));
+ node->TrimInputCount(1);
+ NodeProperties::ChangeOp(node, simplified()->BooleanNot());
+ return Changed(node);
}
return NoChange();
}
@@ -1239,6 +1355,9 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
Node* value = NodeProperties::GetValueInput(node, 2);
Type* key_type = NodeProperties::GetType(key);
Type* value_type = NodeProperties::GetType(value);
+
+ if (!value_type->Is(Type::PlainPrimitive())) return NoChange();
+
HeapObjectMatcher mbase(base);
if (mbase.HasValue() && mbase.Value()->IsJSTypedArray()) {
Handle<JSTypedArray> const array =
@@ -1257,7 +1376,6 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
Handle<FixedTypedArrayBase>::cast(handle(array->elements()));
Node* buffer = jsgraph()->PointerConstant(elements->external_pointer());
Node* length = jsgraph()->Constant(byte_length);
- Node* context = NodeProperties::GetContextInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Convert to a number first.
@@ -1266,12 +1384,8 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
if (number_reduction.Changed()) {
value = number_reduction.replacement();
} else {
- Node* frame_state_for_to_number =
- NodeProperties::FindFrameStateBefore(node);
- value = effect =
- graph()->NewNode(javascript()->ToNumber(), value, context,
- frame_state_for_to_number, effect, control);
- control = graph()->NewNode(common()->IfSuccess(), value);
+ value =
+ graph()->NewNode(simplified()->PlainPrimitiveToNumber(), value);
}
}
// Check if we can avoid the bounds check.
@@ -1316,11 +1430,30 @@ Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node) {
Node* constructor = NodeProperties::GetValueInput(node, 0);
Type* constructor_type = NodeProperties::GetType(constructor);
Node* object = NodeProperties::GetValueInput(node, 1);
+ Type* object_type = NodeProperties::GetType(object);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
+ // Check if the {constructor} cannot be callable.
+ // See ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) step 1.
+ if (!constructor_type->Maybe(Type::Callable())) {
+ Node* value = jsgraph()->FalseConstant();
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+ }
+
+ // If the {constructor} cannot be a JSBoundFunction and then {object}
+ // cannot be a JSReceiver, then this can be constant-folded to false.
+ // See ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) step 2 and 3.
+ if (!object_type->Maybe(Type::Receiver()) &&
+ !constructor_type->Maybe(Type::BoundFunction())) {
+ Node* value = jsgraph()->FalseConstant();
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+ }
+
// Check if the {constructor} is a (known) JSFunction.
if (!constructor_type->IsHeapConstant() ||
!constructor_type->AsHeapConstant()->Value()->IsJSFunction()) {
@@ -1473,16 +1606,17 @@ Reduction JSTypedLowering::ReduceJSLoadContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
ContextAccess const& access = ContextAccessOf(node->op());
Node* effect = NodeProperties::GetEffectInput(node);
+ Node* context = NodeProperties::GetContextInput(node);
Node* control = graph()->start();
for (size_t i = 0; i < access.depth(); ++i) {
- Node* previous = effect = graph()->NewNode(
+ context = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)),
- NodeProperties::GetValueInput(node, 0), effect, control);
- node->ReplaceInput(0, previous);
+ context, effect, control);
}
+ node->ReplaceInput(0, context);
node->ReplaceInput(1, effect);
- node->ReplaceInput(2, control);
+ node->AppendInput(jsgraph()->zone(), control);
NodeProperties::ChangeOp(
node,
simplified()->LoadField(AccessBuilder::ForContextSlot(access.index())));
@@ -1493,15 +1627,17 @@ Reduction JSTypedLowering::ReduceJSStoreContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
ContextAccess const& access = ContextAccessOf(node->op());
Node* effect = NodeProperties::GetEffectInput(node);
+ Node* context = NodeProperties::GetContextInput(node);
Node* control = graph()->start();
+ Node* value = NodeProperties::GetValueInput(node, 0);
for (size_t i = 0; i < access.depth(); ++i) {
- Node* previous = effect = graph()->NewNode(
+ context = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)),
- NodeProperties::GetValueInput(node, 0), effect, control);
- node->ReplaceInput(0, previous);
+ context, effect, control);
}
- node->RemoveInput(2);
+ node->ReplaceInput(0, context);
+ node->ReplaceInput(1, value);
node->ReplaceInput(2, effect);
NodeProperties::ChangeOp(
node,
@@ -1591,7 +1727,6 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
Type* receiver_type = NodeProperties::GetType(receiver);
Node* context = NodeProperties::GetContextInput(node);
Type* context_type = NodeProperties::GetType(context);
- Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
@@ -1614,10 +1749,10 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
} else {
Node* native_context = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
- context, context, effect);
+ context, effect);
receiver = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::GLOBAL_PROXY_INDEX, true),
- native_context, native_context, effect);
+ native_context, effect);
}
ReplaceWithValue(node, receiver, effect, control);
return Replace(receiver);
@@ -1638,14 +1773,15 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
Node* efalse = effect;
Node* rfalse;
{
- // Convert {receiver} using the ToObjectStub.
+ // Convert {receiver} using the ToObjectStub. The call does not require a
+ // frame-state in this case, because neither null nor undefined is passed.
Callable callable = CodeFactory::ToObject(isolate());
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
- CallDescriptor::kNeedsFrameState, node->op()->properties());
+ CallDescriptor::kNoFlags, node->op()->properties());
rfalse = efalse = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
- receiver, context, frame_state, efalse);
+ receiver, context, efalse);
}
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
@@ -1695,14 +1831,15 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
Node* econvert = effect;
Node* rconvert;
{
- // Convert {receiver} using the ToObjectStub.
+ // Convert {receiver} using the ToObjectStub. The call does not require a
+ // frame-state in this case, because neither null nor undefined is passed.
Callable callable = CodeFactory::ToObject(isolate());
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
- CallDescriptor::kNeedsFrameState, node->op()->properties());
+ CallDescriptor::kNoFlags, node->op()->properties());
rconvert = econvert = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
- receiver, context, frame_state, econvert);
+ receiver, context, econvert);
}
// Replace {receiver} with global proxy of {context}.
@@ -1719,10 +1856,10 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
} else {
Node* native_context = eglobal = graph()->NewNode(
javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
- context, context, eglobal);
+ context, eglobal);
rglobal = eglobal = graph()->NewNode(
javascript()->LoadContext(0, Context::GLOBAL_PROXY_INDEX, true),
- native_context, native_context, eglobal);
+ native_context, eglobal);
}
}
@@ -1764,7 +1901,7 @@ void ReduceBuiltin(Isolate* isolate, JSGraph* jsgraph, Node* node,
// The logic contained here is mirrored in Builtins::Generate_Adaptor.
// Keep these in sync.
- const bool is_construct = (node->opcode() == IrOpcode::kJSCallConstruct);
+ const bool is_construct = (node->opcode() == IrOpcode::kJSConstruct);
DCHECK(Builtins::HasCppImplementation(builtin_index));
DCHECK_EQ(0, flags & CallDescriptor::kSupportsTailCalls);
@@ -1824,9 +1961,9 @@ bool NeedsArgumentAdaptorFrame(Handle<SharedFunctionInfo> shared, int arity) {
} // namespace
-Reduction JSTypedLowering::ReduceJSCallConstruct(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
- CallConstructParameters const& p = CallConstructParametersOf(node->op());
+Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
+ ConstructParameters const& p = ConstructParametersOf(node->op());
DCHECK_LE(2u, p.arity());
int const arity = static_cast<int>(p.arity() - 2);
Node* target = NodeProperties::GetValueInput(node, 0);
@@ -1899,10 +2036,38 @@ Reduction JSTypedLowering::ReduceJSCallConstruct(Node* node) {
return NoChange();
}
+Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCallForwardVarargs, node->opcode());
+ CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op());
+ Node* target = NodeProperties::GetValueInput(node, 0);
+ Type* target_type = NodeProperties::GetType(target);
+
+ // Check if {target} is a JSFunction.
+ if (target_type->Is(Type::Function())) {
+ // Compute flags for the call.
+ CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
+ if (p.tail_call_mode() == TailCallMode::kAllow) {
+ flags |= CallDescriptor::kSupportsTailCalls;
+ }
+
+ // Patch {node} to an indirect call via CallFunctionForwardVarargs.
+ Callable callable = CodeFactory::CallFunctionForwardVarargs(isolate());
+ node->InsertInput(graph()->zone(), 0,
+ jsgraph()->HeapConstant(callable.code()));
+ node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(p.start_index()));
+ NodeProperties::ChangeOp(
+ node,
+ common()->Call(Linkage::GetStubCallDescriptor(
+ isolate(), graph()->zone(), callable.descriptor(), 1, flags)));
+ return Changed(node);
+ }
+
+ return NoChange();
+}
-Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
- CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
+Reduction JSTypedLowering::ReduceJSCall(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
int const arity = static_cast<int>(p.arity() - 2);
ConvertReceiverMode convert_mode = p.convert_mode();
Node* target = NodeProperties::GetValueInput(node, 0);
@@ -1911,7 +2076,6 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) {
Type* receiver_type = NodeProperties::GetType(receiver);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
- Node* frame_state = NodeProperties::FindFrameStateBefore(node);
// Try to infer receiver {convert_mode} from {receiver} type.
if (receiver_type->Is(Type::NullOrUndefined())) {
@@ -1944,7 +2108,7 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) {
!receiver_type->Is(Type::Receiver())) {
receiver = effect =
graph()->NewNode(javascript()->ConvertReceiver(convert_mode),
- receiver, context, frame_state, effect, control);
+ receiver, context, effect, control);
NodeProperties::ReplaceValueInput(node, receiver, 1);
}
@@ -2011,8 +2175,9 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) {
// Maybe we did at least learn something about the {receiver}.
if (p.convert_mode() != convert_mode) {
NodeProperties::ChangeOp(
- node, javascript()->CallFunction(p.arity(), p.frequency(), p.feedback(),
- convert_mode, p.tail_call_mode()));
+ node,
+ javascript()->Call(p.arity(), p.frequency(), p.feedback(), convert_mode,
+ p.tail_call_mode()));
return Changed(node);
}
@@ -2031,6 +2196,18 @@ Reduction JSTypedLowering::ReduceJSForInNext(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
+ // We don't support lowering JSForInNext inside try blocks.
+ if (NodeProperties::IsExceptionalCall(node)) return NoChange();
+
+ // We know that the {index} is in Unsigned32 range here, otherwise executing
+ // the JSForInNext wouldn't be valid. Unfortunately due to OSR and generators
+ // this is not always reflected in the types, hence we might need to rename
+ // the {index} here.
+ if (!NodeProperties::GetType(index)->Is(Type::Unsigned32())) {
+ index = graph()->NewNode(common()->TypeGuard(Type::Unsigned32()), index,
+ control);
+ }
+
// Load the next {key} from the {cache_array}.
Node* key = effect = graph()->NewNode(
simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
@@ -2085,6 +2262,28 @@ Reduction JSTypedLowering::ReduceJSForInNext(Node* node) {
return Changed(node);
}
+Reduction JSTypedLowering::ReduceJSLoadMessage(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSLoadMessage, node->opcode());
+ ExternalReference const ref =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ node->ReplaceInput(0, jsgraph()->ExternalConstant(ref));
+ NodeProperties::ChangeOp(
+ node, simplified()->LoadField(AccessBuilder::ForExternalTaggedValue()));
+ return Changed(node);
+}
+
+Reduction JSTypedLowering::ReduceJSStoreMessage(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSStoreMessage, node->opcode());
+ ExternalReference const ref =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ Node* value = NodeProperties::GetValueInput(node, 0);
+ node->ReplaceInput(0, jsgraph()->ExternalConstant(ref));
+ node->ReplaceInput(1, value);
+ NodeProperties::ChangeOp(
+ node, simplified()->StoreField(AccessBuilder::ForExternalTaggedValue()));
+ return Changed(node);
+}
+
Reduction JSTypedLowering::ReduceJSGeneratorStore(Node* node) {
DCHECK_EQ(IrOpcode::kJSGeneratorStore, node->opcode());
Node* generator = NodeProperties::GetValueInput(node, 0);
@@ -2095,7 +2294,7 @@ Reduction JSTypedLowering::ReduceJSGeneratorStore(Node* node) {
Node* control = NodeProperties::GetControlInput(node);
int register_count = OpParameter<int>(node);
- FieldAccess array_field = AccessBuilder::ForJSGeneratorObjectOperandStack();
+ FieldAccess array_field = AccessBuilder::ForJSGeneratorObjectRegisterFile();
FieldAccess context_field = AccessBuilder::ForJSGeneratorObjectContext();
FieldAccess continuation_field =
AccessBuilder::ForJSGeneratorObjectContinuation();
@@ -2149,7 +2348,7 @@ Reduction JSTypedLowering::ReduceJSGeneratorRestoreRegister(Node* node) {
Node* control = NodeProperties::GetControlInput(node);
int index = OpParameter<int>(node);
- FieldAccess array_field = AccessBuilder::ForJSGeneratorObjectOperandStack();
+ FieldAccess array_field = AccessBuilder::ForJSGeneratorObjectRegisterFile();
FieldAccess element_field = AccessBuilder::ForFixedArraySlot(index);
Node* array = effect = graph()->NewNode(simplified()->LoadField(array_field),
@@ -2229,18 +2428,33 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSStoreModule(node);
case IrOpcode::kJSConvertReceiver:
return ReduceJSConvertReceiver(node);
- case IrOpcode::kJSCallConstruct:
- return ReduceJSCallConstruct(node);
- case IrOpcode::kJSCallFunction:
- return ReduceJSCallFunction(node);
+ case IrOpcode::kJSConstruct:
+ return ReduceJSConstruct(node);
+ case IrOpcode::kJSCallForwardVarargs:
+ return ReduceJSCallForwardVarargs(node);
+ case IrOpcode::kJSCall:
+ return ReduceJSCall(node);
case IrOpcode::kJSForInNext:
return ReduceJSForInNext(node);
+ case IrOpcode::kJSLoadMessage:
+ return ReduceJSLoadMessage(node);
+ case IrOpcode::kJSStoreMessage:
+ return ReduceJSStoreMessage(node);
case IrOpcode::kJSGeneratorStore:
return ReduceJSGeneratorStore(node);
case IrOpcode::kJSGeneratorRestoreContinuation:
return ReduceJSGeneratorRestoreContinuation(node);
case IrOpcode::kJSGeneratorRestoreRegister:
return ReduceJSGeneratorRestoreRegister(node);
+ // TODO(mstarzinger): Simplified operations hiding in JS-level reducer not
+ // fooling anyone. Consider moving this into a separate reducer.
+ case IrOpcode::kSpeculativeNumberAdd:
+ return ReduceSpeculativeNumberAdd(node);
+ case IrOpcode::kSpeculativeNumberSubtract:
+ case IrOpcode::kSpeculativeNumberMultiply:
+ case IrOpcode::kSpeculativeNumberDivide:
+ case IrOpcode::kSpeculativeNumberModulus:
+ return ReduceSpeculativeNumberBinop(node);
default:
break;
}