Merge "runtest: Have gcstress tests skip 955-lambda-smali"
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 0f756ef..6952d69 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -20,6 +20,15 @@
 ART_TARGET_SUPPORTED_ARCH := arm arm64 mips mips64 x86 x86_64
 ART_HOST_SUPPORTED_ARCH := x86 x86_64
 
+ifneq ($(HOST_OS),darwin)
+  ART_HOST_SUPPORTED_ARCH := x86 x86_64
+else
+  # Mac OS doesn't support low-4GB allocation in a 64-bit process. So we won't be able to create
+  # our heaps.
+  ART_HOST_SUPPORTED_ARCH := x86
+  ART_MULTILIB_OVERRIDE_host := 32
+endif
+
 ART_COVERAGE := false
 
 ifeq ($(ART_COVERAGE),true)
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index a251c92..72cf978 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -127,6 +127,10 @@
     LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
   endif
 
+  ifdef ART_MULTILIB_OVERRIDE_$$(art_target_or_host)
+    art_multilib := $$(ART_MULTILIB_OVERRIDE_$$(art_target_or_host))
+  endif
+
   LOCAL_MULTILIB := $$(art_multilib)
   art_out_binary_name := $$(LOCAL_MODULE)
 
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 377cd4e..63ad9cf 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -255,6 +255,7 @@
   compiler/optimizing/graph_checker_test.cc \
   compiler/optimizing/graph_test.cc \
   compiler/optimizing/gvn_test.cc \
+  compiler/optimizing/licm_test.cc \
   compiler/optimizing/linearize_test.cc \
   compiler/optimizing/liveness_test.cc \
   compiler/optimizing/live_interval_test.cc \
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
new file mode 100644
index 0000000..2fc66e6
--- /dev/null
+++ b/compiler/optimizing/licm_test.cc
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "base/arena_allocator.h"
+#include "builder.h"
+#include "gtest/gtest.h"
+#include "licm.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+#include "side_effects_analysis.h"
+
+namespace art {
+
+/**
+ * Fixture class for the LICM tests.
+ */
+class LICMTest : public testing::Test {
+ public:
+  LICMTest() : pool_(), allocator_(&pool_) {
+    graph_ = CreateGraph(&allocator_);
+  }
+
+  ~LICMTest() { }
+
+  // Builds a singly-nested loop structure in CFG. Tests can further populate
+  // the basic blocks with instructions to set up interesting scenarios.
+  void BuildLoop() {
+    entry_ = new (&allocator_) HBasicBlock(graph_);
+    loop_preheader_ = new (&allocator_) HBasicBlock(graph_);
+    loop_header_ = new (&allocator_) HBasicBlock(graph_);
+    loop_body_ = new (&allocator_) HBasicBlock(graph_);
+    exit_ = new (&allocator_) HBasicBlock(graph_);
+
+    graph_->AddBlock(entry_);
+    graph_->AddBlock(loop_preheader_);
+    graph_->AddBlock(loop_header_);
+    graph_->AddBlock(loop_body_);
+    graph_->AddBlock(exit_);
+
+    graph_->SetEntryBlock(entry_);
+    graph_->SetExitBlock(exit_);
+
+    // Set up loop flow in CFG.
+    entry_->AddSuccessor(loop_preheader_);
+    loop_preheader_->AddSuccessor(loop_header_);
+    loop_header_->AddSuccessor(loop_body_);
+    loop_header_->AddSuccessor(exit_);
+    loop_body_->AddSuccessor(loop_header_);
+
+    // Provide boiler-plate instructions.
+    parameter_ = new (&allocator_) HParameterValue(0, Primitive::kPrimNot);
+    entry_->AddInstruction(parameter_);
+    constant_ = new (&allocator_) HConstant(Primitive::kPrimInt);
+    loop_preheader_->AddInstruction(constant_);
+    loop_header_->AddInstruction(new (&allocator_) HIf(parameter_));
+    loop_body_->AddInstruction(new (&allocator_) HGoto());
+    exit_->AddInstruction(new (&allocator_) HExit());
+  }
+
+  // Performs LICM optimizations (after proper set up).
+  void PerformLICM() {
+    ASSERT_TRUE(graph_->TryBuildingSsa());
+    SideEffectsAnalysis side_effects(graph_);
+    side_effects.Run();
+    LICM licm(graph_, side_effects);
+    licm.Run();
+  }
+
+  // General building fields.
+  ArenaPool pool_;
+  ArenaAllocator allocator_;
+  HGraph* graph_;
+
+  // Specific basic blocks.
+  HBasicBlock* entry_;
+  HBasicBlock* loop_preheader_;
+  HBasicBlock* loop_header_;
+  HBasicBlock* loop_body_;
+  HBasicBlock* exit_;
+
+  HInstruction* parameter_;  // "this"
+  HInstruction* constant_;
+};
+
+//
+// The actual LICM tests.
+//
+
+TEST_F(LICMTest, ConstantHoisting) {
+  BuildLoop();
+
+  // Populate the loop with instructions: set array to constant.
+  HInstruction* constant = new (&allocator_) HConstant(Primitive::kPrimDouble);
+  loop_body_->InsertInstructionBefore(constant, loop_body_->GetLastInstruction());
+  HInstruction* set_array = new (&allocator_) HArraySet(
+      parameter_, constant_, constant, Primitive::kPrimDouble, 0);
+  loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
+
+  EXPECT_EQ(constant->GetBlock(), loop_body_);
+  EXPECT_EQ(set_array->GetBlock(), loop_body_);
+  PerformLICM();
+  EXPECT_EQ(constant->GetBlock(), loop_preheader_);
+  EXPECT_EQ(set_array->GetBlock(), loop_body_);
+}
+
+TEST_F(LICMTest, FieldHoisting) {
+  BuildLoop();
+
+  // Populate the loop with instructions: set/get field with different types.
+  HInstruction* get_field = new (&allocator_) HInstanceFieldGet(
+      parameter_, Primitive::kPrimLong, MemberOffset(10),
+      false, kUnknownFieldIndex, graph_->GetDexFile());
+  loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
+  HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
+      parameter_, constant_, Primitive::kPrimInt, MemberOffset(20),
+      false, kUnknownFieldIndex, graph_->GetDexFile());
+  loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
+
+  EXPECT_EQ(get_field->GetBlock(), loop_body_);
+  EXPECT_EQ(set_field->GetBlock(), loop_body_);
+  PerformLICM();
+  EXPECT_EQ(get_field->GetBlock(), loop_preheader_);
+  EXPECT_EQ(set_field->GetBlock(), loop_body_);
+}
+
+TEST_F(LICMTest, NoFieldHoisting) {
+  BuildLoop();
+
+  // Populate the loop with instructions: set/get field with same types.
+  HInstruction* get_field = new (&allocator_) HInstanceFieldGet(
+      parameter_, Primitive::kPrimLong, MemberOffset(10),
+      false, kUnknownFieldIndex, graph_->GetDexFile());
+  loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
+  HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
+      parameter_, get_field, Primitive::kPrimLong, MemberOffset(10),
+      false, kUnknownFieldIndex, graph_->GetDexFile());
+  loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
+
+  EXPECT_EQ(get_field->GetBlock(), loop_body_);
+  EXPECT_EQ(set_field->GetBlock(), loop_body_);
+  PerformLICM();
+  EXPECT_EQ(get_field->GetBlock(), loop_body_);
+  EXPECT_EQ(set_field->GetBlock(), loop_body_);
+}
+
+TEST_F(LICMTest, ArrayHoisting) {
+  BuildLoop();
+
+  // Populate the loop with instructions: set/get array with different types.
+  HInstruction* get_array = new (&allocator_) HArrayGet(
+      parameter_, constant_, Primitive::kPrimLong);
+  loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
+  HInstruction* set_array = new (&allocator_) HArraySet(
+      parameter_, constant_, constant_, Primitive::kPrimInt, 0);
+  loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
+
+  EXPECT_EQ(get_array->GetBlock(), loop_body_);
+  EXPECT_EQ(set_array->GetBlock(), loop_body_);
+  PerformLICM();
+  EXPECT_EQ(get_array->GetBlock(), loop_preheader_);
+  EXPECT_EQ(set_array->GetBlock(), loop_body_);
+}
+
+TEST_F(LICMTest, NoArrayHoisting) {
+  BuildLoop();
+
+  // Populate the loop with instructions: set/get array with same types.
+  HInstruction* get_array = new (&allocator_) HArrayGet(
+      parameter_, constant_, Primitive::kPrimLong);
+  loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
+  HInstruction* set_array = new (&allocator_) HArraySet(
+      parameter_, get_array, constant_, Primitive::kPrimLong, 0);
+  loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
+
+  EXPECT_EQ(get_array->GetBlock(), loop_body_);
+  EXPECT_EQ(set_array->GetBlock(), loop_body_);
+  PerformLICM();
+  EXPECT_EQ(get_array->GetBlock(), loop_body_);
+  EXPECT_EQ(set_array->GetBlock(), loop_body_);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 85aa004..f7a8486 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3607,7 +3607,7 @@
                     const DexFile& dex_file)
       : HExpression(
             field_type,
-            SideEffects::SideEffects::FieldReadOfType(field_type, is_volatile)),
+            SideEffects::FieldReadOfType(field_type, is_volatile)),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) {
     SetRawInputAt(0, value);
   }
@@ -4058,7 +4058,7 @@
                   const DexFile& dex_file)
       : HExpression(
             field_type,
-            SideEffects::SideEffects::FieldReadOfType(field_type, is_volatile)),
+            SideEffects::FieldReadOfType(field_type, is_volatile)),
         field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) {
     SetRawInputAt(0, cls);
   }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 710d3bc..b8ce04e 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -86,7 +86,7 @@
  * Filter to apply to the visualizer. Methods whose name contain that filter will
  * be dumped.
  */
-static const char* kStringFilter = "";
+static constexpr const char kStringFilter[] = "";
 
 class PassScope;
 
@@ -105,12 +105,14 @@
         visualizer_enabled_(!compiler_driver->GetDumpCfgFileName().empty()),
         visualizer_(visualizer_output, graph, *codegen),
         graph_in_bad_state_(false) {
-    if (strstr(method_name, kStringFilter) == nullptr) {
-      timing_logger_enabled_ = visualizer_enabled_ = false;
-    }
-    if (visualizer_enabled_) {
-      visualizer_.PrintHeader(method_name_);
-      codegen->SetDisassemblyInformation(&disasm_info_);
+    if (timing_logger_enabled_ || visualizer_enabled_) {
+      if (!IsVerboseMethod(compiler_driver, method_name)) {
+        timing_logger_enabled_ = visualizer_enabled_ = false;
+      }
+      if (visualizer_enabled_) {
+        visualizer_.PrintHeader(method_name_);
+        codegen->SetDisassemblyInformation(&disasm_info_);
+      }
     }
   }
 
@@ -169,6 +171,23 @@
     }
   }
 
+  static bool IsVerboseMethod(CompilerDriver* compiler_driver, const char* method_name) {
+    // Test an exact match to --verbose-methods. If verbose-methods is set, this overrides an
+    // empty kStringFilter matching all methods.
+    if (compiler_driver->GetCompilerOptions().HasVerboseMethods()) {
+      return compiler_driver->GetCompilerOptions().IsVerboseMethod(method_name);
+    }
+
+    // Test the kStringFilter sub-string. constexpr helper variable to silence unreachable-code
+    // warning when the string is empty.
+    constexpr bool kStringFilterEmpty = arraysize(kStringFilter) <= 1;
+    if (kStringFilterEmpty || strstr(method_name, kStringFilter) != nullptr) {
+      return true;
+    }
+
+    return false;
+  }
+
   HGraph* const graph_;
   const char* method_name_;
 
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 413b9ea..b499ddd 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -133,14 +133,27 @@
     AdjustFixupIfNeeded(&fixup, &current_code_size, &fixups_to_recalculate);
   }
   while (!fixups_to_recalculate.empty()) {
-    // Pop the fixup.
-    FixupId fixup_id = fixups_to_recalculate.front();
-    fixups_to_recalculate.pop_front();
-    Fixup* fixup = GetFixup(fixup_id);
-    DCHECK_NE(buffer_.Load<int16_t>(fixup->GetLocation()), 0);
-    buffer_.Store<int16_t>(fixup->GetLocation(), 0);
-    // See if it needs adjustment.
-    AdjustFixupIfNeeded(fixup, &current_code_size, &fixups_to_recalculate);
+    do {
+      // Pop the fixup.
+      FixupId fixup_id = fixups_to_recalculate.front();
+      fixups_to_recalculate.pop_front();
+      Fixup* fixup = GetFixup(fixup_id);
+      DCHECK_NE(buffer_.Load<int16_t>(fixup->GetLocation()), 0);
+      buffer_.Store<int16_t>(fixup->GetLocation(), 0);
+      // See if it needs adjustment.
+      AdjustFixupIfNeeded(fixup, &current_code_size, &fixups_to_recalculate);
+    } while (!fixups_to_recalculate.empty());
+
+    if ((current_code_size & 2) != 0 && !literals_.empty()) {
+      // If we need to add padding before literals, this may just push some out of range,
+      // so recalculate all load literals. This makes up for the fact that we don't mark
+      // load literal as a dependency of all previous Fixups even though it actually is.
+      for (Fixup& fixup : fixups_) {
+        if (fixup.IsLoadLiteral()) {
+          AdjustFixupIfNeeded(&fixup, &current_code_size, &fixups_to_recalculate);
+        }
+      }
+    }
   }
   if (kIsDebugBuild) {
     // Check that no fixup is marked as being in fixups_to_recalculate anymore.
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 838554e..41eb5d3 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -489,6 +489,10 @@
       return type_;
     }
 
+    bool IsLoadLiteral() const {
+      return GetType() >= kLoadLiteralNarrow;
+    }
+
     Size GetOriginalSize() const {
       return original_size_;
     }
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 68b7931..004853f 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -950,4 +950,65 @@
             __ GetAdjustedPosition(label.Position()));
 }
 
+TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
+  // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
+  // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
+  // the second CBZ because it's out of range, then it will resize the first CBZ
+  // which has been pushed out of range. Thus, after the first pass, the code size
+  // will appear Aligned<4>(.) but the final size will not be.
+  Label label0, label1, label2;
+  __ cbz(arm::R0, &label1);
+  constexpr size_t kLdrR0R0Count1 = 63;
+  for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
+    __ ldr(arm::R0, arm::Address(arm::R0));
+  }
+  __ Bind(&label0);
+  __ cbz(arm::R0, &label2);
+  __ Bind(&label1);
+  constexpr size_t kLdrR0R0Count2 = 65;
+  for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
+    __ ldr(arm::R0, arm::Address(arm::R0));
+  }
+  __ Bind(&label2);
+  __ ldr(arm::R0, arm::Address(arm::R0));
+
+  std::string expected_part1 =
+      "cmp r0, #0\n"              // cbz r0, label1
+      "beq.n 1f\n" +
+      RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
+      "0:\n"
+      "cmp r0, #0\n"              // cbz r0, label2
+      "beq.n 2f\n"
+      "1:\n" +
+      RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+      "2:\n"                      // Here the offset is Aligned<4>(.).
+      "ldr r0, [r0]\n";           // Make the first part
+
+  // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
+  // literal will not be Aligned<4>(.) but it will appear to be when we process the
+  // instruction during the first pass, so the literal will need a padding and it
+  // will push the literal out of range, so we shall end up with "ldr.w".
+  arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
+  __ LoadLiteral(arm::R0, literal);
+  Label label;
+  __ Bind(&label);
+  constexpr size_t kLdrR0R0Count = 511;
+  for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+    __ ldr(arm::R0, arm::Address(arm::R0));
+  }
+
+  std::string expected =
+      expected_part1 +
+      "1:\n"
+      "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
+      RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+      ".align 2, 0\n"
+      "2:\n"
+      ".word 0x12345678\n";
+  DriverStr(expected, "LoadLiteralMax1KiB");
+
+  EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
+            __ GetAdjustedPosition(label.Position()));
+}
+
 }  // namespace art
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index d127d35..71e9a28 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -66,16 +66,22 @@
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk
 LOCAL_IS_HOST_MODULE := true
 LOCAL_MULTILIB := both
+ifdef ART_MULTILIB_OVERRIDE_host
+  LOCAL_MULTILIB := $(ART_MULTILIB_OVERRIDE_host)
+endif
+ifeq ($(LOCAL_MULTILIB),both)
 LOCAL_MODULE_STEM_32 := dalvikvm32
 LOCAL_MODULE_STEM_64 := dalvikvm64
+endif
 LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
 include $(BUILD_HOST_EXECUTABLE)
-
 # Create symlink for the primary version target.
+ifeq ($(LOCAL_MULTILIB),both)
 include  $(BUILD_SYSTEM)/executable_prefer_symlink.mk
 
-ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)
 ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)$(ART_PHONY_TEST_HOST_SUFFIX)
 ifdef 2ND_ART_PHONY_TEST_HOST_SUFFIX
   ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
 endif
+endif
+ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)
diff --git a/dexdump/Android.mk b/dexdump/Android.mk
index c6b4d47..a208ccf 100755
--- a/dexdump/Android.mk
+++ b/dexdump/Android.mk
@@ -50,5 +50,5 @@
 LOCAL_CFLAGS += -Wall
 LOCAL_SHARED_LIBRARIES += $(dexdump_libraries)
 LOCAL_MODULE := dexdump2
-LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := $(ART_MULTILIB_OVERRIDE_host)
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/dexlist/Android.mk b/dexlist/Android.mk
index 988fe03..9fbd847 100755
--- a/dexlist/Android.mk
+++ b/dexlist/Android.mk
@@ -50,5 +50,5 @@
 LOCAL_CFLAGS += -Wall
 LOCAL_SHARED_LIBRARIES += $(dexlist_libraries)
 LOCAL_MODULE := dexlist2
-LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := $(ART_MULTILIB_OVERRIDE_host)
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d133fa3..03ad2d5 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -38,6 +38,28 @@
 
 namespace art {
 
+// Some tests very occasionally fail: we expect to have an unrelocated non-pic
+// odex file that is reported as needing relocation, but it is reported
+// instead as being up to date (b/22599792).
+//
+// This function adds extra checks for diagnosing why the given oat file is
+// reported up to date, when it should be non-pic needing relocation.
+// These extra diagnostics checks should be removed once b/22599792 has been
+// resolved.
+static void DiagnoseFlakyTestFailure(const OatFile& oat_file) {
+  Runtime* runtime = Runtime::Current();
+  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+  ASSERT_TRUE(image_space != nullptr);
+  const ImageHeader& image_header = image_space->GetImageHeader();
+  const OatHeader& oat_header = oat_file.GetOatHeader();
+  EXPECT_FALSE(oat_file.IsPic());
+  EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum());
+  EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
+      oat_header.GetImageFileLocationOatDataBegin());
+  EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());
+}
+
+
 class OatFileAssistantTest : public CommonRuntimeTest {
  public:
   virtual void SetUp() {
@@ -186,6 +208,7 @@
 
   // Generate an odex file for the purposes of test.
   // If pic is true, generates a PIC odex.
+  // The generated odex file will be un-relocated.
   void GenerateOdexForTest(const std::string& dex_location,
                            const std::string& odex_location,
                            bool pic = false) {
@@ -210,6 +233,16 @@
     std::string error_msg;
     ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
     setenv("ANDROID_DATA", android_data_.c_str(), 1);
+
+    // Verify the odex file was generated as expected.
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(
+        odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
+        false, dex_location.c_str(), &error_msg));
+    ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+
+    if (!pic) {
+      DiagnoseFlakyTestFailure(*odex_file);
+    }
   }
 
   void GeneratePicOdexForTest(const std::string& dex_location,
@@ -446,27 +479,6 @@
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
-// Some tests very occasionally fail: we expect to have an unrelocated non-pic
-// odex file that is reported as needing relocation, but it is reported
-// instead as being up to date (b/22599792).
-//
-// This function adds extra checks for diagnosing why the given oat file is
-// reported up to date, when it should be non-pic needing relocation.
-// These extra diagnostics checks should be removed once b/22599792 has been
-// resolved.
-static void DiagnoseFlakyTestFailure(const OatFile& oat_file) {
-  Runtime* runtime = Runtime::Current();
-  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
-  ASSERT_TRUE(image_space != nullptr);
-  const ImageHeader& image_header = image_space->GetImageHeader();
-  const OatHeader& oat_header = oat_file.GetOatHeader();
-  EXPECT_FALSE(oat_file.IsPic());
-  EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum());
-  EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
-      oat_header.GetImageFileLocationOatDataBegin());
-  EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());
-}
-
 // Case: We have a DEX file and an ODEX file, but no OAT file.
 // Expect: The status is kPatchOatNeeded.
 TEST_F(OatFileAssistantTest, DexOdexNoOat) {
diff --git a/tools/checker/checker.py b/tools/checker/checker.py
index ed630e3..4e516de 100755
--- a/tools/checker/checker.py
+++ b/tools/checker/checker.py
@@ -62,7 +62,7 @@
 def FindCheckerFiles(path):
   """ Returns a list of files to scan for check annotations in the given path.
       Path to a file is returned as a single-element list, directories are
-      recursively traversed and all '.java' files returned.
+      recursively traversed and all '.java' and '.smali' files returned.
   """
   if not path:
     Logger.fail("No source path provided")
diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py
index 33735cb..f354395 100644
--- a/tools/checker/file_format/checker/parser.py
+++ b/tools/checker/file_format/checker/parser.py
@@ -76,7 +76,7 @@
   if notLine is not None:
     return (notLine, TestAssertion.Variant.Not, lineNo), None
 
-  Logger.fail("Checker assertion could not be parsed", fileName, lineNo)
+  Logger.fail("Checker assertion could not be parsed: '" + line + "'", fileName, lineNo)
 
 def __isMatchAtStart(match):
   """ Tests if the given Match occurred at the beginning of the line. """