Revision: 2797
Author: [email protected]
Date: Tue Sep  1 07:18:27 2009
Log: First step in allocating objects in generated code on ARM

This change moves the allocation of new objects into generated code. The  
allocation will bail out into the runtime system if the number of  
properties to allocate for the object exceeds the number of in-object  
properties.
Review URL: http://codereview.chromium.org/175045
http://code.google.com/p/v8/source/detail?r=2797

Modified:
  /branches/bleeding_edge/src/arm/builtins-arm.cc
  /branches/bleeding_edge/src/arm/codegen-arm.cc
  /branches/bleeding_edge/src/arm/macro-assembler-arm.cc
  /branches/bleeding_edge/src/arm/macro-assembler-arm.h
  /branches/bleeding_edge/src/objects.h

=======================================
--- /branches/bleeding_edge/src/arm/builtins-arm.cc     Thu Aug 27 00:44:37 2009
+++ /branches/bleeding_edge/src/arm/builtins-arm.cc     Tue Sep  1 07:18:27 2009
@@ -88,23 +88,144 @@
    // Enter a construct frame.
    __ EnterConstructFrame();

-  // Preserve the two incoming parameters
+  // Preserve the two incoming parameters on the stack.
    __ mov(r0, Operand(r0, LSL, kSmiTagSize));
-  __ push(r0);  // smi-tagged arguments count
-  __ push(r1);  // constructor function
-
-  // Allocate the new receiver object.
+  __ push(r0);  // Smi-tagged arguments count.
+  __ push(r1);  // Constructor function.
+
+  // Use r7 for holding undefined which is used in several places below.
+  __ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
+
+  // Try to allocate the object without transitioning into C code. If any  
of the
+  // preconditions is not met, the code bails out to the runtime call.
+  Label rt_call, allocated;
+  if (FLAG_inline_new) {
+    Label undo_allocation;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+    ExternalReference debug_step_in_fp =
+        ExternalReference::debug_step_in_fp_address();
+    __ mov(r2, Operand(debug_step_in_fp));
+    __ ldr(r2, MemOperand(r2));
+    __ tst(r2, r2);
+    __ b(nz, &rt_call);
+#endif
+
+    // Load the initial map and verify that it is in fact a map.
+    // r1: constructor function
+    // r7: undefined
+    __ ldr(r2, FieldMemOperand(r1,  
JSFunction::kPrototypeOrInitialMapOffset));
+    __ tst(r2, Operand(kSmiTagMask));
+    __ b(eq, &rt_call);
+    __ CompareObjectType(r2, r3, r4, MAP_TYPE);
+    __ b(ne, &rt_call);
+
+    // Check that the constructor is not constructing a JSFunction (see  
comments
+    // in Runtime_NewObject in runtime.cc). In which case the initial map's
+    // instance type would be JS_FUNCTION_TYPE.
+    // r1: constructor function
+    // r2: initial map
+    // r7: undefined
+    __ CompareInstanceType(r2, r3, JS_FUNCTION_TYPE);
+    __ b(eq, &rt_call);
+
+    // Now allocate the JSObject on the heap.
+    // r1: constructor function
+    // r2: initial map
+    // r7: undefined
+    __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset));
+    // Make sure that the maximum heap object size will never cause us
+    // problem here, because it is always greater than the maximum
+    // instance size that can be represented in a byte.
+    ASSERT(Heap::MaxObjectSizeInPagedSpace() >=  
JSObject::kMaxInstanceSize);
+    __ AllocateObjectInNewSpace(r3, r4, r5, r6, &rt_call, false);
+    // Allocated the JSObject, now initialize the fields. Map is set to  
initial
+    // map and properties and elements are set to empty fixed array.
+    // r1: constructor function
+    // r2: initial map
+    // r3: object size
+    // r4: JSObject (not tagged)
+    // r7: undefined
+    __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex);
+    __ mov(r5, r4);
+    ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
+    __ str(r2, MemOperand(r5, kPointerSize, PostIndex));
+    ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
+    __ str(r6, MemOperand(r5, kPointerSize, PostIndex));
+    ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
+    __ str(r6, MemOperand(r5, kPointerSize, PostIndex));
+
+    // Fill all the in-object properties with undefined.
+    // r1: constructor function
+    // r2: initial map
+    // r3: object size (in words)
+    // r4: JSObject (not tagged)
+    // r5: First in-object property of JSObject (not tagged)
+    // r7: undefined
+    __ add(r6, r4, Operand(r3, LSL, kPointerSizeLog2));  // End of object.
+    ASSERT_EQ(12, JSObject::kHeaderSize);
+    { Label loop, entry;
+      __ b(&entry);
+      __ bind(&loop);
+      __ str(r7, MemOperand(r5, kPointerSize, PostIndex));
+      __ bind(&entry);
+      __ cmp(r5, Operand(r6));
+      __ b(lt, &loop);
+    }
+
+    // Add the object tag to make the JSObject real, so that we can  
continue and
+    // jump into the continuation code at any time from now on. Any  
failures
+    // need to undo the allocation, so that the heap is in a consistent  
state
+    // and verifiable.
+    __ add(r4, r4, Operand(kHeapObjectTag));
+
+    // Check if a non-empty properties array is needed. Continue with  
allocated
+    // object if not fall through to runtime call if it is.
+    // r1: constructor function
+    // r2: initial map
+    // r4: JSObject
+    // r5: start of next object (not tagged)
+    // r7: undefined
+    __ ldrb(r3, FieldMemOperand(r2, Map::kUnusedPropertyFieldsOffset));
+    // The field instance sizes contains both pre-allocated property  
fields and
+    // in-object properties.
+    __ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset));
+    __ and_(r6,
+            r0,
+            Operand(0x000000FF << Map::kPreAllocatedPropertyFieldsByte *  
8));
+    __ add(r3, r3, Operand(r6, LSR, Map::kPreAllocatedPropertyFieldsByte *  
8));
+    __ and_(r6, r0, Operand(0x000000FF << Map::kInObjectPropertiesByte *  
8));
+    __ sub(r3, r3, Operand(r6, LSR, Map::kInObjectPropertiesByte * 8),  
SetCC);
+
+    // Done if no extra properties are to be allocated.
+    __ b(eq, &allocated);
+    __ Assert(pl, "Property allocation count failed.");
+
+    // Undo the setting of the new top so that the heap is verifiable. For
+    // example, the map's unused properties potentially do not match the
+    // allocated objects unused properties.
+    // r4: JSObject (previous new top)
+    __ bind(&undo_allocation);
+    __ UndoAllocationInNewSpace(r4, r5);
+  }
+
+  // Allocate the new receiver object using the runtime call.
+  __ bind(&rt_call);
    __ push(r1);  // argument for Runtime_NewObject
    __ CallRuntime(Runtime::kNewObject, 1);
-  __ push(r0);  // save the receiver
+  __ mov(r4, r0);
+
+  // Receiver for constructor call allocated.
+  // r4: JSObject
+  __ bind(&allocated);
+  __ push(r4);

    // Push the function and the allocated receiver from the stack.
    // sp[0]: receiver (newly allocated object)
    // sp[1]: constructor function
    // sp[2]: number of arguments (smi-tagged)
    __ ldr(r1, MemOperand(sp, kPointerSize));
-  __ push(r1);  // function
-  __ push(r0);  // receiver
+  __ push(r1);  // Constructor function.
+  __ push(r4);  // Receiver.

    // Reload the number of arguments from the stack.
    // r1: constructor function
@@ -194,6 +315,7 @@
    __ LeaveConstructFrame();
    __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
    __ add(sp, sp, Operand(kPointerSize));
+  __ IncrementCounter(&Counters::constructed_objects, 1, r1, r2);
    __ Jump(lr);
  }

=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.cc      Tue Sep  1 00:36:46 2009
+++ /branches/bleeding_edge/src/arm/codegen-arm.cc      Tue Sep  1 07:18:27 2009
@@ -4950,7 +4950,7 @@
      Register scratch2) {  // Another scratch register.
    // Allocate an object in the heap for the heap number and tag it as a  
heap
    // object.
-  __ AllocateObjectInNewSpace(HeapNumber::kSize,
+  __ AllocateObjectInNewSpace(HeapNumber::kSize / kPointerSize,
                                result,
                                scratch1,
                                scratch2,
=======================================
--- /branches/bleeding_edge/src/arm/macro-assembler-arm.cc      Tue Sep  1  
00:36:46 2009
+++ /branches/bleeding_edge/src/arm/macro-assembler-arm.cc      Tue Sep  1  
07:18:27 2009
@@ -790,7 +790,7 @@
        ExternalReference::new_space_allocation_limit_address();
    mov(scratch2, Operand(new_space_allocation_limit));
    ldr(scratch2, MemOperand(scratch2));
-  add(result, result, Operand(object_size));
+  add(result, result, Operand(object_size * kPointerSize));
    cmp(result, Operand(scratch2));
    b(hi, gc_required);

@@ -799,11 +799,70 @@

    // Tag and adjust back to start of new object.
    if (tag_allocated_object) {
-    sub(result, result, Operand(object_size - kHeapObjectTag));
+    sub(result, result, Operand((object_size * kPointerSize) -
+                                kHeapObjectTag));
    } else {
-    sub(result, result, Operand(object_size));
+    sub(result, result, Operand(object_size * kPointerSize));
    }
  }
+
+
+void MacroAssembler::AllocateObjectInNewSpace(Register object_size,
+                                              Register result,
+                                              Register scratch1,
+                                              Register scratch2,
+                                              Label* gc_required,
+                                              bool tag_allocated_object) {
+  ASSERT(!result.is(scratch1));
+  ASSERT(!scratch1.is(scratch2));
+
+  // Load address of new object into result and allocation top address into
+  // scratch1.
+  ExternalReference new_space_allocation_top =
+      ExternalReference::new_space_allocation_top_address();
+  mov(scratch1, Operand(new_space_allocation_top));
+  ldr(result, MemOperand(scratch1));
+
+  // Calculate new top and bail out if new space is exhausted. Use result
+  // to calculate the new top. Object size is in words so a shift is  
required to
+  // get the number of bytes
+  ExternalReference new_space_allocation_limit =
+      ExternalReference::new_space_allocation_limit_address();
+  mov(scratch2, Operand(new_space_allocation_limit));
+  ldr(scratch2, MemOperand(scratch2));
+  add(result, result, Operand(object_size, LSL, kPointerSizeLog2));
+
+  cmp(result, Operand(scratch2));
+  b(hi, gc_required);
+
+  // Update allocation top. result temporarily holds the new top,
+  str(result, MemOperand(scratch1));
+
+  // Tag and adjust back to start of new object.
+  if (tag_allocated_object) {
+    sub(result, result, Operand(object_size, LSL, kPointerSizeLog2));
+    add(result, result, Operand(kHeapObjectTag));
+  } else {
+    sub(result, result, Operand(object_size, LSL, kPointerSizeLog2));
+  }
+}
+
+
+void MacroAssembler::UndoAllocationInNewSpace(Register object,
+                                              Register scratch) {
+  ExternalReference new_space_allocation_top =
+      ExternalReference::new_space_allocation_top_address();
+
+  // Make sure the object has no tag before resetting top.
+  and_(object, object, Operand(~kHeapObjectTagMask));
+#ifdef DEBUG
+  mov(scratch, Operand(new_space_allocation_top));
+  ldr(scratch, MemOperand(scratch));
+  cmp(object, scratch);
+  Check(lt, "Undo allocation of non allocated memory");
+#endif
+  str(object, MemOperand(scratch));
+}


  void MacroAssembler::CompareObjectType(Register function,
@@ -811,6 +870,13 @@
                                         Register type_reg,
                                         InstanceType type) {
    ldr(map, FieldMemOperand(function, HeapObject::kMapOffset));
+  CompareInstanceType(map, type_reg, type);
+}
+
+
+void MacroAssembler::CompareInstanceType(Register map,
+                                         Register type_reg,
+                                         InstanceType type) {
    ldrb(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
    cmp(type_reg, Operand(type));
  }
=======================================
--- /branches/bleeding_edge/src/arm/macro-assembler-arm.h       Tue Sep  1  
00:36:46 2009
+++ /branches/bleeding_edge/src/arm/macro-assembler-arm.h       Tue Sep  1  
07:18:27 2009
@@ -190,16 +190,28 @@
    //  
---------------------------------------------------------------------------
    // Allocation support

-  // Allocate an object in new space. If the new space is exhausted control
-  // continues at the gc_required label. The allocated object is returned  
in
-  // result. If the flag tag_allocated_object is true the result is tagged  
as
-  // as a heap object.
+  // Allocate an object in new space. The object_size is specified in  
words (not
+  // bytes). If the new space is exhausted control continues at the  
gc_required
+  // label. The allocated object is returned in result. If the flag
+  // tag_allocated_object is true the result is tagged as as a heap object.
    void AllocateObjectInNewSpace(int object_size,
                                  Register result,
                                  Register scratch1,
                                  Register scratch2,
                                  Label* gc_required,
                                  bool tag_allocated_object);
+  void AllocateObjectInNewSpace(Register object_size,
+                                Register result,
+                                Register scratch1,
+                                Register scratch2,
+                                Label* gc_required,
+                                bool tag_allocated_object);
+
+  // Undo allocation in new space. The object passed and objects allocated  
after
+  // it will no longer be allocated. The caller must make sure that no  
pointers
+  // are left to the object(s) no longer allocated as they would be  
invalid when
+  // allocation is undone.
+  void UndoAllocationInNewSpace(Register object, Register scratch);

    //  
---------------------------------------------------------------------------
    // Support functions.
@@ -220,12 +232,21 @@
    // It leaves the map in the map register (unless the type_reg and map  
register
    // are the same register).  It leaves the heap object in the heap_object
    // register unless the heap_object register is the same register as one  
of the
-  // other // registers.
+  // other registers.
    void CompareObjectType(Register heap_object,
                           Register map,
                           Register type_reg,
                           InstanceType type);

+  // Compare instance type in a map.  map contains a valid map object whose
+  // object type should be compared with the given type.  This both
+  // sets the flags and leaves the object type in the type_reg register.   
It
+  // leaves the heap object in the heap_object register unless the  
heap_object
+  // register is the same register as type_reg.
+  void CompareInstanceType(Register map,
+                           Register type_reg,
+                           InstanceType type);
+
    inline void BranchOnSmi(Register value, Label* smi_label) {
      tst(value, Operand(kSmiTagMask));
      b(eq, smi_label);
=======================================
--- /branches/bleeding_edge/src/objects.h       Tue Sep  1 06:08:16 2009
+++ /branches/bleeding_edge/src/objects.h       Tue Sep  1 07:18:27 2009
@@ -2891,8 +2891,12 @@

    // Byte offsets within kInstanceSizesOffset.
    static const int kInstanceSizeOffset = kInstanceSizesOffset + 0;
-  static const int kInObjectPropertiesOffset = kInstanceSizesOffset + 1;
-  static const int kPreAllocatedPropertyFieldsOffset =  
kInstanceSizesOffset + 2;
+  static const int kInObjectPropertiesByte = 1;
+  static const int kInObjectPropertiesOffset =
+      kInstanceSizesOffset + kInObjectPropertiesByte;
+  static const int kPreAllocatedPropertyFieldsByte = 2;
+  static const int kPreAllocatedPropertyFieldsOffset =
+      kInstanceSizesOffset + kPreAllocatedPropertyFieldsByte;
    // The byte at position 3 is not in use at the moment.

    // Byte offsets within kInstanceAttributesOffset attributes.

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to