| // 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 |