[optimizing compiler] add HTemporary support for long and doubles

Change-Id: I5247ecd71d0193050484b7632c804c9bfd20f924
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index d3ac4e0..a3ca061 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -41,25 +41,29 @@
  */
 class Temporaries : public ValueObject {
  public:
-  Temporaries(HGraph* graph, size_t count) : graph_(graph), count_(count), index_(0) {
-    graph_->UpdateNumberOfTemporaries(count_);
-  }
+  explicit Temporaries(HGraph* graph) : graph_(graph), index_(0) {}
 
   void Add(HInstruction* instruction) {
-    // We currently only support vreg size temps.
-    DCHECK(instruction->GetType() != Primitive::kPrimLong
-           && instruction->GetType() != Primitive::kPrimDouble);
-    HInstruction* temp = new (graph_->GetArena()) HTemporary(index_++);
+    HInstruction* temp = new (graph_->GetArena()) HTemporary(index_);
     instruction->GetBlock()->AddInstruction(temp);
+
     DCHECK(temp->GetPrevious() == instruction);
+
+    size_t offset;
+    if (instruction->GetType() == Primitive::kPrimLong
+        || instruction->GetType() == Primitive::kPrimDouble) {
+      offset = 2;
+    } else {
+      offset = 1;
+    }
+    index_ += offset;
+
+    graph_->UpdateTemporariesVRegSlots(index_);
   }
 
  private:
   HGraph* const graph_;
 
-  // The total number of temporaries that will be used.
-  const size_t count_;
-
   // Current index in the temporary stack, updated by `Add`.
   size_t index_;
 };
@@ -408,7 +412,7 @@
   }
 
   size_t start_index = 0;
-  Temporaries temps(graph_, is_instance_call ? 1 : 0);
+  Temporaries temps(graph_);
   if (is_instance_call) {
     HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
     HNullCheck* null_check = new (arena_) HNullCheck(arg, dex_offset);
@@ -466,7 +470,7 @@
   HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot);
   current_block_->AddInstruction(new (arena_) HNullCheck(object, dex_offset));
   if (is_put) {
-    Temporaries temps(graph_, 1);
+    Temporaries temps(graph_);
     HInstruction* null_check = current_block_->GetLastInstruction();
     // We need one temporary for the null check.
     temps.Add(null_check);
@@ -531,7 +535,7 @@
 
   if (is_put) {
     // We need to keep the class alive before loading the value.
-    Temporaries temps(graph_, 1);
+    Temporaries temps(graph_);
     temps.Add(cls);
     HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
     DCHECK_EQ(value->GetType(), field_type);
@@ -556,7 +560,7 @@
   HInstruction* second = second_is_lit ? GetIntConstant(second_reg) : LoadLocal(second_reg, type);
   if (!second->IsIntConstant() || (second->AsIntConstant()->GetValue() == 0)) {
     second = new (arena_) HDivZeroCheck(second, dex_offset);
-    Temporaries temps(graph_, 1);
+    Temporaries temps(graph_);
     current_block_->AddInstruction(second);
     temps.Add(current_block_->GetLastInstruction());
   }
@@ -574,7 +578,7 @@
   uint8_t index_reg = instruction.VRegC_23x();
 
   // We need one temporary for the null check, one for the index, and one for the length.
-  Temporaries temps(graph_, 3);
+  Temporaries temps(graph_);
 
   HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot);
   object = new (arena_) HNullCheck(object, dex_offset);
@@ -618,7 +622,7 @@
   bool is_reference_array = (primitive == 'L') || (primitive == '[');
   Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt;
 
-  Temporaries temps(graph_, 1);
+  Temporaries temps(graph_);
   temps.Add(object);
   for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
     HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type);
@@ -644,7 +648,7 @@
 }
 
 void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_offset) {
-  Temporaries temps(graph_, 1);
+  Temporaries temps(graph_);
   HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot);
   HNullCheck* null_check = new (arena_) HNullCheck(array, dex_offset);
   current_block_->AddInstruction(null_check);
@@ -727,7 +731,7 @@
   HLoadClass* cls = new (arena_) HLoadClass(type_index, is_referrers_class, dex_offset);
   current_block_->AddInstruction(cls);
   // The class needs a temporary before being used by the type check.
-  Temporaries temps(graph_, 1);
+  Temporaries temps(graph_);
   temps.Add(cls);
   if (instruction.Opcode() == Instruction::INSTANCE_OF) {
     current_block_->AddInstruction(
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 9d17263..abbb524 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -51,7 +51,7 @@
     MarkNotLeaf();
   }
   ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
-                     + GetGraph()->GetNumberOfTemporaries()
+                     + GetGraph()->GetTemporariesVRegSlots()
                      + 1 /* filler */,
                    0, /* the baseline compiler does not have live registers at slow path */
                    GetGraph()->GetMaximumNumberOfOutVRegs()
@@ -150,12 +150,15 @@
 
 Location CodeGenerator::GetTemporaryLocation(HTemporary* temp) const {
   uint16_t number_of_locals = GetGraph()->GetNumberOfLocalVRegs();
+  // The type of the previous instruction tells us if we need a single or double stack slot.
+  Primitive::Type type = temp->GetType();
+  int32_t temp_size = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble) ? 2 : 1;
   // Use the temporary region (right below the dex registers).
   int32_t slot = GetFrameSize() - FrameEntrySpillSize()
                                 - kVRegSize  // filler
                                 - (number_of_locals * kVRegSize)
-                                - ((1 + temp->GetIndex()) * kVRegSize);
-  return Location::StackSlot(slot);
+                                - ((temp_size + temp->GetIndex()) * kVRegSize);
+  return temp_size == 2 ? Location::DoubleStackSlot(slot) : Location::StackSlot(slot);
 }
 
 int32_t CodeGenerator::GetStackSlot(HLocal* local) const {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 56546c2..87955eb 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -824,7 +824,12 @@
     }
   } else if (instruction->IsTemporary()) {
     Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
-    Move32(location, temp_location);
+    if (temp_location.IsStackSlot()) {
+      Move32(location, temp_location);
+    } else {
+      DCHECK(temp_location.IsDoubleStackSlot());
+      Move64(location, temp_location);
+    }
   } else {
     DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
     switch (instruction->GetType()) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index d470345..d7ce387 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -686,7 +686,12 @@
     }
   } else if (instruction->IsTemporary()) {
     Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
-    Move32(location, temp_location);
+    if (temp_location.IsStackSlot()) {
+      Move32(location, temp_location);
+    } else {
+      DCHECK(temp_location.IsDoubleStackSlot());
+      Move64(location, temp_location);
+    }
   } else if (instruction->IsLoadLocal()) {
     int slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
     switch (instruction->GetType()) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6224a11..59fac52 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -90,7 +90,7 @@
         maximum_number_of_out_vregs_(0),
         number_of_vregs_(0),
         number_of_in_vregs_(0),
-        number_of_temporaries_(0),
+        temporaries_vreg_slots_(0),
         current_instruction_id_(0) {}
 
   ArenaAllocator* GetArena() const { return arena_; }
@@ -129,12 +129,12 @@
     maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_);
   }
 
-  void UpdateNumberOfTemporaries(size_t count) {
-    number_of_temporaries_ = std::max(count, number_of_temporaries_);
+  void UpdateTemporariesVRegSlots(size_t slots) {
+    temporaries_vreg_slots_ = std::max(slots, temporaries_vreg_slots_);
   }
 
-  size_t GetNumberOfTemporaries() const {
-    return number_of_temporaries_;
+  size_t GetTemporariesVRegSlots() const {
+    return temporaries_vreg_slots_;
   }
 
   void SetNumberOfVRegs(uint16_t number_of_vregs) {
@@ -192,8 +192,8 @@
   // The number of virtual registers used by parameters of this method.
   uint16_t number_of_in_vregs_;
 
-  // The number of temporaries that will be needed for the baseline compiler.
-  size_t number_of_temporaries_;
+  // Number of vreg size slots that the temporaries use (used in baseline compiler).
+  size_t temporaries_vreg_slots_;
 
   // The current id to assign to a newly added instruction. See HInstruction.id_.
   int current_instruction_id_;
@@ -2158,8 +2158,8 @@
  * Some DEX instructions are folded into multiple HInstructions that need
  * to stay live until the last HInstruction. This class
  * is used as a marker for the baseline compiler to ensure its preceding
- * HInstruction stays live. `index` is the temporary number that is used
- * for knowing the stack offset where to store the instruction.
+ * HInstruction stays live. `index` represents the stack location index of the
+ * instruction (the actual offset is computed as index * vreg_size).
  */
 class HTemporary : public HTemplateInstruction<0> {
  public: