Timely color fix

See b/10690000

For efficiency, the Quick compiler will not flush incoming register
arguments to the frame if their underlying Dalvik virtual registers
have been promoted to physical registers.  In this case, though,
there was a bug on Arm devices in that an incoming Double was promoted
to physical floating point registers, but not in a usable form.  The
entry code generation saw that both halves of the double were promoted,
but failed to check if it was in a form usable as a double.

In this particular case, it meant that subsequent uses of the incoming
argument referred to the uninitialized home location in the frame,
resulting in garbage color values.

That's the bug.  Another problem is that the incoming double should
never have been promoted to an unusable state in the first place - but
that's merely an efficiency issue and will be addressed in another CL.

Note: no good way to generate a regression test for this issue.  The
bug triggered because of an unusual sequence of events driving register
promotion that can't easily (or robustly) be triggered from Java source.

Change-Id: I7242422277193a04376461134dde71e9dec55576
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index fa60818..72ae91e 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -283,11 +283,28 @@
         need_flush = true;
       }
 
-      // For wide args, force flush if only half is promoted
+      // For wide args, force flush if not fully promoted
       if (t_loc->wide) {
         PromotionMap* p_map = v_map + (t_loc->high_word ? -1 : +1);
+        // Is only half promoted?
         need_flush |= (p_map->core_location != v_map->core_location) ||
             (p_map->fp_location != v_map->fp_location);
+        if ((cu_->instruction_set == kThumb2) && t_loc->fp && !need_flush) {
+          /*
+           * In Arm, a double is represented as a pair of consecutive single float
+           * registers starting at an even number.  It's possible that both Dalvik vRegs
+           * representing the incoming double were independently promoted as singles - but
+           * not in a form usable as a double.  If so, we need to flush - even though the
+           * incoming arg appears fully in register.  At this point in the code, both
+           * halves of the double are promoted.  Make sure they are in a usable form.
+           */
+          int lowreg_index = start_vreg + i + (t_loc->high_word ? -1 : 0);
+          int low_reg = promotion_map_[lowreg_index].FpReg;
+          int high_reg = promotion_map_[lowreg_index + 1].FpReg;
+          if (((low_reg & 0x1) != 0) || (high_reg != (low_reg + 1))) {
+            need_flush = true;
+          }
+        }
       }
       if (need_flush) {
         StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i),