summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-09-14 23:00:54 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-09-14 23:00:54 +0000
commit652d1a4d8b916591b094b39a5872066d7e66f228 (patch)
tree5aae4e675fb1f150d51640bbb5d2993b612140bb
parentb5a4747a0e5f4169902a3bf8b30368c144dccab1 (diff)
parent46c6617f075520fa69b0f0dd52006a7e10062876 (diff)
Snap for 7731944 from 46c6617f075520fa69b0f0dd52006a7e10062876 to sc-v2-release
Change-Id: I269d313fb3f83322d28007f4245b3996dff097ae
-rw-r--r--compiler/optimizing/execution_subgraph.cc13
-rw-r--r--compiler/optimizing/load_store_elimination.h2
-rw-r--r--libdexfile/dex/dex_file.h2
-rw-r--r--runtime/oat.h4
-rw-r--r--runtime/oat_file.cc110
-rw-r--r--runtime/vdex_file.cc3
-rw-r--r--runtime/vdex_file.h5
-rw-r--r--test/530-checker-lse/src/Main.java257
-rw-r--r--test/639-checker-code-sinking/src/Main.java15
-rw-r--r--test/826-infinite-loop/expected-stderr.txt0
-rw-r--r--test/826-infinite-loop/expected-stdout.txt0
-rw-r--r--test/826-infinite-loop/info.txt2
-rw-r--r--test/826-infinite-loop/src/Main.java26
-rw-r--r--test/828-partial-lse/expected-stderr.txt0
-rw-r--r--test/828-partial-lse/expected-stdout.txt0
-rw-r--r--test/828-partial-lse/info.txt2
-rw-r--r--test/828-partial-lse/src/Main.java53
17 files changed, 199 insertions, 295 deletions
diff --git a/compiler/optimizing/execution_subgraph.cc b/compiler/optimizing/execution_subgraph.cc
index 6d105668c0..66fdfcda5b 100644
--- a/compiler/optimizing/execution_subgraph.cc
+++ b/compiler/optimizing/execution_subgraph.cc
@@ -86,12 +86,6 @@ void ExecutionSubgraph::Prune() {
ScopedArenaVector<std::bitset<kMaxFilterableSuccessors>> results(
graph_->GetBlocks().size(), temporaries.Adapter(kArenaAllocLSA));
unreachable_blocks_.ClearAllBits();
- // TODO We should support infinite loops as well.
- if (UNLIKELY(graph_->GetExitBlock() == nullptr)) {
- // Infinite loop
- valid_ = false;
- return;
- }
// Fills up the 'results' map with what we need to add to update
// allowed_successors in order to prune sink nodes.
bool start_reaches_end = false;
@@ -170,8 +164,11 @@ void ExecutionSubgraph::Prune() {
<< "current path size: " << current_path.size()
<< " cur_block id: " << cur_block->GetBlockId() << " entry id "
<< graph_->GetEntryBlock()->GetBlockId();
- DCHECK(!visiting.IsBitSet(id))
- << "Somehow ended up in a loop! This should have been caught before now! " << id;
+ if (visiting.IsBitSet(id)) {
+ // TODO We should support infinite loops as well.
+ start_reaches_end = false;
+ break;
+ }
std::bitset<kMaxFilterableSuccessors>& result = results[id];
if (cur_block == graph_->GetExitBlock()) {
start_reaches_end = true;
diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h
index e73ef5ef34..6ad2eb2c51 100644
--- a/compiler/optimizing/load_store_elimination.h
+++ b/compiler/optimizing/load_store_elimination.h
@@ -27,7 +27,7 @@ class LoadStoreElimination : public HOptimization {
public:
// Whether or not we should attempt partial Load-store-elimination which
// requires additional blocks and predicated instructions.
- static constexpr bool kEnablePartialLSE = true;
+ static constexpr bool kEnablePartialLSE = false;
// Controls whether to enable VLOG(compiler) logs explaining the transforms taking place.
static constexpr bool kVerboseLoggingMode = false;
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 5363b00c24..e3027fc95f 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -738,7 +738,7 @@ class DexFile {
}
// Used by oat writer.
- void SetOatDexFile(OatDexFile* oat_dex_file) const {
+ void SetOatDexFile(const OatDexFile* oat_dex_file) const {
oat_dex_file_ = oat_dex_file;
}
diff --git a/runtime/oat.h b/runtime/oat.h
index ab45b84888..31a328d979 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
- // Last oat version changed reason: Apex versions in key/value store.
- static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '9', '5', '\0' } };
+ // Last oat version changed reason: Disable partial LSE b/197818595.
+ static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '9', '9', '\0' } };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 8f653c2282..14e7a1b60e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -168,7 +168,7 @@ class OatFileBase : public OatFile {
bool Setup(int zip_fd, ArrayRef<const std::string> dex_filenames, std::string* error_msg);
- void Setup(const std::vector<const DexFile*>& dex_files);
+ bool Setup(const std::vector<const DexFile*>& dex_files, std::string* error_msg);
// Setters exposed for ElfOatFile.
@@ -476,18 +476,71 @@ static bool ReadIndexBssMapping(OatFile* oat_file,
return true;
}
-void OatFileBase::Setup(const std::vector<const DexFile*>& dex_files) {
+static bool ComputeAndCheckTypeLookupTableData(const DexFile::Header& header,
+ const uint8_t* type_lookup_table_start,
+ const VdexFile* vdex_file,
+ const uint8_t** type_lookup_table_data,
+ std::string* error_msg) {
+ if (type_lookup_table_start == nullptr ||
+ reinterpret_cast<const uint32_t*>(type_lookup_table_start)[0] == 0) {
+ *type_lookup_table_data = nullptr;
+ return true;
+ }
+
+ *type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
+ size_t expected_table_size = TypeLookupTable::RawDataLength(header.class_defs_size_);
+ size_t found_size = reinterpret_cast<const uint32_t*>(type_lookup_table_start)[0];
+ if (UNLIKELY(found_size != expected_table_size)) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' unexpected type lookup table size: found %zu, expected %zu",
+ vdex_file->GetName().c_str(),
+ found_size,
+ expected_table_size);
+ return false;
+ }
+ if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found invalid type lookup table pointer %p not in [%p, %p]",
+ vdex_file->GetName().c_str(),
+ type_lookup_table_data,
+ vdex_file->Begin(),
+ vdex_file->End());
+ return false;
+ }
+ if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data + expected_table_size - 1))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found overflowing type lookup table %p not in [%p, %p]",
+ vdex_file->GetName().c_str(),
+ type_lookup_table_data + expected_table_size,
+ vdex_file->Begin(),
+ vdex_file->End());
+ return false;
+ }
+ if (UNLIKELY(!IsAligned<4>(type_lookup_table_start))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found invalid type lookup table alignment %p",
+ vdex_file->GetName().c_str(),
+ type_lookup_table_start);
+ return false;
+ }
+ return true;
+}
+
+bool OatFileBase::Setup(const std::vector<const DexFile*>& dex_files, std::string* error_msg) {
uint32_t i = 0;
const uint8_t* type_lookup_table_start = nullptr;
for (const DexFile* dex_file : dex_files) {
- type_lookup_table_start = vdex_->GetNextTypeLookupTableData(type_lookup_table_start, i++);
std::string dex_location = dex_file->GetLocation();
std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location.c_str());
+ type_lookup_table_start = vdex_->GetNextTypeLookupTableData(type_lookup_table_start, i++);
const uint8_t* type_lookup_table_data = nullptr;
- if (type_lookup_table_start != nullptr &&
- (reinterpret_cast<uint32_t*>(type_lookup_table_start[0]) != 0)) {
- type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
+ if (!ComputeAndCheckTypeLookupTableData(dex_file->GetHeader(),
+ type_lookup_table_start,
+ vdex_.get(),
+ &type_lookup_table_data,
+ error_msg)) {
+ return false;
}
// Create an OatDexFile and add it to the owning container.
OatDexFile* oat_dex_file = new OatDexFile(
@@ -497,7 +550,6 @@ void OatFileBase::Setup(const std::vector<const DexFile*>& dex_files) {
dex_location,
canonical_location,
type_lookup_table_data);
- dex_file->SetOatDexFile(oat_dex_file);
oat_dex_files_storage_.push_back(oat_dex_file);
// Add the location and canonical location (if different) to the oat_dex_files_ table.
@@ -508,6 +560,11 @@ void OatFileBase::Setup(const std::vector<const DexFile*>& dex_files) {
oat_dex_files_.Put(canonical_key, oat_dex_file);
}
}
+ // Now that we've created all the OatDexFile, update the dex files.
+ for (i = 0; i < dex_files.size(); ++i) {
+ dex_files[i]->SetOatDexFile(oat_dex_files_storage_[i]);
+ }
+ return true;
}
bool OatFileBase::Setup(int zip_fd,
@@ -1583,7 +1640,11 @@ class OatFileBackedByVdex final : public OatFileBase {
oat_file->SetVdex(vdex_file.release());
oat_file->SetupHeader(dex_files.size());
// Initialize OatDexFiles.
- oat_file->Setup(dex_files);
+ std::string error_msg;
+ if (!oat_file->Setup(dex_files, &error_msg)) {
+ LOG(WARNING) << "Could not create in-memory vdex file: " << error_msg;
+ return nullptr;
+ }
return oat_file.release();
}
@@ -1601,6 +1662,25 @@ class OatFileBackedByVdex final : public OatFileBase {
for (const uint8_t* dex_file_start = vdex_file->GetNextDexFileData(nullptr, i);
dex_file_start != nullptr;
dex_file_start = vdex_file->GetNextDexFileData(dex_file_start, ++i)) {
+ const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_start);
+ if (UNLIKELY(!vdex_file->Contains(dex_file_start))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found invalid dex file pointer %p not in [%p, %p]",
+ dex_location.c_str(),
+ dex_file_start,
+ vdex_file->Begin(),
+ vdex_file->End());
+ return nullptr;
+ }
+ if (UNLIKELY(!vdex_file->Contains(dex_file_start + header->file_size_ - 1))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found overflowing dex file %p not in [%p, %p]",
+ dex_location.c_str(),
+ dex_file_start + header->file_size_,
+ vdex_file->Begin(),
+ vdex_file->End());
+ return nullptr;
+ }
if (UNLIKELY(!DexFileLoader::IsVersionAndMagicValid(dex_file_start))) {
*error_msg =
StringPrintf("In vdex file '%s' found dex file with invalid dex file version",
@@ -1612,10 +1692,14 @@ class OatFileBackedByVdex final : public OatFileBase {
std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(location.c_str());
type_lookup_table_start = vdex_file->GetNextTypeLookupTableData(type_lookup_table_start, i);
const uint8_t* type_lookup_table_data = nullptr;
- if (type_lookup_table_start != nullptr &&
- (reinterpret_cast<uint32_t*>(type_lookup_table_start[0]) != 0)) {
- type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
+ if (!ComputeAndCheckTypeLookupTableData(*header,
+ type_lookup_table_start,
+ vdex_file,
+ &type_lookup_table_data,
+ error_msg)) {
+ return nullptr;
}
+
OatDexFile* oat_dex_file = new OatDexFile(oat_file.get(),
dex_file_start,
vdex_file->GetLocationChecksum(i),
@@ -1656,7 +1740,9 @@ class OatFileBackedByVdex final : public OatFileBase {
return nullptr;
}
oat_file->SetupHeader(oat_file->external_dex_files_.size());
- oat_file->Setup(MakeNonOwningPointerVector(oat_file->external_dex_files_));
+ if (!oat_file->Setup(MakeNonOwningPointerVector(oat_file->external_dex_files_), error_msg)) {
+ return nullptr;
+ }
}
return oat_file.release();
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 29efd4016f..967167961c 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -192,7 +192,8 @@ const uint8_t* VdexFile::GetNextTypeLookupTableData(const uint8_t* cursor,
} else {
const uint8_t* data = cursor + sizeof(uint32_t) + reinterpret_cast<const uint32_t*>(cursor)[0];
// TypeLookupTables are required to be 4 byte aligned. the OatWriter makes sure they are.
- CHECK_ALIGNED(data, 4);
+ // We don't check this here to be defensive against corrupted vdex files.
+ // Callers should check the returned value matches their expectations.
return data;
}
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index eb8b81742b..a66ff88fb2 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -157,8 +157,6 @@ class VdexFile {
return size;
}
- bool IsDexSectionValid() const;
-
bool HasDexSection() const {
return GetSectionHeader(VdexSection::kDexFileSection).section_size != 0u;
}
@@ -251,6 +249,9 @@ class VdexFile {
const uint8_t* Begin() const { return mmap_.Begin(); }
const uint8_t* End() const { return mmap_.End(); }
size_t Size() const { return mmap_.Size(); }
+ bool Contains(const uint8_t* pointer) const {
+ return pointer >= Begin() && pointer < End();
+ }
const VdexFileHeader& GetVdexFileHeader() const {
return *reinterpret_cast<const VdexFileHeader*>(Begin());
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 35f1dc2ee4..a707a8ae5c 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -3964,249 +3964,6 @@ public class Main {
return res;
}
- /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (before)
- /// CHECK-DAG: ParameterValue
- /// CHECK-DAG: NewInstance
- /// CHECK-DAG: InvokeStaticOrDirect
- /// CHECK-DAG: InvokeStaticOrDirect
- /// CHECK-DAG: InvokeStaticOrDirect
- /// CHECK-DAG: InstanceFieldSet
- /// CHECK-DAG: InstanceFieldSet
- /// CHECK-DAG: InstanceFieldSet
- /// CHECK-DAG: InstanceFieldGet
- /// CHECK-DAG: InstanceFieldGet
- //
- /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after)
- /// CHECK-DAG: ParameterValue
- /// CHECK-DAG: NewInstance
- /// CHECK-DAG: Phi
- //
- /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after)
- /// CHECK: InvokeStaticOrDirect
- /// CHECK: InvokeStaticOrDirect
- /// CHECK: InvokeStaticOrDirect
- //
- /// CHECK-NOT: InvokeStaticOrDirect
-
- /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet predicated:false
- /// CHECK-NOT: InstanceFieldSet predicated:false
- //
- /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet predicated:true
- /// CHECK-NOT: InstanceFieldSet predicated:true
- //
- /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldGet
- //
- /// CHECK-NOT: InstanceFieldGet
- //
- /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after)
- /// CHECK: PredicatedInstanceFieldGet
- //
- /// CHECK-NOT: PredicatedInstanceFieldGet
- private static int $noinline$testPartialEscape2(TestClass obj, boolean escape) {
- TestClass i = new SubTestClass();
- if ($noinline$getBoolean(escape)) {
- i.next = obj;
- $noinline$Escape(i);
- } else {
- i.next = obj;
- }
- $noinline$clobberObservables();
- // Predicated-get
- TestClass res = i.next;
- // Predicated-set
- i.next = null;
- return res.i;
- }
-
- /// CHECK-START: float Main.$noinline$testPartialEscape3_float(boolean) load_store_elimination (before)
- /// CHECK-NOT: Phi
- /// CHECK-NOT: PredicatedInstanceFieldGet
- //
- /// CHECK-START: float Main.$noinline$testPartialEscape3_float(boolean) load_store_elimination (after)
- /// CHECK: Phi
- /// CHECK: Phi
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: float Main.$noinline$testPartialEscape3_float(boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet predicated:true
- /// CHECK-NOT: InstanceFieldSet predicated:true
- //
- /// CHECK-START: float Main.$noinline$testPartialEscape3_float(boolean) load_store_elimination (after)
- /// CHECK: PredicatedInstanceFieldGet
- /// CHECK-NOT: PredicatedInstanceFieldGet
- private static float $noinline$testPartialEscape3_float(boolean escape) {
- TestClass4 tc = new TestClass4();
- if ($noinline$getBoolean(escape)) {
- $noinline$Escape4(tc);
- } else {
- tc.floatField -= 1f;
- }
- // Partial escape
- $noinline$clobberObservables();
- // Predicated set
- tc.floatField *= 10;
- // Predicated get
- return tc.floatField;
- }
-
- /// CHECK-START: double Main.$noinline$testPartialEscape3_double(boolean) load_store_elimination (before)
- /// CHECK-NOT: Phi
- /// CHECK-NOT: PredicatedInstanceFieldGet
- //
- /// CHECK-START: double Main.$noinline$testPartialEscape3_double(boolean) load_store_elimination (after)
- /// CHECK: Phi
- /// CHECK: Phi
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: double Main.$noinline$testPartialEscape3_double(boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet predicated:true
- /// CHECK-NOT: InstanceFieldSet predicated:true
- //
- /// CHECK-START: double Main.$noinline$testPartialEscape3_double(boolean) load_store_elimination (after)
- /// CHECK: PredicatedInstanceFieldGet
- /// CHECK-NOT: PredicatedInstanceFieldGet
- private static double $noinline$testPartialEscape3_double(boolean escape) {
- TestClass4 tc = new TestClass4();
- if ($noinline$getBoolean(escape)) {
- $noinline$Escape4(tc);
- } else {
- tc.doubleField -= 1d;
- }
- // Partial escape
- $noinline$clobberObservables();
- // Predicated set
- tc.doubleField *= 10;
- // Predicated get
- return tc.doubleField;
- }
-
- /// CHECK-START: short Main.$noinline$testPartialEscape3_short(boolean) load_store_elimination (before)
- /// CHECK-NOT: Phi
- /// CHECK-NOT: PredicatedInstanceFieldGet
- //
- /// CHECK-START: short Main.$noinline$testPartialEscape3_short(boolean) load_store_elimination (after)
- /// CHECK: Phi
- /// CHECK: Phi
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: short Main.$noinline$testPartialEscape3_short(boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet predicated:true
- /// CHECK-NOT: InstanceFieldSet predicated:true
- //
- /// CHECK-START: short Main.$noinline$testPartialEscape3_short(boolean) load_store_elimination (after)
- /// CHECK: PredicatedInstanceFieldGet
- /// CHECK-NOT: PredicatedInstanceFieldGet
- private static short $noinline$testPartialEscape3_short(boolean escape) {
- TestClass4 tc = new TestClass4();
- if ($noinline$getBoolean(escape)) {
- $noinline$Escape4(tc);
- } else {
- tc.shortField -= 1;
- }
- // Partial escape
- $noinline$clobberObservables();
- // Predicated set
- tc.shortField *= 10;
- // Predicated get
- return tc.shortField;
- }
-
- /// CHECK-START: byte Main.$noinline$testPartialEscape3_byte(boolean) load_store_elimination (before)
- /// CHECK-NOT: Phi
- /// CHECK-NOT: PredicatedInstanceFieldGet
- //
- /// CHECK-START: byte Main.$noinline$testPartialEscape3_byte(boolean) load_store_elimination (after)
- /// CHECK: Phi
- /// CHECK: Phi
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: byte Main.$noinline$testPartialEscape3_byte(boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet predicated:true
- /// CHECK-NOT: InstanceFieldSet predicated:true
- //
- /// CHECK-START: byte Main.$noinline$testPartialEscape3_byte(boolean) load_store_elimination (after)
- /// CHECK: PredicatedInstanceFieldGet
- /// CHECK-NOT: PredicatedInstanceFieldGet
- private static byte $noinline$testPartialEscape3_byte(boolean escape) {
- TestClass4 tc = new TestClass4();
- if ($noinline$getBoolean(escape)) {
- $noinline$Escape4(tc);
- } else {
- tc.byteField -= 1;
- }
- // Partial escape
- $noinline$clobberObservables();
- // Predicated set
- tc.byteField *= 10;
- // Predicated get
- return tc.byteField;
- }
-
- /// CHECK-START: int Main.$noinline$testPartialEscape3_int(boolean) load_store_elimination (before)
- /// CHECK-NOT: Phi
- /// CHECK-NOT: PredicatedInstanceFieldGet
- //
- /// CHECK-START: int Main.$noinline$testPartialEscape3_int(boolean) load_store_elimination (after)
- /// CHECK: Phi
- /// CHECK: Phi
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.$noinline$testPartialEscape3_int(boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet predicated:true
- /// CHECK-NOT: InstanceFieldSet predicated:true
- //
- /// CHECK-START: int Main.$noinline$testPartialEscape3_int(boolean) load_store_elimination (after)
- /// CHECK: PredicatedInstanceFieldGet
- /// CHECK-NOT: PredicatedInstanceFieldGet
- private static int $noinline$testPartialEscape3_int(boolean escape) {
- TestClass4 tc = new TestClass4();
- if ($noinline$getBoolean(escape)) {
- $noinline$Escape4(tc);
- } else {
- tc.intField -= 1;
- }
- // Partial escape
- $noinline$clobberObservables();
- // Predicated set
- tc.intField *= 10;
- // Predicated get
- return tc.intField;
- }
-
- /// CHECK-START: long Main.$noinline$testPartialEscape3_long(boolean) load_store_elimination (before)
- /// CHECK-NOT: Phi
- /// CHECK-NOT: PredicatedInstanceFieldGet
- //
- /// CHECK-START: long Main.$noinline$testPartialEscape3_long(boolean) load_store_elimination (after)
- /// CHECK: Phi
- /// CHECK: Phi
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: long Main.$noinline$testPartialEscape3_long(boolean) load_store_elimination (after)
- /// CHECK: InstanceFieldSet predicated:true
- /// CHECK-NOT: InstanceFieldSet predicated:true
- //
- /// CHECK-START: long Main.$noinline$testPartialEscape3_long(boolean) load_store_elimination (after)
- /// CHECK: PredicatedInstanceFieldGet
- /// CHECK-NOT: PredicatedInstanceFieldGet
- private static long $noinline$testPartialEscape3_long(boolean escape) {
- TestClass4 tc = new TestClass4();
- if ($noinline$getBoolean(escape)) {
- $noinline$Escape4(tc);
- } else {
- tc.longField -= 1;
- }
- // Partial escape
- $noinline$clobberObservables();
- // Predicated set
- tc.longField *= 10;
- // Predicated get
- return tc.longField;
- }
-
private static void $noinline$clobberObservables() {}
static void assertLongEquals(long result, long expected) {
@@ -4613,19 +4370,5 @@ public class Main {
assertLongEquals(testOverlapLoop(50), 7778742049l);
assertIntEquals($noinline$testPartialEscape1(new TestClass(), true), 1);
assertIntEquals($noinline$testPartialEscape1(new TestClass(), false), 0);
- assertIntEquals($noinline$testPartialEscape2(new TestClass(), true), 1);
- assertIntEquals($noinline$testPartialEscape2(new TestClass(), false), 0);
- assertDoubleEquals($noinline$testPartialEscape3_double(true), -20d);
- assertDoubleEquals($noinline$testPartialEscape3_double(false), -40d);
- assertFloatEquals($noinline$testPartialEscape3_float(true), -20f);
- assertFloatEquals($noinline$testPartialEscape3_float(false), -40f);
- assertIntEquals($noinline$testPartialEscape3_int(true), -20);
- assertIntEquals($noinline$testPartialEscape3_int(false), -40);
- assertIntEquals($noinline$testPartialEscape3_byte(true), -20);
- assertIntEquals($noinline$testPartialEscape3_byte(false), -40);
- assertIntEquals($noinline$testPartialEscape3_short(true), -20);
- assertIntEquals($noinline$testPartialEscape3_short(false), -40);
- assertLongEquals($noinline$testPartialEscape3_long(true), -20);
- assertLongEquals($noinline$testPartialEscape3_long(false), -40);
}
}
diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java
index 28fa57cfbf..91c3ec48ab 100644
--- a/test/639-checker-code-sinking/src/Main.java
+++ b/test/639-checker-code-sinking/src/Main.java
@@ -110,8 +110,6 @@ public class Main {
/// CHECK: <<Int42:i\d+>> IntConstant 42
/// CHECK: begin_block
/// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main
- /// CHECK: If
- /// CHECK: begin_block
/// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
/// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>]
/// CHECK: Throw
@@ -121,14 +119,14 @@ public class Main {
/// CHECK-NOT: NewInstance
/// CHECK: If
/// CHECK: begin_block
+ /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error
+ /// CHECK-NOT: begin_block
/// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main
/// CHECK-NOT: begin_block
/// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
/// CHECK-NOT: begin_block
/// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>]
/// CHECK-NOT: begin_block
- /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error
- /// CHECK-NOT: begin_block
/// CHECK: <<Throw:l\d+>> NewInstance [<<Error>>]
/// CHECK-NOT: begin_block
/// CHECK: Throw [<<Throw>>]
@@ -325,12 +323,7 @@ public class Main {
/// CHECK: <<Int42:i\d+>> IntConstant 42
/// CHECK: <<Int43:i\d+>> IntConstant 43
/// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main
- /// CHECK: If
- /// CHECK: begin_block
- // Moved to throw block by partial-LSE and DCE.
/// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
- // These were moved by partial LSE and order of sets is not observable and are
- // in an arbitrary order.
/// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int42>>]
/// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int43>>]
/// CHECK: Throw
@@ -342,14 +335,14 @@ public class Main {
/// CHECK-NOT: NewInstance
/// CHECK: If
/// CHECK: begin_block
+ /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error
+ /// CHECK-NOT: begin_block
/// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main
/// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
/// CHECK-NOT: begin_block
/// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int42>>]
/// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int43>>]
/// CHECK-NOT: begin_block
- /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error
- /// CHECK-NOT: begin_block
/// CHECK: NewInstance [<<Error>>]
/// CHECK: Throw
/// CHECK-NOT: InstanceFieldSet
diff --git a/test/826-infinite-loop/expected-stderr.txt b/test/826-infinite-loop/expected-stderr.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/826-infinite-loop/expected-stderr.txt
diff --git a/test/826-infinite-loop/expected-stdout.txt b/test/826-infinite-loop/expected-stdout.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/826-infinite-loop/expected-stdout.txt
diff --git a/test/826-infinite-loop/info.txt b/test/826-infinite-loop/info.txt
new file mode 100644
index 0000000000..13a89d8ca2
--- /dev/null
+++ b/test/826-infinite-loop/info.txt
@@ -0,0 +1,2 @@
+Regression test for partial escape elimination, which used to crash when
+visiting an infinite loop.
diff --git a/test/826-infinite-loop/src/Main.java b/test/826-infinite-loop/src/Main.java
new file mode 100644
index 0000000000..85bcb28baf
--- /dev/null
+++ b/test/826-infinite-loop/src/Main.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+final class Main {
+ public static void main(String[] args) {
+ Object o = new Object();
+ if (args.length == 0) {
+ while (true) {
+ System.out.println(new Object());
+ }
+ }
+ }
+}
diff --git a/test/828-partial-lse/expected-stderr.txt b/test/828-partial-lse/expected-stderr.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/828-partial-lse/expected-stderr.txt
diff --git a/test/828-partial-lse/expected-stdout.txt b/test/828-partial-lse/expected-stdout.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/828-partial-lse/expected-stdout.txt
diff --git a/test/828-partial-lse/info.txt b/test/828-partial-lse/info.txt
new file mode 100644
index 0000000000..8d28cbdbfa
--- /dev/null
+++ b/test/828-partial-lse/info.txt
@@ -0,0 +1,2 @@
+Regression test for the partial LSE pass, see
+https://issuetracker.google.com/197818595.
diff --git a/test/828-partial-lse/src/Main.java b/test/828-partial-lse/src/Main.java
new file mode 100644
index 0000000000..2dde0eff95
--- /dev/null
+++ b/test/828-partial-lse/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Main {
+
+ public static void $noinline$testMain(Main m) {
+ expectEquals(0, m.myField);
+ }
+
+ public static void main(String[] args) {
+ $noinline$doTest(true);
+ $noinline$doTest(false);
+ }
+
+ public static void $noinline$doTest(boolean testValue) {
+ Main m = new Main();
+ // LSE will find that this store can be removed, as both branches override the value with a new
+ // one.
+ m.myField = 42;
+ if (testValue) {
+ // LSE will remove this store as well, as it's the value after the store of 42 is removed.
+ m.myField = 0;
+ // This makes sure `m` gets materialized. At this point, the bug is that the partial LSE
+ // optimization thinks the value incoming this block for `m.myField` is 42, however that
+ // store, as well as the store to 0, have been removed.
+ $noinline$testMain(m);
+ } else {
+ m.myField = 3;
+ expectEquals(3, m.myField);
+ }
+ }
+
+ public static void expectEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ int myField = 0;
+}