Commit cce56a3f authored by ishell's avatar ishell Committed by Commit bot

[stubs] Port StoreFastElementsStub to TurboFan.

This CL adds CSA::Retain() operation that ensures that the value is kept alive even during GC.

BUG=v8:5269

Review-Url: https://codereview.chromium.org/2330063002
Cr-Commit-Position: refs/heads/master@{#39407}
parent d7ee8124
......@@ -3535,8 +3535,7 @@ compiler::Node* CodeStubAssembler::ElementOffsetFromIndex(Node* index_node,
ElementsKind kind,
ParameterMode mode,
int base_size) {
bool is_double = IsFastDoubleElementsKind(kind);
int element_size_shift = is_double ? kDoubleSizeLog2 : kPointerSizeLog2;
int element_size_shift = ElementsKindToShiftSize(kind);
int element_size = 1 << element_size_shift;
int const kSmiShiftBits = kSmiShiftSize + kSmiTagSize;
intptr_t index = 0;
......@@ -4637,6 +4636,252 @@ Node* CodeStubAssembler::LoadScriptContext(Node* context, int context_index) {
IntPtrConstant(offset));
}
Node* CodeStubAssembler::ClampedToUint8(Node* int32_value) {
Label done(this);
Node* int32_zero = Int32Constant(0);
Node* int32_255 = Int32Constant(255);
Variable var_value(this, MachineRepresentation::kWord32);
var_value.Bind(int32_value);
GotoIf(Uint32LessThanOrEqual(int32_value, int32_255), &done);
var_value.Bind(int32_zero);
GotoIf(Int32LessThan(int32_value, int32_zero), &done);
var_value.Bind(int32_255);
Goto(&done);
Bind(&done);
return var_value.value();
}
namespace {
// Converts typed array elements kind to a machine representations.
MachineRepresentation ElementsKindToMachineRepresentation(ElementsKind kind) {
switch (kind) {
case UINT8_CLAMPED_ELEMENTS:
case UINT8_ELEMENTS:
case INT8_ELEMENTS:
return MachineRepresentation::kWord8;
case UINT16_ELEMENTS:
case INT16_ELEMENTS:
return MachineRepresentation::kWord16;
case UINT32_ELEMENTS:
case INT32_ELEMENTS:
return MachineRepresentation::kWord32;
case FLOAT32_ELEMENTS:
return MachineRepresentation::kFloat32;
case FLOAT64_ELEMENTS:
return MachineRepresentation::kFloat64;
default:
UNREACHABLE();
return MachineRepresentation::kNone;
}
}
} // namespace
void CodeStubAssembler::StoreElement(Node* elements, ElementsKind kind,
Node* index, Node* value,
ParameterMode mode) {
if (IsFixedTypedArrayElementsKind(kind)) {
if (kind == UINT8_CLAMPED_ELEMENTS) {
value = ClampedToUint8(value);
}
Node* offset = ElementOffsetFromIndex(index, kind, mode, 0);
MachineRepresentation rep = ElementsKindToMachineRepresentation(kind);
StoreNoWriteBarrier(rep, elements, offset, value);
return;
}
WriteBarrierMode barrier_mode =
IsFastSmiElementsKind(kind) ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER;
if (IsFastDoubleElementsKind(kind)) {
// Make sure we do not store signalling NaNs into double arrays.
value = Float64SilenceNaN(value);
StoreFixedDoubleArrayElement(elements, index, value, mode);
} else {
StoreFixedArrayElement(elements, index, value, barrier_mode, mode);
}
}
void CodeStubAssembler::EmitElementStore(Node* object, Node* key, Node* value,
bool is_jsarray,
ElementsKind elements_kind,
KeyedAccessStoreMode store_mode,
Label* bailout) {
Node* elements = LoadElements(object);
if (IsFastSmiOrObjectElementsKind(elements_kind) &&
store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
// Bailout in case of COW elements.
GotoIf(WordNotEqual(LoadMap(elements),
LoadRoot(Heap::kFixedArrayMapRootIndex)),
bailout);
}
// TODO(ishell): introduce TryToIntPtrOrSmi() and use OptimalParameterMode().
ParameterMode parameter_mode = INTPTR_PARAMETERS;
key = TryToIntptr(key, bailout);
if (IsFixedTypedArrayElementsKind(elements_kind)) {
Label done(this);
// TODO(ishell): call ToNumber() on value and don't bailout but be careful
// to call it only once if we decide to bailout because of bounds checks.
if (IsFixedFloatElementsKind(elements_kind)) {
// TODO(ishell): move float32 truncation into PrepareValueForWrite.
value = PrepareValueForWrite(value, Representation::Double(), bailout);
if (elements_kind == FLOAT32_ELEMENTS) {
value = TruncateFloat64ToFloat32(value);
}
} else {
// TODO(ishell): It's fine for word8/16/32 to truncate the result.
value = TryToIntptr(value, bailout);
}
// There must be no allocations between the buffer load and
// and the actual store to backing store, because GC may decide that
// the buffer is not alive or move the elements.
// TODO(ishell): introduce DisallowHeapAllocationCode scope here.
// Check if buffer has been neutered.
Node* buffer = LoadObjectField(object, JSArrayBufferView::kBufferOffset);
Node* bitfield = LoadObjectField(buffer, JSArrayBuffer::kBitFieldOffset,
MachineType::Uint32());
Node* neutered_bit =
Word32And(bitfield, Int32Constant(JSArrayBuffer::WasNeutered::kMask));
GotoUnless(Word32Equal(neutered_bit, Int32Constant(0)), bailout);
// Bounds check.
Node* length = UntagParameter(
LoadObjectField(object, JSTypedArray::kLengthOffset), parameter_mode);
if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
// Skip the store if we write beyond the length.
GotoUnless(IntPtrLessThan(key, length), &done);
// ... but bailout if the key is negative.
} else {
DCHECK_EQ(STANDARD_STORE, store_mode);
}
GotoUnless(UintPtrLessThan(key, length), bailout);
// Backing store = external_pointer + base_pointer.
Node* external_pointer =
LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
MachineType::Pointer());
Node* base_pointer =
LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
Node* backing_store = IntPtrAdd(external_pointer, base_pointer);
StoreElement(backing_store, elements_kind, key, value, parameter_mode);
Goto(&done);
Bind(&done);
return;
}
DCHECK(IsFastSmiOrObjectElementsKind(elements_kind) ||
IsFastDoubleElementsKind(elements_kind));
Node* length = is_jsarray ? LoadObjectField(object, JSArray::kLengthOffset)
: LoadFixedArrayBaseLength(elements);
length = UntagParameter(length, parameter_mode);
// In case value is stored into a fast smi array, assure that the value is
// a smi before manipulating the backing store. Otherwise the backing store
// may be left in an invalid state.
if (IsFastSmiElementsKind(elements_kind)) {
GotoUnless(WordIsSmi(value), bailout);
} else if (IsFastDoubleElementsKind(elements_kind)) {
value = PrepareValueForWrite(value, Representation::Double(), bailout);
}
if (IsGrowStoreMode(store_mode)) {
elements = CheckForCapacityGrow(object, elements, elements_kind, length,
key, parameter_mode, is_jsarray, bailout);
} else {
GotoUnless(UintPtrLessThan(key, length), bailout);
if ((store_mode == STORE_NO_TRANSITION_HANDLE_COW) &&
IsFastSmiOrObjectElementsKind(elements_kind)) {
elements = CopyElementsOnWrite(object, elements, elements_kind, length,
parameter_mode, bailout);
}
}
StoreElement(elements, elements_kind, key, value, parameter_mode);
}
Node* CodeStubAssembler::CheckForCapacityGrow(Node* object, Node* elements,
ElementsKind kind, Node* length,
Node* key, ParameterMode mode,
bool is_js_array,
Label* bailout) {
Variable checked_elements(this, MachineRepresentation::kTagged);
Label grow_case(this), no_grow_case(this), done(this);
Node* condition;
if (IsHoleyElementsKind(kind)) {
condition = UintPtrGreaterThanOrEqual(key, length);
} else {
condition = WordEqual(key, length);
}
Branch(condition, &grow_case, &no_grow_case);
Bind(&grow_case);
{
Node* current_capacity =
UntagParameter(LoadFixedArrayBaseLength(elements), mode);
checked_elements.Bind(elements);
Label fits_capacity(this);
GotoIf(UintPtrLessThan(key, current_capacity), &fits_capacity);
{
Node* new_elements = TryGrowElementsCapacity(
object, elements, kind, key, current_capacity, mode, bailout);
checked_elements.Bind(new_elements);
Goto(&fits_capacity);
}
Bind(&fits_capacity);
if (is_js_array) {
Node* new_length = IntPtrAdd(key, IntPtrOrSmiConstant(1, mode));
StoreObjectFieldNoWriteBarrier(object, JSArray::kLengthOffset,
TagParameter(new_length, mode));
}
Goto(&done);
}
Bind(&no_grow_case);
{
GotoUnless(UintPtrLessThan(key, length), bailout);
checked_elements.Bind(elements);
Goto(&done);
}
Bind(&done);
return checked_elements.value();
}
Node* CodeStubAssembler::CopyElementsOnWrite(Node* object, Node* elements,
ElementsKind kind, Node* length,
ParameterMode mode,
Label* bailout) {
Variable new_elements_var(this, MachineRepresentation::kTagged);
Label done(this);
new_elements_var.Bind(elements);
GotoUnless(
WordEqual(LoadMap(elements), LoadRoot(Heap::kFixedCOWArrayMapRootIndex)),
&done);
{
Node* capacity = UntagParameter(LoadFixedArrayBaseLength(elements), mode);
Node* new_elements = GrowElementsCapacity(object, elements, kind, kind,
length, capacity, mode, bailout);
new_elements_var.Bind(new_elements);
Goto(&done);
}
Bind(&done);
return new_elements_var.value();
}
Node* CodeStubAssembler::EnumLength(Node* map) {
Node* bitfield_3 = LoadMapBitField3(map);
Node* enum_length = BitFieldDecode<Map::EnumLengthBits>(bitfield_3);
......
......@@ -662,6 +662,30 @@ class CodeStubAssembler : public compiler::CodeAssembler {
// Loads script context from the script context table.
compiler::Node* LoadScriptContext(compiler::Node* context, int context_index);
compiler::Node* ClampedToUint8(compiler::Node* int32_value);
// Store value to an elements array with given elements kind.
void StoreElement(compiler::Node* elements, ElementsKind kind,
compiler::Node* index, compiler::Node* value,
ParameterMode mode);
void EmitElementStore(compiler::Node* object, compiler::Node* key,
compiler::Node* value, bool is_jsarray,
ElementsKind elements_kind,
KeyedAccessStoreMode store_mode, Label* bailout);
compiler::Node* CheckForCapacityGrow(compiler::Node* object,
compiler::Node* elements,
ElementsKind kind,
compiler::Node* length,
compiler::Node* key, ParameterMode mode,
bool is_js_array, Label* bailout);
compiler::Node* CopyElementsOnWrite(compiler::Node* object,
compiler::Node* elements,
ElementsKind kind, compiler::Node* length,
ParameterMode mode, Label* bailout);
void LoadIC(const LoadICParameters* p);
void LoadGlobalIC(const LoadICParameters* p);
void KeyedLoadIC(const LoadICParameters* p);
......
......@@ -976,23 +976,6 @@ Handle<Code> StoreTransitionStub::GenerateCode() {
}
template <>
HValue* CodeStubGraphBuilder<StoreFastElementStub>::BuildCodeStub() {
BuildUncheckedMonomorphicElementAccess(
GetParameter(Descriptor::kReceiver), GetParameter(Descriptor::kName),
GetParameter(Descriptor::kValue), casted_stub()->is_js_array(),
casted_stub()->elements_kind(), STORE, NEVER_RETURN_HOLE,
casted_stub()->store_mode());
return GetParameter(Descriptor::kValue);
}
Handle<Code> StoreFastElementStub::GenerateCode() {
return DoGenerateCode(this);
}
template <>
HValue* CodeStubGraphBuilder<TransitionElementsKindStub>::BuildCodeStub() {
ElementsKind const from_kind = casted_stub()->from_kind();
......
......@@ -5122,13 +5122,6 @@ CallInterfaceDescriptor HandlerStub::GetCallInterfaceDescriptor() const {
}
void StoreFastElementStub::InitializeDescriptor(
CodeStubDescriptor* descriptor) {
descriptor->Initialize(
FUNCTION_ADDR(Runtime_KeyedStoreIC_MissFromStubFailure));
}
void ElementsTransitionAndStoreStub::InitializeDescriptor(
CodeStubDescriptor* descriptor) {
descriptor->Initialize(
......@@ -5680,6 +5673,35 @@ void StoreElementStub::Generate(MacroAssembler* masm) {
ElementHandlerCompiler::GenerateStoreSlow(masm);
}
void StoreFastElementStub::GenerateAssembly(
CodeStubAssembler* assembler) const {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
assembler->Comment(
"StoreFastElementStub: js_array=%d, elements_kind=%s, store_mode=%d",
is_js_array(), ElementsKindToString(elements_kind()), store_mode());
Node* receiver = assembler->Parameter(Descriptor::kReceiver);
Node* key = assembler->Parameter(Descriptor::kName);
Node* value = assembler->Parameter(Descriptor::kValue);
Node* slot = assembler->Parameter(Descriptor::kSlot);
Node* vector = assembler->Parameter(Descriptor::kVector);
Node* context = assembler->Parameter(Descriptor::kContext);
Label miss(assembler);
assembler->EmitElementStore(receiver, key, value, is_js_array(),
elements_kind(), store_mode(), &miss);
assembler->Return(value);
assembler->Bind(&miss);
{
assembler->Comment("Miss");
assembler->TailCallRuntime(Runtime::kKeyedStoreIC_Miss, context, receiver,
key, value, slot, vector);
}
}
// static
void StoreFastElementStub::GenerateAheadOfTime(Isolate* isolate) {
......
......@@ -86,7 +86,6 @@ class ObjectLiteral;
V(LoadDictionaryElement) \
V(LoadFastElement) \
V(LoadField) \
V(StoreFastElement) \
V(StoreTransition) \
/* These should never be ported to TF */ \
/* because they are either used only by */ \
......@@ -165,6 +164,7 @@ class ObjectLiteral;
V(GetProperty) \
V(LoadICTF) \
V(KeyedLoadICTF) \
V(StoreFastElement) \
V(StoreField) \
V(StoreGlobal) \
V(StoreInterceptor) \
......@@ -2774,38 +2774,38 @@ class LoadFastElementStub : public HandlerStub {
DEFINE_HANDLER_CODE_STUB(LoadFastElement, HandlerStub);
};
class StoreFastElementStub : public HydrogenCodeStub {
class StoreFastElementStub : public TurboFanCodeStub {
public:
StoreFastElementStub(Isolate* isolate, bool is_js_array,
ElementsKind elements_kind, KeyedAccessStoreMode mode)
: HydrogenCodeStub(isolate) {
set_sub_minor_key(CommonStoreModeBits::encode(mode) |
ElementsKindBits::encode(elements_kind) |
IsJSArrayBits::encode(is_js_array));
: TurboFanCodeStub(isolate) {
minor_key_ = CommonStoreModeBits::encode(mode) |
ElementsKindBits::encode(elements_kind) |
IsJSArrayBits::encode(is_js_array);
}
static void GenerateAheadOfTime(Isolate* isolate);
bool is_js_array() const { return IsJSArrayBits::decode(sub_minor_key()); }
bool is_js_array() const { return IsJSArrayBits::decode(minor_key_); }
ElementsKind elements_kind() const {
return ElementsKindBits::decode(sub_minor_key());
return ElementsKindBits::decode(minor_key_);
}
KeyedAccessStoreMode store_mode() const {
return CommonStoreModeBits::decode(sub_minor_key());
return CommonStoreModeBits::decode(minor_key_);
}
Code::Kind GetCodeKind() const override { return Code::HANDLER; }
ExtraICState GetExtraICState() const override { return Code::KEYED_STORE_IC; }
private:
class ElementsKindBits : public BitField<ElementsKind, 3, 8> {};
class IsJSArrayBits : public BitField<bool, 11, 1> {};
class ElementsKindBits
: public BitField<ElementsKind, CommonStoreModeBits::kNext, 8> {};
class IsJSArrayBits : public BitField<bool, ElementsKindBits::kNext, 1> {};
DEFINE_CALL_INTERFACE_DESCRIPTOR(StoreWithVector);
DEFINE_HYDROGEN_CODE_STUB(StoreFastElement, HydrogenCodeStub);
DEFINE_TURBOFAN_CODE_STUB(StoreFastElement, TurboFanCodeStub);
};
......@@ -3004,7 +3004,8 @@ class StoreElementStub : public PlatformCodeStub {
return ElementsKindBits::decode(minor_key_);
}
class ElementsKindBits : public BitField<ElementsKind, 3, 8> {};
class ElementsKindBits
: public BitField<ElementsKind, CommonStoreModeBits::kNext, 8> {};
DEFINE_PLATFORM_CODE_STUB(StoreElement, PlatformCodeStub);
};
......@@ -3104,7 +3105,8 @@ class ElementsTransitionAndStoreStub : public HydrogenCodeStub {
ExtraICState GetExtraICState() const override { return Code::KEYED_STORE_IC; }
private:
class FromBits : public BitField<ElementsKind, 3, 8> {};
class FromBits
: public BitField<ElementsKind, CommonStoreModeBits::kNext, 8> {};
class ToBits : public BitField<ElementsKind, 11, 8> {};
class IsJSArrayBits : public BitField<bool, 19, 1> {};
......
......@@ -306,6 +306,10 @@ Node* CodeAssembler::StoreRoot(Heap::RootListIndex root_index, Node* value) {
IntPtrConstant(root_index * kPointerSize), value);
}
Node* CodeAssembler::Retain(Node* value) {
return raw_assembler_->Retain(value);
}
Node* CodeAssembler::Projection(int index, Node* value) {
return raw_assembler_->Projection(index, value);
}
......
......@@ -54,6 +54,7 @@ class RawMachineLabel;
V(IntPtrGreaterThanOrEqual) \
V(IntPtrEqual) \
V(Uint32LessThan) \
V(Uint32LessThanOrEqual) \
V(Uint32GreaterThanOrEqual) \
V(UintPtrLessThan) \
V(UintPtrGreaterThan) \
......@@ -145,6 +146,7 @@ class RawMachineLabel;
V(ChangeUint32ToFloat64) \
V(ChangeUint32ToUint64) \
V(RoundFloat64ToInt32) \
V(Float64SilenceNaN) \
V(Float64RoundDown) \
V(Float64RoundUp) \
V(Float64RoundTruncate) \
......@@ -294,6 +296,10 @@ class CodeAssembler {
// No-op on 32-bit, otherwise sign extend.
Node* ChangeInt32ToIntPtr(Node* value);
// No-op that guarantees that the value is kept alive till this point even
// if GC happens.
Node* Retain(Node* value);
// Projections
Node* Projection(int index, Node* value);
......
......@@ -135,6 +135,7 @@ class RawMachineAssembler {
return AddNode(machine()->Store(StoreRepresentation(rep, write_barrier)),
base, index, value);
}
Node* Retain(Node* value) { return AddNode(common()->Retain(), value); }
// Unaligned memory operations
Node* UnalignedLoad(MachineType rep, Node* base) {
......@@ -659,6 +660,9 @@ class RawMachineAssembler {
Node* Float64InsertHighWord32(Node* a, Node* b) {
return AddNode(machine()->Float64InsertHighWord32(), a, b);
}
Node* Float64SilenceNaN(Node* a) {
return AddNode(machine()->Float64SilenceNaN(), a);
}
// Stack operations.
Node* LoadStackPointer() { return AddNode(machine()->LoadStackPointer()); }
......
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