Merge "Fix non concurrent GC ergonomics." into dalvik-dev
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 86f6ee5..5732dce 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -306,6 +306,9 @@
     default:
       LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set";
   }
+  if (target <= cur_offset) {
+    insn->backwards_branch = true;
+  }
   BasicBlock *taken_block = FindBlock(target, /* split */ true, /* create */ true,
                                       /* immed_pred_block_p */ &cur_block);
   cur_block->taken = taken_block;
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 84ab259..45e425b 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -232,7 +232,8 @@
  */
 struct MIR {
   DecodedInstruction dalvikInsn;
-  unsigned int width;
+  uint16_t width;
+  bool backwards_branch;          // TODO: may be useful to make this an attribute flag word.
   unsigned int offset;
   int m_unit_index;               // From which method was this MIR included
   MIR* prev;
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index a6314f4..82ba6e3 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -153,7 +153,14 @@
   }
   DCHECK((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode)
       || (bb->block_type == kExitBlock));
-  bb = bb->fall_through;
+  if (((bb->taken != NULL) && (bb->fall_through == NULL)) &&
+      ((bb->taken->block_type == kDalvikByteCode) || (bb->taken->block_type == kExitBlock))) {
+    // Follow simple unconditional branches.
+    bb = bb->taken;
+  } else {
+    // Follow simple fallthrough
+    bb = (bb->taken != NULL) ? NULL : bb->fall_through;
+  }
   if (bb == NULL || (Predecessors(bb) != 1)) {
     return NULL;
   }
@@ -303,7 +310,8 @@
         case Instruction::IF_GEZ:
         case Instruction::IF_GTZ:
         case Instruction::IF_LEZ:
-          if (bb->taken->dominates_return) {
+          // If we've got a backwards branch to return, no need to suspend check.
+          if ((bb->taken->dominates_return) && (mir->backwards_branch)) {
             mir->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK;
             if (cu_->verbose) {
               LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex << mir->offset;
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 74eaa66..8f42999 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -239,7 +239,7 @@
     case Instruction::GOTO:
     case Instruction::GOTO_16:
     case Instruction::GOTO_32:
-      if (bb->taken->start_offset <= mir->offset) {
+      if (mir->backwards_branch) {
         GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken->id]);
       } else {
         OpUnconditionalBranch(&label_list[bb->taken->id]);
@@ -273,19 +273,17 @@
     case Instruction::IF_LE: {
       LIR* taken = &label_list[bb->taken->id];
       LIR* fall_through = &label_list[bb->fall_through->id];
-      bool backward_branch;
-      backward_branch = (bb->taken->start_offset <= mir->offset);
       // Result known at compile time?
       if (rl_src[0].is_const && rl_src[1].is_const) {
         bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg),
                                        mir_graph_->ConstantValue(rl_src[1].orig_sreg));
-        if (is_taken && backward_branch) {
+        if (is_taken && mir->backwards_branch) {
           GenSuspendTest(opt_flags);
         }
         int id = is_taken ? bb->taken->id : bb->fall_through->id;
         OpUnconditionalBranch(&label_list[id]);
       } else {
-        if (backward_branch) {
+        if (mir->backwards_branch) {
           GenSuspendTest(opt_flags);
         }
         GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken,
@@ -302,18 +300,16 @@
     case Instruction::IF_LEZ: {
       LIR* taken = &label_list[bb->taken->id];
       LIR* fall_through = &label_list[bb->fall_through->id];
-      bool backward_branch;
-      backward_branch = (bb->taken->start_offset <= mir->offset);
       // Result known at compile time?
       if (rl_src[0].is_const) {
         bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg), 0);
-        if (is_taken && backward_branch) {
+        if (is_taken && mir->backwards_branch) {
           GenSuspendTest(opt_flags);
         }
         int id = is_taken ? bb->taken->id : bb->fall_through->id;
         OpUnconditionalBranch(&label_list[id]);
       } else {
-        if (backward_branch) {
+        if (mir->backwards_branch) {
           GenSuspendTest(opt_flags);
         }
         GenCompareZeroAndBranch(opcode, rl_src[0], taken, fall_through);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a0a5c01..120f232 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1946,14 +1946,14 @@
   "Ljava/util/Scanner;",  // regex.Pattern.compileImpl.
   "Ljava/util/SimpleTimeZone;",  // Sub-class of TimeZone.
   "Ljava/util/TimeZone;",  // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl.
-  "Ljava/util/concurrent/ConcurrentHashMap$Segment;",  // Calls Runtime.getRuntime().availableProcessors().
-  "Ljava/util/concurrent/ConcurrentSkipListMap;",  // Calls OsConstants.initConstants.
-  "Ljava/util/concurrent/Exchanger;",  // Calls OsConstants.initConstants.
-  "Ljava/util/concurrent/ForkJoinPool;",  // Calls OsConstants.initConstants.
-  "Ljava/util/concurrent/LinkedTransferQueue;",  // Calls OsConstants.initConstants.
-  "Ljava/util/concurrent/Phaser;",  // Calls OsConstants.initConstants.
+  "Ljava/util/concurrent/ConcurrentHashMap;",  // Calls Runtime.getRuntime().availableProcessors().
+  "Ljava/util/concurrent/ConcurrentSkipListMap;",  // Calls Random() -> OsConstants.initConstants.
+  "Ljava/util/concurrent/Exchanger;",  // Calls Runtime.getRuntime().availableProcessors().
+  "Ljava/util/concurrent/ForkJoinPool;",  // Makes a thread pool ..-> calls OsConstants.initConstants.
+  "Ljava/util/concurrent/LinkedTransferQueue;",  // Calls Runtime.getRuntime().availableProcessors().
+  "Ljava/util/concurrent/Phaser;",  // Calls Runtime.getRuntime().availableProcessors().
   "Ljava/util/concurrent/ScheduledThreadPoolExecutor;",  // Calls AtomicLong.VMSupportsCS8()
-  "Ljava/util/concurrent/SynchronousQueue;",  // Calls OsConstants.initConstants.
+  "Ljava/util/concurrent/SynchronousQueue;",  // Calls Runtime.getRuntime().availableProcessors().
   "Ljava/util/concurrent/atomic/AtomicLong;",  // Calls AtomicLong.VMSupportsCS8()
   "Ljava/util/logging/LogManager;",  // Calls System.getProperty -> OsConstants.initConstants.
   "Ljava/util/prefs/AbstractPreferences;",  // Calls OsConstants.initConstants.
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 8fc744d..505e368 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -876,7 +876,8 @@
   // TODO: we call this code when dying but may not have suspended the thread ourself. The
   //       IsSuspended check is therefore racy with the use for dumping (normally we inhibit
   //       the race with the thread_suspend_count_lock_).
-  bool dump_for_abort = (gAborting > 0);
+  // No point dumping for an abort in debug builds where we'll hit the not suspended check in stack.
+  bool dump_for_abort = (gAborting > 0) && !kIsDebugBuild;
   if (this == Thread::Current() || IsSuspended() || dump_for_abort) {
     // If we're currently in native code, dump that stack before dumping the managed stack.
     if (dump_for_abort || ShouldShowNativeStack(this)) {
diff --git a/test/109-suspend-check/expected.txt b/test/109-suspend-check/expected.txt
new file mode 100644
index 0000000..07cc825
--- /dev/null
+++ b/test/109-suspend-check/expected.txt
@@ -0,0 +1,7 @@
+Running (5 seconds) ...
+.
+.
+.
+.
+.
+Done.
diff --git a/test/109-suspend-check/info.txt b/test/109-suspend-check/info.txt
new file mode 100644
index 0000000..d89d66a
--- /dev/null
+++ b/test/109-suspend-check/info.txt
@@ -0,0 +1,2 @@
+To support garbage collection, debugging and profiling the VM must be able to send all threads
+to a safepoint.  This tests the ability of the VM to do this for a tight loop.
diff --git a/test/109-suspend-check/src/Main.java b/test/109-suspend-check/src/Main.java
new file mode 100644
index 0000000..d92b9e5
--- /dev/null
+++ b/test/109-suspend-check/src/Main.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+public class Main {
+    private static final int TEST_TIME = 5;
+
+    public static void main(String[] args) {
+        System.out.println("Running (" + TEST_TIME + " seconds) ...");
+        InfiniteForLoop forLoop = new InfiniteForLoop();
+        InfiniteWhileLoop whileLoop = new InfiniteWhileLoop();
+        InfiniteDoWhileLoop doWhileLoop = new InfiniteDoWhileLoop();
+        MakeGarbage garbage = new MakeGarbage();
+        forLoop.start();
+        whileLoop.start();
+        doWhileLoop.start();
+        garbage.start();
+        for (int i = 0; i < TEST_TIME; i++) {
+          System.gc();
+          System.out.println(".");
+          sleep(1000);
+        }
+        forLoop.stopNow();
+        whileLoop.stopNow();
+        doWhileLoop.stopNow();
+        garbage.stopNow();
+        System.out.println("Done.");
+    }
+
+    public static void sleep(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ie) {
+            System.err.println("sleep was interrupted");
+        }
+    }
+}
+
+class InfiniteWhileLoop extends Thread {
+  volatile private boolean keepGoing = true;
+  public void run() {
+    int i = 0;
+    while (keepGoing) {
+      i++;
+    }
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}
+
+class InfiniteDoWhileLoop extends Thread {
+  volatile private boolean keepGoing = true;
+  public void run() {
+    int i = 0;
+    do {
+      i++;
+    } while (keepGoing);
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}
+
+class InfiniteForLoop extends Thread {
+  int count = 100000;
+  volatile private boolean keepGoing = true;
+  public void run() {
+    int i = 0;
+    for (int j = 0; keepGoing; j++) {
+      i += j;
+    }
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}
+
+
+class MakeGarbage extends Thread {
+  volatile private boolean keepGoing = true;
+  public void run() {
+    while (keepGoing) {
+      byte[] garbage = new byte[100000];
+    }
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}