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) \
......
// 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.
#include "src/compiler/operation-typer.h"
#include "src/factory.h"
#include "src/isolate.h"
#include "src/type-cache.h"
#include "src/types.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
OperationTyper::OperationTyper(Isolate* isolate, Zone* zone)
: zone_(zone), cache_(TypeCache::Get()) {
Factory* factory = isolate->factory();
singleton_false_ = Type::Constant(factory->false_value(), zone);
singleton_true_ = Type::Constant(factory->true_value(), zone);
singleton_the_hole_ = Type::Constant(factory->the_hole_value(), zone);
}
Type* OperationTyper::Merge(Type* left, Type* right) {
return Type::Union(left, right, zone());
}
Type* OperationTyper::WeakenRange(Type* previous_range, Type* current_range) {
static const double kWeakenMinLimits[] = {0.0,
-1073741824.0,
-2147483648.0,
-4294967296.0,
-8589934592.0,
-17179869184.0,
-34359738368.0,
-68719476736.0,
-137438953472.0,
-274877906944.0,
-549755813888.0,
-1099511627776.0,
-2199023255552.0,
-4398046511104.0,
-8796093022208.0,
-17592186044416.0,
-35184372088832.0,
-70368744177664.0,
-140737488355328.0,
-281474976710656.0,
-562949953421312.0};
static const double kWeakenMaxLimits[] = {0.0,
1073741823.0,
2147483647.0,
4294967295.0,
8589934591.0,
17179869183.0,
34359738367.0,
68719476735.0,
137438953471.0,
274877906943.0,
549755813887.0,
1099511627775.0,
2199023255551.0,
4398046511103.0,
8796093022207.0,
17592186044415.0,
35184372088831.0,
70368744177663.0,
140737488355327.0,
281474976710655.0,
562949953421311.0};
STATIC_ASSERT(arraysize(kWeakenMinLimits) == arraysize(kWeakenMaxLimits));
double current_min = current_range->Min();
double new_min = current_min;
// Find the closest lower entry in the list of allowed
// minima (or negative infinity if there is no such entry).
if (current_min != previous_range->Min()) {
new_min = -V8_INFINITY;
for (double const min : kWeakenMinLimits) {
if (min <= current_min) {
new_min = min;
break;
}
}
}
double current_max = current_range->Max();
double new_max = current_max;
// Find the closest greater entry in the list of allowed
// maxima (or infinity if there is no such entry).
if (current_max != previous_range->Max()) {
new_max = V8_INFINITY;
for (double const max : kWeakenMaxLimits) {
if (max >= current_max) {
new_max = max;
break;
}
}
}
return Type::Range(new_min, new_max, zone());
}
Type* OperationTyper::Rangify(Type* type) {
if (type->IsRange()) return type; // Shortcut.
if (!type->Is(cache_.kInteger)) {
return type; // Give up on non-integer types.
}
double min = type->Min();
double max = type->Max();
// Handle the degenerate case of empty bitset types (such as
// OtherUnsigned31 and OtherSigned32 on 64-bit architectures).
if (std::isnan(min)) {
DCHECK(std::isnan(max));
return type;
}
return Type::Range(min, max, zone());
}
namespace {
// Returns the array's least element, ignoring NaN.
// There must be at least one non-NaN element.
// Any -0 is converted to 0.
double array_min(double a[], size_t n) {
DCHECK(n != 0);
double x = +V8_INFINITY;
for (size_t i = 0; i < n; ++i) {
if (!std::isnan(a[i])) {
x = std::min(a[i], x);
}
}
DCHECK(!std::isnan(x));
return x == 0 ? 0 : x; // -0 -> 0
}
// Returns the array's greatest element, ignoring NaN.
// There must be at least one non-NaN element.
// Any -0 is converted to 0.
double array_max(double a[], size_t n) {
DCHECK(n != 0);
double x = -V8_INFINITY;
for (size_t i = 0; i < n; ++i) {
if (!std::isnan(a[i])) {
x = std::max(a[i], x);
}
}
DCHECK(!std::isnan(x));
return x == 0 ? 0 : x; // -0 -> 0
}
} // namespace
Type* OperationTyper::AddRanger(RangeType* lhs, RangeType* rhs) {
double results[4];
results[0] = lhs->Min() + rhs->Min();
results[1] = lhs->Min() + rhs->Max();
results[2] = lhs->Max() + rhs->Min();
results[3] = lhs->Max() + rhs->Max();
// Since none of the inputs can be -0, the result cannot be -0 either.
// However, it can be nan (the sum of two infinities of opposite sign).
// On the other hand, if none of the "results" above is nan, then the actual
// result cannot be nan either.
int nans = 0;
for (int i = 0; i < 4; ++i) {
if (std::isnan(results[i])) ++nans;
}
if (nans == 4) return Type::NaN(); // [-inf..-inf] + [inf..inf] or vice versa
Type* range =
Type::Range(array_min(results, 4), array_max(results, 4), zone());
return nans == 0 ? range : Type::Union(range, Type::NaN(), zone());
// Examples:
// [-inf, -inf] + [+inf, +inf] = NaN
// [-inf, -inf] + [n, +inf] = [-inf, -inf] \/ NaN
// [-inf, +inf] + [n, +inf] = [-inf, +inf] \/ NaN
// [-inf, m] + [n, +inf] = [-inf, +inf] \/ NaN
}
Type* OperationTyper::SubtractRanger(RangeType* lhs, RangeType* rhs) {
double results[4];
results[0] = lhs->Min() - rhs->Min();
results[1] = lhs->Min() - rhs->Max();
results[2] = lhs->Max() - rhs->Min();
results[3] = lhs->Max() - rhs->Max();
// Since none of the inputs can be -0, the result cannot be -0.
// However, it can be nan (the subtraction of two infinities of same sign).
// On the other hand, if none of the "results" above is nan, then the actual
// result cannot be nan either.
int nans = 0;
for (int i = 0; i < 4; ++i) {
if (std::isnan(results[i])) ++nans;
}
if (nans == 4) return Type::NaN(); // [inf..inf] - [inf..inf] (all same sign)
Type* range =
Type::Range(array_min(results, 4), array_max(results, 4), zone());
return nans == 0 ? range : Type::Union(range, Type::NaN(), zone());
// Examples:
// [-inf, +inf] - [-inf, +inf] = [-inf, +inf] \/ NaN
// [-inf, -inf] - [-inf, -inf] = NaN
// [-inf, -inf] - [n, +inf] = [-inf, -inf] \/ NaN
// [m, +inf] - [-inf, n] = [-inf, +inf] \/ NaN
}
Type* OperationTyper::ModulusRanger(RangeType* lhs, RangeType* rhs) {
double lmin = lhs->Min();
double lmax = lhs->Max();
double rmin = rhs->Min();
double rmax = rhs->Max();
double labs = std::max(std::abs(lmin), std::abs(lmax));
double rabs = std::max(std::abs(rmin), std::abs(rmax)) - 1;
double abs = std::min(labs, rabs);
bool maybe_minus_zero = false;
double omin = 0;
double omax = 0;
if (lmin >= 0) { // {lhs} positive.
omin = 0;
omax = abs;
} else if (lmax <= 0) { // {lhs} negative.
omin = 0 - abs;
omax = 0;
maybe_minus_zero = true;
} else {
omin = 0 - abs;
omax = abs;
maybe_minus_zero = true;
}
Type* result = Type::Range(omin, omax, zone());
if (maybe_minus_zero) result = Type::Union(result, Type::MinusZero(), zone());
return result;
}
Type* OperationTyper::ToNumber(Type* type) {
if (type->Is(Type::Number())) return type;
if (type->Is(Type::NullOrUndefined())) {
if (type->Is(Type::Null())) return cache_.kSingletonZero;
if (type->Is(Type::Undefined())) return Type::NaN();
return Type::Union(Type::NaN(), cache_.kSingletonZero, zone());
}
if (type->Is(Type::NumberOrUndefined())) {
return Type::Union(Type::Intersect(type, Type::Number(), zone()),
Type::NaN(), zone());
}
if (type->Is(singleton_false_)) return cache_.kSingletonZero;
if (type->Is(singleton_true_)) return cache_.kSingletonOne;
if (type->Is(Type::Boolean())) return cache_.kZeroOrOne;
if (type->Is(Type::BooleanOrNumber())) {
return Type::Union(Type::Intersect(type, Type::Number(), zone()),
cache_.kZeroOrOne, zone());
}
return Type::Number();
}
Type* OperationTyper::NumericAdd(Type* lhs, Type* rhs) {
DCHECK(lhs->Is(Type::Number()));
DCHECK(rhs->Is(Type::Number()));
lhs = Rangify(lhs);
rhs = Rangify(rhs);
if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
if (lhs->IsRange() && rhs->IsRange()) {
return AddRanger(lhs->AsRange(), rhs->AsRange());
}
// TODO(neis): Deal with numeric bitsets here and elsewhere.
return Type::Number();
}
Type* OperationTyper::NumericSubtract(Type* lhs, Type* rhs) {
DCHECK(lhs->Is(Type::Number()));
DCHECK(rhs->Is(Type::Number()));
lhs = Rangify(lhs);
rhs = Rangify(rhs);
if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
if (lhs->IsRange() && rhs->IsRange()) {
return SubtractRanger(lhs->AsRange(), rhs->AsRange());
}
// TODO(neis): Deal with numeric bitsets here and elsewhere.
return Type::Number();
}
Type* OperationTyper::ToPrimitive(Type* type) {
if (type->Is(Type::Primitive()) && !type->Maybe(Type::Receiver())) {
return type;
}
return Type::Primitive();
}
Type* OperationTyper::Invert(Type* type) {
DCHECK(type->Is(Type::Boolean()));
DCHECK(type->IsInhabited());
if (type->Is(singleton_false())) return singleton_true();
if (type->Is(singleton_true())) return singleton_false();
return type;
}
OperationTyper::ComparisonOutcome OperationTyper::Invert(
ComparisonOutcome outcome) {
ComparisonOutcome result(0);
if ((outcome & kComparisonUndefined) != 0) result |= kComparisonUndefined;
if ((outcome & kComparisonTrue) != 0) result |= kComparisonFalse;
if ((outcome & kComparisonFalse) != 0) result |= kComparisonTrue;
return result;
}
Type* OperationTyper::FalsifyUndefined(ComparisonOutcome outcome) {
if ((outcome & kComparisonFalse) != 0 ||
(outcome & kComparisonUndefined) != 0) {
return (outcome & kComparisonTrue) != 0 ? Type::Boolean()
: singleton_false();
}
// Type should be non empty, so we know it should be true.
DCHECK((outcome & kComparisonTrue) != 0);
return singleton_true();
}
Type* OperationTyper::TypeJSAdd(Type* lhs, Type* rhs) {
lhs = ToPrimitive(lhs);
rhs = ToPrimitive(rhs);
if (lhs->Maybe(Type::String()) || rhs->Maybe(Type::String())) {
if (lhs->Is(Type::String()) || rhs->Is(Type::String())) {
return Type::String();
} else {
return Type::NumberOrString();
}
}
lhs = ToNumber(lhs);
rhs = ToNumber(rhs);
return NumericAdd(lhs, rhs);
}
Type* OperationTyper::TypeJSSubtract(Type* lhs, Type* rhs) {
return NumericSubtract(ToNumber(lhs), ToNumber(rhs));
}
} // namespace compiler
} // namespace internal
} // namespace v8
// 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
......
......@@ -105,46 +105,55 @@ bool IsWord(MachineRepresentation rep) {
} // namespace
// Changes representation from {output_rep} to {use_rep}. The {truncation}
// parameter is only used for sanity checking - if the changer cannot figure
// out signedness for the word32->float64 conversion, then we check that the
// uses truncate to word32 (so they do not care about signedness).
Node* RepresentationChanger::GetRepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type,
MachineRepresentation use_rep, Truncation truncation) {
Node* use_node, UseInfo use_info) {
if (output_rep == MachineRepresentation::kNone) {
// The output representation should be set.
return TypeError(node, output_rep, output_type, use_rep);
}
if (use_rep == output_rep) {
// Representations are the same. That's a no-op.
return node;
return TypeError(node, output_rep, output_type, use_info.representation());
}
if (IsWord(use_rep) && IsWord(output_rep)) {
// Both are words less than or equal to 32-bits.
// Since loads of integers from memory implicitly sign or zero extend the
// value to the full machine word size and stores implicitly truncate,
// no representation change is necessary.
return node;
// Handle the no-op shortcuts when no checking is necessary.
if (use_info.type_check() == TypeCheckKind::kNone ||
output_rep != MachineRepresentation::kWord32) {
if (use_info.representation() == output_rep) {
// Representations are the same. That's a no-op.
return node;
}
if (IsWord(use_info.representation()) && IsWord(output_rep)) {
// Both are words less than or equal to 32-bits.
// Since loads of integers from memory implicitly sign or zero extend the
// value to the full machine word size and stores implicitly truncate,
// no representation change is necessary.
return node;
}
}
switch (use_rep) {
switch (use_info.representation()) {
case MachineRepresentation::kTagged:
DCHECK(use_info.type_check() == TypeCheckKind::kNone);
return GetTaggedRepresentationFor(node, output_rep, output_type);
case MachineRepresentation::kFloat32:
DCHECK(use_info.type_check() == TypeCheckKind::kNone);
return GetFloat32RepresentationFor(node, output_rep, output_type,
truncation);
use_info.truncation());
case MachineRepresentation::kFloat64:
return GetFloat64RepresentationFor(node, output_rep, output_type,
truncation);
use_node, use_info);
case MachineRepresentation::kBit:
DCHECK(use_info.type_check() == TypeCheckKind::kNone);
return GetBitRepresentationFor(node, output_rep, output_type);
case MachineRepresentation::kWord8:
case MachineRepresentation::kWord16:
case MachineRepresentation::kWord32:
return GetWord32RepresentationFor(node, output_rep, output_type,
truncation);
return GetWord32RepresentationFor(node, output_rep, output_type, use_node,
use_info);
case MachineRepresentation::kWord64:
DCHECK(use_info.type_check() == TypeCheckKind::kNone);
return GetWord64RepresentationFor(node, output_rep, output_type);
case MachineRepresentation::kSimd128: // Fall through.
// TODO(bbudge) Handle conversions between tagged and untagged.
......@@ -156,7 +165,6 @@ Node* RepresentationChanger::GetRepresentationFor(
return nullptr;
}
Node* RepresentationChanger::GetTaggedRepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type) {
// Eagerly fold representation changes for constants.
......@@ -290,29 +298,31 @@ Node* RepresentationChanger::GetFloat32RepresentationFor(
return jsgraph()->graph()->NewNode(op, node);
}
Node* RepresentationChanger::GetFloat64RepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type,
Truncation truncation) {
Node* use_node, UseInfo use_info) {
// Eagerly fold representation changes for constants.
switch (node->opcode()) {
case IrOpcode::kNumberConstant:
return jsgraph()->Float64Constant(OpParameter<double>(node));
case IrOpcode::kInt32Constant:
if (output_type->Is(Type::Signed32())) {
int32_t value = OpParameter<int32_t>(node);
return jsgraph()->Float64Constant(value);
} else {
DCHECK(output_type->Is(Type::Unsigned32()));
uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
return jsgraph()->Float64Constant(static_cast<double>(value));
}
case IrOpcode::kFloat64Constant:
return node; // No change necessary.
case IrOpcode::kFloat32Constant:
return jsgraph()->Float64Constant(OpParameter<float>(node));
default:
break;
if ((use_info.type_check() == TypeCheckKind::kNone)) {
// TODO(jarin) Handle checked constant conversions.
switch (node->opcode()) {
case IrOpcode::kNumberConstant:
return jsgraph()->Float64Constant(OpParameter<double>(node));
case IrOpcode::kInt32Constant:
if (output_type->Is(Type::Signed32())) {
int32_t value = OpParameter<int32_t>(node);
return jsgraph()->Float64Constant(value);
} else {
DCHECK(output_type->Is(Type::Unsigned32()));
uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
return jsgraph()->Float64Constant(static_cast<double>(value));
}
case IrOpcode::kFloat64Constant:
return node; // No change necessary.
case IrOpcode::kFloat32Constant:
return jsgraph()->Float64Constant(OpParameter<float>(node));
default:
break;
}
}
// Select the correct X -> Float64 operator.
const Operator* op = nullptr;
......@@ -320,7 +330,7 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
if (output_type->Is(Type::Signed32())) {
op = machine()->ChangeInt32ToFloat64();
} else if (output_type->Is(Type::Unsigned32()) ||
truncation.TruncatesToWord32()) {
use_info.truncation().TruncatesToWord32()) {
// Either the output is uint32 or the uses only care about the
// low 32 bits (so we can pick uint32 safely).
op = machine()->ChangeUint32ToFloat64();
......@@ -335,7 +345,10 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
} else if (output_type->Is(Type::Number())) {
op = simplified()->ChangeTaggedToFloat64();
} else if (output_type->Is(Type::NumberOrUndefined())) {
// TODO(jarin) Here we should check that truncation is Number.
op = simplified()->TruncateTaggedToFloat64();
} else if (use_info.type_check() == TypeCheckKind::kNumberOrUndefined) {
op = simplified()->CheckedTaggedToFloat64();
}
} else if (output_rep == MachineRepresentation::kFloat32) {
op = machine()->ChangeFloat32ToFloat64();
......@@ -344,29 +357,32 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
return TypeError(node, output_rep, output_type,
MachineRepresentation::kFloat64);
}
return jsgraph()->graph()->NewNode(op, node);
return InsertConversion(node, op, use_node);
}
Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) {
return jsgraph()->Int32Constant(DoubleToInt32(value));
}
Node* RepresentationChanger::GetWord32RepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type,
Truncation truncation) {
Node* use_node, UseInfo use_info) {
// Eagerly fold representation changes for constants.
switch (node->opcode()) {
case IrOpcode::kInt32Constant:
return node; // No change necessary.
case IrOpcode::kFloat32Constant:
return MakeTruncatedInt32Constant(OpParameter<float>(node));
case IrOpcode::kNumberConstant:
case IrOpcode::kFloat64Constant:
return MakeTruncatedInt32Constant(OpParameter<double>(node));
default:
break;
// TODO(jarin) Properly fold constants in presence of type check.
if (use_info.type_check() == TypeCheckKind::kNone) {
switch (node->opcode()) {
case IrOpcode::kInt32Constant:
return node; // No change necessary.
case IrOpcode::kFloat32Constant:
return MakeTruncatedInt32Constant(OpParameter<float>(node));
case IrOpcode::kNumberConstant:
case IrOpcode::kFloat64Constant:
return MakeTruncatedInt32Constant(OpParameter<double>(node));
default:
break;
}
}
// Select the correct X -> Word32 operator.
const Operator* op = nullptr;
if (output_rep == MachineRepresentation::kBit) {
......@@ -376,8 +392,10 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
op = machine()->ChangeFloat64ToUint32();
} else if (output_type->Is(Type::Signed32())) {
op = machine()->ChangeFloat64ToInt32();
} else if (truncation.TruncatesToWord32()) {
} else if (use_info.truncation().TruncatesToWord32()) {
op = machine()->TruncateFloat64ToWord32();
} else if (use_info.type_check() == TypeCheckKind::kSigned32) {
op = simplified()->CheckedFloat64ToInt32();
}
} else if (output_rep == MachineRepresentation::kFloat32) {
node = InsertChangeFloat32ToFloat64(node); // float32 -> float64 -> int32
......@@ -385,8 +403,10 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
op = machine()->ChangeFloat64ToUint32();
} else if (output_type->Is(Type::Signed32())) {
op = machine()->ChangeFloat64ToInt32();
} else if (truncation.TruncatesToWord32()) {
} else if (use_info.truncation().TruncatesToWord32()) {
op = machine()->TruncateFloat64ToWord32();
} else if (use_info.type_check() == TypeCheckKind::kSigned32) {
op = simplified()->CheckedFloat64ToInt32();
}
} else if (output_rep == MachineRepresentation::kTagged) {
if (output_type->Is(Type::TaggedSigned())) {
......@@ -395,14 +415,49 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
op = simplified()->ChangeTaggedToUint32();
} else if (output_type->Is(Type::Signed32())) {
op = simplified()->ChangeTaggedToInt32();
} else if (truncation.TruncatesToWord32()) {
} else if (use_info.truncation().TruncatesToWord32()) {
op = simplified()->TruncateTaggedToWord32();
} else if (use_info.type_check() == TypeCheckKind::kSigned32) {
op = simplified()->CheckedTaggedToInt32();
}
} else if (output_rep == MachineRepresentation::kWord32) {
// Only the checked case should get here, the non-checked case is
// handled in GetRepresentationFor.
DCHECK(use_info.type_check() == TypeCheckKind::kSigned32);
if (output_type->Is(Type::Signed32())) {
return node;
} else if (output_type->Is(Type::Unsigned32())) {
op = simplified()->CheckedUint32ToInt32();
}
} else if (output_rep == MachineRepresentation::kWord8 ||
output_rep == MachineRepresentation::kWord16) {
DCHECK(use_info.representation() == MachineRepresentation::kWord32);
DCHECK(use_info.type_check() == TypeCheckKind::kSigned32);
return node;
}
if (op == nullptr) {
return TypeError(node, output_rep, output_type,
MachineRepresentation::kWord32);
}
return InsertConversion(node, op, use_node);
}
Node* RepresentationChanger::InsertConversion(Node* node, const Operator* op,
Node* use_node) {
if (op->ControlInputCount() > 0) {
// If the operator can deoptimize (which means it has control
// input), we need to connect it to the effect and control chains
// and also provide it with a frame state.
Node* effect = NodeProperties::GetEffectInput(use_node);
Node* control = NodeProperties::GetControlInput(use_node);
Node* frame_state = NodeProperties::GetFrameStateInput(use_node, 0);
Node* conversion =
jsgraph()->graph()->NewNode(op, node, frame_state, effect, control);
NodeProperties::ReplaceControlInput(use_node, control);
NodeProperties::ReplaceEffectInput(use_node, effect);
return conversion;
}
return jsgraph()->graph()->NewNode(op, node);
}
......@@ -432,7 +487,6 @@ Node* RepresentationChanger::GetBitRepresentationFor(
return jsgraph()->graph()->NewNode(op, node);
}
Node* RepresentationChanger::GetWord64RepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type) {
if (output_rep == MachineRepresentation::kBit) {
......@@ -443,12 +497,82 @@ Node* RepresentationChanger::GetWord64RepresentationFor(
MachineRepresentation::kWord64);
}
Node* RepresentationChanger::GetCheckedWord32RepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type,
Node* use_node, Truncation truncation, TypeCheckKind check) {
// TODO(jarin) Eagerly fold constants (or insert hard deopt if the constant
// does not pass the check).
// If the input is already Signed32 in Word32 representation, we do not
// have to do anything. (We could fold this into the big if below, but
// it feels nicer to have the shortcut return first).
if (output_rep == MachineRepresentation::kWord32 ||
output_type->Is(Type::Signed32())) {
return node;
}
// Select the correct X -> Word32 operator.
const Operator* op = nullptr;
if (output_rep == MachineRepresentation::kWord32) {
if (output_type->Is(Type::Unsigned32())) {
op = simplified()->CheckedUint32ToInt32();
}
} else if (output_rep == MachineRepresentation::kBit) {
return node; // Sloppy comparison -> word32
} else if (output_rep == MachineRepresentation::kFloat64) {
if (output_type->Is(Type::Unsigned32())) {
op = machine()->ChangeFloat64ToUint32();
} else if (output_type->Is(Type::Signed32())) {
op = machine()->ChangeFloat64ToInt32();
} else if (truncation.TruncatesToWord32()) {
op = machine()->TruncateFloat64ToWord32();
} else if (check == TypeCheckKind::kSigned32) {
op = simplified()->CheckedFloat64ToInt32();
}
} else if (output_rep == MachineRepresentation::kFloat32) {
node = InsertChangeFloat32ToFloat64(node); // float32 -> float64 -> int32
if (output_type->Is(Type::Unsigned32())) {
op = machine()->ChangeFloat64ToUint32();
} else if (output_type->Is(Type::Signed32())) {
op = machine()->ChangeFloat64ToInt32();
} else if (truncation.TruncatesToWord32()) {
op = machine()->TruncateFloat64ToWord32();
} else if (check == TypeCheckKind::kSigned32) {
op = simplified()->CheckedFloat64ToInt32();
}
} else if (output_rep == MachineRepresentation::kTagged) {
if (output_type->Is(Type::TaggedSigned())) {
op = simplified()->ChangeTaggedSignedToInt32();
} else if (output_type->Is(Type::Unsigned32())) {
op = simplified()->ChangeTaggedToUint32();
} else if (output_type->Is(Type::Signed32())) {
op = simplified()->ChangeTaggedToInt32();
} else if (truncation.TruncatesToWord32()) {
op = simplified()->TruncateTaggedToWord32();
} else if (check == TypeCheckKind::kSigned32) {
op = simplified()->CheckedTaggedToInt32();
}
}
if (op == nullptr) {
return TypeError(node, output_rep, output_type,
MachineRepresentation::kWord32);
}
if (op->ControlInputCount() > 0) {
// If the operator can deoptimize (which means it has control
// input), we need to connect it to the effect and control chains
// and also provide it with a frame state.
UNIMPLEMENTED();
}
return jsgraph()->graph()->NewNode(op, node);
}
const Operator* RepresentationChanger::Int32OperatorFor(
IrOpcode::Value opcode) {
switch (opcode) {
case IrOpcode::kSpeculativeNumberAdd: // Fall through.
case IrOpcode::kNumberAdd:
return machine()->Int32Add();
case IrOpcode::kSpeculativeNumberSubtract: // Fall through.
case IrOpcode::kNumberSubtract:
return machine()->Int32Sub();
case IrOpcode::kNumberMultiply:
......@@ -475,6 +599,18 @@ const Operator* RepresentationChanger::Int32OperatorFor(
}
}
const Operator* RepresentationChanger::Int32OverflowOperatorFor(
IrOpcode::Value opcode) {
switch (opcode) {
case IrOpcode::kSpeculativeNumberAdd: // Fall through.
return machine()->Int32AddWithOverflow();
case IrOpcode::kSpeculativeNumberSubtract: // Fall through.
return machine()->Int32SubWithOverflow();
default:
UNREACHABLE();
return nullptr;
}
}
const Operator* RepresentationChanger::Uint32OperatorFor(
IrOpcode::Value opcode) {
......@@ -509,8 +645,10 @@ const Operator* RepresentationChanger::Uint32OperatorFor(
const Operator* RepresentationChanger::Float64OperatorFor(
IrOpcode::Value opcode) {
switch (opcode) {
case IrOpcode::kSpeculativeNumberAdd:
case IrOpcode::kNumberAdd:
return machine()->Float64Add();
case IrOpcode::kSpeculativeNumberSubtract:
case IrOpcode::kNumberSubtract:
return machine()->Float64Sub();
case IrOpcode::kNumberMultiply:
......
......@@ -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(); }
......
......@@ -15,6 +15,7 @@
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operation-typer.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/representation-change.h"
#include "src/compiler/simplified-operator.h"
......@@ -62,63 +63,6 @@ enum Phase {
namespace {
// 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 preferred, Truncation truncation)
: preferred_(preferred), truncation_(truncation) {}
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());
}
// Undetermined representation.
static UseInfo Any() {
return UseInfo(MachineRepresentation::kNone, Truncation::Any());
}
static UseInfo None() {
return UseInfo(MachineRepresentation::kNone, Truncation::None());
}
static UseInfo AnyTruncatingToBool() {
return UseInfo(MachineRepresentation::kNone, Truncation::Bool());
}
MachineRepresentation preferred() const { return preferred_; }
Truncation truncation() const { return truncation_; }
private:
MachineRepresentation preferred_;
Truncation truncation_;
};
UseInfo TruncatingUseInfoFromRepresentation(MachineRepresentation rep) {
switch (rep) {
......@@ -223,7 +167,8 @@ class InputUseInfos {
ZoneVector<UseInfo> input_use_infos_;
static bool IsUseLessGeneral(UseInfo use1, UseInfo use2) {
return MachineRepresentationIsSubtype(use1.preferred(), use2.preferred()) &&
return MachineRepresentationIsSubtype(use1.representation(),
use2.representation()) &&
use1.truncation().IsLessGeneralThan(use2.truncation());
}
};
......@@ -246,27 +191,43 @@ class RepresentationSelector {
return truncation_ != old_truncation;
}
void set_queued(bool value) { queued_ = value; }
bool queued() const { return queued_; }
void set_visited() { visited_ = true; }
bool visited() const { return visited_; }
void set_queued() { state_ = kQueued; }
void set_visited() { state_ = kVisited; }
void set_pushed() { state_ = kPushed; }
void reset_state() { state_ = kUnvisited; }
bool visited() const { return state_ == kVisited; }
bool queued() const { return state_ == kQueued; }
bool unvisited() const { return state_ == kUnvisited; }
Truncation truncation() const { return truncation_; }
void set_output(MachineRepresentation output) { representation_ = output; }
MachineRepresentation representation() const { return representation_; }
// Helpers for feedback typing.
void set_feedback_type(Type* type) { feedback_type_ = type; }
Type* feedback_type() { return feedback_type_; }
void set_weakened() { weakened_ = true; }
bool weakened() { return weakened_; }
TypeCheckKind type_check() { return type_check_; }
void set_type_check(TypeCheckKind type_check) { type_check_ = type_check; }
private:
bool queued_ = false; // Bookkeeping for the traversal.
bool visited_ = false; // Bookkeeping for the traversal.
enum State : uint8_t { kUnvisited, kPushed, kVisited, kQueued };
State state_ = kUnvisited;
MachineRepresentation representation_ =
MachineRepresentation::kNone; // Output representation.
Truncation truncation_ = Truncation::None(); // Information about uses.
TypeCheckKind type_check_ = TypeCheckKind::kNone; // Runtime check kind.
Type* feedback_type_ = nullptr;
bool weakened_ = false;
};
RepresentationSelector(JSGraph* jsgraph, Zone* zone,
RepresentationChanger* changer,
SourcePositionTable* source_positions)
: jsgraph_(jsgraph),
zone_(zone),
count_(jsgraph->graph()->NodeCount()),
info_(count_, zone),
#ifdef DEBUG
......@@ -277,11 +238,260 @@ class RepresentationSelector {
phase_(PROPAGATE),
changer_(changer),
queue_(zone),
typing_stack_(zone),
source_positions_(source_positions),
type_cache_(TypeCache::Get()) {
type_cache_(TypeCache::Get()),
op_typer_(jsgraph->isolate(), graph_zone()) {
}
void Run(SimplifiedLowering* lowering) {
// Forward propagation of types from type feedback.
void RunTypePropagationPhase() {
DCHECK(typing_stack_.empty());
typing_stack_.push({graph()->end(), 0});
GetInfo(graph()->end())->set_pushed();
while (!typing_stack_.empty()) {
NodeState& current = typing_stack_.top();
// If there is an unvisited input, push it and continue.
bool pushed_unvisited = false;
while (current.input_index < current.node->InputCount()) {
Node* input = current.node->InputAt(current.input_index);
NodeInfo* input_info = GetInfo(input);
current.input_index++;
if (input_info->unvisited()) {
input_info->set_pushed();
typing_stack_.push({input, 0});
pushed_unvisited = true;
break;
}
}
if (pushed_unvisited) continue;
// Process the top of the stack.
Node* node = current.node;
typing_stack_.pop();
NodeInfo* info = GetInfo(node);
info->set_visited();
bool updated = UpdateFeedbackType(node);
if (updated) {
for (Node* const user : node->uses()) {
if (GetInfo(user)->visited()) {
GetInfo(user)->set_queued();
queue_.push(user);
}
}
}
}
// Process the revisit queue.
while (!queue_.empty()) {
Node* node = queue_.front();
queue_.pop();
NodeInfo* info = GetInfo(node);
info->set_visited();
bool updated = UpdateFeedbackType(node);
if (updated) {
for (Node* const user : node->uses()) {
if (GetInfo(user)->visited()) {
GetInfo(user)->set_queued();
queue_.push(user);
}
}
}
}
}
void ResetNodeInfoState() {
// Clean up for the next phase.
for (NodeInfo& info : info_) {
info.reset_state();
}
}
Type* TypeOf(Node* node) {
Type* type = GetInfo(node)->feedback_type();
return type == nullptr ? NodeProperties::GetType(node) : type;
}
Type* FeedbackTypeOf(Node* node) {
Type* type = GetInfo(node)->feedback_type();
return type == nullptr ? Type::None() : type;
}
Type* TypePhi(Node* node) {
int arity = node->op()->ValueInputCount();
Type* type = FeedbackTypeOf(node->InputAt(0));
for (int i = 1; i < arity; ++i) {
type = op_typer_.Merge(type, FeedbackTypeOf(node->InputAt(i)));
}
return type;
}
Type* TypeSelect(Node* node) {
return op_typer_.Merge(FeedbackTypeOf(node->InputAt(1)),
FeedbackTypeOf(node->InputAt(2)));
}
static Type* TypeOfSpeculativeOp(TypeCheckKind type_check) {
switch (type_check) {
case TypeCheckKind::kNone:
return Type::Any();
case TypeCheckKind::kSigned32:
return Type::Signed32();
case TypeCheckKind::kNumber:
return Type::Number();
// Unexpected cases.
case TypeCheckKind::kNumberOrUndefined:
FATAL("Unexpected checked type.");
break;
}
UNREACHABLE();
return nullptr;
}
bool UpdateFeedbackType(Node* node) {
if (node->op()->ValueOutputCount() == 0) return false;
NodeInfo* info = GetInfo(node);
Type* type = info->feedback_type();
Type* new_type = type;
switch (node->opcode()) {
case IrOpcode::kSpeculativeNumberAdd: {
Type* lhs = FeedbackTypeOf(node->InputAt(0));
Type* rhs = FeedbackTypeOf(node->InputAt(1));
if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false;
// TODO(jarin) The ToNumber conversion is too conservative here,
// e.g. it will treat true as 1 even though the number check will
// fail on a boolean. OperationTyper should have a function that
// computes a more precise type.
lhs = op_typer_.ToNumber(lhs);
rhs = op_typer_.ToNumber(rhs);
Type* static_type = op_typer_.NumericAdd(lhs, rhs);
if (info->type_check() == TypeCheckKind::kNone) {
new_type = static_type;
} else {
Type* feedback_type = TypeOfSpeculativeOp(info->type_check());
new_type = Type::Intersect(static_type, feedback_type, graph_zone());
}
break;
}
case IrOpcode::kSpeculativeNumberSubtract: {
Type* lhs = FeedbackTypeOf(node->InputAt(0));
Type* rhs = FeedbackTypeOf(node->InputAt(1));
if (lhs->Is(Type::None()) || rhs->Is(Type::None())) return false;
// TODO(jarin) The ToNumber conversion is too conservative here,
// e.g. it will treat true as 1 even though the number check will
// fail on a boolean. OperationTyper should have a function that
// computes a more precise type.
lhs = op_typer_.ToNumber(lhs);
rhs = op_typer_.ToNumber(rhs);
Type* static_type = op_typer_.NumericSubtract(lhs, rhs);
if (info->type_check() == TypeCheckKind::kNone) {
new_type = static_type;
} else {
Type* feedback_type = TypeOfSpeculativeOp(info->type_check());
new_type = Type::Intersect(static_type, feedback_type, graph_zone());
}
break;
}
case IrOpcode::kPhi: {
new_type = TypePhi(node);
if (type != nullptr) {
new_type = Weaken(node, type, new_type);
}
// Recompute the phi representation based on the new type.
MachineRepresentation output =
GetOutputInfoForPhi(node, GetInfo(node)->truncation(), new_type);
ResetOutput(node, output);
break;
}
case IrOpcode::kSelect: {
new_type = TypeSelect(node);
// Recompute representation based on the new type.
MachineRepresentation output =
GetOutputInfoForPhi(node, GetInfo(node)->truncation(), new_type);
ResetOutput(node, output);
break;
}
default:
// Shortcut for operations that we do not handle.
if (type == nullptr) {
GetInfo(node)->set_feedback_type(NodeProperties::GetType(node));
return true;
}
return false;
}
if (type != nullptr && new_type->Is(type)) return false;
GetInfo(node)->set_feedback_type(new_type);
if (FLAG_trace_representation) {
PrintNodeFeedbackType(node);
}
return true;
}
void PrintNodeFeedbackType(Node* n) {
OFStream os(stdout);
os << "#" << n->id() << ":" << *n->op() << "(";
int j = 0;
for (Node* const i : n->inputs()) {
if (j++ > 0) os << ", ";
os << "#" << i->id() << ":" << i->op()->mnemonic();
}
os << ")";
if (NodeProperties::IsTyped(n)) {
os << " [Static type: ";
Type* static_type = NodeProperties::GetType(n);
static_type->PrintTo(os);
Type* feedback_type = GetInfo(n)->feedback_type();
if (feedback_type != nullptr && feedback_type != static_type) {
os << ", Feedback type: ";
feedback_type->PrintTo(os);
}
os << "]";
}
os << std::endl;
}
Type* Weaken(Node* node, Type* previous_type, Type* current_type) {
// If the types have nothing to do with integers, return the types.
Type* const integer = type_cache_.kInteger;
if (!previous_type->Maybe(integer)) {
return current_type;
}
DCHECK(current_type->Maybe(integer));
Type* current_integer =
Type::Intersect(current_type, integer, graph_zone());
Type* previous_integer =
Type::Intersect(previous_type, integer, graph_zone());
// Once we start weakening a node, we should always weaken.
if (!GetInfo(node)->weakened()) {
// Only weaken if there is range involved; we should converge quickly
// for all other types (the exception is a union of many constants,
// but we currently do not increase the number of constants in unions).
Type* previous = previous_integer->GetRange();
Type* current = current_integer->GetRange();
if (current == nullptr || previous == nullptr) {
return current_type;
}
// Range is involved => we are weakening.
GetInfo(node)->set_weakened();
}
return Type::Union(current_type,
op_typer_.WeakenRange(previous_integer, current_integer),
graph_zone());
}
// Backward propagation of truncations.
void RunTruncationPropagationPhase() {
// Run propagation phase to a fixpoint.
TRACE("--{Propagation phase}--\n");
phase_ = PROPAGATE;
......@@ -291,13 +501,22 @@ class RepresentationSelector {
Node* node = queue_.front();
NodeInfo* info = GetInfo(node);
queue_.pop();
info->set_queued(false);
info->set_visited();
TRACE(" visit #%d: %s\n", node->id(), node->op()->mnemonic());
VisitNode(node, info->truncation(), nullptr);
TRACE(" ==> output ");
PrintOutputInfo(info);
TRACE("\n");
}
}
void Run(SimplifiedLowering* lowering) {
RunTruncationPropagationPhase();
if (lowering->flags() & SimplifiedLowering::kTypeFeedbackEnabled) {
ResetNodeInfoState();
RunTypePropagationPhase();
}
// Run lowering and change insertion phase.
TRACE("--{Simplified lowering phase}--\n");
......@@ -319,6 +538,7 @@ class RepresentationSelector {
Node* node = *i;
Node* replacement = *(++i);
node->ReplaceUses(replacement);
node->Kill();
// We also need to replace the node in the rest of the vector.
for (NodeVector::iterator j = i + 1; j != replacements_.end(); ++j) {
++j;
......@@ -329,8 +549,7 @@ class RepresentationSelector {
void EnqueueInitial(Node* node) {
NodeInfo* info = GetInfo(node);
info->set_visited();
info->set_queued(true);
info->set_queued();
nodes_.push_back(node);
queue_.push(node);
}
......@@ -348,10 +567,9 @@ class RepresentationSelector {
node_input_use_infos_[use_node->id()].SetAndCheckInput(use_node, index,
use_info);
#endif // DEBUG
if (!info->visited()) {
if (info->unvisited()) {
// First visit of this node.
info->set_visited();
info->set_queued(true);
info->set_queued();
nodes_.push_back(node);
queue_.push(node);
TRACE(" initial: ");
......@@ -365,7 +583,7 @@ class RepresentationSelector {
// New usage information for the node is available.
if (!info->queued()) {
queue_.push(node);
info->set_queued(true);
info->set_queued();
TRACE(" added: ");
} else {
TRACE(" inqueue: ");
......@@ -375,30 +593,20 @@ class RepresentationSelector {
}
bool lower() { return phase_ == LOWER; }
bool propagate() { return phase_ == PROPAGATE; }
void EnqueueUses(Node* node) {
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsValueEdge(edge)) {
Node* const user = edge.from();
if (user->id() < count_) {
// New type information for the node is available.
NodeInfo* info = GetInfo(user);
// Enqueue the node only if we are sure it is reachable from
// the end and it has not been queued yet.
if (info->visited() && !info->queued()) {
queue_.push(user);
info->set_queued(true);
}
}
}
}
void SetOutput(Node* node, MachineRepresentation representation,
TypeCheckKind type_check = TypeCheckKind::kNone) {
DCHECK(MachineRepresentationIsSubtype(GetInfo(node)->representation(),
representation));
ResetOutput(node, representation, type_check);
}
void SetOutput(Node* node, MachineRepresentation representation) {
void ResetOutput(Node* node, MachineRepresentation representation,
TypeCheckKind type_check = TypeCheckKind::kNone) {
NodeInfo* info = GetInfo(node);
DCHECK(
MachineRepresentationIsSubtype(info->representation(), representation));
info->set_output(representation);
info->set_type_check(type_check);
}
Type* GetUpperBound(Node* node) { return NodeProperties::GetType(node); }
......@@ -416,7 +624,7 @@ class RepresentationSelector {
}
bool BothInputsAre(Node* node, Type* type) {
DCHECK_EQ(2, node->InputCount());
DCHECK_EQ(2, node->op()->ValueInputCount());
return GetUpperBound(node->InputAt(0))->Is(type) &&
GetUpperBound(node->InputAt(1))->Is(type);
}
......@@ -424,11 +632,12 @@ class RepresentationSelector {
void ConvertInput(Node* node, int index, UseInfo use) {
Node* input = node->InputAt(index);
// In the change phase, insert a change before the use if necessary.
if (use.preferred() == MachineRepresentation::kNone)
if (use.representation() == MachineRepresentation::kNone)
return; // No input requirement on the use.
NodeInfo* input_info = GetInfo(input);
MachineRepresentation input_rep = input_info->representation();
if (input_rep != use.preferred()) {
if (input_rep != use.representation() ||
use.type_check() != TypeCheckKind::kNone) {
// Output representation doesn't match usage.
TRACE(" change: #%d:%s(@%d #%d:%s) ", node->id(), node->op()->mnemonic(),
index, input->id(), input->op()->mnemonic());
......@@ -438,8 +647,7 @@ class RepresentationSelector {
PrintUseInfo(use);
TRACE("\n");
Node* n = changer_->GetRepresentationFor(
input, input_info->representation(), GetUpperBound(input),
use.preferred(), use.truncation());
input, input_info->representation(), TypeOf(input), node, use);
node->ReplaceInput(index, n);
}
}
......@@ -484,19 +692,21 @@ class RepresentationSelector {
// Helper for binops of the R x L -> O variety.
void VisitBinop(Node* node, UseInfo left_use, UseInfo right_use,
MachineRepresentation output) {
MachineRepresentation output,
TypeCheckKind type_check = TypeCheckKind::kNone) {
DCHECK_EQ(2, node->op()->ValueInputCount());
ProcessInput(node, 0, left_use);
ProcessInput(node, 1, right_use);
for (int i = 2; i < node->InputCount(); i++) {
EnqueueInput(node, i);
}
SetOutput(node, output);
SetOutput(node, output, type_check);
}
// Helper for binops of the I x I -> O variety.
void VisitBinop(Node* node, UseInfo input_use, MachineRepresentation output) {
VisitBinop(node, input_use, input_use, output);
void VisitBinop(Node* node, UseInfo input_use, MachineRepresentation output,
TypeCheckKind type_check = TypeCheckKind::kNone) {
VisitBinop(node, input_use, input_use, output, type_check);
}
// Helper for unops of the I -> O variety.
......@@ -554,9 +764,12 @@ class RepresentationSelector {
}
// Infer representation for phi-like nodes.
MachineRepresentation GetOutputInfoForPhi(Node* node, Truncation use) {
MachineRepresentation GetOutputInfoForPhi(Node* node, Truncation use,
Type* type = nullptr) {
// Compute the representation.
Type* type = GetUpperBound(node);
if (type == nullptr) {
type = TypeOf(node);
}
if (type->Is(Type::None())) {
return MachineRepresentation::kNone;
} else if (type->Is(Type::Signed32()) || type->Is(Type::Unsigned32())) {
......@@ -579,6 +792,7 @@ class RepresentationSelector {
MachineRepresentation::kWord64;
#ifdef DEBUG
// Check that all the inputs agree on being Word64.
DCHECK_EQ(IrOpcode::kPhi, node->opcode()); // This only works for phis.
for (int i = 1; i < node->op()->ValueInputCount(); i++) {
DCHECK_EQ(is_word64, GetInfo(node->InputAt(i))->representation() ==
MachineRepresentation::kWord64);
......@@ -617,6 +831,8 @@ class RepresentationSelector {
void VisitPhi(Node* node, Truncation truncation,
SimplifiedLowering* lowering) {
MachineRepresentation output = GetOutputInfoForPhi(node, truncation);
// Only set the output representation if not running with type
// feedback. (Feedback typing will set the representation.)
SetOutput(node, output);
int values = node->op()->ValueInputCount();
......@@ -686,7 +902,7 @@ class RepresentationSelector {
Node* input = node->InputAt(i);
NodeInfo* input_info = GetInfo(input);
MachineType machine_type(input_info->representation(),
DeoptValueSemanticOf(GetUpperBound(input)));
DeoptValueSemanticOf(TypeOf(input)));
DCHECK(machine_type.representation() !=
MachineRepresentation::kWord32 ||
machine_type.semantic() == MachineSemantic::kInt32 ||
......@@ -703,6 +919,10 @@ class RepresentationSelector {
return changer_->Int32OperatorFor(node->opcode());
}
const Operator* Int32OverflowOp(Node* node) {
return changer_->Int32OverflowOperatorFor(node->opcode());
}
const Operator* Uint32Op(Node* node) {
return changer_->Uint32OperatorFor(node->opcode());
}
......@@ -776,6 +996,114 @@ class RepresentationSelector {
field_type, value);
}
Graph* graph() const { return jsgraph_->graph(); }
CommonOperatorBuilder* common() const { return jsgraph_->common(); }
SimplifiedOperatorBuilder* simplified() const {
return jsgraph_->simplified();
}
void ChangeToPureOp(Node* node, const Operator* new_op) {
// Disconnect the node from effect and control chains.
Node* control = NodeProperties::GetControlInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsControlEdge(edge)) {
edge.UpdateTo(control);
} else if (NodeProperties::IsEffectEdge(edge)) {
edge.UpdateTo(effect);
} else {
DCHECK(NodeProperties::IsValueEdge(edge));
}
}
node->TrimInputCount(new_op->ValueInputCount());
NodeProperties::ChangeOp(node, new_op);
}
void ReplaceEffectControlUses(Node* node, Node* effect, Node* control) {
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsControlEdge(edge)) {
edge.UpdateTo(control);
} else if (NodeProperties::IsEffectEdge(edge)) {
edge.UpdateTo(effect);
} else {
DCHECK(NodeProperties::IsValueEdge(edge));
}
}
}
void ChangeToInt32OverflowOp(Node* node, const Operator* op) {
Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* arith = graph()->NewNode(op, node->InputAt(0), node->InputAt(1));
Node* overflow = graph()->NewNode(common()->Projection(1), arith);
control = effect = graph()->NewNode(common()->DeoptimizeIf(), overflow,
frame_state, effect, control);
Node* value = graph()->NewNode(common()->Projection(0), arith);
ReplaceEffectControlUses(node, effect, control);
DeferReplacement(node, value);
}
void VisitSpeculativeAdditiveOp(Node* node, Truncation truncation,
SimplifiedLowering* lowering) {
if (BothInputsAre(node, Type::Signed32()) &&
NodeProperties::GetType(node)->Is(Type::Signed32())) {
// int32 + int32 = int32 ==> signed Int32Add/Sub
VisitInt32Binop(node);
if (lower()) ChangeToPureOp(node, Int32Op(node));
return;
}
// Use truncation if available.
if (BothInputsAre(node, type_cache_.kAdditiveSafeInteger) &&
truncation.TruncatesToWord32()) {
// safe-int + safe-int = x (truncated to int32)
// => signed Int32Add/Sub (truncated)
VisitWord32TruncatingBinop(node);
if (lower()) ChangeToPureOp(node, Int32Op(node));
return;
}
// Try to use type feedback.
BinaryOperationHints::Hint hint = BinaryOperationHintOf(node->op());
// TODO(jarin) This might not be necessary (covered by the next case).
// The only real difference is that this one actually treats the
// inputs as truncated to word32.
if (BothInputsAre(node, Type::Signed32())) {
// If both the inputs the feedback are int32, use the overflow op.
if (hint == BinaryOperationHints::kSignedSmall ||
hint == BinaryOperationHints::kSigned32) {
VisitBinop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32, TypeCheckKind::kSigned32);
if (lower()) {
ChangeToInt32OverflowOp(node, Int32OverflowOp(node));
}
return;
}
}
if (hint == BinaryOperationHints::kSignedSmall ||
hint == BinaryOperationHints::kSigned32) {
VisitBinop(node, UseInfo::CheckedSigned32AsWord32(),
MachineRepresentation::kWord32, TypeCheckKind::kSigned32);
if (lower()) {
ChangeToInt32OverflowOp(node, Int32OverflowOp(node));
}
return;
}
// default case => Float64Add/Sub
VisitBinop(node, UseInfo::CheckedNumberOrUndefinedAsFloat64(),
MachineRepresentation::kFloat64, TypeCheckKind::kNumber);
if (lower()) {
ChangeToPureOp(node, Float64Op(node));
}
return;
}
// Dispatching routine for visiting the node {node} with the usage {use}.
// Depending on the operator, propagate new usage info to the inputs.
void VisitNode(Node* node, Truncation truncation,
......@@ -813,15 +1141,15 @@ class RepresentationSelector {
ProcessInput(node, 0, UseInfo::Bool());
ProcessInput(node, 1, UseInfo::AnyTagged());
ProcessRemainingInputs(node, 2);
break;
return;
case IrOpcode::kBranch:
ProcessInput(node, 0, UseInfo::Bool());
EnqueueInput(node, NodeProperties::FirstControlIndex(node));
break;
return;
case IrOpcode::kSwitch:
ProcessInput(node, 0, UseInfo::TruncatingWord32());
EnqueueInput(node, NodeProperties::FirstControlIndex(node));
break;
return;
case IrOpcode::kSelect:
return VisitSelect(node, truncation, lowering);
case IrOpcode::kPhi:
......@@ -844,7 +1172,7 @@ class RepresentationSelector {
} else {
SetOutput(node, MachineRepresentation::kTagged);
}
break;
return;
}
//------------------------------------------------------------------
......@@ -867,7 +1195,7 @@ class RepresentationSelector {
ProcessInput(node, 0, UseInfo::AnyTruncatingToBool());
SetOutput(node, MachineRepresentation::kBit);
}
break;
return;
}
case IrOpcode::kBooleanToNumber: {
if (lower()) {
......@@ -885,7 +1213,7 @@ class RepresentationSelector {
ProcessInput(node, 0, UseInfo::AnyTruncatingToBool());
SetOutput(node, MachineRepresentation::kWord32);
}
break;
return;
}
case IrOpcode::kNumberEqual:
case IrOpcode::kNumberLessThan:
......@@ -904,8 +1232,13 @@ class RepresentationSelector {
VisitFloat64Cmp(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
}
break;
return;
}
case IrOpcode::kSpeculativeNumberAdd:
case IrOpcode::kSpeculativeNumberSubtract:
return VisitSpeculativeAdditiveOp(node, truncation, lowering);
case IrOpcode::kNumberAdd:
case IrOpcode::kNumberSubtract: {
if (BothInputsAre(node, Type::Signed32()) &&
......@@ -925,7 +1258,7 @@ class RepresentationSelector {
VisitFloat64Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
}
break;
return;
}
case IrOpcode::kNumberMultiply: {
if (BothInputsAreSigned32(node)) {
......@@ -934,7 +1267,7 @@ class RepresentationSelector {
// are integers.
VisitInt32Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
break;
return;
}
if (truncation.TruncatesToWord32() &&
NodeProperties::GetType(node)->Is(type_cache_.kSafeInteger)) {
......@@ -943,13 +1276,13 @@ class RepresentationSelector {
// integer range.
VisitWord32TruncatingBinop(node);
if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
break;
return;
}
}
// => Float64Mul
VisitFloat64Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
break;
return;
}
case IrOpcode::kNumberDivide: {
if (BothInputsAreSigned32(node)) {
......@@ -957,25 +1290,25 @@ class RepresentationSelector {
// => signed Int32Div
VisitInt32Binop(node);
if (lower()) DeferReplacement(node, lowering->Int32Div(node));
break;
return;
}
if (truncation.TruncatesToWord32()) {
// => signed Int32Div
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Int32Div(node));
break;
return;
}
}
if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) {
// => unsigned Uint32Div
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Uint32Div(node));
break;
return;
}
// => Float64Div
VisitFloat64Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
break;
return;
}
case IrOpcode::kNumberModulus: {
if (BothInputsAreSigned32(node)) {
......@@ -983,32 +1316,32 @@ class RepresentationSelector {
// => signed Int32Mod
VisitInt32Binop(node);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
break;
return;
}
if (truncation.TruncatesToWord32()) {
// => signed Int32Mod
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Int32Mod(node));
break;
return;
}
}
if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) {
// => unsigned Uint32Mod
VisitWord32TruncatingBinop(node);
if (lower()) DeferReplacement(node, lowering->Uint32Mod(node));
break;
return;
}
// => Float64Mod
VisitFloat64Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Float64Op(node));
break;
return;
}
case IrOpcode::kNumberBitwiseOr:
case IrOpcode::kNumberBitwiseXor:
case IrOpcode::kNumberBitwiseAnd: {
VisitInt32Binop(node);
if (lower()) NodeProperties::ChangeOp(node, Int32Op(node));
break;
return;
}
case IrOpcode::kNumberShiftLeft: {
Type* rhs_type = GetUpperBound(node->InputAt(1));
......@@ -1017,7 +1350,7 @@ class RepresentationSelector {
if (lower()) {
lowering->DoShift(node, lowering->machine()->Word32Shl(), rhs_type);
}
break;
return;
}
case IrOpcode::kNumberShiftRight: {
Type* rhs_type = GetUpperBound(node->InputAt(1));
......@@ -1026,7 +1359,7 @@ class RepresentationSelector {
if (lower()) {
lowering->DoShift(node, lowering->machine()->Word32Sar(), rhs_type);
}
break;
return;
}
case IrOpcode::kNumberShiftRightLogical: {
Type* rhs_type = GetUpperBound(node->InputAt(1));
......@@ -1035,57 +1368,57 @@ class RepresentationSelector {
if (lower()) {
lowering->DoShift(node, lowering->machine()->Word32Shr(), rhs_type);
}
break;
return;
}
case IrOpcode::kNumberClz32: {
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
break;
return;
}
case IrOpcode::kNumberImul: {
VisitBinop(node, UseInfo::TruncatingWord32(),
UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
break;
return;
}
case IrOpcode::kNumberCeil: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) DeferReplacement(node, lowering->Float64Ceil(node));
break;
return;
}
case IrOpcode::kNumberFloor: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) DeferReplacement(node, lowering->Float64Floor(node));
break;
return;
}
case IrOpcode::kNumberRound: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) DeferReplacement(node, lowering->Float64Round(node));
break;
return;
}
case IrOpcode::kNumberTrunc: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) DeferReplacement(node, lowering->Float64Trunc(node));
break;
return;
}
case IrOpcode::kNumberToInt32: {
// Just change representation if necessary.
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, node->InputAt(0));
break;
return;
}
case IrOpcode::kNumberToUint32: {
// Just change representation if necessary.
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, node->InputAt(0));
break;
return;
}
case IrOpcode::kNumberIsHoleNaN: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
......@@ -1101,14 +1434,14 @@ class RepresentationSelector {
jsgraph_->Int32Constant(kHoleNanUpper32));
NodeProperties::ChangeOp(node, jsgraph_->machine()->Word32Equal());
}
break;
return;
}
case IrOpcode::kReferenceEqual: {
VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
if (lower()) {
NodeProperties::ChangeOp(node, lowering->machine()->WordEqual());
}
break;
return;
}
case IrOpcode::kStringEqual: {
VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
......@@ -1128,7 +1461,7 @@ class RepresentationSelector {
node->AppendInput(jsgraph_->zone(), jsgraph_->graph()->start());
NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc));
}
break;
return;
}
case IrOpcode::kStringLessThan: {
VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
......@@ -1147,7 +1480,7 @@ class RepresentationSelector {
node->AppendInput(jsgraph_->zone(), jsgraph_->graph()->start());
NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc));
}
break;
return;
}
case IrOpcode::kStringLessThanOrEqual: {
VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
......@@ -1168,12 +1501,12 @@ class RepresentationSelector {
node->AppendInput(jsgraph_->zone(), jsgraph_->graph()->start());
NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc));
}
break;
return;
}
case IrOpcode::kStringFromCharCode: {
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kTagged);
break;
return;
}
case IrOpcode::kStringToNumber: {
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
......@@ -1192,20 +1525,20 @@ class RepresentationSelector {
node->AppendInput(jsgraph_->zone(), jsgraph_->graph()->start());
NodeProperties::ChangeOp(node, jsgraph_->common()->Call(desc));
}
break;
return;
}
case IrOpcode::kAllocate: {
ProcessInput(node, 0, UseInfo::TruncatingWord32());
ProcessRemainingInputs(node, 1);
SetOutput(node, MachineRepresentation::kTagged);
break;
return;
}
case IrOpcode::kLoadField: {
FieldAccess access = FieldAccessOf(node->op());
ProcessInput(node, 0, UseInfoForBasePointer(access));
ProcessRemainingInputs(node, 1);
SetOutput(node, access.machine_type.representation());
break;
return;
}
case IrOpcode::kStoreField: {
FieldAccess access = FieldAccessOf(node->op());
......@@ -1224,7 +1557,7 @@ class RepresentationSelector {
node, jsgraph_->simplified()->StoreField(access));
}
}
break;
return;
}
case IrOpcode::kLoadBuffer: {
BufferAccess access = BufferAccessOf(node->op());
......@@ -1257,7 +1590,7 @@ class RepresentationSelector {
}
SetOutput(node, output);
if (lower()) lowering->DoLoadBuffer(node, output, changer_);
break;
return;
}
case IrOpcode::kStoreBuffer: {
BufferAccess access = BufferAccessOf(node->op());
......@@ -1270,7 +1603,7 @@ class RepresentationSelector {
ProcessRemainingInputs(node, 4);
SetOutput(node, MachineRepresentation::kNone);
if (lower()) lowering->DoStoreBuffer(node);
break;
return;
}
case IrOpcode::kLoadElement: {
ElementAccess access = ElementAccessOf(node->op());
......@@ -1278,7 +1611,7 @@ class RepresentationSelector {
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // index
ProcessRemainingInputs(node, 2);
SetOutput(node, access.machine_type.representation());
break;
return;
}
case IrOpcode::kStoreElement: {
ElementAccess access = ElementAccessOf(node->op());
......@@ -1299,7 +1632,7 @@ class RepresentationSelector {
node, jsgraph_->simplified()->StoreElement(access));
}
}
break;
return;
}
case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsNumber:
......@@ -1309,7 +1642,7 @@ class RepresentationSelector {
case IrOpcode::kObjectIsUndetectable: {
ProcessInput(node, 0, UseInfo::AnyTagged());
SetOutput(node, MachineRepresentation::kBit);
break;
return;
}
//------------------------------------------------------------------
......@@ -1322,8 +1655,7 @@ class RepresentationSelector {
ProcessInput(node, 0, UseInfo::AnyTagged()); // tagged pointer
ProcessInput(node, 1, UseInfo::PointerInt()); // index
ProcessRemainingInputs(node, 2);
SetOutput(node, rep.representation());
break;
return SetOutput(node, rep.representation());
}
case IrOpcode::kStore: {
// TODO(jarin) Eventually, we should get rid of all machine stores
......@@ -1334,8 +1666,7 @@ class RepresentationSelector {
ProcessInput(node, 2,
TruncatingUseInfoFromRepresentation(rep.representation()));
ProcessRemainingInputs(node, 3);
SetOutput(node, MachineRepresentation::kNone);
break;
return SetOutput(node, MachineRepresentation::kNone);
}
case IrOpcode::kWord32Shr:
// We output unsigned int32 for shift right because JavaScript.
......@@ -1420,10 +1751,6 @@ class RepresentationSelector {
return VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kWord32);
case IrOpcode::kChangeFloat32ToFloat64:
UNREACHABLE();
return VisitUnop(node, UseInfo::TruncatingFloat32(),
MachineRepresentation::kFloat64);
case IrOpcode::kChangeInt32ToFloat64:
return VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kFloat64);
......@@ -1463,8 +1790,7 @@ class RepresentationSelector {
case IrOpcode::kLoadParentFramePointer:
return VisitLeaf(node, MachineType::PointerRepresentation());
case IrOpcode::kStateValues:
VisitStateValues(node);
break;
return VisitStateValues(node);
// The following opcodes are not produced before representation
// inference runs, so we do not have any real test coverage.
......@@ -1472,14 +1798,20 @@ class RepresentationSelector {
case IrOpcode::kChangeFloat64ToInt32:
case IrOpcode::kChangeFloat64ToUint32:
case IrOpcode::kTruncateInt64ToInt32:
case IrOpcode::kChangeFloat32ToFloat64:
case IrOpcode::kCheckedUint32ToInt32:
case IrOpcode::kCheckedFloat64ToInt32:
case IrOpcode::kCheckedTaggedToInt32:
case IrOpcode::kCheckedTaggedToFloat64:
FATAL("Representation inference: unsupported opcodes.");
break;
default:
VisitInputs(node);
// Assume the output is tagged.
SetOutput(node, MachineRepresentation::kTagged);
break;
return SetOutput(node, MachineRepresentation::kTagged);
}
UNREACHABLE();
}
void DeferReplacement(Node* node, Node* replacement) {
......@@ -1488,7 +1820,8 @@ class RepresentationSelector {
replacement->op()->mnemonic());
if (replacement->id() < count_ &&
GetUpperBound(node)->Is(GetUpperBound(replacement))) {
GetUpperBound(node)->Is(GetUpperBound(replacement)) &&
TypeOf(node)->Is(TypeOf(replacement))) {
// Replace with a previously existing node eagerly only if the type is the
// same.
node->ReplaceUses(replacement);
......@@ -1520,19 +1853,20 @@ class RepresentationSelector {
void PrintTruncation(Truncation truncation) {
if (FLAG_trace_representation) {
OFStream os(stdout);
os << truncation.description();
os << truncation.description() << std::endl;
}
}
void PrintUseInfo(UseInfo info) {
if (FLAG_trace_representation) {
OFStream os(stdout);
os << info.preferred() << ":" << info.truncation().description();
os << info.representation() << ":" << info.truncation().description();
}
}
private:
JSGraph* jsgraph_;
Zone* zone_; // Temporary zone.
size_t const count_; // number of nodes in the graph
ZoneVector<NodeInfo> info_; // node id -> usage information
#ifdef DEBUG
......@@ -1544,6 +1878,12 @@ class RepresentationSelector {
Phase phase_; // current phase of algorithm
RepresentationChanger* changer_; // for inserting representation changes
ZoneQueue<Node*> queue_; // queue for traversing the graph
struct NodeState {
Node* node;
int input_index;
};
ZoneStack<NodeState> typing_stack_; // stack for graph typing.
// TODO(danno): RepresentationSelector shouldn't know anything about the
// source positions table, but must for now since there currently is no other
// way to pass down source position information to nodes created during
......@@ -1551,23 +1891,26 @@ class RepresentationSelector {
// position information via the SourcePositionWrapper like all other reducers.
SourcePositionTable* source_positions_;
TypeCache const& type_cache_;
OperationTyper op_typer_; // helper for the feedback typer
NodeInfo* GetInfo(Node* node) {
DCHECK(node->id() >= 0);
DCHECK(node->id() < count_);
return &info_[node->id()];
}
Zone* zone() { return zone_; }
Zone* graph_zone() { return jsgraph_->zone(); }
};
SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, Zone* zone,
SourcePositionTable* source_positions)
SourcePositionTable* source_positions,
Flags flags)
: jsgraph_(jsgraph),
zone_(zone),
type_cache_(TypeCache::Get()),
flags_(flags),
source_positions_(source_positions) {}
void SimplifiedLowering::LowerAllNodes() {
RepresentationChanger changer(jsgraph(), jsgraph()->isolate());
RepresentationSelector selector(jsgraph(), zone_, &changer,
......@@ -1763,8 +2106,8 @@ void SimplifiedLowering::DoLoadBuffer(Node* node,
Type* element_type =
Type::Intersect(NodeProperties::GetType(node), Type::Number(), zone());
Node* vtrue = changer->GetRepresentationFor(
etrue, access_type.representation(), element_type, output_rep,
Truncation::None());
etrue, access_type.representation(), element_type, node,
UseInfo(output_rep, Truncation::None()));
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = effect;
......
......@@ -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',
......
......@@ -83,12 +83,20 @@ class RepresentationChangerTester : public HandleAndZoneScope,
return n;
}
Node* Return(Node* input) {
Node* n = graph()->NewNode(common()->Return(), input, graph()->start(),
graph()->start());
return n;
}
void CheckTypeError(MachineRepresentation from, Type* from_type,
MachineRepresentation to) {
changer()->testing_type_errors_ = true;
changer()->type_error_ = false;
Node* n = Parameter(0);
Node* c = changer()->GetRepresentationFor(n, from, from_type, to);
Node* use = Return(n);
Node* c = changer()->GetRepresentationFor(n, from, from_type, use,
UseInfo(to, Truncation::None()));
CHECK(changer()->type_error_);
CHECK_EQ(n, c);
}
......@@ -96,7 +104,9 @@ class RepresentationChangerTester : public HandleAndZoneScope,
void CheckNop(MachineRepresentation from, Type* from_type,
MachineRepresentation to) {
Node* n = Parameter(0);
Node* c = changer()->GetRepresentationFor(n, from, from_type, to);
Node* use = Return(n);
Node* c = changer()->GetRepresentationFor(n, from, from_type, use,
UseInfo(to, Truncation::None()));
CHECK_EQ(n, c);
}
};
......@@ -113,15 +123,17 @@ TEST(BoolToBit_constant) {
RepresentationChangerTester r;
Node* true_node = r.jsgraph()->TrueConstant();
Node* true_use = r.Return(true_node);
Node* true_bit = r.changer()->GetRepresentationFor(
true_node, MachineRepresentation::kTagged, Type::None(),
MachineRepresentation::kBit);
true_node, MachineRepresentation::kTagged, Type::None(), true_use,
UseInfo(MachineRepresentation::kBit, Truncation::None()));
r.CheckInt32Constant(true_bit, 1);
Node* false_node = r.jsgraph()->FalseConstant();
Node* false_use = r.Return(false_node);
Node* false_bit = r.changer()->GetRepresentationFor(
false_node, MachineRepresentation::kTagged, Type::None(),
MachineRepresentation::kBit);
false_node, MachineRepresentation::kTagged, Type::None(), false_use,
UseInfo(MachineRepresentation::kBit, Truncation::None()));
r.CheckInt32Constant(false_bit, 0);
}
......@@ -131,9 +143,10 @@ TEST(BitToBool_constant) {
for (int i = -5; i < 5; i++) {
Node* node = r.jsgraph()->Int32Constant(i);
Node* use = r.Return(node);
Node* val = r.changer()->GetRepresentationFor(
node, MachineRepresentation::kBit, Type::Boolean(),
MachineRepresentation::kTagged);
node, MachineRepresentation::kBit, Type::Boolean(), use,
UseInfo(MachineRepresentation::kTagged, Truncation::None()));
r.CheckHeapConstant(val, i == 0 ? r.isolate()->heap()->false_value()
: r.isolate()->heap()->true_value());
}
......@@ -146,49 +159,54 @@ TEST(ToTagged_constant) {
{
FOR_FLOAT64_INPUTS(i) {
Node* n = r.jsgraph()->Float64Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::None(),
MachineRepresentation::kTagged);
r.CheckNumberConstant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::None(), use,
UseInfo(MachineRepresentation::kTagged, Truncation::None()));
r.CheckNumberConstant(c, *i);
}
}
{
FOR_FLOAT64_INPUTS(i) {
Node* n = r.jsgraph()->Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::None(),
MachineRepresentation::kTagged);
r.CheckNumberConstant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::None(), use,
UseInfo(MachineRepresentation::kTagged, Truncation::None()));
r.CheckNumberConstant(c, *i);
}
}
{
FOR_FLOAT32_INPUTS(i) {
Node* n = r.jsgraph()->Float32Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat32, Type::None(),
MachineRepresentation::kTagged);
r.CheckNumberConstant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat32, Type::None(), use,
UseInfo(MachineRepresentation::kTagged, Truncation::None()));
r.CheckNumberConstant(c, *i);
}
}
{
FOR_INT32_INPUTS(i) {
Node* n = r.jsgraph()->Int32Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Signed32(),
MachineRepresentation::kTagged);
r.CheckNumberConstant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Signed32(), use,
UseInfo(MachineRepresentation::kTagged, Truncation::None()));
r.CheckNumberConstant(c, *i);
}
}
{
FOR_UINT32_INPUTS(i) {
Node* n = r.jsgraph()->Int32Constant(*i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Unsigned32(),
MachineRepresentation::kTagged);
n, MachineRepresentation::kWord32, Type::Unsigned32(), use,
UseInfo(MachineRepresentation::kTagged, Truncation::None()));
r.CheckNumberConstant(c, *i);
}
}
......@@ -201,49 +219,54 @@ TEST(ToFloat64_constant) {
{
FOR_FLOAT64_INPUTS(i) {
Node* n = r.jsgraph()->Float64Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::None(),
MachineRepresentation::kFloat64);
CHECK_EQ(n, c);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::None(), use,
UseInfo(MachineRepresentation::kFloat64, Truncation::None()));
CHECK_EQ(n, c);
}
}
{
FOR_FLOAT64_INPUTS(i) {
Node* n = r.jsgraph()->Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, Type::None(),
MachineRepresentation::kFloat64);
r.CheckFloat64Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, Type::None(), use,
UseInfo(MachineRepresentation::kFloat64, Truncation::None()));
r.CheckFloat64Constant(c, *i);
}
}
{
FOR_FLOAT32_INPUTS(i) {
Node* n = r.jsgraph()->Float32Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat32, Type::None(),
MachineRepresentation::kFloat64);
r.CheckFloat64Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat32, Type::None(), use,
UseInfo(MachineRepresentation::kFloat64, Truncation::None()));
r.CheckFloat64Constant(c, *i);
}
}
{
FOR_INT32_INPUTS(i) {
Node* n = r.jsgraph()->Int32Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Signed32(),
MachineRepresentation::kFloat64);
r.CheckFloat64Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Signed32(), use,
UseInfo(MachineRepresentation::kFloat64, Truncation::None()));
r.CheckFloat64Constant(c, *i);
}
}
{
FOR_UINT32_INPUTS(i) {
Node* n = r.jsgraph()->Int32Constant(*i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Unsigned32(),
MachineRepresentation::kFloat64);
n, MachineRepresentation::kWord32, Type::Unsigned32(), use,
UseInfo(MachineRepresentation::kFloat64, Truncation::None()));
r.CheckFloat64Constant(c, *i);
}
}
......@@ -264,30 +287,33 @@ TEST(ToFloat32_constant) {
{
FOR_FLOAT32_INPUTS(i) {
Node* n = r.jsgraph()->Float32Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat32, Type::None(),
MachineRepresentation::kFloat32);
CHECK_EQ(n, c);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat32, Type::None(), use,
UseInfo(MachineRepresentation::kFloat32, Truncation::None()));
CHECK_EQ(n, c);
}
}
{
FOR_FLOAT32_INPUTS(i) {
Node* n = r.jsgraph()->Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, Type::None(),
MachineRepresentation::kFloat32);
r.CheckFloat32Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, Type::None(), use,
UseInfo(MachineRepresentation::kFloat32, Truncation::None()));
r.CheckFloat32Constant(c, *i);
}
}
{
FOR_FLOAT32_INPUTS(i) {
Node* n = r.jsgraph()->Float64Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::None(),
MachineRepresentation::kFloat32);
r.CheckFloat32Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::None(), use,
UseInfo(MachineRepresentation::kFloat32, Truncation::None()));
r.CheckFloat32Constant(c, *i);
}
}
......@@ -295,9 +321,10 @@ TEST(ToFloat32_constant) {
FOR_INT32_INPUTS(i) {
if (!IsFloat32Int32(*i)) continue;
Node* n = r.jsgraph()->Int32Constant(*i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Signed32(),
MachineRepresentation::kFloat32);
n, MachineRepresentation::kWord32, Type::Signed32(), use,
UseInfo(MachineRepresentation::kFloat32, Truncation::None()));
r.CheckFloat32Constant(c, static_cast<float>(*i));
}
}
......@@ -306,9 +333,10 @@ TEST(ToFloat32_constant) {
FOR_UINT32_INPUTS(i) {
if (!IsFloat32Uint32(*i)) continue;
Node* n = r.jsgraph()->Int32Constant(*i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Unsigned32(),
MachineRepresentation::kFloat32);
n, MachineRepresentation::kWord32, Type::Unsigned32(), use,
UseInfo(MachineRepresentation::kFloat32, Truncation::None()));
r.CheckFloat32Constant(c, static_cast<float>(*i));
}
}
......@@ -321,10 +349,11 @@ TEST(ToInt32_constant) {
{
FOR_INT32_INPUTS(i) {
Node* n = r.jsgraph()->Int32Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Signed32(),
MachineRepresentation::kWord32);
r.CheckInt32Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Signed32(), use,
UseInfo(MachineRepresentation::kWord32, Truncation::None()));
r.CheckInt32Constant(c, *i);
}
}
......@@ -332,9 +361,10 @@ TEST(ToInt32_constant) {
FOR_INT32_INPUTS(i) {
if (!IsFloat32Int32(*i)) continue;
Node* n = r.jsgraph()->Float32Constant(static_cast<float>(*i));
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat32, Type::Signed32(),
MachineRepresentation::kWord32);
n, MachineRepresentation::kFloat32, Type::Signed32(), use,
UseInfo(MachineRepresentation::kWord32, Truncation::None()));
r.CheckInt32Constant(c, *i);
}
}
......@@ -342,19 +372,21 @@ TEST(ToInt32_constant) {
{
FOR_INT32_INPUTS(i) {
Node* n = r.jsgraph()->Float64Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::Signed32(),
MachineRepresentation::kWord32);
r.CheckInt32Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::Signed32(), use,
UseInfo(MachineRepresentation::kWord32, Truncation::None()));
r.CheckInt32Constant(c, *i);
}
}
{
FOR_INT32_INPUTS(i) {
Node* n = r.jsgraph()->Constant(*i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, Type::Signed32(),
MachineRepresentation::kWord32);
n, MachineRepresentation::kTagged, Type::Signed32(), use,
UseInfo(MachineRepresentation::kWord32, Truncation::None()));
r.CheckInt32Constant(c, *i);
}
}
......@@ -367,10 +399,11 @@ TEST(ToUint32_constant) {
{
FOR_UINT32_INPUTS(i) {
Node* n = r.jsgraph()->Int32Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Unsigned32(),
MachineRepresentation::kWord32);
r.CheckUint32Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kWord32, Type::Unsigned32(), use,
UseInfo(MachineRepresentation::kWord32, Truncation::None()));
r.CheckUint32Constant(c, *i);
}
}
......@@ -378,9 +411,10 @@ TEST(ToUint32_constant) {
FOR_UINT32_INPUTS(i) {
if (!IsFloat32Uint32(*i)) continue;
Node* n = r.jsgraph()->Float32Constant(static_cast<float>(*i));
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat32, Type::Unsigned32(),
MachineRepresentation::kWord32);
n, MachineRepresentation::kFloat32, Type::Unsigned32(), use,
UseInfo(MachineRepresentation::kWord32, Truncation::None()));
r.CheckUint32Constant(c, *i);
}
}
......@@ -388,19 +422,21 @@ TEST(ToUint32_constant) {
{
FOR_UINT32_INPUTS(i) {
Node* n = r.jsgraph()->Float64Constant(*i);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::Unsigned32(),
MachineRepresentation::kWord32);
r.CheckUint32Constant(c, *i);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kFloat64, Type::Unsigned32(), use,
UseInfo(MachineRepresentation::kWord32, Truncation::None()));
r.CheckUint32Constant(c, *i);
}
}
{
FOR_UINT32_INPUTS(i) {
Node* n = r.jsgraph()->Constant(static_cast<double>(*i));
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, Type::Unsigned32(),
MachineRepresentation::kWord32);
n, MachineRepresentation::kTagged, Type::Unsigned32(), use,
UseInfo(MachineRepresentation::kWord32, Truncation::None()));
r.CheckUint32Constant(c, *i);
}
}
......@@ -412,7 +448,9 @@ static void CheckChange(IrOpcode::Value expected, MachineRepresentation from,
RepresentationChangerTester r;
Node* n = r.Parameter();
Node* c = r.changer()->GetRepresentationFor(n, from, from_type, to);
Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor(n, from, from_type, use,
UseInfo(to, Truncation::None()));
CHECK_NE(c, n);
CHECK_EQ(expected, c->opcode());
......@@ -427,7 +465,9 @@ static void CheckTwoChanges(IrOpcode::Value expected2,
RepresentationChangerTester r;
Node* n = r.Parameter();
Node* c1 = r.changer()->GetRepresentationFor(n, from, from_type, to);
Node* use = r.Return(n);
Node* c1 = r.changer()->GetRepresentationFor(n, from, from_type, use,
UseInfo(to, Truncation::None()));
CHECK_NE(c1, n);
CHECK_EQ(expected1, c1->opcode());
......
// 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