// 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/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h"
#include "src/counters.h"
#include "src/interface-descriptors.h"
#include "src/macro-assembler.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
BUILTIN(Illegal) {
UNREACHABLE();
return isolate->heap()->undefined_value(); // Make compiler happy.
}
BUILTIN(EmptyFunction) { return isolate->heap()->undefined_value(); }
BUILTIN(UnsupportedThrower) {
HandleScope scope(isolate);
THROW_NEW_ERROR_RETURN_FAILURE(isolate,
NewError(MessageTemplate::kUnsupported));
}
// -----------------------------------------------------------------------------
// Throwers for restricted function properties and strict arguments object
// properties
BUILTIN(RestrictedFunctionPropertiesThrower) {
HandleScope scope(isolate);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kRestrictedFunctionProperties));
}
BUILTIN(RestrictedStrictArgumentsPropertiesThrower) {
HandleScope scope(isolate);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kStrictPoisonPill));
}
// -----------------------------------------------------------------------------
// 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.
void Builtins::Generate_CopyFastSmiOrObjectElements(
compiler::CodeAssemblerState* state) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CopyFastSmiOrObjectElementsDescriptor Descriptor;
CodeStubAssembler assembler(state);
Node* object = assembler.Parameter(Descriptor::kObject);
// Load the {object}s elements.
Node* source = assembler.LoadObjectField(object, JSObject::kElementsOffset);
CodeStubAssembler::ParameterMode mode = assembler.OptimalParameterMode();
Node* length = assembler.TaggedToParameter(
assembler.LoadFixedArrayBaseLength(source), mode);
// Check if we can allocate in new space.
ElementsKind kind = FAST_ELEMENTS;
int max_elements = FixedArrayBase::GetMaxLengthForNewSpaceAllocation(kind);
Label if_newspace(&assembler), if_oldspace(&assembler);
assembler.Branch(
assembler.UintPtrOrSmiLessThan(
length, assembler.IntPtrOrSmiConstant(max_elements, mode), mode),
&if_newspace, &if_oldspace);
assembler.Bind(&if_newspace);
{
Node* target = assembler.AllocateFixedArray(kind, length, mode);
assembler.CopyFixedArrayElements(kind, source, target, length,
SKIP_WRITE_BARRIER, mode);
assembler.StoreObjectField(object, JSObject::kElementsOffset, target);
assembler.Return(target);
}
assembler.Bind(&if_oldspace);
{
Node* target = assembler.AllocateFixedArray(kind, length, mode,
CodeStubAssembler::kPretenured);
assembler.CopyFixedArrayElements(kind, source, target, length,
UPDATE_WRITE_BARRIER, mode);
assembler.StoreObjectField(object, JSObject::kElementsOffset, target);
assembler.Return(target);
}
}
void Builtins::Generate_GrowFastDoubleElements(
compiler::CodeAssemblerState* state) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef GrowArrayElementsDescriptor Descriptor;
CodeStubAssembler assembler(state);
Node* object = assembler.Parameter(Descriptor::kObject);
Node* key = assembler.Parameter(Descriptor::kKey);
Node* context = assembler.Parameter(Descriptor::kContext);
Label runtime(&assembler, CodeStubAssembler::Label::kDeferred);
Node* elements = assembler.LoadElements(object);
elements = assembler.TryGrowElementsCapacity(
object, elements, FAST_DOUBLE_ELEMENTS, key, &runtime);
assembler.Return(elements);
assembler.Bind(&runtime);
assembler.TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
}
void Builtins::Generate_GrowFastSmiOrObjectElements(
compiler::CodeAssemblerState* state) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef GrowArrayElementsDescriptor Descriptor;
CodeStubAssembler assembler(state);
Node* object = assembler.Parameter(Descriptor::kObject);
Node* key = assembler.Parameter(Descriptor::kKey);
Node* context = assembler.Parameter(Descriptor::kContext);
Label runtime(&assembler, CodeStubAssembler::Label::kDeferred);
Node* elements = assembler.LoadElements(object);
elements = assembler.TryGrowElementsCapacity(object, elements, FAST_ELEMENTS,
key, &runtime);
assembler.Return(elements);
assembler.Bind(&runtime);
assembler.TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
}
namespace {
void Generate_NewArgumentsElements(CodeStubAssembler* assembler,
compiler::Node* frame,
compiler::Node* length) {
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
typedef compiler::Node Node;
// Check if we can allocate in new space.
ElementsKind kind = FAST_ELEMENTS;
int max_elements = FixedArray::GetMaxLengthForNewSpaceAllocation(kind);
Label if_newspace(assembler), if_oldspace(assembler, Label::kDeferred);
assembler->Branch(assembler->IntPtrLessThan(
length, assembler->IntPtrConstant(max_elements)),
&if_newspace, &if_oldspace);
assembler->Bind(&if_newspace);
{
// Prefer EmptyFixedArray in case of non-positive {length} (the {length}
// can be negative here for rest parameters).
Label if_empty(assembler), if_notempty(assembler);
assembler->Branch(
assembler->IntPtrLessThanOrEqual(length, assembler->IntPtrConstant(0)),
&if_empty, &if_notempty);
assembler->Bind(&if_empty);
assembler->Return(assembler->EmptyFixedArrayConstant());
assembler->Bind(&if_notempty);
{
// Allocate a FixedArray in new space.
Node* result = assembler->AllocateFixedArray(kind, length);
// Compute the effective {offset} into the {frame}.
Node* offset = assembler->IntPtrAdd(length, assembler->IntPtrConstant(1));
// Copy the parameters from {frame} (starting at {offset}) to {result}.
Variable var_index(assembler, MachineType::PointerRepresentation());
Label loop(assembler, &var_index), done_loop(assembler);
var_index.Bind(assembler->IntPtrConstant(0));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {index}.
Node* index = var_index.value();
// Check if we are done.
assembler->GotoIf(assembler->WordEqual(index, length), &done_loop);
// Load the parameter at the given {index}.
Node* value = assembler->Load(
MachineType::AnyTagged(), frame,
assembler->WordShl(assembler->IntPtrSub(offset, index),
assembler->IntPtrConstant(kPointerSizeLog2)));
// Store the {value} into the {result}.
assembler->StoreFixedArrayElement(result, index, value,
SKIP_WRITE_BARRIER);
// Continue with next {index}.
var_index.Bind(
assembler->IntPtrAdd(index, assembler->IntPtrConstant(1)));
assembler->Goto(&loop);
}
assembler->Bind(&done_loop);
assembler->Return(result);
}
}
assembler->Bind(&if_oldspace);
{
// Allocate in old space (or large object space).
assembler->TailCallRuntime(
Runtime::kNewArgumentsElements, assembler->NoContextConstant(),
assembler->BitcastWordToTagged(frame), assembler->SmiFromWord(length));
}
}
} // namespace
void Builtins::Generate_NewUnmappedArgumentsElements(
compiler::CodeAssemblerState* state) {
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
typedef compiler::Node Node;
typedef NewArgumentsElementsDescriptor Descriptor;
CodeStubAssembler assembler(state);
Node* formal_parameter_count =
assembler.Parameter(Descriptor::kFormalParameterCount);
// Determine the frame that holds the parameters.
Label done(&assembler);
Variable var_frame(&assembler, MachineType::PointerRepresentation()),
var_length(&assembler, MachineType::PointerRepresentation());
var_frame.Bind(assembler.LoadParentFramePointer());
var_length.Bind(formal_parameter_count);
Node* parent_frame = assembler.Load(
MachineType::Pointer(), var_frame.value(),
assembler.IntPtrConstant(StandardFrameConstants::kCallerFPOffset));
Node* parent_frame_type =
assembler.Load(MachineType::AnyTagged(), parent_frame,
assembler.IntPtrConstant(
CommonFrameConstants::kContextOrFrameTypeOffset));
assembler.GotoIfNot(assembler.MarkerIsFrameType(
parent_frame_type, StackFrame::ARGUMENTS_ADAPTOR),
&done);
{
// Determine the length from the ArgumentsAdaptorFrame.
Node* length = assembler.LoadAndUntagSmi(
parent_frame, ArgumentsAdaptorFrameConstants::kLengthOffset);
// Take the arguments from the ArgumentsAdaptorFrame.
var_frame.Bind(parent_frame);
var_length.Bind(length);
}
assembler.Goto(&done);
// Allocate the actual FixedArray for the elements.
assembler.Bind(&done);
Generate_NewArgumentsElements(&assembler, var_frame.value(),
var_length.value());
}
void Builtins::Generate_NewRestParameterElements(
compiler::CodeAssemblerState* state) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef NewArgumentsElementsDescriptor Descriptor;
CodeStubAssembler assembler(state);
Node* formal_parameter_count =
assembler.Parameter(Descriptor::kFormalParameterCount);
// Check if we have an ArgumentsAdaptorFrame, as we will only have rest
// parameters in that case.
Label if_empty(&assembler);
Node* frame = assembler.Load(
MachineType::Pointer(), assembler.LoadParentFramePointer(),
assembler.IntPtrConstant(StandardFrameConstants::kCallerFPOffset));
Node* frame_type =
assembler.Load(MachineType::AnyTagged(), frame,
assembler.IntPtrConstant(
CommonFrameConstants::kContextOrFrameTypeOffset));
assembler.GotoIfNot(
assembler.MarkerIsFrameType(frame_type, StackFrame::ARGUMENTS_ADAPTOR),
&if_empty);
// Determine the length from the ArgumentsAdaptorFrame.
Node* frame_length = assembler.LoadAndUntagSmi(
frame, ArgumentsAdaptorFrameConstants::kLengthOffset);
// Compute the actual rest parameter length (may be negative).
Node* length = assembler.IntPtrSub(frame_length, formal_parameter_count);
// Allocate the actual FixedArray for the elements.
Generate_NewArgumentsElements(&assembler, frame, length);
// No rest parameters, return an empty FixedArray.
assembler.Bind(&if_empty);
assembler.Return(assembler.EmptyFixedArrayConstant());
}
void Builtins::Generate_ReturnReceiver(compiler::CodeAssemblerState* state) {
CodeStubAssembler assembler(state);
assembler.Return(assembler.Parameter(0));
}
} // namespace internal
} // namespace v8