Merge "More diagnostics for flaky oat file assistant test."
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..2fc66e6d
--- /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/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, ¤t_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, ¤t_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, ¤t_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, ¤t_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/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 764b6ba..8c950a0 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -329,14 +329,21 @@
} else {
// Bad method data.
CHECK_NE(verifier.failures_.size(), 0U);
- CHECK(verifier.have_pending_hard_failure_);
- verifier.DumpFailures(LOG(INFO) << "Verification error in "
- << PrettyMethod(method_idx, *dex_file) << "\n");
+
+ if (UNLIKELY(verifier.have_pending_experimental_failure_)) {
+ // Failed due to being forced into interpreter. This is ok because
+ // we just want to skip verification.
+ result = kSoftFailure;
+ } else {
+ CHECK(verifier.have_pending_hard_failure_);
+ verifier.DumpFailures(LOG(INFO) << "Verification error in "
+ << PrettyMethod(method_idx, *dex_file) << "\n");
+ result = kHardFailure;
+ }
if (gDebugVerify) {
std::cout << "\n" << verifier.info_messages_.str();
verifier.Dump(std::cout);
}
- result = kHardFailure;
}
if (kTimeVerifyMethod) {
uint64_t duration_ns = NanoTime() - start_ns;
@@ -402,6 +409,7 @@
monitor_enter_dex_pcs_(nullptr),
have_pending_hard_failure_(false),
have_pending_runtime_throw_failure_(false),
+ have_pending_experimental_failure_(false),
have_any_pending_runtime_throw_failure_(false),
new_instance_count_(0),
monitor_enter_count_(0),
@@ -813,6 +821,17 @@
}
bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_offset) {
+ if (UNLIKELY(inst->IsExperimental())) {
+ // Experimental instructions don't yet have verifier support implementation.
+ // While it is possible to use them by themselves, when we try to use stable instructions
+ // with a virtual register that was created by an experimental instruction,
+ // the data flow analysis will fail.
+ Fail(VERIFY_ERROR_FORCE_INTERPRETER)
+ << "experimental instruction is not supported by verifier; skipping verification";
+ have_pending_experimental_failure_ = true;
+ return false;
+ }
+
bool result = true;
switch (inst->GetVerifyTypeArgumentA()) {
case Instruction::kVerifyRegA:
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index d933448..a2835f5 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -736,6 +736,8 @@
// instructions that would hard fail the verification.
// Note: this flag is reset after processing each instruction.
bool have_pending_runtime_throw_failure_;
+ // Is there a pending experimental failure?
+ bool have_pending_experimental_failure_;
// A version of the above that is not reset and thus captures if there were *any* throw failures.
bool have_any_pending_runtime_throw_failure_;
diff --git a/test/955-lambda-smali/expected.txt b/test/955-lambda-smali/expected.txt
index 5059f4b..3637099 100644
--- a/test/955-lambda-smali/expected.txt
+++ b/test/955-lambda-smali/expected.txt
@@ -7,3 +7,12 @@
(BoxUnbox) Caught NPE for unbox-lambda
(BoxUnbox) Caught NPE for box-lambda
(BoxUnbox) Caught ClassCastException for unbox-lambda
+(MoveResult) testZ success
+(MoveResult) testB success
+(MoveResult) testS success
+(MoveResult) testI success
+(MoveResult) testC success
+(MoveResult) testJ success
+(MoveResult) testF success
+(MoveResult) testD success
+(MoveResult) testL success
diff --git a/test/955-lambda-smali/smali/Main.smali b/test/955-lambda-smali/smali/Main.smali
index 92afd79..5d2aabb 100644
--- a/test/955-lambda-smali/smali/Main.smali
+++ b/test/955-lambda-smali/smali/Main.smali
@@ -23,6 +23,7 @@
invoke-static {}, LSanityCheck;->run()I
invoke-static {}, LTrivialHelloWorld;->run()V
invoke-static {}, LBoxUnbox;->run()V
+ invoke-static {}, LMoveResult;->run()V
# TODO: add tests when verification fails
diff --git a/test/955-lambda-smali/smali/MoveResult.smali b/test/955-lambda-smali/smali/MoveResult.smali
new file mode 100644
index 0000000..1725da3
--- /dev/null
+++ b/test/955-lambda-smali/smali/MoveResult.smali
@@ -0,0 +1,330 @@
+#
+# 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.
+#
+.class public LMoveResult;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+.registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static run()V
+.registers 8
+ invoke-static {}, LMoveResult;->testZ()V
+ invoke-static {}, LMoveResult;->testB()V
+ invoke-static {}, LMoveResult;->testS()V
+ invoke-static {}, LMoveResult;->testI()V
+ invoke-static {}, LMoveResult;->testC()V
+ invoke-static {}, LMoveResult;->testJ()V
+ invoke-static {}, LMoveResult;->testF()V
+ invoke-static {}, LMoveResult;->testD()V
+ invoke-static {}, LMoveResult;->testL()V
+
+ return-void
+.end method
+
+# Test that booleans are returned correctly via move-result.
+.method public static testZ()V
+ .registers 6
+
+ create-lambda v0, LMoveResult;->lambdaZ(Ljava/lang/reflect/ArtMethod;)Z
+ invoke-lambda v0, {}
+ move-result v2
+ const v3, 1
+
+ if-ne v3, v2, :is_not_equal
+ const-string v4, "(MoveResult) testZ success"
+ goto :end
+
+:is_not_equal
+ const-string v4, "(MoveResult) testZ failed"
+
+:end
+ sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v5, v4}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testZ. Always returns "true".
+.method public static lambdaZ(Ljava/lang/reflect/ArtMethod;)Z
+ .registers 3
+
+ const v0, 1
+ return v0
+
+.end method
+
+# Test that bytes are returned correctly via move-result.
+.method public static testB()V
+ .registers 6
+
+ create-lambda v0, LMoveResult;->lambdaB(Ljava/lang/reflect/ArtMethod;)B
+ invoke-lambda v0, {}
+ move-result v2
+ const v3, 15
+
+ if-ne v3, v2, :is_not_equal
+ const-string v4, "(MoveResult) testB success"
+ goto :end
+
+:is_not_equal
+ const-string v4, "(MoveResult) testB failed"
+
+:end
+ sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v5, v4}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testB. Always returns "15".
+.method public static lambdaB(Ljava/lang/reflect/ArtMethod;)B
+ .registers 3 # 1 parameters, 2 locals
+
+ const v0, 15
+ return v0
+
+.end method
+
+# Test that shorts are returned correctly via move-result.
+.method public static testS()V
+ .registers 6
+
+ create-lambda v0, LMoveResult;->lambdaS(Ljava/lang/reflect/ArtMethod;)S
+ invoke-lambda v0, {}
+ move-result v2
+ const/16 v3, 31000
+
+ if-ne v3, v2, :is_not_equal
+ const-string v4, "(MoveResult) testS success"
+ goto :end
+
+:is_not_equal
+ const-string v4, "(MoveResult) testS failed"
+
+:end
+ sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v5, v4}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testS. Always returns "31000".
+.method public static lambdaS(Ljava/lang/reflect/ArtMethod;)S
+ .registers 3
+
+ const/16 v0, 31000
+ return v0
+
+.end method
+
+# Test that ints are returned correctly via move-result.
+.method public static testI()V
+ .registers 6
+
+ create-lambda v0, LMoveResult;->lambdaI(Ljava/lang/reflect/ArtMethod;)I
+ invoke-lambda v0, {}
+ move-result v2
+ const v3, 128000
+
+ if-ne v3, v2, :is_not_equal
+ const-string v4, "(MoveResult) testI success"
+ goto :end
+
+:is_not_equal
+ const-string v4, "(MoveResult) testI failed"
+
+:end
+ sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v5, v4}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testI. Always returns "128000".
+.method public static lambdaI(Ljava/lang/reflect/ArtMethod;)I
+ .registers 3
+
+ const v0, 128000
+ return v0
+
+.end method
+
+# Test that chars are returned correctly via move-result.
+.method public static testC()V
+ .registers 6
+
+ create-lambda v0, LMoveResult;->lambdaC(Ljava/lang/reflect/ArtMethod;)C
+ invoke-lambda v0, {}
+ move-result v2
+ const v3, 65535
+
+ if-ne v3, v2, :is_not_equal
+ const-string v4, "(MoveResult) testC success"
+ goto :end
+
+:is_not_equal
+ const-string v4, "(MoveResult) testC failed"
+
+:end
+ sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v5, v4}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testC. Always returns "65535".
+.method public static lambdaC(Ljava/lang/reflect/ArtMethod;)C
+ .registers 3
+
+ const v0, 65535
+ return v0
+
+.end method
+
+# Test that longs are returned correctly via move-result.
+.method public static testJ()V
+ .registers 8
+
+ create-lambda v0, LMoveResult;->lambdaJ(Ljava/lang/reflect/ArtMethod;)J
+ invoke-lambda v0, {}
+ move-result v2
+ const-wide v4, 0xdeadf00dc0ffee
+
+ if-ne v4, v2, :is_not_equal
+ const-string v6, "(MoveResult) testJ success"
+ goto :end
+
+:is_not_equal
+ const-string v6, "(MoveResult) testJ failed"
+
+:end
+ sget-object v7, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testC. Always returns "0xdeadf00dc0ffee".
+.method public static lambdaJ(Ljava/lang/reflect/ArtMethod;)J
+ .registers 4
+
+ const-wide v0, 0xdeadf00dc0ffee
+ return-wide v0
+
+.end method
+
+# Test that floats are returned correctly via move-result.
+.method public static testF()V
+ .registers 6
+
+ create-lambda v0, LMoveResult;->lambdaF(Ljava/lang/reflect/ArtMethod;)F
+ invoke-lambda v0, {}
+ move-result v2
+ const v3, infinityf
+
+ if-ne v3, v2, :is_not_equal
+ const-string v4, "(MoveResult) testF success"
+ goto :end
+
+:is_not_equal
+ const-string v4, "(MoveResult) testF failed"
+
+:end
+ sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v5, v4}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testF. Always returns "infinityf".
+.method public static lambdaF(Ljava/lang/reflect/ArtMethod;)F
+ .registers 3
+
+ const v0, infinityf
+ return v0
+
+.end method
+
+# Test that doubles are returned correctly via move-result.
+.method public static testD()V
+ .registers 8
+
+ create-lambda v0, LMoveResult;->lambdaD(Ljava/lang/reflect/ArtMethod;)D
+ invoke-lambda v0, {}
+ move-result-wide v2
+ const-wide v4, infinity
+
+ if-ne v4, v2, :is_not_equal
+ const-string v6, "(MoveResult) testD success"
+ goto :end
+
+:is_not_equal
+ const-string v6, "(MoveResult) testD failed"
+
+:end
+ sget-object v7, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testD. Always returns "infinity".
+.method public static lambdaD(Ljava/lang/reflect/ArtMethod;)D
+ .registers 4
+
+ const-wide v0, infinity # 123.456789
+ return-wide v0
+
+.end method
+
+
+# Test that objects are returned correctly via move-result.
+.method public static testL()V
+ .registers 8
+
+ create-lambda v0, LMoveResult;->lambdaL(Ljava/lang/reflect/ArtMethod;)Ljava/lang/String;
+ invoke-lambda v0, {}
+ move-result-object v2
+ const-string v4, "Interned string"
+
+ # relies on string interning returning identical object references
+ if-ne v4, v2, :is_not_equal
+ const-string v6, "(MoveResult) testL success"
+ goto :end
+
+:is_not_equal
+ const-string v6, "(MoveResult) testL failed"
+
+:end
+ sget-object v7, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+
+.end method
+
+# Lambda target for testL. Always returns "Interned string" (string).
+.method public static lambdaL(Ljava/lang/reflect/ArtMethod;)Ljava/lang/String;
+ .registers 4
+
+ const-string v0, "Interned string"
+ return-object v0
+
+.end method
+
+
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. """