Support check-cast elision in DEX-to-DEX compiler.

Bug: 9648428

Replaces safe check-cast by 2 consecutive nop instructions.

Change-Id: I2cd99c629a6a00a6e0effc853c3439bc92683d6d
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index ee68a5d..4419fed 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -30,8 +30,8 @@
 
 // Controls quickening activation.
 const bool kEnableQuickening = true;
-// Controls logging.
-const bool kEnableLogging = false;
+// Control check-cast elision.
+const bool kEnableCheckCastEllision = true;
 
 class DexCompiler {
  public:
@@ -59,6 +59,11 @@
   // a barrier is required.
   void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
 
+  // Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In
+  // this case, returns the second NOP instruction pointer. Otherwise, returns
+  // the given "inst".
+  Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc);
+
   // Compiles a field access into a quick field access.
   // The field index is replaced by an offset within an Object where we can read
   // from / write to this field. Therefore, this does not involve any resolution
@@ -145,6 +150,10 @@
         CompileReturnVoid(inst, dex_pc);
         break;
 
+      case Instruction::CHECK_CAST:
+        inst = CompileCheckCast(inst, dex_pc);
+        break;
+
       case Instruction::IGET:
         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
         break;
@@ -202,16 +211,44 @@
     return;
   }
   // Replace RETURN_VOID by RETURN_VOID_BARRIER.
-  if (kEnableLogging) {
-    LOG(INFO) << "Replacing " << Instruction::Name(inst->Opcode())
-    << " by " << Instruction::Name(Instruction::RETURN_VOID_BARRIER)
-    << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
-    << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
-  }
+  VLOG(compiler) << "Replacing " << Instruction::Name(inst->Opcode())
+                 << " by " << Instruction::Name(Instruction::RETURN_VOID_BARRIER)
+                 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+                 << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
   ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 2u);
   inst->SetOpcode(Instruction::RETURN_VOID_BARRIER);
 }
 
+Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
+  if (!kEnableCheckCastEllision) {
+    return inst;
+  }
+  MethodReference referrer(&GetDexFile(), unit_.GetDexMethodIndex());
+  if (!driver_.IsSafeCast(referrer, dex_pc)) {
+    return inst;
+  }
+  // Ok, this is a safe cast. Since the "check-cast" instruction size is 2 code
+  // units and a "nop" instruction size is 1 code unit, we need to replace it by
+  // 2 consecutive NOP instructions.
+  // Because the caller loops over instructions by calling Instruction::Next onto
+  // the current instruction, we need to return the 2nd NOP instruction. Indeed,
+  // its next instruction is the former check-cast's next instruction.
+  VLOG(compiler) << "Removing " << Instruction::Name(inst->Opcode())
+                 << " by replacing it with 2 NOPs at dex pc "
+                 << StringPrintf("0x%x", dex_pc) << " in method "
+                 << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+  // We are modifying 4 consecutive bytes.
+  ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
+  inst->SetOpcode(Instruction::NOP);
+  inst->SetVRegA_10x(0u); // keep compliant with verifier.
+  // Get to next instruction which is the second half of check-cast and replace
+  // it by a NOP.
+  inst = const_cast<Instruction*>(inst->Next());
+  inst->SetOpcode(Instruction::NOP);
+  inst->SetVRegA_10x(0u); // keep compliant with verifier.
+  return inst;
+}
+
 void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
                                              uint32_t dex_pc,
                                              Instruction::Code new_opcode,
@@ -225,15 +262,12 @@
   bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset,
                                                     is_volatile, is_put);
   if (fast_path && !is_volatile && IsUint(16, field_offset)) {
-    // TODO: use VLOG ?
-    if (kEnableLogging) {
-      LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
-                << " to " << Instruction::Name(new_opcode)
-                << " by replacing field index " << field_idx
-                << " by field offset " << field_offset
-                << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
-                << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
-    }
+    VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
+                   << " to " << Instruction::Name(new_opcode)
+                   << " by replacing field index " << field_idx
+                   << " by field offset " << field_offset
+                   << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+                   << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
     // We are modifying 4 consecutive bytes.
     ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
     inst->SetOpcode(new_opcode);
@@ -263,16 +297,13 @@
   // TODO: support devirtualization.
   if (fast_path && original_invoke_type == invoke_type) {
     if (vtable_idx >= 0 && IsUint(16, vtable_idx)) {
-      // TODO: use VLOG ?
-      if (kEnableLogging) {
-        LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
-                  << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
-                  << " to " << Instruction::Name(new_opcode)
-                  << " by replacing method index " << method_idx
-                  << " by vtable index " << vtable_idx
-                  << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
-                  << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
-      }
+      VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
+                     << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
+                     << " to " << Instruction::Name(new_opcode)
+                     << " by replacing method index " << method_idx
+                     << " by vtable index " << vtable_idx
+                     << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+                     << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
       // We are modifying 4 consecutive bytes.
       ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
       inst->SetOpcode(new_opcode);
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 7d078f9..ff81659 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -292,6 +292,12 @@
     insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode);
   }
 
+  void SetVRegA_10x(uint8_t val) {
+    DCHECK(FormatOf(Opcode()) == k10x);
+    uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+    insns[0] = (val << 8) | (insns[0] & 0x00ff);
+  }
+
   void SetVRegB_3rc(uint16_t val) {
     DCHECK(FormatOf(Opcode()) == k3rc);
     uint16_t* insns = reinterpret_cast<uint16_t*>(this);