Reduced memory usage of primitive fields smaller than 4-bytes

Reduced memory used by byte and boolean fields from 4 bytes down to a
single byte and shorts and chars down to two bytes. Fields are now
arranged as Reference followed by decreasing component sizes, with
fields shuffled forward as needed.

Bug: 8135266
Change-Id: I65eaf31ed27e5bd5ba0c7d4606454b720b074752
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index dcf8f5f..9d85fa6 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2078,6 +2078,8 @@
                                                        const DexFile::ClassDef& dex_class_def) {
   const byte* class_data = dex_file.GetClassData(dex_class_def);
   size_t num_ref = 0;
+  size_t num_8 = 0;
+  size_t num_16 = 0;
   size_t num_32 = 0;
   size_t num_64 = 0;
   if (class_data != NULL) {
@@ -2085,16 +2087,33 @@
       const DexFile::FieldId& field_id = dex_file.GetFieldId(it.GetMemberIndex());
       const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id);
       char c = descriptor[0];
-      if (c == 'L' || c == '[') {
-        num_ref++;
-      } else if (c == 'J' || c == 'D') {
-        num_64++;
-      } else {
-        num_32++;
+      switch (c) {
+        case 'L':
+        case '[':
+          num_ref++;
+          break;
+        case 'J':
+        case 'D':
+          num_64++;
+          break;
+        case 'I':
+        case 'F':
+          num_32++;
+          break;
+        case 'S':
+        case 'C':
+          num_16++;
+          break;
+        case 'B':
+        case 'Z':
+          num_8++;
+          break;
+        default:
+          LOG(FATAL) << "Unknown descriptor: " << c;
       }
     }
   }
-  return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref);
+  return mirror::Class::ComputeClassSize(false, 0, num_8, num_16, num_32, num_64, num_ref);
 }
 
 OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx,
@@ -2455,6 +2474,89 @@
                                                    have_portable_code);
 }
 
+template<int n>
+void ClassLinker::AlignFields(size_t& current_field, const size_t num_fields,
+                              MemberOffset& field_offset,
+                              mirror::ObjectArray<mirror::ArtField>* fields,
+                              std::deque<mirror::ArtField*>& grouped_and_sorted_fields) {
+  if (current_field != num_fields && !IsAligned<n>(field_offset.Uint32Value())) {
+    size_t gap = (n - (field_offset.Uint32Value() & (n - 1)));
+    // Avoid padding unless a field that requires alignment actually exists.
+    bool needs_padding = false;
+    for (size_t i = 0; i < grouped_and_sorted_fields.size(); ++i) {
+      mirror::ArtField* field = grouped_and_sorted_fields[i];
+      Primitive::Type type = field->GetTypeAsPrimitiveType();
+      CHECK(type != Primitive::kPrimNot) << PrettyField(field);  // should be primitive types
+      // Too big to fill the gap.
+      if (Primitive::ComponentSize(type) >= n) {
+        needs_padding = true;
+        continue;
+      }
+      if (needs_padding) {
+        // Shift as many fields as possible to fill the gaps.
+        size_t cursor = i;
+        mirror::ArtField* shift_field;
+        Primitive::Type shift_type;
+        while (cursor < grouped_and_sorted_fields.size() && gap > 0) {
+          // Find field that can current in current gap.
+          do {
+            DCHECK_LT(cursor, grouped_and_sorted_fields.size()) << "Cursor overran fields.";
+            shift_field = grouped_and_sorted_fields[cursor];
+            shift_type = shift_field->GetTypeAsPrimitiveType();
+            CHECK(shift_type != Primitive::kPrimNot) << PrettyField(shift_field);
+            // Can fit.
+            if (Primitive::ComponentSize(shift_type) <= gap) {
+              break;
+            }
+            ++cursor;
+          } while (cursor < grouped_and_sorted_fields.size());
+
+          if (cursor < grouped_and_sorted_fields.size()) {
+            fields->Set<false>(current_field++, shift_field);
+            shift_field->SetOffset(field_offset);
+            field_offset = MemberOffset(field_offset.Uint32Value() +
+                Primitive::ComponentSize(shift_type));
+            gap -= Primitive::ComponentSize(shift_type);
+            grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + cursor);
+          }
+        }
+      }
+      break;
+    }
+    // No further shuffling available, pad whatever space is left.
+    if (needs_padding) {
+      field_offset = MemberOffset(field_offset.Uint32Value() + gap);
+    }
+    DCHECK(!needs_padding || IsAligned<n>(field_offset.Uint32Value())) << "Needed " <<
+      n << " byte alignment, but not aligned after align with offset: " <<
+      field_offset.Uint32Value();
+  }
+}
+
+template<int n>
+void ClassLinker::ShuffleForward(size_t &current_field, const size_t num_fields,
+                                 MemberOffset& field_offset,
+                                 mirror::ObjectArray<mirror::ArtField>* fields,
+                                 std::deque<mirror::ArtField*>& grouped_and_sorted_fields) {
+  while (!grouped_and_sorted_fields.empty() && current_field != num_fields) {
+    mirror::ArtField* field = grouped_and_sorted_fields.front();
+    Primitive::Type type = field->GetTypeAsPrimitiveType();
+    CHECK(type != Primitive::kPrimNot) << PrettyField(field);  // should be primitive types
+    if (Primitive::ComponentSize(type) != n) {
+      DCHECK_LT(Primitive::ComponentSize(type), static_cast<unsigned int>(n)) <<
+          "Encountered a larger field (" << Primitive::ComponentSize(type) << ") " <<
+          "while shuffling fields of size: " << n;
+      // We should've shuffled all field of size n forward by this point.
+      break;
+    }
+    DCHECK(IsAligned<n>(field_offset.Uint32Value()));
+    grouped_and_sorted_fields.pop_front();
+    fields->Set<false>(current_field++, field);
+    field->SetOffset(field_offset);
+    field_offset = MemberOffset(field_offset.Uint32Value() + n);
+  }
+}
+
 void ClassLinker::LoadClass(const DexFile& dex_file,
                             const DexFile::ClassDef& dex_class_def,
                             ConstHandle<mirror::Class> klass,
@@ -4674,20 +4776,20 @@
   // No thread safety analysis as will be called from STL. Checked lock held in constructor.
   bool operator()(mirror::ArtField* field1, mirror::ArtField* field2)
       NO_THREAD_SAFETY_ANALYSIS {
-    // First come reference fields, then 64-bit, and finally 32-bit
+    // First come reference fields, then 64-bit, then 32-bit, and then 16-bit, then finally 8-bit.
     Primitive::Type type1 = field1->GetTypeAsPrimitiveType();
     Primitive::Type type2 = field2->GetTypeAsPrimitiveType();
     if (type1 != type2) {
       bool is_primitive1 = type1 != Primitive::kPrimNot;
       bool is_primitive2 = type2 != Primitive::kPrimNot;
-      bool is64bit1 = is_primitive1 && (type1 == Primitive::kPrimLong ||
-                                        type1 == Primitive::kPrimDouble);
-      bool is64bit2 = is_primitive2 && (type2 == Primitive::kPrimLong ||
-                                        type2 == Primitive::kPrimDouble);
-      int order1 = !is_primitive1 ? 0 : (is64bit1 ? 1 : 2);
-      int order2 = !is_primitive2 ? 0 : (is64bit2 ? 1 : 2);
-      if (order1 != order2) {
-        return order1 < order2;
+      if (type1 != type2) {
+        if (is_primitive1 && is_primitive2) {
+          // Larger primitive types go first.
+          return Primitive::ComponentSize(type1) > Primitive::ComponentSize(type2);
+        } else {
+          // Reference always goes first.
+          return !is_primitive1;
+        }
       }
     }
     // same basic group? then sort by string.
@@ -4709,7 +4811,7 @@
     if (klass->ShouldHaveEmbeddedImtAndVTable()) {
       // Static fields come after the embedded tables.
       base = mirror::Class::ComputeClassSize(true, klass->GetVTableDuringLinking()->GetLength(),
-                                             0, 0, 0);
+                                             0, 0, 0, 0, 0);
     }
     field_offset = MemberOffset(base);
   } else {
@@ -4726,6 +4828,8 @@
   // we want a relatively stable order so that adding new fields
   // minimizes disruption of C++ version such as Class and Method.
   std::deque<mirror::ArtField*> grouped_and_sorted_fields;
+  const char* old_no_suspend_cause  = Thread::Current()->StartAssertNoThreadSuspension(
+      "Naked ArtField references in deque");
   for (size_t i = 0; i < num_fields; i++) {
     mirror::ArtField* f = fields->Get(i);
     CHECK(f != NULL) << PrettyClass(klass.Get());
@@ -4734,7 +4838,7 @@
   std::sort(grouped_and_sorted_fields.begin(), grouped_and_sorted_fields.end(),
             LinkFieldsComparator());
 
-  // References should be at the front.
+  // References should be at the front, unless we need to pad.
   size_t current_field = 0;
   size_t num_reference_fields = 0;
   for (; current_field < num_fields; current_field++) {
@@ -4751,44 +4855,21 @@
     field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
   }
 
-  // Now we want to pack all of the double-wide fields together.  If
-  // we're not aligned, though, we want to shuffle one 32-bit field
-  // into place.  If we can't find one, we'll have to pad it.
-  if (current_field != num_fields && !IsAligned<8>(field_offset.Uint32Value())) {
-    for (size_t i = 0; i < grouped_and_sorted_fields.size(); i++) {
-      mirror::ArtField* field = grouped_and_sorted_fields[i];
-      Primitive::Type type = field->GetTypeAsPrimitiveType();
-      CHECK(type != Primitive::kPrimNot) << PrettyField(field);  // should be primitive types
-      if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) {
-        continue;
-      }
-      fields->Set<false>(current_field++, field);
-      field->SetOffset(field_offset);
-      // drop the consumed field
-      grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + i);
-      break;
-    }
-    // whether we found a 32-bit field for padding or not, we advance
-    field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
-  }
+  AlignFields<8>(current_field, num_fields, field_offset, fields, grouped_and_sorted_fields);
+  ShuffleForward<8>(current_field, num_fields, field_offset, fields, grouped_and_sorted_fields);
+  // No need for further alignment, start of object is 4-byte aligned.
+  ShuffleForward<4>(current_field, num_fields, field_offset, fields, grouped_and_sorted_fields);
+  ShuffleForward<2>(current_field, num_fields, field_offset, fields, grouped_and_sorted_fields);
+  ShuffleForward<1>(current_field, num_fields, field_offset, fields, grouped_and_sorted_fields);
+  CHECK(grouped_and_sorted_fields.empty()) << "Missed " << grouped_and_sorted_fields.size() <<
+      " fields.";
 
-  // Alignment is good, shuffle any double-wide fields forward, and
-  // finish assigning field offsets to all fields.
-  DCHECK(current_field == num_fields || IsAligned<8>(field_offset.Uint32Value()))
-      << PrettyClass(klass.Get());
-  while (!grouped_and_sorted_fields.empty()) {
-    mirror::ArtField* field = grouped_and_sorted_fields.front();
-    grouped_and_sorted_fields.pop_front();
-    Primitive::Type type = field->GetTypeAsPrimitiveType();
-    CHECK(type != Primitive::kPrimNot) << PrettyField(field);  // should be primitive types
-    fields->Set<false>(current_field, field);
-    field->SetOffset(field_offset);
-    field_offset = MemberOffset(field_offset.Uint32Value() +
-                                ((type == Primitive::kPrimLong || type == Primitive::kPrimDouble)
-                                 ? sizeof(uint64_t)
-                                 : sizeof(uint32_t)));
-    current_field++;
+  // Subclass expects superclass to be 4 byte aligned at end.
+  if (!IsAligned<4>(field_offset.Uint32Value())) {
+    field_offset = MemberOffset(RoundUp(field_offset.Uint32Value(), 4));
   }
+  CHECK(IsAligned<4>(field_offset.Uint32Value()));
+  Thread::Current()->EndAssertNoThreadSuspension(old_no_suspend_cause);
 
   // We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it.
   if (!is_static && klass->DescriptorEquals("Ljava/lang/ref/Reference;")) {