Commit 216bcf9f authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Initial version of number type feedback.

This introduces optimized number operations based on type feedback.

Summary of changes:

1. Typed lowering produces SpeculativeNumberAdd/Subtract for JSAdd/Subtract if
   there is suitable feedback. The speculative nodes are connected to both the
   effect chain and the control chain and they retain the eager frame state.

2. Simplified lowering now executes in three phases:
  a. Propagation phase computes truncations by traversing the graph from uses to
     definitions until checkpoint is reached. It also records type-check decisions
     for later typing phase, and computes representation.
  b. The typing phase computes more precise types base on the speculative types (and recomputes
     representation for affected nodes).
  c. The lowering phase performs lowering and inserts representation changes and/or checks.

3. Effect-control linearization lowers the checks to machine graphs.

Notes:

- SimplifiedLowering will be refactored to have handling of each operation one place and
  with clearer input/output protocol for each sub-phase. I would prefer to do this once
  we have more operations implemented, and the pattern is clearer.

- The check operations (Checked<A>To<B>) should have some flags that would affect
  the kind of truncations that they can handle. E.g., if we know that a node produces
  a number, we can omit the oddball check in the CheckedTaggedToFloat64 lowering.

- In future, we want the typer to reuse the logic from OperationTyper.

BUG=v8:4583
LOG=n

Review-Url: https://codereview.chromium.org/1921563002
Cr-Commit-Position: refs/heads/master@{#36674}
parent a8f57df4
......@@ -964,6 +964,8 @@ v8_source_set("v8_base") {
"src/compiler/node.h",
"src/compiler/opcodes.cc",
"src/compiler/opcodes.h",
"src/compiler/operation-typer.cc",
"src/compiler/operation-typer.h",
"src/compiler/operator-properties.cc",
"src/compiler/operator-properties.h",
"src/compiler/operator.cc",
......
......@@ -146,6 +146,7 @@ class CompilationInfo final {
kSourcePositionsEnabled = 1 << 15,
kBailoutOnUninitialized = 1 << 16,
kOptimizeFromBytecode = 1 << 17,
kTypeFeedbackEnabled = 1 << 18,
};
CompilationInfo(ParseInfo* parse_info, Handle<JSFunction> closure);
......@@ -256,6 +257,12 @@ class CompilationInfo final {
return GetFlag(kDeoptimizationEnabled);
}
void MarkAsTypeFeedbackEnabled() { SetFlag(kTypeFeedbackEnabled); }
bool is_type_feedback_enabled() const {
return GetFlag(kTypeFeedbackEnabled);
}
void MarkAsSourcePositionsEnabled() { SetFlag(kSourcePositionsEnabled); }
bool is_source_positions_enabled() const {
......
......@@ -2735,6 +2735,7 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
// Create a proper eager frame state for the stores.
environment()->Push(old_value);
FrameStateBeforeAndAfter store_states(this, expr->ToNumberId());
FrameStateBeforeAndAfter binop_states(this, expr->ToNumberId());
old_value = environment()->Pop();
// Save result for postfix expressions at correct stack depth.
......@@ -2747,16 +2748,12 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
}
// Create node to perform +1/-1 operation.
Node* value;
{
// TODO(bmeurer): Cleanup this feedback/bailout mess!
FrameStateBeforeAndAfter states(this, BailoutId::None());
value = BuildBinaryOp(old_value, jsgraph()->OneConstant(),
expr->binary_op(), expr->CountBinOpFeedbackId());
// This should never deoptimize because we have converted to number before.
states.AddToNode(value, BailoutId::None(),
OutputFrameStateCombine::Ignore());
}
// TODO(bmeurer): Cleanup this feedback/bailout mess!
Node* value = BuildBinaryOp(old_value, jsgraph()->OneConstant(),
expr->binary_op(), expr->CountBinOpFeedbackId());
// This should never deoptimize because we have converted to number before.
binop_states.AddToNode(value, BailoutId::None(),
OutputFrameStateCombine::Ignore());
// Store the value.
VectorSlotPair feedback = CreateVectorSlotPair(expr->CountSlot());
......
......@@ -387,6 +387,18 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, Node** effect,
case IrOpcode::kTruncateTaggedToFloat64:
state = LowerTruncateTaggedToFloat64(node, *effect, *control);
break;
case IrOpcode::kCheckedUint32ToInt32:
state = LowerCheckedUint32ToInt32(node, *effect, *control);
break;
case IrOpcode::kCheckedFloat64ToInt32:
state = LowerCheckedFloat64ToInt32(node, *effect, *control);
break;
case IrOpcode::kCheckedTaggedToInt32:
state = LowerCheckedTaggedToInt32(node, *effect, *control);
break;
case IrOpcode::kCheckedTaggedToFloat64:
state = LowerCheckedTaggedToFloat64(node, *effect, *control);
break;
case IrOpcode::kTruncateTaggedToWord32:
state = LowerTruncateTaggedToWord32(node, *effect, *control);
break;
......@@ -705,6 +717,186 @@ EffectControlLinearizer::LowerTruncateTaggedToFloat64(Node* node, Node* effect,
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckedUint32ToInt32(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
Node* max_int = jsgraph()->Int32Constant(std::numeric_limits<int32_t>::max());
Node* is_safe =
graph()->NewNode(machine()->Uint32LessThanOrEqual(), value, max_int);
control = effect = graph()->NewNode(common()->DeoptimizeUnless(), is_safe,
frame_state, effect, control);
// Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::BuildCheckedFloat64ToInt32(Node* value,
Node* frame_state,
Node* effect,
Node* control) {
Node* value32 = graph()->NewNode(machine()->RoundFloat64ToInt32(), value);
Node* check_same = graph()->NewNode(
machine()->Float64Equal(), value,
graph()->NewNode(machine()->ChangeInt32ToFloat64(), value32));
control = effect = graph()->NewNode(common()->DeoptimizeUnless(), check_same,
frame_state, effect, control);
// Check if {value} is -0.
Node* check_zero = graph()->NewNode(machine()->Word32Equal(), value32,
jsgraph()->Int32Constant(0));
Node* branch_zero = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check_zero, control);
Node* if_zero = graph()->NewNode(common()->IfTrue(), branch_zero);
Node* if_notzero = graph()->NewNode(common()->IfFalse(), branch_zero);
// In case of 0, we need to check the high bits for the IEEE -0 pattern.
Node* check_negative = graph()->NewNode(
machine()->Int32LessThan(),
graph()->NewNode(machine()->Float64ExtractHighWord32(), value),
jsgraph()->Int32Constant(0));
Node* deopt_minus_zero = graph()->NewNode(
common()->DeoptimizeIf(), check_negative, frame_state, effect, if_zero);
Node* merge =
graph()->NewNode(common()->Merge(2), deopt_minus_zero, if_notzero);
effect =
graph()->NewNode(common()->EffectPhi(2), deopt_minus_zero, effect, merge);
return ValueEffectControl(value32, effect, merge);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckedFloat64ToInt32(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
// Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0);
return BuildCheckedFloat64ToInt32(value, frame_state, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckedTaggedToInt32(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
Node* check = ObjectIsSmi(value);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
// In the Smi case, just convert to int32.
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;
Node* vtrue = ChangeSmiToInt32(value);
// In the non-Smi case, check the heap numberness, load the number and convert
// to int32.
// TODO(jarin) Propagate/handle possible truncations here.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
ValueEffectControl number_state = BuildCheckedHeapNumberOrOddballToFloat64(
value, frame_state, effect, if_false);
number_state =
BuildCheckedFloat64ToInt32(number_state.value, frame_state,
number_state.effect, number_state.control);
Node* merge =
graph()->NewNode(common()->Merge(2), if_true, number_state.control);
Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), etrue,
number_state.effect, merge);
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.
node->TrimInputCount(0);
return ValueEffectControl(result, effect_phi, merge);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::BuildCheckedHeapNumberOrOddballToFloat64(
Node* value, Node* frame_state, Node* effect, Node* control) {
Node* value_map = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
Node* check_number = graph()->NewNode(machine()->WordEqual(), value_map,
jsgraph()->HeapNumberMapConstant());
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check_number, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
// For oddballs also contain the numeric value, let us just check that
// we have an oddball here.
Node* efalse = effect;
Node* instance_type = efalse = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
efalse, if_false);
Node* check_oddball =
graph()->NewNode(machine()->Word32Equal(), instance_type,
jsgraph()->Int32Constant(ODDBALL_TYPE));
if_false = efalse =
graph()->NewNode(common()->DeoptimizeUnless(), check_oddball, frame_state,
efalse, if_false);
STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset);
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
Node* result = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), value,
effect, control);
return ValueEffectControl(result, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckedTaggedToFloat64(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
Node* check = ObjectIsSmi(value);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
// In the Smi case, just convert to int32 and then float64.
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;
Node* vtrue = ChangeSmiToInt32(value);
vtrue = graph()->NewNode(machine()->ChangeInt32ToFloat64(), vtrue);
// Otherwise, check heap numberness and load the number.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
ValueEffectControl number_state = BuildCheckedHeapNumberOrOddballToFloat64(
value, frame_state, effect, if_false);
Node* merge =
graph()->NewNode(common()->Merge(2), if_true, number_state.control);
Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), etrue,
number_state.effect, merge);
Node* result =
graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2), vtrue,
number_state.value, merge);
// Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0);
return ValueEffectControl(result, effect_phi, merge);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerTruncateTaggedToWord32(Node* node, Node* effect,
Node* control) {
......
......@@ -60,6 +60,14 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl LowerChangeTaggedToUint32(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerCheckedUint32ToInt32(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerCheckedFloat64ToInt32(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerCheckedTaggedToInt32(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerCheckedTaggedToFloat64(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerChangeTaggedToFloat64(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerTruncateTaggedToFloat64(Node* node, Node* effect,
......@@ -81,7 +89,12 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl AllocateHeapNumberWithValue(Node* node, Node* effect,
Node* control);
ValueEffectControl BuildCheckedFloat64ToInt32(Node* value, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl BuildCheckedHeapNumberOrOddballToFloat64(Node* value,
Node* frame_state,
Node* effect,
Node* control);
Node* ChangeInt32ToSmi(Node* value);
Node* ChangeUint32ToSmi(Node* value);
Node* ChangeInt32ToFloat64(Node* value);
......
......@@ -356,6 +356,7 @@ Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
ParseInfo parse_info(&zone, function);
CompilationInfo info(&parse_info, function);
if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled();
if (info_->is_type_feedback_enabled()) info.MarkAsTypeFeedbackEnabled();
if (!Compiler::ParseAndAnalyze(info.parse_info())) {
TRACE("Not inlining %s into %s because parsing failed\n",
......
......@@ -376,6 +376,12 @@ const CreateLiteralParameters& CreateLiteralParametersOf(const Operator* op) {
return OpParameter<CreateLiteralParameters>(op);
}
const BinaryOperationHints& BinaryOperationHintsOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kJSAdd ||
op->opcode() == IrOpcode::kJSSubtract);
return OpParameter<BinaryOperationHints>(op);
}
#define CACHED_OP_LIST(V) \
V(Equal, Operator::kNoProperties, 2, 1) \
V(NotEqual, Operator::kNoProperties, 2, 1) \
......
......@@ -344,7 +344,6 @@ std::ostream& operator<<(std::ostream&, CreateClosureParameters const&);
const CreateClosureParameters& CreateClosureParametersOf(const Operator* op);
// Defines shared information for the literal that should be created. This is
// used as parameter by JSCreateLiteralArray, JSCreateLiteralObject and
// JSCreateLiteralRegExp operators.
......@@ -375,6 +374,7 @@ std::ostream& operator<<(std::ostream&, CreateLiteralParameters const&);
const CreateLiteralParameters& CreateLiteralParametersOf(const Operator* op);
const BinaryOperationHints& BinaryOperationHintsOf(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
......
......@@ -27,6 +27,24 @@ class JSBinopReduction final {
JSBinopReduction(JSTypedLowering* lowering, Node* node)
: lowering_(lowering), node_(node) {}
BinaryOperationHints::Hint GetUsableNumberFeedback() {
if (!(lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) ||
!(lowering_->flags() & JSTypedLowering::kTypeFeedbackEnabled)) {
return BinaryOperationHints::kAny;
}
DCHECK_NE(0, node_->op()->ControlOutputCount());
DCHECK_EQ(1, node_->op()->EffectOutputCount());
DCHECK_EQ(2, OperatorProperties::GetFrameStateInputCount(node_->op()));
BinaryOperationHints hints = BinaryOperationHintsOf(node_->op());
BinaryOperationHints::Hint combined = hints.combined();
if (combined == BinaryOperationHints::kSignedSmall ||
combined == BinaryOperationHints::kSigned32 ||
combined == BinaryOperationHints::kNumberOrUndefined) {
return combined;
}
return BinaryOperationHints::kAny;
}
void ConvertInputsToNumberOrUndefined(Node* frame_state) {
// To convert the inputs to numbers, we have to provide frame states
// for lazy bailouts in the ToNumber conversions.
......@@ -107,6 +125,52 @@ class JSBinopReduction final {
return lowering_->Changed(node_);
}
Reduction ChangeToSpeculativeOperator(const Operator* op) {
DCHECK_EQ(1, op->EffectInputCount());
DCHECK_EQ(1, op->EffectOutputCount());
DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
DCHECK_EQ(1, op->ControlInputCount());
DCHECK_EQ(1, op->ControlOutputCount());
DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(op));
DCHECK_EQ(2, op->ValueInputCount());
DCHECK_EQ(1, node_->op()->EffectInputCount());
DCHECK_EQ(1, node_->op()->EffectOutputCount());
DCHECK_EQ(1, node_->op()->ControlInputCount());
DCHECK_LT(1, node_->op()->ControlOutputCount());
DCHECK_EQ(2, OperatorProperties::GetFrameStateInputCount(node_->op()));
DCHECK_EQ(2, node_->op()->ValueInputCount());
// Reconnect the control output to bypass the IfSuccess node and
// possibly disconnect from the IfException node.
for (Edge edge : node_->use_edges()) {
Node* const user = edge.from();
DCHECK(!user->IsDead());
if (NodeProperties::IsControlEdge(edge)) {
if (user->opcode() == IrOpcode::kIfSuccess) {
user->ReplaceUses(node_);
user->Kill();
} else {
DCHECK_EQ(user->opcode(), IrOpcode::kIfException);
edge.UpdateTo(jsgraph()->Dead());
}
}
}
// Remove the lazy bailout frame state and the context.
node_->RemoveInput(NodeProperties::FirstFrameStateIndex(node_));
node_->RemoveInput(NodeProperties::FirstContextIndex(node_));
NodeProperties::ChangeOp(node_, op);
// Update the type to number.
Type* node_type = NodeProperties::GetType(node_);
NodeProperties::SetType(node_,
Type::Intersect(node_type, Type::Number(), zone()));
return lowering_->Changed(node_);
}
Reduction ChangeToPureOperator(const Operator* op, Type* type) {
return ChangeToPureOperator(op, false, type);
}
......@@ -340,9 +404,18 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
JSBinopReduction r(this, node);
BinaryOperationHints::Hint feedback = r.GetUsableNumberFeedback();
if (feedback != BinaryOperationHints::kAny) {
// Lower to the optimistic number binop.
return r.ChangeToSpeculativeOperator(
simplified()->SpeculativeNumberAdd(feedback));
}
if (r.BothInputsAre(Type::NumberOrUndefined())) {
// JSAdd(x:number, y:number) => NumberAdd(x, y)
return ReduceNumberBinop(node, simplified()->NumberAdd());
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumberOrUndefined(frame_state);
return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
}
if (r.NeitherInputCanBe(Type::StringOrReceiver())) {
// JSAdd(x:-string, y:-string) => NumberAdd(ToNumber(x), ToNumber(y))
......@@ -387,21 +460,34 @@ Reduction JSTypedLowering::ReduceJSModulus(Node* node) {
return NoChange();
}
Reduction JSTypedLowering::ReduceJSSubtract(Node* node) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
JSBinopReduction r(this, node);
BinaryOperationHints::Hint feedback = r.GetUsableNumberFeedback();
if (feedback != BinaryOperationHints::kAny) {
// Lower to the optimistic number binop.
return r.ChangeToSpeculativeOperator(
simplified()->SpeculativeNumberSubtract(feedback));
}
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumberOrUndefined(frame_state);
return r.ChangeToPureOperator(simplified()->NumberSubtract(), Type::Number());
}
Reduction JSTypedLowering::ReduceNumberBinop(Node* node,
const Operator* numberOp) {
Reduction JSTypedLowering::ReduceJSMultiply(Node* node) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
JSBinopReduction r(this, node);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumberOrUndefined(frame_state);
return r.ChangeToPureOperator(simplified()->NumberMultiply(), Type::Number());
}
Reduction JSTypedLowering::ReduceJSDivide(Node* node) {
if (flags() & kDisableBinaryOpReduction) return NoChange();
JSBinopReduction r(this, node);
if (numberOp == simplified()->NumberModulus()) {
if (r.BothInputsAre(Type::NumberOrUndefined())) {
return r.ChangeToPureOperator(numberOp, Type::Number());
}
return NoChange();
}
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
r.ConvertInputsToNumberOrUndefined(frame_state);
return r.ChangeToPureOperator(numberOp, Type::Number());
return r.ChangeToPureOperator(simplified()->NumberDivide(), Type::Number());
}
......@@ -1785,11 +1871,11 @@ Reduction JSTypedLowering::Reduce(Node* node) {
case IrOpcode::kJSAdd:
return ReduceJSAdd(node);
case IrOpcode::kJSSubtract:
return ReduceNumberBinop(node, simplified()->NumberSubtract());
return ReduceJSSubtract(node);
case IrOpcode::kJSMultiply:
return ReduceNumberBinop(node, simplified()->NumberMultiply());
return ReduceJSMultiply(node);
case IrOpcode::kJSDivide:
return ReduceNumberBinop(node, simplified()->NumberDivide());
return ReduceJSDivide(node);
case IrOpcode::kJSModulus:
return ReduceJSModulus(node);
case IrOpcode::kJSToBoolean:
......
......@@ -36,6 +36,7 @@ class JSTypedLowering final : public AdvancedReducer {
kNoFlags = 0u,
kDeoptimizationEnabled = 1u << 0,
kDisableBinaryOpReduction = 1u << 1,
kTypeFeedbackEnabled = 1u << 2,
};
typedef base::Flags<Flag> Flags;
......@@ -80,7 +81,8 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSGeneratorRestoreContinuation(Node* node);
Reduction ReduceJSGeneratorRestoreRegister(Node* node);
Reduction ReduceSelect(Node* node);
Reduction ReduceNumberBinop(Node* node, const Operator* numberOp);
Reduction ReduceJSSubtract(Node* node);
Reduction ReduceJSDivide(Node* node);
Reduction ReduceInt32Binop(Node* node, const Operator* intOp);
Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
const Operator* shift_op);
......
......@@ -177,6 +177,8 @@
SIMPLIFIED_COMPARE_BINOP_LIST(V) \
V(BooleanNot) \
V(BooleanToNumber) \
V(SpeculativeNumberAdd) \
V(SpeculativeNumberSubtract) \
V(NumberAdd) \
V(NumberSubtract) \
V(NumberMultiply) \
......@@ -209,6 +211,10 @@
V(ChangeFloat64ToTagged) \
V(ChangeTaggedToBit) \
V(ChangeBitToTagged) \
V(CheckedUint32ToInt32) \
V(CheckedFloat64ToInt32) \
V(CheckedTaggedToInt32) \
V(CheckedTaggedToFloat64) \
V(TruncateTaggedToWord32) \
V(TruncateTaggedToFloat64) \
V(Allocate) \
......
This diff is collapsed.
// 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.
#ifndef V8_COMPILER_OPERATION_TYPER_H_
#define V8_COMPILER_OPERATION_TYPER_H_
#include "src/base/flags.h"
#include "src/compiler/opcodes.h"
namespace v8 {
namespace internal {
class Isolate;
class RangeType;
class Type;
class TypeCache;
class Zone;
namespace compiler {
class OperationTyper {
public:
OperationTyper(Isolate* isolate, Zone* zone);
// Typing Phi.
Type* Merge(Type* left, Type* right);
Type* ToPrimitive(Type* type);
// Helpers for number operation typing.
Type* ToNumber(Type* type);
Type* WeakenRange(Type* current_range, Type* previous_range);
Type* NumericAdd(Type* lhs, Type* rhs);
Type* NumericSubtract(Type* lhs, Type* rhs);
enum ComparisonOutcomeFlags {
kComparisonTrue = 1,
kComparisonFalse = 2,
kComparisonUndefined = 4
};
// Javascript binop typers.
#define DECLARE_CASE(x) Type* Type##x(Type* lhs, Type* rhs);
JS_SIMPLE_BINOP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
Type* singleton_false() { return singleton_false_; }
Type* singleton_true() { return singleton_true_; }
Type* singleton_the_hole() { return singleton_the_hole_; }
private:
typedef base::Flags<ComparisonOutcomeFlags> ComparisonOutcome;
ComparisonOutcome Invert(ComparisonOutcome);
Type* Invert(Type*);
Type* FalsifyUndefined(ComparisonOutcome);
Type* Rangify(Type*);
Type* AddRanger(RangeType* lhs, RangeType* rhs);
Type* SubtractRanger(RangeType* lhs, RangeType* rhs);
Type* ModulusRanger(RangeType* lhs, RangeType* rhs);
Zone* zone() { return zone_; }
Zone* zone_;
TypeCache const& cache_;
Type* singleton_false_;
Type* singleton_true_;
Type* singleton_the_hole_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_OPERATION_TYPER_H_
......@@ -107,6 +107,16 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
case IrOpcode::kJSLessThanOrEqual:
return 2;
// Simplified operators with type feedback.
case IrOpcode::kSpeculativeNumberAdd:
case IrOpcode::kSpeculativeNumberSubtract:
// Checked conversions.
case IrOpcode::kCheckedUint32ToInt32:
case IrOpcode::kCheckedFloat64ToInt32:
case IrOpcode::kCheckedTaggedToInt32:
case IrOpcode::kCheckedTaggedToFloat64:
return 1;
default:
return 0;
}
......
......@@ -597,6 +597,9 @@ PipelineCompilationJob::Status PipelineCompilationJob::CreateGraphImpl() {
if (!info()->shared_info()->asm_function() || FLAG_turbo_asm_deoptimization) {
info()->MarkAsDeoptimizationEnabled();
}
if (info()->is_deoptimization_enabled() && FLAG_turbo_type_feedback) {
info()->MarkAsTypeFeedbackEnabled();
}
if (!info()->is_optimizing_from_bytecode()) {
if (!Compiler::EnsureDeoptimizationSupport(info())) return FAILED;
}
......@@ -883,6 +886,9 @@ struct TypedLoweringPhase {
if (data->info()->shared_info()->HasBytecodeArray()) {
typed_lowering_flags |= JSTypedLowering::kDisableBinaryOpReduction;
}
if (data->info()->is_type_feedback_enabled()) {
typed_lowering_flags |= JSTypedLowering::kTypeFeedbackEnabled;
}
JSTypedLowering typed_lowering(&graph_reducer, data->info()->dependencies(),
typed_lowering_flags, data->jsgraph(),
temp_zone);
......@@ -947,8 +953,12 @@ struct RepresentationSelectionPhase {
static const char* phase_name() { return "representation selection"; }
void Run(PipelineData* data, Zone* temp_zone) {
SimplifiedLowering::Flags flags =
data->info()->is_type_feedback_enabled()
? SimplifiedLowering::kTypeFeedbackEnabled
: SimplifiedLowering::kNoFlag;
SimplifiedLowering lowering(data->jsgraph(), temp_zone,
data->source_positions());
data->source_positions(), flags);
lowering.LowerAllNodes();
}
};
......@@ -1416,7 +1426,7 @@ bool PipelineImpl::CreateGraph() {
// Select representations.
Run<RepresentationSelectionPhase>();
RunPrintAndVerify("Representations selected");
RunPrintAndVerify("Representations selected", true);
}
#ifdef DEBUG
......
This diff is collapsed.
......@@ -73,6 +73,86 @@ class Truncation final {
static bool LessGeneral(TruncationKind rep1, TruncationKind rep2);
};
enum class TypeCheckKind : uint8_t {
kNone,
kSigned32,
kNumberOrUndefined,
kNumber
};
// The {UseInfo} class is used to describe a use of an input of a node.
//
// This information is used in two different ways, based on the phase:
//
// 1. During propagation, the use info is used to inform the input node
// about what part of the input is used (we call this truncation) and what
// is the preferred representation.
//
// 2. During lowering, the use info is used to properly convert the input
// to the preferred representation. The preferred representation might be
// insufficient to do the conversion (e.g. word32->float64 conv), so we also
// need the signedness information to produce the correct value.
class UseInfo {
public:
UseInfo(MachineRepresentation representation, Truncation truncation,
TypeCheckKind type_check = TypeCheckKind::kNone)
: representation_(representation),
truncation_(truncation),
type_check_(type_check) {}
static UseInfo TruncatingWord32() {
return UseInfo(MachineRepresentation::kWord32, Truncation::Word32());
}
static UseInfo TruncatingWord64() {
return UseInfo(MachineRepresentation::kWord64, Truncation::Word64());
}
static UseInfo Bool() {
return UseInfo(MachineRepresentation::kBit, Truncation::Bool());
}
static UseInfo TruncatingFloat32() {
return UseInfo(MachineRepresentation::kFloat32, Truncation::Float32());
}
static UseInfo TruncatingFloat64() {
return UseInfo(MachineRepresentation::kFloat64, Truncation::Float64());
}
static UseInfo PointerInt() {
return kPointerSize == 4 ? TruncatingWord32() : TruncatingWord64();
}
static UseInfo AnyTagged() {
return UseInfo(MachineRepresentation::kTagged, Truncation::Any());
}
// Possibly deoptimizing conversions.
static UseInfo CheckedSigned32AsWord32() {
return UseInfo(MachineRepresentation::kWord32, Truncation::Any(),
TypeCheckKind::kSigned32);
}
static UseInfo CheckedNumberOrUndefinedAsFloat64() {
return UseInfo(MachineRepresentation::kFloat64, Truncation::Any(),
TypeCheckKind::kNumberOrUndefined);
}
// Undetermined representation.
static UseInfo Any() {
return UseInfo(MachineRepresentation::kNone, Truncation::Any());
}
static UseInfo AnyTruncatingToBool() {
return UseInfo(MachineRepresentation::kNone, Truncation::Bool());
}
// Value not used.
static UseInfo None() {
return UseInfo(MachineRepresentation::kNone, Truncation::None());
}
MachineRepresentation representation() const { return representation_; }
Truncation truncation() const { return truncation_; }
TypeCheckKind type_check() const { return type_check_; }
private:
MachineRepresentation representation_;
Truncation truncation_;
TypeCheckKind type_check_;
};
// Contains logic related to changing the representation of values for constants
// and other nodes, as well as lowering Simplified->Machine operators.
......@@ -90,9 +170,10 @@ class RepresentationChanger final {
// out signedness for the word32->float64 conversion, then we check that the
// uses truncate to word32 (so they do not care about signedness).
Node* GetRepresentationFor(Node* node, MachineRepresentation output_rep,
Type* output_type, MachineRepresentation use_rep,
Truncation truncation = Truncation::None());
Type* output_type, Node* use_node,
UseInfo use_info);
const Operator* Int32OperatorFor(IrOpcode::Value opcode);
const Operator* Int32OverflowOperatorFor(IrOpcode::Value opcode);
const Operator* Uint32OperatorFor(IrOpcode::Value opcode);
const Operator* Float64OperatorFor(IrOpcode::Value opcode);
......@@ -122,13 +203,20 @@ class RepresentationChanger final {
Type* output_type, Truncation truncation);
Node* GetFloat64RepresentationFor(Node* node,
MachineRepresentation output_rep,
Type* output_type, Truncation truncation);
Type* output_type, Node* use_node,
UseInfo use_info);
Node* GetWord32RepresentationFor(Node* node, MachineRepresentation output_rep,
Type* output_type, Truncation truncation);
Type* output_type, Node* use_node,
UseInfo use_info);
Node* GetBitRepresentationFor(Node* node, MachineRepresentation output_rep,
Type* output_type);
Node* GetWord64RepresentationFor(Node* node, MachineRepresentation output_rep,
Type* output_type);
Node* GetCheckedWord32RepresentationFor(Node* node,
MachineRepresentation output_rep,
Type* output_type, Node* use_node,
Truncation truncation,
TypeCheckKind check);
Node* TypeError(Node* node, MachineRepresentation output_rep,
Type* output_type, MachineRepresentation use);
Node* MakeTruncatedInt32Constant(double value);
......@@ -138,6 +226,8 @@ class RepresentationChanger final {
Node* InsertChangeTaggedSignedToInt32(Node* node);
Node* InsertChangeTaggedToFloat64(Node* node);
Node* InsertConversion(Node* node, const Operator* op, Node* use_node);
JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const { return isolate_; }
Factory* factory() const { return isolate()->factory(); }
......
This diff is collapsed.
......@@ -5,6 +5,7 @@
#ifndef V8_COMPILER_SIMPLIFIED_LOWERING_H_
#define V8_COMPILER_SIMPLIFIED_LOWERING_H_
#include "src/base/flags.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node.h"
......@@ -26,8 +27,11 @@ class SourcePositionTable;
class SimplifiedLowering final {
public:
enum Flag { kNoFlag = 0u, kTypeFeedbackEnabled = 1u << 0 };
typedef base::Flags<Flag> Flags;
SimplifiedLowering(JSGraph* jsgraph, Zone* zone,
SourcePositionTable* source_positions);
SourcePositionTable* source_positions,
Flags flags = kNoFlag);
~SimplifiedLowering() {}
void LowerAllNodes();
......@@ -43,12 +47,15 @@ class SimplifiedLowering final {
void DoStoreBuffer(Node* node);
void DoShift(Node* node, Operator const* op, Type* rhs_type);
Flags flags() const { return flags_; }
private:
JSGraph* const jsgraph_;
Zone* const zone_;
TypeCache const& type_cache_;
SetOncePointer<Node> to_number_code_;
SetOncePointer<Operator const> to_number_operator_;
Flags flags_;
// TODO(danno): SimplifiedLowering shouldn't know anything about the source
// positions table, but must for now since there currently is no other way to
......
......@@ -177,6 +177,12 @@ Type* TypeOf(const Operator* op) {
return OpParameter<Type*>(op);
}
BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kSpeculativeNumberAdd ||
op->opcode() == IrOpcode::kSpeculativeNumberSubtract);
return OpParameter<BinaryOperationHints::Hint>(op);
}
#define PURE_OP_LIST(V) \
V(BooleanNot, Operator::kNoProperties, 1) \
V(BooleanToNumber, Operator::kNoProperties, 1) \
......@@ -227,6 +233,12 @@ Type* TypeOf(const Operator* op) {
V(StringLessThan, Operator::kNoProperties, 2) \
V(StringLessThanOrEqual, Operator::kNoProperties, 2)
#define CHECKED_OP_LIST(V) \
V(CheckedUint32ToInt32) \
V(CheckedFloat64ToInt32) \
V(CheckedTaggedToInt32) \
V(CheckedTaggedToFloat64)
struct SimplifiedOperatorGlobalCache final {
#define PURE(Name, properties, input_count) \
struct Name##Operator final : public Operator { \
......@@ -238,6 +250,16 @@ struct SimplifiedOperatorGlobalCache final {
PURE_OP_LIST(PURE)
#undef PURE
#define CHECKED(Name) \
struct Name##Operator final : public Operator { \
Name##Operator() \
: Operator(IrOpcode::k##Name, Operator::kNoThrow, #Name, 1, 1, 1, 1, \
1, 1) {} \
}; \
Name##Operator k##Name;
CHECKED_OP_LIST(CHECKED)
#undef CHECKED
template <PretenureFlag kPretenure>
struct AllocateOperator final : public Operator1<PretenureFlag> {
AllocateOperator()
......@@ -276,12 +298,15 @@ static base::LazyInstance<SimplifiedOperatorGlobalCache>::type kCache =
SimplifiedOperatorBuilder::SimplifiedOperatorBuilder(Zone* zone)
: cache_(kCache.Get()), zone_(zone) {}
#define GET_FROM_CACHE(Name, properties, input_count) \
const Operator* SimplifiedOperatorBuilder::Name() { return &cache_.k##Name; }
PURE_OP_LIST(GET_FROM_CACHE)
#undef GET_FROM_CACHE
#define GET_FROM_CACHE(Name) \
const Operator* SimplifiedOperatorBuilder::Name() { return &cache_.k##Name; }
CHECKED_OP_LIST(GET_FROM_CACHE)
#undef GET_FROM_CACHE
const Operator* SimplifiedOperatorBuilder::ReferenceEqual(Type* type) {
return new (zone()) Operator(IrOpcode::kReferenceEqual,
......@@ -343,6 +368,19 @@ const Operator* SimplifiedOperatorBuilder::StoreBuffer(BufferAccess access) {
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberAdd(
BinaryOperationHints::Hint hint) {
return new (zone()) Operator1<BinaryOperationHints::Hint>(
IrOpcode::kSpeculativeNumberAdd, Operator::kNoThrow,
"SpeculativeNumberAdd", 2, 1, 1, 1, 1, 1, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberSubtract(
BinaryOperationHints::Hint hint) {
return new (zone()) Operator1<BinaryOperationHints::Hint>(
IrOpcode::kSpeculativeNumberSubtract, Operator::kNoThrow,
"SpeculativeNumberSubtract", 2, 1, 1, 1, 1, 1, hint);
}
#define ACCESS_OP_LIST(V) \
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
......
......@@ -7,6 +7,7 @@
#include <iosfwd>
#include "src/compiler/type-hints.h"
#include "src/handles.h"
#include "src/machine-type.h"
#include "src/objects.h"
......@@ -104,6 +105,8 @@ ElementAccess const& ElementAccessOf(const Operator* op) WARN_UNUSED_RESULT;
Type* TypeOf(const Operator* op) WARN_UNUSED_RESULT;
BinaryOperationHints::Hint BinaryOperationHintOf(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.
......@@ -157,6 +160,9 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* NumberToUint32();
const Operator* NumberIsHoleNaN();
const Operator* SpeculativeNumberAdd(BinaryOperationHints::Hint hint);
const Operator* SpeculativeNumberSubtract(BinaryOperationHints::Hint hint);
const Operator* ReferenceEqual(Type* type);
const Operator* StringEqual();
......@@ -178,6 +184,11 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* TruncateTaggedToWord32();
const Operator* TruncateTaggedToFloat64();
const Operator* CheckedUint32ToInt32();
const Operator* CheckedFloat64ToInt32();
const Operator* CheckedTaggedToInt32();
const Operator* CheckedTaggedToFloat64();
const Operator* ObjectIsCallable();
const Operator* ObjectIsNumber();
const Operator* ObjectIsReceiver();
......
......@@ -20,7 +20,7 @@ BinaryOperationHints::Hint ToHint(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;
if (type->Is(Type::Number())) return BinaryOperationHints::kNumber;
if (type->Is(Type::Number())) return BinaryOperationHints::kNumberOrUndefined;
if (type->Is(Type::String())) return BinaryOperationHints::kString;
return BinaryOperationHints::kAny;
}
......
......@@ -16,8 +16,8 @@ std::ostream& operator<<(std::ostream& os, BinaryOperationHints::Hint hint) {
return os << "SignedSmall";
case BinaryOperationHints::kSigned32:
return os << "Signed32";
case BinaryOperationHints::kNumber:
return os << "Number";
case BinaryOperationHints::kNumberOrUndefined:
return os << "NumberOrUndefined";
case BinaryOperationHints::kString:
return os << "String";
case BinaryOperationHints::kAny:
......@@ -78,6 +78,34 @@ std::ostream& operator<<(std::ostream& os, ToBooleanHints hints) {
return os;
}
// static
bool BinaryOperationHints::Is(Hint h1, Hint h2) {
if (h1 == h2) return true;
switch (h1) {
case kNone:
return true;
case kSignedSmall:
return h2 == kSigned32 || h2 == kNumberOrUndefined || h2 == kAny;
case kSigned32:
return h2 == kNumberOrUndefined || h2 == kAny;
case kNumberOrUndefined:
return h2 == kAny;
case kString:
return h2 == kAny;
case kAny:
return false;
}
UNREACHABLE();
return false;
}
// static
BinaryOperationHints::Hint BinaryOperationHints::Combine(Hint h1, Hint h2) {
if (Is(h1, h2)) return h2;
if (Is(h2, h1)) return h1;
return kAny;
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -15,7 +15,14 @@ namespace compiler {
// Type hints for an binary operation.
class BinaryOperationHints final {
public:
enum Hint { kNone, kSignedSmall, kSigned32, kNumber, kString, kAny };
enum Hint {
kNone,
kSignedSmall,
kSigned32,
kNumberOrUndefined,
kString,
kAny
};
BinaryOperationHints() : BinaryOperationHints(kNone, kNone, kNone) {}
BinaryOperationHints(Hint left, Hint right, Hint result)
......@@ -29,6 +36,11 @@ class BinaryOperationHints final {
Hint left() const { return LeftField::decode(bit_field_); }
Hint right() const { return RightField::decode(bit_field_); }
Hint result() const { return ResultField::decode(bit_field_); }
Hint combined() const { return Combine(Combine(left(), right()), result()); }
// Hint 'subtyping' and generalization.
static bool Is(Hint h1, Hint h2);
static Hint Combine(Hint h1, Hint h2);
bool operator==(BinaryOperationHints const& that) const {
return this->bit_field_ == that.bit_field_;
......
......@@ -1702,7 +1702,6 @@ Type* Typer::Visitor::TypeJSGeneratorRestoreRegister(Node* node) {
Type* Typer::Visitor::TypeJSStackCheck(Node* node) { return Type::Any(); }
// Simplified operators.
Type* Typer::Visitor::TypeBooleanNot(Node* node) { return Type::Boolean(); }
......@@ -1723,6 +1722,14 @@ Type* Typer::Visitor::TypeNumberAdd(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeNumberSubtract(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeSpeculativeNumberAdd(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeSpeculativeNumberSubtract(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeNumberMultiply(Node* node) { return Type::Number(); }
Type* Typer::Visitor::TypeNumberDivide(Node* node) { return Type::Number(); }
......@@ -1919,6 +1926,22 @@ Type* Typer::Visitor::TypeChangeBitToTagged(Node* node) {
return ChangeRepresentation(arg, Type::TaggedPointer(), zone());
}
Type* Typer::Visitor::TypeCheckedUint32ToInt32(Node* node) {
return Type::Signed32();
}
Type* Typer::Visitor::TypeCheckedFloat64ToInt32(Node* node) {
return Type::Signed32();
}
Type* Typer::Visitor::TypeCheckedTaggedToInt32(Node* node) {
return Type::Signed32();
}
Type* Typer::Visitor::TypeCheckedTaggedToFloat64(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeTruncateTaggedToWord32(Node* node) {
Type* arg = Operand(node, 0);
// TODO(neis): DCHECK(arg->Is(Type::Number()));
......
......@@ -682,6 +682,9 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 1, Type::NumberOrUndefined());
CheckUpperIs(node, Type::Boolean());
break;
case IrOpcode::kSpeculativeNumberAdd:
case IrOpcode::kSpeculativeNumberSubtract:
break;
case IrOpcode::kNumberAdd:
case IrOpcode::kNumberSubtract:
case IrOpcode::kNumberMultiply:
......@@ -901,6 +904,12 @@ void Verifier::Visitor::Check(Node* node) {
break;
}
case IrOpcode::kCheckedUint32ToInt32:
case IrOpcode::kCheckedFloat64ToInt32:
case IrOpcode::kCheckedTaggedToInt32:
case IrOpcode::kCheckedTaggedToFloat64:
break;
case IrOpcode::kLoadField:
// Object -> fieldtype
// TODO(rossberg): activate once machine ops are typed.
......
......@@ -449,6 +449,8 @@ DEFINE_BOOL(turbo_asm_deoptimization, false,
DEFINE_BOOL(turbo_verify, DEBUG_BOOL, "verify TurboFan graphs at each phase")
DEFINE_BOOL(turbo_stats, false, "print TurboFan statistics")
DEFINE_BOOL(turbo_splitting, true, "split nodes during scheduling in TurboFan")
DEFINE_BOOL(turbo_type_feedback, false,
"use typed feedback for representation inference in Turbofan")
DEFINE_BOOL(turbo_source_positions, false,
"track source code positions when building TurboFan IR")
DEFINE_IMPLICATION(trace_turbo, turbo_source_positions)
......
......@@ -628,6 +628,8 @@
'compiler/node.h',
'compiler/opcodes.cc',
'compiler/opcodes.h',
'compiler/operation-typer.cc',
'compiler/operation-typer.h',
'compiler/operator-properties.cc',
'compiler/operator-properties.h',
'compiler/operator.cc',
......
// 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 --turbo-type-feedback
(function AddSubtractSmis() {
function f0(a, b, c) {
return a + b - c;
}
assertEquals(4, f0(3, 2, 1));
assertEquals(4, f0(3, 2, 1));
%OptimizeFunctionOnNextCall(f0);
assertEquals(4, f0(3, 2, 1));
})();
(function AddSubtractDoubles() {
function f1(a, b, c) {
return a + b - c;
}
assertEquals(4.5, f1(3.5, 2.5, 1.5));
assertEquals(4.5, f1(3.5, 2.5, 1.5));
%OptimizeFunctionOnNextCall(f1);
assertEquals(4.5, f1(3.5, 2.5, 1.5));
assertEquals(4, f1(3, 2, 1));
assertTrue(isNaN(f1(3, 2, undefined)));
assertTrue(isNaN(f1(3, undefined, 1)));
})();
(function CheckUint32ToInt32Conv() {
function f2(a) {
return (a >>> 0) + 1;
}
assertEquals(1, f2(0));
assertEquals(1, f2(0));
%OptimizeFunctionOnNextCall(f2);
assertEquals(1, f2(0));
assertEquals(4294967295, f2(-2));
})();
(function CheckFloat64ToInt32Conv() {
function f3(a, b) {
var x = 0;
if (a) {
x = 0.5;
}
return x + b;
}
assertEquals(1, f3(0, 1));
assertEquals(1, f3(0, 1));
%OptimizeFunctionOnNextCall(f3);
assertEquals(1, f3(0, 1));
assertEquals(1.5, f3(1, 1));
})();
......@@ -87,8 +87,9 @@ class JSTypedLoweringTest : public TypedGraphTest {
// TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(zone(), graph());
JSTypedLowering reducer(&graph_reducer, &deps_,
JSTypedLowering::kDeoptimizationEnabled, &jsgraph,
zone());
JSTypedLowering::kDeoptimizationEnabled |
JSTypedLowering::kTypeFeedbackEnabled,
&jsgraph, zone());
return reducer.Reduce(node);
}
......@@ -838,6 +839,52 @@ TEST_F(JSTypedLoweringTest, JSAddWithString) {
lhs, rhs, context, frame_state0, effect, control));
}
TEST_F(JSTypedLoweringTest, JSAddSmis) {
BinaryOperationHints const hints(BinaryOperationHints::kSignedSmall,
BinaryOperationHints::kSignedSmall,
BinaryOperationHints::kSignedSmall);
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Node* lhs = Parameter(Type::Number(), 0);
Node* rhs = Parameter(Type::Number(), 1);
Node* context = Parameter(Type::Any(), 2);
Node* frame_state0 = EmptyFrameState();
Node* frame_state1 = EmptyFrameState();
Node* effect = graph()->start();
Node* control = graph()->start();
Reduction r =
Reduce(graph()->NewNode(javascript()->Add(hints), lhs, rhs, context,
frame_state0, frame_state1, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsSpeculativeNumberAdd(BinaryOperationHints::kSignedSmall, lhs,
rhs, frame_state1, effect, control));
}
}
// -----------------------------------------------------------------------------
// JSSubtract
TEST_F(JSTypedLoweringTest, JSSubtractSmis) {
BinaryOperationHints const hints(BinaryOperationHints::kSignedSmall,
BinaryOperationHints::kSignedSmall,
BinaryOperationHints::kSignedSmall);
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
Node* lhs = Parameter(Type::Number(), 0);
Node* rhs = Parameter(Type::Number(), 1);
Node* context = Parameter(Type::Any(), 2);
Node* frame_state0 = EmptyFrameState();
Node* frame_state1 = EmptyFrameState();
Node* effect = graph()->start();
Node* control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->Subtract(hints), lhs,
rhs, context, frame_state0,
frame_state1, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsSpeculativeNumberSubtract(
BinaryOperationHints::kSignedSmall, lhs,
rhs, frame_state1, effect, control));
}
}
// -----------------------------------------------------------------------------
// JSInstanceOf
......
......@@ -800,6 +800,46 @@ class IsReferenceEqualMatcher final : public NodeMatcher {
const Matcher<Node*> rhs_matcher_;
};
class IsSpeculativeBinopMatcher final : public NodeMatcher {
public:
IsSpeculativeBinopMatcher(
IrOpcode::Value opcode,
const Matcher<BinaryOperationHints::Hint>& hint_matcher,
const Matcher<Node*>& lhs_matcher, const Matcher<Node*>& rhs_matcher,
const Matcher<Node*>& frame_state_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(opcode),
lhs_matcher_(lhs_matcher),
rhs_matcher_(rhs_matcher),
frame_state_matcher_(frame_state_matcher),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (NodeMatcher::MatchAndExplain(node, listener) &&
// TODO(bmeurer): The type parameter is currently ignored.
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "lhs",
lhs_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1), "rhs",
rhs_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetFrameStateInput(node, 0),
"frame state", frame_state_matcher_,
listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener));
}
private:
const Matcher<Type*> type_matcher_;
const Matcher<Node*> lhs_matcher_;
const Matcher<Node*> rhs_matcher_;
const Matcher<Node*> frame_state_matcher_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
class IsAllocateMatcher final : public NodeMatcher {
public:
......@@ -2029,6 +2069,27 @@ Matcher<Node*> IsReferenceEqual(const Matcher<Type*>& type_matcher,
new IsReferenceEqualMatcher(type_matcher, lhs_matcher, rhs_matcher));
}
Matcher<Node*> IsSpeculativeNumberAdd(
const Matcher<BinaryOperationHints::Hint>& hint_matcher,
const Matcher<Node*>& lhs_matcher, const Matcher<Node*>& rhs_matcher,
const Matcher<Node*>& frame_state_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsSpeculativeBinopMatcher(
IrOpcode::kSpeculativeNumberAdd, hint_matcher, lhs_matcher, rhs_matcher,
frame_state_matcher, effect_matcher, control_matcher));
}
Matcher<Node*> IsSpeculativeNumberSubtract(
const Matcher<BinaryOperationHints::Hint>& hint_matcher,
const Matcher<Node*>& lhs_matcher, const Matcher<Node*>& rhs_matcher,
const Matcher<Node*>& frame_state_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsSpeculativeBinopMatcher(
IrOpcode::kSpeculativeNumberSubtract, hint_matcher, lhs_matcher,
rhs_matcher, frame_state_matcher, effect_matcher, control_matcher));
}
Matcher<Node*> IsAllocate(const Matcher<Node*>& size_matcher,
const Matcher<Node*>& effect_matcher,
......
......@@ -6,6 +6,7 @@
#define V8_UNITTESTS_COMPILER_NODE_TEST_UTILS_H_
#include "src/compiler/machine-operator.h"
#include "src/compiler/type-hints.h"
#include "src/machine-type.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -199,6 +200,20 @@ Matcher<Node*> IsNumberEqual(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsNumberLessThan(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsNumberAdd(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsSpeculativeNumberAdd(
const Matcher<BinaryOperationHints::Hint>& hint_matcher,
const Matcher<Node*>& lhs_matcher, const Matcher<Node*>& rhs_matcher,
const Matcher<Node*>& frame_state_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsSpeculativeNumberSubtract(
const Matcher<BinaryOperationHints::Hint>& hint_matcher,
const Matcher<Node*>& lhs_matcher, const Matcher<Node*>& rhs_matcher,
const Matcher<Node*>& frame_state_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsNumberSubtract(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& rhs_matcher);
Matcher<Node*> IsNumberMultiply(const Matcher<Node*>& lhs_matcher,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment