diff options
-rw-r--r-- | compiler/optimizing/stack_map_stream.cc | 92 | ||||
-rw-r--r-- | compiler/optimizing/stack_map_stream.h | 8 | ||||
-rw-r--r-- | compiler/optimizing/stack_map_test.cc | 21 | ||||
-rw-r--r-- | libartbase/base/bit_memory_region.h | 14 | ||||
-rw-r--r-- | oatdump/oatdump.cc | 10 | ||||
-rw-r--r-- | patchoat/patchoat_test.cc | 41 | ||||
-rw-r--r-- | runtime/common_throws.cc | 6 | ||||
-rw-r--r-- | runtime/dex_register_location.h | 6 | ||||
-rw-r--r-- | runtime/jni/java_vm_ext.cc | 6 | ||||
-rw-r--r-- | runtime/stack.cc | 4 | ||||
-rw-r--r-- | runtime/stack_map.cc | 65 | ||||
-rw-r--r-- | runtime/stack_map.h | 85 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 9 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 3 | ||||
-rw-r--r-- | test/551-checker-shifter-operand/build | 20 | ||||
-rw-r--r-- | test/551-checker-shifter-operand/src/Main.java | 136 | ||||
-rwxr-xr-x | test/565-checker-doublenegbitwise/build | 20 | ||||
-rw-r--r-- | test/565-checker-doublenegbitwise/smali/SmaliTests.smali | 588 | ||||
-rw-r--r-- | test/565-checker-doublenegbitwise/src/Main.java | 301 | ||||
-rw-r--r-- | test/testrunner/env.py | 3 | ||||
-rwxr-xr-x | test/testrunner/testrunner.py | 4 | ||||
-rw-r--r-- | tools/build/var_list | 3 | ||||
-rw-r--r-- | tools/veridex/Android.mk | 4 |
23 files changed, 935 insertions, 514 deletions
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 3685ab2df4..094b75de69 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -46,6 +46,13 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint8_t inlining_depth) { DCHECK(!in_stack_map_) << "Mismatched Begin/End calls"; in_stack_map_ = true; + // num_dex_registers_ is the constant per-method number of registers. + // However we initially don't know what the value is, so lazily initialize it. + if (num_dex_registers_ == 0) { + num_dex_registers_ = num_dex_registers; + } else if (num_dex_registers > 0) { + DCHECK_EQ(num_dex_registers_, num_dex_registers) << "Inconsistent register count"; + } current_stack_map_ = StackMapEntry { .packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_), @@ -85,7 +92,6 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, } CHECK_EQ(stack_map.HasInlineInfo(), (inlining_depth != 0)); CHECK_EQ(code_info.GetInlineDepthOf(stack_map), inlining_depth); - CHECK_EQ(stack_map.HasDexRegisterMap(), (num_dex_registers != 0)); }); } } @@ -102,16 +108,14 @@ void StackMapStream::EndStackMapEntry() { inline_infos_.Dedup(current_inline_infos_.data(), current_inline_infos_.size()); } + // Generate delta-compressed dex register map. + CreateDexRegisterMap(); + stack_maps_.Add(current_stack_map_); } void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { current_dex_registers_.push_back(DexRegisterLocation(kind, value)); - - // We have collected all the dex registers for StackMap/InlineInfo - create the map. - if (current_dex_registers_.size() == expected_num_dex_registers_) { - CreateDexRegisterMap(); - } } void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) { @@ -142,14 +146,15 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, in_inline_info_ = true; DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); + expected_num_dex_registers_ += num_dex_registers; + InlineInfoEntry entry = { .is_last = InlineInfo::kMore, .dex_pc = dex_pc, .method_info_index = kNoValue, .art_method_hi = kNoValue, .art_method_lo = kNoValue, - .dex_register_mask_index = kNoValue, - .dex_register_map_index = kNoValue, + .num_dex_registers = static_cast<uint32_t>(expected_num_dex_registers_), }; if (EncodeArtMethodInInlineInfo(method)) { entry.art_method_hi = High32Bits(reinterpret_cast<uintptr_t>(method)); @@ -164,9 +169,6 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, } current_inline_infos_.push_back(entry); - current_dex_registers_.clear(); - expected_num_dex_registers_ = num_dex_registers; - if (kVerifyStackMaps) { size_t stack_map_index = stack_maps_.size(); size_t depth = current_inline_infos_.size() - 1; @@ -182,7 +184,6 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()], method->GetDexMethodIndexUnchecked()); } - CHECK_EQ(inline_info.HasDexRegisterMap(), (num_dex_registers != 0)); }); } } @@ -193,56 +194,68 @@ void StackMapStream::EndInlineInfoEntry() { DCHECK_EQ(expected_num_dex_registers_, current_dex_registers_.size()); } -// Create dex register map (bitmap + indices + catalogue entries) -// based on the currently accumulated list of DexRegisterLocations. +// Create delta-compressed dex register map based on the current list of DexRegisterLocations. +// All dex registers for a stack map are concatenated - inlined registers are just appended. void StackMapStream::CreateDexRegisterMap() { - // Create mask and map based on current registers. + // These are fields rather than local variables so that we can reuse the reserved memory. temp_dex_register_mask_.ClearAllBits(); temp_dex_register_map_.clear(); + + // Ensure that the arrays that hold previous state are big enough to be safely indexed below. + if (previous_dex_registers_.size() < current_dex_registers_.size()) { + previous_dex_registers_.resize(current_dex_registers_.size(), DexRegisterLocation::None()); + dex_register_timestamp_.resize(current_dex_registers_.size(), 0u); + } + + // Set bit in the mask for each register that has been changed since the previous stack map. + // Modified registers are stored in the catalogue and the catalogue index added to the list. for (size_t i = 0; i < current_dex_registers_.size(); i++) { DexRegisterLocation reg = current_dex_registers_[i]; - if (reg.IsLive()) { - DexRegisterEntry entry = DexRegisterEntry { + // Distance is difference between this index and the index of last modification. + uint32_t distance = stack_maps_.size() - dex_register_timestamp_[i]; + if (previous_dex_registers_[i] != reg || distance > kMaxDexRegisterMapSearchDistance) { + DexRegisterEntry entry = DexRegisterEntry{ .kind = static_cast<uint32_t>(reg.GetKind()), .packed_value = DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue()), }; + uint32_t index = reg.IsLive() ? dex_register_catalog_.Dedup(&entry) : kNoValue; temp_dex_register_mask_.SetBit(i); - temp_dex_register_map_.push_back(dex_register_catalog_.Dedup(&entry)); + temp_dex_register_map_.push_back(index); + previous_dex_registers_[i] = reg; + dex_register_timestamp_[i] = stack_maps_.size(); } } - // Set the mask and map for the current StackMap/InlineInfo. - uint32_t mask_index = StackMap::kNoValue; // Represents mask with all zero bits. + // Set the mask and map for the current StackMap (which includes inlined registers). if (temp_dex_register_mask_.GetNumberOfBits() != 0) { - mask_index = dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(), - temp_dex_register_mask_.GetNumberOfBits()); + current_stack_map_.dex_register_mask_index = + dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(), + temp_dex_register_mask_.GetNumberOfBits()); } - uint32_t map_index = dex_register_maps_.Dedup(temp_dex_register_map_.data(), - temp_dex_register_map_.size()); - if (!current_inline_infos_.empty()) { - current_inline_infos_.back().dex_register_mask_index = mask_index; - current_inline_infos_.back().dex_register_map_index = map_index; - } else { - current_stack_map_.dex_register_mask_index = mask_index; - current_stack_map_.dex_register_map_index = map_index; + if (!current_dex_registers_.empty()) { + current_stack_map_.dex_register_map_index = + dex_register_maps_.Dedup(temp_dex_register_map_.data(), + temp_dex_register_map_.size()); } if (kVerifyStackMaps) { size_t stack_map_index = stack_maps_.size(); - int32_t depth = current_inline_infos_.size() - 1; + uint32_t depth = current_inline_infos_.size(); // We need to make copy of the current registers for later (when the check is run). - auto expected_dex_registers = std::make_shared<std::vector<DexRegisterLocation>>( + auto expected_dex_registers = std::make_shared<dchecked_vector<DexRegisterLocation>>( current_dex_registers_.begin(), current_dex_registers_.end()); dchecks_.emplace_back([=](const CodeInfo& code_info) { StackMap stack_map = code_info.GetStackMapAt(stack_map_index); - size_t num_dex_registers = expected_dex_registers->size(); - DexRegisterMap map = (depth == -1) - ? code_info.GetDexRegisterMapOf(stack_map, num_dex_registers) - : code_info.GetDexRegisterMapAtDepth(depth, stack_map, num_dex_registers); - CHECK_EQ(map.size(), num_dex_registers); - for (size_t r = 0; r < num_dex_registers; r++) { - CHECK_EQ(expected_dex_registers->at(r), map.Get(r)); + uint32_t expected_reg = 0; + for (DexRegisterLocation reg : code_info.GetDexRegisterMapOf(stack_map)) { + CHECK_EQ((*expected_dex_registers)[expected_reg++], reg); + } + for (uint32_t d = 0; d < depth; d++) { + for (DexRegisterLocation reg : code_info.GetDexRegisterMapAtDepth(d, stack_map)) { + CHECK_EQ((*expected_dex_registers)[expected_reg++], reg); + } } + CHECK_EQ(expected_reg, expected_dex_registers->size()); }); } } @@ -290,6 +303,7 @@ size_t StackMapStream::PrepareForFillIn() { dex_register_masks_.Encode(&out_, &bit_offset); dex_register_maps_.Encode(&out_, &bit_offset); dex_register_catalog_.Encode(&out_, &bit_offset); + EncodeVarintBits(&out_, &bit_offset, num_dex_registers_); return UnsignedLeb128Size(out_.size()) + out_.size(); } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index d634c703ff..02fb6cb434 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -55,6 +55,8 @@ class StackMapStream : public ValueObject { in_inline_info_(false), current_inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)), + previous_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)), + dex_register_timestamp_(allocator->Adapter(kArenaAllocStackMapStream)), temp_dex_register_mask_(allocator, 32, true, kArenaAllocStackMapStream), temp_dex_register_map_(allocator->Adapter(kArenaAllocStackMapStream)) { } @@ -113,8 +115,7 @@ class StackMapStream : public ValueObject { uint32_t method_info_index; uint32_t art_method_hi; uint32_t art_method_lo; - uint32_t dex_register_mask_index; - uint32_t dex_register_map_index; + uint32_t num_dex_registers; }; // The fields must be uint32_t and mirror the InvokeInfo accessor in stack_map.h! @@ -147,6 +148,7 @@ class StackMapStream : public ValueObject { BitmapTableBuilder dex_register_masks_; BitTableBuilder<uint32_t> dex_register_maps_; BitTableBuilder<DexRegisterEntry> dex_register_catalog_; + uint32_t num_dex_registers_ = 0; // TODO: Make this const and get the value in constructor. ScopedArenaVector<uint8_t> out_; BitTableBuilder<uint32_t> method_infos_; @@ -159,6 +161,8 @@ class StackMapStream : public ValueObject { StackMapEntry current_stack_map_; ScopedArenaVector<InlineInfoEntry> current_inline_infos_; ScopedArenaVector<DexRegisterLocation> current_dex_registers_; + ScopedArenaVector<DexRegisterLocation> previous_dex_registers_; + ScopedArenaVector<uint32_t> dex_register_timestamp_; // Stack map index of last change. size_t expected_num_dex_registers_; // Temporary variables used in CreateDexRegisterMap. diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 77aa3ef965..0be276cfd6 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -358,13 +358,6 @@ TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { ASSERT_EQ(Kind::kConstant, location1.GetKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); - - // Test that the inline info dex register map deduplicated to the same offset as the stack map - // one. - ASSERT_TRUE(stack_map.HasInlineInfo()); - InlineInfo inline_info = code_info.GetInlineInfoAtDepth(stack_map, 0); - EXPECT_EQ(inline_info.GetDexRegisterMapIndex(), - stack_map.GetDexRegisterMapIndex()); } } @@ -466,13 +459,9 @@ TEST(StackMapTest, TestShareDexRegisterMap) { ASSERT_EQ(2, dex_registers2.GetMachineRegister(0)); ASSERT_EQ(-2, dex_registers2.GetConstant(1)); - // Verify dex register map offsets. - ASSERT_EQ(sm0.GetDexRegisterMapIndex(), - sm1.GetDexRegisterMapIndex()); - ASSERT_NE(sm0.GetDexRegisterMapIndex(), - sm2.GetDexRegisterMapIndex()); - ASSERT_NE(sm1.GetDexRegisterMapIndex(), - sm2.GetDexRegisterMapIndex()); + // Verify dex register mask offsets. + ASSERT_FALSE(sm1.HasDexRegisterMaskIndex()); // No delta. + ASSERT_TRUE(sm2.HasDexRegisterMaskIndex()); // Has delta. } TEST(StackMapTest, TestNoDexRegisterMap) { @@ -649,8 +638,6 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0)); ASSERT_EQ(10, dex_registers2.GetConstant(1)); ASSERT_EQ(5, dex_registers2.GetMachineRegister(2)); - - ASSERT_FALSE(if1_2.HasDexRegisterMap()); } { @@ -682,8 +669,6 @@ TEST(StackMapTest, InlineTest) { ASSERT_EQ(10u, if2_2.GetDexPc()); ASSERT_TRUE(if2_2.EncodesArtMethod()); - ASSERT_FALSE(if2_0.HasDexRegisterMap()); - DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, sm3, 1); ASSERT_EQ(2, dex_registers1.GetMachineRegister(0)); diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h index a3d3ee41d6..b4764fd5ce 100644 --- a/libartbase/base/bit_memory_region.h +++ b/libartbase/base/bit_memory_region.h @@ -151,6 +151,20 @@ class BitMemoryRegion FINAL : public ValueObject { StoreBits(bit_offset + bit, src.LoadBits(bit, num_bits), num_bits); } + // Count the number of set bits within the given bit range. + ALWAYS_INLINE size_t PopCount(size_t bit_offset, size_t bit_length) const { + DCHECK_LE(bit_offset, bit_size_); + DCHECK_LE(bit_length, bit_size_ - bit_offset); + size_t count = 0; + size_t bit = 0; + constexpr size_t kNumBits = BitSizeOf<uint32_t>(); + for (; bit + kNumBits <= bit_length; bit += kNumBits) { + count += POPCOUNT(LoadBits(bit_offset + bit, kNumBits)); + } + count += POPCOUNT(LoadBits(bit_offset + bit, bit_length - bit)); + return count; + } + ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const { return data_ == other.data_ && bit_start_ == other.bit_start_ && diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 25676f736c..2c17d5d2e7 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -757,7 +757,7 @@ class OatDumper { kByteKindInlineInfoMethodIndexIdx, kByteKindInlineInfoDexPc, kByteKindInlineInfoArtMethod, - kByteKindInlineInfoDexRegisterMap, + kByteKindInlineInfoNumDexRegisters, kByteKindInlineInfoIsLast, kByteKindCount, // Special ranges for std::accumulate convenience. @@ -859,8 +859,8 @@ class OatDumper { inline_info_bits, "inline info"); Dump(os, - "InlineInfoDexRegisterMap ", - bits[kByteKindInlineInfoDexRegisterMap], + "InlineInfoNumDexRegisters ", + bits[kByteKindInlineInfoNumDexRegisters], inline_info_bits, "inline info"); Dump(os, @@ -1761,8 +1761,8 @@ class OatDumper { inline_infos.NumColumnBits(InlineInfo::kArtMethodHi) * num_inline_infos + inline_infos.NumColumnBits(InlineInfo::kArtMethodLo) * num_inline_infos); stats_.AddBits( - Stats::kByteKindInlineInfoDexRegisterMap, - inline_infos.NumColumnBits(InlineInfo::kDexRegisterMapIndex) * num_inline_infos); + Stats::kByteKindInlineInfoNumDexRegisters, + inline_infos.NumColumnBits(InlineInfo::kNumberOfDexRegisters) * num_inline_infos); stats_.AddBits(Stats::kByteKindInlineInfoIsLast, num_inline_infos); } } diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc index 934936d4b3..08bf31c4bd 100644 --- a/patchoat/patchoat_test.cc +++ b/patchoat/patchoat_test.cc @@ -445,19 +445,15 @@ TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { #endif } +// These tests check that a boot image relocated using patchoat can be unrelocated +// using the .rel file created by patchoat. +// +// The tests don't work when heap poisoning is enabled because some of the +// references are negated. b/72117833 is tracking the effort to have patchoat +// and its tests support heap poisoning. class PatchoatVerificationTest : public PatchoatTest { protected: - virtual void SetUp() { - PatchoatTest::SetUp(); - - // This test checks that a boot image relocated using patchoat can be unrelocated using the .rel - // file created by patchoat. - - // This test doesn't work when heap poisoning is enabled because some of the - // references are negated. b/72117833 is tracking the effort to have patchoat - // and its tests support heap poisoning. - TEST_DISABLED_FOR_HEAP_POISONING(); - + void CreateRelocatedBootImage() { // Compile boot image into a random directory using dex2oat ScratchFile dex2oat_orig_scratch; dex2oat_orig_scratch.Unlink(); @@ -534,12 +530,14 @@ class PatchoatVerificationTest : public PatchoatTest { } virtual void TearDown() { - ClearDirectory(dex2oat_orig_dir_.c_str(), /*recursive*/ true); - ClearDirectory(relocated_dir_.c_str(), /*recursive*/ true); - - rmdir(dex2oat_orig_dir_.c_str()); - rmdir(relocated_dir_.c_str()); - + if (!dex2oat_orig_dir_.empty()) { + ClearDirectory(dex2oat_orig_dir_.c_str(), /*recursive*/ true); + rmdir(dex2oat_orig_dir_.c_str()); + } + if (!relocated_dir_.empty()) { + ClearDirectory(relocated_dir_.c_str(), /*recursive*/ true); + rmdir(relocated_dir_.c_str()); + } PatchoatTest::TearDown(); } @@ -550,6 +548,9 @@ class PatchoatVerificationTest : public PatchoatTest { // Assert that verification works with the .rel files. TEST_F(PatchoatVerificationTest, Sucessful) { + TEST_DISABLED_FOR_HEAP_POISONING(); + CreateRelocatedBootImage(); + std::string error_msg; if (!VerifyBootImage( dex2oat_orig_dir_ + "/boot.art", @@ -562,6 +563,9 @@ TEST_F(PatchoatVerificationTest, Sucessful) { // Corrupt the image file and check that the verification fails gracefully. TEST_F(PatchoatVerificationTest, CorruptedImage) { + TEST_DISABLED_FOR_HEAP_POISONING(); + CreateRelocatedBootImage(); + std::string error_msg; std::string relocated_image_filename; if (!GetDalvikCacheFilename((dex2oat_orig_dir_ + "/boot.art").c_str(), @@ -584,6 +588,9 @@ TEST_F(PatchoatVerificationTest, CorruptedImage) { // Corrupt the relocation file and check that the verification fails gracefully. TEST_F(PatchoatVerificationTest, CorruptedRelFile) { + TEST_DISABLED_FOR_HEAP_POISONING(); + CreateRelocatedBootImage(); + std::string error_msg; std::string art_filename = dex2oat_orig_dir_ + "/boot.art"; std::string rel_filename = dex2oat_orig_dir_ + "/boot.art.rel"; diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 657a78bd2f..2ffadb3ca2 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -787,7 +787,7 @@ void ThrowStackOverflowError(Thread* self) { // Object stackState; // StackTraceElement[] stackTrace; // Only Throwable has a non-empty constructor: - // this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; + // this.stackTrace = Throwable.UNASSIGNED_STACK; // fillInStackTrace(); // detailMessage. @@ -822,8 +822,8 @@ void ThrowStackOverflowError(Thread* self) { // stackTrace. ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField( - WellKnownClasses::libcore_util_EmptyArray, - WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT)); + WellKnownClasses::java_lang_Throwable, + WellKnownClasses::java_lang_Throwable_UNASSIGNED_STACK)); env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_stackTrace, stack_trace_elem.get()); diff --git a/runtime/dex_register_location.h b/runtime/dex_register_location.h index c6d4ad2feb..a20dccbc12 100644 --- a/runtime/dex_register_location.h +++ b/runtime/dex_register_location.h @@ -29,6 +29,7 @@ namespace art { class DexRegisterLocation { public: enum class Kind : int32_t { + kInvalid = -2, // only used internally during register map decoding. kNone = -1, // vreg has not been set. kInStack, // vreg is on the stack, value holds the stack offset. kConstant, // vreg is a constant value. @@ -40,9 +41,8 @@ class DexRegisterLocation { DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {} - static DexRegisterLocation None() { - return DexRegisterLocation(Kind::kNone, 0); - } + static DexRegisterLocation None() { return DexRegisterLocation(Kind::kNone, 0); } + static DexRegisterLocation Invalid() { return DexRegisterLocation(Kind::kInvalid, 0); } bool IsLive() const { return kind_ != Kind::kNone; } diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc index 8fe68bd318..44679a5afa 100644 --- a/runtime/jni/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc @@ -912,7 +912,11 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, return utf.c_str(); } } - env->ExceptionClear(); + if (env->ExceptionCheck()) { + // We can't do much better logging, really. So leave it with a Describe. + env->ExceptionDescribe(); + env->ExceptionClear(); + } return "(Error calling toString)"; } return "null"; diff --git a/runtime/stack.cc b/runtime/stack.cc index bd0d5d680e..0b3441aa45 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -236,7 +236,9 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin size_t depth_in_stack_map = current_inlining_depth_ - 1; DexRegisterMap dex_register_map = IsInInlinedFrame() - ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, stack_map, number_of_dex_registers) + ? code_info.GetDexRegisterMapAtDepth(depth_in_stack_map, + stack_map, + number_of_dex_registers) : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); if (!dex_register_map.IsValid()) { diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index a5749b84a7..59a89e12b8 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -25,6 +25,69 @@ namespace art { +// Scan backward to determine dex register locations at given stack map. +// All registers for a stack map are combined - inlined registers are just appended, +// therefore 'first_dex_register' allows us to select a sub-range to decode. +void CodeInfo::DecodeDexRegisterMap(uint32_t stack_map_index, + uint32_t first_dex_register, + /*out*/ DexRegisterMap* map) const { + // Count remaining work so we know when we have finished. + uint32_t remaining_registers = map->size(); + + // Keep scanning backwards and collect the most recent location of each register. + for (int32_t s = stack_map_index; s >= 0 && remaining_registers != 0; s--) { + StackMap stack_map = GetStackMapAt(s); + DCHECK_LE(stack_map_index - s, kMaxDexRegisterMapSearchDistance) << "Unbounded search"; + + // The mask specifies which registers where modified in this stack map. + // NB: the mask can be shorter than expected if trailing zero bits were removed. + uint32_t mask_index = stack_map.GetDexRegisterMaskIndex(); + if (mask_index == StackMap::kNoValue) { + continue; // Nothing changed at this stack map. + } + BitMemoryRegion mask = dex_register_masks_.GetBitMemoryRegion(mask_index); + if (mask.size_in_bits() <= first_dex_register) { + continue; // Nothing changed after the first register we are interested in. + } + + // The map stores one catalogue index per each modified register location. + uint32_t map_index = stack_map.GetDexRegisterMapIndex(); + DCHECK_NE(map_index, StackMap::kNoValue); + + // Skip initial registers which we are not interested in (to get to inlined registers). + map_index += mask.PopCount(0, first_dex_register); + mask = mask.Subregion(first_dex_register, mask.size_in_bits() - first_dex_register); + + // Update registers that we see for first time (i.e. most recent value). + DexRegisterLocation* regs = map->data(); + const uint32_t end = std::min<uint32_t>(map->size(), mask.size_in_bits()); + const size_t kNumBits = BitSizeOf<uint32_t>(); + for (uint32_t reg = 0; reg < end; reg += kNumBits) { + // Process the mask in chunks of kNumBits for performance. + uint32_t bits = mask.LoadBits(reg, std::min<uint32_t>(end - reg, kNumBits)); + while (bits != 0) { + uint32_t bit = CTZ(bits); + if (regs[reg + bit].GetKind() == DexRegisterLocation::Kind::kInvalid) { + regs[reg + bit] = GetDexRegisterCatalogEntry(dex_register_maps_.Get(map_index)); + remaining_registers--; + } + map_index++; + bits ^= 1u << bit; // Clear the bit. + } + } + } + + // Set any remaining registers to None (which is the default state at first stack map). + if (remaining_registers != 0) { + DexRegisterLocation* regs = map->data(); + for (uint32_t r = 0; r < map->size(); r++) { + if (regs[r].GetKind() == DexRegisterLocation::Kind::kInvalid) { + regs[r] = DexRegisterLocation::None(); + } + } + } +} + std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg) { using Kind = DexRegisterLocation::Kind; switch (reg.GetKind()) { @@ -42,6 +105,8 @@ std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg) { return stream << "f" << reg.GetValue() << "/hi"; case Kind::kConstant: return stream << "#" << reg.GetValue(); + case Kind::kInvalid: + return stream << "Invalid"; default: return stream << "DexRegisterLocation(" << static_cast<uint32_t>(reg.GetKind()) << "," << reg.GetValue() << ")"; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 6da002138c..ff70b6c759 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -39,6 +39,12 @@ class VariableIndentationOutputStream; // (signed) values. static constexpr ssize_t kFrameSlotSize = 4; +// The delta compression of dex register maps means we need to scan the stackmaps backwards. +// We compress the data in such a way so that there is an upper bound on the search distance. +// Max distance 0 means each stack map must be fully defined and no scanning back is allowed. +// If this value is changed, the oat file version should be incremented (for DCHECK to pass). +static constexpr size_t kMaxDexRegisterMapSearchDistance = 32; + class ArtMethod; class CodeInfo; @@ -49,12 +55,14 @@ std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation& reg); // If the size is small enough, it keeps the data on the stack. class DexRegisterMap { public: - // Create map for given number of registers and initialize all locations to None. - explicit DexRegisterMap(size_t count) : count_(count), regs_small_{} { + using iterator = DexRegisterLocation*; + + // Create map for given number of registers and initialize them to the given value. + DexRegisterMap(size_t count, DexRegisterLocation value) : count_(count), regs_small_{} { if (count_ <= kSmallCount) { - std::fill_n(regs_small_.begin(), count, DexRegisterLocation::None()); + std::fill_n(regs_small_.begin(), count, value); } else { - regs_large_.resize(count, DexRegisterLocation::None()); + regs_large_.resize(count, value); } } @@ -62,6 +70,9 @@ class DexRegisterMap { return count_ <= kSmallCount ? regs_small_.data() : regs_large_.data(); } + iterator begin() { return data(); } + iterator end() { return data() + count_; } + size_t size() const { return count_; } bool IsValid() const { return count_ != 0; } @@ -192,7 +203,7 @@ class StackMap : public BitTable<7>::Accessor { * The row referenced from the StackMap holds information at depth 0. * Following rows hold information for further depths. */ -class InlineInfo : public BitTable<7>::Accessor { +class InlineInfo : public BitTable<6>::Accessor { public: BIT_TABLE_HEADER() BIT_TABLE_COLUMN(0, IsLast) // Determines if there are further rows for further depths. @@ -200,7 +211,7 @@ class InlineInfo : public BitTable<7>::Accessor { BIT_TABLE_COLUMN(2, MethodInfoIndex) BIT_TABLE_COLUMN(3, ArtMethodHi) // High bits of ArtMethod*. BIT_TABLE_COLUMN(4, ArtMethodLo) // Low bits of ArtMethod*. - BIT_TABLE_COLUMN(5, DexRegisterMaskIndex) + BIT_TABLE_COLUMN(5, NumberOfDexRegisters) // Includes outer levels and the main method. BIT_TABLE_COLUMN(6, DexRegisterMapIndex) static constexpr uint32_t kLast = -1; @@ -220,10 +231,6 @@ class InlineInfo : public BitTable<7>::Accessor { return reinterpret_cast<ArtMethod*>((hi << 32) | lo); } - ALWAYS_INLINE bool HasDexRegisterMap() const { - return HasDexRegisterMapIndex(); - } - void Dump(VariableIndentationOutputStream* vios, const CodeInfo& info, const StackMap& stack_map, @@ -338,7 +345,9 @@ class CodeInfo { } ALWAYS_INLINE DexRegisterLocation GetDexRegisterCatalogEntry(size_t index) const { - return DexRegisterInfo(&dex_register_catalog_, index).GetLocation(); + return (index == StackMap::kNoValue) + ? DexRegisterLocation::None() + : DexRegisterInfo(&dex_register_catalog_, index).GetLocation(); } uint32_t GetNumberOfStackMaps() const { @@ -350,19 +359,30 @@ class CodeInfo { } ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, - size_t num_dex_registers) const { - return DecodeDexRegisterMap(stack_map.GetDexRegisterMaskIndex(), - stack_map.GetDexRegisterMapIndex(), - num_dex_registers); + size_t vregs ATTRIBUTE_UNUSED = 0) const { + if (stack_map.HasDexRegisterMap()) { + DexRegisterMap map(number_of_dex_registers_, DexRegisterLocation::Invalid()); + DecodeDexRegisterMap(stack_map.Row(), /* first_dex_register */ 0, &map); + return map; + } + return DexRegisterMap(0, DexRegisterLocation::None()); } ALWAYS_INLINE DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth, StackMap stack_map, - size_t num_dex_registers) const { - InlineInfo inline_info = GetInlineInfoAtDepth(stack_map, depth); - return DecodeDexRegisterMap(inline_info.GetDexRegisterMaskIndex(), - inline_info.GetDexRegisterMapIndex(), - num_dex_registers); + size_t vregs ATTRIBUTE_UNUSED = 0) const { + if (stack_map.HasDexRegisterMap()) { + // The register counts are commutative and include all outer levels. + // This allows us to determine the range [first, last) in just two lookups. + // If we are at depth 0 (the first inlinee), the count from the main method is used. + uint32_t first = (depth == 0) ? number_of_dex_registers_ + : GetInlineInfoAtDepth(stack_map, depth - 1).GetNumberOfDexRegisters(); + uint32_t last = GetInlineInfoAtDepth(stack_map, depth).GetNumberOfDexRegisters(); + DexRegisterMap map(last - first, DexRegisterLocation::Invalid()); + DecodeDexRegisterMap(stack_map.Row(), first, &map); + return map; + } + return DexRegisterMap(0, DexRegisterLocation::None()); } InlineInfo GetInlineInfo(size_t index) const { @@ -421,8 +441,6 @@ class CodeInfo { if (other.GetDexPc() == dex_pc && other.GetNativePcOffset(kRuntimeISA) == stack_map.GetNativePcOffset(kRuntimeISA)) { - DCHECK_EQ(other.GetDexRegisterMapIndex(), - stack_map.GetDexRegisterMapIndex()); if (i < e - 2) { // Make sure there are not three identical stack maps following each other. DCHECK_NE( @@ -469,23 +487,10 @@ class CodeInfo { const MethodInfo& method_info) const; private: - ALWAYS_INLINE DexRegisterMap DecodeDexRegisterMap(uint32_t mask_index, - uint32_t map_index, - uint32_t num_dex_registers) const { - DexRegisterMap map(map_index == StackMap::kNoValue ? 0 : num_dex_registers); - if (mask_index != StackMap::kNoValue) { - BitMemoryRegion mask = dex_register_masks_.GetBitMemoryRegion(mask_index); - num_dex_registers = std::min<uint32_t>(num_dex_registers, mask.size_in_bits()); - DexRegisterLocation* regs = map.data(); - for (uint32_t r = 0; r < mask.size_in_bits(); r++) { - if (mask.LoadBit(r) /* is_live */) { - DCHECK_LT(r, map.size()); - regs[r] = GetDexRegisterCatalogEntry(dex_register_maps_.Get(map_index++)); - } - } - } - return map; - } + // Scan backward to determine dex register locations at given stack map. + void DecodeDexRegisterMap(uint32_t stack_map_index, + uint32_t first_dex_register, + /*out*/ DexRegisterMap* map) const; void Decode(const uint8_t* data) { size_t non_header_size = DecodeUnsignedLeb128(&data); @@ -500,6 +505,7 @@ class CodeInfo { dex_register_masks_.Decode(region, &bit_offset); dex_register_maps_.Decode(region, &bit_offset); dex_register_catalog_.Decode(region, &bit_offset); + number_of_dex_registers_ = DecodeVarintBits(region, &bit_offset); CHECK_EQ(non_header_size, BitsToBytesRoundUp(bit_offset)) << "Invalid CodeInfo"; } @@ -512,6 +518,7 @@ class CodeInfo { BitTable<1> dex_register_masks_; BitTable<1> dex_register_maps_; BitTable<DexRegisterInfo::kCount> dex_register_catalog_; + uint32_t number_of_dex_registers_; // Excludes any inlined methods. friend class OatDumper; }; diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index c64e7bbca1..7b54b2fabc 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -76,7 +76,6 @@ jclass WellKnownClasses::java_util_Collections; jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; -jclass WellKnownClasses::libcore_util_EmptyArray; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer; @@ -137,6 +136,7 @@ jfieldID WellKnownClasses::java_lang_Throwable_detailMessage; jfieldID WellKnownClasses::java_lang_Throwable_stackTrace; jfieldID WellKnownClasses::java_lang_Throwable_stackState; jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions; +jfieldID WellKnownClasses::java_lang_Throwable_UNASSIGNED_STACK; jfieldID WellKnownClasses::java_nio_ByteBuffer_address; jfieldID WellKnownClasses::java_nio_ByteBuffer_hb; jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly; @@ -145,7 +145,6 @@ jfieldID WellKnownClasses::java_nio_ByteBuffer_offset; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress; jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST; -jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset; @@ -331,7 +330,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); - libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk"); org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer"); @@ -385,6 +383,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;"); java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;"); java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;"); + java_lang_Throwable_UNASSIGNED_STACK = CacheField(env, java_lang_Throwable, true, "UNASSIGNED_STACK", "[Ljava/lang/StackTraceElement;"); java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J"); java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B"); java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z"); @@ -393,7 +392,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I"); java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J"); java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;"); - libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;"); org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B"); org_apache_harmony_dalvik_ddmc_Chunk_length = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I"); org_apache_harmony_dalvik_ddmc_Chunk_offset = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I"); @@ -462,7 +460,6 @@ void WellKnownClasses::Clear() { java_nio_DirectByteBuffer = nullptr; libcore_reflect_AnnotationFactory = nullptr; libcore_reflect_AnnotationMember = nullptr; - libcore_util_EmptyArray = nullptr; org_apache_harmony_dalvik_ddmc_Chunk = nullptr; org_apache_harmony_dalvik_ddmc_DdmServer = nullptr; @@ -522,6 +519,7 @@ void WellKnownClasses::Clear() { java_lang_Throwable_stackTrace = nullptr; java_lang_Throwable_stackState = nullptr; java_lang_Throwable_suppressedExceptions = nullptr; + java_lang_Throwable_UNASSIGNED_STACK = nullptr; java_nio_ByteBuffer_address = nullptr; java_nio_ByteBuffer_hb = nullptr; java_nio_ByteBuffer_isReadOnly = nullptr; @@ -530,7 +528,6 @@ void WellKnownClasses::Clear() { java_nio_DirectByteBuffer_capacity = nullptr; java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr; java_util_Collections_EMPTY_LIST = nullptr; - libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_length = nullptr; org_apache_harmony_dalvik_ddmc_Chunk_offset = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index c81062f594..bed8770c45 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -86,7 +86,6 @@ struct WellKnownClasses { static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; static jclass libcore_reflect_AnnotationMember; - static jclass libcore_util_EmptyArray; static jclass org_apache_harmony_dalvik_ddmc_Chunk; static jclass org_apache_harmony_dalvik_ddmc_DdmServer; @@ -147,6 +146,7 @@ struct WellKnownClasses { static jfieldID java_lang_Throwable_stackTrace; static jfieldID java_lang_Throwable_stackState; static jfieldID java_lang_Throwable_suppressedExceptions; + static jfieldID java_lang_Throwable_UNASSIGNED_STACK; static jfieldID java_nio_ByteBuffer_address; static jfieldID java_nio_ByteBuffer_hb; static jfieldID java_nio_ByteBuffer_isReadOnly; @@ -156,7 +156,6 @@ struct WellKnownClasses { static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress; static jfieldID java_util_Collections_EMPTY_LIST; - static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset; diff --git a/test/551-checker-shifter-operand/build b/test/551-checker-shifter-operand/build deleted file mode 100644 index d85147f17b..0000000000 --- a/test/551-checker-shifter-operand/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 2018 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index b3e4a60e9a..8311b60df7 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -896,7 +896,7 @@ public class Main { } // Each test line below should see one merge. - /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) + /// CHECK-START-ARM: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp @@ -933,7 +933,7 @@ public class Main { /// CHECK-NOT: DataProcWithShifterOp // On ARM shifts by 1 are not merged. - /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) + /// CHECK-START-ARM: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) /// CHECK: Shl /// CHECK-NOT: Shl /// CHECK: Shr @@ -941,7 +941,7 @@ public class Main { /// CHECK: UShr /// CHECK-NOT: UShr - /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) + /// CHECK-START-ARM64: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp /// CHECK: DataProcWithShifterOp @@ -980,50 +980,98 @@ public class Main { /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp - /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) + /// CHECK-START-ARM64: long[] Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) /// CHECK-NOT: Shl /// CHECK-NOT: Shr /// CHECK-NOT: UShr - public static void $opt$validateShiftLong(long a, long b) { - assertLongEquals(a + $noinline$LongShl(b, 1), a + (b << 1)); - assertLongEquals(a + $noinline$LongShl(b, 6), a + (b << 6)); - assertLongEquals(a + $noinline$LongShl(b, 7), a + (b << 7)); - assertLongEquals(a + $noinline$LongShl(b, 8), a + (b << 8)); - assertLongEquals(a + $noinline$LongShl(b, 14), a + (b << 14)); - assertLongEquals(a + $noinline$LongShl(b, 15), a + (b << 15)); - assertLongEquals(a + $noinline$LongShl(b, 16), a + (b << 16)); - assertLongEquals(a + $noinline$LongShl(b, 30), a + (b << 30)); - assertLongEquals(a + $noinline$LongShl(b, 31), a + (b << 31)); - assertLongEquals(a + $noinline$LongShl(b, 32), a + (b << 32)); - assertLongEquals(a + $noinline$LongShl(b, 62), a + (b << 62)); - assertLongEquals(a + $noinline$LongShl(b, 63), a + (b << 63)); - - assertLongEquals(a - $noinline$LongShr(b, 1), a - (b >> 1)); - assertLongEquals(a - $noinline$LongShr(b, 6), a - (b >> 6)); - assertLongEquals(a - $noinline$LongShr(b, 7), a - (b >> 7)); - assertLongEquals(a - $noinline$LongShr(b, 8), a - (b >> 8)); - assertLongEquals(a - $noinline$LongShr(b, 14), a - (b >> 14)); - assertLongEquals(a - $noinline$LongShr(b, 15), a - (b >> 15)); - assertLongEquals(a - $noinline$LongShr(b, 16), a - (b >> 16)); - assertLongEquals(a - $noinline$LongShr(b, 30), a - (b >> 30)); - assertLongEquals(a - $noinline$LongShr(b, 31), a - (b >> 31)); - assertLongEquals(a - $noinline$LongShr(b, 32), a - (b >> 32)); - assertLongEquals(a - $noinline$LongShr(b, 62), a - (b >> 62)); - assertLongEquals(a - $noinline$LongShr(b, 63), a - (b >> 63)); - - assertLongEquals(a ^ $noinline$LongUshr(b, 1), a ^ (b >>> 1)); - assertLongEquals(a ^ $noinline$LongUshr(b, 6), a ^ (b >>> 6)); - assertLongEquals(a ^ $noinline$LongUshr(b, 7), a ^ (b >>> 7)); - assertLongEquals(a ^ $noinline$LongUshr(b, 8), a ^ (b >>> 8)); - assertLongEquals(a ^ $noinline$LongUshr(b, 14), a ^ (b >>> 14)); - assertLongEquals(a ^ $noinline$LongUshr(b, 15), a ^ (b >>> 15)); - assertLongEquals(a ^ $noinline$LongUshr(b, 16), a ^ (b >>> 16)); - assertLongEquals(a ^ $noinline$LongUshr(b, 30), a ^ (b >>> 30)); - assertLongEquals(a ^ $noinline$LongUshr(b, 31), a ^ (b >>> 31)); - assertLongEquals(a ^ $noinline$LongUshr(b, 32), a ^ (b >>> 32)); - assertLongEquals(a ^ $noinline$LongUshr(b, 62), a ^ (b >>> 62)); - assertLongEquals(a ^ $noinline$LongUshr(b, 63), a ^ (b >>> 63)); + public static long[] $opt$validateShiftLong(long a, long b) { + long[] results = new long[36]; + + results[0] = a + (b << 1); + results[1] = a + (b << 6); + results[2] = a + (b << 7); + results[3] = a + (b << 8); + results[4] = a + (b << 14); + results[5] = a + (b << 15); + results[6] = a + (b << 16); + results[7] = a + (b << 30); + results[8] = a + (b << 31); + results[9] = a + (b << 32); + results[10] = a + (b << 62); + results[11] = a + (b << 63); + + results[12] = a - (b >> 1); + results[13] = a - (b >> 6); + results[14] = a - (b >> 7); + results[15] = a - (b >> 8); + results[16] = a - (b >> 14); + results[17] = a - (b >> 15); + results[18] = a - (b >> 16); + results[19] = a - (b >> 30); + results[20] = a - (b >> 31); + results[21] = a - (b >> 32); + results[22] = a - (b >> 62); + results[23] = a - (b >> 63); + + results[24] = a ^ (b >>> 1); + results[25] = a ^ (b >>> 6); + results[26] = a ^ (b >>> 7); + results[27] = a ^ (b >>> 8); + results[28] = a ^ (b >>> 14); + results[29] = a ^ (b >>> 15); + results[30] = a ^ (b >>> 16); + results[31] = a ^ (b >>> 30); + results[32] = a ^ (b >>> 31); + results[33] = a ^ (b >>> 32); + results[34] = a ^ (b >>> 62); + results[35] = a ^ (b >>> 63); + + return results; + } + + public static void $opt$validateShiftLongAsserts(long a, long b) { + long[] results = $opt$validateShiftLong(a, b); + assertIntEquals(3 * 12, results.length); + + assertLongEquals(a + $noinline$LongShl(b, 1), results[0]); + assertLongEquals(a + $noinline$LongShl(b, 6), results[1]); + assertLongEquals(a + $noinline$LongShl(b, 7), results[2]); + assertLongEquals(a + $noinline$LongShl(b, 8), results[3]); + assertLongEquals(a + $noinline$LongShl(b, 14), results[4]); + assertLongEquals(a + $noinline$LongShl(b, 15), results[5]); + assertLongEquals(a + $noinline$LongShl(b, 16), results[6]); + assertLongEquals(a + $noinline$LongShl(b, 30), results[7]); + assertLongEquals(a + $noinline$LongShl(b, 31), results[8]); + assertLongEquals(a + $noinline$LongShl(b, 32), results[9]); + assertLongEquals(a + $noinline$LongShl(b, 62), results[10]); + assertLongEquals(a + $noinline$LongShl(b, 63), results[11]); + + assertLongEquals(a - $noinline$LongShr(b, 1), results[12]); + assertLongEquals(a - $noinline$LongShr(b, 6), results[13]); + assertLongEquals(a - $noinline$LongShr(b, 7), results[14]); + assertLongEquals(a - $noinline$LongShr(b, 8), results[15]); + assertLongEquals(a - $noinline$LongShr(b, 14), results[16]); + assertLongEquals(a - $noinline$LongShr(b, 15), results[17]); + assertLongEquals(a - $noinline$LongShr(b, 16), results[18]); + assertLongEquals(a - $noinline$LongShr(b, 30), results[19]); + assertLongEquals(a - $noinline$LongShr(b, 31), results[20]); + assertLongEquals(a - $noinline$LongShr(b, 32), results[21]); + assertLongEquals(a - $noinline$LongShr(b, 62), results[22]); + assertLongEquals(a - $noinline$LongShr(b, 63), results[23]); + + assertLongEquals(a ^ $noinline$LongUshr(b, 1), results[24]); + assertLongEquals(a ^ $noinline$LongUshr(b, 6), results[25]); + assertLongEquals(a ^ $noinline$LongUshr(b, 7), results[26]); + assertLongEquals(a ^ $noinline$LongUshr(b, 8), results[27]); + assertLongEquals(a ^ $noinline$LongUshr(b, 14), results[28]); + assertLongEquals(a ^ $noinline$LongUshr(b, 15), results[29]); + assertLongEquals(a ^ $noinline$LongUshr(b, 16), results[30]); + assertLongEquals(a ^ $noinline$LongUshr(b, 30), results[31]); + assertLongEquals(a ^ $noinline$LongUshr(b, 31), results[32]); + assertLongEquals(a ^ $noinline$LongUshr(b, 32), results[33]); + assertLongEquals(a ^ $noinline$LongUshr(b, 62), results[34]); + assertLongEquals(a ^ $noinline$LongUshr(b, 63), results[35]); } @@ -1072,7 +1120,7 @@ public class Main { $opt$validateExtendLong(inputs[i], inputs[j]); $opt$validateShiftInt((int)inputs[i], (int)inputs[j]); - $opt$validateShiftLong(inputs[i], inputs[j]); + $opt$validateShiftLongAsserts(inputs[i], inputs[j]); } } diff --git a/test/565-checker-doublenegbitwise/build b/test/565-checker-doublenegbitwise/build deleted file mode 100755 index 10ffcc537d..0000000000 --- a/test/565-checker-doublenegbitwise/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 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. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/565-checker-doublenegbitwise/smali/SmaliTests.smali b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali index 2e0802276e..ce691549ce 100644 --- a/test/565-checker-doublenegbitwise/smali/SmaliTests.smali +++ b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali @@ -403,3 +403,591 @@ sput-boolean v0, LSmaliTests;->doThrow:Z return-void .end method + + +# Test transformation of Not/Not/And into Or/Not. + +# Note: before the instruction_simplifier pass, Xor's are used instead of +# Not's (the simplification happens during the same pass). +## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (before) +## CHECK-DAG: <<P1:i\d+>> ParameterValue +## CHECK-DAG: <<P2:i\d+>> ParameterValue +## CHECK-DAG: <<CstM1:i\d+>> IntConstant -1 +## CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>] +## CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>] +## CHECK-DAG: <<And:i\d+>> And [<<Not1>>,<<Not2>>] +## CHECK-DAG: Return [<<And>>] + +## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after) +## CHECK-DAG: <<P1:i\d+>> ParameterValue +## CHECK-DAG: <<P2:i\d+>> ParameterValue +## CHECK-DAG: <<Or:i\d+>> Or [<<P1>>,<<P2>>] +## CHECK-DAG: <<Not:i\d+>> Not [<<Or>>] +## CHECK-DAG: Return [<<Not>>] + +## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after) +## CHECK-DAG: Not +## CHECK-NOT: Not + +## CHECK-START: int SmaliTests.$opt$noinline$andToOrV2(int, int) instruction_simplifier (after) +## CHECK-NOT: And + +# Original java source: +# +# public static int $opt$noinline$andToOr(int a, int b) { +# if (doThrow) throw new Error(); +# return ~a & ~b; +# } + +.method public static $opt$noinline$andToOrV2(II)I + .registers 4 + .param p0, "a" # I + .param p1, "b" # I + + .prologue + .line 85 + sget-boolean v0, LMain;->doThrow:Z + + if-eqz v0, :cond_a + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;-><init>()V + + throw v0 + + .line 86 + :cond_a + xor-int/lit8 v0, p0, -0x1 + + xor-int/lit8 v1, p1, -0x1 + + and-int/2addr v0, v1 + + return v0 +.end method + + +# Test transformation of Not/Not/And into Or/Not for boolean negations. +# Note that the graph before this instruction simplification pass does not +# contain `HBooleanNot` instructions. This is because this transformation +# follows the optimization of `HSelect` to `HBooleanNot` occurring in the +# same pass. + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_gvn (before) +## CHECK-DAG: <<P1:z\d+>> ParameterValue +## CHECK-DAG: <<P2:z\d+>> ParameterValue +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>] +## CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>] +## CHECK-DAG: <<And:i\d+>> And [<<Select1>>,<<Select2>>] +## CHECK-DAG: Return [<<And>>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <<Cond1:z\d+>> ParameterValue +## CHECK-DAG: <<Cond2:z\d+>> ParameterValue +## CHECK-DAG: <<Or:i\d+>> Or [<<Cond1>>,<<Cond2>>] +## CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<Or>>] +## CHECK-DAG: Return [<<BooleanNot>>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-DAG: BooleanNot +## CHECK-NOT: BooleanNot + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOrV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-NOT: And + +# Original java source: +# +# public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) { +# if (doThrow) throw new Error(); +# return !a & !b; +# } + +.method public static $opt$noinline$booleanAndToOrV2(ZZ)Z + .registers 5 + .param p0, "a" # Z + .param p1, "b" # Z + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 122 + sget-boolean v2, LMain;->doThrow:Z + + if-eqz v2, :cond_c + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;-><init>()V + + throw v0 + + .line 123 + :cond_c + if-nez p0, :cond_13 + + move v2, v0 + + :goto_f + if-nez p1, :cond_15 + + :goto_11 + and-int/2addr v0, v2 + + return v0 + + :cond_13 + move v2, v1 + + goto :goto_f + + :cond_15 + move v0, v1 + + goto :goto_11 +.end method + + +# Test transformation of Not/Not/Or into And/Not. + +# See note above. +# The second Xor has its arguments reversed for no obvious reason. +## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (before) +## CHECK-DAG: <<P1:j\d+>> ParameterValue +## CHECK-DAG: <<P2:j\d+>> ParameterValue +## CHECK-DAG: <<CstM1:j\d+>> LongConstant -1 +## CHECK-DAG: <<Not1:j\d+>> Xor [<<P1>>,<<CstM1>>] +## CHECK-DAG: <<Not2:j\d+>> Xor [<<CstM1>>,<<P2>>] +## CHECK-DAG: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>] +## CHECK-DAG: Return [<<Or>>] + +## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after) +## CHECK-DAG: <<P1:j\d+>> ParameterValue +## CHECK-DAG: <<P2:j\d+>> ParameterValue +## CHECK-DAG: <<And:j\d+>> And [<<P1>>,<<P2>>] +## CHECK-DAG: <<Not:j\d+>> Not [<<And>>] +## CHECK-DAG: Return [<<Not>>] + +## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after) +## CHECK-DAG: Not +## CHECK-NOT: Not + +## CHECK-START: long SmaliTests.$opt$noinline$orToAndV2(long, long) instruction_simplifier (after) +## CHECK-NOT: Or + +# Original java source: +# +# public static long $opt$noinline$orToAnd(long a, long b) { +# if (doThrow) throw new Error(); +# return ~a | ~b; +# } + +.method public static $opt$noinline$orToAndV2(JJ)J + .registers 8 + .param p0, "a" # J + .param p2, "b" # J + + .prologue + const-wide/16 v2, -0x1 + + .line 156 + sget-boolean v0, LMain;->doThrow:Z + + if-eqz v0, :cond_c + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;-><init>()V + + throw v0 + + .line 157 + :cond_c + xor-long v0, p0, v2 + + xor-long/2addr v2, p2 + + or-long/2addr v0, v2 + + return-wide v0 +.end method + +# Test transformation of Not/Not/Or into Or/And for boolean negations. +# Note that the graph before this instruction simplification pass does not +# contain `HBooleanNot` instructions. This is because this transformation +# follows the optimization of `HSelect` to `HBooleanNot` occurring in the +# same pass. + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_gvn (before) +## CHECK-DAG: <<P1:z\d+>> ParameterValue +## CHECK-DAG: <<P2:z\d+>> ParameterValue +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>] +## CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>] +## CHECK-DAG: <<Or:i\d+>> Or [<<Select1>>,<<Select2>>] +## CHECK-DAG: Return [<<Or>>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <<Cond1:z\d+>> ParameterValue +## CHECK-DAG: <<Cond2:z\d+>> ParameterValue +## CHECK-DAG: <<And:i\d+>> And [<<Cond1>>,<<Cond2>>] +## CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<And>>] +## CHECK-DAG: Return [<<BooleanNot>>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-DAG: BooleanNot +## CHECK-NOT: BooleanNot + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAndV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-NOT: Or + +# Original java source: +# +# public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) { +# if (doThrow) throw new Error(); +# return !a | !b; +# } + +.method public static $opt$noinline$booleanOrToAndV2(ZZ)Z + .registers 5 + .param p0, "a" # Z + .param p1, "b" # Z + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 193 + sget-boolean v2, LMain;->doThrow:Z + + if-eqz v2, :cond_c + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;-><init>()V + + throw v0 + + .line 194 + :cond_c + if-nez p0, :cond_13 + + move v2, v0 + + :goto_f + if-nez p1, :cond_15 + + :goto_11 + or-int/2addr v0, v2 + + return v0 + + :cond_13 + move v2, v1 + + goto :goto_f + + :cond_15 + move v0, v1 + + goto :goto_11 +.end method + + +# Test that the transformation copes with inputs being separated from the +# bitwise operations. +# This is a regression test. The initial logic was inserting the new bitwise +# operation incorrectly. + +## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (before) +## CHECK-DAG: <<P1:i\d+>> ParameterValue +## CHECK-DAG: <<P2:i\d+>> ParameterValue +## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 +## CHECK-DAG: <<CstM1:i\d+>> IntConstant -1 +## CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>] +## CHECK-DAG: <<Not1:i\d+>> Xor [<<AddP1>>,<<CstM1>>] +## CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>] +## CHECK-DAG: <<Not2:i\d+>> Xor [<<AddP2>>,<<CstM1>>] +## CHECK-DAG: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>] +## CHECK-DAG: Return [<<Or>>] + +## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after) +## CHECK-DAG: <<P1:i\d+>> ParameterValue +## CHECK-DAG: <<P2:i\d+>> ParameterValue +## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 +## CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>] +## CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>] +## CHECK-DAG: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>] +## CHECK-DAG: <<Not:i\d+>> Not [<<And>>] +## CHECK-DAG: Return [<<Not>>] + +## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after) +## CHECK-DAG: Not +## CHECK-NOT: Not + +## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAwayV2(int, int) instruction_simplifier (after) +## CHECK-NOT: Or + +# Original java source: +# +# public static int $opt$noinline$regressInputsAway(int a, int b) { +# if (doThrow) throw new Error(); +# int a1 = a + 1; +# int not_a1 = ~a1; +# int b1 = b + 1; +# int not_b1 = ~b1; +# return not_a1 | not_b1; +# } + +.method public static $opt$noinline$regressInputsAwayV2(II)I + .registers 7 + .param p0, "a" # I + .param p1, "b" # I + + .prologue + .line 234 + sget-boolean v4, LMain;->doThrow:Z + + if-eqz v4, :cond_a + + new-instance v4, Ljava/lang/Error; + + invoke-direct {v4}, Ljava/lang/Error;-><init>()V + + throw v4 + + .line 235 + :cond_a + add-int/lit8 v0, p0, 0x1 + + .line 236 + .local v0, "a1":I + xor-int/lit8 v2, v0, -0x1 + + .line 237 + .local v2, "not_a1":I + add-int/lit8 v1, p1, 0x1 + + .line 238 + .local v1, "b1":I + xor-int/lit8 v3, v1, -0x1 + + .line 239 + .local v3, "not_b1":I + or-int v4, v2, v3 + + return v4 +.end method + + +# Test transformation of Not/Not/Xor into Xor. + +# See first note above. +## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (before) +## CHECK-DAG: <<P1:i\d+>> ParameterValue +## CHECK-DAG: <<P2:i\d+>> ParameterValue +## CHECK-DAG: <<CstM1:i\d+>> IntConstant -1 +## CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>] +## CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>] +## CHECK-DAG: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>] +## CHECK-DAG: Return [<<Xor>>] + +## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (after) +## CHECK-DAG: <<P1:i\d+>> ParameterValue +## CHECK-DAG: <<P2:i\d+>> ParameterValue +## CHECK-DAG: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>] +## CHECK-DAG: Return [<<Xor>>] + +## CHECK-START: int SmaliTests.$opt$noinline$notXorToXorV2(int, int) instruction_simplifier (after) +## CHECK-NOT: Not + +# Original java source: +# +# public static int $opt$noinline$notXorToXor(int a, int b) { +# if (doThrow) throw new Error(); +# return ~a ^ ~b; +# } + +.method public static $opt$noinline$notXorToXorV2(II)I + .registers 4 + .param p0, "a" # I + .param p1, "b" # I + + .prologue + .line 266 + sget-boolean v0, LMain;->doThrow:Z + + if-eqz v0, :cond_a + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;-><init>()V + + throw v0 + + .line 267 + :cond_a + xor-int/lit8 v0, p0, -0x1 + + xor-int/lit8 v1, p1, -0x1 + + xor-int/2addr v0, v1 + + return v0 +.end method + + +# Test transformation of Not/Not/Xor into Xor for boolean negations. +# Note that the graph before this instruction simplification pass does not +# contain `HBooleanNot` instructions. This is because this transformation +# follows the optimization of `HSelect` to `HBooleanNot` occurring in the +# same pass. + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_gvn (before) +## CHECK-DAG: <<P1:z\d+>> ParameterValue +## CHECK-DAG: <<P2:z\d+>> ParameterValue +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>] +## CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>] +## CHECK-DAG: <<Xor:i\d+>> Xor [<<Select1>>,<<Select2>>] +## CHECK-DAG: Return [<<Xor>>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_gvn (after) +## CHECK-DAG: <<Cond1:z\d+>> ParameterValue +## CHECK-DAG: <<Cond2:z\d+>> ParameterValue +## CHECK-DAG: <<Xor:i\d+>> Xor [<<Cond1>>,<<Cond2>>] +## CHECK-DAG: Return [<<Xor>>] + +## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXorV2(boolean, boolean) instruction_simplifier$after_bce (after) +## CHECK-NOT: BooleanNot + +# Original java source: +# +# public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) { +# if (doThrow) throw new Error(); +# return !a ^ !b; +# } + +.method public static $opt$noinline$booleanNotXorToXorV2(ZZ)Z + .registers 5 + .param p0, "a" # Z + .param p1, "b" # Z + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 298 + sget-boolean v2, LMain;->doThrow:Z + + if-eqz v2, :cond_c + + new-instance v0, Ljava/lang/Error; + + invoke-direct {v0}, Ljava/lang/Error;-><init>()V + + throw v0 + + .line 299 + :cond_c + if-nez p0, :cond_13 + + move v2, v0 + + :goto_f + if-nez p1, :cond_15 + + :goto_11 + xor-int/2addr v0, v2 + + return v0 + + :cond_13 + move v2, v1 + + goto :goto_f + + :cond_15 + move v0, v1 + + goto :goto_11 +.end method + + +# Check that no transformation is done when one Not has multiple uses. + +## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (before) +## CHECK-DAG: <<P1:i\d+>> ParameterValue +## CHECK-DAG: <<P2:i\d+>> ParameterValue +## CHECK-DAG: <<CstM1:i\d+>> IntConstant -1 +## CHECK-DAG: <<One:i\d+>> IntConstant 1 +## CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>] +## CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>] +## CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>] +## CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>] +## CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>] +## CHECK-DAG: Return [<<Add>>] + +## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (after) +## CHECK-DAG: <<P1:i\d+>> ParameterValue +## CHECK-DAG: <<P2:i\d+>> ParameterValue +## CHECK-DAG: <<One:i\d+>> IntConstant 1 +## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>] +## CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>] +## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>] +## CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>] +## CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>] +## CHECK-DAG: Return [<<Add>>] + +## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUsesV2(int, int) instruction_simplifier (after) +## CHECK-NOT: Or + +# Original java source: +# +# public static int $opt$noinline$notMultipleUses(int a, int b) { +# if (doThrow) throw new Error(); +# int tmp = ~b; +# return (tmp & 0x1) + (~a & tmp); +# } + +.method public static $opt$noinline$notMultipleUsesV2(II)I + .registers 5 + .param p0, "a" # I + .param p1, "b" # I + + .prologue + .line 333 + sget-boolean v1, LMain;->doThrow:Z + + if-eqz v1, :cond_a + + new-instance v1, Ljava/lang/Error; + + invoke-direct {v1}, Ljava/lang/Error;-><init>()V + + throw v1 + + .line 334 + :cond_a + xor-int/lit8 v0, p1, -0x1 + + .line 335 + .local v0, "tmp":I + and-int/lit8 v1, v0, 0x1 + + xor-int/lit8 v2, p0, -0x1 + + and-int/2addr v2, v0 + + add-int/2addr v1, v2 + + return v1 +.end method diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java index e36a2bab40..5121569632 100644 --- a/test/565-checker-doublenegbitwise/src/Main.java +++ b/test/565-checker-doublenegbitwise/src/Main.java @@ -52,305 +52,22 @@ public class Main { } } - /** - * Test transformation of Not/Not/And into Or/Not. - */ - - // Note: before the instruction_simplifier pass, Xor's are used instead of - // Not's (the simplification happens during the same pass). - /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before) - /// CHECK-DAG: <<P1:i\d+>> ParameterValue - /// CHECK-DAG: <<P2:i\d+>> ParameterValue - /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1 - /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>] - /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>] - /// CHECK-DAG: <<And:i\d+>> And [<<Not1>>,<<Not2>>] - /// CHECK-DAG: Return [<<And>>] - - /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after) - /// CHECK-DAG: <<P1:i\d+>> ParameterValue - /// CHECK-DAG: <<P2:i\d+>> ParameterValue - /// CHECK-DAG: <<Or:i\d+>> Or [<<P1>>,<<P2>>] - /// CHECK-DAG: <<Not:i\d+>> Not [<<Or>>] - /// CHECK-DAG: Return [<<Not>>] - - /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after) - /// CHECK-DAG: Not - /// CHECK-NOT: Not - - /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after) - /// CHECK-NOT: And - - public static int $opt$noinline$andToOr(int a, int b) { - if (doThrow) throw new Error(); - return ~a & ~b; - } - - /** - * Test transformation of Not/Not/And into Or/Not for boolean negations. - * Note that the graph before this instruction simplification pass does not - * contain `HBooleanNot` instructions. This is because this transformation - * follows the optimization of `HSelect` to `HBooleanNot` occurring in the - * same pass. - */ - - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (before) - /// CHECK-DAG: <<P1:z\d+>> ParameterValue - /// CHECK-DAG: <<P2:z\d+>> ParameterValue - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>] - /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>] - /// CHECK-DAG: <<And:i\d+>> And [<<Select1>>,<<Select2>>] - /// CHECK-DAG: Return [<<And>>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_gvn (after) - /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue - /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue - /// CHECK-DAG: <<Or:i\d+>> Or [<<Cond1>>,<<Cond2>>] - /// CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<Or>>] - /// CHECK-DAG: Return [<<BooleanNot>>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-DAG: BooleanNot - /// CHECK-NOT: BooleanNot - - /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-NOT: And - - public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) { - if (doThrow) throw new Error(); - return !a & !b; - } - - /** - * Test transformation of Not/Not/Or into And/Not. - */ - - // See note above. - // The second Xor has its arguments reversed for no obvious reason. - /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before) - /// CHECK-DAG: <<P1:j\d+>> ParameterValue - /// CHECK-DAG: <<P2:j\d+>> ParameterValue - /// CHECK-DAG: <<CstM1:j\d+>> LongConstant -1 - /// CHECK-DAG: <<Not1:j\d+>> Xor [<<P1>>,<<CstM1>>] - /// CHECK-DAG: <<Not2:j\d+>> Xor [<<CstM1>>,<<P2>>] - /// CHECK-DAG: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>] - /// CHECK-DAG: Return [<<Or>>] - - /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after) - /// CHECK-DAG: <<P1:j\d+>> ParameterValue - /// CHECK-DAG: <<P2:j\d+>> ParameterValue - /// CHECK-DAG: <<And:j\d+>> And [<<P1>>,<<P2>>] - /// CHECK-DAG: <<Not:j\d+>> Not [<<And>>] - /// CHECK-DAG: Return [<<Not>>] - - /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after) - /// CHECK-DAG: Not - /// CHECK-NOT: Not - - /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after) - /// CHECK-NOT: Or - - public static long $opt$noinline$orToAnd(long a, long b) { - if (doThrow) throw new Error(); - return ~a | ~b; - } - - /** - * Test transformation of Not/Not/Or into Or/And for boolean negations. - * Note that the graph before this instruction simplification pass does not - * contain `HBooleanNot` instructions. This is because this transformation - * follows the optimization of `HSelect` to `HBooleanNot` occurring in the - * same pass. - */ - - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (before) - /// CHECK-DAG: <<P1:z\d+>> ParameterValue - /// CHECK-DAG: <<P2:z\d+>> ParameterValue - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>] - /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>] - /// CHECK-DAG: <<Or:i\d+>> Or [<<Select1>>,<<Select2>>] - /// CHECK-DAG: Return [<<Or>>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_gvn (after) - /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue - /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue - /// CHECK-DAG: <<And:i\d+>> And [<<Cond1>>,<<Cond2>>] - /// CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<And>>] - /// CHECK-DAG: Return [<<BooleanNot>>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-DAG: BooleanNot - /// CHECK-NOT: BooleanNot - - /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-NOT: Or - - public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) { - if (doThrow) throw new Error(); - return !a | !b; - } - - /** - * Test that the transformation copes with inputs being separated from the - * bitwise operations. - * This is a regression test. The initial logic was inserting the new bitwise - * operation incorrectly. - */ - - /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before) - /// CHECK-DAG: <<P1:i\d+>> ParameterValue - /// CHECK-DAG: <<P2:i\d+>> ParameterValue - /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1 - /// CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>] - /// CHECK-DAG: <<Not1:i\d+>> Xor [<<AddP1>>,<<CstM1>>] - /// CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>] - /// CHECK-DAG: <<Not2:i\d+>> Xor [<<AddP2>>,<<CstM1>>] - /// CHECK-DAG: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>] - /// CHECK-DAG: Return [<<Or>>] - - /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after) - /// CHECK-DAG: <<P1:i\d+>> ParameterValue - /// CHECK-DAG: <<P2:i\d+>> ParameterValue - /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>] - /// CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>] - /// CHECK-DAG: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>] - /// CHECK-DAG: <<Not:i\d+>> Not [<<And>>] - /// CHECK-DAG: Return [<<Not>>] - - /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after) - /// CHECK-DAG: Not - /// CHECK-NOT: Not - - /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after) - /// CHECK-NOT: Or - - public static int $opt$noinline$regressInputsAway(int a, int b) { - if (doThrow) throw new Error(); - int a1 = a + 1; - int not_a1 = ~a1; - int b1 = b + 1; - int not_b1 = ~b1; - return not_a1 | not_b1; - } - - /** - * Test transformation of Not/Not/Xor into Xor. - */ - - // See first note above. - /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before) - /// CHECK-DAG: <<P1:i\d+>> ParameterValue - /// CHECK-DAG: <<P2:i\d+>> ParameterValue - /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1 - /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>] - /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>] - /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>] - /// CHECK-DAG: Return [<<Xor>>] - - /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after) - /// CHECK-DAG: <<P1:i\d+>> ParameterValue - /// CHECK-DAG: <<P2:i\d+>> ParameterValue - /// CHECK-DAG: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>] - /// CHECK-DAG: Return [<<Xor>>] - - /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after) - /// CHECK-NOT: Not - - public static int $opt$noinline$notXorToXor(int a, int b) { - if (doThrow) throw new Error(); - return ~a ^ ~b; - } - - /** - * Test transformation of Not/Not/Xor into Xor for boolean negations. - * Note that the graph before this instruction simplification pass does not - * contain `HBooleanNot` instructions. This is because this transformation - * follows the optimization of `HSelect` to `HBooleanNot` occurring in the - * same pass. - */ - - /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (before) - /// CHECK-DAG: <<P1:z\d+>> ParameterValue - /// CHECK-DAG: <<P2:z\d+>> ParameterValue - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>] - /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>] - /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Select1>>,<<Select2>>] - /// CHECK-DAG: Return [<<Xor>>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_gvn (after) - /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue - /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue - /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Cond1>>,<<Cond2>>] - /// CHECK-DAG: Return [<<Xor>>] - - /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after) - /// CHECK-NOT: BooleanNot - - public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) { - if (doThrow) throw new Error(); - return !a ^ !b; - } - - /** - * Check that no transformation is done when one Not has multiple uses. - */ - - /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before) - /// CHECK-DAG: <<P1:i\d+>> ParameterValue - /// CHECK-DAG: <<P2:i\d+>> ParameterValue - /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1 - /// CHECK-DAG: <<One:i\d+>> IntConstant 1 - /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>] - /// CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>] - /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>] - /// CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>] - /// CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>] - /// CHECK-DAG: Return [<<Add>>] - - /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after) - /// CHECK-DAG: <<P1:i\d+>> ParameterValue - /// CHECK-DAG: <<P2:i\d+>> ParameterValue - /// CHECK-DAG: <<One:i\d+>> IntConstant 1 - /// CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>] - /// CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>] - /// CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>] - /// CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>] - /// CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>] - /// CHECK-DAG: Return [<<Add>>] - - /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after) - /// CHECK-NOT: Or - - public static int $opt$noinline$notMultipleUses(int a, int b) { - if (doThrow) throw new Error(); - int tmp = ~b; - return (tmp & 0x1) + (~a & tmp); - } - - public static void main(String[] args) { - assertIntEquals(~0xff, $opt$noinline$andToOr(0xf, 0xff)); + public static void main(String[] args) throws Exception { + assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOrV2", int.class, 0xf, 0xff)); assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOr", int.class, 0xf, 0xff)); - assertEquals(true, $opt$noinline$booleanAndToOr(false, false)); + assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOrV2", boolean.class, false, false)); assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOr", boolean.class, false, false)); - assertLongEquals(~0xf, $opt$noinline$orToAnd(0xf, 0xff)); + assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAndV2", long.class, 0xfL, 0xffL)); assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAnd", long.class, 0xfL, 0xffL)); - assertEquals(false, $opt$noinline$booleanOrToAnd(true, true)); + assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAndV2", boolean.class, true, true)); assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAnd", boolean.class, true, true)); - assertIntEquals(-1, $opt$noinline$regressInputsAway(0xf, 0xff)); + assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAwayV2", int.class, 0xf, 0xff)); assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAway", int.class, 0xf, 0xff)); - assertIntEquals(0xf0, $opt$noinline$notXorToXor(0xf, 0xff)); + assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXorV2", int.class, 0xf, 0xff)); assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXor", int.class, 0xf, 0xff)); - assertEquals(true, $opt$noinline$booleanNotXorToXor(true, false)); + assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXorV2", boolean.class, true, false)); assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXor", boolean.class, true, false)); - assertIntEquals(~0xff, $opt$noinline$notMultipleUses(0xf, 0xff)); + assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUsesV2", int.class, 0xf, 0xff)); assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUses", int.class, 0xf, 0xff)); } } diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 66ed0d0004..1f4b829989 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -140,3 +140,6 @@ ANDROID_JAVA_TOOLCHAIN = os.path.join(ANDROID_BUILD_TOP, # include platform prebuilt java, javac, etc in $PATH. os.environ['PATH'] = ANDROID_JAVA_TOOLCHAIN + ':' + os.environ['PATH'] + +DIST_DIR = _get_build_var('DIST_DIR') +SOONG_OUT_DIR = _get_build_var('SOONG_OUT_DIR') diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 2d1398e3fe..044e8dc12a 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -52,6 +52,7 @@ import json import multiprocessing import os import re +import shutil import subprocess import sys import tempfile @@ -1007,6 +1008,9 @@ def main(): build_command += ' -C ' + env.ANDROID_BUILD_TOP build_command += ' ' + build_targets if subprocess.call(build_command.split()): + # Debugging for b/62653020 + if env.DIST_DIR: + shutil.copyfile(env.SOONG_OUT_DIR + '/build.ninja', env.DIST_DIR + '/soong.ninja') sys.exit(1) if user_requested_tests: test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_tests,)) diff --git a/tools/build/var_list b/tools/build/var_list index bb005cf77c..98a54725da 100644 --- a/tools/build/var_list +++ b/tools/build/var_list @@ -34,3 +34,6 @@ HOST_PREFER_32_BIT HOST_OUT_EXECUTABLES ANDROID_JAVA_TOOLCHAIN +# b/62653020 +DIST_DIR +SOONG_OUT_DIR diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk index f8463c1c33..2faa577262 100644 --- a/tools/veridex/Android.mk +++ b/tools/veridex/Android.mk @@ -22,13 +22,13 @@ LOCAL_PATH := $(call my-dir) system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex $(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 $(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(DX) - $(transform-classes-d8.jar-to-dex) + $(transform-classes.jar-to-dex) oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex $(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000 $(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX) - $(transform-classes-d8.jar-to-dex) + $(transform-classes.jar-to-dex) app_compat_lists := \ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \ |