Commit 66f2d3bd authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent 8135caef
......@@ -327,6 +327,27 @@ FieldAccess AccessBuilder::ForFixedArrayLength() {
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
FieldAccess AccessBuilder::ForDescriptorArrayEnumCache() {
......
......@@ -106,6 +106,12 @@ class AccessBuilder final : public AllStatic {
// Provides access to FixedArray::length() field.
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.
static FieldAccess ForDescriptorArrayEnumCache();
......
......@@ -25,6 +25,8 @@ bool CanInlineElementAccess(Handle<Map> map) {
ElementsKind const elements_kind = map->elements_kind();
if (IsFastElementsKind(elements_kind)) return true;
// TODO(bmeurer): Add support for other elements kind.
if (elements_kind == UINT8_CLAMPED_ELEMENTS) return false;
if (IsFixedTypedArrayElementsKind(elements_kind)) return true;
return false;
}
......
......@@ -235,21 +235,22 @@ std::ostream& operator<<(std::ostream& os,
return os;
}
#define CACHED_OP_LIST(V) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(IfTrue, 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(IfDefault, Operator::kKontrol, 0, 0, 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(OsrNormalEntry, 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(LoopExitValue, Operator::kPure, 1, 0, 1, 1, 0, 0) \
V(LoopExitEffect, Operator::kNoThrow, 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)
#define CACHED_OP_LIST(V) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(IfTrue, 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(IfDefault, Operator::kKontrol, 0, 0, 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(OsrNormalEntry, 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(LoopExitValue, Operator::kPure, 1, 0, 1, 1, 0, 0) \
V(LoopExitEffect, Operator::kNoThrow, 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(Retain, Operator::kKontrol, 1, 1, 0, 0, 1, 0)
#define CACHED_RETURN_LIST(V) \
V(1) \
......
......@@ -248,6 +248,7 @@ class CommonOperatorBuilder final : public ZoneObject {
const Operator* Call(const CallDescriptor* descriptor);
const Operator* TailCall(const CallDescriptor* descriptor);
const Operator* Projection(size_t index);
const Operator* Retain();
// Constructs a new merge or phi operator with the same opcode as {op}, but
// with {size} inputs.
......
......@@ -722,6 +722,12 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kTransitionElementsKind:
state = LowerTransitionElementsKind(node, *effect, *control);
break;
case IrOpcode::kLoadTypedElement:
state = LowerLoadTypedElement(node, *effect, *control);
break;
case IrOpcode::kStoreTypedElement:
state = LowerStoreTypedElement(node, *effect, *control);
break;
default:
return false;
}
......@@ -2619,6 +2625,59 @@ EffectControlLinearizer::LowerTransitionElementsKind(Node* node, Node* effect,
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 {
return isolate()->factory();
}
......
......@@ -134,6 +134,10 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl LowerTransitionElementsKind(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerLoadTypedElement(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerStoreTypedElement(Node* node, Node* effect,
Node* control);
ValueEffectControl AllocateHeapNumberWithValue(Node* node, Node* effect,
Node* control);
......
......@@ -299,6 +299,9 @@ void InstructionSelector::MarkAsDefined(Node* node) {
bool InstructionSelector::IsUsed(Node* node) const {
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;
size_t const id = node->id();
DCHECK_LT(id, used_.size());
......@@ -929,6 +932,9 @@ void InstructionSelector::VisitNode(Node* node) {
case IrOpcode::kComment:
VisitComment(node);
return;
case IrOpcode::kRetain:
VisitRetain(node);
return;
case IrOpcode::kLoad: {
LoadRepresentation type = LoadRepresentationOf(node->op());
MarkAsRepresentation(type.representation(), node);
......@@ -1297,6 +1303,9 @@ void InstructionSelector::VisitNode(Node* node) {
}
case IrOpcode::kAtomicStore:
return VisitAtomicStore(node);
case IrOpcode::kUnsafePointerAdd:
MarkAsRepresentation(MachineType::PointerRepresentation(), node);
return VisitUnsafePointerAdd(node);
default:
V8_Fatal(__FILE__, __LINE__, "Unexpected operator #%d:%s @ node #%d",
node->opcode(), node->op()->mnemonic(), node->id());
......@@ -2016,6 +2025,19 @@ void InstructionSelector::VisitComment(Node* node) {
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) {
// TODO(jarin) Improve the heuristic here.
if (node->opcode() == IrOpcode::kFloat64Add ||
......
......@@ -296,6 +296,7 @@ class InstructionSelector final {
Node* value);
void VisitReturn(Node* ret);
void VisitThrow(Node* value);
void VisitRetain(Node* node);
void EmitPrepareArguments(ZoneVector<compiler::PushParameter>* arguments,
const CallDescriptor* descriptor, Node* node);
......
......@@ -732,9 +732,9 @@ Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor(
jsgraph()->ZeroConstant());
// Default to zero if the {receiver}s buffer was neutered.
Node* value =
graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
check, receiver_length, jsgraph()->ZeroConstant());
Node* value = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue),
check, receiver_length, jsgraph()->ZeroConstant());
ReplaceWithValue(node, value, effect, control);
return Replace(value);
......
......@@ -63,6 +63,8 @@ Reduction LoadElimination::Reduce(Node* node) {
return ReduceLoadElement(node);
case IrOpcode::kStoreElement:
return ReduceStoreElement(node);
case IrOpcode::kStoreTypedElement:
return ReduceStoreTypedElement(node);
case IrOpcode::kEffectPhi:
return ReduceEffectPhi(node);
case IrOpcode::kDead:
......@@ -455,6 +457,13 @@ Reduction LoadElimination::ReduceStoreElement(Node* node) {
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) {
Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
Node* const control = NodeProperties::GetControlInput(node);
......@@ -571,15 +580,20 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
// static
int LoadElimination::FieldIndexOf(FieldAccess const& access) {
switch (access.machine_type.representation()) {
MachineRepresentation rep = access.machine_type.representation();
switch (rep) {
case MachineRepresentation::kNone:
case MachineRepresentation::kBit:
UNREACHABLE();
break;
case MachineRepresentation::kWord8:
case MachineRepresentation::kWord16:
case MachineRepresentation::kWord32:
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:
return -1; // Currently untracked.
case MachineRepresentation::kFloat64:
......
......@@ -155,6 +155,7 @@ class LoadElimination final : public AdvancedReducer {
Reduction ReduceStoreField(Node* node);
Reduction ReduceLoadElement(Node* node);
Reduction ReduceStoreElement(Node* node);
Reduction ReduceStoreTypedElement(Node* node);
Reduction ReduceEffectPhi(Node* node);
Reduction ReduceStart(Node* node);
Reduction ReduceOtherNode(Node* node);
......
......@@ -611,6 +611,13 @@ struct MachineOperatorGlobalCache {
0, 0, 0, 0, 0) {}
};
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*> {
......@@ -728,6 +735,10 @@ const Operator* MachineOperatorBuilder::Store(StoreRepresentation store_rep) {
return nullptr;
}
const Operator* MachineOperatorBuilder::UnsafePointerAdd() {
return &cache_.kUnsafePointerAdd;
}
const Operator* MachineOperatorBuilder::DebugBreak() {
return &cache_.kDebugBreak;
}
......
......@@ -214,6 +214,7 @@ class MachineOperatorBuilder final : public ZoneObject {
const Operator* Comment(const char* msg);
const Operator* DebugBreak();
const Operator* UnsafePointerAdd();
const Operator* Word32And();
const Operator* Word32Or();
......
......@@ -92,6 +92,8 @@ void MemoryOptimizer::VisitNode(Node* node, AllocationState const* state) {
case IrOpcode::kIfException:
case IrOpcode::kLoad:
case IrOpcode::kStore:
case IrOpcode::kRetain:
case IrOpcode::kUnsafePointerAdd:
return VisitOtherEffect(node, state);
default:
break;
......
......@@ -61,7 +61,8 @@
V(LoopExit) \
V(LoopExitValue) \
V(LoopExitEffect) \
V(Projection)
V(Projection) \
V(Retain)
#define COMMON_OP_LIST(V) \
CONSTANT_OP_LIST(V) \
......@@ -282,9 +283,11 @@
V(LoadField) \
V(LoadBuffer) \
V(LoadElement) \
V(LoadTypedElement) \
V(StoreField) \
V(StoreBuffer) \
V(StoreElement) \
V(StoreTypedElement) \
V(ObjectIsCallable) \
V(ObjectIsNumber) \
V(ObjectIsReceiver) \
......@@ -473,7 +476,8 @@
V(Word32PairShr) \
V(Word32PairSar) \
V(AtomicLoad) \
V(AtomicStore)
V(AtomicStore) \
V(UnsafePointerAdd)
#define MACHINE_SIMD_RETURN_SIMD_OP_LIST(V) \
V(CreateFloat32x4) \
......
......@@ -65,6 +65,27 @@ enum Phase {
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) {
switch (rep) {
......@@ -2209,6 +2230,30 @@ class RepresentationSelector {
}
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: {
if (InputIs(node, Type::Boolean())) {
VisitUnop(node, UseInfo::Bool(), MachineRepresentation::kWord32);
......
......@@ -181,6 +181,12 @@ const ElementAccess& ElementAccessOf(const Operator* 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) {
return static_cast<size_t>(mode);
}
......@@ -634,11 +640,13 @@ const Operator* SimplifiedOperatorBuilder::SpeculativeNumberLessThanOrEqual(
"SpeculativeNumberLessThanOrEqual", 2, 1, 1, 1, 1, 0, hint);
}
#define ACCESS_OP_LIST(V) \
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
V(StoreField, FieldAccess, Operator::kNoRead, 2, 1, 0) \
V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1, 1) \
V(StoreElement, ElementAccess, Operator::kNoRead, 3, 1, 0)
#define ACCESS_OP_LIST(V) \
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
V(StoreField, FieldAccess, Operator::kNoRead, 2, 1, 0) \
V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1, 1) \
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, \
output_count) \
......
......@@ -107,6 +107,8 @@ std::ostream& operator<<(std::ostream&, ElementAccess const&);
ElementAccess const& ElementAccessOf(const Operator* op) WARN_UNUSED_RESULT;
ExternalArrayType ExternalArrayTypeOf(const Operator* op) WARN_UNUSED_RESULT;
enum class CheckFloat64HoleMode : uint8_t {
kNeverReturnHole, // Never return the hole (deoptimize instead).
kAllowReturnHole // Allow to return the hole (signaling NaN).
......@@ -319,12 +321,18 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
// store-buffer buffer, offset, length, value
const Operator* StoreBuffer(BufferAccess);
// load-element [base + index], length
// load-element [base + index]
const Operator* LoadElement(ElementAccess const&);
// store-element [base + index], length, value
// store-element [base + index], value
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:
Zone* zone() const { return zone_; }
......
......@@ -1975,6 +1975,17 @@ Type* Typer::Visitor::TypeLoadElement(Node* node) {
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) {
UNREACHABLE();
......@@ -1993,6 +2004,11 @@ Type* Typer::Visitor::TypeStoreElement(Node* node) {
return nullptr;
}
Type* Typer::Visitor::TypeStoreTypedElement(Node* node) {
UNREACHABLE();
return nullptr;
}
Type* Typer::Visitor::TypeObjectIsCallable(Node* node) {
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::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::TypeStackSlot(Node* node) { return Type::Any(); }
......
......@@ -650,11 +650,10 @@ void Verifier::Visitor::Check(Node* node) {
CheckNotTyped(node);
break;
case IrOpcode::kDebugBreak:
CheckNotTyped(node);
break;
case IrOpcode::kComment:
case IrOpcode::kDebugBreak:
case IrOpcode::kRetain:
case IrOpcode::kUnsafePointerAdd:
CheckNotTyped(node);
break;
......@@ -1042,6 +1041,8 @@ void Verifier::Visitor::Check(Node* node) {
// CheckValueInputIs(node, 0, Type::Object());
// CheckUpperIs(node, ElementAccessOf(node->op()).type));
break;
case IrOpcode::kLoadTypedElement:
break;
case IrOpcode::kStoreField:
// (Object, fieldtype) -> _|_
// 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));
CheckNotTyped(node);
break;
case IrOpcode::kStoreTypedElement:
CheckNotTyped(node);
break;
case IrOpcode::kNumberSilenceNaN:
CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::Number());
......
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