From cf46d30de947fb7671a2ce83814a5b7c0c0f044f Mon Sep 17 00:00:00 2001 From: "sgjesse@chromium.org" Date: Wed, 23 Sep 2009 09:05:12 +0000 Subject: [PATCH] Handle array construction in native code (ARM version). Ported the handle array construction in native code to ARM. See http://codereview.chromium.org/193125 for details. Review URL: http://codereview.chromium.org/217014 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2956 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/builtins-arm.cc | 395 +++++++++++++++++++++++++++++++++++++++++++++++- src/builtins.cc | 4 +- src/objects.h | 3 + 3 files changed, 399 insertions(+), 3 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index cdea1cb..4d1fe73 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -51,8 +51,372 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) { } +// Load the built-in Array function from the current context. +static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { + // Load the global context. + + __ ldr(result, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ ldr(result, + FieldMemOperand(result, GlobalObject::kGlobalContextOffset)); + // Load the Array function from the global context. + __ ldr(result, + MemOperand(result, + Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); +} + + +// This constant has the same value as JSArray::kPreallocatedArrayElements and +// if JSArray::kPreallocatedArrayElements is changed handling of loop unfolding +// below should be reconsidered. +static const int kLoopUnfoldLimit = 4; + + +// Allocate an empty JSArray. The allocated array is put into the result +// register. An elements backing store is allocated with size initial_capacity +// and filled with the hole values. +static void AllocateEmptyJSArray(MacroAssembler* masm, + Register array_function, + Register result, + Register scratch1, + Register scratch2, + Register scratch3, + int initial_capacity, + Label* gc_required) { + ASSERT(initial_capacity > 0); + // Load the initial map from the array function. + __ ldr(scratch1, FieldMemOperand(array_function, + JSFunction::kPrototypeOrInitialMapOffset)); + + // Allocate the JSArray object together with space for a fixed array with the + // requested elements. + int size = JSArray::kSize + FixedArray::SizeFor(initial_capacity); + __ AllocateObjectInNewSpace(size / kPointerSize, + result, + scratch2, + scratch3, + gc_required, + TAG_OBJECT); + + // Allocated the JSArray. Now initialize the fields except for the elements + // array. + // result: JSObject + // scratch1: initial map + // scratch2: start of next object + __ str(scratch1, FieldMemOperand(result, JSObject::kMapOffset)); + __ LoadRoot(scratch1, Heap::kEmptyFixedArrayRootIndex); + __ str(scratch1, FieldMemOperand(result, JSArray::kPropertiesOffset)); + // Field JSArray::kElementsOffset is initialized later. + __ mov(scratch3, Operand(0)); + __ str(scratch3, FieldMemOperand(result, JSArray::kLengthOffset)); + + // Calculate the location of the elements array and set elements array member + // of the JSArray. + // result: JSObject + // scratch2: start of next object + __ lea(scratch1, MemOperand(result, JSArray::kSize)); + __ str(scratch1, FieldMemOperand(result, JSArray::kElementsOffset)); + + // Clear the heap tag on the elements array. + __ and_(scratch1, scratch1, Operand(~kHeapObjectTagMask)); + + // Initialize the FixedArray and fill it with holes. FixedArray length is not + // stored as a smi. + // result: JSObject + // scratch1: elements array (untagged) + // scratch2: start of next object + __ LoadRoot(scratch3, Heap::kFixedArrayMapRootIndex); + ASSERT_EQ(0 * kPointerSize, FixedArray::kMapOffset); + __ str(scratch3, MemOperand(scratch1, kPointerSize, PostIndex)); + __ mov(scratch3, Operand(initial_capacity)); + ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); + __ str(scratch3, MemOperand(scratch1, kPointerSize, PostIndex)); + + // Fill the FixedArray with the hole value. + ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize); + ASSERT(initial_capacity <= kLoopUnfoldLimit); + __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex); + for (int i = 0; i < initial_capacity; i++) { + __ str(scratch3, MemOperand(scratch1, kPointerSize, PostIndex)); + } +} + +// Allocate a JSArray with the number of elements stored in a register. The +// register array_function holds the built-in Array function and the register +// array_size holds the size of the array as a smi. The allocated array is put +// into the result register and beginning and end of the FixedArray elements +// storage is put into registers elements_array_storage and elements_array_end +// (see below for when that is not the case). If the parameter fill_with_holes +// is true the allocated elements backing store is filled with the hole values +// otherwise it is left uninitialized. When the backing store is filled the +// register elements_array_storage is scratched. +static void AllocateJSArray(MacroAssembler* masm, + Register array_function, // Array function. + Register array_size, // As a smi. + Register result, + Register elements_array_storage, + Register elements_array_end, + Register scratch1, + Register scratch2, + bool fill_with_hole, + Label* gc_required) { + Label not_empty, allocated; + + // Load the initial map from the array function. + __ ldr(elements_array_storage, + FieldMemOperand(array_function, + JSFunction::kPrototypeOrInitialMapOffset)); + + // Check whether an empty sized array is requested. + __ tst(array_size, array_size); + __ b(nz, ¬_empty); + + // If an empty array is requested allocate a small elements array anyway. This + // keeps the code below free of special casing for the empty array. + int size = JSArray::kSize + + FixedArray::SizeFor(JSArray::kPreallocatedArrayElements); + __ AllocateObjectInNewSpace(size / kPointerSize, + result, + elements_array_end, + scratch1, + gc_required, + TAG_OBJECT); + __ jmp(&allocated); + + // Allocate the JSArray object together with space for a FixedArray with the + // requested number of elements. + __ bind(¬_empty); + ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + __ mov(elements_array_end, + Operand((JSArray::kSize + FixedArray::kHeaderSize) / kPointerSize)); + __ add(elements_array_end, + elements_array_end, + Operand(array_size, ASR, kSmiTagSize)); + __ AllocateObjectInNewSpace(elements_array_end, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + // Allocated the JSArray. Now initialize the fields except for the elements + // array. + // result: JSObject + // elements_array_storage: initial map + // array_size: size of array (smi) + __ bind(&allocated); + __ str(elements_array_storage, FieldMemOperand(result, JSObject::kMapOffset)); + __ LoadRoot(elements_array_storage, Heap::kEmptyFixedArrayRootIndex); + __ str(elements_array_storage, + FieldMemOperand(result, JSArray::kPropertiesOffset)); + // Field JSArray::kElementsOffset is initialized later. + __ str(array_size, FieldMemOperand(result, JSArray::kLengthOffset)); + + // Calculate the location of the elements array and set elements array member + // of the JSArray. + // result: JSObject + // array_size: size of array (smi) + __ add(elements_array_storage, result, Operand(JSArray::kSize)); + __ str(elements_array_storage, + FieldMemOperand(result, JSArray::kElementsOffset)); + + // Clear the heap tag on the elements array. + __ and_(elements_array_storage, + elements_array_storage, + Operand(~kHeapObjectTagMask)); + // Initialize the fixed array and fill it with holes. FixedArray length is not + // stored as a smi. + // result: JSObject + // elements_array_storage: elements array (untagged) + // array_size: size of array (smi) + ASSERT(kSmiTag == 0); + __ LoadRoot(scratch1, Heap::kFixedArrayMapRootIndex); + ASSERT_EQ(0 * kPointerSize, FixedArray::kMapOffset); + __ str(scratch1, MemOperand(elements_array_storage, kPointerSize, PostIndex)); + // Convert array_size from smi to value. + __ mov(array_size, + Operand(array_size, ASR, kSmiTagSize)); + __ tst(array_size, array_size); + // Length of the FixedArray is the number of pre-allocated elements if + // the actual JSArray has length 0 and the size of the JSArray for non-empty + // JSArrays. The length of a FixedArray is not stored as a smi. + __ mov(array_size, Operand(JSArray::kPreallocatedArrayElements), LeaveCC, eq); + ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset); + __ str(array_size, + MemOperand(elements_array_storage, kPointerSize, PostIndex)); + + // Calculate elements array and elements array end. + // result: JSObject + // elements_array_storage: elements array element storage + // array_size: size of elements array + __ add(elements_array_end, + elements_array_storage, + Operand(array_size, LSL, kPointerSizeLog2)); + + // Fill the allocated FixedArray with the hole value if requested. + // result: JSObject + // elements_array_storage: elements array element storage + // elements_array_end: start of next object + if (fill_with_hole) { + Label loop, entry; + __ LoadRoot(scratch1, Heap::kTheHoleValueRootIndex); + __ jmp(&entry); + __ bind(&loop); + __ str(scratch1, + MemOperand(elements_array_storage, kPointerSize, PostIndex)); + __ bind(&entry); + __ cmp(elements_array_storage, elements_array_end); + __ b(lt, &loop); + } +} + +// Create a new array for the built-in Array function. This function allocates +// the JSArray object and the FixedArray elements array and initializes these. +// If the Array cannot be constructed in native code the runtime is called. This +// function assumes the following state: +// r0: argc +// r1: constructor (built-in Array function) +// lr: return address +// sp[0]: last argument +// This function is used for both construct and normal calls of Array. The only +// difference between handling a construct call and a normal call is that for a +// construct call the constructor function in r1 needs to be preserved for +// entering the generic code. In both cases argc in r0 needs to be preserved. +// Both registers are preserved by this code so no need to differentiate between +// construct call and normal call. +static void ArrayNativeCode(MacroAssembler* masm, + Label *call_generic_code) { + Label argc_one_or_more, argc_two_or_more; + + // Check for array construction with zero arguments or one. + __ cmp(r0, Operand(0)); + __ b(ne, &argc_one_or_more); + + // Handle construction of an empty array. + AllocateEmptyJSArray(masm, + r1, + r2, + r3, + r4, + r5, + JSArray::kPreallocatedArrayElements, + call_generic_code); + __ IncrementCounter(&Counters::array_function_native, 1, r3, r4); + // Setup return value, remove receiver from stack and return. + __ mov(r0, r2); + __ add(sp, sp, Operand(kPointerSize)); + __ Jump(lr); + + // Check for one argument. Bail out if argument is not smi or if it is + // negative. + __ bind(&argc_one_or_more); + __ cmp(r0, Operand(1)); + __ b(ne, &argc_two_or_more); + ASSERT(kSmiTag == 0); + __ ldr(r2, MemOperand(sp)); // Get the argument from the stack. + __ and_(r3, r2, Operand(kIntptrSignBit | kSmiTagMask), SetCC); + __ b(ne, call_generic_code); + + // Handle construction of an empty array of a certain size. Bail out if size + // is too large to actually allocate an elements array. + ASSERT(kSmiTag == 0); + __ cmp(r2, Operand(JSObject::kInitialMaxFastElementArray << kSmiTagSize)); + __ b(ge, call_generic_code); + + // r0: argc + // r1: constructor + // r2: array_size (smi) + // sp[0]: argument + AllocateJSArray(masm, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + true, + call_generic_code); + __ IncrementCounter(&Counters::array_function_native, 1, r2, r4); + // Setup return value, remove receiver and argument from stack and return. + __ mov(r0, r3); + __ add(sp, sp, Operand(2 * kPointerSize)); + __ Jump(lr); + + // Handle construction of an array from a list of arguments. + __ bind(&argc_two_or_more); + __ mov(r2, Operand(r0, LSL, kSmiTagSize)); // Convet argc to a smi. + + // r0: argc + // r1: constructor + // r2: array_size (smi) + // sp[0]: last argument + AllocateJSArray(masm, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + false, + call_generic_code); + __ IncrementCounter(&Counters::array_function_native, 1, r2, r6); + + // Fill arguments as array elements. Copy from the top of the stack (last + // element) to the array backing store filling it backwards. Note: + // elements_array_end points after the backing store therefore PreIndex is + // used when filling the backing store. + // r0: argc + // r3: JSArray + // r4: elements_array storage start (untagged) + // r5: elements_array_end (untagged) + // sp[0]: last argument + Label loop, entry; + __ jmp(&entry); + __ bind(&loop); + __ ldr(r2, MemOperand(sp, kPointerSize, PostIndex)); + __ str(r2, MemOperand(r5, -kPointerSize, PreIndex)); + __ bind(&entry); + __ cmp(r4, r5); + __ b(lt, &loop); + + // Remove caller arguments and receiver from the stack, setup return value and + // return. + // r0: argc + // r3: JSArray + // sp[0]: receiver + __ add(sp, sp, Operand(kPointerSize)); + __ mov(r0, r3); + __ Jump(lr); +} + + void Builtins::Generate_ArrayCode(MacroAssembler* masm) { - // Just jump to the generic array code. + // ----------- S t a t e ------------- + // -- r0 : number of arguments + // -- lr : return address + // -- sp[...]: constructor arguments + // ----------------------------------- + Label generic_array_code, one_or_more_arguments, two_or_more_arguments; + + // Get the Array function. + GenerateLoadArrayFunction(masm, r1); + + if (FLAG_debug_code) { + // Initial map for the builtin Array function shoud be a map. + __ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset)); + __ tst(r2, Operand(kSmiTagMask)); + __ Assert(ne, "Unexpected initial map for Array function"); + __ CompareObjectType(r2, r3, r4, MAP_TYPE); + __ Assert(eq, "Unexpected initial map for Array function"); + } + + // Run the native code for the Array function called as a normal function. + ArrayNativeCode(masm, &generic_array_code); + + // Jump to the generic array code if the specialized code cannot handle + // the construction. + __ bind(&generic_array_code); Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric); Handle array_code(code); __ Jump(array_code, RelocInfo::CODE_TARGET); @@ -60,7 +424,34 @@ void Builtins::Generate_ArrayCode(MacroAssembler* masm) { void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { - // Just jump to the generic construct code. + // ----------- S t a t e ------------- + // -- r0 : number of arguments + // -- r1 : constructor function + // -- lr : return address + // -- sp[...]: constructor arguments + // ----------------------------------- + Label generic_constructor; + + if (FLAG_debug_code) { + // The array construct code is only set for the builtin Array function which + // always have a map. + GenerateLoadArrayFunction(masm, r2); + __ cmp(r1, r2); + __ Assert(eq, "Unexpected Array function"); + // Initial map for the builtin Array function should be a map. + __ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset)); + __ tst(r2, Operand(kSmiTagMask)); + __ Assert(ne, "Unexpected initial map for Array function"); + __ CompareObjectType(r2, r3, r4, MAP_TYPE); + __ Assert(eq, "Unexpected initial map for Array function"); + } + + // Run the native code for the Array function called as a constructor. + ArrayNativeCode(masm, &generic_constructor); + + // Jump to the generic construct code in case the specialized code cannot + // handle the construction. + __ bind(&generic_constructor); Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric); Handle generic_construct_stub(code); __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET); diff --git a/src/builtins.cc b/src/builtins.cc index 5fe4ba9..afb5427 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -172,7 +172,9 @@ BUILTIN(ArrayCodeGeneric) { } // Optimize the case where there are no parameters passed. - if (args.length() == 1) return array->Initialize(4); + if (args.length() == 1) { + return array->Initialize(JSArray::kPreallocatedArrayElements); + } // Take the arguments as elements. int number_of_elements = args.length() - 1; diff --git a/src/objects.h b/src/objects.h index 5de9afa..f74ec86 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4389,6 +4389,9 @@ class JSArray: public JSObject { void JSArrayVerify(); #endif + // Number of element slots to pre-allocate for an empty array. + static const int kPreallocatedArrayElements = 4; + // Layout description. static const int kLengthOffset = JSObject::kHeaderSize; static const int kSize = kLengthOffset + kPointerSize; -- 2.7.4