Merge "Fix verifier deps determinism"
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index fc7cd01..f4e8a89 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -770,18 +770,18 @@
       *result_ = true;
     }
 
-    // Record the object visited in case of circular reference.
-    visited_->emplace(ref);
     if (ref->IsClass()) {
       *result_ = *result_ ||
           image_writer_->PruneAppImageClassInternal(ref->AsClass(), early_exit_, visited_);
     } else {
+      // Record the object visited in case of circular reference.
+      visited_->emplace(ref);
       *result_ = *result_ ||
           image_writer_->PruneAppImageClassInternal(klass, early_exit_, visited_);
       ref->VisitReferences(*this, *this);
+      // Clean up before exit for next call of this function.
+      visited_->erase(ref);
     }
-    // Clean up before exit for next call of this function.
-    visited_->erase(ref);
   }
 
   ALWAYS_INLINE void operator() (ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 274f065..0ef7dcd 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -40,6 +40,8 @@
   instruction->RemoveAsUserOfAllInputs();
   instruction->RemoveEnvironmentUsers();
   instruction->GetBlock()->RemoveInstructionOrPhi(instruction, /*ensure_safety=*/ false);
+  RemoveEnvironmentUses(instruction);
+  ResetEnvironmentInputRecords(instruction);
 }
 
 // Detect a goto block and sets succ to the single successor.
@@ -267,6 +269,21 @@
   return instruction;
 }
 
+// Check that instructions from the induction sets are fully removed: have no uses
+// and no other instructions use them.
+static bool CheckInductionSetFullyRemoved(ArenaSet<HInstruction*>* iset) {
+  for (HInstruction* instr : *iset) {
+    if (instr->GetBlock() != nullptr ||
+        !instr->GetUses().empty() ||
+        !instr->GetEnvUses().empty() ||
+        HasEnvironmentUsedByOthers(instr)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 //
 // Class methods.
 //
@@ -448,6 +465,9 @@
         for (HInstruction* i : *iset_) {
           RemoveFromCycle(i);
         }
+
+        // Check that there are no records of the deleted instructions.
+        DCHECK(CheckInductionSetFullyRemoved(iset_));
         simplified_ = true;
       }
     }
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ca48e08..3a1864b 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -90,7 +90,8 @@
   }
 }
 
-static void RemoveEnvironmentUses(HInstruction* instruction) {
+// Remove the environment use records of the instruction for users.
+void RemoveEnvironmentUses(HInstruction* instruction) {
   for (HEnvironment* environment = instruction->GetEnvironment();
        environment != nullptr;
        environment = environment->GetParent()) {
@@ -102,6 +103,35 @@
   }
 }
 
+// Return whether the instruction has an environment and it's used by others.
+bool HasEnvironmentUsedByOthers(HInstruction* instruction) {
+  for (HEnvironment* environment = instruction->GetEnvironment();
+       environment != nullptr;
+       environment = environment->GetParent()) {
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      HInstruction* user = environment->GetInstructionAt(i);
+      if (user != nullptr) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// Reset environment records of the instruction itself.
+void ResetEnvironmentInputRecords(HInstruction* instruction) {
+  for (HEnvironment* environment = instruction->GetEnvironment();
+       environment != nullptr;
+       environment = environment->GetParent()) {
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      DCHECK(environment->GetHolder() == instruction);
+      if (environment->GetInstructionAt(i) != nullptr) {
+        environment->SetRawEnvAt(i, nullptr);
+      }
+    }
+  }
+}
+
 static void RemoveAsUser(HInstruction* instruction) {
   instruction->RemoveAsUserOfAllInputs();
   RemoveEnvironmentUses(instruction);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index fa29378..e443142 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -7059,6 +7059,10 @@
   return instruction;
 }
 
+void RemoveEnvironmentUses(HInstruction* instruction);
+bool HasEnvironmentUsedByOthers(HInstruction* instruction);
+void ResetEnvironmentInputRecords(HInstruction* instruction);
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
index 871604b..b1bc3f8 100644
--- a/runtime/aot_class_linker.cc
+++ b/runtime/aot_class_linker.cc
@@ -31,18 +31,29 @@
 bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
                                   bool can_init_statics, bool can_init_parents) {
   Runtime* const runtime = Runtime::Current();
+  bool strict_mode_ = runtime->IsActiveStrictTransactionMode();
 
   DCHECK(klass != nullptr);
   if (klass->IsInitialized() || klass->IsInitializing()) {
     return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
   }
 
-  if (runtime->IsActiveStrictTransactionMode()) {
+  // Don't initialize klass if it's superclass is not initialized, because superclass might abort
+  // the transaction and rolled back after klass's change is commited.
+  if (strict_mode_ && !klass->IsInterface() && klass->HasSuperClass()) {
+    if (klass->GetSuperClass()->GetStatus() == mirror::Class::kStatusInitializing) {
+      runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve "
+          + klass->PrettyTypeOf() + " because it's superclass is not initialized.");
+      return false;
+    }
+  }
+
+  if (strict_mode_) {
     runtime->EnterTransactionMode(true, klass.Get()->AsClass());
   }
   bool success = ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
 
-  if (runtime->IsActiveStrictTransactionMode()) {
+  if (strict_mode_) {
     if (success) {
       // Exit Transaction if success.
       runtime->ExitTransactionMode();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 775651b..3ac87c5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4900,7 +4900,9 @@
       if (!super_initialized) {
         // The super class was verified ahead of entering initializing, we should only be here if
         // the super class became erroneous due to initialization.
-        CHECK(handle_scope_super->IsErroneous() && self->IsExceptionPending())
+        // For the case of aot compiler, the super class might also be initializing but we don't
+        // want to process circular dependencies in pre-compile.
+        CHECK(self->IsExceptionPending())
             << "Super class initialization failed for "
             << handle_scope_super->PrettyDescriptor()
             << " that has unexpected status " << handle_scope_super->GetStatus()
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index aca997e..fc7bcb2 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -426,6 +426,21 @@
     }
   }
 
+  // Environment of an instruction, removed during SimplifyInduction, should be adjusted.
+  //
+  /// CHECK-START: void Main.inductionMax(int[]) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.inductionMax(int[]) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static void inductionMax(int[] a) {
+   int s = 0;
+    for (int i = 0; i < 10; i++) {
+      s = Math.max(s, 5);
+    }
+  }
+
   public static void main(String[] args) {
     expectEquals(10, earlyExitFirst(-1));
     for (int i = 0; i <= 10; i++) {
@@ -539,6 +554,8 @@
       expectEquals((byte)(i + 1), b1[i]);
     }
 
+    inductionMax(yy);
+
     System.out.println("passed");
   }
 
diff --git a/test/660-clinit/profile b/test/660-clinit/profile
new file mode 100644
index 0000000..0239f22
--- /dev/null
+++ b/test/660-clinit/profile
@@ -0,0 +1,10 @@
+LMain;
+LClInit;
+LDay;
+LA;
+LB;
+LC;
+LG;
+LGs;
+LObjectRef;
+
diff --git a/test/660-clinit/run b/test/660-clinit/run
new file mode 100644
index 0000000..d24ef42
--- /dev/null
+++ b/test/660-clinit/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} $@ --profile
diff --git a/test/660-clinit/src/Main.java b/test/660-clinit/src/Main.java
index cf2ffe7..f9b068e 100644
--- a/test/660-clinit/src/Main.java
+++ b/test/660-clinit/src/Main.java
@@ -30,6 +30,13 @@
     expectNotPreInit(A.class); // should pass
     expectNotPreInit(B.class); // should fail
     expectNotPreInit(C.class); // should fail
+    expectNotPreInit(G.class); // should fail
+    expectNotPreInit(Gs.class); // should fail
+    expectNotPreInit(Gss.class); // should fail
+
+    expectNotPreInit(Add.class);
+    expectNotPreInit(Mul.class);
+    expectNotPreInit(ObjectRef.class);
 
     A x = new A();
     System.out.println("A.a: " + A.a);
@@ -146,3 +153,38 @@
     c = A.a; // read other's static field, fail
   }
 }
+
+class G {
+  static G g;
+  static int i;
+  static {
+    g = new Gss(); // fail because recursive dependency
+    i = A.a;  // read other's static field, fail
+  }
+}
+
+// Gs will be successfully initialized as G's status is initializing at that point, which will
+// later aborted but Gs' transaction is already committed.
+// Instantiation of Gs will fail because we try to invoke G's <init>
+// but G's status will be StatusVerified. INVOKE_DIRECT will not initialize class.
+class Gs extends G {}  // fail because super class can't be initialized
+class Gss extends Gs {}
+
+// pruned because holding reference to non-image class
+class ObjectRef {
+  static Class<?> klazz[] = new Class<?>[]{Add.class, Mul.class};
+}
+
+// non-image
+class Add {
+  static int exec(int a, int b) {
+    return a + b;
+  }
+}
+
+// non-image
+class Mul {
+  static int exec(int a, int b) {
+    return a * b;
+  }
+}
diff --git a/test/run-test b/test/run-test
index bd8b9e8..e6196a0 100755
--- a/test/run-test
+++ b/test/run-test
@@ -45,7 +45,7 @@
 export RUN="${progdir}/etc/run-test-jar"
 export DEX_LOCATION=/data/run-test/${test_dir}
 export NEED_DEX="true"
-export USE_JACK="true"
+export USE_JACK="false"
 export USE_DESUGAR="true"
 export SMALI_ARGS=""
 
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index b996b04..d45d009 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -33,7 +33,8 @@
                         'TARGET_ARCH',
                         'HOST_PREFER_32_BIT',
                         'HOST_OUT_EXECUTABLES',
-                        'ANDROID_JAVA_TOOLCHAIN']
+                        'ANDROID_JAVA_TOOLCHAIN',
+                        'ANDROID_COMPILE_WITH_JACK']
 _DUMP_MANY_VARS = None  # To be set to a dictionary with above list being the keys,
                         # and the build variable being the value.
 def _dump_many_vars(var_name):
@@ -78,6 +79,15 @@
 def _get_build_var(var_name):
   return _dump_many_vars(var_name)
 
+def _get_build_var_boolean(var, default):
+  val = _get_build_var(var)
+  if val:
+    if val == "True" or val == "true":
+      return True
+    if val == "False" or val == "false":
+      return False
+  return default
+
 def get_env(key):
   return _env.get(key)
 
@@ -97,7 +107,7 @@
 ANDROID_BUILD_TOP = _get_android_build_top()
 
 # Compiling with jack? Possible values in (True, False, 'default')
-ANDROID_COMPILE_WITH_JACK = _getEnvBoolean('ANDROID_COMPILE_WITH_JACK', 'default')
+ANDROID_COMPILE_WITH_JACK = _get_build_var_boolean('ANDROID_COMPILE_WITH_JACK', 'default')
 
 # Directory used for temporary test files on the host.
 ART_HOST_TEST_DIR = tempfile.mkdtemp(prefix = 'test-art-')
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 75694c3..4f99ac3 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -19,6 +19,8 @@
   exit 1
 fi
 
+source build/envsetup.sh >&/dev/null # for get_build_var
+
 # Logic for setting out_dir from build/make/core/envsetup.mk:
 if [[ -z $OUT_DIR ]]; then
   if [[ -z $OUT_DIR_COMMON_BASE ]]; then
@@ -30,10 +32,7 @@
   out_dir=${OUT_DIR}
 fi
 
-using_jack=true
-if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
-  using_jack=false
-fi
+using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK)
 
 java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES
 common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target"
@@ -63,7 +62,7 @@
   fi
 done
 
-if $using_jack; then
+if [[ $using_jack == "true" ]]; then
   common_targets="$common_targets ${out_dir}/host/linux-x86/bin/jack"
 fi
 
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 17c84b4..d2322bb 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -19,24 +19,19 @@
   exit 1
 fi
 
-if [ -z "$ANDROID_JAVA_TOOLCHAIN" ] ; then
-  source build/envsetup.sh
-  setpaths # include platform prebuilt java, javac, etc in $PATH.
-fi
+source build/envsetup.sh >&/dev/null # for get_build_var, setpaths
+setpaths # include platform prebuilt java, javac, etc in $PATH.
 
 if [ -z "$ANDROID_HOST_OUT" ] ; then
   ANDROID_HOST_OUT=${OUT_DIR-$ANDROID_BUILD_TOP/out}/host/linux-x86
 fi
 
-using_jack=true
-if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
-  using_jack=false
-fi
+using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK)
 
 function jlib_suffix {
   local str=$1
   local suffix="jar"
-  if $using_jack; then
+  if [[ $using_jack == "true" ]]; then
     suffix="jack"
   fi
   echo "$str.$suffix"
@@ -166,7 +161,7 @@
   art_debugee="$art_debugee -verbose:jdwp"
 fi
 
-if $using_jack; then
+if [[ $using_jack == "true" ]]; then
   toolchain_args="--toolchain jack --language JN --jack-arg -g"
 else
   toolchain_args="--toolchain jdk --language CUR"
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index d549098..eecdd2f 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -19,10 +19,8 @@
   exit 1
 fi
 
-if [ -z "$ANDROID_JAVA_TOOLCHAIN" ] ; then
-  source build/envsetup.sh
-  setpaths # include platform prebuilt java, javac, etc in $PATH.
-fi
+source build/envsetup.sh >&/dev/null # for get_build_var, setpaths
+setpaths # include platform prebuilt java, javac, etc in $PATH.
 
 if [ -z "$ANDROID_PRODUCT_OUT" ] ; then
   JAVA_LIBRARIES=out/target/common/obj/JAVA_LIBRARIES
@@ -30,16 +28,13 @@
   JAVA_LIBRARIES=${ANDROID_PRODUCT_OUT}/../../common/obj/JAVA_LIBRARIES
 fi
 
-using_jack=true
-if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
-  using_jack=false
-fi
+using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK)
 
 function classes_jar_path {
   local var="$1"
   local suffix="jar"
 
-  if $using_jack; then
+  if [[ $using_jack == "true" ]]; then
     suffix="jack"
   fi
 
@@ -151,7 +146,7 @@
 vogar_args="$vogar_args --timeout 480"
 
 # Switch between using jack or javac+desugar+dx
-if $using_jack; then
+if [[ $using_jack == "true" ]]; then
   vogar_args="$vogar_args --toolchain jack --language JO"
 else
   vogar_args="$vogar_args --toolchain jdk --language CUR"