Commit 0df0e254 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for storing to double fields.

Adds new Guard[Type] common operator, which takes value and control
inputs and records a guaranty that a certain value has a certain type
in that control path.  This is some kind of ad-hoc SSI similar to what
we have to do in Crankshaft in some places.

Also introduces an ObjectIsNumber simplified operator, which checks
whether a certain value is a number (either a Smi or a HeapNumber).

This doesn't yet support transitioning stores to double fields, which
require support for allocating mutable heap numbers.

R=jarin@chromium.org
BUG=v8:4470
LOG=n

Review URL: https://codereview.chromium.org/1420283009

Cr-Commit-Position: refs/heads/master@{#31675}
parent 98dfe024
......@@ -67,6 +67,8 @@ Reduction CommonOperatorReducer::Reduce(Node* node) {
return ReduceReturn(node);
case IrOpcode::kSelect:
return ReduceSelect(node);
case IrOpcode::kGuard:
return ReduceGuard(node);
default:
break;
}
......@@ -358,6 +360,16 @@ Reduction CommonOperatorReducer::ReduceSelect(Node* node) {
}
Reduction CommonOperatorReducer::ReduceGuard(Node* node) {
DCHECK_EQ(IrOpcode::kGuard, node->opcode());
Node* const input = NodeProperties::GetValueInput(node, 0);
Type* const input_type = NodeProperties::GetTypeOrAny(input);
Type* const guard_type = OpParameter<Type*>(node);
if (input_type->Is(guard_type)) return Replace(input);
return NoChange();
}
Reduction CommonOperatorReducer::Change(Node* node, Operator const* op,
Node* a) {
node->ReplaceInput(0, a);
......
......@@ -35,6 +35,7 @@ class CommonOperatorReducer final : public AdvancedReducer {
Reduction ReducePhi(Node* node);
Reduction ReduceReturn(Node* node);
Reduction ReduceSelect(Node* node);
Reduction ReduceGuard(Node* node);
Reduction Change(Node* node, Operator const* op, Node* a);
Reduction Change(Node* node, Operator const* op, Node* a, Node* b);
......
......@@ -671,6 +671,15 @@ const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) {
}
const Operator* CommonOperatorBuilder::Guard(Type* type) {
return new (zone()) Operator1<Type*>( // --
IrOpcode::kGuard, Operator::kKontrol, // opcode
"Guard", // name
1, 0, 1, 1, 0, 0, // counts
type); // parameter
}
const Operator* CommonOperatorBuilder::EffectSet(int arguments) {
DCHECK(arguments > 1); // Disallow empty/singleton sets.
return new (zone()) Operator( // --
......
......@@ -14,6 +14,10 @@ namespace internal {
// Forward declarations.
class ExternalReference;
template <class>
class TypeImpl;
struct ZoneTypeConfig;
typedef TypeImpl<ZoneTypeConfig> Type;
namespace compiler {
......@@ -145,6 +149,7 @@ class CommonOperatorBuilder final : public ZoneObject {
const Operator* Phi(MachineType type, int value_input_count);
const Operator* EffectPhi(int effect_input_count);
const Operator* EffectSet(int arguments);
const Operator* Guard(Type* type);
const Operator* BeginRegion();
const Operator* FinishRegion();
const Operator* StateValues(int arguments);
......
......@@ -556,6 +556,8 @@ void InstructionSelector::VisitNode(Node* node) {
return MarkAsReference(node), VisitIfException(node);
case IrOpcode::kFinishRegion:
return MarkAsReference(node), VisitFinishRegion(node);
case IrOpcode::kGuard:
return MarkAsReference(node), VisitGuard(node);
case IrOpcode::kParameter: {
MachineType type =
linkage()->GetParameterType(ParameterIndexOf(node->op()));
......@@ -941,6 +943,13 @@ void InstructionSelector::VisitFinishRegion(Node* node) {
}
void InstructionSelector::VisitGuard(Node* node) {
OperandGenerator g(this);
Node* value = node->InputAt(0);
Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value));
}
void InstructionSelector::VisitParameter(Node* node) {
OperandGenerator g(this);
int index = ParameterIndexOf(node->op());
......
......@@ -202,6 +202,7 @@ class InstructionSelector final {
#undef DECLARE_GENERATOR
void VisitFinishRegion(Node* node);
void VisitGuard(Node* node);
void VisitParameter(Node* node);
void VisitIfException(Node* node);
void VisitOsrValue(Node* node);
......
......@@ -408,10 +408,6 @@ bool JSNativeContextSpecialization::ComputePropertyAccessInfo(
if (field_representation.IsSmi()) {
field_type = type_cache_.kSmi;
} else if (field_representation.IsDouble()) {
if (access_mode == kStore) {
// TODO(bmeurer): Add support for storing to double fields.
return false;
}
field_type = type_cache_.kFloat64;
} else if (field_representation.IsHeapObject()) {
// Extract the field type from the property details (make sure its
......@@ -681,32 +677,44 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
}
FieldAccess field_access = {kTaggedBase, field_index.offset(), name,
field_type, kMachAnyTagged};
if (access_mode == kLoad) {
if (field_type->Is(Type::UntaggedFloat64())) {
if (!field_index.is_inobject() || field_index.is_hidden_field() ||
!FLAG_unbox_double_fields) {
this_storage = this_effect =
graph()->NewNode(simplified()->LoadField(field_access),
this_storage, this_effect, this_control);
field_access.offset = HeapNumber::kValueOffset;
field_access.name = MaybeHandle<Name>();
}
field_access.machine_type = kMachFloat64;
if (field_type->Is(Type::UntaggedFloat64())) {
if (!field_index.is_inobject() || field_index.is_hidden_field() ||
!FLAG_unbox_double_fields) {
this_storage = this_effect =
graph()->NewNode(simplified()->LoadField(field_access),
this_storage, this_effect, this_control);
field_access.offset = HeapNumber::kValueOffset;
field_access.name = MaybeHandle<Name>();
}
field_access.machine_type = kMachFloat64;
}
if (access_mode == kLoad) {
this_value = this_effect =
graph()->NewNode(simplified()->LoadField(field_access),
this_storage, this_effect, this_control);
} else {
DCHECK_EQ(kStore, access_mode);
if (field_type->Is(Type::TaggedSigned())) {
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
if (field_type->Is(Type::UntaggedFloat64())) {
Node* check =
graph()->NewNode(simplified()->ObjectIsNumber(), this_value);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check, this_control);
exit_controls.push_back(
graph()->NewNode(common()->IfFalse(), branch));
this_control = graph()->NewNode(common()->IfTrue(), branch);
this_value = graph()->NewNode(common()->Guard(Type::Number()),
this_value, this_control);
} else if (field_type->Is(Type::TaggedSigned())) {
Node* check =
graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check, this_control);
exit_controls.push_back(
graph()->NewNode(common()->IfFalse(), branch));
this_control = graph()->NewNode(common()->IfTrue(), branch);
} else if (field_type->Is(Type::TaggedPointer())) {
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
Node* check =
graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check, this_control);
exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
......@@ -714,14 +722,14 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
if (field_type->NumClasses() > 0) {
// Emit a (sequence of) map checks for the value.
ZoneVector<Node*> this_controls(zone());
Node* value_map = this_effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMap()), value,
Node* this_value_map = this_effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMap()), this_value,
this_effect, this_control);
for (auto i = field_type->Classes(); !i.Done(); i.Advance()) {
Handle<Map> field_map(i.Current());
check = graph()->NewNode(
simplified()->ReferenceEqual(Type::Internal()), value_map,
jsgraph()->Constant(field_map));
simplified()->ReferenceEqual(Type::Internal()),
this_value_map, jsgraph()->Constant(field_map));
branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check, this_control);
this_control = graph()->NewNode(common()->IfFalse(), branch);
......
......@@ -7,6 +7,7 @@
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/verifier.h"
#include "src/types-inl.h"
namespace v8 {
namespace internal {
......@@ -282,6 +283,12 @@ void NodeProperties::CollectControlProjections(Node* node, Node** projections,
}
// static
Type* NodeProperties::GetTypeOrAny(Node* node) {
return IsTyped(node) ? node->type() : Type::Any();
}
// static
bool NodeProperties::AllValueInputsAreTyped(Node* node) {
int input_count = node->op()->ValueInputCount();
......
......@@ -122,6 +122,7 @@ class NodeProperties final {
DCHECK(IsTyped(node));
return node->type();
}
static Type* GetTypeOrAny(Node* node);
static void SetType(Node* node, Type* type) {
DCHECK_NOT_NULL(type);
node->set_type(type);
......
......@@ -44,6 +44,7 @@
V(Phi) \
V(EffectSet) \
V(EffectPhi) \
V(Guard) \
V(BeginRegion) \
V(FinishRegion) \
V(FrameState) \
......@@ -197,6 +198,7 @@
V(StoreField) \
V(StoreBuffer) \
V(StoreElement) \
V(ObjectIsNumber) \
V(ObjectIsSmi)
// Opcodes for Machine-level operators.
......
......@@ -921,18 +921,16 @@ class RepresentationSelector {
if (lower()) lowering->DoStoreElement(node);
break;
}
case IrOpcode::kObjectIsNumber: {
ProcessInput(node, 0, kMachAnyTagged);
SetOutput(node, kRepBit | kTypeBool);
if (lower()) lowering->DoObjectIsNumber(node);
break;
}
case IrOpcode::kObjectIsSmi: {
ProcessInput(node, 0, kMachAnyTagged);
SetOutput(node, kRepBit | kTypeBool);
if (lower()) {
Node* is_tagged = jsgraph_->graph()->NewNode(
jsgraph_->machine()->WordAnd(), node->InputAt(0),
jsgraph_->IntPtrConstant(kSmiTagMask));
Node* is_smi = jsgraph_->graph()->NewNode(
jsgraph_->machine()->WordEqual(), is_tagged,
jsgraph_->IntPtrConstant(kSmiTag));
DeferReplacement(node, is_smi);
}
if (lower()) lowering->DoObjectIsSmi(node);
break;
}
......@@ -1347,6 +1345,42 @@ void SimplifiedLowering::DoStoreElement(Node* node) {
}
void SimplifiedLowering::DoObjectIsNumber(Node* node) {
Node* input = NodeProperties::GetValueInput(node, 0);
// TODO(bmeurer): Optimize somewhat based on input type.
Node* check =
graph()->NewNode(machine()->WordEqual(),
graph()->NewNode(machine()->WordAnd(), input,
jsgraph()->IntPtrConstant(kSmiTagMask)),
jsgraph()->IntPtrConstant(kSmiTag));
Node* branch = graph()->NewNode(common()->Branch(), check, graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = jsgraph()->Int32Constant(1);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = graph()->NewNode(
machine()->WordEqual(),
graph()->NewNode(
machine()->Load(kMachAnyTagged), input,
jsgraph()->IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag),
graph()->start(), if_false),
jsgraph()->HeapConstant(isolate()->factory()->heap_number_map()));
Node* control = graph()->NewNode(common()->Merge(2), if_true, if_false);
node->ReplaceInput(0, vtrue);
node->AppendInput(graph()->zone(), vfalse);
node->AppendInput(graph()->zone(), control);
NodeProperties::ChangeOp(node, common()->Phi(kMachBool, 2));
}
void SimplifiedLowering::DoObjectIsSmi(Node* node) {
node->ReplaceInput(0,
graph()->NewNode(machine()->WordAnd(), node->InputAt(0),
jsgraph()->IntPtrConstant(kSmiTagMask)));
node->AppendInput(graph()->zone(), jsgraph()->IntPtrConstant(kSmiTag));
NodeProperties::ChangeOp(node, machine()->WordEqual());
}
Node* SimplifiedLowering::StringComparison(Node* node) {
Operator::Properties properties = node->op()->properties();
Callable callable = CodeFactory::StringCompare(isolate());
......
......@@ -37,6 +37,8 @@ class SimplifiedLowering final {
void DoStoreBuffer(Node* node);
void DoLoadElement(Node* node);
void DoStoreElement(Node* node);
void DoObjectIsNumber(Node* node);
void DoObjectIsSmi(Node* node);
void DoShift(Node* node, Operator const* op);
void DoStringEqual(Node* node);
void DoStringLessThan(Node* node);
......
......@@ -185,6 +185,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
V(ChangeFloat64ToTagged, Operator::kNoProperties, 1) \
V(ChangeBoolToBit, Operator::kNoProperties, 1) \
V(ChangeBitToBool, Operator::kNoProperties, 1) \
V(ObjectIsNumber, Operator::kNoProperties, 1) \
V(ObjectIsSmi, Operator::kNoProperties, 1)
#define NO_THROW_OP_LIST(V) \
......
......@@ -166,6 +166,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* ChangeBoolToBit();
const Operator* ChangeBitToBool();
const Operator* ObjectIsNumber();
const Operator* ObjectIsSmi();
const Operator* Allocate(PretenureFlag pretenure = NOT_TENURED);
......
......@@ -557,6 +557,13 @@ Type* Typer::Visitor::TypeEffectSet(Node* node) {
}
Type* Typer::Visitor::TypeGuard(Node* node) {
Type* input_type = Operand(node, 0);
Type* guard_type = OpParameter<Type*>(node);
return Type::Intersect(input_type, guard_type, zone());
}
Type* Typer::Visitor::TypeBeginRegion(Node* node) {
UNREACHABLE();
return nullptr;
......@@ -1786,6 +1793,15 @@ Type* Typer::Visitor::TypeStoreElement(Node* node) {
}
Type* Typer::Visitor::TypeObjectIsNumber(Node* node) {
Type* arg = Operand(node, 0);
if (arg->Is(Type::None())) return Type::None();
if (arg->Is(Type::Number())) return typer_->singleton_true_;
if (!arg->Maybe(Type::Number())) return typer_->singleton_false_;
return Type::Boolean();
}
Type* Typer::Visitor::TypeObjectIsSmi(Node* node) {
Type* arg = Operand(node, 0);
if (arg->Is(Type::None())) return Type::None();
......
......@@ -413,6 +413,9 @@ void Verifier::Visitor::Check(Node* node) {
CHECK_LT(1, effect_count);
break;
}
case IrOpcode::kGuard:
// TODO(bmeurer): what are the constraints on these?
break;
case IrOpcode::kBeginRegion:
// TODO(rossberg): what are the constraints on these?
break;
......@@ -686,6 +689,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckUpperIs(node, Type::Boolean());
break;
}
case IrOpcode::kObjectIsNumber:
case IrOpcode::kObjectIsSmi:
CheckValueInputIs(node, 0, Type::Any());
CheckUpperIs(node, Type::Boolean());
......
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