Index: src/arm/builtins-arm.cc |
=================================================================== |
--- src/arm/builtins-arm.cc (revision 2951) |
+++ src/arm/builtins-arm.cc (working copy) |
@@ -51,8 +51,372 @@ |
} |
+// 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<Code> array_code(code); |
__ Jump(array_code, RelocInfo::CODE_TARGET); |
@@ -60,7 +424,34 @@ |
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<Code> generic_construct_stub(code); |
__ Jump(generic_construct_stub, RelocInfo::CODE_TARGET); |