Constructor barrier support in DEX-to-DEX compiler.

Bug: 9307738

Some constructors require a barrier before returning. This CL introduces the
RETURN-VOID-BARRIER instruction. The DEX-to-DEX compiler replaces all
RETURN-VOID instructions where a barrier is required by this instruction.
The interpreter and the verifier are updated to support this new instruction.

Change-Id: If31979b4027bc12157b933eda9fcbd5270edd202
diff --git a/src/compiler/dex/dex_to_dex_compiler.cc b/src/compiler/dex/dex_to_dex_compiler.cc
index afb29f4..938de7a 100644
--- a/src/compiler/dex/dex_to_dex_compiler.cc
+++ b/src/compiler/dex/dex_to_dex_compiler.cc
@@ -55,8 +55,25 @@
     return *const_cast<DexFile*>(unit_.GetDexFile());
   }
 
+  // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
+  // a barrier is required.
+  void CompileReturnVoid(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
+  // at runtime.
+  // Since the field index is encoded with 16 bits, we can replace it only if the
+  // field offset can be encoded with 16 bits too.
   void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc,
                                   Instruction::Code new_opcode, bool is_put);
+
+  // Compiles a virtual method invocation into a quick virtual method invocation.
+  // The method index is replaced by the vtable index where the corresponding
+  // AbstractMethod can be found. Therefore, this does not involve any resolution
+  // at runtime.
+  // Since the method index is encoded with 16 bits, we can replace it only if the
+  // vtable index can be encoded with 16 bits too.
   void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
                             Instruction::Code new_opcode, bool is_range);
 
@@ -124,9 +141,14 @@
   for (uint32_t dex_pc = 0; dex_pc < insns_size;
        inst = const_cast<Instruction*>(inst->Next()), dex_pc = inst->GetDexPc(insns)) {
     switch (inst->Opcode()) {
+      case Instruction::RETURN_VOID:
+        CompileReturnVoid(inst, dex_pc);
+        break;
+
       case Instruction::IGET:
         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
         break;
+
       case Instruction::IGET_WIDE:
         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
         break;
@@ -162,12 +184,34 @@
         break;
 
       default:
-        // No optimization.
+        // Nothing to do.
         break;
     }
   }
 }
 
+void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
+  DCHECK(inst->Opcode() == Instruction::RETURN_VOID);
+  // Are we compiling a constructor ?
+  if ((unit_.GetAccessFlags() & kAccConstructor) == 0) {
+    return;
+  }
+  // Do we need a constructor barrier ?
+  if (!driver_.RequiresConstructorBarrier(Thread::Current(), unit_.GetDexFile(),
+                                         unit_.GetClassDefIndex())) {
+    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);
+  }
+  ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 2u);
+  inst->SetOpcode(Instruction::RETURN_VOID_BARRIER);
+}
+
 void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
                                              uint32_t dex_pc,
                                              Instruction::Code new_opcode,
diff --git a/src/dex_instruction_list.h b/src/dex_instruction_list.h
index 9daec61..8257c78 100644
--- a/src/dex_instruction_list.h
+++ b/src/dex_instruction_list.h
@@ -130,7 +130,7 @@
   V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \
   V(0x71, INVOKE_STATIC, "invoke-static", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \
   V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \
-  V(0x73, UNUSED_73, "unused-73", k10x, false, kUnknown, 0, kVerifyError) \
+  V(0x73, RETURN_VOID_BARRIER, "return-void-barrier", k10x, false, kNone, kReturn, kVerifyNone) \
   V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \
   V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \
   V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index 1e8ee9c..21725de 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -1136,6 +1136,17 @@
         }
         return result;
       }
+      case Instruction::RETURN_VOID_BARRIER: {
+        PREAMBLE();
+        ANDROID_MEMBAR_STORE();
+        JValue result;
+        if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+          instrumentation->MethodExitEvent(self, this_object_ref.get(),
+                                           shadow_frame.GetMethod(), inst->GetDexPc(insns),
+                                           result);
+        }
+        return result;
+      }
       case Instruction::RETURN: {
         PREAMBLE();
         JValue result;
@@ -2932,7 +2943,6 @@
         break;
       case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
       case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
-      case Instruction::UNUSED_73:
       case Instruction::UNUSED_79:
       case Instruction::UNUSED_7A:
         UnexpectedOpcode(inst, mh);
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 87cc328..32ebcf8 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -2412,7 +2412,12 @@
       break;
 
     // Special instructions.
-    //
+    case Instruction::RETURN_VOID_BARRIER:
+      DCHECK(Runtime::Current()->IsStarted());
+      if (!IsConstructor()) {
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void-barrier not expected";
+      }
+      break;
     // Note: the following instructions encode offsets derived from class linking.
     // As such they use Class*/Field*/AbstractMethod* as these offsets only have
     // meaning if the class linking and resolution were successful.
@@ -2458,7 +2463,6 @@
     case Instruction::UNUSED_41:
     case Instruction::UNUSED_42:
     case Instruction::UNUSED_43:
-    case Instruction::UNUSED_73:
     case Instruction::UNUSED_79:
     case Instruction::UNUSED_7A:
     case Instruction::UNUSED_EB: