Merge "Removed quick support from dexfuzz."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4fce235..9902628 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -116,10 +116,14 @@
ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
$(HOST_CORE_IMAGE_optimizing_pic_64) \
$(HOST_CORE_IMAGE_optimizing_pic_32) \
+ $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
+ $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
$(HOST_OUT_EXECUTABLES)/patchoatd
ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_optimizing_pic_64) \
$(TARGET_CORE_IMAGE_optimizing_pic_32) \
+ $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
+ $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
$(TARGET_OUT_EXECUTABLES)/patchoatd
ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 1737376..b883e08 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -53,6 +53,7 @@
"optimizing/code_generator_utils.cc",
"optimizing/constant_folding.cc",
"optimizing/dead_code_elimination.cc",
+ "optimizing/escape.cc",
"optimizing/graph_checker.cc",
"optimizing/graph_visualizer.cc",
"optimizing/gvn.cc",
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e155e10..ad75ec4 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -45,6 +45,7 @@
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
#include "dex/dex_to_dex_compiler.h"
+#include "dex/dex_to_dex_decompiler.h"
#include "dex/verification_results.h"
#include "dex/verified_method.h"
#include "driver/compiler_options.h"
@@ -72,6 +73,7 @@
#include "transaction.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "utils/swap_space.h"
+#include "vdex_file.h"
#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
#include "verifier/verifier_log_mode.h"
@@ -394,7 +396,6 @@
void CompilerDriver::CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
@@ -406,7 +407,7 @@
// 2) Resolve all classes
// 3) Attempt to verify all classes
// 4) Attempt to initialize image classes, and trivially initialized classes
- PreCompile(class_loader, dex_files, verifier_deps, timings);
+ PreCompile(class_loader, dex_files, timings);
if (GetCompilerOptions().IsBootImage()) {
// We don't need to setup the intrinsics for non boot image compilation, as
// those compilations will pick up a boot image that have the ArtMethod already
@@ -433,6 +434,72 @@
FreeThreadPools();
}
+// In-place unquicken the given `dex_files` based on `quickening_info`.
+static void Unquicken(const std::vector<const DexFile*>& dex_files,
+ const ArrayRef<const uint8_t>& quickening_info) {
+ const uint8_t* quickening_info_ptr = quickening_info.data();
+ const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
+ for (const DexFile* dex_file : dex_files) {
+ for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data == nullptr) {
+ continue;
+ }
+ ClassDataItemIterator it(*dex_file, class_data);
+ // Skip fields
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+
+ // Unquicken each method.
+ while (it.HasNextDirectMethod()) {
+ const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ if (code_item != nullptr) {
+ uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+ quickening_info_ptr += sizeof(uint32_t);
+ optimizer::ArtDecompileDEX(
+ *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+ quickening_info_ptr += quickening_size;
+ }
+ it.Next();
+ }
+
+ while (it.HasNextVirtualMethod()) {
+ const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ if (code_item != nullptr) {
+ uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+ quickening_info_ptr += sizeof(uint32_t);
+ optimizer::ArtDecompileDEX(
+ *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+ quickening_info_ptr += quickening_size;
+ }
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+ }
+ DCHECK_EQ(quickening_info_ptr, quickening_info_end) << "Failed to use all quickening info";
+}
+
+void CompilerDriver::CompileAll(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ VdexFile* vdex_file,
+ TimingLogger* timings) {
+ if (vdex_file != nullptr) {
+ // TODO: we unquicken unconditionnally, as we don't know
+ // if the boot image has changed. How exactly we'll know is under
+ // experimentation.
+ Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+ Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
+ new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
+ }
+ CompileAll(class_loader, dex_files, timings);
+}
+
static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file, const DexFile::ClassDef& class_def)
@@ -673,7 +740,7 @@
InitializeThreadPools();
- PreCompile(jclass_loader, dex_files, /* verifier_deps */ nullptr, timings);
+ PreCompile(jclass_loader, dex_files, timings);
// Can we run DEX-to-DEX compiler on this class ?
optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
@@ -870,7 +937,6 @@
void CompilerDriver::PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
CheckThreadPools();
@@ -904,7 +970,7 @@
VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false);
}
- Verify(class_loader, dex_files, verifier_deps, timings);
+ Verify(class_loader, dex_files, timings);
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -1936,8 +2002,10 @@
void CompilerDriver::Verify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
+ verifier::VerifierDeps* verifier_deps =
+ Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
+ // If there is an existing `VerifierDeps`, try to use it for fast verification.
if (verifier_deps != nullptr) {
TimingLogger::ScopedTiming t("Fast Verify", timings);
ScopedObjectAccess soa(Thread::Current());
@@ -1975,16 +2043,15 @@
}
}
- // If there is no passed `verifier_deps` (because of non-existing vdex), or
- // the passed `verifier_deps` is not valid anymore, create a new one for
+ // If there is no existing `verifier_deps` (because of non-existing vdex), or
+ // the existing `verifier_deps` is not valid anymore, create a new one for
// non boot image compilation. The verifier will need it to record the new dependencies.
// Then dex2oat can update the vdex file with these new dependencies.
if (!GetCompilerOptions().IsBootImage()) {
// Create the main VerifierDeps, and set it to this thread.
- Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
- new verifier::VerifierDeps(dex_files));
- Thread::Current()->SetVerifierDeps(
- Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps());
+ verifier_deps = new verifier::VerifierDeps(dex_files);
+ Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps);
+ Thread::Current()->SetVerifierDeps(verifier_deps);
// Create per-thread VerifierDeps to avoid contention on the main one.
// We will merge them after verification.
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
@@ -2005,13 +2072,11 @@
}
if (!GetCompilerOptions().IsBootImage()) {
- verifier::VerifierDeps* main_deps =
- Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
// Merge all VerifierDeps into the main one.
for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
worker->GetThread()->SetVerifierDeps(nullptr);
- main_deps->MergeWith(*thread_deps, dex_files);;
+ verifier_deps->MergeWith(*thread_deps, dex_files);;
delete thread_deps;
}
Thread::Current()->SetVerifierDeps(nullptr);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index c7719fb..7418b00 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -51,7 +51,6 @@
namespace verifier {
class MethodVerifier;
-class VerifierDeps;
class VerifierDepsTest;
} // namespace verifier
@@ -69,6 +68,7 @@
using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
template<class T> class Handle;
class TimingLogger;
+class VdexFile;
class VerificationResults;
class VerifiedMethod;
@@ -119,7 +119,12 @@
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
+ TimingLogger* timings)
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
+
+ void CompileAll(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ VdexFile* vdex_file,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
@@ -420,7 +425,6 @@
private:
void PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
@@ -443,7 +447,6 @@
void Verify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- verifier::VerifierDeps* verifier_deps,
TimingLogger* timings);
void VerifyDexFile(jobject class_loader,
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index eed9d11..153aff4 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -403,6 +403,35 @@
return true;
}
+// Add dex file source(s) from a vdex file specified by a file handle.
+bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table) {
+ DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+ const uint8_t* current_dex_data = nullptr;
+ for (size_t i = 0; ; ++i) {
+ current_dex_data = vdex_file.GetNextDexFileData(current_dex_data);
+ if (current_dex_data == nullptr) {
+ break;
+ }
+ if (!DexFile::IsMagicValid(current_dex_data)) {
+ LOG(ERROR) << "Invalid magic in vdex file created from " << location;
+ return false;
+ }
+ // We used `zipped_dex_file_locations_` to keep the strings in memory.
+ zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location));
+ const char* full_location = zipped_dex_file_locations_.back().c_str();
+ oat_dex_files_.emplace_back(full_location,
+ DexFileSource(current_dex_data),
+ create_type_lookup_table);
+ }
+ if (oat_dex_files_.empty()) {
+ LOG(ERROR) << "No dex files in vdex file created from " << location;
+ return false;
+ }
+ return true;
+}
+
// Add dex file source from raw memory.
bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data,
const char* location,
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index f9671d7..0dcf79e 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -42,6 +42,7 @@
class OutputStream;
class TimingLogger;
class TypeLookupTable;
+class VdexFile;
class ZipEntry;
namespace debug {
@@ -116,7 +117,8 @@
// To produce a valid oat file, the user must first add sources with any combination of
// - AddDexFileSource(),
// - AddZippedDexFilesSource(),
- // - AddRawDexFileSource().
+ // - AddRawDexFileSource(),
+ // - AddVdexDexFilesSource().
// Then the user must call in order
// - WriteAndOpenDexFiles()
// - Initialize()
@@ -145,6 +147,11 @@
const char* location,
uint32_t location_checksum,
CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
+ // Add dex file source(s) from a vdex file.
+ bool AddVdexDexFilesSource(
+ const VdexFile& vdex_file,
+ const char* location,
+ CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
dchecked_vector<const char*> GetSourceLocations() const;
// Write raw dex files to the vdex file, mmap the file and open the dex files from it.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 59e1784..a78b3da 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -572,8 +572,10 @@
// We are about to use the assembler to place literals directly. Make sure we have enough
// underlying code buffer and we have generated the jump table with right size.
- CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), num_entries * sizeof(int32_t),
- CodeBufferCheckScope::kCheck, CodeBufferCheckScope::kExactSize);
+ vixl::CodeBufferCheckScope scope(codegen->GetVIXLAssembler(),
+ num_entries * sizeof(int32_t),
+ vixl::CodeBufferCheckScope::kReserveBufferSpace,
+ vixl::CodeBufferCheckScope::kExactSize);
__ Bind(&table_start_);
const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
@@ -2260,10 +2262,10 @@
masm->GetCursorAddress<vixl::aarch64::Instruction*>() - kInstructionSize;
if (prev->IsLoadOrStore()) {
// Make sure we emit only exactly one nop.
- vixl::aarch64::CodeBufferCheckScope scope(masm,
- kInstructionSize,
- vixl::aarch64::CodeBufferCheckScope::kCheck,
- vixl::aarch64::CodeBufferCheckScope::kExactSize);
+ vixl::CodeBufferCheckScope scope(masm,
+ kInstructionSize,
+ vixl::CodeBufferCheckScope::kReserveBufferSpace,
+ vixl::CodeBufferCheckScope::kExactSize);
__ nop();
}
}
@@ -4036,7 +4038,8 @@
vixl::aarch64::Label* label = &relative_call_patches_.back().label;
SingleEmissionCheckScope guard(GetVIXLAssembler());
__ Bind(label);
- __ bl(0); // Branch and link to itself. This will be overriden at link time.
+ // Branch and link to itself. This will be overriden at link time.
+ __ bl(static_cast<int64_t>(0));
break;
}
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
@@ -4167,7 +4170,7 @@
DCHECK(reg.IsX());
SingleEmissionCheckScope guard(GetVIXLAssembler());
__ Bind(fixup_label);
- __ adrp(reg, /* offset placeholder */ 0);
+ __ adrp(reg, /* offset placeholder */ static_cast<int64_t>(0));
}
void CodeGeneratorARM64::EmitAddPlaceholder(vixl::aarch64::Label* fixup_label,
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index b8d9a91..e399f32 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -47,6 +47,7 @@
using helpers::InputRegisterAt;
using helpers::InputSRegisterAt;
using helpers::InputVRegisterAt;
+using helpers::Int32ConstantFrom;
using helpers::LocationFrom;
using helpers::LowRegisterFrom;
using helpers::LowSRegisterFrom;
@@ -132,7 +133,7 @@
vixl32::Register base = sp;
if (stack_offset != 0) {
base = temps.Acquire();
- __ Add(base, sp, stack_offset);
+ __ Add(base, sp, Operand::From(stack_offset));
}
__ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
}
@@ -180,7 +181,7 @@
vixl32::Register base = sp;
if (stack_offset != 0) {
base = temps.Acquire();
- __ Add(base, sp, stack_offset);
+ __ Add(base, sp, Operand::From(stack_offset));
}
__ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
}
@@ -673,8 +674,8 @@
DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
// We are about to use the assembler to place literals directly. Make sure we have enough
- // underlying code buffer and we have generated the jump table with right size.
- codegen->GetVIXLAssembler()->GetBuffer().Align();
+ // underlying code buffer and we have generated a jump table of the right size, using
+ // codegen->GetVIXLAssembler()->GetBuffer().Align();
AssemblerAccurateScope aas(codegen->GetVIXLAssembler(),
num_entries * sizeof(int32_t),
CodeBufferCheckScope::kMaximumSize);
@@ -701,7 +702,7 @@
DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
- bb_addresses_[i].get()->UpdateValue(jump_offset, &codegen->GetVIXLAssembler()->GetBuffer());
+ bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
}
}
@@ -1152,7 +1153,8 @@
void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
vixl32::Label* true_target,
- vixl32::Label* false_target) {
+ vixl32::Label* false_target,
+ bool far_target) {
HInstruction* cond = instruction->InputAt(condition_input_index);
if (true_target == nullptr && false_target == nullptr) {
@@ -1188,9 +1190,13 @@
DCHECK(cond_val.IsRegister());
}
if (true_target == nullptr) {
- __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target);
+ __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
+ false_target,
+ far_target);
} else {
- __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target);
+ __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
+ true_target,
+ far_target);
}
} else {
// Condition has not been materialized. Use its inputs as the comparison and
@@ -1285,7 +1291,8 @@
GenerateTestAndBranch(select,
/* condition_input_index */ 2,
/* true_target */ nullptr,
- &false_target);
+ &false_target,
+ /* far_target */ false);
codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
__ Bind(&false_target);
}
@@ -1661,7 +1668,20 @@
// Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
// instruction from clobbering it as they might use r12 as a scratch register.
DCHECK(hidden_reg.Is(r12));
- __ Mov(hidden_reg, invoke->GetDexMethodIndex());
+
+ {
+ // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
+ // so it checks if the application is using them (by passing them to the macro assembler
+ // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
+ // what is available, and is the opposite of the standard usage: Instead of requesting a
+ // temporary location, it imposes an external constraint (i.e. a specific register is reserved
+ // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
+ // (to materialize the constant), since the destination register becomes available for such use
+ // internally for the duration of the macro instruction.
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ temps.Exclude(hidden_reg);
+ __ Mov(hidden_reg, invoke->GetDexMethodIndex());
+ }
{
AssemblerAccurateScope aas(GetVIXLAssembler(),
@@ -2452,13 +2472,13 @@
vixl32::Register dividend = InputRegisterAt(instruction, 0);
vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
- int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t imm = Int32ConstantFrom(second);
int64_t magic;
int shift;
CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
- __ Mov(temp1, magic);
+ __ Mov(temp1, Operand::From(magic));
__ Smull(temp2, temp1, dividend, temp1);
if (imm > 0 && magic < 0) {
@@ -2775,7 +2795,7 @@
case Primitive::kPrimShort:
case Primitive::kPrimInt: {
if (value.IsRegister()) {
- __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
} else {
DCHECK(value.IsConstant()) << value;
if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
@@ -2851,9 +2871,9 @@
}
// Rotate, or mov to out for zero or word size rotations.
if (rot != 0u) {
- __ Lsr(out_reg_hi, in_reg_hi, rot);
+ __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
__ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
- __ Lsr(out_reg_lo, in_reg_lo, rot);
+ __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
__ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
} else {
__ Mov(out_reg_lo, in_reg_lo);
@@ -2868,7 +2888,7 @@
__ And(shift_right, RegisterFrom(rhs), 0x1F);
__ Lsrs(shift_left, RegisterFrom(rhs), 6);
// TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled.
- __ Rsb(shift_left, shift_right, kArmBitsPerWord);
+ __ Rsb(shift_left, shift_right, Operand::From(kArmBitsPerWord));
__ B(cc, &shift_by_32_plus_shift_right);
// out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
@@ -3034,11 +3054,11 @@
// Shift the high part
__ Lsl(o_h, high, o_l);
// Shift the low part and `or` what overflew on the high part
- __ Rsb(temp, o_l, kArmBitsPerWord);
+ __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
__ Lsr(temp, low, temp);
__ Orr(o_h, o_h, temp);
// If the shift is > 32 bits, override the high part
- __ Subs(temp, o_l, kArmBitsPerWord);
+ __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
{
AssemblerAccurateScope guard(GetVIXLAssembler(),
3 * kArmInstrMaxSizeInBytes,
@@ -3053,11 +3073,11 @@
// Shift the low part
__ Lsr(o_l, low, o_h);
// Shift the high part and `or` what underflew on the low part
- __ Rsb(temp, o_h, kArmBitsPerWord);
+ __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
__ Lsl(temp, high, temp);
__ Orr(o_l, o_l, temp);
// If the shift is > 32 bits, override the low part
- __ Subs(temp, o_h, kArmBitsPerWord);
+ __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
{
AssemblerAccurateScope guard(GetVIXLAssembler(),
3 * kArmInstrMaxSizeInBytes,
@@ -3071,10 +3091,10 @@
__ And(o_h, second_reg, kMaxLongShiftDistance);
// same as Shr except we use `Lsr`s and not `Asr`s
__ Lsr(o_l, low, o_h);
- __ Rsb(temp, o_h, kArmBitsPerWord);
+ __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
__ Lsl(temp, high, temp);
__ Orr(o_l, o_l, temp);
- __ Subs(temp, o_h, kArmBitsPerWord);
+ __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
{
AssemblerAccurateScope guard(GetVIXLAssembler(),
3 * kArmInstrMaxSizeInBytes,
@@ -3418,7 +3438,7 @@
__ Add(temp, addr, offset);
addr = temp;
}
- __ Ldrexd(out_lo, out_hi, addr);
+ __ Ldrexd(out_lo, out_hi, MemOperand(addr));
}
void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
@@ -3438,10 +3458,10 @@
__ Bind(&fail);
// We need a load followed by store. (The address used in a STREX instruction must
// be the same as the address in the most recently executed LDREX instruction.)
- __ Ldrexd(temp1, temp2, addr);
+ __ Ldrexd(temp1, temp2, MemOperand(addr));
codegen_->MaybeRecordImplicitNullCheck(instruction);
- __ Strexd(temp1, value_lo, value_hi, addr);
- __ Cbnz(temp1, &fail);
+ __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
+ __ CompareAndBranchIfNonZero(temp1, &fail);
}
void LocationsBuilderARMVIXL::HandleFieldSet(
@@ -3957,7 +3977,7 @@
NullCheckSlowPathARMVIXL* slow_path =
new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
AddSlowPath(slow_path);
- __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
}
void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
@@ -4413,7 +4433,7 @@
codegen_->AddSlowPath(slow_path);
if (instruction->GetValueCanBeNull()) {
vixl32::Label non_zero;
- __ Cbnz(value, &non_zero);
+ __ CompareAndBranchIfNonZero(value, &non_zero);
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
@@ -4461,7 +4481,7 @@
GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
// If heap poisoning is enabled, no need to unpoison
// `temp1`, as we are comparing against null below.
- __ Cbnz(temp1, slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
__ Bind(&do_put);
} else {
__ B(ne, slow_path->GetEntryLabel());
@@ -4638,11 +4658,11 @@
bool can_be_null) {
vixl32::Label is_null;
if (can_be_null) {
- __ Cbz(value, &is_null);
+ __ CompareAndBranchIfZero(value, &is_null);
}
GetAssembler()->LoadFromOffset(
kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
- __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
+ __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
__ Strb(card, MemOperand(card, temp));
if (can_be_null) {
__ Bind(&is_null);
@@ -4697,10 +4717,10 @@
GetAssembler()->LoadFromOffset(
kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
if (successor == nullptr) {
- __ Cbnz(temp, slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
__ Bind(slow_path->GetReturnLabel());
} else {
- __ Cbz(temp, codegen_->GetLabelOf(successor));
+ __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
__ B(slow_path->GetEntryLabel());
}
}
@@ -5015,7 +5035,7 @@
cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
codegen_->AddSlowPath(slow_path);
if (generate_null_check) {
- __ Cbz(out, slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
}
if (cls->MustGenerateClinitCheck()) {
GenerateClassInitializationCheck(slow_path, out);
@@ -5206,7 +5226,7 @@
// Return 0 if `obj` is null.
// avoid null check if we know obj is not null.
if (instruction->MustDoNullCheck()) {
- __ Cbz(obj, &zero);
+ __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
}
switch (type_check_kind) {
@@ -5239,7 +5259,7 @@
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
// If `out` is null, we use it for the result, and jump to `done`.
- __ Cbz(out, &done);
+ __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
__ Cmp(out, cls);
__ B(ne, &loop);
__ Mov(out, 1);
@@ -5263,7 +5283,7 @@
__ B(eq, &success);
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
- __ Cbnz(out, &loop);
+ __ CompareAndBranchIfNonZero(out, &loop);
// If `out` is null, we use it for the result, and jump to `done`.
__ B(&done);
__ Bind(&success);
@@ -5289,10 +5309,10 @@
// /* HeapReference<Class> */ out = out->component_type_
GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc);
// If `out` is null, we use it for the result, and jump to `done`.
- __ Cbz(out, &done);
+ __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
- __ Cbnz(out, &zero);
+ __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
__ Bind(&exact_check);
__ Mov(out, 1);
__ B(&done);
@@ -5428,7 +5448,7 @@
vixl32::Label done;
// Avoid null check if we know obj is not null.
if (instruction->MustDoNullCheck()) {
- __ Cbz(obj, &done);
+ __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
}
// /* HeapReference<Class> */ temp = obj->klass_
@@ -5454,7 +5474,7 @@
// If the class reference currently in `temp` is null, jump to the slow path to throw the
// exception.
- __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
// Otherwise, compare the classes.
__ Cmp(temp, cls);
@@ -5474,7 +5494,7 @@
// If the class reference currently in `temp` is null, jump to the slow path to throw the
// exception.
- __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
// Otherwise, jump to the beginning of the loop.
__ B(&loop);
break;
@@ -5489,12 +5509,12 @@
// /* HeapReference<Class> */ temp = temp->component_type_
GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc);
// If the component type is null, jump to the slow path to throw the exception.
- __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
// Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
// to further check that this component type is not a primitive type.
GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
- __ Cbnz(temp, type_check_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
break;
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 302ee38..38c756f 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -369,7 +369,7 @@
FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
ArmVIXLAssembler* GetAssembler() const { return assembler_; }
- vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
+ ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
private:
void VisitUnimplemementedInstruction(HInstruction* instruction) {
@@ -451,7 +451,8 @@
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
vixl::aarch32::Label* true_target,
- vixl::aarch32::Label* false_target);
+ vixl::aarch32::Label* false_target,
+ bool far_target = true);
void GenerateCompareTestAndBranch(HCondition* condition,
vixl::aarch32::Label* true_target,
vixl::aarch32::Label* false_target);
@@ -504,7 +505,7 @@
const ArmVIXLAssembler& GetAssembler() const OVERRIDE { return assembler_; }
- vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
+ ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
size_t GetWordSize() const OVERRIDE { return kArmWordSize; }
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index 5129daf..d3623f1 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -139,9 +139,14 @@
HConstant* instr = location.GetConstant();
if (instr->IsIntConstant()) {
return instr->AsIntConstant()->GetValue();
- } else {
- DCHECK(instr->IsNullConstant()) << instr->DebugName();
+ } else if (instr->IsNullConstant()) {
return 0;
+ } else {
+ DCHECK(instr->IsLongConstant()) << instr->DebugName();
+ const int64_t ret = instr->AsLongConstant()->GetValue();
+ DCHECK_GE(ret, std::numeric_limits<int32_t>::min());
+ DCHECK_LE(ret, std::numeric_limits<int32_t>::max());
+ return ret;
}
}
@@ -161,7 +166,7 @@
if (location.IsRegister()) {
return vixl::aarch32::Operand(RegisterFrom(location, type));
} else {
- return vixl::aarch32::Operand(Int64ConstantFrom(location));
+ return vixl::aarch32::Operand(Int32ConstantFrom(location));
}
}
diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc
new file mode 100644
index 0000000..c80e19e
--- /dev/null
+++ b/compiler/optimizing/escape.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "escape.h"
+
+#include "nodes.h"
+
+namespace art {
+
+void CalculateEscape(HInstruction* reference,
+ bool (*no_escape)(HInstruction*, HInstruction*),
+ /*out*/ bool* is_singleton,
+ /*out*/ bool* is_singleton_and_non_escaping) {
+ // For references not allocated in the method, don't assume anything.
+ if (!reference->IsNewInstance() && !reference->IsNewArray()) {
+ *is_singleton = false;
+ *is_singleton_and_non_escaping = false;
+ return;
+ }
+ // Assume the best until proven otherwise.
+ *is_singleton = true;
+ *is_singleton_and_non_escaping = true;
+ // Visit all uses to determine if this reference can escape into the heap,
+ // a method call, an alias, etc.
+ for (const HUseListNode<HInstruction*>& use : reference->GetUses()) {
+ HInstruction* user = use.GetUser();
+ if (no_escape != nullptr && (*no_escape)(reference, user)) {
+ // Client supplied analysis says there is no escape.
+ continue;
+ } else if (user->IsBoundType() || user->IsNullCheck()) {
+ // BoundType shouldn't normally be necessary for an allocation. Just be conservative
+ // for the uncommon cases. Similarly, null checks are eventually eliminated for explicit
+ // allocations, but if we see one before it is simplified, assume an alias.
+ *is_singleton = false;
+ *is_singleton_and_non_escaping = false;
+ return;
+ } else if (user->IsPhi() || user->IsSelect() || user->IsInvoke() ||
+ (user->IsInstanceFieldSet() && (reference == user->InputAt(1))) ||
+ (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(1))) ||
+ (user->IsStaticFieldSet() && (reference == user->InputAt(1))) ||
+ (user->IsUnresolvedStaticFieldSet() && (reference == user->InputAt(0))) ||
+ (user->IsArraySet() && (reference == user->InputAt(2)))) {
+ // The reference is merged to HPhi/HSelect, passed to a callee, or stored to heap.
+ // Hence, the reference is no longer the only name that can refer to its value.
+ *is_singleton = false;
+ *is_singleton_and_non_escaping = false;
+ return;
+ } else if ((user->IsUnresolvedInstanceFieldGet() && (reference == user->InputAt(0))) ||
+ (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(0)))) {
+ // The field is accessed in an unresolved way. We mark the object as a non-singleton.
+ // Note that we could optimize this case and still perform some optimizations until
+ // we hit the unresolved access, but the conservative assumption is the simplest.
+ *is_singleton = false;
+ *is_singleton_and_non_escaping = false;
+ return;
+ } else if (user->IsReturn()) {
+ *is_singleton_and_non_escaping = false;
+ }
+ }
+
+ // Need for further analysis?
+ if (!*is_singleton_and_non_escaping) {
+ return;
+ }
+
+ // Look at the environment uses and if it's for HDeoptimize, it's treated the
+ // same as a return which escapes at the end of executing the compiled code.
+ // Other environment uses are fine, as long as all client optimizations that
+ // rely on this informations are disabled for debuggable.
+ for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
+ HEnvironment* user = use.GetUser();
+ if (user->GetHolder()->IsDeoptimize()) {
+ *is_singleton_and_non_escaping = false;
+ break;
+ }
+ }
+}
+
+bool IsNonEscapingSingleton(HInstruction* reference,
+ bool (*no_escape)(HInstruction*, HInstruction*)) {
+ bool is_singleton = true;
+ bool is_singleton_and_non_escaping = true;
+ CalculateEscape(reference, no_escape, &is_singleton, &is_singleton_and_non_escaping);
+ return is_singleton_and_non_escaping;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/escape.h b/compiler/optimizing/escape.h
new file mode 100644
index 0000000..6514843
--- /dev/null
+++ b/compiler/optimizing/escape.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_ESCAPE_H_
+#define ART_COMPILER_OPTIMIZING_ESCAPE_H_
+
+namespace art {
+
+class HInstruction;
+
+/*
+ * Methods related to escape analysis, i.e. determining whether an object
+ * allocation is visible outside ('escapes') its immediate method context.
+ */
+
+/*
+ * Performs escape analysis on the given instruction, typically a reference to an
+ * allocation. The method assigns true to parameter 'is_singleton' if the reference
+ * is the only name that can refer to its value during the lifetime of the method,
+ * meaning that the reference is not aliased with something else, is not stored to
+ * heap memory, and not passed to another method. The method assigns true to parameter
+ * 'is_singleton_and_non_escaping' if the reference is a singleton and is not returned
+ * to the caller or used as an environment local of an HDeoptimize instruction.
+ *
+ * When set, the no_escape function is applied to any use of the allocation instruction
+ * prior to any built-in escape analysis. This allows clients to define better escape
+ * analysis in certain case-specific circumstances. If 'no_escape(reference, user)'
+ * returns true, the user is assumed *not* to cause any escape right away. The return
+ * value false means the client cannot provide a definite answer and built-in escape
+ * analysis is applied to the user instead.
+ */
+void CalculateEscape(HInstruction* reference,
+ bool (*no_escape)(HInstruction*, HInstruction*),
+ /*out*/ bool* is_singleton,
+ /*out*/ bool* is_singleton_and_non_escaping);
+
+/*
+ * Convenience method for testing singleton and non-escaping property at once.
+ * Callers should be aware that this method invokes the full analysis at each call.
+ */
+bool IsNonEscapingSingleton(HInstruction* reference,
+ bool (*no_escape)(HInstruction*, HInstruction*));
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_ESCAPE_H_
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 110430f..c8e3534 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -303,7 +303,7 @@
vixl32::Register in_reg_hi = HighRegisterFrom(in);
vixl32::Label end;
__ Clz(out, in_reg_hi);
- __ Cbnz(in_reg_hi, &end);
+ __ CompareAndBranchIfNonZero(in_reg_hi, &end, /* far_target */ false);
__ Clz(out, in_reg_lo);
__ Add(out, out, 32);
__ Bind(&end);
@@ -345,7 +345,7 @@
vixl32::Label end;
__ Rbit(out, in_reg_lo);
__ Clz(out, out);
- __ Cbnz(in_reg_lo, &end);
+ __ CompareAndBranchIfNonZero(in_reg_lo, &end, /* far_target */ false);
__ Rbit(out, in_reg_hi);
__ Clz(out, out);
__ Add(out, out, 32);
@@ -518,7 +518,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
// Ignore upper 4B of long address.
- __ Ldrsb(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+ __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
@@ -528,7 +528,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
// Ignore upper 4B of long address.
- __ Ldr(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+ __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
@@ -545,9 +545,9 @@
vixl32::Register hi = HighRegisterFrom(invoke->GetLocations()->Out());
if (addr.Is(lo)) {
__ Ldr(hi, MemOperand(addr, 4));
- __ Ldr(lo, addr);
+ __ Ldr(lo, MemOperand(addr));
} else {
- __ Ldr(lo, addr);
+ __ Ldr(lo, MemOperand(addr));
__ Ldr(hi, MemOperand(addr, 4));
}
}
@@ -559,7 +559,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
// Ignore upper 4B of long address.
- __ Ldrsh(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+ __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
}
static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
@@ -576,7 +576,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
- __ Strb(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+ __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
@@ -585,7 +585,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
- __ Str(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+ __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
@@ -598,7 +598,7 @@
vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
// Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
// exception. So we can't use ldrd as addr may be unaligned.
- __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), addr);
+ __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr));
__ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4));
}
@@ -608,7 +608,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
- __ Strh(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+ __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
}
void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
@@ -842,8 +842,8 @@
__ Add(temp_reg, base, offset);
vixl32::Label loop_head;
__ Bind(&loop_head);
- __ Ldrexd(temp_lo, temp_hi, temp_reg);
- __ Strexd(temp_lo, value_lo, value_hi, temp_reg);
+ __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
+ __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
__ Cmp(temp_lo, 0);
__ B(ne, &loop_head);
} else {
@@ -1042,7 +1042,7 @@
vixl32::Label loop_head;
__ Bind(&loop_head);
- __ Ldrex(tmp, tmp_ptr);
+ __ Ldrex(tmp, MemOperand(tmp_ptr));
__ Subs(tmp, tmp, expected);
@@ -1052,7 +1052,7 @@
CodeBufferCheckScope::kMaximumSize);
__ itt(eq);
- __ strex(eq, tmp, value, tmp_ptr);
+ __ strex(eq, tmp, value, MemOperand(tmp_ptr));
__ cmp(eq, tmp, 1);
}
@@ -1158,7 +1158,7 @@
if (can_slow_path) {
slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
codegen_->AddSlowPath(slow_path);
- __ Cbz(arg, slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
}
// Reference equality check, return 0 if same reference.
@@ -1191,7 +1191,9 @@
}
// Shorter string is empty?
- __ Cbz(temp0, &end);
+ // Note that mirror::kUseStringCompression==true introduces lots of instructions,
+ // which makes &end label far away from this branch and makes it not 'CBZ-encodable'.
+ __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression);
if (mirror::kUseStringCompression) {
// Check if both strings using same compression style to use this comparison loop.
@@ -1218,7 +1220,7 @@
static_assert(IsAligned<8>(kObjectAlignment),
"String data must be 8-byte aligned for unrolled CompareTo loop.");
- const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ const unsigned char_size = Primitive::ComponentSize(Primitive::kPrimChar);
DCHECK_EQ(char_size, 2u);
UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
@@ -1414,7 +1416,7 @@
StringEqualsOptimizations optimizations(invoke);
if (!optimizations.GetArgumentNotNull()) {
// Check if input is null, return false if it is.
- __ Cbz(arg, &return_false);
+ __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false);
}
// Reference equality check, return true if same reference.
@@ -1442,7 +1444,7 @@
// Return true if both strings are empty. Even with string compression `count == 0` means empty.
static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
"Expecting 0=compressed, 1=uncompressed");
- __ Cbz(temp, &return_true);
+ __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
// Assertions that must hold in order to compare strings 4 bytes at a time.
DCHECK_ALIGNED(value_offset, 4);
@@ -1467,7 +1469,7 @@
__ Bind(&loop);
__ Ldr(out, MemOperand(str, temp1));
__ Ldr(temp2, MemOperand(arg, temp1));
- __ Add(temp1, temp1, sizeof(uint32_t));
+ __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
__ Cmp(out, temp2);
__ B(ne, &return_false);
// With string compression, we have compared 4 bytes, otherwise 2 chars.
@@ -1718,7 +1720,7 @@
} else if (length_is_input_length) {
// The only way the copy can succeed is if pos is zero.
vixl32::Register pos_reg = RegisterFrom(pos);
- __ Cbnz(pos_reg, slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
} else {
// Check that pos >= 0.
vixl32::Register pos_reg = RegisterFrom(pos);
@@ -1815,12 +1817,12 @@
if (!optimizations.GetSourceIsNotNull()) {
// Bail out if the source is null.
- __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
}
if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
// Bail out if the destination is null.
- __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
}
// If the length is negative, bail out.
@@ -1865,13 +1867,13 @@
// /* HeapReference<Class> */ temp1 = temp1->component_type_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
- __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
// If heap poisoning is enabled, `temp1` has been unpoisoned
// by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
// /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
__ Ldrh(temp1, MemOperand(temp1, primitive_offset));
static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
- __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
}
// /* HeapReference<Class> */ temp1 = dest->klass_
@@ -1889,13 +1891,13 @@
// /* HeapReference<Class> */ temp2 = temp1->component_type_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
- __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
// If heap poisoning is enabled, `temp2` has been unpoisoned
// by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
// /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
__ Ldrh(temp2, MemOperand(temp2, primitive_offset));
static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
- __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
}
// For the same reason given earlier, `temp1` is not trashed by the
@@ -1918,7 +1920,7 @@
// comparison with null below, and this reference is not
// kept afterwards.
__ Ldr(temp1, MemOperand(temp1, super_offset));
- __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
__ Bind(&do_copy);
} else {
__ B(ne, intrinsic_slow_path->GetEntryLabel());
@@ -1944,24 +1946,24 @@
// Bail out if the destination is not a non primitive array.
// /* HeapReference<Class> */ temp3 = temp1->component_type_
__ Ldr(temp3, MemOperand(temp1, component_offset));
- __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
assembler->MaybeUnpoisonHeapReference(temp3);
// /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
__ Ldrh(temp3, MemOperand(temp3, primitive_offset));
static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
- __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
}
if (!optimizations.GetSourceIsNonPrimitiveArray()) {
// Bail out if the source is not a non primitive array.
// /* HeapReference<Class> */ temp3 = temp2->component_type_
__ Ldr(temp3, MemOperand(temp2, component_offset));
- __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
assembler->MaybeUnpoisonHeapReference(temp3);
// /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
__ Ldrh(temp3, MemOperand(temp3, primitive_offset));
static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
- __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
}
__ Cmp(temp1, temp2);
@@ -1978,7 +1980,7 @@
// /* HeapReference<Class> */ temp1 = temp1->super_class_
__ Ldr(temp1, MemOperand(temp1, super_offset));
// No need to unpoison the result, we're comparing against null.
- __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
__ Bind(&do_copy);
} else {
__ B(ne, intrinsic_slow_path->GetEntryLabel());
@@ -1994,7 +1996,7 @@
// /* HeapReference<Class> */ temp3 = temp1->component_type_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
- __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
// If heap poisoning is enabled, `temp3` has been unpoisoned
// by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
} else {
@@ -2003,13 +2005,13 @@
assembler->MaybeUnpoisonHeapReference(temp1);
// /* HeapReference<Class> */ temp3 = temp1->component_type_
__ Ldr(temp3, MemOperand(temp1, component_offset));
- __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
assembler->MaybeUnpoisonHeapReference(temp3);
}
// /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
__ Ldrh(temp3, MemOperand(temp3, primitive_offset));
static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
- __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+ __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
}
int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 15e6059..edecf17 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -15,6 +15,8 @@
*/
#include "load_store_elimination.h"
+
+#include "escape.h"
#include "side_effects_analysis.h"
#include <iostream>
@@ -31,70 +33,12 @@
// whether it's a singleton, returned, etc.
class ReferenceInfo : public ArenaObject<kArenaAllocMisc> {
public:
- ReferenceInfo(HInstruction* reference, size_t pos) : reference_(reference), position_(pos) {
- is_singleton_ = true;
- is_singleton_and_non_escaping_ = true;
- if (!reference_->IsNewInstance() && !reference_->IsNewArray()) {
- // For references not allocated in the method, don't assume anything.
- is_singleton_ = false;
- is_singleton_and_non_escaping_ = false;
- return;
- }
-
- // Visit all uses to determine if this reference can spread into the heap,
- // a method call, etc.
- for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) {
- HInstruction* user = use.GetUser();
- DCHECK(!user->IsNullCheck()) << "NullCheck should have been eliminated";
- if (user->IsBoundType()) {
- // BoundType shouldn't normally be necessary for a NewInstance.
- // Just be conservative for the uncommon cases.
- is_singleton_ = false;
- is_singleton_and_non_escaping_ = false;
- return;
- }
- if (user->IsPhi() || user->IsSelect() || user->IsInvoke() ||
- (user->IsInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
- (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
- (user->IsStaticFieldSet() && (reference_ == user->InputAt(1))) ||
- (user->IsUnresolvedStaticFieldSet() && (reference_ == user->InputAt(0))) ||
- (user->IsArraySet() && (reference_ == user->InputAt(2)))) {
- // reference_ is merged to HPhi/HSelect, passed to a callee, or stored to heap.
- // reference_ isn't the only name that can refer to its value anymore.
- is_singleton_ = false;
- is_singleton_and_non_escaping_ = false;
- return;
- }
- if ((user->IsUnresolvedInstanceFieldGet() && (reference_ == user->InputAt(0))) ||
- (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(0)))) {
- // The field is accessed in an unresolved way. We mark the object as a non-singleton
- // to disable load/store optimizations on it.
- // Note that we could optimize this case and still perform some optimizations until
- // we hit the unresolved access, but disabling is the simplest.
- is_singleton_ = false;
- is_singleton_and_non_escaping_ = false;
- return;
- }
- if (user->IsReturn()) {
- is_singleton_and_non_escaping_ = false;
- }
- }
-
- if (!is_singleton_ || !is_singleton_and_non_escaping_) {
- return;
- }
-
- // Look at Environment uses and if it's for HDeoptimize, it's treated the same
- // as a return which escapes at the end of executing the compiled code. We don't
- // do store elimination for singletons that escape through HDeoptimize.
- // Other Environment uses are fine since LSE is disabled for debuggable.
- for (const HUseListNode<HEnvironment*>& use : reference_->GetEnvUses()) {
- HEnvironment* user = use.GetUser();
- if (user->GetHolder()->IsDeoptimize()) {
- is_singleton_and_non_escaping_ = false;
- break;
- }
- }
+ ReferenceInfo(HInstruction* reference, size_t pos)
+ : reference_(reference),
+ position_(pos),
+ is_singleton_(true),
+ is_singleton_and_non_escaping_(true) {
+ CalculateEscape(reference_, nullptr, &is_singleton_, &is_singleton_and_non_escaping_);
}
HInstruction* GetReference() const {
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index e3b9fb6..c35c393 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -43,12 +43,12 @@
}
const uint8_t* ArmVIXLAssembler::CodeBufferBaseAddress() const {
- return vixl_masm_.GetStartAddress<uint8_t*>();
+ return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>();
}
void ArmVIXLAssembler::FinalizeInstructions(const MemoryRegion& region) {
// Copy the instructions from the buffer.
- MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize());
+ MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize());
region.CopyFrom(0, from);
}
@@ -365,7 +365,7 @@
if (stack_offset != 0) {
base = temps.Acquire();
DCHECK_EQ(regs & (1u << base.GetCode()), 0u);
- ___ Add(base, sp, stack_offset);
+ ___ Add(base, sp, Operand::From(stack_offset));
}
___ Stm(base, NO_WRITE_BACK, RegisterList(regs));
} else {
@@ -385,7 +385,7 @@
vixl32::Register base = sp;
if (stack_offset != 0) {
base = temps.Acquire();
- ___ Add(base, sp, stack_offset);
+ ___ Add(base, sp, Operand::From(stack_offset));
}
___ Ldm(base, NO_WRITE_BACK, RegisterList(regs));
} else {
@@ -429,5 +429,31 @@
}
}
+void ArmVIXLMacroAssembler::CompareAndBranchIfZero(vixl32::Register rn,
+ vixl32::Label* label,
+ bool is_far_target) {
+ if (!is_far_target && rn.IsLow() && !label->IsBound()) {
+ // In T32, Cbz/Cbnz instructions have following limitations:
+ // - There are only 7 bits (i:imm5:0) to encode branch target address (cannot be far target).
+ // - Only low registers (i.e R0 .. R7) can be encoded.
+ // - Only forward branches (unbound labels) are supported.
+ Cbz(rn, label);
+ return;
+ }
+ Cmp(rn, 0);
+ B(eq, label);
+}
+
+void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn,
+ vixl32::Label* label,
+ bool is_far_target) {
+ if (!is_far_target && rn.IsLow() && !label->IsBound()) {
+ Cbnz(rn, label);
+ return;
+ }
+ Cmp(rn, 0);
+ B(ne, label);
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index e020628..b4a4abc 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -37,6 +37,25 @@
namespace art {
namespace arm {
+class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler {
+ public:
+ // The following interfaces can generate CMP+Bcc or Cbz/Cbnz.
+ // CMP+Bcc are generated by default.
+ // If a hint is given (is_far_target = false) and rn and label can all fit into Cbz/Cbnz,
+ // then Cbz/Cbnz is generated.
+ // Prefer following interfaces to using vixl32::MacroAssembler::Cbz/Cbnz.
+ // In T32, Cbz/Cbnz instructions have following limitations:
+ // - Far targets, which are over 126 bytes away, are not supported.
+ // - Only low registers can be encoded.
+ // - Backward branches are not supported.
+ void CompareAndBranchIfZero(vixl32::Register rn,
+ vixl32::Label* label,
+ bool is_far_target = true);
+ void CompareAndBranchIfNonZero(vixl32::Register rn,
+ vixl32::Label* label,
+ bool is_far_target = true);
+};
+
class ArmVIXLAssembler FINAL : public Assembler {
private:
class ArmException;
@@ -48,7 +67,7 @@
}
virtual ~ArmVIXLAssembler() {}
- vixl32::MacroAssembler* GetVIXLAssembler() { return &vixl_masm_; }
+ ArmVIXLMacroAssembler* GetVIXLAssembler() { return &vixl_masm_; }
void FinalizeCode() OVERRIDE;
// Size of generated code.
@@ -117,7 +136,7 @@
private:
// VIXL assembler.
- vixl32::MacroAssembler vixl_masm_;
+ ArmVIXLMacroAssembler vixl_masm_;
};
// Thread register declaration.
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 23b2774..f20ed0a 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -168,6 +168,8 @@
CHECK_EQ(0u, size);
} else if (src.IsCoreRegister()) {
CHECK_EQ(4u, size);
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(src.AsVIXLRegister());
asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
} else if (src.IsRegisterPair()) {
CHECK_EQ(8u, size);
@@ -186,12 +188,16 @@
void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
ArmManagedRegister src = msrc.AsArm();
CHECK(src.IsCoreRegister()) << src;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(src.AsVIXLRegister());
asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
}
void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
ArmManagedRegister src = msrc.AsArm();
CHECK(src.IsCoreRegister()) << src;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(src.AsVIXLRegister());
asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
}
@@ -202,6 +208,8 @@
ArmManagedRegister src = msrc.AsArm();
ArmManagedRegister scratch = mscratch.AsArm();
asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(scratch.AsVIXLRegister());
asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, in_off.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value() + 4);
}
@@ -210,6 +218,8 @@
FrameOffset src,
ManagedRegister mscratch) {
ArmManagedRegister scratch = mscratch.AsArm();
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(scratch.AsVIXLRegister());
asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, src.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value());
}
@@ -220,6 +230,8 @@
bool unpoison_reference) {
ArmManagedRegister dst = dest.AsArm();
CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister());
asm_.LoadFromOffset(kLoadWord,
dst.AsVIXLRegister(),
base.AsArm().AsVIXLRegister(),
@@ -246,6 +258,8 @@
ManagedRegister scratch) {
ArmManagedRegister mscratch = scratch.AsArm();
CHECK(mscratch.IsCoreRegister()) << mscratch;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(mscratch.AsVIXLRegister());
asm_.LoadImmediate(mscratch.AsVIXLRegister(), imm);
asm_.StoreToOffset(kStoreWord, mscratch.AsVIXLRegister(), sp, dest.Int32Value());
}
@@ -263,6 +277,8 @@
void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) {
ArmManagedRegister dst = m_dst.AsArm();
CHECK(dst.IsCoreRegister()) << dst;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(dst.AsVIXLRegister());
asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), tr, offs.Int32Value());
}
@@ -271,6 +287,8 @@
ManagedRegister mscratch) {
ArmManagedRegister scratch = mscratch.AsArm();
CHECK(scratch.IsCoreRegister()) << scratch;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(scratch.AsVIXLRegister());
asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, fr_offs.Int32Value());
}
@@ -286,6 +304,8 @@
ManagedRegister mscratch) {
ArmManagedRegister scratch = mscratch.AsArm();
CHECK(scratch.IsCoreRegister()) << scratch;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(scratch.AsVIXLRegister());
asm_.AddConstant(scratch.AsVIXLRegister(), sp, fr_offs.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value());
}
@@ -312,6 +332,8 @@
if (!dst.Equals(src)) {
if (dst.IsCoreRegister()) {
CHECK(src.IsCoreRegister()) << src;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(dst.AsVIXLRegister());
___ Mov(dst.AsVIXLRegister(), src.AsVIXLRegister());
} else if (dst.IsDRegister()) {
if (src.IsDRegister()) {
@@ -351,6 +373,8 @@
ArmManagedRegister temp = scratch.AsArm();
CHECK(temp.IsCoreRegister()) << temp;
CHECK(size == 4 || size == 8) << size;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(temp.AsVIXLRegister());
if (size == 4) {
asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value());
asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value());
@@ -414,6 +438,8 @@
ArmManagedRegister in_reg = min_reg.AsArm();
CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
CHECK(out_reg.IsCoreRegister()) << out_reg;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(out_reg.AsVIXLRegister());
if (null_allowed) {
// Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
// the address in the handle scope holding the reference.
@@ -425,6 +451,8 @@
handle_scope_offset.Int32Value());
in_reg = out_reg;
}
+
+ temps.Exclude(in_reg.AsVIXLRegister());
___ Cmp(in_reg.AsVIXLRegister(), 0);
if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
@@ -457,6 +485,8 @@
bool null_allowed) {
ArmManagedRegister scratch = mscratch.AsArm();
CHECK(scratch.IsCoreRegister()) << scratch;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(scratch.AsVIXLRegister());
if (null_allowed) {
asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value());
// Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
@@ -503,6 +533,8 @@
ArmManagedRegister scratch = mscratch.AsArm();
CHECK(base.IsCoreRegister()) << base;
CHECK(scratch.IsCoreRegister()) << scratch;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(scratch.AsVIXLRegister());
asm_.LoadFromOffset(kLoadWord,
scratch.AsVIXLRegister(),
base.AsVIXLRegister(),
@@ -514,6 +546,8 @@
void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
ArmManagedRegister scratch = mscratch.AsArm();
CHECK(scratch.IsCoreRegister()) << scratch;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(scratch.AsVIXLRegister());
// Call *(*(SP + base) + offset)
asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, base.Int32Value());
asm_.LoadFromOffset(kLoadWord,
@@ -541,6 +575,8 @@
void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) {
CHECK_ALIGNED(stack_adjust, kStackAlignment);
ArmManagedRegister scratch = m_scratch.AsArm();
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(scratch.AsVIXLRegister());
exception_blocks_.emplace_back(
new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust));
asm_.LoadFromOffset(kLoadWord,
@@ -574,10 +610,12 @@
switch (condition) {
case JNIMacroUnaryCondition::kZero:
- ___ Cbz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+ ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(),
+ ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
break;
case JNIMacroUnaryCondition::kNotZero:
- ___ Cbnz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+ ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(),
+ ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
break;
default:
LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(condition);
@@ -596,11 +634,14 @@
if (exception->stack_adjust_ != 0) { // Fix up the frame.
DecreaseFrameSize(exception->stack_adjust_);
}
+
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(exception->scratch_.AsVIXLRegister());
// Pass exception object as argument.
// Don't care about preserving r0 as this won't return.
___ Mov(r0, exception->scratch_.AsVIXLRegister());
+ temps.Include(exception->scratch_.AsVIXLRegister());
// TODO: check that exception->scratch_ is dead by this point.
- UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
vixl32::Register temp = temps.Acquire();
___ Ldr(temp,
MemOperand(tr,
@@ -622,6 +663,9 @@
} else if (dest.IsCoreRegister()) {
CHECK(!dest.AsVIXLRegister().Is(sp)) << dest;
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ temps.Exclude(dest.AsVIXLRegister());
+
if (size == 1u) {
___ Ldrb(dest.AsVIXLRegister(), MemOperand(base, offset));
} else {
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index f91bcfa..6ed0e9b 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -40,12 +40,12 @@
}
const uint8_t* Arm64Assembler::CodeBufferBaseAddress() const {
- return vixl_masm_.GetStartAddress<uint8_t*>();
+ return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>();
}
void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) {
// Copy the instructions from the buffer.
- MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize());
+ MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize());
region.CopyFrom(0, from);
}
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 10bed13..50a1d9f 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1753,7 +1753,10 @@
__ LoadFromOffset(kLoadWordPair, R2, R4, 0x40400);
__ LoadFromOffset(kLoadWordPair, R4, R4, 0x40400);
+ vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler());
+ temps.Exclude(R12);
__ LoadFromOffset(kLoadWord, R0, R12, 12); // 32-bit because of R12.
+ temps.Include(R12);
__ LoadFromOffset(kLoadWord, R2, R4, 0xa4 - 0x100000);
__ LoadFromOffset(kLoadSignedByte, R2, R4, 12);
@@ -1783,7 +1786,10 @@
__ StoreToOffset(kStoreWordPair, R2, R4, 0x40400);
__ StoreToOffset(kStoreWordPair, R4, R4, 0x40400);
+ vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler());
+ temps.Exclude(R12);
__ StoreToOffset(kStoreWord, R0, R12, 12); // 32-bit because of R12.
+ temps.Include(R12);
__ StoreToOffset(kStoreWord, R2, R4, 0xa4 - 0x100000);
__ StoreToOffset(kStoreByte, R2, R4, 12);
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index dcf3619..525a2ee 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -83,10 +83,13 @@
// The compiler driver handles the verifier deps in the callbacks, so
// remove what this class did for unit testing.
verifier_deps_.reset(nullptr);
- callbacks_->SetVerifierDeps(nullptr);
- compiler_driver_->Verify(class_loader_, dex_files_, deps, &timings);
+ callbacks_->SetVerifierDeps(deps);
+ compiler_driver_->Verify(class_loader_, dex_files_, &timings);
// The compiler driver may have updated the VerifierDeps in the callback object.
- verifier_deps_.reset(callbacks_->GetVerifierDeps());
+ if (callbacks_->GetVerifierDeps() != deps) {
+ verifier_deps_.reset(callbacks_->GetVerifierDeps());
+ }
+ callbacks_->SetVerifierDeps(nullptr);
}
void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 81baa80..9e6032f 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -78,6 +78,7 @@
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "utils.h"
+#include "vdex_file.h"
#include "verifier/verifier_deps.h"
#include "well_known_classes.h"
#include "zip_archive.h"
@@ -520,6 +521,7 @@
oat_fd_(-1),
input_vdex_fd_(-1),
output_vdex_fd_(-1),
+ input_vdex_file_(nullptr),
zip_fd_(-1),
image_base_(0U),
image_classes_zip_filename_(nullptr),
@@ -710,6 +712,10 @@
Usage("Output must be supplied with either --oat-file or --oat-fd");
}
+ if (input_vdex_fd_ != -1 && !input_vdex_.empty()) {
+ Usage("Can't have both --input-vdex-fd and --input-vdex");
+ }
+
if (!oat_filenames_.empty() && oat_fd_ != -1) {
Usage("--oat-file should not be used with --oat-fd");
}
@@ -1123,6 +1129,8 @@
zip_location_ = option.substr(strlen("--zip-location=")).data();
} else if (option.starts_with("--input-vdex-fd=")) {
ParseInputVdexFd(option);
+ } else if (option.starts_with("--input-vdex=")) {
+ input_vdex_ = option.substr(strlen("--input-vdex=")).data();
} else if (option.starts_with("--output-vdex-fd=")) {
ParseOutputVdexFd(option);
} else if (option.starts_with("--oat-file=")) {
@@ -1266,6 +1274,17 @@
return false;
}
oat_files_.push_back(std::move(oat_file));
+ DCHECK_EQ(input_vdex_fd_, -1);
+ if (!input_vdex_.empty()) {
+ std::string error_msg;
+ input_vdex_file_.reset(VdexFile::Open(input_vdex_,
+ /* writable */ false,
+ /* low_4gb */ false,
+ &error_msg));
+ if (input_vdex_file_ != nullptr && !input_vdex_file_->IsValid()) {
+ input_vdex_file_.reset(nullptr);
+ }
+ }
DCHECK_EQ(output_vdex_fd_, -1);
std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
@@ -1293,6 +1312,31 @@
}
oat_files_.push_back(std::move(oat_file));
+ DCHECK_NE(input_vdex_fd_, output_vdex_fd_);
+ if (input_vdex_fd_ != -1) {
+ struct stat s;
+ int rc = TEMP_FAILURE_RETRY(fstat(input_vdex_fd_, &s));
+ if (rc == -1) {
+ PLOG(WARNING) << "Failed getting length of vdex file";
+ } else {
+ std::string error_msg;
+ input_vdex_file_.reset(VdexFile::Open(input_vdex_fd_,
+ s.st_size,
+ "vdex",
+ /* writable */ false,
+ /* low_4gb */ false,
+ &error_msg));
+ // If there's any problem with the passed vdex, just warn and proceed
+ // without it.
+ if (input_vdex_file_ == nullptr) {
+ PLOG(WARNING) << "Failed opening vdex file " << error_msg;
+ } else if (!input_vdex_file_->IsValid()) {
+ PLOG(WARNING) << "Existing vdex file is invalid";
+ input_vdex_file_.reset(nullptr);
+ }
+ }
+ }
+
DCHECK_NE(output_vdex_fd_, -1);
std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex");
std::unique_ptr<File> vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true));
@@ -1388,7 +1432,6 @@
// boot class path.
bool Setup() {
TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
- art::MemMap::Init(); // For ZipEntry::ExtractToMemMap.
if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
return false;
@@ -1480,8 +1523,11 @@
// Unzip or copy dex files straight to the oat file.
std::unique_ptr<MemMap> opened_dex_files_map;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
- // Dexlayout verifies the dex file, so disable dex file verification in that case.
- bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile;
+ // No need to verify the dex file for:
+ // 1) dexlayout, which already verified it
+ // 2) when we have a vdex file, which means it was already verified.
+ bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile &&
+ (input_vdex_file_ == nullptr);
if (!oat_writers_[i]->WriteAndOpenDexFiles(
kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(),
rodata_.back(),
@@ -1668,7 +1714,7 @@
swap_fd_,
profile_compilation_info_.get()));
driver_->SetDexFilesForOatFile(dex_files_);
- driver_->CompileAll(class_loader_, dex_files_, /* verifier_deps */ nullptr, timings_);
+ driver_->CompileAll(class_loader_, dex_files_, input_vdex_file_.get(), timings_);
}
// Notes on the interleaving of creating the images and oat files to
@@ -2241,7 +2287,14 @@
bool AddDexFileSources() {
TimingLogger::ScopedTiming t2("AddDexFileSources", timings_);
- if (zip_fd_ != -1) {
+ if (input_vdex_file_ != nullptr) {
+ DCHECK_EQ(oat_writers_.size(), 1u);
+ const std::string& name = zip_location_.empty() ? dex_locations_[0] : zip_location_;
+ DCHECK(!name.empty());
+ if (!oat_writers_[0]->AddVdexDexFilesSource(*input_vdex_file_.get(), name.c_str())) {
+ return false;
+ }
+ } else if (zip_fd_ != -1) {
DCHECK_EQ(oat_writers_.size(), 1u);
if (!oat_writers_[0]->AddZippedDexFilesSource(File(zip_fd_, /* check_usage */ false),
zip_location_.c_str())) {
@@ -2599,6 +2652,8 @@
int oat_fd_;
int input_vdex_fd_;
int output_vdex_fd_;
+ std::string input_vdex_;
+ std::unique_ptr<VdexFile> input_vdex_file_;
std::vector<const char*> dex_filenames_;
std::vector<const char*> dex_locations_;
int zip_fd_;
@@ -2795,6 +2850,8 @@
}
}
+ art::MemMap::Init(); // For ZipEntry::ExtractToMemMap, and vdex.
+
// Check early that the result of compilation can be written
if (!dex2oat->OpenFile()) {
return EXIT_FAILURE;
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 30b708c..3347dac 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -63,9 +63,7 @@
case kVld2Location:
case kVld3Location:
case kVld4Location: {
- const uintptr_t pc_delta = disasm_->IsT32()
- ? vixl::aarch32::kT32PcDelta
- : vixl::aarch32::kA32PcDelta;
+ const uintptr_t pc_delta = label.GetLabel()->GetPcOffset();
const int32_t offset = label.GetLabel()->GetLocation();
os() << "[pc, #" << offset - pc_delta << "]";
@@ -77,7 +75,7 @@
}
}
- DisassemblerStream& operator<<(const vixl::aarch32::Register reg) OVERRIDE {
+ DisassemblerStream& operator<<(vixl::aarch32::Register reg) OVERRIDE {
if (reg.Is(tr)) {
os() << "tr";
return *this;
@@ -118,20 +116,11 @@
CustomDisassembler(std::ostream& os, const DisassemblerOptions* options)
: PrintDisassembler(&disassembler_stream_), disassembler_stream_(os, this, options) {}
- void PrintPc(uint32_t prog_ctr) OVERRIDE {
+ void PrintCodeAddress(uint32_t prog_ctr) OVERRIDE {
os() << "0x" << std::hex << std::setw(8) << std::setfill('0') << prog_ctr << ": ";
}
- bool IsT32() const {
- return is_t32_;
- }
-
- void SetT32(bool is_t32) {
- is_t32_ = is_t32;
- }
-
private:
- bool is_t32_;
CustomDisassemblerStream disassembler_stream_;
};
@@ -152,7 +141,7 @@
sizeof(unaligned_float), sizeof(unaligned_double)};
const uintptr_t begin = reinterpret_cast<uintptr_t>(options_->base_address_);
const uintptr_t end = reinterpret_cast<uintptr_t>(options_->end_address_);
- uintptr_t literal_addr = RoundDown(disasm_->GetPc(), vixl::aarch32::kRegSizeInBytes) + offset;
+ uintptr_t literal_addr = RoundDown(disasm_->GetCodeAddress(), vixl::aarch32::kRegSizeInBytes) + offset;
if (!options_->absolute_addresses_) {
literal_addr += begin;
@@ -208,12 +197,14 @@
// Remove the Thumb specifier bit; no effect if begin does not point to T32 code.
const uintptr_t instr_ptr = reinterpret_cast<uintptr_t>(begin) & ~1;
- disasm_->SetT32((reinterpret_cast<uintptr_t>(begin) & 1) != 0);
- disasm_->JumpToPc(GetPc(instr_ptr));
+ const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0;
+ disasm_->SetCodeAddress(GetPc(instr_ptr));
- if (disasm_->IsT32()) {
+ if (is_t32) {
const uint16_t* const ip = reinterpret_cast<const uint16_t*>(instr_ptr);
- next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip));
+ const uint16_t* const end_address = reinterpret_cast<const uint16_t*>(
+ GetDisassemblerOptions()->end_address_);
+ next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip, end_address));
} else {
const uint32_t* const ip = reinterpret_cast<const uint32_t*>(instr_ptr);
next = reinterpret_cast<uintptr_t>(disasm_->DecodeA32At(ip));
@@ -230,10 +221,10 @@
// Remove the Thumb specifier bit; no effect if begin does not point to T32 code.
const uintptr_t base = reinterpret_cast<uintptr_t>(begin) & ~1;
- disasm_->SetT32((reinterpret_cast<uintptr_t>(begin) & 1) != 0);
- disasm_->JumpToPc(GetPc(base));
+ const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0;
+ disasm_->SetCodeAddress(GetPc(base));
- if (disasm_->IsT32()) {
+ if (is_t32) {
// The Thumb specifier bits cancel each other.
disasm_->DisassembleT32Buffer(reinterpret_cast<const uint16_t*>(base), end - begin);
} else {
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index d717ec0..b0c4597 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -136,7 +136,9 @@
+ "/core.art";
}
- bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
+ bool GetCachedImageFile(const std::string& image_location,
+ /*out*/std::string* image,
+ /*out*/std::string* error_msg) const {
std::string cache;
bool have_android_data;
bool dalvik_cache_exists;
@@ -151,7 +153,14 @@
*error_msg = "Failed to create dalvik cache";
return false;
}
- return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
+ return GetDalvikCacheFilename(image_location.c_str(), cache.c_str(), image, error_msg);
+ }
+
+ // Returns the path to an image location whose contents differ from the
+ // image at GetImageLocation(). This is used for testing mismatched
+ // image checksums in the oat_file_assistant_tests.
+ std::string GetImageLocation2() const {
+ return GetImageDirectory() + "/core-npic.art";
}
std::string GetDexSrc1() const {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index d098ee2..72dbe6a 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -427,7 +427,7 @@
if (!reg->VerifierInstanceOf(field_class.Ptr())) {
// This should never happen.
std::string temp1, temp2, temp3;
- self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+ self->ThrowNewExceptionF("Ljava/lang/InternalError;",
"Put '%s' that is not instance of field '%s' in '%s'",
reg->GetClass()->GetDescriptor(&temp1),
field_class->GetDescriptor(&temp2),
@@ -1493,7 +1493,7 @@
if (!o->VerifierInstanceOf(arg_type)) {
// This should never happen.
std::string temp1, temp2;
- self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+ self->ThrowNewExceptionF("Ljava/lang/InternalError;",
"Invoking %s with bad arg %d, type '%s' not instance of '%s'",
new_shadow_frame->GetMethod()->GetName(), shorty_pos,
o->GetClass()->GetDescriptor(&temp1),
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 989b7da..22c0fe0 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -287,7 +287,7 @@
if (!obj_result->VerifierInstanceOf(return_type)) {
// This should never happen.
std::string temp1, temp2;
- self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+ self->ThrowNewExceptionF("Ljava/lang/InternalError;",
"Returning '%s' that is not instance of return type '%s'",
obj_result->GetClass()->GetDescriptor(&temp1),
return_type->GetDescriptor(&temp2));
@@ -577,7 +577,7 @@
} else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
// This should never happen.
std::string temp;
- self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+ self->ThrowNewExceptionF("Ljava/lang/InternalError;",
"Throwing '%s' that is not instance of Throwable",
exception->GetClass()->GetDescriptor(&temp));
} else {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index f0ed237..3531852 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -428,10 +428,16 @@
core_spill_mask,
fp_spill_mask,
code_size);
+ // Flush caches before we remove write permission because on some ARMv8 hardware,
+ // flushing caches require write permissions.
+ //
+ // For reference, here are kernel patches discussing about this issue:
+ // https://android.googlesource.com/kernel/msm/%2B/0e7f7bcc3fc87489cda5aa6aff8ce40eed912279
+ // https://patchwork.kernel.org/patch/9047921/
+ FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
+ reinterpret_cast<char*>(code_ptr + code_size));
}
- FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
- reinterpret_cast<char*>(code_ptr + code_size));
number_of_compilations_++;
}
// We need to update the entry point in the runnable state for the instrumentation.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index df0849a..1a77072 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -413,33 +413,7 @@
OatFileAssistant oat_file_assistant(filename.c_str(), target_instruction_set,
false /* load_executable */);
-
- std::ostringstream status;
- bool oat_file_exists = false;
- bool odex_file_exists = false;
- if (oat_file_assistant.OatFileExists()) {
- oat_file_exists = true;
- status << *oat_file_assistant.OatFileName() << " [compilation_filter=";
- status << CompilerFilter::NameOfFilter(oat_file_assistant.OatFileCompilerFilter());
- status << ", status=" << oat_file_assistant.OatFileStatus();
- }
-
- if (oat_file_assistant.OdexFileExists()) {
- odex_file_exists = true;
- if (oat_file_exists) {
- status << "] ";
- }
- status << *oat_file_assistant.OdexFileName() << " [compilation_filter=";
- status << CompilerFilter::NameOfFilter(oat_file_assistant.OdexFileCompilerFilter());
- status << ", status=" << oat_file_assistant.OdexFileStatus();
- }
-
- if (!oat_file_exists && !odex_file_exists) {
- status << "invalid[";
- }
-
- status << "]";
- return env->NewStringUTF(status.str().c_str());
+ return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str());
}
static jint DexFile_getDexOptNeeded(JNIEnv* env,
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 0679360..4d1e1ea 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -16,6 +16,8 @@
#include "oat_file_assistant.h"
+#include <sstream>
+
#include <sys/stat.h>
#include "base/logging.h"
#include "base/stringprintf.h"
@@ -34,15 +36,21 @@
std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
switch (status) {
- case OatFileAssistant::kOatOutOfDate:
- stream << "kOatOutOfDate";
+ case OatFileAssistant::kOatCannotOpen:
+ stream << "kOatCannotOpen";
+ break;
+ case OatFileAssistant::kOatDexOutOfDate:
+ stream << "kOatDexOutOfDate";
+ break;
+ case OatFileAssistant::kOatBootImageOutOfDate:
+ stream << "kOatBootImageOutOfDate";
+ break;
+ case OatFileAssistant::kOatRelocationOutOfDate:
+ stream << "kOatRelocationOutOfDate";
break;
case OatFileAssistant::kOatUpToDate:
stream << "kOatUpToDate";
break;
- case OatFileAssistant::kOatNeedsRelocation:
- stream << "kOatNeedsRelocation";
- break;
default:
UNREACHABLE();
}
@@ -60,7 +68,10 @@
const char* oat_location,
const InstructionSet isa,
bool load_executable)
- : isa_(isa), load_executable_(load_executable), odex_(this), oat_(this) {
+ : isa_(isa),
+ load_executable_(load_executable),
+ odex_(this, /*is_oat_location*/ false),
+ oat_(this, /*is_oat_location*/ true) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
dex_location_.assign(dex_location);
@@ -135,51 +146,13 @@
return true;
}
-OatFileAssistant::DexOptNeeded
-OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
- bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
-
- // See if the oat file is in good shape as is.
- bool oat_okay = oat_.CompilerFilterIsOkay(target, profile_changed);
- if (oat_okay) {
- if (compilation_desired) {
- if (oat_.IsUpToDate()) {
- return kNoDexOptNeeded;
- }
- } else {
- if (!oat_.IsOutOfDate()) {
- return kNoDexOptNeeded;
- }
- }
+int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
+ OatFileInfo& info = GetBestInfo();
+ DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed);
+ if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) {
+ return dexopt_needed;
}
-
- // See if the odex file is in good shape as is.
- bool odex_okay = odex_.CompilerFilterIsOkay(target, profile_changed);
- if (odex_okay) {
- if (compilation_desired) {
- if (odex_.IsUpToDate()) {
- return kNoDexOptNeeded;
- }
- } else {
- if (!odex_.IsOutOfDate()) {
- return kNoDexOptNeeded;
- }
- }
- }
-
- // See if we can get an up-to-date file by running patchoat.
- if (compilation_desired) {
- if (odex_okay && odex_.NeedsRelocation() && odex_.HasPatchInfo()) {
- return kPatchOatNeeded;
- }
-
- if (oat_okay && oat_.NeedsRelocation() && oat_.HasPatchInfo()) {
- return kSelfPatchOatNeeded;
- }
- }
-
- // We can only run dex2oat if there are original dex files.
- return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
+ return -dexopt_needed;
}
// Figure out the currently specified compile filter option in the runtime.
@@ -205,7 +178,7 @@
}
bool OatFileAssistant::IsUpToDate() {
- return OatFileIsUpToDate() || OdexFileIsUpToDate();
+ return GetBestInfo().Status() == kOatUpToDate;
}
OatFileAssistant::ResultOfAttemptToUpdate
@@ -215,59 +188,66 @@
return kUpdateNotAttempted;
}
- switch (GetDexOptNeeded(target, profile_changed)) {
- case kNoDexOptNeeded: return kUpdateSucceeded;
- case kDex2OatNeeded: return GenerateOatFile(error_msg);
- case kPatchOatNeeded: return RelocateOatFile(odex_.Filename(), error_msg);
- case kSelfPatchOatNeeded: return RelocateOatFile(oat_.Filename(), error_msg);
+ OatFileInfo& info = GetBestInfo();
+ switch (info.GetDexOptNeeded(target, profile_changed)) {
+ case kNoDexOptNeeded:
+ return kUpdateSucceeded;
+
+ // TODO: For now, don't bother with all the different ways we can call
+ // dex2oat to generate the oat file. Always generate the oat file as if it
+ // were kDex2OatFromScratch.
+ case kDex2OatFromScratch:
+ case kDex2OatForBootImage:
+ case kDex2OatForRelocation:
+ case kDex2OatForFilter:
+ return GenerateOatFile(error_msg);
+
+ case kPatchoatForRelocation: {
+ return RelocateOatFile(info.Filename(), error_msg);
+ }
}
UNREACHABLE();
}
std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
- // The best oat files are, in descending order of bestness:
- // 1. Properly relocated files. These may be opened executable.
- // 2. Not out-of-date files that are already opened non-executable.
- // 3. Not out-of-date files that we must reopen non-executable.
+ return GetBestInfo().ReleaseFileForUse();
+}
- if (oat_.IsUpToDate()) {
- return oat_.ReleaseFile();
+std::string OatFileAssistant::GetStatusDump() {
+ std::ostringstream status;
+ bool oat_file_exists = false;
+ bool odex_file_exists = false;
+ if (oat_.Status() != kOatCannotOpen) {
+ // If we can open the file, neither Filename nor GetFile should return null.
+ CHECK(oat_.Filename() != nullptr);
+ CHECK(oat_.GetFile() != nullptr);
+
+ oat_file_exists = true;
+ status << *oat_.Filename() << " [compilation_filter=";
+ status << CompilerFilter::NameOfFilter(oat_.GetFile()->GetCompilerFilter());
+ status << ", status=" << oat_.Status();
}
- if (odex_.IsUpToDate()) {
- return odex_.ReleaseFile();
- }
+ if (odex_.Status() != kOatCannotOpen) {
+ // If we can open the file, neither Filename nor GetFile should return null.
+ CHECK(odex_.Filename() != nullptr);
+ CHECK(odex_.GetFile() != nullptr);
- VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
- << " attempting to fall back to interpreting oat file instead.";
-
- if (!oat_.IsOutOfDate() && !oat_.IsExecutable()) {
- return oat_.ReleaseFile();
- }
-
- if (!odex_.IsOutOfDate() && !odex_.IsExecutable()) {
- return odex_.ReleaseFile();
- }
-
- if (!oat_.IsOutOfDate()) {
- load_executable_ = false;
- oat_.Reset();
- if (!oat_.IsOutOfDate()) {
- CHECK(!oat_.IsExecutable());
- return oat_.ReleaseFile();
+ odex_file_exists = true;
+ if (oat_file_exists) {
+ status << "] ";
}
+ status << *odex_.Filename() << " [compilation_filter=";
+ status << CompilerFilter::NameOfFilter(odex_.GetFile()->GetCompilerFilter());
+ status << ", status=" << odex_.Status();
}
- if (!odex_.IsOutOfDate()) {
- load_executable_ = false;
- odex_.Reset();
- if (!odex_.IsOutOfDate()) {
- CHECK(!odex_.IsExecutable());
- return odex_.ReleaseFile();
- }
+ if (!oat_file_exists && !odex_file_exists) {
+ status << "invalid[";
}
- return std::unique_ptr<OatFile>();
+ status << "]";
+ return status.str();
}
std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
@@ -317,62 +297,14 @@
return has_original_dex_files_;
}
-const std::string* OatFileAssistant::OdexFileName() {
- return odex_.Filename();
-}
-
-bool OatFileAssistant::OdexFileExists() {
- return odex_.Exists();
-}
-
OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
return odex_.Status();
}
-bool OatFileAssistant::OdexFileIsOutOfDate() {
- return odex_.IsOutOfDate();
-}
-
-bool OatFileAssistant::OdexFileNeedsRelocation() {
- return odex_.NeedsRelocation();
-}
-
-bool OatFileAssistant::OdexFileIsUpToDate() {
- return odex_.IsUpToDate();
-}
-
-CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() {
- return odex_.CompilerFilter();
-}
-
-const std::string* OatFileAssistant::OatFileName() {
- return oat_.Filename();
-}
-
-bool OatFileAssistant::OatFileExists() {
- return oat_.Exists();
-}
-
OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
return oat_.Status();
}
-bool OatFileAssistant::OatFileIsOutOfDate() {
- return oat_.IsOutOfDate();
-}
-
-bool OatFileAssistant::OatFileNeedsRelocation() {
- return oat_.NeedsRelocation();
-}
-
-bool OatFileAssistant::OatFileIsUpToDate() {
- return oat_.IsUpToDate();
-}
-
-CompilerFilter::Filter OatFileAssistant::OatFileCompilerFilter() {
- return oat_.CompilerFilter();
-}
-
OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
// Verify the dex checksum.
// Note: GetOatDexFile will return null if the dex checksum doesn't match
@@ -383,7 +315,7 @@
dex_location_.c_str(), dex_checksum_pointer, &error_msg);
if (oat_dex_file == nullptr) {
VLOG(oat) << error_msg;
- return kOatOutOfDate;
+ return kOatDexOutOfDate;
}
// Verify the dex checksums for any secondary multidex files
@@ -406,7 +338,7 @@
<< secondary_dex_location
<< ". Expected: " << expected_secondary_checksum
<< ", Actual: " << actual_secondary_checksum;
- return kOatOutOfDate;
+ return kOatDexOutOfDate;
}
} else {
// If we can't get the checksum for the secondary location, we assume
@@ -425,7 +357,7 @@
VLOG(oat) << "No image for oat image checksum to match against.";
if (HasOriginalDexFiles()) {
- return kOatOutOfDate;
+ return kOatBootImageOutOfDate;
}
// If there is no original dex file to fall back to, grudgingly accept
@@ -439,7 +371,7 @@
} else if (file.GetOatHeader().GetImageFileLocationOatChecksum()
!= GetCombinedImageChecksum()) {
VLOG(oat) << "Oat image checksum does not match image checksum.";
- return kOatOutOfDate;
+ return kOatBootImageOutOfDate;
}
} else {
VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
@@ -450,7 +382,7 @@
const ImageInfo* image_info = GetImageInfo();
if (image_info == nullptr) {
VLOG(oat) << "No image to check oat relocation against.";
- return kOatNeedsRelocation;
+ return kOatRelocationOutOfDate;
}
// Verify the oat_data_begin recorded for the image in the oat file matches
@@ -462,7 +394,7 @@
": Oat file image oat_data_begin (" << oat_data_begin << ")"
<< " does not match actual image oat_data_begin ("
<< image_info->oat_data_begin << ")";
- return kOatNeedsRelocation;
+ return kOatRelocationOutOfDate;
}
// Verify the oat_patch_delta recorded for the image in the oat file matches
@@ -473,7 +405,7 @@
": Oat file image patch delta (" << oat_patch_delta << ")"
<< " does not match actual image patch delta ("
<< image_info->patch_delta << ")";
- return kOatNeedsRelocation;
+ return kOatRelocationOutOfDate;
}
} else {
// Oat files compiled in PIC mode do not require relocation.
@@ -841,6 +773,11 @@
return combined_image_checksum_;
}
+OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
+ bool use_oat = oat_.IsUseable() || odex_.Status() == kOatCannotOpen;
+ return use_oat ? oat_ : odex_;
+}
+
std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
DCHECK(oat_file != nullptr);
std::string art_file = ReplaceFileExtension(oat_file->GetLocation(), "art");
@@ -857,16 +794,29 @@
return ret;
}
-OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant)
- : oat_file_assistant_(oat_file_assistant)
+OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant,
+ bool is_oat_location)
+ : oat_file_assistant_(oat_file_assistant), is_oat_location_(is_oat_location)
{}
+bool OatFileAssistant::OatFileInfo::IsOatLocation() {
+ return is_oat_location_;
+}
+
const std::string* OatFileAssistant::OatFileInfo::Filename() {
return filename_provided_ ? &filename_ : nullptr;
}
-bool OatFileAssistant::OatFileInfo::Exists() {
- return GetFile() != nullptr;
+bool OatFileAssistant::OatFileInfo::IsUseable() {
+ switch (Status()) {
+ case kOatCannotOpen:
+ case kOatDexOutOfDate:
+ case kOatBootImageOutOfDate: return false;
+
+ case kOatRelocationOutOfDate:
+ case kOatUpToDate: return true;
+ }
+ UNREACHABLE();
}
OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
@@ -874,7 +824,7 @@
status_attempted_ = true;
const OatFile* file = GetFile();
if (file == nullptr) {
- status_ = kOatOutOfDate;
+ status_ = kOatCannotOpen;
} else {
status_ = oat_file_assistant_->GivenOatFileStatus(*file);
VLOG(oat) << file->GetLocation() << " is " << status_
@@ -884,22 +834,46 @@
return status_;
}
-bool OatFileAssistant::OatFileInfo::IsOutOfDate() {
- return Status() == kOatOutOfDate;
-}
+OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
+ CompilerFilter::Filter target, bool profile_changed) {
+ bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
+ bool filter_okay = CompilerFilterIsOkay(target, profile_changed);
-bool OatFileAssistant::OatFileInfo::NeedsRelocation() {
- return Status() == kOatNeedsRelocation;
-}
+ if (filter_okay && Status() == kOatUpToDate) {
+ // The oat file is in good shape as is.
+ return kNoDexOptNeeded;
+ }
-bool OatFileAssistant::OatFileInfo::IsUpToDate() {
- return Status() == kOatUpToDate;
-}
+ if (filter_okay && !compilation_desired && Status() == kOatRelocationOutOfDate) {
+ // If no compilation is desired, then it doesn't matter if the oat
+ // file needs relocation. It's in good shape as is.
+ return kNoDexOptNeeded;
+ }
-CompilerFilter::Filter OatFileAssistant::OatFileInfo::CompilerFilter() {
- const OatFile* file = GetFile();
- CHECK(file != nullptr);
- return file->GetCompilerFilter();
+ if (filter_okay && Status() == kOatRelocationOutOfDate && HasPatchInfo()) {
+ return kPatchoatForRelocation;
+ }
+
+ if (oat_file_assistant_->HasOriginalDexFiles()) {
+ // Run dex2oat for relocation if we didn't have the patch info necessary
+ // to use patchoat.
+ if (filter_okay && Status() == kOatRelocationOutOfDate) {
+ return kDex2OatForRelocation;
+ }
+
+ if (IsUseable()) {
+ return kDex2OatForFilter;
+ }
+
+ if (Status() == kOatBootImageOutOfDate) {
+ return kDex2OatForBootImage;
+ }
+
+ return kDex2OatFromScratch;
+ }
+
+ // Otherwise there is nothing we can do, even if we want to.
+ return kNoDexOptNeeded;
}
const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
@@ -967,5 +941,31 @@
return std::move(file_);
}
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() {
+ if (Status() == kOatUpToDate) {
+ return ReleaseFile();
+ }
+
+ VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
+ << " attempting to fall back to interpreting oat file instead.";
+
+ if (Status() == kOatRelocationOutOfDate && !IsExecutable()) {
+ return ReleaseFile();
+ }
+
+ if (Status() == kOatRelocationOutOfDate) {
+ // We are loading an oat file for runtime use that needs relocation.
+ // Reload the file non-executable to ensure that we interpret out of the
+ // dex code in the oat file rather than trying to execute the unrelocated
+ // compiled code.
+ oat_file_assistant_->load_executable_ = false;
+ Reset();
+ if (IsUseable()) {
+ CHECK(!IsExecutable());
+ return ReleaseFile();
+ }
+ }
+ return std::unique_ptr<OatFile>();
+}
} // namespace art
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 3f018dc..bed1edc 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -48,41 +48,56 @@
class OatFileAssistant {
public:
enum DexOptNeeded {
- // kNoDexOptNeeded - The code for this dex location is up to date and can
- // be used as is.
+ // No dexopt should (or can) be done to update the apk/jar.
// Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0
kNoDexOptNeeded = 0,
- // kDex2OatNeeded - In order to make the code for this dex location up to
- // date, dex2oat must be run on the dex file.
- // Matches Java: dalvik.system.DexFile.DEX2OAT_NEEDED = 1
- kDex2OatNeeded = 1,
+ // dex2oat should be run to update the apk/jar from scratch.
+ // Matches Java: dalvik.system.DexFile.DEX2OAT_FROM_SCRATCH = 1
+ kDex2OatFromScratch = 1,
- // kPatchOatNeeded - In order to make the code for this dex location up to
- // date, patchoat must be run on the odex file.
- // Matches Java: dalvik.system.DexFile.PATCHOAT_NEEDED = 2
- kPatchOatNeeded = 2,
+ // dex2oat should be run to update the apk/jar because the existing code
+ // is out of date with respect to the boot image.
+ // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_BOOT_IMAGE
+ kDex2OatForBootImage = 2,
- // kSelfPatchOatNeeded - In order to make the code for this dex location
- // up to date, patchoat must be run on the oat file.
- // Matches Java: dalvik.system.DexFile.SELF_PATCHOAT_NEEDED = 3
- kSelfPatchOatNeeded = 3,
+ // dex2oat should be run to update the apk/jar because the existing code
+ // is out of date with respect to the target compiler filter.
+ // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_FILTER
+ kDex2OatForFilter = 3,
+
+ // dex2oat should be run to update the apk/jar because the existing code
+ // is not relocated to match the boot image and does not have the
+ // necessary patch information to use patchoat.
+ // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_RELOCATION
+ kDex2OatForRelocation = 4,
+
+ // patchoat should be run to update the apk/jar.
+ // Matches Java: dalvik.system.DexFile.PATCHOAT_FOR_RELOCATION
+ kPatchoatForRelocation = 5,
};
enum OatStatus {
- // kOatOutOfDate - An oat file is said to be out of date if the file does
- // not exist, is out of date with respect to the dex file or boot image,
- // or does not meet the target compilation type.
- kOatOutOfDate,
+ // kOatCannotOpen - The oat file cannot be opened, because it does not
+ // exist, is unreadable, or otherwise corrupted.
+ kOatCannotOpen,
- // kOatNeedsRelocation - An oat file is said to need relocation if the
- // code is up to date, but not yet properly relocated for address space
- // layout randomization (ASLR). In this case, the oat file is neither
- // "out of date" nor "up to date".
- kOatNeedsRelocation,
+ // kOatDexOutOfDate - The oat file is out of date with respect to the dex file.
+ kOatDexOutOfDate,
- // kOatUpToDate - An oat file is said to be up to date if it is not out of
- // date and has been properly relocated for the purposes of ASLR.
+ // kOatBootImageOutOfDate - The oat file is up to date with respect to the
+ // dex file, but is out of date with respect to the boot image.
+ kOatBootImageOutOfDate,
+
+ // kOatRelocationOutOfDate - The oat file is up to date with respect to
+ // the dex file and boot image, but contains compiled code that has the
+ // wrong patch delta with respect to the boot image. Patchoat should be
+ // run on the oat file to update the patch delta of the compiled code to
+ // match the boot image.
+ kOatRelocationOutOfDate,
+
+ // kOatUpToDate - The oat file is completely up to date with respect to
+ // the dex file and boot image.
kOatUpToDate,
};
@@ -142,8 +157,10 @@
// dex location that is at least as good as an oat file generated with the
// given compiler filter. profile_changed should be true to indicate the
// profile has recently changed for this dex location.
- DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
- bool profile_changed = false);
+ // Returns a positive status code if the status refers to the oat file in
+ // the oat location. Returns a negative status code if the status refers to
+ // the oat file in the odex location.
+ int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, bool profile_changed = false);
// Returns true if there is up-to-date code for this dex location,
// irrespective of the compiler filter of the up-to-date code.
@@ -179,6 +196,10 @@
// the OatFileAssistant object.
std::unique_ptr<OatFile> GetBestOatFile();
+ // Returns a human readable description of the status of the code for the
+ // dex file. The returned description is for debugging purposes only.
+ std::string GetStatusDump();
+
// Open and returns an image space associated with the oat file.
static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
@@ -205,43 +226,16 @@
// really an oat file. The odex file will often, but not always, have a
// patch delta of 0 and need to be relocated before use for the purposes of
// ASLR. The odex file is treated as if it were read-only.
- // These methods return the location and status of the odex file for the dex
- // location.
- // Notes:
- // * OdexFileName may return null if the odex file name could not be
- // determined.
- const std::string* OdexFileName();
- bool OdexFileExists();
+ //
+ // Returns the status of the odex file for the dex location.
OatStatus OdexFileStatus();
- bool OdexFileIsOutOfDate();
- bool OdexFileNeedsRelocation();
- bool OdexFileIsUpToDate();
- // Must only be called if the associated odex file exists, i.e, if
- // |OdexFileExists() == true|.
- CompilerFilter::Filter OdexFileCompilerFilter();
// When the dex files is compiled on the target device, the oat file is the
// result. The oat file will have been relocated to some
// (possibly-out-of-date) offset for ASLR.
- // These methods return the location and status of the target oat file for
- // the dex location.
//
- // Notes:
- // * OatFileName may return null if the oat file name could not be
- // determined.
- const std::string* OatFileName();
- bool OatFileExists();
+ // Returns the status of the oat file for the dex location.
OatStatus OatFileStatus();
- bool OatFileIsOutOfDate();
- bool OatFileNeedsRelocation();
- bool OatFileIsUpToDate();
- // Must only be called if the associated oat file exists, i.e, if
- // |OatFileExists() == true|.
- CompilerFilter::Filter OatFileCompilerFilter();
-
- // Return the status for a given opened oat file with respect to the dex
- // location.
- OatStatus GivenOatFileStatus(const OatFile& file);
// Generates the oat file by relocation from the named input file.
// This does not check the current status before attempting to relocate the
@@ -311,29 +305,39 @@
// Initially the info is for no file in particular. It will treat the
// file as out of date until Reset is called with a real filename to use
// the cache for.
- explicit OatFileInfo(OatFileAssistant* oat_file_assistant);
+ // Pass true for is_oat_location if the information associated with this
+ // OatFileInfo is for the oat location, as opposed to the odex location.
+ OatFileInfo(OatFileAssistant* oat_file_assistant, bool is_oat_location);
+
+ bool IsOatLocation();
const std::string* Filename();
- bool Exists();
+
+ // Returns true if this oat file can be used for running code. The oat
+ // file can be used for running code as long as it is not out of date with
+ // respect to the dex code or boot image. An oat file that is out of date
+ // with respect to relocation is considered useable, because it's possible
+ // to interpret the dex code rather than run the unrelocated compiled
+ // code.
+ bool IsUseable();
+
+ // Returns the status of this oat file.
OatStatus Status();
- bool IsOutOfDate();
- bool NeedsRelocation();
- bool IsUpToDate();
- // Must only be called if the associated file exists, i.e, if
- // |Exists() == true|.
- CompilerFilter::Filter CompilerFilter();
+
+ // Return the DexOptNeeded value for this oat file with respect to the
+ // given target_compilation_filter.
+ // profile_changed should be true to indicate the profile has recently
+ // changed for this dex location.
+ // If patchoat is needed, this function will return the kPatchOatNeeded
+ // status, not the kSelfPatchOatNeeded status.
+ DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+ bool profile_changed);
// Returns the loaded file.
// Loads the file if needed. Returns null if the file failed to load.
// The caller shouldn't clean up or free the returned pointer.
const OatFile* GetFile();
- // Returns true if the compiler filter used to generate the file is at
- // least as good as the given target filter. profile_changed should be
- // true to indicate the profile has recently changed for this dex
- // location.
- bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
-
// Returns true if the file is opened executable.
bool IsExecutable();
@@ -348,6 +352,23 @@
// file with the given filename.
void Reset(const std::string& filename);
+ // Release the loaded oat file for runtime use.
+ // Returns null if the oat file hasn't been loaded or is out of date.
+ // Ensures the returned file is not loaded executable if it has unuseable
+ // compiled code.
+ //
+ // After this call, no other methods of the OatFileInfo should be
+ // called, because access to the loaded oat file has been taken away from
+ // the OatFileInfo object.
+ std::unique_ptr<OatFile> ReleaseFileForUse();
+
+ private:
+ // Returns true if the compiler filter used to generate the file is at
+ // least as good as the given target filter. profile_changed should be
+ // true to indicate the profile has recently changed for this dex
+ // location.
+ bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
+
// Release the loaded oat file.
// Returns null if the oat file hasn't been loaded.
//
@@ -356,8 +377,8 @@
// the OatFileInfo object.
std::unique_ptr<OatFile> ReleaseFile();
- private:
OatFileAssistant* oat_file_assistant_;
+ const bool is_oat_location_;
bool filename_provided_ = false;
std::string filename_;
@@ -374,6 +395,13 @@
bool file_released_ = false;
};
+ // Return info for the best oat file.
+ OatFileInfo& GetBestInfo();
+
+ // Return the status for a given opened oat file with respect to the dex
+ // location.
+ OatStatus GivenOatFileStatus(const OatFile& file);
+
// Returns the current image location.
// Returns an empty string if the image location could not be retrieved.
//
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d4337b9..5730cf2 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -49,9 +49,9 @@
// Pre-Relocate the image to a known non-zero offset so we don't have to
// deal with the runtime randomly relocating the image by 0 and messing up
// the expected results of the tests.
- bool PreRelocateImage(std::string* error_msg) {
+ bool PreRelocateImage(const std::string& image_location, std::string* error_msg) {
std::string image;
- if (!GetCachedImageFile(&image, error_msg)) {
+ if (!GetCachedImageFile(image_location, &image, error_msg)) {
return false;
}
@@ -60,7 +60,7 @@
std::vector<std::string> argv;
argv.push_back(patchoat);
- argv.push_back("--input-image-location=" + GetImageLocation());
+ argv.push_back("--input-image-location=" + image_location);
argv.push_back("--output-image-file=" + image);
argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
argv.push_back("--base-offset-delta=0x00008000");
@@ -69,8 +69,8 @@
virtual void PreRuntimeCreate() {
std::string error_msg;
- ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg;
-
+ ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg;
+ ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg;
UnreserveImageSpace();
}
@@ -78,24 +78,32 @@
ReserveImageSpace();
}
- // Generate a non-PIC odex file for the purposes of test.
- // The generated odex file will be un-relocated.
- void GenerateOdexForTest(const std::string& dex_location,
- const std::string& odex_location,
- CompilerFilter::Filter filter,
- bool pic = false,
- bool with_patch_info = true) {
- // Temporarily redirect the dalvik cache so dex2oat doesn't find the
- // relocated image file.
+ // Generate an oat file for the purposes of test.
+ void GenerateOatForTest(const std::string& dex_location,
+ const std::string& oat_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_patch_info,
+ bool with_alternate_image) {
std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
- ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+
+ if (!relocate) {
+ // Temporarily redirect the dalvik cache so dex2oat doesn't find the
+ // relocated image file.
+ ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+ }
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
- args.push_back("--oat-file=" + odex_location);
+ args.push_back("--oat-file=" + oat_location);
args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
args.push_back("--runtime-arg");
+
+ // Use -Xnorelocate regardless of the relocate argument.
+ // We control relocation by redirecting the dalvik cache when needed
+ // rather than use this flag.
args.push_back("-Xnorelocate");
if (pic) {
@@ -106,14 +114,22 @@
args.push_back("--include-patch-information");
}
+ std::string image_location = GetImageLocation();
+ if (with_alternate_image) {
+ args.push_back("--boot-image=" + GetImageLocation2());
+ }
+
std::string error_msg;
ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
- ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
- // Verify the odex file was generated as expected and really is
- // unrelocated.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
- odex_location.c_str(),
+ if (!relocate) {
+ // Restore the dalvik cache if needed.
+ ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
+ }
+
+ // Verify the odex file was generated as expected.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
nullptr,
nullptr,
false,
@@ -125,24 +141,59 @@
EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo());
EXPECT_EQ(filter, odex_file->GetCompilerFilter());
- if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
- const std::vector<gc::space::ImageSpace*> image_spaces =
- Runtime::Current()->GetHeap()->GetBootImageSpaces();
- ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
- const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
- const OatHeader& oat_header = odex_file->GetOatHeader();
- uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
- EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
- EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());
+ std::unique_ptr<ImageHeader> image_header(
+ gc::space::ImageSpace::ReadImageHeader(image_location.c_str(),
+ kRuntimeISA,
+ &error_msg));
+ ASSERT_TRUE(image_header != nullptr) << error_msg;
+ const OatHeader& oat_header = odex_file->GetOatHeader();
+ uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
+
+ if (CompilerFilter::DependsOnImageChecksum(filter)) {
+ if (with_alternate_image) {
+ EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+ } else {
+ EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+ }
}
+
+ if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+ if (relocate) {
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ } else {
+ EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ }
+ }
+ }
+
+ // Generate a non-PIC odex file for the purposes of test.
+ // The generated odex file will be un-relocated.
+ void GenerateOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/false,
+ /*with_patch_info*/true,
+ /*with_alternate_image*/false);
}
void GeneratePicOdexForTest(const std::string& dex_location,
const std::string& odex_location,
CompilerFilter::Filter filter) {
- GenerateOdexForTest(dex_location, odex_location, filter, true, false);
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/true,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/false);
}
// Generate a non-PIC odex file without patch information for the purposes
@@ -150,7 +201,43 @@
void GenerateNoPatchOdexForTest(const std::string& dex_location,
const std::string& odex_location,
CompilerFilter::Filter filter) {
- GenerateOdexForTest(dex_location, odex_location, filter, false, false);
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/false,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/false);
+ }
+
+ // Generate an oat file in the oat location.
+ void GenerateOatForTest(const char* dex_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_patch_info,
+ bool with_alternate_image) {
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+ GenerateOatForTest(dex_location,
+ oat_location,
+ filter,
+ relocate,
+ pic,
+ with_patch_info,
+ with_alternate_image);
+ }
+
+ // Generate a standard oat file in the oat location.
+ void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ filter,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/false);
}
private:
@@ -211,36 +298,6 @@
}
};
-// Generate an oat file for the purposes of test, as opposed to testing
-// generation of oat files.
-static void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
- // Use an oat file assistant to find the proper oat location.
- std::string oat_location;
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
- dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
-
- std::vector<std::string> args;
- args.push_back("--dex-file=" + std::string(dex_location));
- args.push_back("--oat-file=" + oat_location);
- args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- args.push_back("--runtime-arg");
- args.push_back("-Xnorelocate");
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
- // Verify the oat file was generated as expected.
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location.c_str(),
- oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- dex_location,
- &error_msg));
- ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
- EXPECT_EQ(filter, oat_file->GetCompilerFilter());
-}
-
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, DexNoOat) {
@@ -249,26 +306,18 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OdexFileStatus());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
- EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -307,17 +356,11 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -337,17 +380,12 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// Case: We have a DEX file and speed-profile OAT file for it.
@@ -364,19 +402,13 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, false));
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, false));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, true));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, true));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -416,7 +448,7 @@
Copy(GetMultiDexSrc2(), dex_location);
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed, false));
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -453,10 +485,10 @@
EXPECT_EQ(2u, dex_files.size());
}
-// Case: We have a DEX file and out-of-date OAT file.
-// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, OatOutOfDate) {
- std::string dex_location = GetScratchDir() + "/OatOutOfDate.jar";
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// dex checksum.
+TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
// We create a dex, generate an oat for it, then overwrite the dex with a
// different dex to make the oat out of date.
@@ -465,18 +497,68 @@
Copy(GetDexSrc2(), dex_location);
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
+ EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// boot image.
+TEST_F(OatFileAssistantTest, OatImageOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kSpeed,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/true);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+ EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+ EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OatFileStatus());
+ EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
+// respect to the boot image.
+// It shouldn't matter that the OAT file is out of date, because it is
+// verify-at-runtime.
+TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kVerifyAtRuntime,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_patch_info*/false,
+ /*with_alternate_image*/true);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+ EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -495,17 +577,12 @@
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
- EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+ EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
// We should still be able to get the non-executable odex file to run from.
@@ -529,16 +606,12 @@
// Verify the status.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+ EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Make the oat file up to date.
@@ -551,12 +624,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Verify we can load the dex files from it.
@@ -590,19 +659,14 @@
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
- EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+ EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, // Can't run dex2oat because dex file is stripped.
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Make the oat file up to date.
@@ -617,14 +681,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Verify we can load the dex files from it.
@@ -654,13 +712,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Make the oat file up to date. This should have no effect.
@@ -673,13 +726,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -699,20 +747,14 @@
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
- EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded,
+ EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
// Make the oat file up to date.
@@ -725,14 +767,8 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
- EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -757,7 +793,7 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(),
oat_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
// Make the oat file up to date.
@@ -778,7 +814,7 @@
// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
// OAT files both have patch delta of 0.
-// Expect: It shouldn't crash, and status is kPatchOatNeeded.
+// Expect: It shouldn't crash, and status is kSelfPatchOatNeeded.
TEST_F(OatFileAssistantTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -796,16 +832,15 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(),
oat_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+ // kPatchoatForRelocation is expected rather than -kPatchoatForRelocation
+ // based on the assumption that the oat location is more up-to-date than the odex
+ // location, even if they both need relocation.
+ EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
// Things aren't relocated, so it should fall back to interpreted.
@@ -833,16 +868,12 @@
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -861,16 +892,12 @@
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -958,7 +985,7 @@
// Verify it didn't create an oat in the default location.
OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_FALSE(ofm.OatFileExists());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OatFileStatus());
}
// Case: We have a DEX file but can't write the oat file.
@@ -1043,14 +1070,10 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// Case: Very short, non-existent Dex location.
@@ -1063,12 +1086,8 @@
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
// Trying to make it up to date should have no effect.
@@ -1087,16 +1106,12 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_FALSE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// A task to generate a dex location. Used by the RaceToGenerate test.
@@ -1226,7 +1241,7 @@
oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+ EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
@@ -1263,6 +1278,15 @@
// Verify the dexopt status values from dalvik.system.DexFile
// match the OatFileAssistant::DexOptStatus values.
TEST_F(OatFileAssistantTest, DexOptStatusValues) {
+ std::pair<OatFileAssistant::DexOptNeeded, const char*> mapping[] = {
+ {OatFileAssistant::kNoDexOptNeeded, "NO_DEXOPT_NEEDED"},
+ {OatFileAssistant::kDex2OatFromScratch, "DEX2OAT_FROM_SCRATCH"},
+ {OatFileAssistant::kDex2OatForBootImage, "DEX2OAT_FOR_BOOT_IMAGE"},
+ {OatFileAssistant::kDex2OatForFilter, "DEX2OAT_FOR_FILTER"},
+ {OatFileAssistant::kDex2OatForRelocation, "DEX2OAT_FOR_RELOCATION"},
+ {OatFileAssistant::kPatchoatForRelocation, "PATCHOAT_FOR_RELOCATION"}
+ };
+
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
ClassLinker* linker = Runtime::Current()->GetClassLinker();
@@ -1271,35 +1295,16 @@
ASSERT_FALSE(dexfile.Get() == nullptr);
linker->EnsureInitialized(soa.Self(), dexfile, true, true);
- ArtField* no_dexopt_needed = mirror::Class::FindStaticField(
- soa.Self(), dexfile, "NO_DEXOPT_NEEDED", "I");
- ASSERT_FALSE(no_dexopt_needed == nullptr);
- EXPECT_EQ(no_dexopt_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, no_dexopt_needed->GetInt(dexfile.Get()));
-
- ArtField* dex2oat_needed = mirror::Class::FindStaticField(
- soa.Self(), dexfile, "DEX2OAT_NEEDED", "I");
- ASSERT_FALSE(dex2oat_needed == nullptr);
- EXPECT_EQ(dex2oat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, dex2oat_needed->GetInt(dexfile.Get()));
-
- ArtField* patchoat_needed = mirror::Class::FindStaticField(
- soa.Self(), dexfile, "PATCHOAT_NEEDED", "I");
- ASSERT_FALSE(patchoat_needed == nullptr);
- EXPECT_EQ(patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
- EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, patchoat_needed->GetInt(dexfile.Get()));
-
- ArtField* self_patchoat_needed = mirror::Class::FindStaticField(
- soa.Self(), dexfile, "SELF_PATCHOAT_NEEDED", "I");
- ASSERT_FALSE(self_patchoat_needed == nullptr);
- EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
- EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get()));
+ for (std::pair<OatFileAssistant::DexOptNeeded, const char*> field : mapping) {
+ ArtField* art_field = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, field.second, "I");
+ ASSERT_FALSE(art_field == nullptr);
+ EXPECT_EQ(art_field->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(field.first, art_field->GetInt(dexfile.Get()));
+ }
}
// TODO: More Tests:
-// * Image checksum change is out of date for kIntepretOnly, but not
-// kVerifyAtRuntime. But target of kVerifyAtRuntime still says current
-// kInterpretOnly is out of date.
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
// * Test using secondary isa
@@ -1313,5 +1318,4 @@
// because it's unrelocated and no dex2oat
// * Test unrelocated specific target compilation type can be relocated to
// make it up to date.
-
} // namespace art
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index b3dab58..843be92 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -20,6 +20,7 @@
#include "base/logging.h"
#include "base/unix_file/fd_file.h"
+#include "dex_file.h"
namespace art {
@@ -73,10 +74,19 @@
return nullptr;
}
+ return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg);
+}
+
+VdexFile* VdexFile::Open(int file_fd,
+ size_t vdex_length,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ std::string* error_msg) {
std::unique_ptr<MemMap> mmap(MemMap::MapFile(vdex_length,
writable ? PROT_READ | PROT_WRITE : PROT_READ,
MAP_SHARED,
- vdex_file->Fd(),
+ file_fd,
0 /* start offset */,
low_4gb,
vdex_filename.c_str(),
@@ -90,4 +100,16 @@
return new VdexFile(mmap.release());
}
+const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const {
+ DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End()));
+ if (cursor == nullptr) {
+ // Beginning of the iteration, return the first dex file if there is one.
+ return HasDexSection() ? DexBegin() : nullptr;
+ } else {
+ // Fetch the next dex file. Return null if there is none.
+ const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_;
+ return (data == DexEnd()) ? nullptr : data;
+ }
+}
+
} // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index edd6ffe..75a0d5e 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -71,6 +71,13 @@
bool low_4gb,
std::string* error_msg);
+ static VdexFile* Open(int file_fd,
+ size_t vdex_length,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ std::string* error_msg);
+
const uint8_t* Begin() const { return mmap_->Begin(); }
const uint8_t* End() const { return mmap_->End(); }
size_t Size() const { return mmap_->Size(); }
@@ -84,9 +91,37 @@
Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize());
}
+ ArrayRef<const uint8_t> GetQuickeningInfo() const {
+ return ArrayRef<const uint8_t>(
+ GetVerifierDepsData().data() + GetHeader().GetVerifierDepsSize(),
+ GetHeader().GetQuickeningInfoSize());
+ }
+
+ bool IsValid() const {
+ return mmap_->Size() >= sizeof(Header) && GetHeader().IsValid();
+ }
+
+ // This method is for iterating over the dex files in the vdex. If `cursor` is null,
+ // the first dex file is returned. If `cursor` is not null, it must point to a dex
+ // file and this method returns the next dex file if there is one, or null if there
+ // is none.
+ const uint8_t* GetNextDexFileData(const uint8_t* cursor) const;
+
private:
explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
+ bool HasDexSection() const {
+ return GetHeader().GetDexSize() != 0;
+ }
+
+ const uint8_t* DexBegin() const {
+ return Begin() + sizeof(Header);
+ }
+
+ const uint8_t* DexEnd() const {
+ return Begin() + sizeof(Header) + GetHeader().GetDexSize();
+ }
+
std::unique_ptr<MemMap> mmap_;
DISALLOW_COPY_AND_ASSIGN(VdexFile);
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
index 02c609e..6b25747 100644
--- a/test/538-checker-embed-constants/src/Main.java
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -30,7 +30,7 @@
/// CHECK-START-ARM: int Main.and255(int) disassembly (after)
/// CHECK-NOT: movs {{r\d+}}, #255
- /// CHECK: and {{r\d+}}, {{r\d+}}, #255
+ /// CHECK: and {{r\d+}}, {{r\d+}}, #0xff
public static int and255(int arg) {
return arg & 255;
@@ -46,7 +46,7 @@
/// CHECK-START-ARM: int Main.andNot15(int) disassembly (after)
/// CHECK-NOT: mvn {{r\d+}}, #15
- /// CHECK: bic {{r\d+}}, {{r\d+}}, #15
+ /// CHECK: bic {{r\d+}}, {{r\d+}}, #0xf
public static int andNot15(int arg) {
return arg & ~15;
@@ -54,7 +54,7 @@
/// CHECK-START-ARM: int Main.or255(int) disassembly (after)
/// CHECK-NOT: movs {{r\d+}}, #255
- /// CHECK: orr {{r\d+}}, {{r\d+}}, #255
+ /// CHECK: orr {{r\d+}}, {{r\d+}}, #0xff
public static int or255(int arg) {
return arg | 255;
@@ -70,7 +70,7 @@
/// CHECK-START-ARM: int Main.orNot15(int) disassembly (after)
/// CHECK-NOT: mvn {{r\d+}}, #15
- /// CHECK: orn {{r\d+}}, {{r\d+}}, #15
+ /// CHECK: orn {{r\d+}}, {{r\d+}}, #0xf
public static int orNot15(int arg) {
return arg | ~15;
@@ -78,7 +78,7 @@
/// CHECK-START-ARM: int Main.xor255(int) disassembly (after)
/// CHECK-NOT: movs {{r\d+}}, #255
- /// CHECK: eor {{r\d+}}, {{r\d+}}, #255
+ /// CHECK: eor {{r\d+}}, {{r\d+}}, #0xff
public static int xor255(int arg) {
return arg ^ 255;
@@ -104,7 +104,7 @@
/// CHECK-NOT: movs {{r\d+}}, #255
/// CHECK-NOT: and{{(\.w)?}}
/// CHECK-NOT: bic{{(\.w)?}}
- /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #255
+ /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #0xff
/// CHECK-DAG: movs {{r\d+}}, #0
/// CHECK-NOT: and{{(\.w)?}}
/// CHECK-NOT: bic{{(\.w)?}}
@@ -131,7 +131,7 @@
/// CHECK-NOT: mvn {{r\d+}}, #15
/// CHECK-NOT: and{{(\.w)?}}
/// CHECK-NOT: bic{{(\.w)?}}
- /// CHECK: bic {{r\d+}}, {{r\d+}}, #15
+ /// CHECK: bic {{r\d+}}, {{r\d+}}, #0xf
/// CHECK-NOT: and{{(\.w)?}}
/// CHECK-NOT: bic{{(\.w)?}}
@@ -144,8 +144,8 @@
/// CHECK-NOT: mvn {{r\d+}}, #15
/// CHECK-NOT: and{{(\.w)?}}
/// CHECK-NOT: bic{{(\.w)?}}
- /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #15
- /// CHECK-DAG: bic {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #0xf
+ /// CHECK-DAG: bic {{r\d+}}, {{r\d+}}, #0xf
/// CHECK-NOT: and{{(\.w)?}}
/// CHECK-NOT: bic{{(\.w)?}}
@@ -157,7 +157,7 @@
/// CHECK-NOT: movs {{r\d+}}, #255
/// CHECK-NOT: orr{{(\.w)?}}
/// CHECK-NOT: orn
- /// CHECK: orr {{r\d+}}, {{r\d+}}, #255
+ /// CHECK: orr {{r\d+}}, {{r\d+}}, #0xff
/// CHECK-NOT: orr{{(\.w)?}}
/// CHECK-NOT: orn
@@ -183,7 +183,7 @@
/// CHECK-NOT: mvn {{r\d+}}, #15
/// CHECK-NOT: orr{{(\.w)?}}
/// CHECK-NOT: orn
- /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #0xf
/// CHECK-DAG: mvn {{r\d+}}, #0
/// CHECK-NOT: orr{{(\.w)?}}
/// CHECK-NOT: orn
@@ -197,8 +197,8 @@
/// CHECK-NOT: mvn {{r\d+}}, #15
/// CHECK-NOT: orr{{(\.w)?}}
/// CHECK-NOT: orn
- /// CHECK-DAG: orr {{r\d+}}, {{r\d+}}, #15
- /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #15
+ /// CHECK-DAG: orr {{r\d+}}, {{r\d+}}, #0xf
+ /// CHECK-DAG: orn {{r\d+}}, {{r\d+}}, #0xf
/// CHECK-NOT: orr{{(\.w)?}}
/// CHECK-NOT: orn
@@ -209,7 +209,7 @@
/// CHECK-START-ARM: long Main.xor255(long) disassembly (after)
/// CHECK-NOT: movs {{r\d+}}, #255
/// CHECK-NOT: eor{{(\.w)?}}
- /// CHECK: eor {{r\d+}}, {{r\d+}}, #255
+ /// CHECK: eor {{r\d+}}, {{r\d+}}, #0xff
/// CHECK-NOT: eor{{(\.w)?}}
public static long xor255(long arg) {
@@ -257,8 +257,8 @@
/// CHECK-NOT: movs {{r\d+}}, #15
/// CHECK-NOT: mov.w {{r\d+}}, #-268435456
/// CHECK-NOT: eor{{(\.w)?}}
- /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #15
- /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #4026531840
+ /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #0xf
+ /// CHECK-DAG: eor {{r\d+}}, {{r\d+}}, #0xf0000000
/// CHECK-NOT: eor{{(\.w)?}}
public static long xor0xf00000000000000f(long arg) {
diff --git a/test/628-vdex/expected.txt b/test/628-vdex/expected.txt
new file mode 100644
index 0000000..d0f61f6
--- /dev/null
+++ b/test/628-vdex/expected.txt
@@ -0,0 +1,2 @@
+In foo
+In foo
diff --git a/test/628-vdex/info.txt b/test/628-vdex/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/628-vdex/info.txt
diff --git a/test/628-vdex/run b/test/628-vdex/run
new file mode 100644
index 0000000..f1b0a95
--- /dev/null
+++ b/test/628-vdex/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 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.
+
+exec ${RUN} --vdex "${@}"
diff --git a/test/628-vdex/src/Main.java b/test/628-vdex/src/Main.java
new file mode 100644
index 0000000..7ceab2c
--- /dev/null
+++ b/test/628-vdex/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+ Main() {
+ // Will be quickened with RETURN_VOID_NO_BARRIER.
+ }
+
+ public static void main(String[] args) {
+ Main m = new Main();
+ Object o = m;
+ // The call and field accesses will be quickened.
+ m.foo(m.a);
+
+ // The checkcast will be quickened.
+ m.foo(((Main)o).a);
+ }
+
+ int a;
+ void foo(int a) {
+ System.out.println("In foo");
+ }
+}
+
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 554f66d..96b984d 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -608,10 +608,7 @@
TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS :=
# Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT).
-# 484: Baker's fast path based read barrier compiler instrumentation generates code containing
-# more parallel moves on x86, thus some Checker assertions may fail.
-TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \
- 484-checker-register-hints
+TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS :=
# Tests that should fail in the read barrier configuration with JIT (Optimizing compiler).
TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index c525b2b..bb3a3ad 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -58,6 +58,7 @@
ARGS=""
EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
DRY_RUN="n" # if y prepare to run the test but don't run it.
+TEST_VDEX="n"
while true; do
if [ "x$1" = "x--quiet" ]; then
@@ -243,6 +244,9 @@
elif [ "x$1" = "x--dry-run" ]; then
DRY_RUN="y"
shift
+ elif [ "x$1" = "x--vdex" ]; then
+ TEST_VDEX="y"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -444,6 +448,7 @@
fi
dex2oat_cmdline="true"
+vdex_cmdline="true"
mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
strip_cmdline="true"
@@ -473,6 +478,9 @@
# Use -k 1m to SIGKILL it a minute later if it hasn't ended.
dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}"
fi
+ if [ "$TEST_VDEX" = "y" ]; then
+ vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+ fi
fi
if [ "$STRIP_DEX" = "y" ]; then
@@ -513,6 +521,7 @@
# Remove whitespace.
dex2oat_cmdline=$(echo $dex2oat_cmdline)
dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
+vdex_cmdline=$(echo $vdex_cmdline)
if [ "$HOST" = "n" ]; then
adb root > /dev/null
@@ -553,6 +562,7 @@
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
export PATH=$ANDROID_ROOT/bin:$PATH && \
$dex2oat_cmdline && \
+ $vdex_cmdline && \
$strip_cmdline && \
$dalvikvm_cmdline"
@@ -626,7 +636,7 @@
fi
if [ "$DEV_MODE" = "y" ]; then
- echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $strip_cmdline && $cmdline"
+ echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline"
fi
cd $ANDROID_BUILD_TOP
@@ -634,6 +644,7 @@
rm -rf ${DEX_LOCATION}/dalvik-cache/
mkdir -p ${mkdir_locations} || exit 1
$dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
+ $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
# For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
diff --git a/test/run-test b/test/run-test
index 37eefb3..ea9622a 100755
--- a/test/run-test
+++ b/test/run-test
@@ -351,6 +351,9 @@
elif [ "x$1" = "x--bisection-search" ]; then
bisection_search="yes"
shift
+ elif [ "x$1" = "x--vdex" ]; then
+ run_args="${run_args} --vdex"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
usage="yes"
@@ -640,6 +643,7 @@
echo " --pic-test Compile the test code position independent."
echo " --quiet Don't print anything except failure messages"
echo " --bisection-search Perform bisection bug search."
+ echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild."
) 1>&2 # Direct to stderr so usage is not printed if --quiet is set.
exit 1
fi
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index 27c2054..493eafb 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -48,7 +48,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, test)
LOCAL_JAR_MANIFEST := test/manifest.txt
-LOCAL_STATIC_JAVA_LIBRARIES := ahat junit
+LOCAL_STATIC_JAVA_LIBRARIES := ahat junit-host
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := ahat-tests