Handle a special case of lock aliasing during lock verification

Check whether the two instructions immediately before a monitor-enter
are const-class, establishing previously untracked lock aliasing
in a low-overhead manner.

Test: m test-art-host
Change-Id: I94c187cbc422f8f0c13b688b09a5d9579a735b56
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5961748..01fcbd1 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2255,6 +2255,45 @@
             }
             break;
 
+          // Catch a case of register aliasing when two registers are linked to the same
+          // java.lang.Class object via two consequent const-class instructions immediately
+          // preceding monitor-enter called on one of those registers.
+          case Instruction::CONST_CLASS: {
+            // Get the second previous instruction.
+            if (prev_idx == 0 || GetInstructionFlags(prev_idx).IsBranchTarget()) {
+              break;
+            }
+            prev_idx--;
+            while (0 != prev_idx && !GetInstructionFlags(prev_idx).IsOpcode()) {
+              prev_idx--;
+            }
+            const Instruction& prev2_inst = code_item_accessor_.InstructionAt(prev_idx);
+
+            // Match the pattern "const-class; const-class; monitor-enter;"
+            if (prev2_inst.Opcode() != Instruction::CONST_CLASS) {
+              break;
+            }
+
+            // Ensure both const-classes are called for the same type_idx.
+            if (prev_inst.VRegB_21c() != prev2_inst.VRegB_21c()) {
+              break;
+            }
+
+            // Update the lock status for the aliased register.
+            if (prev_inst.VRegA() == inst->VRegA_11x()) {
+              work_line_->CopyRegister1(this,
+                                        prev2_inst.VRegA(),
+                                        inst->VRegA_11x(),
+                                        kTypeCategoryRef);
+            } else if (prev2_inst.VRegA() == inst->VRegA_11x()) {
+              work_line_->CopyRegister1(this,
+                                        prev_inst.VRegA(),
+                                        inst->VRegA_11x(),
+                                        kTypeCategoryRef);
+            }
+            break;
+          }
+
           default:  // Other instruction types ignored.
             break;
         }
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index b8324e5..f3c3f03 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -1,3 +1,4 @@
+JNI_OnLoad called
 PackedSwitch
 PackedSwitch key INT_MAX
 PackedSwitch key overflow
@@ -71,4 +72,5 @@
 b/29778499 (2)
 b/30458218
 b/31313170
+ConstClassAliasing
 Done!
diff --git a/test/800-smali/smali/ConstClassAliasing.smali b/test/800-smali/smali/ConstClassAliasing.smali
new file mode 100644
index 0000000..a65d9a7
--- /dev/null
+++ b/test/800-smali/smali/ConstClassAliasing.smali
@@ -0,0 +1,12 @@
+.class public LConstClassAliasing;
+
+.super Ljava/lang/Object;
+
+.method public static run()V
+   .registers 2
+   const-class v0, Ljava/lang/Object;
+   const-class v1, Ljava/lang/Object;
+   monitor-enter v0
+   monitor-exit v1
+   return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 8d39f09..75a0264 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -27,13 +27,21 @@
 
     private static class TestCase {
         public TestCase(String testName, String testClass, String testMethodName, Object[] values,
-                        Throwable expectedException, Object expectedReturn) {
+                        Throwable expectedException, Object expectedReturn,
+                        boolean checkCompiled) {
             this.testName = testName;
             this.testClass = testClass;
             this.testMethodName = testMethodName;
             this.values = values;
             this.expectedException = expectedException;
             this.expectedReturn = expectedReturn;
+            this.checkCompiled = checkCompiled;
+        }
+
+        public TestCase(String testName, String testClass, String testMethodName, Object[] values,
+                        Throwable expectedException, Object expectedReturn) {
+            this(testName, testClass, testMethodName, values, expectedException,
+                 expectedReturn, false);
         }
 
         String testName;
@@ -42,6 +50,7 @@
         Object[] values;
         Throwable expectedException;
         Object expectedReturn;
+        boolean checkCompiled;
     }
 
     private List<TestCase> testCases;
@@ -182,6 +191,8 @@
                 new IncompatibleClassChangeError(), null));
         testCases.add(new TestCase("b/30458218", "B30458218", "run", null, null, null));
         testCases.add(new TestCase("b/31313170", "B31313170", "run", null, null, 0));
+        testCases.add(new TestCase("ConstClassAliasing", "ConstClassAliasing", "run", null, null,
+                null, true));
     }
 
     public void runTests() {
@@ -235,6 +246,11 @@
                     errorReturn = new IllegalStateException("Expected return " +
                                                             tc.expectedReturn +
                                                             ", but got " + retValue);
+                } else if (tc.checkCompiled && compiledWithOptimizing() &&
+                           !isAotCompiled(c, method.getName())) {
+                    errorReturn = new IllegalStateException("Expected method " + method.getName() +
+                                                            " of class " + c.getName() +
+                                                            " be compiled in test " + tc.testName);
                 } else {
                     // Expected result, do nothing.
                 }
@@ -260,10 +276,15 @@
     }
 
     public static void main(String[] args) throws Exception {
+        System.loadLibrary(args[0]);
+
         Main main = new Main();
 
         main.runTests();
 
         System.out.println("Done!");
     }
+
+    private native static boolean isAotCompiled(Class<?> cls, String methodName);
+    private native static boolean compiledWithOptimizing();
 }
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index f89888b..8dabad9 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -132,9 +132,13 @@
     constexpr const char* kInterpretOnly = "interpret-only";
     constexpr const char* kVerifyNone = "verify-none";
     constexpr const char* kVerifyAtRuntime = "verify-at-runtime";
+    constexpr const char* kQuicken = "quicken";
+    constexpr const char* kExtract = "extract";
     if (strncmp(filter, kInterpretOnly, strlen(kInterpretOnly)) == 0 ||
         strncmp(filter, kVerifyNone, strlen(kVerifyNone)) == 0 ||
-        strncmp(filter, kVerifyAtRuntime, strlen(kVerifyAtRuntime)) == 0) {
+        strncmp(filter, kVerifyAtRuntime, strlen(kVerifyAtRuntime)) == 0 ||
+        strncmp(filter, kExtract, strlen(kExtract)) == 0 ||
+        strncmp(filter, kQuicken, strlen(kQuicken)) == 0) {
       return JNI_FALSE;
     }
   }