blob: 3cbd626b8e4f53e71c2161597f422c6739423f43 [file] [log] [blame]
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/optional.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/codegen/code-stub-assembler.h"
#include "src/ic/ic.h"
#include "src/ic/keyed-store-generic.h"
#include "src/objects/objects-inl.h"
#include "torque-generated/exported-macros-assembler.h"
namespace v8 {
namespace internal {
class HandlerBuiltinsAssembler : public CodeStubAssembler {
public:
explicit HandlerBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
protected:
void Generate_KeyedStoreIC_SloppyArguments();
// Essentially turns runtime elements kinds (TNode<Int32T>) into
// compile-time types (int) by dispatching over the runtime type and
// emitting a specialized copy of the given case function for each elements
// kind. Use with caution. This produces a *lot* of code.
using ElementsKindSwitchCase = std::function<void(ElementsKind)>;
void DispatchByElementsKind(TNode<Int32T> elements_kind,
const ElementsKindSwitchCase& case_function,
bool handle_typed_elements_kind);
// Dispatches over all possible combinations of {from,to} elements kinds.
using ElementsKindTransitionSwitchCase =
std::function<void(ElementsKind, ElementsKind)>;
void DispatchForElementsKindTransition(
TNode<Int32T> from_kind, TNode<Int32T> to_kind,
const ElementsKindTransitionSwitchCase& case_function);
void Generate_ElementsTransitionAndStore(KeyedAccessStoreMode store_mode);
void Generate_StoreFastElementIC(KeyedAccessStoreMode store_mode);
};
TF_BUILTIN(LoadIC_StringLength, CodeStubAssembler) {
auto string = Parameter<String>(Descriptor::kReceiver);
Return(LoadStringLengthAsSmi(string));
}
TF_BUILTIN(LoadIC_StringWrapperLength, CodeStubAssembler) {
auto value = Parameter<JSPrimitiveWrapper>(Descriptor::kReceiver);
TNode<String> string = CAST(LoadJSPrimitiveWrapperValue(value));
Return(LoadStringLengthAsSmi(string));
}
void Builtins::Generate_KeyedStoreIC_Megamorphic(
compiler::CodeAssemblerState* state) {
KeyedStoreGenericGenerator::Generate(state);
}
void Builtins::Generate_StoreIC_NoFeedback(
compiler::CodeAssemblerState* state) {
StoreICNoFeedbackGenerator::Generate(state);
}
// All possible fast-to-fast transitions. Transitions to dictionary mode are not
// handled by ElementsTransitionAndStore.
#define ELEMENTS_KIND_TRANSITIONS(V) \
V(PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS) \
V(PACKED_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS) \
V(PACKED_SMI_ELEMENTS, HOLEY_DOUBLE_ELEMENTS) \
V(PACKED_SMI_ELEMENTS, PACKED_ELEMENTS) \
V(PACKED_SMI_ELEMENTS, HOLEY_ELEMENTS) \
V(HOLEY_SMI_ELEMENTS, HOLEY_DOUBLE_ELEMENTS) \
V(HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS) \
V(PACKED_DOUBLE_ELEMENTS, HOLEY_DOUBLE_ELEMENTS) \
V(PACKED_DOUBLE_ELEMENTS, PACKED_ELEMENTS) \
V(PACKED_DOUBLE_ELEMENTS, HOLEY_ELEMENTS) \
V(HOLEY_DOUBLE_ELEMENTS, HOLEY_ELEMENTS) \
V(PACKED_ELEMENTS, HOLEY_ELEMENTS)
void HandlerBuiltinsAssembler::DispatchForElementsKindTransition(
TNode<Int32T> from_kind, TNode<Int32T> to_kind,
const ElementsKindTransitionSwitchCase& case_function) {
STATIC_ASSERT(sizeof(ElementsKind) == sizeof(uint8_t));
Label next(this), if_unknown_type(this, Label::kDeferred);
int32_t combined_elements_kinds[] = {
#define ELEMENTS_KINDS_CASE(FROM, TO) (FROM << kBitsPerByte) | TO,
ELEMENTS_KIND_TRANSITIONS(ELEMENTS_KINDS_CASE)
#undef ELEMENTS_KINDS_CASE
};
#define ELEMENTS_KINDS_CASE(FROM, TO) Label if_##FROM##_##TO(this);
ELEMENTS_KIND_TRANSITIONS(ELEMENTS_KINDS_CASE)
#undef ELEMENTS_KINDS_CASE
Label* elements_kind_labels[] = {
#define ELEMENTS_KINDS_CASE(FROM, TO) &if_##FROM##_##TO,
ELEMENTS_KIND_TRANSITIONS(ELEMENTS_KINDS_CASE)
#undef ELEMENTS_KINDS_CASE
};
STATIC_ASSERT(arraysize(combined_elements_kinds) ==
arraysize(elements_kind_labels));
TNode<Int32T> combined_elements_kind =
Word32Or(Word32Shl(from_kind, Int32Constant(kBitsPerByte)), to_kind);
Switch(combined_elements_kind, &if_unknown_type, combined_elements_kinds,
elements_kind_labels, arraysize(combined_elements_kinds));
#define ELEMENTS_KINDS_CASE(FROM, TO) \
BIND(&if_##FROM##_##TO); \
{ \
case_function(FROM, TO); \
Goto(&next); \
}
ELEMENTS_KIND_TRANSITIONS(ELEMENTS_KINDS_CASE)
#undef ELEMENTS_KINDS_CASE
BIND(&if_unknown_type);
Unreachable();
BIND(&next);
}
#undef ELEMENTS_KIND_TRANSITIONS
void HandlerBuiltinsAssembler::Generate_ElementsTransitionAndStore(
KeyedAccessStoreMode store_mode) {
using Descriptor = StoreTransitionDescriptor;
auto receiver = Parameter<JSObject>(Descriptor::kReceiver);
auto key = Parameter<Object>(Descriptor::kName);
auto value = Parameter<Object>(Descriptor::kValue);
auto map = Parameter<Map>(Descriptor::kMap);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<FeedbackVector>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
Comment("ElementsTransitionAndStore: store_mode=", store_mode);
Label miss(this);
if (FLAG_trace_elements_transitions) {
// Tracing elements transitions is the job of the runtime.
Goto(&miss);
} else {
// TODO(v8:8481): Pass from_kind and to_kind in feedback vector slots.
DispatchForElementsKindTransition(
LoadElementsKind(receiver), LoadMapElementsKind(map),
[=, &miss](ElementsKind from_kind, ElementsKind to_kind) {
TransitionElementsKind(receiver, map, from_kind, to_kind, &miss);
EmitElementStore(receiver, key, value, to_kind, store_mode, &miss,
context, nullptr);
});
Return(value);
}
BIND(&miss);
TailCallRuntime(Runtime::kElementsTransitionAndStoreIC_Miss, context,
receiver, key, value, map, slot, vector);
}
TF_BUILTIN(ElementsTransitionAndStore_Standard, HandlerBuiltinsAssembler) {
Generate_ElementsTransitionAndStore(STANDARD_STORE);
}
TF_BUILTIN(ElementsTransitionAndStore_GrowNoTransitionHandleCOW,
HandlerBuiltinsAssembler) {
Generate_ElementsTransitionAndStore(STORE_AND_GROW_HANDLE_COW);
}
TF_BUILTIN(ElementsTransitionAndStore_NoTransitionIgnoreOOB,
HandlerBuiltinsAssembler) {
Generate_ElementsTransitionAndStore(STORE_IGNORE_OUT_OF_BOUNDS);
}
TF_BUILTIN(ElementsTransitionAndStore_NoTransitionHandleCOW,
HandlerBuiltinsAssembler) {
Generate_ElementsTransitionAndStore(STORE_HANDLE_COW);
}
// All elements kinds handled by EmitElementStore. Specifically, this includes
// fast elements and fixed typed array elements.
#define ELEMENTS_KINDS(V) \
V(PACKED_SMI_ELEMENTS) \
V(HOLEY_SMI_ELEMENTS) \
V(PACKED_ELEMENTS) \
V(PACKED_NONEXTENSIBLE_ELEMENTS) \
V(PACKED_SEALED_ELEMENTS) \
V(HOLEY_ELEMENTS) \
V(HOLEY_NONEXTENSIBLE_ELEMENTS) \
V(HOLEY_SEALED_ELEMENTS) \
V(PACKED_DOUBLE_ELEMENTS) \
V(HOLEY_DOUBLE_ELEMENTS) \
V(UINT8_ELEMENTS) \
V(INT8_ELEMENTS) \
V(UINT16_ELEMENTS) \
V(INT16_ELEMENTS) \
V(UINT32_ELEMENTS) \
V(INT32_ELEMENTS) \
V(FLOAT32_ELEMENTS) \
V(FLOAT64_ELEMENTS) \
V(UINT8_CLAMPED_ELEMENTS) \
V(BIGUINT64_ELEMENTS) \
V(BIGINT64_ELEMENTS)
void HandlerBuiltinsAssembler::DispatchByElementsKind(
TNode<Int32T> elements_kind, const ElementsKindSwitchCase& case_function,
bool handle_typed_elements_kind) {
Label next(this), if_unknown_type(this, Label::kDeferred);
int32_t elements_kinds[] = {
#define ELEMENTS_KINDS_CASE(KIND) KIND,
ELEMENTS_KINDS(ELEMENTS_KINDS_CASE)
#undef ELEMENTS_KINDS_CASE
};
#define ELEMENTS_KINDS_CASE(KIND) Label if_##KIND(this);
ELEMENTS_KINDS(ELEMENTS_KINDS_CASE)
#undef ELEMENTS_KINDS_CASE
Label* elements_kind_labels[] = {
#define ELEMENTS_KINDS_CASE(KIND) &if_##KIND,
ELEMENTS_KINDS(ELEMENTS_KINDS_CASE)
#undef ELEMENTS_KINDS_CASE
};
STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
// TODO(mythria): Do not emit cases for typed elements kind when
// handle_typed_elements is false to decrease the size of the jump table.
Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels,
arraysize(elements_kinds));
#define ELEMENTS_KINDS_CASE(KIND) \
BIND(&if_##KIND); \
{ \
if (!FLAG_enable_sealed_frozen_elements_kind && \
IsAnyNonextensibleElementsKindUnchecked(KIND)) { \
/* Disable support for frozen or sealed elements kinds. */ \
Unreachable(); \
} else if (!handle_typed_elements_kind && \
IsTypedArrayElementsKind(KIND)) { \
Unreachable(); \
} else { \
case_function(KIND); \
Goto(&next); \
} \
}
ELEMENTS_KINDS(ELEMENTS_KINDS_CASE)
#undef ELEMENTS_KINDS_CASE
BIND(&if_unknown_type);
Unreachable();
BIND(&next);
}
#undef ELEMENTS_KINDS
void HandlerBuiltinsAssembler::Generate_StoreFastElementIC(
KeyedAccessStoreMode store_mode) {
using Descriptor = StoreWithVectorDescriptor;
auto receiver = Parameter<JSObject>(Descriptor::kReceiver);
auto key = Parameter<Object>(Descriptor::kName);
auto value = Parameter<Object>(Descriptor::kValue);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<HeapObject>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
Comment("StoreFastElementStub: store_mode=", store_mode);
Label miss(this);
bool handle_typed_elements_kind =
store_mode == STANDARD_STORE || store_mode == STORE_IGNORE_OUT_OF_BOUNDS;
// For typed arrays maybe_converted_value contains the value obtained after
// calling ToNumber. We should pass the converted value to the runtime to
// avoid doing the user visible conversion again.
TVARIABLE(Object, maybe_converted_value, value);
// TODO(v8:8481): Pass elements_kind in feedback vector slots.
DispatchByElementsKind(
LoadElementsKind(receiver),
[=, &miss, &maybe_converted_value](ElementsKind elements_kind) {
EmitElementStore(receiver, key, value, elements_kind, store_mode, &miss,
context, &maybe_converted_value);
},
handle_typed_elements_kind);
Return(value);
BIND(&miss);
TailCallRuntime(Runtime::kKeyedStoreIC_Miss, context,
maybe_converted_value.value(), slot, vector, receiver, key);
}
TF_BUILTIN(StoreFastElementIC_Standard, HandlerBuiltinsAssembler) {
Generate_StoreFastElementIC(STANDARD_STORE);
}
TF_BUILTIN(StoreFastElementIC_GrowNoTransitionHandleCOW,
HandlerBuiltinsAssembler) {
Generate_StoreFastElementIC(STORE_AND_GROW_HANDLE_COW);
}
TF_BUILTIN(StoreFastElementIC_NoTransitionIgnoreOOB, HandlerBuiltinsAssembler) {
Generate_StoreFastElementIC(STORE_IGNORE_OUT_OF_BOUNDS);
}
TF_BUILTIN(StoreFastElementIC_NoTransitionHandleCOW, HandlerBuiltinsAssembler) {
Generate_StoreFastElementIC(STORE_HANDLE_COW);
}
TF_BUILTIN(LoadIC_FunctionPrototype, CodeStubAssembler) {
auto receiver = Parameter<JSFunction>(Descriptor::kReceiver);
auto name = Parameter<Name>(Descriptor::kName);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<FeedbackVector>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
Label miss(this, Label::kDeferred);
Return(LoadJSFunctionPrototype(receiver, &miss));
BIND(&miss);
TailCallRuntime(Runtime::kLoadIC_Miss, context, receiver, name, slot, vector);
}
TF_BUILTIN(StoreGlobalIC_Slow, CodeStubAssembler) {
auto receiver = Parameter<Object>(Descriptor::kReceiver);
auto name = Parameter<Name>(Descriptor::kName);
auto value = Parameter<Object>(Descriptor::kValue);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<FeedbackVector>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
// The slow case calls into the runtime to complete the store without causing
// an IC miss that would otherwise cause a transition to the generic stub.
TailCallRuntime(Runtime::kStoreGlobalIC_Slow, context, value, slot, vector,
receiver, name);
}
TF_BUILTIN(KeyedLoadIC_SloppyArguments, HandlerBuiltinsAssembler) {
auto receiver = Parameter<JSObject>(Descriptor::kReceiver);
auto key = Parameter<Object>(Descriptor::kName);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<HeapObject>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
Label miss(this);
TNode<Object> result = SloppyArgumentsLoad(receiver, key, &miss);
Return(result);
BIND(&miss);
{
Comment("Miss");
TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, key, slot,
vector);
}
}
void HandlerBuiltinsAssembler::Generate_KeyedStoreIC_SloppyArguments() {
using Descriptor = StoreWithVectorDescriptor;
auto receiver = Parameter<JSObject>(Descriptor::kReceiver);
auto key = Parameter<Object>(Descriptor::kName);
auto value = Parameter<Object>(Descriptor::kValue);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<HeapObject>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
Label miss(this);
SloppyArgumentsStore(receiver, key, value, &miss);
Return(value);
BIND(&miss);
TailCallRuntime(Runtime::kKeyedStoreIC_Miss, context, value, slot, vector,
receiver, key);
}
TF_BUILTIN(KeyedStoreIC_SloppyArguments_Standard, HandlerBuiltinsAssembler) {
Generate_KeyedStoreIC_SloppyArguments();
}
TF_BUILTIN(KeyedStoreIC_SloppyArguments_GrowNoTransitionHandleCOW,
HandlerBuiltinsAssembler) {
Generate_KeyedStoreIC_SloppyArguments();
}
TF_BUILTIN(KeyedStoreIC_SloppyArguments_NoTransitionIgnoreOOB,
HandlerBuiltinsAssembler) {
Generate_KeyedStoreIC_SloppyArguments();
}
TF_BUILTIN(KeyedStoreIC_SloppyArguments_NoTransitionHandleCOW,
HandlerBuiltinsAssembler) {
Generate_KeyedStoreIC_SloppyArguments();
}
TF_BUILTIN(LoadIndexedInterceptorIC, CodeStubAssembler) {
auto receiver = Parameter<JSObject>(Descriptor::kReceiver);
auto key = Parameter<Object>(Descriptor::kName);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<HeapObject>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
Label if_keyispositivesmi(this), if_keyisinvalid(this);
Branch(TaggedIsPositiveSmi(key), &if_keyispositivesmi, &if_keyisinvalid);
BIND(&if_keyispositivesmi);
TailCallRuntime(Runtime::kLoadElementWithInterceptor, context, receiver, key);
BIND(&if_keyisinvalid);
TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, key, slot,
vector);
}
TF_BUILTIN(KeyedHasIC_SloppyArguments, HandlerBuiltinsAssembler) {
auto receiver = Parameter<JSObject>(Descriptor::kReceiver);
auto key = Parameter<Object>(Descriptor::kName);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<HeapObject>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
Label miss(this);
TNode<Object> result = SloppyArgumentsHas(receiver, key, &miss);
Return(result);
BIND(&miss);
{
Comment("Miss");
TailCallRuntime(Runtime::kKeyedHasIC_Miss, context, receiver, key, slot,
vector);
}
}
TF_BUILTIN(HasIndexedInterceptorIC, CodeStubAssembler) {
auto receiver = Parameter<JSObject>(Descriptor::kReceiver);
auto key = Parameter<Object>(Descriptor::kName);
auto slot = Parameter<Smi>(Descriptor::kSlot);
auto vector = Parameter<HeapObject>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
Label if_keyispositivesmi(this), if_keyisinvalid(this);
Branch(TaggedIsPositiveSmi(key), &if_keyispositivesmi, &if_keyisinvalid);
BIND(&if_keyispositivesmi);
TailCallRuntime(Runtime::kHasElementWithInterceptor, context, receiver, key);
BIND(&if_keyisinvalid);
TailCallRuntime(Runtime::kKeyedHasIC_Miss, context, receiver, key, slot,
vector);
}
} // namespace internal
} // namespace v8