Commit a49c4b0a authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Type feedback for numeric comparisons.

Review-Url: https://codereview.chromium.org/2035383003
Cr-Commit-Position: refs/heads/master@{#37024}
parent b95de044
......@@ -281,7 +281,9 @@ class AstGraphBuilder::ControlScope::DeferredCommands : public ZoneObject {
return NewPathToken(TokenDispenserForFinally::kFallThroughToken);
}
Node* NewPathDispatchCondition(Node* t1, Node* t2) {
return owner_->NewNode(owner_->javascript()->StrictEqual(), t1, t2);
return owner_->NewNode(
owner_->javascript()->StrictEqual(CompareOperationHints::Any()), t1,
t2);
}
private:
......@@ -1303,7 +1305,15 @@ void AstGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
VisitForValue(clause->label());
Node* label = environment()->Pop();
Node* tag = environment()->Top();
const Operator* op = javascript()->StrictEqual();
CompareOperationHints hints;
if (!type_hint_analysis_ ||
!type_hint_analysis_->GetCompareOperationHints(clause->CompareId(),
&hints)) {
hints = CompareOperationHints::Any();
}
const Operator* op = javascript()->StrictEqual(hints);
Node* condition = NewNode(op, tag, label);
compare_switch.BeginLabel(i, condition);
......@@ -1379,10 +1389,12 @@ void AstGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
for_block.BeginBlock();
// Check for null or undefined before entering loop.
Node* is_null_cond =
NewNode(javascript()->StrictEqual(), object, jsgraph()->NullConstant());
NewNode(javascript()->StrictEqual(CompareOperationHints::Any()), object,
jsgraph()->NullConstant());
for_block.BreakWhen(is_null_cond, BranchHint::kFalse);
Node* is_undefined_cond = NewNode(javascript()->StrictEqual(), object,
jsgraph()->UndefinedConstant());
Node* is_undefined_cond =
NewNode(javascript()->StrictEqual(CompareOperationHints::Any()), object,
jsgraph()->UndefinedConstant());
for_block.BreakWhen(is_undefined_cond, BranchHint::kFalse);
{
// Convert object to jsobject.
......@@ -1425,8 +1437,9 @@ void AstGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
PrepareFrameState(value, stmt->FilterId(),
OutputFrameStateCombine::Push());
IfBuilder test_value(this);
Node* test_value_cond = NewNode(javascript()->StrictEqual(), value,
jsgraph()->UndefinedConstant());
Node* test_value_cond =
NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
value, jsgraph()->UndefinedConstant());
test_value.If(test_value_cond, BranchHint::kFalse);
test_value.Then();
test_value.Else();
......@@ -2831,10 +2844,10 @@ void AstGraphBuilder::VisitLiteralCompareNil(CompareOperation* expr,
const Operator* op = nullptr;
switch (expr->op()) {
case Token::EQ:
op = javascript()->Equal();
op = javascript()->Equal(CompareOperationHints::Any());
break;
case Token::EQ_STRICT:
op = javascript()->StrictEqual();
op = javascript()->StrictEqual(CompareOperationHints::Any());
break;
default:
UNREACHABLE();
......@@ -2853,8 +2866,8 @@ void AstGraphBuilder::VisitLiteralCompareTypeof(CompareOperation* expr,
VisitTypeofExpression(sub_expr);
PrepareEagerCheckpoint(sub_expr->id());
Node* typeof_arg = NewNode(javascript()->TypeOf(), environment()->Pop());
Node* value = NewNode(javascript()->StrictEqual(), typeof_arg,
jsgraph()->Constant(check));
Node* value = NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
typeof_arg, jsgraph()->Constant(check));
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
return ast_context()->ProduceValue(value);
}
......@@ -2876,31 +2889,38 @@ void AstGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
return VisitLiteralCompareNil(expr, sub_expr, jsgraph()->NullConstant());
}
CompareOperationHints hints;
if (!type_hint_analysis_ ||
!type_hint_analysis_->GetCompareOperationHints(
expr->CompareOperationFeedbackId(), &hints)) {
hints = CompareOperationHints::Any();
}
const Operator* op;
switch (expr->op()) {
case Token::EQ:
op = javascript()->Equal();
op = javascript()->Equal(hints);
break;
case Token::NE:
op = javascript()->NotEqual();
op = javascript()->NotEqual(hints);
break;
case Token::EQ_STRICT:
op = javascript()->StrictEqual();
op = javascript()->StrictEqual(hints);
break;
case Token::NE_STRICT:
op = javascript()->StrictNotEqual();
op = javascript()->StrictNotEqual(hints);
break;
case Token::LT:
op = javascript()->LessThan();
op = javascript()->LessThan(hints);
break;
case Token::GT:
op = javascript()->GreaterThan();
op = javascript()->GreaterThan(hints);
break;
case Token::LTE:
op = javascript()->LessThanOrEqual();
op = javascript()->LessThanOrEqual(hints);
break;
case Token::GTE:
op = javascript()->GreaterThanOrEqual();
op = javascript()->GreaterThanOrEqual(hints);
break;
case Token::INSTANCEOF:
op = javascript()->InstanceOf();
......@@ -3319,7 +3339,8 @@ Node* AstGraphBuilder::BuildHoleCheckThenThrow(Node* value, Variable* variable,
BailoutId bailout_id) {
IfBuilder hole_check(this);
Node* the_hole = jsgraph()->TheHoleConstant();
Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
Node* check = NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
value, the_hole);
hole_check.If(check);
hole_check.Then();
Node* error = BuildThrowReferenceError(variable, bailout_id);
......@@ -3336,7 +3357,8 @@ Node* AstGraphBuilder::BuildHoleCheckElseThrow(Node* value, Variable* variable,
BailoutId bailout_id) {
IfBuilder hole_check(this);
Node* the_hole = jsgraph()->TheHoleConstant();
Node* check = NewNode(javascript()->StrictEqual(), value, the_hole);
Node* check = NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
value, the_hole);
hole_check.If(check);
hole_check.Then();
environment()->Push(for_hole);
......@@ -3353,7 +3375,8 @@ Node* AstGraphBuilder::BuildThrowIfStaticPrototype(Node* name,
IfBuilder prototype_check(this);
Node* prototype_string =
jsgraph()->Constant(isolate()->factory()->prototype_string());
Node* check = NewNode(javascript()->StrictEqual(), name, prototype_string);
Node* check = NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
name, prototype_string);
prototype_check.If(check);
prototype_check.Then();
Node* error = BuildThrowStaticPrototypeError(bailout_id);
......@@ -3903,8 +3926,9 @@ Node* AstGraphBuilder::TryLoadDynamicVariable(Variable* variable,
Node* load = NewNode(
javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
current_context());
Node* check = NewNode(javascript()->StrictEqual(), load,
jsgraph()->TheHoleConstant());
Node* check =
NewNode(javascript()->StrictEqual(CompareOperationHints::Any()), load,
jsgraph()->TheHoleConstant());
fast_block.BreakUnless(check, BranchHint::kTrue);
}
......@@ -3949,8 +3973,9 @@ Node* AstGraphBuilder::TryLoadDynamicVariable(Variable* variable,
Node* load = NewNode(
javascript()->LoadContext(depth, Context::EXTENSION_INDEX, false),
current_context());
Node* check = NewNode(javascript()->StrictEqual(), load,
jsgraph()->TheHoleConstant());
Node* check =
NewNode(javascript()->StrictEqual(CompareOperationHints::Any()), load,
jsgraph()->TheHoleConstant());
fast_block.BreakUnless(check, BranchHint::kTrue);
}
......
......@@ -1224,31 +1224,38 @@ void BytecodeGraphBuilder::BuildCompareOp(const Operator* js_op) {
}
void BytecodeGraphBuilder::VisitTestEqual() {
BuildCompareOp(javascript()->Equal());
CompareOperationHints hints = CompareOperationHints::Any();
BuildCompareOp(javascript()->Equal(hints));
}
void BytecodeGraphBuilder::VisitTestNotEqual() {
BuildCompareOp(javascript()->NotEqual());
CompareOperationHints hints = CompareOperationHints::Any();
BuildCompareOp(javascript()->NotEqual(hints));
}
void BytecodeGraphBuilder::VisitTestEqualStrict() {
BuildCompareOp(javascript()->StrictEqual());
CompareOperationHints hints = CompareOperationHints::Any();
BuildCompareOp(javascript()->StrictEqual(hints));
}
void BytecodeGraphBuilder::VisitTestLessThan() {
BuildCompareOp(javascript()->LessThan());
CompareOperationHints hints = CompareOperationHints::Any();
BuildCompareOp(javascript()->LessThan(hints));
}
void BytecodeGraphBuilder::VisitTestGreaterThan() {
BuildCompareOp(javascript()->GreaterThan());
CompareOperationHints hints = CompareOperationHints::Any();
BuildCompareOp(javascript()->GreaterThan(hints));
}
void BytecodeGraphBuilder::VisitTestLessThanOrEqual() {
BuildCompareOp(javascript()->LessThanOrEqual());
CompareOperationHints hints = CompareOperationHints::Any();
BuildCompareOp(javascript()->LessThanOrEqual(hints));
}
void BytecodeGraphBuilder::VisitTestGreaterThanOrEqual() {
BuildCompareOp(javascript()->GreaterThanOrEqual());
CompareOperationHints hints = CompareOperationHints::Any();
BuildCompareOp(javascript()->GreaterThanOrEqual(hints));
}
void BytecodeGraphBuilder::VisitTestIn() {
......@@ -1518,7 +1525,8 @@ void BytecodeGraphBuilder::BuildConditionalJump(Node* condition) {
void BytecodeGraphBuilder::BuildJumpIfEqual(Node* comperand) {
Node* accumulator = environment()->LookupAccumulator();
Node* condition =
NewNode(javascript()->StrictEqual(), accumulator, comperand);
NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
accumulator, comperand);
BuildConditionalJump(condition);
}
......@@ -1527,14 +1535,17 @@ void BytecodeGraphBuilder::BuildJumpIfToBooleanEqual(Node* comperand) {
Node* accumulator = environment()->LookupAccumulator();
Node* to_boolean =
NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), accumulator);
Node* condition = NewNode(javascript()->StrictEqual(), to_boolean, comperand);
Node* condition =
NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
to_boolean, comperand);
BuildConditionalJump(condition);
}
void BytecodeGraphBuilder::BuildJumpIfNotHole() {
Node* accumulator = environment()->LookupAccumulator();
Node* condition = NewNode(javascript()->StrictEqual(), accumulator,
jsgraph()->TheHoleConstant());
Node* condition =
NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
accumulator, jsgraph()->TheHoleConstant());
Node* node =
NewNode(common()->Select(MachineRepresentation::kTagged), condition,
jsgraph()->FalseConstant(), jsgraph()->TrueConstant());
......
......@@ -323,8 +323,9 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
}
// Check that the {target} is still the {array_function}.
Node* check = graph()->NewNode(javascript()->StrictEqual(), target,
array_function, context);
Node* check = graph()->NewNode(
javascript()->StrictEqual(CompareOperationHints::Any()), target,
array_function, context);
control = effect = graph()->NewNode(common()->DeoptimizeUnless(), check,
frame_state, effect, control);
......@@ -340,8 +341,9 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
jsgraph()->Constant(handle(cell->value(), isolate()));
// Check that the {target} is still the {target_function}.
Node* check = graph()->NewNode(javascript()->StrictEqual(), target,
target_function, context);
Node* check = graph()->NewNode(
javascript()->StrictEqual(CompareOperationHints::Any()), target,
target_function, context);
control = effect = graph()->NewNode(common()->DeoptimizeUnless(), check,
frame_state, effect, control);
......@@ -443,8 +445,9 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
}
// Check that the {target} is still the {array_function}.
Node* check = graph()->NewNode(javascript()->StrictEqual(), target,
array_function, context);
Node* check = graph()->NewNode(
javascript()->StrictEqual(CompareOperationHints::Any()), target,
array_function, context);
control = effect = graph()->NewNode(common()->DeoptimizeUnless(), check,
frame_state, effect, control);
......@@ -465,8 +468,9 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
jsgraph()->Constant(handle(cell->value(), isolate()));
// Check that the {target} is still the {target_function}.
Node* check = graph()->NewNode(javascript()->StrictEqual(), target,
target_function, context);
Node* check = graph()->NewNode(
javascript()->StrictEqual(CompareOperationHints::Any()), target,
target_function, context);
control = effect = graph()->NewNode(common()->DeoptimizeUnless(), check,
frame_state, effect, control);
......
......@@ -377,20 +377,33 @@ const CreateLiteralParameters& CreateLiteralParametersOf(const Operator* op) {
}
const BinaryOperationHints& BinaryOperationHintsOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kJSAdd ||
op->opcode() == IrOpcode::kJSSubtract);
DCHECK(op->opcode() == IrOpcode::kJSBitwiseOr ||
op->opcode() == IrOpcode::kJSBitwiseXor ||
op->opcode() == IrOpcode::kJSBitwiseAnd ||
op->opcode() == IrOpcode::kJSShiftLeft ||
op->opcode() == IrOpcode::kJSShiftRight ||
op->opcode() == IrOpcode::kJSShiftRightLogical ||
op->opcode() == IrOpcode::kJSAdd ||
op->opcode() == IrOpcode::kJSSubtract ||
op->opcode() == IrOpcode::kJSMultiply ||
op->opcode() == IrOpcode::kJSDivide ||
op->opcode() == IrOpcode::kJSModulus);
return OpParameter<BinaryOperationHints>(op);
}
const CompareOperationHints& CompareOperationHintsOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kJSEqual ||
op->opcode() == IrOpcode::kJSNotEqual ||
op->opcode() == IrOpcode::kJSStrictEqual ||
op->opcode() == IrOpcode::kJSStrictNotEqual ||
op->opcode() == IrOpcode::kJSLessThan ||
op->opcode() == IrOpcode::kJSGreaterThan ||
op->opcode() == IrOpcode::kJSLessThanOrEqual ||
op->opcode() == IrOpcode::kJSGreaterThanOrEqual);
return OpParameter<CompareOperationHints>(op);
}
#define CACHED_OP_LIST(V) \
V(Equal, Operator::kNoProperties, 2, 1) \
V(NotEqual, Operator::kNoProperties, 2, 1) \
V(StrictEqual, Operator::kPure, 2, 1) \
V(StrictNotEqual, Operator::kPure, 2, 1) \
V(LessThan, Operator::kNoProperties, 2, 1) \
V(GreaterThan, Operator::kNoProperties, 2, 1) \
V(LessThanOrEqual, Operator::kNoProperties, 2, 1) \
V(GreaterThanOrEqual, Operator::kNoProperties, 2, 1) \
V(ToInteger, Operator::kNoProperties, 1, 1) \
V(ToLength, Operator::kNoProperties, 1, 1) \
V(ToName, Operator::kNoProperties, 1, 1) \
......@@ -544,6 +557,79 @@ const Operator* JSOperatorBuilder::Modulus(BinaryOperationHints hints) {
hints); // parameter
}
const Operator* JSOperatorBuilder::Equal(CompareOperationHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<CompareOperationHints>( //--
IrOpcode::kJSEqual, Operator::kNoProperties, // opcode
"JSEqual", // name
2, 1, 1, 1, 1, 2, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::NotEqual(CompareOperationHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<CompareOperationHints>( //--
IrOpcode::kJSNotEqual, Operator::kNoProperties, // opcode
"JSNotEqual", // name
2, 1, 1, 1, 1, 2, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::StrictEqual(CompareOperationHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<CompareOperationHints>( //--
IrOpcode::kJSStrictEqual, Operator::kPure, // opcode
"JSStrictEqual", // name
2, 0, 0, 1, 0, 0, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::StrictNotEqual(CompareOperationHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<CompareOperationHints>( //--
IrOpcode::kJSStrictNotEqual, Operator::kPure, // opcode
"JSStrictNotEqual", // name
2, 0, 0, 1, 0, 0, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::LessThan(CompareOperationHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<CompareOperationHints>( //--
IrOpcode::kJSLessThan, Operator::kNoProperties, // opcode
"JSLessThan", // name
2, 1, 1, 1, 1, 2, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::GreaterThan(CompareOperationHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<CompareOperationHints>( //--
IrOpcode::kJSGreaterThan, Operator::kNoProperties, // opcode
"JSGreaterThan", // name
2, 1, 1, 1, 1, 2, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::LessThanOrEqual(
CompareOperationHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<CompareOperationHints>( //--
IrOpcode::kJSLessThanOrEqual, Operator::kNoProperties, // opcode
"JSLessThanOrEqual", // name
2, 1, 1, 1, 1, 2, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::GreaterThanOrEqual(
CompareOperationHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<CompareOperationHints>( //--
IrOpcode::kJSGreaterThanOrEqual, Operator::kNoProperties, // opcode
"JSGreaterThanOrEqual", // name
2, 1, 1, 1, 1, 2, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::ToBoolean(ToBooleanHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
......
......@@ -376,6 +376,8 @@ const CreateLiteralParameters& CreateLiteralParametersOf(const Operator* op);
const BinaryOperationHints& BinaryOperationHintsOf(const Operator* op);
const CompareOperationHints& CompareOperationHintsOf(const Operator* op);
// Interface for building JavaScript-level operators, e.g. directly from the
// AST. Most operators have no parameters, thus can be globally shared for all
// graphs.
......@@ -383,14 +385,14 @@ class JSOperatorBuilder final : public ZoneObject {
public:
explicit JSOperatorBuilder(Zone* zone);
const Operator* Equal();
const Operator* NotEqual();
const Operator* StrictEqual();
const Operator* StrictNotEqual();
const Operator* LessThan();
const Operator* GreaterThan();
const Operator* LessThanOrEqual();
const Operator* GreaterThanOrEqual();
const Operator* Equal(CompareOperationHints hints);
const Operator* NotEqual(CompareOperationHints hints);
const Operator* StrictEqual(CompareOperationHints hints);
const Operator* StrictNotEqual(CompareOperationHints hints);
const Operator* LessThan(CompareOperationHints hints);
const Operator* GreaterThan(CompareOperationHints hints);
const Operator* LessThanOrEqual(CompareOperationHints hints);
const Operator* GreaterThanOrEqual(CompareOperationHints hints);
const Operator* BitwiseOr(BinaryOperationHints hints);
const Operator* BitwiseXor(BinaryOperationHints hints);
const Operator* BitwiseAnd(BinaryOperationHints hints);
......
......@@ -27,7 +27,7 @@ class JSBinopReduction final {
JSBinopReduction(JSTypedLowering* lowering, Node* node)
: lowering_(lowering), node_(node) {}
BinaryOperationHints::Hint GetUsableNumberFeedback() {
BinaryOperationHints::Hint GetNumberBinaryOperationFeedback() {
if (!(lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) ||
!(lowering_->flags() & JSTypedLowering::kTypeFeedbackEnabled)) {
return BinaryOperationHints::kAny;
......@@ -45,6 +45,23 @@ class JSBinopReduction final {
return BinaryOperationHints::kAny;
}
CompareOperationHints::Hint GetNumberCompareOperationFeedback() {
if (!(lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) ||
!(lowering_->flags() & JSTypedLowering::kTypeFeedbackEnabled)) {
return CompareOperationHints::kAny;
}
DCHECK_NE(0, node_->op()->ControlOutputCount());
DCHECK_EQ(1, node_->op()->EffectOutputCount());
DCHECK_EQ(2, OperatorProperties::GetFrameStateInputCount(node_->op()));
CompareOperationHints hints = CompareOperationHintsOf(node_->op());
CompareOperationHints::Hint combined = hints.combined();
if (combined == CompareOperationHints::kSignedSmall ||
combined == CompareOperationHints::kNumber) {
return combined;
}
return CompareOperationHints::kAny;
}
void ConvertInputsToNumber(Node* frame_state) {
// To convert the inputs to numbers, we have to provide frame states
// for lazy bailouts in the ToNumber conversions.
......@@ -125,7 +142,7 @@ class JSBinopReduction final {
return lowering_->Changed(node_);
}
Reduction ChangeToSpeculativeOperator(const Operator* op) {
Reduction ChangeToSpeculativeOperator(const Operator* op, Type* upper_bound) {
DCHECK_EQ(1, op->EffectInputCount());
DCHECK_EQ(1, op->EffectOutputCount());
DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
......@@ -167,7 +184,7 @@ class JSBinopReduction final {
// Update the type to number.
Type* node_type = NodeProperties::GetType(node_);
NodeProperties::SetType(node_,
Type::Intersect(node_type, Type::Number(), zone()));
Type::Intersect(node_type, upper_bound, zone()));
return lowering_->Changed(node_);
}
......@@ -403,7 +420,7 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
JSBinopReduction r(this, node);
BinaryOperationHints::Hint feedback = r.GetUsableNumberFeedback();
BinaryOperationHints::Hint feedback = r.GetNumberBinaryOperationFeedback();
if (feedback == BinaryOperationHints::kNumberOrUndefined &&
r.BothInputsAre(Type::PlainPrimitive()) &&
r.NeitherInputCanBe(Type::StringOrReceiver())) {
......@@ -415,7 +432,7 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
if (feedback != BinaryOperationHints::kAny) {
// Lower to the optimistic number binop.
return r.ChangeToSpeculativeOperator(
simplified()->SpeculativeNumberAdd(feedback));
simplified()->SpeculativeNumberAdd(feedback), Type::Number());
}
if (r.BothInputsAre(Type::Number())) {
// JSAdd(x:number, y:number) => NumberAdd(x, y)
......@@ -469,7 +486,7 @@ Reduction JSTypedLowering::ReduceJSModulus(Node* node) {
Reduction JSTypedLowering::ReduceJSSubtract(Node* node) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
JSBinopReduction r(this, node);
BinaryOperationHints::Hint feedback = r.GetUsableNumberFeedback();
BinaryOperationHints::Hint feedback = r.GetNumberBinaryOperationFeedback();
if (feedback == BinaryOperationHints::kNumberOrUndefined &&
r.BothInputsAre(Type::PlainPrimitive())) {
// JSSubtract(x:plain-primitive, y:plain-primitive)
......@@ -482,7 +499,7 @@ Reduction JSTypedLowering::ReduceJSSubtract(Node* node) {
if (feedback != BinaryOperationHints::kAny) {
// Lower to the optimistic number binop.
return r.ChangeToSpeculativeOperator(
simplified()->SpeculativeNumberSubtract(feedback));
simplified()->SpeculativeNumberSubtract(feedback), Type::Number());
}
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumber(frame_state);
......@@ -558,7 +575,10 @@ Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
r.ChangeToPureOperator(stringOp);
return Changed(node);
}
if (r.OneInputCannotBe(Type::StringOrReceiver())) {
CompareOperationHints::Hint hint = r.GetNumberCompareOperationFeedback();
if (hint != CompareOperationHints::kAny ||
r.OneInputCannotBe(Type::StringOrReceiver())) {
const Operator* less_than;
const Operator* less_than_or_equal;
if (r.BothInputsAre(Type::Unsigned32())) {
......@@ -567,6 +587,9 @@ Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
} else if (r.BothInputsAre(Type::Signed32())) {
less_than = machine()->Int32LessThan();
less_than_or_equal = machine()->Int32LessThanOrEqual();
} else if (hint != CompareOperationHints::kAny) {
less_than = simplified()->SpeculativeNumberLessThan(hint);
less_than_or_equal = simplified()->SpeculativeNumberLessThanOrEqual(hint);
} else {
// TODO(turbofan): mixed signed/unsigned int32 comparisons.
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
......@@ -593,7 +616,11 @@ Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
default:
return NoChange();
}
return r.ChangeToPureOperator(comparison);
if (comparison->EffectInputCount() > 0) {
return r.ChangeToSpeculativeOperator(comparison, Type::Boolean());
} else {
return r.ChangeToPureOperator(comparison);
}
}
// TODO(turbofan): relax/remove effects of this operator in other cases.
return NoChange(); // Keep a generic comparison.
......
......@@ -173,76 +173,79 @@
V(StringLessThan) \
V(StringLessThanOrEqual)
#define SIMPLIFIED_OP_LIST(V) \
SIMPLIFIED_COMPARE_BINOP_LIST(V) \
V(PlainPrimitiveToNumber) \
V(PlainPrimitiveToWord32) \
V(PlainPrimitiveToFloat64) \
V(BooleanNot) \
V(BooleanToNumber) \
V(SpeculativeNumberAdd) \
V(SpeculativeNumberSubtract) \
V(NumberAdd) \
V(NumberSubtract) \
V(NumberMultiply) \
V(NumberDivide) \
V(NumberModulus) \
V(NumberBitwiseOr) \
V(NumberBitwiseXor) \
V(NumberBitwiseAnd) \
V(NumberShiftLeft) \
V(NumberShiftRight) \
V(NumberShiftRightLogical) \
V(NumberImul) \
V(NumberClz32) \
V(NumberCeil) \
V(NumberFloor) \
V(NumberFround) \
V(NumberAtan) \
V(NumberAtan2) \
V(NumberLog) \
V(NumberLog1p) \
V(NumberRound) \
V(NumberSqrt) \
V(NumberTrunc) \
V(NumberToInt32) \
V(NumberToUint32) \
V(NumberSilenceNaN) \
V(StringFromCharCode) \
V(StringToNumber) \
V(ChangeTaggedSignedToInt32) \
V(ChangeTaggedToInt32) \
V(ChangeTaggedToUint32) \
V(ChangeTaggedToFloat64) \
V(ChangeInt31ToTaggedSigned) \
V(ChangeInt32ToTagged) \
V(ChangeUint32ToTagged) \
V(ChangeFloat64ToTagged) \
V(ChangeTaggedToBit) \
V(ChangeBitToTagged) \
V(CheckBounds) \
V(CheckedUint32ToInt32) \
V(CheckedFloat64ToInt32) \
V(CheckedTaggedToInt32) \
V(CheckedTaggedToFloat64) \
V(CheckFloat64Hole) \
V(CheckTaggedHole) \
V(CheckIf) \
V(TruncateTaggedToWord32) \
V(TruncateTaggedToFloat64) \
V(Allocate) \
V(LoadField) \
V(LoadBuffer) \
V(LoadElement) \
V(StoreField) \
V(StoreBuffer) \
V(StoreElement) \
V(ObjectIsCallable) \
V(ObjectIsNumber) \
V(ObjectIsReceiver) \
V(ObjectIsSmi) \
V(ObjectIsString) \
V(ObjectIsUndetectable) \
#define SIMPLIFIED_OP_LIST(V) \
SIMPLIFIED_COMPARE_BINOP_LIST(V) \
V(PlainPrimitiveToNumber) \
V(PlainPrimitiveToWord32) \
V(PlainPrimitiveToFloat64) \
V(BooleanNot) \
V(BooleanToNumber) \
V(SpeculativeNumberAdd) \
V(SpeculativeNumberSubtract) \
V(SpeculativeNumberEqual) \
V(SpeculativeNumberLessThan) \
V(SpeculativeNumberLessThanOrEqual) \
V(NumberAdd) \
V(NumberSubtract) \
V(NumberMultiply) \
V(NumberDivide) \
V(NumberModulus) \
V(NumberBitwiseOr) \
V(NumberBitwiseXor) \
V(NumberBitwiseAnd) \
V(NumberShiftLeft) \
V(NumberShiftRight) \
V(NumberShiftRightLogical) \
V(NumberImul) \
V(NumberClz32) \
V(NumberCeil) \
V(NumberFloor) \
V(NumberFround) \
V(NumberAtan) \
V(NumberAtan2) \
V(NumberLog) \
V(NumberLog1p) \
V(NumberRound) \
V(NumberSqrt) \
V(NumberTrunc) \
V(NumberToInt32) \
V(NumberToUint32) \
V(NumberSilenceNaN) \
V(StringFromCharCode) \
V(StringToNumber) \
V(ChangeTaggedSignedToInt32) \
V(ChangeTaggedToInt32) \
V(ChangeTaggedToUint32) \
V(ChangeTaggedToFloat64) \
V(ChangeInt31ToTaggedSigned) \
V(ChangeInt32ToTagged) \
V(ChangeUint32ToTagged) \
V(ChangeFloat64ToTagged) \
V(ChangeTaggedToBit) \
V(ChangeBitToTagged) \
V(CheckBounds) \
V(CheckedUint32ToInt32) \
V(CheckedFloat64ToInt32) \
V(CheckedTaggedToInt32) \
V(CheckedTaggedToFloat64) \
V(CheckFloat64Hole) \
V(CheckTaggedHole) \
V(CheckIf) \
V(TruncateTaggedToWord32) \
V(TruncateTaggedToFloat64) \
V(Allocate) \
V(LoadField) \
V(LoadBuffer) \
V(LoadElement) \
V(StoreField) \
V(StoreBuffer) \
V(StoreElement) \
V(ObjectIsCallable) \
V(ObjectIsNumber) \
V(ObjectIsReceiver) \
V(ObjectIsSmi) \
V(ObjectIsString) \
V(ObjectIsUndetectable) \
V(TypeGuard)
// Opcodes for Machine-level operators.
......
......@@ -583,10 +583,13 @@ const Operator* RepresentationChanger::Int32OperatorFor(
case IrOpcode::kNumberBitwiseAnd:
return machine()->Word32And();
case IrOpcode::kNumberEqual:
case IrOpcode::kSpeculativeNumberEqual:
return machine()->Word32Equal();
case IrOpcode::kNumberLessThan:
case IrOpcode::kSpeculativeNumberLessThan:
return machine()->Int32LessThan();
case IrOpcode::kNumberLessThanOrEqual:
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
return machine()->Int32LessThanOrEqual();
default:
UNREACHABLE();
......@@ -621,10 +624,13 @@ const Operator* RepresentationChanger::Uint32OperatorFor(
case IrOpcode::kNumberModulus:
return machine()->Uint32Mod();
case IrOpcode::kNumberEqual:
case IrOpcode::kSpeculativeNumberEqual:
return machine()->Word32Equal();
case IrOpcode::kNumberLessThan:
case IrOpcode::kSpeculativeNumberLessThan:
return machine()->Uint32LessThan();
case IrOpcode::kNumberLessThanOrEqual:
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
return machine()->Uint32LessThanOrEqual();
case IrOpcode::kNumberClz32:
return machine()->Word32Clz();
......@@ -653,10 +659,13 @@ const Operator* RepresentationChanger::Float64OperatorFor(
case IrOpcode::kNumberModulus:
return machine()->Float64Mod();
case IrOpcode::kNumberEqual:
case IrOpcode::kSpeculativeNumberEqual:
return machine()->Float64Equal();
case IrOpcode::kNumberLessThan:
case IrOpcode::kSpeculativeNumberLessThan:
return machine()->Float64LessThan();
case IrOpcode::kNumberLessThanOrEqual:
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
return machine()->Float64LessThanOrEqual();
case IrOpcode::kNumberAtan:
return machine()->Float64Atan();
......
......@@ -1040,7 +1040,6 @@ class RepresentationSelector {
Node* overflow = graph()->NewNode(common()->Projection(1), arith);
effect =
graph()->NewNode(simplified()->CheckIf(), overflow, effect, control);
Node* value = graph()->NewNode(common()->Projection(0), arith);
ReplaceEffectControlUses(node, effect, control);
DeferReplacement(node, value);
......@@ -1219,11 +1218,13 @@ class RepresentationSelector {
case IrOpcode::kNumberLessThan:
case IrOpcode::kNumberLessThanOrEqual: {
// Number comparisons reduce to integer comparisons for integer inputs.
if (BothInputsAreSigned32(node)) {
if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) &&
TypeOf(node->InputAt(1))->Is(Type::Signed32())) {
// => signed Int32Cmp
VisitInt32Cmp(node);
if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
} else if (BothInputsAreUnsigned32(node)) {
} else if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) {
// => unsigned Int32Cmp
VisitUint32Cmp(node);
if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
......@@ -1239,6 +1240,40 @@ class RepresentationSelector {
case IrOpcode::kSpeculativeNumberSubtract:
return VisitSpeculativeAdditiveOp(node, truncation, lowering);
case IrOpcode::kSpeculativeNumberLessThan:
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
case IrOpcode::kSpeculativeNumberEqual: {
// Number comparisons reduce to integer comparisons for integer inputs.
if (TypeOf(node->InputAt(0))->Is(Type::Signed32()) &&
TypeOf(node->InputAt(1))->Is(Type::Signed32())) {
// => signed Int32Cmp
VisitInt32Cmp(node);
if (lower()) ChangeToPureOp(node, Int32Op(node));
return;
} else if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) {
// => unsigned Int32Cmp
VisitUint32Cmp(node);
if (lower()) ChangeToPureOp(node, Uint32Op(node));
return;
}
// Try to use type feedback.
CompareOperationHints::Hint hint = CompareOperationHintOf(node->op());
if (hint == CompareOperationHints::kSignedSmall) {
VisitBinop(node, UseInfo::CheckedSigned32AsWord32(),
MachineRepresentation::kBit);
if (lower()) ChangeToPureOp(node, Int32Op(node));
return;
}
DCHECK_EQ(CompareOperationHints::kNumber, hint);
// default case => Float64 comparison
VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(),
MachineRepresentation::kBit);
if (lower()) ChangeToPureOp(node, Float64Op(node));
return;
}
case IrOpcode::kNumberAdd:
case IrOpcode::kNumberSubtract: {
if (BothInputsAre(node, Type::Signed32()) &&
......
......@@ -223,6 +223,13 @@ BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op) {
return OpParameter<BinaryOperationHints::Hint>(op);
}
CompareOperationHints::Hint CompareOperationHintOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kSpeculativeNumberEqual ||
op->opcode() == IrOpcode::kSpeculativeNumberLessThan ||
op->opcode() == IrOpcode::kSpeculativeNumberLessThanOrEqual);
return OpParameter<CompareOperationHints::Hint>(op);
}
#define PURE_OP_LIST(V) \
V(BooleanNot, Operator::kNoProperties, 1) \
V(BooleanToNumber, Operator::kNoProperties, 1) \
......@@ -497,6 +504,27 @@ const Operator* SimplifiedOperatorBuilder::SpeculativeNumberSubtract(
"SpeculativeNumberSubtract", 2, 1, 1, 1, 1, 1, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberEqual(
CompareOperationHints::Hint hint) {
return new (zone()) Operator1<CompareOperationHints::Hint>(
IrOpcode::kSpeculativeNumberEqual, Operator::kPure,
"SpeculativeNumberEqual", 2, 1, 1, 1, 1, 1, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberLessThan(
CompareOperationHints::Hint hint) {
return new (zone()) Operator1<CompareOperationHints::Hint>(
IrOpcode::kSpeculativeNumberLessThan, Operator::kPure,
"SpeculativeNumberLessThan", 2, 1, 1, 1, 1, 1, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberLessThanOrEqual(
CompareOperationHints::Hint hint) {
return new (zone()) Operator1<CompareOperationHints::Hint>(
IrOpcode::kSpeculativeNumberLessThanOrEqual, Operator::kPure,
"SpeculativeNumberLessThanOrEqual", 2, 1, 1, 1, 1, 1, hint);
}
#define ACCESS_OP_LIST(V) \
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
V(StoreField, FieldAccess, Operator::kNoRead, 2, 1, 0) \
......
......@@ -129,6 +129,8 @@ Type* TypeOf(const Operator* op) WARN_UNUSED_RESULT;
BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op);
CompareOperationHints::Hint CompareOperationHintOf(const Operator* op);
// Interface for building simplified operators, which represent the
// medium-level operations of V8, including adding numbers, allocating objects,
// indexing into objects and arrays, etc.
......@@ -192,6 +194,11 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* SpeculativeNumberAdd(BinaryOperationHints::Hint hint);
const Operator* SpeculativeNumberSubtract(BinaryOperationHints::Hint hint);
const Operator* SpeculativeNumberLessThan(CompareOperationHints::Hint hint);
const Operator* SpeculativeNumberLessThanOrEqual(
CompareOperationHints::Hint hint);
const Operator* SpeculativeNumberEqual(CompareOperationHints::Hint hint);
const Operator* ReferenceEqual(Type* type);
const Operator* StringEqual();
......
......@@ -16,7 +16,7 @@ namespace compiler {
namespace {
// TODO(bmeurer): This detour via types is ugly.
BinaryOperationHints::Hint ToHint(Type* type) {
BinaryOperationHints::Hint ToBinaryOperationHint(Type* type) {
if (type->Is(Type::None())) return BinaryOperationHints::kNone;
if (type->Is(Type::SignedSmall())) return BinaryOperationHints::kSignedSmall;
if (type->Is(Type::Signed32())) return BinaryOperationHints::kSigned32;
......@@ -25,8 +25,34 @@ BinaryOperationHints::Hint ToHint(Type* type) {
return BinaryOperationHints::kAny;
}
} // namespace
CompareOperationHints::Hint ToCompareOperationHint(
CompareICState::State state) {
switch (state) {
case CompareICState::UNINITIALIZED:
return CompareOperationHints::kNone;
case CompareICState::BOOLEAN:
return CompareOperationHints::kBoolean;
case CompareICState::SMI:
return CompareOperationHints::kSignedSmall;
case CompareICState::NUMBER:
return CompareOperationHints::kNumber;
case CompareICState::STRING:
return CompareOperationHints::kString;
case CompareICState::INTERNALIZED_STRING:
return CompareOperationHints::kInternalizedString;
case CompareICState::UNIQUE_NAME:
return CompareOperationHints::kUniqueName;
case CompareICState::RECEIVER:
case CompareICState::KNOWN_RECEIVER:
return CompareOperationHints::kReceiver;
case CompareICState::GENERIC:
return CompareOperationHints::kAny;
}
UNREACHABLE();
return CompareOperationHints::kAny;
}
} // namespace
bool TypeHintAnalysis::GetBinaryOperationHints(
TypeFeedbackId id, BinaryOperationHints* hints) const {
......@@ -35,12 +61,29 @@ bool TypeHintAnalysis::GetBinaryOperationHints(
Handle<Code> code = i->second;
DCHECK_EQ(Code::BINARY_OP_IC, code->kind());
BinaryOpICState state(code->GetIsolate(), code->extra_ic_state());
*hints = BinaryOperationHints(ToHint(state.GetLeftType()),
ToHint(state.GetRightType()),
ToHint(state.GetResultType()));
*hints = BinaryOperationHints(ToBinaryOperationHint(state.GetLeftType()),
ToBinaryOperationHint(state.GetRightType()),
ToBinaryOperationHint(state.GetResultType()));
return true;
}
bool TypeHintAnalysis::GetCompareOperationHints(
TypeFeedbackId id, CompareOperationHints* hints) const {
auto i = infos_.find(id);
if (i == infos_.end()) return false;
Handle<Code> code = i->second;
DCHECK_EQ(Code::COMPARE_IC, code->kind());
Handle<Map> map;
Map* raw_map = code->FindFirstMap();
if (raw_map != nullptr) Map::TryUpdate(handle(raw_map)).ToHandle(&map);
CompareICStub stub(code->stub_key(), code->GetIsolate());
*hints = CompareOperationHints(ToCompareOperationHint(stub.left()),
ToCompareOperationHint(stub.right()),
ToCompareOperationHint(stub.state()));
return true;
}
bool TypeHintAnalysis::GetToBooleanHints(TypeFeedbackId id,
ToBooleanHints* hints) const {
......@@ -67,7 +110,6 @@ bool TypeHintAnalysis::GetToBooleanHints(TypeFeedbackId id,
return true;
}
TypeHintAnalysis* TypeHintAnalyzer::Analyze(Handle<Code> code) {
DisallowHeapAllocation no_gc;
TypeHintAnalysis::Infos infos(zone());
......@@ -79,6 +121,7 @@ TypeHintAnalysis* TypeHintAnalyzer::Analyze(Handle<Code> code) {
Code* target = Code::GetCodeFromTargetAddress(target_address);
switch (target->kind()) {
case Code::BINARY_OP_IC:
case Code::COMPARE_IC:
case Code::TO_BOOLEAN_IC: {
// Add this feedback to the {infos}.
TypeFeedbackId id(static_cast<unsigned>(rinfo->data()));
......@@ -90,7 +133,7 @@ TypeHintAnalysis* TypeHintAnalyzer::Analyze(Handle<Code> code) {
break;
}
}
return new (zone()) TypeHintAnalysis(infos);
return new (zone()) TypeHintAnalysis(infos, zone());
}
} // namespace compiler
......
......@@ -18,14 +18,20 @@ class TypeHintAnalysis final : public ZoneObject {
public:
typedef ZoneMap<TypeFeedbackId, Handle<Code>> Infos;
explicit TypeHintAnalysis(Infos const& infos) : infos_(infos) {}
explicit TypeHintAnalysis(Infos const& infos, Zone* zone)
: infos_(infos), zone_(zone) {}
bool GetBinaryOperationHints(TypeFeedbackId id,
BinaryOperationHints* hints) const;
bool GetCompareOperationHints(TypeFeedbackId id,
CompareOperationHints* hints) const;
bool GetToBooleanHints(TypeFeedbackId id, ToBooleanHints* hints) const;
private:
Zone* zone() const { return zone_; }
Infos const infos_;
Zone* zone_;
};
......
......@@ -27,11 +27,39 @@ std::ostream& operator<<(std::ostream& os, BinaryOperationHints::Hint hint) {
return os;
}
std::ostream& operator<<(std::ostream& os, BinaryOperationHints hints) {
return os << hints.left() << "*" << hints.right() << "->" << hints.result();
}
std::ostream& operator<<(std::ostream& os, CompareOperationHints::Hint hint) {
switch (hint) {
case CompareOperationHints::kNone:
return os << "None";
case CompareOperationHints::kBoolean:
return os << "Boolean";
case CompareOperationHints::kSignedSmall:
return os << "SignedSmall";
case CompareOperationHints::kNumber:
return os << "Number";
case CompareOperationHints::kString:
return os << "String";
case CompareOperationHints::kInternalizedString:
return os << "InternalizedString";
case CompareOperationHints::kUniqueName:
return os << "UniqueName";
case CompareOperationHints::kReceiver:
return os << "Receiver";
case CompareOperationHints::kAny:
return os << "Any";
}
UNREACHABLE();
return os;
}
std::ostream& operator<<(std::ostream& os, CompareOperationHints hints) {
return os << hints.left() << "*" << hints.right() << " (" << hints.combined()
<< ")";
}
std::ostream& operator<<(std::ostream& os, ToBooleanHint hint) {
switch (hint) {
......@@ -62,7 +90,6 @@ std::ostream& operator<<(std::ostream& os, ToBooleanHint hint) {
return os;
}
std::ostream& operator<<(std::ostream& os, ToBooleanHints hints) {
if (hints == ToBooleanHint::kAny) return os << "Any";
if (hints == ToBooleanHint::kNone) return os << "None";
......
......@@ -64,6 +64,55 @@ class BinaryOperationHints final {
std::ostream& operator<<(std::ostream&, BinaryOperationHints::Hint);
std::ostream& operator<<(std::ostream&, BinaryOperationHints);
// Type hints for an binary operation.
class CompareOperationHints final {
public:
enum Hint {
kNone,
kBoolean,
kSignedSmall,
kNumber,
kString,
kInternalizedString,
kUniqueName,
kReceiver,
kAny
};
CompareOperationHints() : CompareOperationHints(kNone, kNone, kNone) {}
CompareOperationHints(Hint left, Hint right, Hint combined)
: bit_field_(LeftField::encode(left) | RightField::encode(right) |
CombinedField::encode(combined)) {}
static CompareOperationHints Any() {
return CompareOperationHints(kAny, kAny, kAny);
}
Hint left() const { return LeftField::decode(bit_field_); }
Hint right() const { return RightField::decode(bit_field_); }
Hint combined() const { return CombinedField::decode(bit_field_); }
bool operator==(CompareOperationHints const& that) const {
return this->bit_field_ == that.bit_field_;
}
bool operator!=(CompareOperationHints const& that) const {
return !(*this == that);
}
friend size_t hash_value(CompareOperationHints const& hints) {
return hints.bit_field_;
}
private:
typedef BitField<Hint, 0, 4> LeftField;
typedef BitField<Hint, 4, 4> RightField;
typedef BitField<Hint, 8, 4> CombinedField;
uint32_t bit_field_;
};
std::ostream& operator<<(std::ostream&, CompareOperationHints::Hint);
std::ostream& operator<<(std::ostream&, CompareOperationHints);
// Type hints for the ToBoolean type conversion.
enum class ToBooleanHint : uint16_t {
......
......@@ -873,7 +873,6 @@ Type* Typer::Visitor::JSGreaterThanOrEqualTyper(
return FalsifyUndefined(Invert(JSCompareTyper(lhs, rhs, t), t), t);
}
// JS bitwise operators.
......@@ -1710,6 +1709,18 @@ Type* Typer::Visitor::TypeNumberLessThanOrEqual(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeSpeculativeNumberEqual(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeSpeculativeNumberLessThan(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeSpeculativeNumberLessThanOrEqual(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeNumberAdd(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeNumberSubtract(Node* node) { return Type::Number(); }
......
......@@ -689,6 +689,12 @@ void Verifier::Visitor::Check(Node* node) {
break;
case IrOpcode::kSpeculativeNumberAdd:
case IrOpcode::kSpeculativeNumberSubtract:
CheckUpperIs(node, Type::Number());
break;
case IrOpcode::kSpeculativeNumberEqual:
case IrOpcode::kSpeculativeNumberLessThan:
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
CheckUpperIs(node, Type::Boolean());
break;
case IrOpcode::kNumberAdd:
case IrOpcode::kNumberSubtract:
......
......@@ -40,22 +40,12 @@ const SharedOperator kSharedOperators[] = {
control_input_count, value_output_count, effect_output_count, \
control_output_count \
}
SHARED(Equal, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(NotEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(StrictEqual, Operator::kPure, 2, 0, 0, 0, 1, 0, 0),
SHARED(StrictNotEqual, Operator::kPure, 2, 0, 0, 0, 1, 0, 0),
SHARED(LessThan, Operator::kNoProperties, 2, 2, 1, 1, 1, 1, 2),
SHARED(GreaterThan, Operator::kNoProperties, 2, 2, 1, 1, 1, 1, 2),
SHARED(LessThanOrEqual, Operator::kNoProperties, 2, 2, 1, 1, 1, 1, 2),
SHARED(GreaterThanOrEqual, Operator::kNoProperties, 2, 2, 1, 1, 1, 1, 2),
SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToString, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToObject, Operator::kFoldable, 1, 1, 1, 1, 1, 1, 2),
SHARED(Create, Operator::kEliminatable, 2, 1, 1, 0, 1, 1, 0),
SHARED(HasProperty, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(TypeOf, Operator::kPure, 1, 0, 0, 0, 1, 0, 0),
SHARED(InstanceOf, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(CreateWithContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2),
SHARED(CreateModuleContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2),
#undef SHARED
......
......@@ -384,8 +384,9 @@ TEST_F(JSTypedLoweringTest, JSStrictEqualWithTheHole) {
Node* const context = UndefinedConstant();
TRACED_FOREACH(Type*, type, kJSTypes) {
Node* const lhs = Parameter(type);
Reduction r = Reduce(
graph()->NewNode(javascript()->StrictEqual(), lhs, the_hole, context));
Reduction r = Reduce(graph()->NewNode(
javascript()->StrictEqual(CompareOperationHints::Any()), lhs, the_hole,
context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
......@@ -396,8 +397,9 @@ TEST_F(JSTypedLoweringTest, JSStrictEqualWithUnique) {
Node* const lhs = Parameter(Type::Unique(), 0);
Node* const rhs = Parameter(Type::Unique(), 1);
Node* const context = Parameter(Type::Any(), 2);
Reduction r =
Reduce(graph()->NewNode(javascript()->StrictEqual(), lhs, rhs, context));
Reduction r = Reduce(
graph()->NewNode(javascript()->StrictEqual(CompareOperationHints::Any()),
lhs, rhs, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsReferenceEqual(Type::Unique(), lhs, rhs));
}
......
......@@ -290,44 +290,51 @@ TEST_F(TyperTest, TypeJSShiftRight) {
TEST_F(TyperTest, TypeJSLessThan) {
TestBinaryCompareOp(javascript_.LessThan(), std::less<double>());
TestBinaryCompareOp(javascript_.LessThan(CompareOperationHints::Any()),
std::less<double>());
}
TEST_F(TyperTest, TypeJSLessThanOrEqual) {
TestBinaryCompareOp(javascript_.LessThanOrEqual(), std::less_equal<double>());
TestBinaryCompareOp(javascript_.LessThanOrEqual(CompareOperationHints::Any()),
std::less_equal<double>());
}
TEST_F(TyperTest, TypeJSGreaterThan) {
TestBinaryCompareOp(javascript_.GreaterThan(), std::greater<double>());
TestBinaryCompareOp(javascript_.GreaterThan(CompareOperationHints::Any()),
std::greater<double>());
}
TEST_F(TyperTest, TypeJSGreaterThanOrEqual) {
TestBinaryCompareOp(javascript_.GreaterThanOrEqual(),
std::greater_equal<double>());
TestBinaryCompareOp(
javascript_.GreaterThanOrEqual(CompareOperationHints::Any()),
std::greater_equal<double>());
}
TEST_F(TyperTest, TypeJSEqual) {
TestBinaryCompareOp(javascript_.Equal(), std::equal_to<double>());
TestBinaryCompareOp(javascript_.Equal(CompareOperationHints::Any()),
std::equal_to<double>());
}
TEST_F(TyperTest, TypeJSNotEqual) {
TestBinaryCompareOp(javascript_.NotEqual(), std::not_equal_to<double>());
TestBinaryCompareOp(javascript_.NotEqual(CompareOperationHints::Any()),
std::not_equal_to<double>());
}
// For numbers there's no difference between strict and non-strict equality.
TEST_F(TyperTest, TypeJSStrictEqual) {
TestBinaryCompareOp(javascript_.StrictEqual(), std::equal_to<double>());
TestBinaryCompareOp(javascript_.StrictEqual(CompareOperationHints::Any()),
std::equal_to<double>());
}
TEST_F(TyperTest, TypeJSStrictNotEqual) {
TestBinaryCompareOp(javascript_.StrictNotEqual(),
TestBinaryCompareOp(javascript_.StrictNotEqual(CompareOperationHints::Any()),
std::not_equal_to<double>());
}
......@@ -335,10 +342,9 @@ TEST_F(TyperTest, TypeJSStrictNotEqual) {
//------------------------------------------------------------------------------
// Monotonicity
#define TEST_BINARY_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
TestBinaryMonotonicity(javascript_.name()); \
#define TEST_BINARY_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
TestBinaryMonotonicity(javascript_.name(CompareOperationHints::Any())); \
}
TEST_BINARY_MONOTONICITY(Equal)
TEST_BINARY_MONOTONICITY(NotEqual)
......
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