[turbofan] Introduce initial support for TypedArrays.
This adds support for lowering keyed access to JSTypedArray objects to element loads and stores instead of IC calls. There's still a lot of room for improvement, but the improvements can be done incrementally later. We add a dedicated UnsafePointerAdd operator, which sits in the effect chain, and does the (GC invisible) computation of addresses that are potentially inside HeapObjects. Also there's now a dedicated Retain operator, which ensures that we retain a certain tagged value, which is necessary to ensure that we keep a JSArrayBuffer alive as long as we might still potentially access elements in its backing store. R=epertoso@chromium.org Review-Url: https://codereview.chromium.org/2203693002 Cr-Commit-Position: refs/heads/master@{#38235}
This commit is contained in:
parent
8135caef32
commit
66f2d3bd66
@ -327,6 +327,27 @@ FieldAccess AccessBuilder::ForFixedArrayLength() {
|
|||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
FieldAccess AccessBuilder::ForFixedTypedArrayBaseBasePointer() {
|
||||||
|
FieldAccess access = {kTaggedBase,
|
||||||
|
FixedTypedArrayBase::kBasePointerOffset,
|
||||||
|
MaybeHandle<Name>(),
|
||||||
|
Type::Tagged(),
|
||||||
|
MachineType::AnyTagged(),
|
||||||
|
kPointerWriteBarrier};
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
FieldAccess AccessBuilder::ForFixedTypedArrayBaseExternalPointer() {
|
||||||
|
FieldAccess access = {kTaggedBase,
|
||||||
|
FixedTypedArrayBase::kExternalPointerOffset,
|
||||||
|
MaybeHandle<Name>(),
|
||||||
|
Type::UntaggedPointer(),
|
||||||
|
MachineType::Pointer(),
|
||||||
|
kNoWriteBarrier};
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
FieldAccess AccessBuilder::ForDescriptorArrayEnumCache() {
|
FieldAccess AccessBuilder::ForDescriptorArrayEnumCache() {
|
||||||
|
@ -106,6 +106,12 @@ class AccessBuilder final : public AllStatic {
|
|||||||
// Provides access to FixedArray::length() field.
|
// Provides access to FixedArray::length() field.
|
||||||
static FieldAccess ForFixedArrayLength();
|
static FieldAccess ForFixedArrayLength();
|
||||||
|
|
||||||
|
// Provides access to FixedTypedArrayBase::base_pointer() field.
|
||||||
|
static FieldAccess ForFixedTypedArrayBaseBasePointer();
|
||||||
|
|
||||||
|
// Provides access to FixedTypedArrayBase::external_pointer() field.
|
||||||
|
static FieldAccess ForFixedTypedArrayBaseExternalPointer();
|
||||||
|
|
||||||
// Provides access to DescriptorArray::enum_cache() field.
|
// Provides access to DescriptorArray::enum_cache() field.
|
||||||
static FieldAccess ForDescriptorArrayEnumCache();
|
static FieldAccess ForDescriptorArrayEnumCache();
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ bool CanInlineElementAccess(Handle<Map> map) {
|
|||||||
ElementsKind const elements_kind = map->elements_kind();
|
ElementsKind const elements_kind = map->elements_kind();
|
||||||
if (IsFastElementsKind(elements_kind)) return true;
|
if (IsFastElementsKind(elements_kind)) return true;
|
||||||
// TODO(bmeurer): Add support for other elements kind.
|
// TODO(bmeurer): Add support for other elements kind.
|
||||||
|
if (elements_kind == UINT8_CLAMPED_ELEMENTS) return false;
|
||||||
|
if (IsFixedTypedArrayElementsKind(elements_kind)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,21 +235,22 @@ std::ostream& operator<<(std::ostream& os,
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CACHED_OP_LIST(V) \
|
#define CACHED_OP_LIST(V) \
|
||||||
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
|
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
|
||||||
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
||||||
V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
||||||
V(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
V(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
||||||
V(IfDefault, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
V(IfDefault, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
||||||
V(Throw, Operator::kKontrol, 1, 1, 1, 0, 0, 1) \
|
V(Throw, Operator::kKontrol, 1, 1, 1, 0, 0, 1) \
|
||||||
V(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1) \
|
V(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1) \
|
||||||
V(OsrNormalEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
|
V(OsrNormalEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
|
||||||
V(OsrLoopEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
|
V(OsrLoopEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
|
||||||
V(LoopExit, Operator::kKontrol, 0, 0, 2, 0, 0, 1) \
|
V(LoopExit, Operator::kKontrol, 0, 0, 2, 0, 0, 1) \
|
||||||
V(LoopExitValue, Operator::kPure, 1, 0, 1, 1, 0, 0) \
|
V(LoopExitValue, Operator::kPure, 1, 0, 1, 1, 0, 0) \
|
||||||
V(LoopExitEffect, Operator::kNoThrow, 0, 1, 1, 0, 1, 0) \
|
V(LoopExitEffect, Operator::kNoThrow, 0, 1, 1, 0, 1, 0) \
|
||||||
V(Checkpoint, Operator::kKontrol, 0, 1, 1, 0, 1, 0) \
|
V(Checkpoint, Operator::kKontrol, 0, 1, 1, 0, 1, 0) \
|
||||||
V(FinishRegion, Operator::kKontrol, 1, 1, 0, 1, 1, 0)
|
V(FinishRegion, Operator::kKontrol, 1, 1, 0, 1, 1, 0) \
|
||||||
|
V(Retain, Operator::kKontrol, 1, 1, 0, 0, 1, 0)
|
||||||
|
|
||||||
#define CACHED_RETURN_LIST(V) \
|
#define CACHED_RETURN_LIST(V) \
|
||||||
V(1) \
|
V(1) \
|
||||||
|
@ -248,6 +248,7 @@ class CommonOperatorBuilder final : public ZoneObject {
|
|||||||
const Operator* Call(const CallDescriptor* descriptor);
|
const Operator* Call(const CallDescriptor* descriptor);
|
||||||
const Operator* TailCall(const CallDescriptor* descriptor);
|
const Operator* TailCall(const CallDescriptor* descriptor);
|
||||||
const Operator* Projection(size_t index);
|
const Operator* Projection(size_t index);
|
||||||
|
const Operator* Retain();
|
||||||
|
|
||||||
// Constructs a new merge or phi operator with the same opcode as {op}, but
|
// Constructs a new merge or phi operator with the same opcode as {op}, but
|
||||||
// with {size} inputs.
|
// with {size} inputs.
|
||||||
|
@ -722,6 +722,12 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
|
|||||||
case IrOpcode::kTransitionElementsKind:
|
case IrOpcode::kTransitionElementsKind:
|
||||||
state = LowerTransitionElementsKind(node, *effect, *control);
|
state = LowerTransitionElementsKind(node, *effect, *control);
|
||||||
break;
|
break;
|
||||||
|
case IrOpcode::kLoadTypedElement:
|
||||||
|
state = LowerLoadTypedElement(node, *effect, *control);
|
||||||
|
break;
|
||||||
|
case IrOpcode::kStoreTypedElement:
|
||||||
|
state = LowerStoreTypedElement(node, *effect, *control);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2619,6 +2625,59 @@ EffectControlLinearizer::LowerTransitionElementsKind(Node* node, Node* effect,
|
|||||||
return ValueEffectControl(nullptr, effect, control);
|
return ValueEffectControl(nullptr, effect, control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EffectControlLinearizer::ValueEffectControl
|
||||||
|
EffectControlLinearizer::LowerLoadTypedElement(Node* node, Node* effect,
|
||||||
|
Node* control) {
|
||||||
|
ExternalArrayType array_type = ExternalArrayTypeOf(node->op());
|
||||||
|
Node* buffer = node->InputAt(0);
|
||||||
|
Node* base = node->InputAt(1);
|
||||||
|
Node* external = node->InputAt(2);
|
||||||
|
Node* index = node->InputAt(3);
|
||||||
|
|
||||||
|
// We need to keep the {buffer} alive so that the GC will not release the
|
||||||
|
// ArrayBuffer (if there's any) as long as we are still operating on it.
|
||||||
|
effect = graph()->NewNode(common()->Retain(), buffer, effect);
|
||||||
|
|
||||||
|
// Compute the effective storage pointer.
|
||||||
|
Node* storage = effect = graph()->NewNode(machine()->UnsafePointerAdd(), base,
|
||||||
|
external, effect, control);
|
||||||
|
|
||||||
|
// Perform the actual typed element access.
|
||||||
|
Node* value = effect = graph()->NewNode(
|
||||||
|
simplified()->LoadElement(
|
||||||
|
AccessBuilder::ForTypedArrayElement(array_type, true)),
|
||||||
|
storage, index, effect, control);
|
||||||
|
|
||||||
|
return ValueEffectControl(value, effect, control);
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectControlLinearizer::ValueEffectControl
|
||||||
|
EffectControlLinearizer::LowerStoreTypedElement(Node* node, Node* effect,
|
||||||
|
Node* control) {
|
||||||
|
ExternalArrayType array_type = ExternalArrayTypeOf(node->op());
|
||||||
|
Node* buffer = node->InputAt(0);
|
||||||
|
Node* base = node->InputAt(1);
|
||||||
|
Node* external = node->InputAt(2);
|
||||||
|
Node* index = node->InputAt(3);
|
||||||
|
Node* value = node->InputAt(4);
|
||||||
|
|
||||||
|
// We need to keep the {buffer} alive so that the GC will not release the
|
||||||
|
// ArrayBuffer (if there's any) as long as we are still operating on it.
|
||||||
|
effect = graph()->NewNode(common()->Retain(), buffer, effect);
|
||||||
|
|
||||||
|
// Compute the effective storage pointer.
|
||||||
|
Node* storage = effect = graph()->NewNode(machine()->UnsafePointerAdd(), base,
|
||||||
|
external, effect, control);
|
||||||
|
|
||||||
|
// Perform the actual typed element access.
|
||||||
|
effect = graph()->NewNode(
|
||||||
|
simplified()->StoreElement(
|
||||||
|
AccessBuilder::ForTypedArrayElement(array_type, true)),
|
||||||
|
storage, index, value, effect, control);
|
||||||
|
|
||||||
|
return ValueEffectControl(nullptr, effect, control);
|
||||||
|
}
|
||||||
|
|
||||||
Factory* EffectControlLinearizer::factory() const {
|
Factory* EffectControlLinearizer::factory() const {
|
||||||
return isolate()->factory();
|
return isolate()->factory();
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,10 @@ class EffectControlLinearizer {
|
|||||||
Node* control);
|
Node* control);
|
||||||
ValueEffectControl LowerTransitionElementsKind(Node* node, Node* effect,
|
ValueEffectControl LowerTransitionElementsKind(Node* node, Node* effect,
|
||||||
Node* control);
|
Node* control);
|
||||||
|
ValueEffectControl LowerLoadTypedElement(Node* node, Node* effect,
|
||||||
|
Node* control);
|
||||||
|
ValueEffectControl LowerStoreTypedElement(Node* node, Node* effect,
|
||||||
|
Node* control);
|
||||||
|
|
||||||
ValueEffectControl AllocateHeapNumberWithValue(Node* node, Node* effect,
|
ValueEffectControl AllocateHeapNumberWithValue(Node* node, Node* effect,
|
||||||
Node* control);
|
Node* control);
|
||||||
|
@ -299,6 +299,9 @@ void InstructionSelector::MarkAsDefined(Node* node) {
|
|||||||
|
|
||||||
bool InstructionSelector::IsUsed(Node* node) const {
|
bool InstructionSelector::IsUsed(Node* node) const {
|
||||||
DCHECK_NOT_NULL(node);
|
DCHECK_NOT_NULL(node);
|
||||||
|
// TODO(bmeurer): This is a terrible monster hack, but we have to make sure
|
||||||
|
// that the Retain is actually emitted, otherwise the GC will mess up.
|
||||||
|
if (node->opcode() == IrOpcode::kRetain) return true;
|
||||||
if (!node->op()->HasProperty(Operator::kEliminatable)) return true;
|
if (!node->op()->HasProperty(Operator::kEliminatable)) return true;
|
||||||
size_t const id = node->id();
|
size_t const id = node->id();
|
||||||
DCHECK_LT(id, used_.size());
|
DCHECK_LT(id, used_.size());
|
||||||
@ -929,6 +932,9 @@ void InstructionSelector::VisitNode(Node* node) {
|
|||||||
case IrOpcode::kComment:
|
case IrOpcode::kComment:
|
||||||
VisitComment(node);
|
VisitComment(node);
|
||||||
return;
|
return;
|
||||||
|
case IrOpcode::kRetain:
|
||||||
|
VisitRetain(node);
|
||||||
|
return;
|
||||||
case IrOpcode::kLoad: {
|
case IrOpcode::kLoad: {
|
||||||
LoadRepresentation type = LoadRepresentationOf(node->op());
|
LoadRepresentation type = LoadRepresentationOf(node->op());
|
||||||
MarkAsRepresentation(type.representation(), node);
|
MarkAsRepresentation(type.representation(), node);
|
||||||
@ -1297,6 +1303,9 @@ void InstructionSelector::VisitNode(Node* node) {
|
|||||||
}
|
}
|
||||||
case IrOpcode::kAtomicStore:
|
case IrOpcode::kAtomicStore:
|
||||||
return VisitAtomicStore(node);
|
return VisitAtomicStore(node);
|
||||||
|
case IrOpcode::kUnsafePointerAdd:
|
||||||
|
MarkAsRepresentation(MachineType::PointerRepresentation(), node);
|
||||||
|
return VisitUnsafePointerAdd(node);
|
||||||
default:
|
default:
|
||||||
V8_Fatal(__FILE__, __LINE__, "Unexpected operator #%d:%s @ node #%d",
|
V8_Fatal(__FILE__, __LINE__, "Unexpected operator #%d:%s @ node #%d",
|
||||||
node->opcode(), node->op()->mnemonic(), node->id());
|
node->opcode(), node->op()->mnemonic(), node->id());
|
||||||
@ -2016,6 +2025,19 @@ void InstructionSelector::VisitComment(Node* node) {
|
|||||||
Emit(kArchComment, 0, nullptr, 1, &operand);
|
Emit(kArchComment, 0, nullptr, 1, &operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstructionSelector::VisitUnsafePointerAdd(Node* node) {
|
||||||
|
#if V8_TARGET_ARCH_64_BIT
|
||||||
|
VisitInt64Add(node);
|
||||||
|
#else // V8_TARGET_ARCH_64_BIT
|
||||||
|
VisitInt32Add(node);
|
||||||
|
#endif // V8_TARGET_ARCH_64_BIT
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionSelector::VisitRetain(Node* node) {
|
||||||
|
OperandGenerator g(this);
|
||||||
|
Emit(kArchNop, g.NoOutput(), g.UseAny(node->InputAt(0)));
|
||||||
|
}
|
||||||
|
|
||||||
bool InstructionSelector::CanProduceSignalingNaN(Node* node) {
|
bool InstructionSelector::CanProduceSignalingNaN(Node* node) {
|
||||||
// TODO(jarin) Improve the heuristic here.
|
// TODO(jarin) Improve the heuristic here.
|
||||||
if (node->opcode() == IrOpcode::kFloat64Add ||
|
if (node->opcode() == IrOpcode::kFloat64Add ||
|
||||||
|
@ -296,6 +296,7 @@ class InstructionSelector final {
|
|||||||
Node* value);
|
Node* value);
|
||||||
void VisitReturn(Node* ret);
|
void VisitReturn(Node* ret);
|
||||||
void VisitThrow(Node* value);
|
void VisitThrow(Node* value);
|
||||||
|
void VisitRetain(Node* node);
|
||||||
|
|
||||||
void EmitPrepareArguments(ZoneVector<compiler::PushParameter>* arguments,
|
void EmitPrepareArguments(ZoneVector<compiler::PushParameter>* arguments,
|
||||||
const CallDescriptor* descriptor, Node* node);
|
const CallDescriptor* descriptor, Node* node);
|
||||||
|
@ -732,9 +732,9 @@ Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor(
|
|||||||
jsgraph()->ZeroConstant());
|
jsgraph()->ZeroConstant());
|
||||||
|
|
||||||
// Default to zero if the {receiver}s buffer was neutered.
|
// Default to zero if the {receiver}s buffer was neutered.
|
||||||
Node* value =
|
Node* value = graph()->NewNode(
|
||||||
graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
|
common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue),
|
||||||
check, receiver_length, jsgraph()->ZeroConstant());
|
check, receiver_length, jsgraph()->ZeroConstant());
|
||||||
|
|
||||||
ReplaceWithValue(node, value, effect, control);
|
ReplaceWithValue(node, value, effect, control);
|
||||||
return Replace(value);
|
return Replace(value);
|
||||||
|
@ -930,6 +930,24 @@ JSNativeContextSpecialization::BuildPropertyAccess(
|
|||||||
return ValueEffectControl(value, effect, control);
|
return ValueEffectControl(value, effect, control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
|
||||||
|
switch (kind) {
|
||||||
|
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
||||||
|
case TYPE##_ELEMENTS: \
|
||||||
|
return kExternal##Type##Array;
|
||||||
|
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||||
|
#undef TYPED_ARRAY_CASE
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return kExternalInt8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
JSNativeContextSpecialization::ValueEffectControl
|
JSNativeContextSpecialization::ValueEffectControl
|
||||||
JSNativeContextSpecialization::BuildElementAccess(
|
JSNativeContextSpecialization::BuildElementAccess(
|
||||||
Node* receiver, Node* index, Node* value, Node* effect, Node* control,
|
Node* receiver, Node* index, Node* value, Node* effect, Node* control,
|
||||||
@ -963,105 +981,167 @@ JSNativeContextSpecialization::BuildElementAccess(
|
|||||||
effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
|
effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the length of the {receiver}.
|
if (IsFixedTypedArrayElementsKind(elements_kind)) {
|
||||||
Node* length = effect =
|
// Load the {receiver}s length.
|
||||||
HasOnlyJSArrayMaps(receiver_maps)
|
Node* length = effect = graph()->NewNode(
|
||||||
? graph()->NewNode(
|
simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()),
|
||||||
simplified()->LoadField(
|
receiver, effect, control);
|
||||||
AccessBuilder::ForJSArrayLength(elements_kind)),
|
|
||||||
receiver, effect, control)
|
|
||||||
: graph()->NewNode(
|
|
||||||
simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
|
|
||||||
elements, effect, control);
|
|
||||||
|
|
||||||
// Check that the {index} is in the valid range for the {receiver}.
|
// Check if the {receiver}s buffer was neutered.
|
||||||
index = effect = graph()->NewNode(simplified()->CheckBounds(), index, length,
|
Node* buffer = effect = graph()->NewNode(
|
||||||
effect, control);
|
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
|
||||||
|
receiver, effect, control);
|
||||||
|
Node* buffer_bitfield = effect = graph()->NewNode(
|
||||||
|
simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()),
|
||||||
|
buffer, effect, control);
|
||||||
|
Node* check = graph()->NewNode(
|
||||||
|
simplified()->NumberEqual(),
|
||||||
|
graph()->NewNode(
|
||||||
|
simplified()->NumberBitwiseAnd(), buffer_bitfield,
|
||||||
|
jsgraph()->Constant(JSArrayBuffer::WasNeutered::kMask)),
|
||||||
|
jsgraph()->ZeroConstant());
|
||||||
|
|
||||||
// Compute the element access.
|
// Default to zero if the {receiver}s buffer was neutered.
|
||||||
Type* element_type = Type::Any();
|
length = graph()->NewNode(
|
||||||
MachineType element_machine_type = MachineType::AnyTagged();
|
common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue),
|
||||||
if (IsFastDoubleElementsKind(elements_kind)) {
|
check, length, jsgraph()->ZeroConstant());
|
||||||
element_type = Type::Number();
|
|
||||||
element_machine_type = MachineType::Float64();
|
|
||||||
} else if (IsFastSmiElementsKind(elements_kind)) {
|
|
||||||
element_type = type_cache_.kSmi;
|
|
||||||
}
|
|
||||||
ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize,
|
|
||||||
element_type, element_machine_type,
|
|
||||||
kFullWriteBarrier};
|
|
||||||
|
|
||||||
// Access the actual element.
|
// Check that the {index} is in the valid range for the {receiver}.
|
||||||
// TODO(bmeurer): Refactor this into separate methods or even a separate
|
index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
|
||||||
// class that deals with the elements access.
|
length, effect, control);
|
||||||
if (access_mode == AccessMode::kLoad) {
|
|
||||||
// Compute the real element access type, which includes the hole in case
|
// Load the base and external pointer for the {receiver}.
|
||||||
// of holey backing stores.
|
Node* base_pointer = effect = graph()->NewNode(
|
||||||
if (elements_kind == FAST_HOLEY_ELEMENTS ||
|
simplified()->LoadField(
|
||||||
elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
|
AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
|
||||||
element_access.type = Type::Union(
|
elements, effect, control);
|
||||||
element_type,
|
Node* external_pointer = effect = graph()->NewNode(
|
||||||
Type::Constant(factory()->the_hole_value(), graph()->zone()),
|
simplified()->LoadField(
|
||||||
graph()->zone());
|
AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
|
||||||
}
|
elements, effect, control);
|
||||||
// Perform the actual backing store access.
|
|
||||||
value = effect = graph()->NewNode(simplified()->LoadElement(element_access),
|
// Access the actual element.
|
||||||
elements, index, effect, control);
|
ExternalArrayType external_array_type =
|
||||||
// Handle loading from holey backing stores correctly, by either mapping
|
GetArrayTypeFromElementsKind(elements_kind);
|
||||||
// the hole to undefined if possible, or deoptimizing otherwise.
|
switch (access_mode) {
|
||||||
if (elements_kind == FAST_HOLEY_ELEMENTS ||
|
case AccessMode::kLoad: {
|
||||||
elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
|
value = effect = graph()->NewNode(
|
||||||
// Perform the hole check on the result.
|
simplified()->LoadTypedElement(external_array_type), buffer,
|
||||||
CheckTaggedHoleMode mode = CheckTaggedHoleMode::kNeverReturnHole;
|
base_pointer, external_pointer, index, effect, control);
|
||||||
// Check if we are allowed to turn the hole into undefined.
|
break;
|
||||||
// TODO(bmeurer): We might check the JSArray map from a different
|
|
||||||
// context here; may need reinvestigation.
|
|
||||||
if (receiver_maps.size() == 1 &&
|
|
||||||
receiver_maps[0].is_identical_to(
|
|
||||||
handle(isolate()->get_initial_js_array_map(elements_kind))) &&
|
|
||||||
isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
|
|
||||||
// Add a code dependency on the array protector cell.
|
|
||||||
dependencies()->AssumePrototypeMapsStable(
|
|
||||||
receiver_maps[0], isolate()->initial_object_prototype());
|
|
||||||
dependencies()->AssumePropertyCell(factory()->array_protector());
|
|
||||||
// Turn the hole into undefined.
|
|
||||||
mode = CheckTaggedHoleMode::kConvertHoleToUndefined;
|
|
||||||
}
|
}
|
||||||
value = effect = graph()->NewNode(simplified()->CheckTaggedHole(mode),
|
case AccessMode::kStore: {
|
||||||
value, effect, control);
|
// Ensure that the {value} is actually a Number.
|
||||||
} else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
|
value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
|
||||||
// Perform the hole check on the result.
|
effect, control);
|
||||||
CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
|
effect = graph()->NewNode(
|
||||||
// Check if we are allowed to return the hole directly.
|
simplified()->StoreTypedElement(external_array_type), buffer,
|
||||||
// TODO(bmeurer): We might check the JSArray map from a different
|
base_pointer, external_pointer, index, value, effect, control);
|
||||||
// context here; may need reinvestigation.
|
break;
|
||||||
if (receiver_maps.size() == 1 &&
|
|
||||||
receiver_maps[0].is_identical_to(
|
|
||||||
handle(isolate()->get_initial_js_array_map(elements_kind))) &&
|
|
||||||
isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
|
|
||||||
// Add a code dependency on the array protector cell.
|
|
||||||
dependencies()->AssumePrototypeMapsStable(
|
|
||||||
receiver_maps[0], isolate()->initial_object_prototype());
|
|
||||||
dependencies()->AssumePropertyCell(factory()->array_protector());
|
|
||||||
// Return the signaling NaN hole directly if all uses are truncating.
|
|
||||||
mode = CheckFloat64HoleMode::kAllowReturnHole;
|
|
||||||
}
|
}
|
||||||
value = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode),
|
|
||||||
value, effect, control);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DCHECK_EQ(AccessMode::kStore, access_mode);
|
// Load the length of the {receiver}.
|
||||||
if (IsFastSmiElementsKind(elements_kind)) {
|
Node* length = effect =
|
||||||
value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(),
|
HasOnlyJSArrayMaps(receiver_maps)
|
||||||
value, effect, control);
|
? graph()->NewNode(
|
||||||
} else if (IsFastDoubleElementsKind(elements_kind)) {
|
simplified()->LoadField(
|
||||||
value = effect =
|
AccessBuilder::ForJSArrayLength(elements_kind)),
|
||||||
graph()->NewNode(simplified()->CheckNumber(), value, effect, control);
|
receiver, effect, control)
|
||||||
// Make sure we do not store signalling NaNs into double arrays.
|
: graph()->NewNode(
|
||||||
value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
|
simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
|
||||||
|
elements, effect, control);
|
||||||
|
|
||||||
|
// Check that the {index} is in the valid range for the {receiver}.
|
||||||
|
index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
|
||||||
|
length, effect, control);
|
||||||
|
|
||||||
|
// Compute the element access.
|
||||||
|
Type* element_type = Type::Any();
|
||||||
|
MachineType element_machine_type = MachineType::AnyTagged();
|
||||||
|
if (IsFastDoubleElementsKind(elements_kind)) {
|
||||||
|
element_type = Type::Number();
|
||||||
|
element_machine_type = MachineType::Float64();
|
||||||
|
} else if (IsFastSmiElementsKind(elements_kind)) {
|
||||||
|
element_type = type_cache_.kSmi;
|
||||||
|
}
|
||||||
|
ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize,
|
||||||
|
element_type, element_machine_type,
|
||||||
|
kFullWriteBarrier};
|
||||||
|
|
||||||
|
// Access the actual element.
|
||||||
|
// TODO(bmeurer): Refactor this into separate methods or even a separate
|
||||||
|
// class that deals with the elements access.
|
||||||
|
if (access_mode == AccessMode::kLoad) {
|
||||||
|
// Compute the real element access type, which includes the hole in case
|
||||||
|
// of holey backing stores.
|
||||||
|
if (elements_kind == FAST_HOLEY_ELEMENTS ||
|
||||||
|
elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
|
||||||
|
element_access.type = Type::Union(
|
||||||
|
element_type,
|
||||||
|
Type::Constant(factory()->the_hole_value(), graph()->zone()),
|
||||||
|
graph()->zone());
|
||||||
|
}
|
||||||
|
// Perform the actual backing store access.
|
||||||
|
value = effect =
|
||||||
|
graph()->NewNode(simplified()->LoadElement(element_access), elements,
|
||||||
|
index, effect, control);
|
||||||
|
// Handle loading from holey backing stores correctly, by either mapping
|
||||||
|
// the hole to undefined if possible, or deoptimizing otherwise.
|
||||||
|
if (elements_kind == FAST_HOLEY_ELEMENTS ||
|
||||||
|
elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
|
||||||
|
// Perform the hole check on the result.
|
||||||
|
CheckTaggedHoleMode mode = CheckTaggedHoleMode::kNeverReturnHole;
|
||||||
|
// Check if we are allowed to turn the hole into undefined.
|
||||||
|
// TODO(bmeurer): We might check the JSArray map from a different
|
||||||
|
// context here; may need reinvestigation.
|
||||||
|
if (receiver_maps.size() == 1 &&
|
||||||
|
receiver_maps[0].is_identical_to(
|
||||||
|
handle(isolate()->get_initial_js_array_map(elements_kind))) &&
|
||||||
|
isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
|
||||||
|
// Add a code dependency on the array protector cell.
|
||||||
|
dependencies()->AssumePrototypeMapsStable(
|
||||||
|
receiver_maps[0], isolate()->initial_object_prototype());
|
||||||
|
dependencies()->AssumePropertyCell(factory()->array_protector());
|
||||||
|
// Turn the hole into undefined.
|
||||||
|
mode = CheckTaggedHoleMode::kConvertHoleToUndefined;
|
||||||
|
}
|
||||||
|
value = effect = graph()->NewNode(simplified()->CheckTaggedHole(mode),
|
||||||
|
value, effect, control);
|
||||||
|
} else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
|
||||||
|
// Perform the hole check on the result.
|
||||||
|
CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
|
||||||
|
// Check if we are allowed to return the hole directly.
|
||||||
|
// TODO(bmeurer): We might check the JSArray map from a different
|
||||||
|
// context here; may need reinvestigation.
|
||||||
|
if (receiver_maps.size() == 1 &&
|
||||||
|
receiver_maps[0].is_identical_to(
|
||||||
|
handle(isolate()->get_initial_js_array_map(elements_kind))) &&
|
||||||
|
isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
|
||||||
|
// Add a code dependency on the array protector cell.
|
||||||
|
dependencies()->AssumePrototypeMapsStable(
|
||||||
|
receiver_maps[0], isolate()->initial_object_prototype());
|
||||||
|
dependencies()->AssumePropertyCell(factory()->array_protector());
|
||||||
|
// Return the signaling NaN hole directly if all uses are truncating.
|
||||||
|
mode = CheckFloat64HoleMode::kAllowReturnHole;
|
||||||
|
}
|
||||||
|
value = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode),
|
||||||
|
value, effect, control);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DCHECK_EQ(AccessMode::kStore, access_mode);
|
||||||
|
if (IsFastSmiElementsKind(elements_kind)) {
|
||||||
|
value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(),
|
||||||
|
value, effect, control);
|
||||||
|
} else if (IsFastDoubleElementsKind(elements_kind)) {
|
||||||
|
value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
|
||||||
|
effect, control);
|
||||||
|
// Make sure we do not store signalling NaNs into double arrays.
|
||||||
|
value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
|
||||||
|
}
|
||||||
|
effect = graph()->NewNode(simplified()->StoreElement(element_access),
|
||||||
|
elements, index, value, effect, control);
|
||||||
}
|
}
|
||||||
effect = graph()->NewNode(simplified()->StoreElement(element_access),
|
|
||||||
elements, index, value, effect, control);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ValueEffectControl(value, effect, control);
|
return ValueEffectControl(value, effect, control);
|
||||||
|
@ -63,6 +63,8 @@ Reduction LoadElimination::Reduce(Node* node) {
|
|||||||
return ReduceLoadElement(node);
|
return ReduceLoadElement(node);
|
||||||
case IrOpcode::kStoreElement:
|
case IrOpcode::kStoreElement:
|
||||||
return ReduceStoreElement(node);
|
return ReduceStoreElement(node);
|
||||||
|
case IrOpcode::kStoreTypedElement:
|
||||||
|
return ReduceStoreTypedElement(node);
|
||||||
case IrOpcode::kEffectPhi:
|
case IrOpcode::kEffectPhi:
|
||||||
return ReduceEffectPhi(node);
|
return ReduceEffectPhi(node);
|
||||||
case IrOpcode::kDead:
|
case IrOpcode::kDead:
|
||||||
@ -455,6 +457,13 @@ Reduction LoadElimination::ReduceStoreElement(Node* node) {
|
|||||||
return UpdateState(node, state);
|
return UpdateState(node, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reduction LoadElimination::ReduceStoreTypedElement(Node* node) {
|
||||||
|
Node* const effect = NodeProperties::GetEffectInput(node);
|
||||||
|
AbstractState const* state = node_states_.Get(effect);
|
||||||
|
if (state == nullptr) return NoChange();
|
||||||
|
return UpdateState(node, state);
|
||||||
|
}
|
||||||
|
|
||||||
Reduction LoadElimination::ReduceEffectPhi(Node* node) {
|
Reduction LoadElimination::ReduceEffectPhi(Node* node) {
|
||||||
Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
|
Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
|
||||||
Node* const control = NodeProperties::GetControlInput(node);
|
Node* const control = NodeProperties::GetControlInput(node);
|
||||||
@ -571,15 +580,20 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
|
|||||||
|
|
||||||
// static
|
// static
|
||||||
int LoadElimination::FieldIndexOf(FieldAccess const& access) {
|
int LoadElimination::FieldIndexOf(FieldAccess const& access) {
|
||||||
switch (access.machine_type.representation()) {
|
MachineRepresentation rep = access.machine_type.representation();
|
||||||
|
switch (rep) {
|
||||||
case MachineRepresentation::kNone:
|
case MachineRepresentation::kNone:
|
||||||
case MachineRepresentation::kBit:
|
case MachineRepresentation::kBit:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
break;
|
break;
|
||||||
case MachineRepresentation::kWord8:
|
|
||||||
case MachineRepresentation::kWord16:
|
|
||||||
case MachineRepresentation::kWord32:
|
case MachineRepresentation::kWord32:
|
||||||
case MachineRepresentation::kWord64:
|
case MachineRepresentation::kWord64:
|
||||||
|
if (rep != MachineType::PointerRepresentation()) {
|
||||||
|
return -1; // We currently only track pointer size fields.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MachineRepresentation::kWord8:
|
||||||
|
case MachineRepresentation::kWord16:
|
||||||
case MachineRepresentation::kFloat32:
|
case MachineRepresentation::kFloat32:
|
||||||
return -1; // Currently untracked.
|
return -1; // Currently untracked.
|
||||||
case MachineRepresentation::kFloat64:
|
case MachineRepresentation::kFloat64:
|
||||||
|
@ -155,6 +155,7 @@ class LoadElimination final : public AdvancedReducer {
|
|||||||
Reduction ReduceStoreField(Node* node);
|
Reduction ReduceStoreField(Node* node);
|
||||||
Reduction ReduceLoadElement(Node* node);
|
Reduction ReduceLoadElement(Node* node);
|
||||||
Reduction ReduceStoreElement(Node* node);
|
Reduction ReduceStoreElement(Node* node);
|
||||||
|
Reduction ReduceStoreTypedElement(Node* node);
|
||||||
Reduction ReduceEffectPhi(Node* node);
|
Reduction ReduceEffectPhi(Node* node);
|
||||||
Reduction ReduceStart(Node* node);
|
Reduction ReduceStart(Node* node);
|
||||||
Reduction ReduceOtherNode(Node* node);
|
Reduction ReduceOtherNode(Node* node);
|
||||||
|
@ -611,6 +611,13 @@ struct MachineOperatorGlobalCache {
|
|||||||
0, 0, 0, 0, 0) {}
|
0, 0, 0, 0, 0) {}
|
||||||
};
|
};
|
||||||
DebugBreakOperator kDebugBreak;
|
DebugBreakOperator kDebugBreak;
|
||||||
|
|
||||||
|
struct UnsafePointerAddOperator final : public Operator {
|
||||||
|
UnsafePointerAddOperator()
|
||||||
|
: Operator(IrOpcode::kUnsafePointerAdd, Operator::kKontrol,
|
||||||
|
"UnsafePointerAdd", 2, 1, 1, 1, 1, 0) {}
|
||||||
|
};
|
||||||
|
UnsafePointerAddOperator kUnsafePointerAdd;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CommentOperator : public Operator1<const char*> {
|
struct CommentOperator : public Operator1<const char*> {
|
||||||
@ -728,6 +735,10 @@ const Operator* MachineOperatorBuilder::Store(StoreRepresentation store_rep) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Operator* MachineOperatorBuilder::UnsafePointerAdd() {
|
||||||
|
return &cache_.kUnsafePointerAdd;
|
||||||
|
}
|
||||||
|
|
||||||
const Operator* MachineOperatorBuilder::DebugBreak() {
|
const Operator* MachineOperatorBuilder::DebugBreak() {
|
||||||
return &cache_.kDebugBreak;
|
return &cache_.kDebugBreak;
|
||||||
}
|
}
|
||||||
|
@ -214,6 +214,7 @@ class MachineOperatorBuilder final : public ZoneObject {
|
|||||||
|
|
||||||
const Operator* Comment(const char* msg);
|
const Operator* Comment(const char* msg);
|
||||||
const Operator* DebugBreak();
|
const Operator* DebugBreak();
|
||||||
|
const Operator* UnsafePointerAdd();
|
||||||
|
|
||||||
const Operator* Word32And();
|
const Operator* Word32And();
|
||||||
const Operator* Word32Or();
|
const Operator* Word32Or();
|
||||||
|
@ -92,6 +92,8 @@ void MemoryOptimizer::VisitNode(Node* node, AllocationState const* state) {
|
|||||||
case IrOpcode::kIfException:
|
case IrOpcode::kIfException:
|
||||||
case IrOpcode::kLoad:
|
case IrOpcode::kLoad:
|
||||||
case IrOpcode::kStore:
|
case IrOpcode::kStore:
|
||||||
|
case IrOpcode::kRetain:
|
||||||
|
case IrOpcode::kUnsafePointerAdd:
|
||||||
return VisitOtherEffect(node, state);
|
return VisitOtherEffect(node, state);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -61,7 +61,8 @@
|
|||||||
V(LoopExit) \
|
V(LoopExit) \
|
||||||
V(LoopExitValue) \
|
V(LoopExitValue) \
|
||||||
V(LoopExitEffect) \
|
V(LoopExitEffect) \
|
||||||
V(Projection)
|
V(Projection) \
|
||||||
|
V(Retain)
|
||||||
|
|
||||||
#define COMMON_OP_LIST(V) \
|
#define COMMON_OP_LIST(V) \
|
||||||
CONSTANT_OP_LIST(V) \
|
CONSTANT_OP_LIST(V) \
|
||||||
@ -282,9 +283,11 @@
|
|||||||
V(LoadField) \
|
V(LoadField) \
|
||||||
V(LoadBuffer) \
|
V(LoadBuffer) \
|
||||||
V(LoadElement) \
|
V(LoadElement) \
|
||||||
|
V(LoadTypedElement) \
|
||||||
V(StoreField) \
|
V(StoreField) \
|
||||||
V(StoreBuffer) \
|
V(StoreBuffer) \
|
||||||
V(StoreElement) \
|
V(StoreElement) \
|
||||||
|
V(StoreTypedElement) \
|
||||||
V(ObjectIsCallable) \
|
V(ObjectIsCallable) \
|
||||||
V(ObjectIsNumber) \
|
V(ObjectIsNumber) \
|
||||||
V(ObjectIsReceiver) \
|
V(ObjectIsReceiver) \
|
||||||
@ -473,7 +476,8 @@
|
|||||||
V(Word32PairShr) \
|
V(Word32PairShr) \
|
||||||
V(Word32PairSar) \
|
V(Word32PairSar) \
|
||||||
V(AtomicLoad) \
|
V(AtomicLoad) \
|
||||||
V(AtomicStore)
|
V(AtomicStore) \
|
||||||
|
V(UnsafePointerAdd)
|
||||||
|
|
||||||
#define MACHINE_SIMD_RETURN_SIMD_OP_LIST(V) \
|
#define MACHINE_SIMD_RETURN_SIMD_OP_LIST(V) \
|
||||||
V(CreateFloat32x4) \
|
V(CreateFloat32x4) \
|
||||||
|
@ -65,6 +65,27 @@ enum Phase {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
MachineRepresentation MachineRepresentationFromArrayType(
|
||||||
|
ExternalArrayType array_type) {
|
||||||
|
switch (array_type) {
|
||||||
|
case kExternalUint8Array:
|
||||||
|
case kExternalUint8ClampedArray:
|
||||||
|
case kExternalInt8Array:
|
||||||
|
return MachineRepresentation::kWord8;
|
||||||
|
case kExternalUint16Array:
|
||||||
|
case kExternalInt16Array:
|
||||||
|
return MachineRepresentation::kWord16;
|
||||||
|
case kExternalUint32Array:
|
||||||
|
case kExternalInt32Array:
|
||||||
|
return MachineRepresentation::kWord32;
|
||||||
|
case kExternalFloat32Array:
|
||||||
|
return MachineRepresentation::kFloat32;
|
||||||
|
case kExternalFloat64Array:
|
||||||
|
return MachineRepresentation::kFloat64;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return MachineRepresentation::kNone;
|
||||||
|
}
|
||||||
|
|
||||||
UseInfo TruncatingUseInfoFromRepresentation(MachineRepresentation rep) {
|
UseInfo TruncatingUseInfoFromRepresentation(MachineRepresentation rep) {
|
||||||
switch (rep) {
|
switch (rep) {
|
||||||
@ -2209,6 +2230,30 @@ class RepresentationSelector {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case IrOpcode::kLoadTypedElement: {
|
||||||
|
MachineRepresentation const rep =
|
||||||
|
MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op()));
|
||||||
|
ProcessInput(node, 0, UseInfo::AnyTagged()); // buffer
|
||||||
|
ProcessInput(node, 1, UseInfo::AnyTagged()); // base pointer
|
||||||
|
ProcessInput(node, 2, UseInfo::PointerInt()); // external pointer
|
||||||
|
ProcessInput(node, 3, UseInfo::TruncatingWord32()); // index
|
||||||
|
ProcessRemainingInputs(node, 4);
|
||||||
|
SetOutput(node, rep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case IrOpcode::kStoreTypedElement: {
|
||||||
|
MachineRepresentation const rep =
|
||||||
|
MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op()));
|
||||||
|
ProcessInput(node, 0, UseInfo::AnyTagged()); // buffer
|
||||||
|
ProcessInput(node, 1, UseInfo::AnyTagged()); // base pointer
|
||||||
|
ProcessInput(node, 2, UseInfo::PointerInt()); // external pointer
|
||||||
|
ProcessInput(node, 3, UseInfo::TruncatingWord32()); // index
|
||||||
|
ProcessInput(node, 4,
|
||||||
|
TruncatingUseInfoFromRepresentation(rep)); // value
|
||||||
|
ProcessRemainingInputs(node, 5);
|
||||||
|
SetOutput(node, MachineRepresentation::kNone);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case IrOpcode::kPlainPrimitiveToNumber: {
|
case IrOpcode::kPlainPrimitiveToNumber: {
|
||||||
if (InputIs(node, Type::Boolean())) {
|
if (InputIs(node, Type::Boolean())) {
|
||||||
VisitUnop(node, UseInfo::Bool(), MachineRepresentation::kWord32);
|
VisitUnop(node, UseInfo::Bool(), MachineRepresentation::kWord32);
|
||||||
|
@ -181,6 +181,12 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
|
|||||||
return OpParameter<ElementAccess>(op);
|
return OpParameter<ElementAccess>(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExternalArrayType ExternalArrayTypeOf(const Operator* op) {
|
||||||
|
DCHECK(op->opcode() == IrOpcode::kLoadTypedElement ||
|
||||||
|
op->opcode() == IrOpcode::kStoreTypedElement);
|
||||||
|
return OpParameter<ExternalArrayType>(op);
|
||||||
|
}
|
||||||
|
|
||||||
size_t hash_value(CheckFloat64HoleMode mode) {
|
size_t hash_value(CheckFloat64HoleMode mode) {
|
||||||
return static_cast<size_t>(mode);
|
return static_cast<size_t>(mode);
|
||||||
}
|
}
|
||||||
@ -634,11 +640,13 @@ const Operator* SimplifiedOperatorBuilder::SpeculativeNumberLessThanOrEqual(
|
|||||||
"SpeculativeNumberLessThanOrEqual", 2, 1, 1, 1, 1, 0, hint);
|
"SpeculativeNumberLessThanOrEqual", 2, 1, 1, 1, 1, 0, hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ACCESS_OP_LIST(V) \
|
#define ACCESS_OP_LIST(V) \
|
||||||
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
|
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
|
||||||
V(StoreField, FieldAccess, Operator::kNoRead, 2, 1, 0) \
|
V(StoreField, FieldAccess, Operator::kNoRead, 2, 1, 0) \
|
||||||
V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1, 1) \
|
V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1, 1) \
|
||||||
V(StoreElement, ElementAccess, Operator::kNoRead, 3, 1, 0)
|
V(StoreElement, ElementAccess, Operator::kNoRead, 3, 1, 0) \
|
||||||
|
V(LoadTypedElement, ExternalArrayType, Operator::kNoWrite, 4, 1, 1) \
|
||||||
|
V(StoreTypedElement, ExternalArrayType, Operator::kNoRead, 5, 1, 0)
|
||||||
|
|
||||||
#define ACCESS(Name, Type, properties, value_input_count, control_input_count, \
|
#define ACCESS(Name, Type, properties, value_input_count, control_input_count, \
|
||||||
output_count) \
|
output_count) \
|
||||||
|
@ -107,6 +107,8 @@ std::ostream& operator<<(std::ostream&, ElementAccess const&);
|
|||||||
|
|
||||||
ElementAccess const& ElementAccessOf(const Operator* op) WARN_UNUSED_RESULT;
|
ElementAccess const& ElementAccessOf(const Operator* op) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
ExternalArrayType ExternalArrayTypeOf(const Operator* op) WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
enum class CheckFloat64HoleMode : uint8_t {
|
enum class CheckFloat64HoleMode : uint8_t {
|
||||||
kNeverReturnHole, // Never return the hole (deoptimize instead).
|
kNeverReturnHole, // Never return the hole (deoptimize instead).
|
||||||
kAllowReturnHole // Allow to return the hole (signaling NaN).
|
kAllowReturnHole // Allow to return the hole (signaling NaN).
|
||||||
@ -319,12 +321,18 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
|
|||||||
// store-buffer buffer, offset, length, value
|
// store-buffer buffer, offset, length, value
|
||||||
const Operator* StoreBuffer(BufferAccess);
|
const Operator* StoreBuffer(BufferAccess);
|
||||||
|
|
||||||
// load-element [base + index], length
|
// load-element [base + index]
|
||||||
const Operator* LoadElement(ElementAccess const&);
|
const Operator* LoadElement(ElementAccess const&);
|
||||||
|
|
||||||
// store-element [base + index], length, value
|
// store-element [base + index], value
|
||||||
const Operator* StoreElement(ElementAccess const&);
|
const Operator* StoreElement(ElementAccess const&);
|
||||||
|
|
||||||
|
// load-typed-element buffer, [base + external + index]
|
||||||
|
const Operator* LoadTypedElement(ExternalArrayType const&);
|
||||||
|
|
||||||
|
// store-typed-element buffer, [base + external + index], value
|
||||||
|
const Operator* StoreTypedElement(ExternalArrayType const&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Zone* zone() const { return zone_; }
|
Zone* zone() const { return zone_; }
|
||||||
|
|
||||||
|
@ -1975,6 +1975,17 @@ Type* Typer::Visitor::TypeLoadElement(Node* node) {
|
|||||||
return ElementAccessOf(node->op()).type;
|
return ElementAccessOf(node->op()).type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type* Typer::Visitor::TypeLoadTypedElement(Node* node) {
|
||||||
|
switch (ExternalArrayTypeOf(node->op())) {
|
||||||
|
#define TYPED_ARRAY_CASE(ElemType, type, TYPE, ctype, size) \
|
||||||
|
case kExternal##ElemType##Array: \
|
||||||
|
return typer_->cache_.k##ElemType;
|
||||||
|
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||||
|
#undef TYPED_ARRAY_CASE
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Type* Typer::Visitor::TypeStoreField(Node* node) {
|
Type* Typer::Visitor::TypeStoreField(Node* node) {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
@ -1993,6 +2004,11 @@ Type* Typer::Visitor::TypeStoreElement(Node* node) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type* Typer::Visitor::TypeStoreTypedElement(Node* node) {
|
||||||
|
UNREACHABLE();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Type* Typer::Visitor::TypeObjectIsCallable(Node* node) {
|
Type* Typer::Visitor::TypeObjectIsCallable(Node* node) {
|
||||||
return TypeUnaryOp(node, ObjectIsCallable);
|
return TypeUnaryOp(node, ObjectIsCallable);
|
||||||
}
|
}
|
||||||
@ -2026,6 +2042,13 @@ Type* Typer::Visitor::TypeDebugBreak(Node* node) { return Type::None(); }
|
|||||||
|
|
||||||
Type* Typer::Visitor::TypeComment(Node* node) { return Type::None(); }
|
Type* Typer::Visitor::TypeComment(Node* node) { return Type::None(); }
|
||||||
|
|
||||||
|
Type* Typer::Visitor::TypeRetain(Node* node) {
|
||||||
|
UNREACHABLE();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type* Typer::Visitor::TypeUnsafePointerAdd(Node* node) { return Type::None(); }
|
||||||
|
|
||||||
Type* Typer::Visitor::TypeLoad(Node* node) { return Type::Any(); }
|
Type* Typer::Visitor::TypeLoad(Node* node) { return Type::Any(); }
|
||||||
|
|
||||||
Type* Typer::Visitor::TypeStackSlot(Node* node) { return Type::Any(); }
|
Type* Typer::Visitor::TypeStackSlot(Node* node) { return Type::Any(); }
|
||||||
|
@ -650,11 +650,10 @@ void Verifier::Visitor::Check(Node* node) {
|
|||||||
CheckNotTyped(node);
|
CheckNotTyped(node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IrOpcode::kDebugBreak:
|
|
||||||
CheckNotTyped(node);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IrOpcode::kComment:
|
case IrOpcode::kComment:
|
||||||
|
case IrOpcode::kDebugBreak:
|
||||||
|
case IrOpcode::kRetain:
|
||||||
|
case IrOpcode::kUnsafePointerAdd:
|
||||||
CheckNotTyped(node);
|
CheckNotTyped(node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1042,6 +1041,8 @@ void Verifier::Visitor::Check(Node* node) {
|
|||||||
// CheckValueInputIs(node, 0, Type::Object());
|
// CheckValueInputIs(node, 0, Type::Object());
|
||||||
// CheckUpperIs(node, ElementAccessOf(node->op()).type));
|
// CheckUpperIs(node, ElementAccessOf(node->op()).type));
|
||||||
break;
|
break;
|
||||||
|
case IrOpcode::kLoadTypedElement:
|
||||||
|
break;
|
||||||
case IrOpcode::kStoreField:
|
case IrOpcode::kStoreField:
|
||||||
// (Object, fieldtype) -> _|_
|
// (Object, fieldtype) -> _|_
|
||||||
// TODO(rossberg): activate once machine ops are typed.
|
// TODO(rossberg): activate once machine ops are typed.
|
||||||
@ -1058,6 +1059,9 @@ void Verifier::Visitor::Check(Node* node) {
|
|||||||
// CheckValueInputIs(node, 1, ElementAccessOf(node->op()).type));
|
// CheckValueInputIs(node, 1, ElementAccessOf(node->op()).type));
|
||||||
CheckNotTyped(node);
|
CheckNotTyped(node);
|
||||||
break;
|
break;
|
||||||
|
case IrOpcode::kStoreTypedElement:
|
||||||
|
CheckNotTyped(node);
|
||||||
|
break;
|
||||||
case IrOpcode::kNumberSilenceNaN:
|
case IrOpcode::kNumberSilenceNaN:
|
||||||
CheckValueInputIs(node, 0, Type::Number());
|
CheckValueInputIs(node, 0, Type::Number());
|
||||||
CheckUpperIs(node, Type::Number());
|
CheckUpperIs(node, Type::Number());
|
||||||
|
Loading…
Reference in New Issue
Block a user