blob: b152d63bbd68d9c3820149ed4f350256740f2a0c [file] [log] [blame]
// Copyright 2017 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/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h"
#include "src/macro-assembler.h"
#include "src/runtime/runtime.h"
namespace v8 {
namespace internal {
// -----------------------------------------------------------------------------
// Interrupt and stack checks.
void Builtins::Generate_InterruptCheck(MacroAssembler* masm) {
masm->TailCallRuntime(Runtime::kInterrupt);
}
void Builtins::Generate_StackCheck(MacroAssembler* masm) {
masm->TailCallRuntime(Runtime::kStackGuard);
}
// -----------------------------------------------------------------------------
// TurboFan support builtins.
TF_BUILTIN(CopyFastSmiOrObjectElements, CodeStubAssembler) {
Node* object = Parameter(Descriptor::kObject);
// Load the {object}s elements.
Node* source = LoadObjectField(object, JSObject::kElementsOffset);
ParameterMode mode = OptimalParameterMode();
Node* length = TaggedToParameter(LoadFixedArrayBaseLength(source), mode);
// Check if we can allocate in new space.
ElementsKind kind = FAST_ELEMENTS;
int max_elements = FixedArrayBase::GetMaxLengthForNewSpaceAllocation(kind);
Label if_newspace(this), if_oldspace(this);
Branch(UintPtrOrSmiLessThan(length, IntPtrOrSmiConstant(max_elements, mode),
mode),
&if_newspace, &if_oldspace);
BIND(&if_newspace);
{
Node* target = AllocateFixedArray(kind, length, mode);
CopyFixedArrayElements(kind, source, target, length, SKIP_WRITE_BARRIER,
mode);
StoreObjectField(object, JSObject::kElementsOffset, target);
Return(target);
}
BIND(&if_oldspace);
{
Node* target = AllocateFixedArray(kind, length, mode, kPretenured);
CopyFixedArrayElements(kind, source, target, length, UPDATE_WRITE_BARRIER,
mode);
StoreObjectField(object, JSObject::kElementsOffset, target);
Return(target);
}
}
TF_BUILTIN(GrowFastDoubleElements, CodeStubAssembler) {
Node* object = Parameter(Descriptor::kObject);
Node* key = Parameter(Descriptor::kKey);
Node* context = Parameter(Descriptor::kContext);
Label runtime(this, Label::kDeferred);
Node* elements = LoadElements(object);
elements = TryGrowElementsCapacity(object, elements, FAST_DOUBLE_ELEMENTS,
key, &runtime);
Return(elements);
BIND(&runtime);
TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
}
TF_BUILTIN(GrowFastSmiOrObjectElements, CodeStubAssembler) {
Node* object = Parameter(Descriptor::kObject);
Node* key = Parameter(Descriptor::kKey);
Node* context = Parameter(Descriptor::kContext);
Label runtime(this, Label::kDeferred);
Node* elements = LoadElements(object);
elements =
TryGrowElementsCapacity(object, elements, FAST_ELEMENTS, key, &runtime);
Return(elements);
BIND(&runtime);
TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
}
TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) {
Node* frame = Parameter(Descriptor::kFrame);
Node* length = SmiToWord(Parameter(Descriptor::kLength));
// Check if we can allocate in new space.
ElementsKind kind = FAST_ELEMENTS;
int max_elements = FixedArray::GetMaxLengthForNewSpaceAllocation(kind);
Label if_newspace(this), if_oldspace(this, Label::kDeferred);
Branch(IntPtrLessThan(length, IntPtrConstant(max_elements)), &if_newspace,
&if_oldspace);
BIND(&if_newspace);
{
// Prefer EmptyFixedArray in case of non-positive {length} (the {length}
// can be negative here for rest parameters).
Label if_empty(this), if_notempty(this);
Branch(IntPtrLessThanOrEqual(length, IntPtrConstant(0)), &if_empty,
&if_notempty);
BIND(&if_empty);
Return(EmptyFixedArrayConstant());
BIND(&if_notempty);
{
// Allocate a FixedArray in new space.
Node* result = AllocateFixedArray(kind, length);
// Compute the effective {offset} into the {frame}.
Node* offset = IntPtrAdd(length, IntPtrConstant(1));
// Copy the parameters from {frame} (starting at {offset}) to {result}.
VARIABLE(var_index, MachineType::PointerRepresentation());
Label loop(this, &var_index), done_loop(this);
var_index.Bind(IntPtrConstant(0));
Goto(&loop);
BIND(&loop);
{
// Load the current {index}.
Node* index = var_index.value();
// Check if we are done.
GotoIf(WordEqual(index, length), &done_loop);
// Load the parameter at the given {index}.
Node* value = Load(MachineType::AnyTagged(), frame,
WordShl(IntPtrSub(offset, index),
IntPtrConstant(kPointerSizeLog2)));
// Store the {value} into the {result}.
StoreFixedArrayElement(result, index, value, SKIP_WRITE_BARRIER);
// Continue with next {index}.
var_index.Bind(IntPtrAdd(index, IntPtrConstant(1)));
Goto(&loop);
}
BIND(&done_loop);
Return(result);
}
}
BIND(&if_oldspace);
{
// Allocate in old space (or large object space).
TailCallRuntime(Runtime::kNewArgumentsElements, NoContextConstant(),
BitcastWordToTagged(frame), SmiFromWord(length));
}
}
TF_BUILTIN(ReturnReceiver, CodeStubAssembler) {
Return(Parameter(Descriptor::kReceiver));
}
class DeletePropertyBaseAssembler : public CodeStubAssembler {
public:
explicit DeletePropertyBaseAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
void DeleteDictionaryProperty(Node* receiver, Node* properties, Node* name,
Node* context, Label* dont_delete,
Label* notfound) {
VARIABLE(var_name_index, MachineType::PointerRepresentation());
Label dictionary_found(this, &var_name_index);
NameDictionaryLookup<NameDictionary>(properties, name, &dictionary_found,
&var_name_index, notfound);
BIND(&dictionary_found);
Node* key_index = var_name_index.value();
Node* details =
LoadDetailsByKeyIndex<NameDictionary>(properties, key_index);
GotoIf(IsSetWord32(details, PropertyDetails::kAttributesDontDeleteMask),
dont_delete);
// Overwrite the entry itself (see NameDictionary::SetEntry).
Node* filler = TheHoleConstant();
DCHECK(Heap::RootIsImmortalImmovable(Heap::kTheHoleValueRootIndex));
StoreFixedArrayElement(properties, key_index, filler, SKIP_WRITE_BARRIER);
StoreValueByKeyIndex<NameDictionary>(properties, key_index, filler,
SKIP_WRITE_BARRIER);
StoreDetailsByKeyIndex<NameDictionary>(properties, key_index,
SmiConstant(Smi::kZero));
// Update bookkeeping information (see NameDictionary::ElementRemoved).
Node* nof = GetNumberOfElements<NameDictionary>(properties);
Node* new_nof = SmiSub(nof, SmiConstant(1));
SetNumberOfElements<NameDictionary>(properties, new_nof);
Node* num_deleted = GetNumberOfDeletedElements<NameDictionary>(properties);
Node* new_deleted = SmiAdd(num_deleted, SmiConstant(1));
SetNumberOfDeletedElements<NameDictionary>(properties, new_deleted);
// Shrink the dictionary if necessary (see NameDictionary::Shrink).
Label shrinking_done(this);
Node* capacity = GetCapacity<NameDictionary>(properties);
GotoIf(SmiGreaterThan(new_nof, SmiShr(capacity, 2)), &shrinking_done);
GotoIf(SmiLessThan(new_nof, SmiConstant(16)), &shrinking_done);
CallRuntime(Runtime::kShrinkPropertyDictionary, context, receiver, name);
Goto(&shrinking_done);
BIND(&shrinking_done);
Return(TrueConstant());
}
};
TF_BUILTIN(DeleteProperty, DeletePropertyBaseAssembler) {
Node* receiver = Parameter(Descriptor::kObject);
Node* key = Parameter(Descriptor::kKey);
Node* language_mode = Parameter(Descriptor::kLanguageMode);
Node* context = Parameter(Descriptor::kContext);
VARIABLE(var_index, MachineType::PointerRepresentation());
VARIABLE(var_unique, MachineRepresentation::kTagged, key);
Label if_index(this), if_unique_name(this), if_notunique(this),
if_notfound(this), slow(this);
GotoIf(TaggedIsSmi(receiver), &slow);
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
GotoIf(Int32LessThanOrEqual(instance_type,
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
&slow);
TryToName(key, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
&if_notunique);
BIND(&if_index);
{
Comment("integer index");
Goto(&slow); // TODO(jkummerow): Implement more smarts here.
}
BIND(&if_unique_name);
{
Comment("key is unique name");
Node* unique = var_unique.value();
CheckForAssociatedProtector(unique, &slow);
Label dictionary(this), dont_delete(this);
Node* properties = LoadProperties(receiver);
Node* properties_map = LoadMap(properties);
GotoIf(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)),
&dictionary);
// TODO(jkummerow): Implement support for fast properties?
Goto(&slow);
BIND(&dictionary);
{
DeleteDictionaryProperty(receiver, properties, unique, context,
&dont_delete, &if_notfound);
}
BIND(&dont_delete);
{
STATIC_ASSERT(LANGUAGE_END == 2);
GotoIf(SmiNotEqual(language_mode, SmiConstant(SLOPPY)), &slow);
Return(FalseConstant());
}
}
BIND(&if_notunique);
{
// If the string was not found in the string table, then no object can
// have a property with that name.
TryInternalizeString(key, &if_index, &var_index, &if_unique_name,
&var_unique, &if_notfound, &slow);
}
BIND(&if_notfound);
Return(TrueConstant());
BIND(&slow);
{
TailCallRuntime(Runtime::kDeleteProperty, context, receiver, key,
language_mode);
}
}
} // namespace internal
} // namespace v8