summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/stack_map_stream.cc92
-rw-r--r--compiler/optimizing/stack_map_stream.h8
-rw-r--r--compiler/optimizing/stack_map_test.cc21
-rw-r--r--libartbase/base/bit_memory_region.h14
-rw-r--r--oatdump/oatdump.cc10
-rw-r--r--patchoat/patchoat_test.cc41
-rw-r--r--runtime/common_throws.cc6
-rw-r--r--runtime/dex_register_location.h6
-rw-r--r--runtime/jni/java_vm_ext.cc6
-rw-r--r--runtime/stack.cc4
-rw-r--r--runtime/stack_map.cc65
-rw-r--r--runtime/stack_map.h85
-rw-r--r--runtime/well_known_classes.cc9
-rw-r--r--runtime/well_known_classes.h3
-rw-r--r--test/551-checker-shifter-operand/build20
-rw-r--r--test/551-checker-shifter-operand/src/Main.java136
-rwxr-xr-xtest/565-checker-doublenegbitwise/build20
-rw-r--r--test/565-checker-doublenegbitwise/smali/SmaliTests.smali588
-rw-r--r--test/565-checker-doublenegbitwise/src/Main.java301
-rw-r--r--test/testrunner/env.py3
-rwxr-xr-xtest/testrunner/testrunner.py4
-rw-r--r--tools/build/var_list3
-rw-r--r--tools/veridex/Android.mk4
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) \