Merge "Handle safe mode in PackageManager."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c4374f7..0a465c4 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -83,16 +83,16 @@
ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX := $(dir $(ART_TEST_TARGET_GTEST_Main_DEX))$(subst Main,VerifierDepsMulti,$(basename $(notdir $(ART_TEST_TARGET_GTEST_Main_DEX))))$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
$(ART_TEST_HOST_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(HOST_OUT_EXECUTABLES)/smali
- $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^)
+ $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
$(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(HOST_OUT_EXECUTABLES)/smali
- $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^)
+ $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
$(ART_TEST_HOST_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMulti_SRC) $(HOST_OUT_EXECUTABLES)/smali
- $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^)
+ $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
$(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMulti_SRC) $(HOST_OUT_EXECUTABLES)/smali
- $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^)
+ $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
# Dex file dependencies for each gtest.
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
@@ -171,6 +171,12 @@
# TODO: document why this is needed.
ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
+# The dexdiag test requires the dexdiag utility.
+ART_GTEST_dexdiag_test_HOST_DEPS := \
+ $(HOST_OUT_EXECUTABLES)/dexdiag
+ART_GTEST_dexdiag_test_TARGET_DEPS := \
+ dexdiag
+
# The dexdump test requires an image and the dexdump utility.
# TODO: rename into dexdump when migration completes
ART_GTEST_dexdump_test_HOST_DEPS := \
@@ -244,6 +250,7 @@
art_compiler_tests \
art_compiler_host_tests \
art_dex2oat_tests \
+ art_dexdiag_tests \
art_dexdump_tests \
art_dexlayout_tests \
art_dexlist_tests \
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 874e357..fbab9df 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -179,6 +179,40 @@
uint16_t class_def_index,
bool requires)
REQUIRES(!requires_constructor_barrier_lock_);
+
+ // Do the <init> methods for this class require a constructor barrier (prior to the return)?
+ // The answer is "yes", if and only if this class has any instance final fields.
+ // (This must not be called for any non-<init> methods; the answer would be "no").
+ //
+ // ---
+ //
+ // JLS 17.5.1 "Semantics of final fields" mandates that all final fields are frozen at the end
+ // of the invoked constructor. The constructor barrier is a conservative implementation means of
+ // enforcing the freezes happen-before the object being constructed is observable by another
+ // thread.
+ //
+ // Note: This question only makes sense for instance constructors;
+ // static constructors (despite possibly having finals) never need
+ // a barrier.
+ //
+ // JLS 12.4.2 "Detailed Initialization Procedure" approximately describes
+ // class initialization as:
+ //
+ // lock(class.lock)
+ // class.state = initializing
+ // unlock(class.lock)
+ //
+ // invoke <clinit>
+ //
+ // lock(class.lock)
+ // class.state = initialized
+ // unlock(class.lock) <-- acts as a release
+ //
+ // The last operation in the above example acts as an atomic release
+ // for any stores in <clinit>, which ends up being stricter
+ // than what a constructor barrier needs.
+ //
+ // See also QuasiAtomic::ThreadFenceForConstructor().
bool RequiresConstructorBarrier(Thread* self,
const DexFile* dex_file,
uint16_t class_def_index)
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 476906a..d38d5f8 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1749,6 +1749,7 @@
phi = NewPhi(new_preheader, instruction, type);
}
user->ReplaceInput(phi, index); // Removes the use node from the list.
+ induction_range_.Replace(user, instruction, phi); // update induction
}
}
// Scan all environment uses of an instruction and replace each later use with a phi node.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index d7cc577..ebd578c 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -3067,6 +3067,15 @@
// Will be generated at use site.
}
+void LocationsBuilderARM::VisitConstructorFence(HConstructorFence* constructor_fence) {
+ constructor_fence->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM::VisitConstructorFence(
+ HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
+}
+
void LocationsBuilderARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
memory_barrier->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index eee832a..78b627a 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -5479,6 +5479,15 @@
}
}
+void LocationsBuilderARM64::VisitConstructorFence(HConstructorFence* constructor_fence) {
+ constructor_fence->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitConstructorFence(
+ HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
+}
+
void LocationsBuilderARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
memory_barrier->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index b6678b0..d65b327 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -3103,6 +3103,15 @@
// Will be generated at use site.
}
+void LocationsBuilderARMVIXL::VisitConstructorFence(HConstructorFence* constructor_fence) {
+ constructor_fence->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitConstructorFence(
+ HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
+}
+
void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
memory_barrier->SetLocations(nullptr);
}
@@ -8512,10 +8521,6 @@
return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
}
-VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateDexCacheAddressLiteral(uint32_t address) {
- return DeduplicateUint32Literal(address, &uint32_literals_);
-}
-
VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
const DexFile& dex_file,
dex::StringIndex string_index,
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 1e9669d..7281069 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -577,7 +577,6 @@
VIXLUInt32Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
dex::TypeIndex type_index);
VIXLUInt32Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
- VIXLUInt32Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
VIXLUInt32Literal* DeduplicateJitStringLiteral(const DexFile& dex_file,
dex::StringIndex string_index,
Handle<mirror::String> handle);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index aa030b2..357df97 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -219,15 +219,33 @@
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
+ Location out = locations->Out();
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
-
+ const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6();
+ const bool r2_baker_or_no_read_barriers = !isR6 && (!kUseReadBarrier || kUseBakerReadBarrier);
+ InvokeRuntimeCallingConvention calling_convention;
+ DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
+ const bool is_load_class_bss_entry =
+ (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
- InvokeRuntimeCallingConvention calling_convention;
+ // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
+ Register entry_address = kNoRegister;
+ if (is_load_class_bss_entry && r2_baker_or_no_read_barriers) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0));
+ // In the unlucky case that `temp` is A0, we preserve the address in `out` across the
+ // kSaveEverything call.
+ entry_address = temp_is_a0 ? out.AsRegister<Register>() : temp;
+ DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
+ if (temp_is_a0) {
+ __ Move(entry_address, temp);
+ }
+ }
+
dex::TypeIndex type_index = cls_->GetTypeIndex();
__ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_);
-
QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
: kQuickInitializeType;
mips_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
@@ -237,25 +255,27 @@
CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
}
+ // For HLoadClass/kBssEntry, store the resolved class to the BSS entry.
+ if (is_load_class_bss_entry && r2_baker_or_no_read_barriers) {
+ // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
+ __ StoreToOffset(kStoreWord, calling_convention.GetRegisterAt(0), entry_address, 0);
+ }
+
// Move the class to the desired location.
- Location out = locations->Out();
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
Primitive::Type type = instruction_->GetType();
- mips_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type);
+ mips_codegen->MoveLocation(out,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ type);
}
-
RestoreLiveRegisters(codegen, locations);
- // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
- DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
- if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
- DCHECK(out.IsValid());
- // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
- // kSaveEverything and use a temporary for the .bss entry address in the fast path,
- // so that we can avoid another calculation here.
- bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6();
+
+ // For HLoadClass/kBssEntry, store the resolved class to the BSS entry.
+ if (is_load_class_bss_entry && !r2_baker_or_no_read_barriers) {
+ // For non-Baker read barriers (or on R6), we need to re-calculate the address of
+ // the class entry.
Register base = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>();
- DCHECK_NE(out.AsRegister<Register>(), AT);
CodeGeneratorMIPS::PcRelativePatchInfo* info =
mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
bool reordering = __ SetReorder(false);
@@ -286,40 +306,62 @@
explicit LoadStringSlowPathMIPS(HLoadString* instruction) : SlowPathCodeMIPS(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ DCHECK(instruction_->IsLoadString());
+ DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
LocationSummary* locations = instruction_->GetLocations();
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+ HLoadString* load = instruction_->AsLoadString();
+ const dex::StringIndex string_index = load->GetStringIndex();
+ Register out = locations->Out().AsRegister<Register>();
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
-
+ const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6();
+ const bool r2_baker_or_no_read_barriers = !isR6 && (!kUseReadBarrier || kUseBakerReadBarrier);
+ InvokeRuntimeCallingConvention calling_convention;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
- InvokeRuntimeCallingConvention calling_convention;
- HLoadString* load = instruction_->AsLoadString();
- const dex::StringIndex string_index = load->GetStringIndex();
+ // For HLoadString/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
+ Register entry_address = kNoRegister;
+ if (r2_baker_or_no_read_barriers) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0));
+ // In the unlucky case that `temp` is A0, we preserve the address in `out` across the
+ // kSaveEverything call.
+ entry_address = temp_is_a0 ? out : temp;
+ DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
+ if (temp_is_a0) {
+ __ Move(entry_address, temp);
+ }
+ }
+
__ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_);
mips_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
+
+ // Store the resolved string to the BSS entry.
+ if (r2_baker_or_no_read_barriers) {
+ // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
+ __ StoreToOffset(kStoreWord, calling_convention.GetRegisterAt(0), entry_address, 0);
+ }
+
Primitive::Type type = instruction_->GetType();
mips_codegen->MoveLocation(locations->Out(),
- calling_convention.GetReturnLocation(type),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
type);
-
RestoreLiveRegisters(codegen, locations);
- // Store the resolved String to the BSS entry.
- // TODO: Change art_quick_resolve_string to kSaveEverything and use a temporary for the
- // .bss entry address in the fast path, so that we can avoid another calculation here.
- bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6();
- Register base = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>();
- Register out = locations->Out().AsRegister<Register>();
- DCHECK_NE(out, AT);
- CodeGeneratorMIPS::PcRelativePatchInfo* info =
- mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
- bool reordering = __ SetReorder(false);
- mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info, TMP, base);
- __ StoreToOffset(kStoreWord, out, TMP, /* placeholder */ 0x5678);
- __ SetReorder(reordering);
-
+ // Store the resolved string to the BSS entry.
+ if (!r2_baker_or_no_read_barriers) {
+ // For non-Baker read barriers (or on R6), we need to re-calculate the address of
+ // the string entry.
+ Register base = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>();
+ CodeGeneratorMIPS::PcRelativePatchInfo* info =
+ mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
+ bool reordering = __ SetReorder(false);
+ mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info, TMP, base);
+ __ StoreToOffset(kStoreWord, out, TMP, /* placeholder */ 0x5678);
+ __ SetReorder(reordering);
+ }
__ B(GetExitLabel());
}
@@ -1719,15 +1761,14 @@
DCHECK_EQ(code[literal_offset + 1], 0x12);
DCHECK_EQ((code[literal_offset + 2] & 0xE0), 0x00);
DCHECK_EQ(code[literal_offset + 3], 0x3C);
- // lw reg, reg, addr32_low
+ // instr reg, reg, addr32_low
DCHECK_EQ(code[literal_offset + 4], 0x78);
DCHECK_EQ(code[literal_offset + 5], 0x56);
- DCHECK_EQ((code[literal_offset + 7] & 0xFC), 0x8C);
- addr32 += (addr32 & 0x8000) << 1; // Account for sign extension in "lw reg, reg, addr32_low".
+ addr32 += (addr32 & 0x8000) << 1; // Account for sign extension in "instr reg, reg, addr32_low".
// lui reg, addr32_high
code[literal_offset + 0] = static_cast<uint8_t>(addr32 >> 16);
code[literal_offset + 1] = static_cast<uint8_t>(addr32 >> 24);
- // lw reg, reg, addr32_low
+ // instr reg, reg, addr32_low
code[literal_offset + 4] = static_cast<uint8_t>(addr32 >> 0);
code[literal_offset + 5] = static_cast<uint8_t>(addr32 >> 8);
}
@@ -2436,6 +2477,9 @@
object_array_get_with_read_barrier
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall);
+ if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
if (Primitive::IsFloatingPointType(type)) {
@@ -5767,6 +5811,9 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall));
+ if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::RequiresRegister());
if (generate_volatile) {
InvokeRuntimeCallingConvention calling_convention;
@@ -6445,6 +6492,7 @@
void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+ bool baker_read_barrier_slow_path = false;
switch (type_check_kind) {
case TypeCheckKind::kExactCheck:
case TypeCheckKind::kAbstractClassCheck:
@@ -6452,6 +6500,7 @@
case TypeCheckKind::kArrayObjectCheck:
call_kind =
kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
+ baker_read_barrier_slow_path = kUseBakerReadBarrier;
break;
case TypeCheckKind::kArrayCheck:
case TypeCheckKind::kUnresolvedCheck:
@@ -6461,6 +6510,9 @@
}
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+ if (baker_read_barrier_slow_path) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// The output does overlap inputs.
@@ -7048,26 +7100,27 @@
HLoadClass::LoadKind load_kind = cls->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
InvokeRuntimeCallingConvention calling_convention;
- CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
- cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
- calling_convention.GetReturnLocation(Primitive::kPrimNot));
+ Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+ CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(cls, loc, loc);
return;
}
DCHECK(!cls->NeedsAccessCheck());
-
+ const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
switch (load_kind) {
// We need an extra register for PC-relative literals on R2.
case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
case HLoadClass::LoadKind::kBootImageAddress:
case HLoadClass::LoadKind::kBssEntry:
- if (codegen_->GetInstructionSetFeatures().IsR6()) {
+ if (isR6) {
break;
}
FALLTHROUGH_INTENDED;
@@ -7078,6 +7131,22 @@
break;
}
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution or initialization and marking to save everything we need.
+ // Request a temp to hold the BSS entry location for the slow path on R2
+ // (no benefit for R6).
+ if (!isR6) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barriers we have a temp-clobbering call.
+ }
+ }
}
// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
@@ -7160,10 +7229,22 @@
case HLoadClass::LoadKind::kBssEntry: {
CodeGeneratorMIPS::PcRelativePatchInfo* info =
codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
- bool reordering = __ SetReorder(false);
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg);
- GenerateGcRootFieldLoad(cls, out_loc, out, /* placeholder */ 0x5678, read_barrier_option);
- __ SetReorder(reordering);
+ constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier;
+ if (isR6 || non_baker_read_barrier) {
+ bool reordering = __ SetReorder(false);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg);
+ GenerateGcRootFieldLoad(cls, out_loc, out, /* placeholder */ 0x5678, read_barrier_option);
+ __ SetReorder(reordering);
+ } else {
+ // On R2 save the BSS entry address in a temporary register instead of
+ // recalculating it in the slow path.
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ bool reordering = __ SetReorder(false);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, temp, base_or_current_method_reg);
+ __ Addiu(temp, temp, /* placeholder */ 0x5678);
+ __ SetReorder(reordering);
+ GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
+ }
generate_null_check = true;
break;
}
@@ -7227,13 +7308,14 @@
LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
HLoadString::LoadKind load_kind = load->GetLoadKind();
+ const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
switch (load_kind) {
// We need an extra register for PC-relative literals on R2.
case HLoadString::LoadKind::kBootImageLinkTimeAddress:
case HLoadString::LoadKind::kBootImageAddress:
case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
case HLoadString::LoadKind::kBssEntry:
- if (codegen_->GetInstructionSetFeatures().IsR6()) {
+ if (isR6) {
break;
}
FALLTHROUGH_INTENDED;
@@ -7246,9 +7328,25 @@
}
if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
InvokeRuntimeCallingConvention calling_convention;
- locations->SetOut(calling_convention.GetReturnLocation(load->GetType()));
+ locations->SetOut(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
} else {
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadString::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the pResolveString and marking to save everything we need.
+ // Request a temp to hold the BSS entry location for the slow path on R2
+ // (no benefit for R6).
+ if (!isR6) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barriers we have a temp-clobbering call.
+ }
+ }
}
}
@@ -7305,14 +7403,26 @@
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
CodeGeneratorMIPS::PcRelativePatchInfo* info =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
- bool reordering = __ SetReorder(false);
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg);
- GenerateGcRootFieldLoad(load,
- out_loc,
- out,
- /* placeholder */ 0x5678,
- kCompilerReadBarrierOption);
- __ SetReorder(reordering);
+ constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier;
+ if (isR6 || non_baker_read_barrier) {
+ bool reordering = __ SetReorder(false);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg);
+ GenerateGcRootFieldLoad(load,
+ out_loc,
+ out,
+ /* placeholder */ 0x5678,
+ kCompilerReadBarrierOption);
+ __ SetReorder(reordering);
+ } else {
+ // On R2 save the BSS entry address in a temporary register instead of
+ // recalculating it in the slow path.
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ bool reordering = __ SetReorder(false);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, temp, base_or_current_method_reg);
+ __ Addiu(temp, temp, /* placeholder */ 0x5678);
+ __ SetReorder(reordering);
+ GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
+ }
SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load);
codegen_->AddSlowPath(slow_path);
__ Beqz(out, slow_path->GetEntryLabel());
@@ -7342,6 +7452,7 @@
// TODO: Re-add the compiler code to do string dex cache lookup again.
DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod);
InvokeRuntimeCallingConvention calling_convention;
+ DCHECK_EQ(calling_convention.GetRegisterAt(0), out);
__ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
@@ -7766,6 +7877,15 @@
}
}
+void LocationsBuilderMIPS::VisitConstructorFence(HConstructorFence* constructor_fence) {
+ constructor_fence->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorMIPS::VisitConstructorFence(
+ HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
+ GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
+}
+
void LocationsBuilderMIPS::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
memory_barrier->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 19250c6..a9c4964 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -141,7 +141,8 @@
class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
- explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction) : SlowPathCodeMIPS64(instruction) {}
+ explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction)
+ : SlowPathCodeMIPS64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
@@ -192,7 +193,9 @@
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
Primitive::Type type = instruction_->GetType();
- mips64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type);
+ mips64_codegen->MoveLocation(out,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ type);
}
RestoreLiveRegisters(codegen, locations);
@@ -200,10 +203,6 @@
DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
DCHECK(out.IsValid());
- // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
- // kSaveEverything and use a temporary for the .bss entry address in the fast path,
- // so that we can avoid another calculation here.
- DCHECK_NE(out.AsRegister<GpuRegister>(), AT);
CodeGeneratorMIPS64::PcRelativePatchInfo* info =
mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT);
@@ -250,16 +249,13 @@
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Primitive::Type type = instruction_->GetType();
mips64_codegen->MoveLocation(locations->Out(),
- calling_convention.GetReturnLocation(type),
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
type);
RestoreLiveRegisters(codegen, locations);
// Store the resolved String to the BSS entry.
- // TODO: Change art_quick_resolve_string to kSaveEverything and use a temporary for the
- // .bss entry address in the fast path, so that we can avoid another calculation here.
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
- DCHECK_NE(out, AT);
CodeGeneratorMIPS64::PcRelativePatchInfo* info =
mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT);
@@ -1986,6 +1982,9 @@
object_array_get_with_read_barrier
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall);
+ if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
if (Primitive::IsFloatingPointType(type)) {
@@ -3982,6 +3981,9 @@
object_field_get_with_read_barrier
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall);
+ if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::RequiresRegister());
if (Primitive::IsFloatingPointType(instruction->GetType())) {
locations->SetOut(Location::RequiresFpuRegister());
@@ -4544,6 +4546,7 @@
void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+ bool baker_read_barrier_slow_path = false;
switch (type_check_kind) {
case TypeCheckKind::kExactCheck:
case TypeCheckKind::kAbstractClassCheck:
@@ -4551,6 +4554,7 @@
case TypeCheckKind::kArrayObjectCheck:
call_kind =
kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
+ baker_read_barrier_slow_path = kUseBakerReadBarrier;
break;
case TypeCheckKind::kArrayCheck:
case TypeCheckKind::kUnresolvedCheck:
@@ -4560,6 +4564,9 @@
}
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+ if (baker_read_barrier_slow_path) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// The output does overlap inputs.
@@ -5077,10 +5084,8 @@
HLoadClass::LoadKind load_kind = cls->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
InvokeRuntimeCallingConvention calling_convention;
- CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
- cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
- calling_convention.GetReturnLocation(Primitive::kPrimNot));
+ Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+ CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(cls, loc, loc);
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -5090,10 +5095,24 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution or initialization and marking to save everything we need.
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
@@ -5224,9 +5243,20 @@
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
InvokeRuntimeCallingConvention calling_convention;
- locations->SetOut(calling_convention.GetReturnLocation(load->GetType()));
+ locations->SetOut(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
} else {
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadString::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the pResolveString and marking to save everything we need.
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
}
@@ -5294,6 +5324,7 @@
// TODO: Re-add the compiler code to do string dex cache lookup again.
DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod);
InvokeRuntimeCallingConvention calling_convention;
+ DCHECK_EQ(calling_convention.GetRegisterAt(0), out);
__ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
@@ -5653,6 +5684,15 @@
}
}
+void LocationsBuilderMIPS64::VisitConstructorFence(HConstructorFence* constructor_fence) {
+ constructor_fence->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitConstructorFence(
+ HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
+ GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
+}
+
void LocationsBuilderMIPS64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
memory_barrier->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 08a752f..1e867dd 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -2057,6 +2057,15 @@
// Will be generated at use site.
}
+void LocationsBuilderX86::VisitConstructorFence(HConstructorFence* constructor_fence) {
+ constructor_fence->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86::VisitConstructorFence(
+ HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
+}
+
void LocationsBuilderX86::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
memory_barrier->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index ff6e099..f413739 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2165,6 +2165,15 @@
// Will be generated at use site.
}
+void LocationsBuilderX86_64::VisitConstructorFence(HConstructorFence* constructor_fence) {
+ constructor_fence->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86_64::VisitConstructorFence(
+ HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
+}
+
void LocationsBuilderX86_64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
memory_barrier->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 12340b4..6a14045 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -341,7 +341,12 @@
const HInstructionList& list = input->IsPhi()
? input->GetBlock()->GetPhis()
: input->GetBlock()->GetInstructions();
- if (!list.Contains(input)) {
+ if (input->GetBlock() == nullptr) {
+ AddError(StringPrintf("Input %d of instruction %d is not in any "
+ "basic block of the control-flow graph.",
+ input->GetId(),
+ instruction->GetId()));
+ } else if (!list.Contains(input)) {
AddError(StringPrintf("Input %d of instruction %d is not defined "
"in a basic block of the control-flow graph.",
input->GetId(),
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 92d0f3c..3b681c1 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -817,7 +817,17 @@
}
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
- bool is_referrer = (klass.Get() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+ bool is_referrer;
+ ArtMethod* outermost_art_method = outermost_graph_->GetArtMethod();
+ if (outermost_art_method == nullptr) {
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ // We are in AOT mode and we don't have an ART method to determine
+ // if the inlined method belongs to the referrer. Assume it doesn't.
+ is_referrer = false;
+ } else {
+ is_referrer = klass.Get() == outermost_art_method->GetDeclaringClass();
+ }
+
// Note that we will just compare the classes, so we don't need Java semantics access checks.
// Note that the type index and the dex file are relative to the method this type guard is
// inlined into.
@@ -1470,8 +1480,13 @@
}
}
if (needs_constructor_barrier) {
- HMemoryBarrier* barrier = new (graph_->GetArena()) HMemoryBarrier(kStoreStore, kNoDexPc);
- invoke_instruction->GetBlock()->InsertInstructionBefore(barrier, invoke_instruction);
+ // See CompilerDriver::RequiresConstructorBarrier for more details.
+ DCHECK(obj != nullptr) << "only non-static methods can have a constructor fence";
+
+ HConstructorFence* constructor_fence =
+ new (graph_->GetArena()) HConstructorFence(obj, kNoDexPc, graph_->GetArena());
+ invoke_instruction->GetBlock()->InsertInstructionBefore(constructor_fence,
+ invoke_instruction);
}
*return_replacement = nullptr;
break;
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 978c6a2..8b79da8 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -451,10 +451,13 @@
referrer_method_id.class_idx_,
parameter_index++,
Primitive::kPrimNot,
- true);
+ /* is_this */ true);
AppendInstruction(parameter);
UpdateLocal(locals_index++, parameter);
number_of_parameters--;
+ current_this_parameter_ = parameter;
+ } else {
+ DCHECK(current_this_parameter_ == nullptr);
}
const DexFile::ProtoId& proto = dex_file_->GetMethodPrototype(referrer_method_id);
@@ -465,7 +468,7 @@
arg_types->GetTypeItem(shorty_pos - 1).type_idx_,
parameter_index++,
Primitive::GetType(shorty[shorty_pos]),
- false);
+ /* is_this */ false);
++shorty_pos;
AppendInstruction(parameter);
// Store the parameter value in the local that the dex code will use
@@ -588,6 +591,8 @@
UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
}
+// Does the method being compiled need any constructor barriers being inserted?
+// (Always 'false' for methods that aren't <init>.)
static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, CompilerDriver* driver) {
// Can be null in unit tests only.
if (UNLIKELY(cu == nullptr)) {
@@ -596,6 +601,11 @@
Thread* self = Thread::Current();
return cu->IsConstructor()
+ && !cu->IsStatic()
+ // RequiresConstructorBarrier must only be queried for <init> methods;
+ // it's effectively "false" for every other method.
+ //
+ // See CompilerDriver::RequiresConstructBarrier for more explanation.
&& driver->RequiresConstructorBarrier(self, cu->GetDexFile(), cu->GetClassDefIndex());
}
@@ -639,13 +649,24 @@
Primitive::Type type,
uint32_t dex_pc) {
if (type == Primitive::kPrimVoid) {
+ // Only <init> (which is a return-void) could possibly have a constructor fence.
// This may insert additional redundant constructor fences from the super constructors.
// TODO: remove redundant constructor fences (b/36656456).
if (RequiresConstructorBarrier(dex_compilation_unit_, compiler_driver_)) {
- AppendInstruction(new (arena_) HMemoryBarrier(kStoreStore, dex_pc));
+ // Compiling instance constructor.
+ if (kIsDebugBuild) {
+ std::string method_name = graph_->GetMethodName();
+ CHECK_EQ(std::string("<init>"), method_name);
+ }
+
+ HInstruction* fence_target = current_this_parameter_;
+ DCHECK(fence_target != nullptr);
+
+ AppendInstruction(new (arena_) HConstructorFence(fence_target, dex_pc, arena_));
}
AppendInstruction(new (arena_) HReturnVoid(dex_pc));
} else {
+ DCHECK(!RequiresConstructorBarrier(dex_compilation_unit_, compiler_driver_));
HInstruction* value = LoadLocal(instruction.VRegA(), type);
AppendInstruction(new (arena_) HReturn(value, dex_pc));
}
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 7fdc188..2fb5c7b 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -62,6 +62,7 @@
current_block_(nullptr),
current_locals_(nullptr),
latest_result_(nullptr),
+ current_this_parameter_(nullptr),
compiler_driver_(driver),
code_generator_(code_generator),
dex_compilation_unit_(dex_compilation_unit),
@@ -325,6 +326,11 @@
HBasicBlock* current_block_;
ArenaVector<HInstruction*>* current_locals_;
HInstruction* latest_result_;
+ // Current "this" parameter.
+ // Valid only after InitializeParameters() finishes.
+ // * Null for static methods.
+ // * Non-null for instance methods.
+ HParameterValue* current_this_parameter_;
CompilerDriver* const compiler_driver_;
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index bfe04f5..abf5b12 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1525,6 +1525,9 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall),
kIntrinsified);
+ if (can_call && kUseBakerReadBarrier) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index c5e1160..9dce59b 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1168,6 +1168,9 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall),
kIntrinsified);
+ if (can_call && kUseBakerReadBarrier) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 48699b3..8d8cc93 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -566,14 +566,22 @@
store->GetBlock()->RemoveInstruction(store);
}
- // Eliminate allocations that are not used.
+ // Eliminate singleton-classified instructions:
+ // * - Constructor fences (they never escape this thread).
+ // * - Allocations (if they are unused).
for (HInstruction* new_instance : singleton_new_instances_) {
+ HConstructorFence::RemoveConstructorFences(new_instance);
+
if (!new_instance->HasNonEnvironmentUses()) {
new_instance->RemoveEnvironmentUsers();
new_instance->GetBlock()->RemoveInstruction(new_instance);
}
}
for (HInstruction* new_array : singleton_new_arrays_) {
+ // TODO: Delete constructor fences for new-array
+ // In the future HNewArray instructions will have HConstructorFence's for them.
+ // HConstructorFence::RemoveConstructorFences(new_array);
+
if (!new_array->HasNonEnvironmentUses()) {
new_array->RemoveEnvironmentUsers();
new_array->GetBlock()->RemoveInstruction(new_array);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ca953a1..f250c1a 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -528,6 +528,15 @@
return cached_current_method_;
}
+const char* HGraph::GetMethodName() const {
+ const DexFile::MethodId& method_id = dex_file_.GetMethodId(method_idx_);
+ return dex_file_.GetMethodName(method_id);
+}
+
+std::string HGraph::PrettyMethod(bool with_signature) const {
+ return dex_file_.PrettyMethod(method_idx_, with_signature);
+}
+
HConstant* HGraph::GetConstant(Primitive::Type type, int64_t value, uint32_t dex_pc) {
switch (type) {
case Primitive::Type::kPrimBoolean:
@@ -1150,6 +1159,81 @@
}
}
+void HVariableInputSizeInstruction::RemoveAllInputs() {
+ RemoveAsUserOfAllInputs();
+ DCHECK(!HasNonEnvironmentUses());
+
+ inputs_.clear();
+ DCHECK_EQ(0u, InputCount());
+}
+
+void HConstructorFence::RemoveConstructorFences(HInstruction* instruction) {
+ DCHECK(instruction->GetBlock() != nullptr);
+ // Removing constructor fences only makes sense for instructions with an object return type.
+ DCHECK_EQ(Primitive::kPrimNot, instruction->GetType());
+
+ // Efficient implementation that simultaneously (in one pass):
+ // * Scans the uses list for all constructor fences.
+ // * Deletes that constructor fence from the uses list of `instruction`.
+ // * Deletes `instruction` from the constructor fence's inputs.
+ // * Deletes the constructor fence if it now has 0 inputs.
+
+ const HUseList<HInstruction*>& uses = instruction->GetUses();
+ // Warning: Although this is "const", we might mutate the list when calling RemoveInputAt.
+ for (auto it = uses.begin(), end = uses.end(); it != end; ) {
+ const HUseListNode<HInstruction*>& use_node = *it;
+ HInstruction* const use_instruction = use_node.GetUser();
+
+ // Advance the iterator immediately once we fetch the use_node.
+ // Warning: If the input is removed, the current iterator becomes invalid.
+ ++it;
+
+ if (use_instruction->IsConstructorFence()) {
+ HConstructorFence* ctor_fence = use_instruction->AsConstructorFence();
+ size_t input_index = use_node.GetIndex();
+
+ // Process the candidate instruction for removal
+ // from the graph.
+
+ // Constructor fence instructions are never
+ // used by other instructions.
+ //
+ // If we wanted to make this more generic, it
+ // could be a runtime if statement.
+ DCHECK(!ctor_fence->HasUses());
+
+ // A constructor fence's return type is "kPrimVoid"
+ // and therefore it can't have any environment uses.
+ DCHECK(!ctor_fence->HasEnvironmentUses());
+
+ // Remove the inputs first, otherwise removing the instruction
+ // will try to remove its uses while we are already removing uses
+ // and this operation will fail.
+ DCHECK_EQ(instruction, ctor_fence->InputAt(input_index));
+
+ // Removing the input will also remove the `use_node`.
+ // (Do not look at `use_node` after this, it will be a dangling reference).
+ ctor_fence->RemoveInputAt(input_index);
+
+ // Once all inputs are removed, the fence is considered dead and
+ // is removed.
+ if (ctor_fence->InputCount() == 0u) {
+ ctor_fence->GetBlock()->RemoveInstruction(ctor_fence);
+ }
+ }
+ }
+
+ if (kIsDebugBuild) {
+ // Post-condition checks:
+ // * None of the uses of `instruction` are a constructor fence.
+ // * The `instruction` itself did not get removed from a block.
+ for (const HUseListNode<HInstruction*>& use_node : instruction->GetUses()) {
+ CHECK(!use_node.GetUser()->IsConstructorFence());
+ }
+ CHECK(instruction->GetBlock() != nullptr);
+ }
+}
+
#define DEFINE_ACCEPT(name, super) \
void H##name::Accept(HGraphVisitor* visitor) { \
visitor->Visit##name(this); \
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 36c7df7..e40361e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -46,6 +46,7 @@
class GraphChecker;
class HBasicBlock;
+class HConstructorFence;
class HCurrentMethod;
class HDoubleConstant;
class HEnvironment;
@@ -57,6 +58,7 @@
class HInvoke;
class HLongConstant;
class HNullConstant;
+class HParameterValue;
class HPhi;
class HSuspendCheck;
class HTryBoundary;
@@ -537,6 +539,12 @@
return method_idx_;
}
+ // Get the method name (without the signature), e.g. "<init>"
+ const char* GetMethodName() const;
+
+ // Get the pretty method name (class + name + optionally signature).
+ std::string PrettyMethod(bool with_signature = true) const;
+
InvokeType GetInvokeType() const {
return invoke_type_;
}
@@ -1297,6 +1305,7 @@
M(ClearException, Instruction) \
M(ClinitCheck, Instruction) \
M(Compare, BinaryOperation) \
+ M(ConstructorFence, Instruction) \
M(CurrentMethod, Instruction) \
M(ShouldDeoptimizeFlag, Instruction) \
M(Deoptimize, Instruction) \
@@ -1476,8 +1485,11 @@
template <typename T>
class HUseListNode : public ArenaObject<kArenaAllocUseListNode> {
public:
+ // Get the instruction which has this use as one of the inputs.
T GetUser() const { return user_; }
+ // Get the position of the input record that this use corresponds to.
size_t GetIndex() const { return index_; }
+ // Set the position of the input record that this use corresponds to.
void SetIndex(size_t index) { index_ = index; }
// Hook for the IntrusiveForwardList<>.
@@ -2037,7 +2049,8 @@
!IsNativeDebugInfo() &&
!IsParameterValue() &&
// If we added an explicit barrier then we should keep it.
- !IsMemoryBarrier();
+ !IsMemoryBarrier() &&
+ !IsConstructorFence();
}
bool IsDeadAndRemovable() const {
@@ -2431,6 +2444,11 @@
void InsertInputAt(size_t index, HInstruction* input);
void RemoveInputAt(size_t index);
+ // Removes all the inputs.
+ // Also removes this instructions from each input's use list
+ // (for non-environment uses only).
+ void RemoveAllInputs();
+
protected:
HVariableInputSizeInstruction(SideEffects side_effects,
uint32_t dex_pc,
@@ -5069,7 +5087,7 @@
const DexFile& GetDexFile() const { return dex_file_; }
dex::TypeIndex GetTypeIndex() const { return type_index_; }
uint8_t GetIndex() const { return index_; }
- bool IsThis() const ATTRIBUTE_UNUSED { return GetPackedFlag<kFlagIsThis>(); }
+ bool IsThis() const { return GetPackedFlag<kFlagIsThis>(); }
bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
@@ -6507,6 +6525,137 @@
DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier);
};
+// A constructor fence orders all prior stores to fields that could be accessed via a final field of
+// the specified object(s), with respect to any subsequent store that might "publish"
+// (i.e. make visible) the specified object to another thread.
+//
+// JLS 17.5.1 "Semantics of final fields" states that a freeze action happens
+// for all final fields (that were set) at the end of the invoked constructor.
+//
+// The constructor fence models the freeze actions for the final fields of an object
+// being constructed (semantically at the end of the constructor). Constructor fences
+// have a per-object affinity; two separate objects being constructed get two separate
+// constructor fences.
+//
+// (Note: that if calling a super-constructor or forwarding to another constructor,
+// the freezes would happen at the end of *that* constructor being invoked).
+//
+// The memory model guarantees that when the object being constructed is "published" after
+// constructor completion (i.e. escapes the current thread via a store), then any final field
+// writes must be observable on other threads (once they observe that publication).
+//
+// Further, anything written before the freeze, and read by dereferencing through the final field,
+// must also be visible (so final object field could itself have an object with non-final fields;
+// yet the freeze must also extend to them).
+//
+// Constructor example:
+//
+// class HasFinal {
+// final int field; Optimizing IR for <init>()V:
+// HasFinal() {
+// field = 123; HInstanceFieldSet(this, HasFinal.field, 123)
+// // freeze(this.field); HConstructorFence(this)
+// } HReturn
+// }
+//
+// HConstructorFence can serve double duty as a fence for new-instance/new-array allocations of
+// already-initialized classes; in that case the allocation must act as a "default-initializer"
+// of the object which effectively writes the class pointer "final field".
+//
+// For example, we can model default-initialiation as roughly the equivalent of the following:
+//
+// class Object {
+// private final Class header;
+// }
+//
+// Java code: Optimizing IR:
+//
+// T new_instance<T>() {
+// Object obj = allocate_memory(T.class.size); obj = HInvoke(art_quick_alloc_object, T)
+// obj.header = T.class; // header write is done by above call.
+// // freeze(obj.header) HConstructorFence(obj)
+// return (T)obj;
+// }
+//
+// See also:
+// * CompilerDriver::RequiresConstructorBarrier
+// * QuasiAtomic::ThreadFenceForConstructor
+//
+class HConstructorFence FINAL : public HVariableInputSizeInstruction {
+ // A fence has variable inputs because the inputs can be removed
+ // after prepare_for_register_allocation phase.
+ // (TODO: In the future a fence could freeze multiple objects
+ // after merging two fences together.)
+ public:
+ // `fence_object` is the reference that needs to be protected for correct publication.
+ //
+ // It makes sense in the following situations:
+ // * <init> constructors, it's the "this" parameter (i.e. HParameterValue, s.t. IsThis() == true).
+ // * new-instance-like instructions, it's the return value (i.e. HNewInstance).
+ //
+ // After construction the `fence_object` becomes the 0th input.
+ // This is not an input in a real sense, but just a convenient place to stash the information
+ // about the associated object.
+ HConstructorFence(HInstruction* fence_object,
+ uint32_t dex_pc,
+ ArenaAllocator* arena)
+ // We strongly suspect there is not a more accurate way to describe the fine-grained reordering
+ // constraints described in the class header. We claim that these SideEffects constraints
+ // enforce a superset of the real constraints.
+ //
+ // The ordering described above is conservatively modeled with SideEffects as follows:
+ //
+ // * To prevent reordering of the publication stores:
+ // ----> "Reads of objects" is the initial SideEffect.
+ // * For every primitive final field store in the constructor:
+ // ----> Union that field's type as a read (e.g. "Read of T") into the SideEffect.
+ // * If there are any stores to reference final fields in the constructor:
+ // ----> Use a more conservative "AllReads" SideEffect because any stores to any references
+ // that are reachable from `fence_object` also need to be prevented for reordering
+ // (and we do not want to do alias analysis to figure out what those stores are).
+ //
+ // In the implementation, this initially starts out as an "all reads" side effect; this is an
+ // even more conservative approach than the one described above, and prevents all of the
+ // above reordering without analyzing any of the instructions in the constructor.
+ //
+ // If in a later phase we discover that there are no writes to reference final fields,
+ // we can refine the side effect to a smaller set of type reads (see above constraints).
+ : HVariableInputSizeInstruction(SideEffects::AllReads(),
+ dex_pc,
+ arena,
+ /* number_of_inputs */ 1,
+ kArenaAllocConstructorFenceInputs) {
+ DCHECK(fence_object != nullptr);
+ SetRawInputAt(0, fence_object);
+ }
+
+ // The object associated with this constructor fence.
+ //
+ // (Note: This will be null after the prepare_for_register_allocation phase,
+ // as all constructor fence inputs are removed there).
+ HInstruction* GetFenceObject() const {
+ return InputAt(0);
+ }
+
+ // Find all the HConstructorFence uses (`fence_use`) for `this` and:
+ // - Delete `fence_use` from `this`'s use list.
+ // - Delete `this` from `fence_use`'s inputs list.
+ // - If the `fence_use` is dead, remove it from the graph.
+ //
+ // A fence is considered dead once it no longer has any uses
+ // and all of the inputs are dead.
+ //
+ // This must *not* be called during/after prepare_for_register_allocation,
+ // because that removes all the inputs to the fences but the fence is actually
+ // still considered live.
+ static void RemoveConstructorFences(HInstruction* instruction);
+
+ DECLARE_INSTRUCTION(ConstructorFence);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HConstructorFence);
+};
+
class HMonitorOperation FINAL : public HTemplateInstruction<1> {
public:
enum class OperationKind {
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 66bfea9..c3c141b 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -167,6 +167,13 @@
}
}
+void PrepareForRegisterAllocation::VisitConstructorFence(HConstructorFence* constructor_fence) {
+ // Delete all the inputs to the constructor fence;
+ // they aren't used by the InstructionCodeGenerator and this lets us avoid creating a
+ // LocationSummary in the LocationsBuilder.
+ constructor_fence->RemoveAllInputs();
+}
+
void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
if (invoke->IsStaticWithExplicitClinitCheck()) {
HLoadClass* last_input = invoke->GetInputs().back()->AsLoadClass();
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index 7ffbe44..395d4ba 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -43,6 +43,7 @@
void VisitArraySet(HArraySet* instruction) OVERRIDE;
void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
void VisitCondition(HCondition* condition) OVERRIDE;
+ void VisitConstructorFence(HConstructorFence* constructor_fence) OVERRIDE;
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 9fd42d2..58f3948 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2433,8 +2433,8 @@
// which uses an unstarted runtime.
raw_options.push_back(std::make_pair("-Xgc:nonconcurrent", nullptr));
- // Also force the free-list implementation for large objects.
- raw_options.push_back(std::make_pair("-XX:LargeObjectSpace=freelist", nullptr));
+ // The default LOS implementation (map) is not deterministic. So disable it.
+ raw_options.push_back(std::make_pair("-XX:LargeObjectSpace=disabled", nullptr));
// We also need to turn off the nonmoving space. For that, we need to disable HSpace
// compaction (done above) and ensure that neither foreground nor background collectors
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index a2116cd..588a3ae 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -20,7 +20,7 @@
"dexlayout.cc",
"dex_ir.cc",
"dex_ir_builder.cc",
- "dex_verify.cc",
+ "dex_verify.cc",
"dex_visualize.cc",
"dex_writer.cc",
],
@@ -43,6 +43,7 @@
art_cc_binary {
name: "dexlayout",
+ defaults: ["art_defaults"],
host_supported: true,
srcs: ["dexlayout_main.cc"],
cflags: ["-Wall"],
@@ -61,13 +62,28 @@
art_cc_binary {
name: "dexdiag",
- host_supported: false,
+ defaults: ["art_defaults"],
+ host_supported: true,
srcs: ["dexdiag.cc"],
cflags: ["-Wall"],
shared_libs: [
"libart",
"libart-dexlayout",
- "libpagemap",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libpagemap",
+ ]
+ },
+ }
}
+art_cc_test {
+ name: "art_dexdiag_tests",
+ host_supported: true,
+ defaults: [
+ "art_gtest_defaults",
+ ],
+ srcs: ["dexdiag_test.cc"],
+}
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index ea2679a..49c8185 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -15,6 +15,7 @@
*/
#include <errno.h>
+#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -30,7 +31,9 @@
#include "dex_file.h"
#include "dex_ir.h"
#include "dex_ir_builder.h"
+#ifdef ART_TARGET_ANDROID
#include "pagemap/pagemap.h"
+#endif
#include "runtime.h"
#include "vdex_file.h"
@@ -38,8 +41,6 @@
using android::base::StringPrintf;
-static constexpr size_t kLineLength = 32;
-
static bool g_verbose = false;
// The width needed to print a file page offset (32-bit).
@@ -164,6 +165,7 @@
std::cout << ". (Mapped page not resident)" << std::endl;
}
+#ifdef ART_TARGET_ANDROID
static char PageTypeChar(uint16_t type) {
if (kDexSectionInfoMap.find(type) == kDexSectionInfoMap.end()) {
return '-';
@@ -194,6 +196,7 @@
size_t end,
const std::vector<dex_ir::DexFileSection>& sections,
PageCount* page_counts) {
+ static constexpr size_t kLineLength = 32;
for (size_t page = start; page < end; ++page) {
char type_char = '.';
if (PM_PAGEMAP_PRESENT(pagemap[page])) {
@@ -268,7 +271,7 @@
std::cerr << "Dex file start offset for "
<< dex_file->GetLocation().c_str()
<< " is incorrect: map start "
- << StringPrintf("%zx > dex start %zx\n", map_start, dex_file_start)
+ << StringPrintf("%" PRIx64 " > dex start %" PRIx64 "\n", map_start, dex_file_start)
<< std::endl;
return;
}
@@ -277,7 +280,7 @@
uint64_t end_page = RoundUp(start_address + dex_file_size, kPageSize) / kPageSize;
std::cout << "DEX "
<< dex_file->GetLocation().c_str()
- << StringPrintf(": %zx-%zx",
+ << StringPrintf(": %" PRIx64 "-%" PRIx64,
map_start + start_page * kPageSize,
map_start + end_page * kPageSize)
<< std::endl;
@@ -342,7 +345,7 @@
// Process the dex files.
std::cout << "MAPPING "
<< pm_map_name(map)
- << StringPrintf(": %zx-%zx", pm_map_start(map), pm_map_end(map))
+ << StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map))
<< std::endl;
for (const auto& dex_file : dex_files) {
ProcessOneDexMapping(pagemap,
@@ -356,6 +359,7 @@
}
static void ProcessOneOatMapping(uint64_t* pagemap, size_t size, Printer* printer) {
+ static constexpr size_t kLineLength = 32;
size_t resident_page_count = 0;
for (size_t page = 0; page < size; ++page) {
char type_char = '.';
@@ -406,7 +410,7 @@
// Process the dex files.
std::cout << "MAPPING "
<< pm_map_name(map)
- << StringPrintf(": %zx-%zx", pm_map_start(map), pm_map_end(map))
+ << StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map))
<< std::endl;
ProcessOneOatMapping(pagemap, len, printer);
free(pagemap);
@@ -426,9 +430,10 @@
}
return false;
}
+#endif
static void Usage(const char* cmd) {
- std::cerr << "Usage: " << cmd << " [options] pid" << std::endl
+ std::cout << "Usage: " << cmd << " [options] pid" << std::endl
<< " --contains=<string>: Display sections containing string." << std::endl
<< " --help: Shows this message." << std::endl
<< " --verbose: Makes displays verbose." << std::endl;
@@ -463,6 +468,7 @@
InitLogging(argv, Runtime::Aborter);
MemMap::Init();
+#ifdef ART_TARGET_ANDROID
pid_t pid;
char* endptr;
pid = (pid_t)strtol(argv[argc - 1], &endptr, 10);
@@ -496,7 +502,8 @@
return EXIT_FAILURE;
}
- // Process the mappings that are due to DEX files.
+ bool match_found = false;
+ // Process the mappings that are due to vdex or oat files.
Printer printer;
for (size_t i = 0; i < num_maps; ++i) {
std::string mapped_file_name = pm_map_name(maps[i]);
@@ -504,12 +511,17 @@
if (!FilterByNameContains(mapped_file_name, name_filters)) {
continue;
}
+ match_found = true;
if (!DisplayMappingIfFromVdexFile(maps[i], &printer)) {
return EXIT_FAILURE;
} else if (!DisplayMappingIfFromOatFile(maps[i], &printer)) {
return EXIT_FAILURE;
}
}
+ if (!match_found) {
+ return EXIT_FAILURE;
+ }
+#endif
return EXIT_SUCCESS;
}
diff --git a/dexlayout/dexdiag_test.cc b/dexlayout/dexdiag_test.cc
new file mode 100644
index 0000000..d0d2af1
--- /dev/null
+++ b/dexlayout/dexdiag_test.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+
+#include "common_runtime_test.h"
+
+#include "runtime/exec_utils.h"
+#include "runtime/oat_file.h"
+#include "runtime/os.h"
+
+namespace art {
+
+static const char* kDexDiagContains = "--contains=core.vdex";
+static const char* kDexDiagContainsFails = "--contains=anything_other_than_core.vdex";
+static const char* kDexDiagHelp = "--help";
+static const char* kDexDiagVerbose = "--verbose";
+static const char* kDexDiagBinaryName = "dexdiag";
+
+class DexDiagTest : public CommonRuntimeTest {
+ protected:
+ virtual void SetUp() {
+ CommonRuntimeTest::SetUp();
+ }
+
+ // Path to the dexdiag(d?)[32|64] binary.
+ std::string GetDexDiagFilePath() {
+ std::string root = GetTestAndroidRoot();
+
+ root += "/bin/";
+ root += kDexDiagBinaryName;
+
+ std::string root32 = root + "32";
+ // If we have both a 32-bit and a 64-bit build, the 32-bit file will have a 32 suffix.
+ if (OS::FileExists(root32.c_str()) && !Is64BitInstructionSet(kRuntimeISA)) {
+ return root32;
+ } else {
+ // This is a 64-bit build or only a single build exists.
+ return root;
+ }
+ }
+
+ std::unique_ptr<OatFile> OpenOatAndVdexFiles() {
+ std::cout << "YO!" << std::endl;
+ // Open the core.oat file.
+ // This is a little convoluted because we have to
+ // get the location of the default core image (.../framework/core.oat),
+ // find it in the right architecture subdirectory (.../framework/arm/core.oat),
+ // Then, opening the oat file has the side-effect of opening the corresponding
+ // vdex file (.../framework/arm/core.vdex).
+ const std::string default_location = GetCoreOatLocation();
+ EXPECT_TRUE(!default_location.empty());
+ std::string oat_location = GetSystemImageFilename(default_location.c_str(), kRuntimeISA);
+ EXPECT_TRUE(!oat_location.empty());
+ std::cout << "==" << oat_location << std::endl;
+ std::string error_msg;
+ std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg));
+ EXPECT_TRUE(oat != nullptr) << error_msg;
+ return oat;
+ }
+
+ // Run dexdiag with a custom boot image location.
+ bool Exec(pid_t this_pid, const std::vector<std::string>& args, std::string* error_msg) {
+ // Invoke 'dexdiag' against the current process.
+ // This should succeed because we have a runtime and so it should
+ // be able to map in the boot.art and do a diff for it.
+ std::vector<std::string> exec_argv;
+
+ // Build the command line "dexdiag <args> this_pid".
+ std::string executable_path = GetDexDiagFilePath();
+ EXPECT_TRUE(OS::FileExists(executable_path.c_str())) << executable_path
+ << " should be a valid file path";
+ exec_argv.push_back(executable_path);
+ for (const auto& arg : args) {
+ exec_argv.push_back(arg);
+ }
+ exec_argv.push_back(std::to_string(this_pid));
+
+ return ::art::Exec(exec_argv, error_msg);
+ }
+};
+
+// We can't run these tests on the host, as they will fail when trying to open
+// /proc/pid/pagemap.
+// On the target, we invoke 'dexdiag' against the current process.
+// This should succeed because we have a runtime and so dexdiag should
+// be able to find the map for, e.g., boot.vdex and friends.
+TEST_F(DexDiagTest, DexDiagHelpTest) {
+ // TODO: test the resulting output.
+ std::string error_msg;
+ ASSERT_TRUE(Exec(getpid(), { kDexDiagHelp }, &error_msg)) << "Failed to execute -- because: "
+ << error_msg;
+}
+
+#if defined (ART_TARGET)
+TEST_F(DexDiagTest, DexDiagContainsTest) {
+#else
+TEST_F(DexDiagTest, DISABLED_DexDiagContainsTest) {
+#endif
+ std::unique_ptr<OatFile> oat = OpenOatAndVdexFiles();
+ // TODO: test the resulting output.
+ std::string error_msg;
+ ASSERT_TRUE(Exec(getpid(), { kDexDiagContains }, &error_msg)) << "Failed to execute -- because: "
+ << error_msg;
+}
+
+#if defined (ART_TARGET)
+TEST_F(DexDiagTest, DexDiagContainsFailsTest) {
+#else
+TEST_F(DexDiagTest, DISABLED_DexDiagContainsFailsTest) {
+#endif
+ std::unique_ptr<OatFile> oat = OpenOatAndVdexFiles();
+ // TODO: test the resulting output.
+ std::string error_msg;
+ ASSERT_FALSE(Exec(getpid(), { kDexDiagContainsFails }, &error_msg))
+ << "Failed to execute -- because: "
+ << error_msg;
+}
+
+#if defined (ART_TARGET)
+TEST_F(DexDiagTest, DexDiagVerboseTest) {
+#else
+TEST_F(DexDiagTest, DISABLED_DexDiagVerboseTest) {
+#endif
+ // TODO: test the resulting output.
+ std::unique_ptr<OatFile> oat = OpenOatAndVdexFiles();
+ std::string error_msg;
+ ASSERT_TRUE(Exec(getpid(), { kDexDiagVerbose }, &error_msg)) << "Failed to execute -- because: "
+ << error_msg;
+}
+
+} // namespace art
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index de72d3a..d21d0c0 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -67,19 +67,19 @@
// Long long arithmetics - REM_LONG[_2ADDR] and DIV_LONG[_2ADDR]
extern "C" int64_t __aeabi_ldivmod(int64_t, int64_t);
-void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_marking) {
- qpoints->pReadBarrierMarkReg00 = is_marking ? art_quick_read_barrier_mark_reg00 : nullptr;
- qpoints->pReadBarrierMarkReg01 = is_marking ? art_quick_read_barrier_mark_reg01 : nullptr;
- qpoints->pReadBarrierMarkReg02 = is_marking ? art_quick_read_barrier_mark_reg02 : nullptr;
- qpoints->pReadBarrierMarkReg03 = is_marking ? art_quick_read_barrier_mark_reg03 : nullptr;
- qpoints->pReadBarrierMarkReg04 = is_marking ? art_quick_read_barrier_mark_reg04 : nullptr;
- qpoints->pReadBarrierMarkReg05 = is_marking ? art_quick_read_barrier_mark_reg05 : nullptr;
- qpoints->pReadBarrierMarkReg06 = is_marking ? art_quick_read_barrier_mark_reg06 : nullptr;
- qpoints->pReadBarrierMarkReg07 = is_marking ? art_quick_read_barrier_mark_reg07 : nullptr;
- qpoints->pReadBarrierMarkReg08 = is_marking ? art_quick_read_barrier_mark_reg08 : nullptr;
- qpoints->pReadBarrierMarkReg09 = is_marking ? art_quick_read_barrier_mark_reg09 : nullptr;
- qpoints->pReadBarrierMarkReg10 = is_marking ? art_quick_read_barrier_mark_reg10 : nullptr;
- qpoints->pReadBarrierMarkReg11 = is_marking ? art_quick_read_barrier_mark_reg11 : nullptr;
+void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) {
+ qpoints->pReadBarrierMarkReg00 = is_active ? art_quick_read_barrier_mark_reg00 : nullptr;
+ qpoints->pReadBarrierMarkReg01 = is_active ? art_quick_read_barrier_mark_reg01 : nullptr;
+ qpoints->pReadBarrierMarkReg02 = is_active ? art_quick_read_barrier_mark_reg02 : nullptr;
+ qpoints->pReadBarrierMarkReg03 = is_active ? art_quick_read_barrier_mark_reg03 : nullptr;
+ qpoints->pReadBarrierMarkReg04 = is_active ? art_quick_read_barrier_mark_reg04 : nullptr;
+ qpoints->pReadBarrierMarkReg05 = is_active ? art_quick_read_barrier_mark_reg05 : nullptr;
+ qpoints->pReadBarrierMarkReg06 = is_active ? art_quick_read_barrier_mark_reg06 : nullptr;
+ qpoints->pReadBarrierMarkReg07 = is_active ? art_quick_read_barrier_mark_reg07 : nullptr;
+ qpoints->pReadBarrierMarkReg08 = is_active ? art_quick_read_barrier_mark_reg08 : nullptr;
+ qpoints->pReadBarrierMarkReg09 = is_active ? art_quick_read_barrier_mark_reg09 : nullptr;
+ qpoints->pReadBarrierMarkReg10 = is_active ? art_quick_read_barrier_mark_reg10 : nullptr;
+ qpoints->pReadBarrierMarkReg11 = is_active ? art_quick_read_barrier_mark_reg11 : nullptr;
}
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
@@ -138,7 +138,7 @@
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
- UpdateReadBarrierEntrypoints(qpoints, /*is_marking*/ false);
+ UpdateReadBarrierEntrypoints(qpoints, /*is_active*/ false);
qpoints->pReadBarrierMarkReg12 = nullptr; // Cannot use register 12 (IP) to pass arguments.
qpoints->pReadBarrierMarkReg13 = nullptr; // Cannot use register 13 (SP) to pass arguments.
qpoints->pReadBarrierMarkReg14 = nullptr; // Cannot use register 14 (LR) to pass arguments.
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index bc7bcb1..610cdee 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -75,7 +75,7 @@
extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_arrays(mirror::Object*);
extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_gc_roots(mirror::Object*);
-void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_marking) {
+void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) {
// ARM64 is the architecture with the largest number of core
// registers (32) that supports the read barrier configuration.
// Because registers 30 (LR) and 31 (SP/XZR) cannot be used to pass
@@ -85,35 +85,35 @@
// have less core registers (resp. 16, 8 and 16). (We may have to
// revise that design choice if read barrier support is added for
// MIPS and/or MIPS64.)
- qpoints->pReadBarrierMarkReg00 = is_marking ? art_quick_read_barrier_mark_reg00 : nullptr;
- qpoints->pReadBarrierMarkReg01 = is_marking ? art_quick_read_barrier_mark_reg01 : nullptr;
- qpoints->pReadBarrierMarkReg02 = is_marking ? art_quick_read_barrier_mark_reg02 : nullptr;
- qpoints->pReadBarrierMarkReg03 = is_marking ? art_quick_read_barrier_mark_reg03 : nullptr;
- qpoints->pReadBarrierMarkReg04 = is_marking ? art_quick_read_barrier_mark_reg04 : nullptr;
- qpoints->pReadBarrierMarkReg05 = is_marking ? art_quick_read_barrier_mark_reg05 : nullptr;
- qpoints->pReadBarrierMarkReg06 = is_marking ? art_quick_read_barrier_mark_reg06 : nullptr;
- qpoints->pReadBarrierMarkReg07 = is_marking ? art_quick_read_barrier_mark_reg07 : nullptr;
- qpoints->pReadBarrierMarkReg08 = is_marking ? art_quick_read_barrier_mark_reg08 : nullptr;
- qpoints->pReadBarrierMarkReg09 = is_marking ? art_quick_read_barrier_mark_reg09 : nullptr;
- qpoints->pReadBarrierMarkReg10 = is_marking ? art_quick_read_barrier_mark_reg10 : nullptr;
- qpoints->pReadBarrierMarkReg11 = is_marking ? art_quick_read_barrier_mark_reg11 : nullptr;
- qpoints->pReadBarrierMarkReg12 = is_marking ? art_quick_read_barrier_mark_reg12 : nullptr;
- qpoints->pReadBarrierMarkReg13 = is_marking ? art_quick_read_barrier_mark_reg13 : nullptr;
- qpoints->pReadBarrierMarkReg14 = is_marking ? art_quick_read_barrier_mark_reg14 : nullptr;
- qpoints->pReadBarrierMarkReg15 = is_marking ? art_quick_read_barrier_mark_reg15 : nullptr;
- qpoints->pReadBarrierMarkReg17 = is_marking ? art_quick_read_barrier_mark_reg17 : nullptr;
- qpoints->pReadBarrierMarkReg18 = is_marking ? art_quick_read_barrier_mark_reg18 : nullptr;
- qpoints->pReadBarrierMarkReg19 = is_marking ? art_quick_read_barrier_mark_reg19 : nullptr;
- qpoints->pReadBarrierMarkReg20 = is_marking ? art_quick_read_barrier_mark_reg20 : nullptr;
- qpoints->pReadBarrierMarkReg21 = is_marking ? art_quick_read_barrier_mark_reg21 : nullptr;
- qpoints->pReadBarrierMarkReg22 = is_marking ? art_quick_read_barrier_mark_reg22 : nullptr;
- qpoints->pReadBarrierMarkReg23 = is_marking ? art_quick_read_barrier_mark_reg23 : nullptr;
- qpoints->pReadBarrierMarkReg24 = is_marking ? art_quick_read_barrier_mark_reg24 : nullptr;
- qpoints->pReadBarrierMarkReg25 = is_marking ? art_quick_read_barrier_mark_reg25 : nullptr;
- qpoints->pReadBarrierMarkReg26 = is_marking ? art_quick_read_barrier_mark_reg26 : nullptr;
- qpoints->pReadBarrierMarkReg27 = is_marking ? art_quick_read_barrier_mark_reg27 : nullptr;
- qpoints->pReadBarrierMarkReg28 = is_marking ? art_quick_read_barrier_mark_reg28 : nullptr;
- qpoints->pReadBarrierMarkReg29 = is_marking ? art_quick_read_barrier_mark_reg29 : nullptr;
+ qpoints->pReadBarrierMarkReg00 = is_active ? art_quick_read_barrier_mark_reg00 : nullptr;
+ qpoints->pReadBarrierMarkReg01 = is_active ? art_quick_read_barrier_mark_reg01 : nullptr;
+ qpoints->pReadBarrierMarkReg02 = is_active ? art_quick_read_barrier_mark_reg02 : nullptr;
+ qpoints->pReadBarrierMarkReg03 = is_active ? art_quick_read_barrier_mark_reg03 : nullptr;
+ qpoints->pReadBarrierMarkReg04 = is_active ? art_quick_read_barrier_mark_reg04 : nullptr;
+ qpoints->pReadBarrierMarkReg05 = is_active ? art_quick_read_barrier_mark_reg05 : nullptr;
+ qpoints->pReadBarrierMarkReg06 = is_active ? art_quick_read_barrier_mark_reg06 : nullptr;
+ qpoints->pReadBarrierMarkReg07 = is_active ? art_quick_read_barrier_mark_reg07 : nullptr;
+ qpoints->pReadBarrierMarkReg08 = is_active ? art_quick_read_barrier_mark_reg08 : nullptr;
+ qpoints->pReadBarrierMarkReg09 = is_active ? art_quick_read_barrier_mark_reg09 : nullptr;
+ qpoints->pReadBarrierMarkReg10 = is_active ? art_quick_read_barrier_mark_reg10 : nullptr;
+ qpoints->pReadBarrierMarkReg11 = is_active ? art_quick_read_barrier_mark_reg11 : nullptr;
+ qpoints->pReadBarrierMarkReg12 = is_active ? art_quick_read_barrier_mark_reg12 : nullptr;
+ qpoints->pReadBarrierMarkReg13 = is_active ? art_quick_read_barrier_mark_reg13 : nullptr;
+ qpoints->pReadBarrierMarkReg14 = is_active ? art_quick_read_barrier_mark_reg14 : nullptr;
+ qpoints->pReadBarrierMarkReg15 = is_active ? art_quick_read_barrier_mark_reg15 : nullptr;
+ qpoints->pReadBarrierMarkReg17 = is_active ? art_quick_read_barrier_mark_reg17 : nullptr;
+ qpoints->pReadBarrierMarkReg18 = is_active ? art_quick_read_barrier_mark_reg18 : nullptr;
+ qpoints->pReadBarrierMarkReg19 = is_active ? art_quick_read_barrier_mark_reg19 : nullptr;
+ qpoints->pReadBarrierMarkReg20 = is_active ? art_quick_read_barrier_mark_reg20 : nullptr;
+ qpoints->pReadBarrierMarkReg21 = is_active ? art_quick_read_barrier_mark_reg21 : nullptr;
+ qpoints->pReadBarrierMarkReg22 = is_active ? art_quick_read_barrier_mark_reg22 : nullptr;
+ qpoints->pReadBarrierMarkReg23 = is_active ? art_quick_read_barrier_mark_reg23 : nullptr;
+ qpoints->pReadBarrierMarkReg24 = is_active ? art_quick_read_barrier_mark_reg24 : nullptr;
+ qpoints->pReadBarrierMarkReg25 = is_active ? art_quick_read_barrier_mark_reg25 : nullptr;
+ qpoints->pReadBarrierMarkReg26 = is_active ? art_quick_read_barrier_mark_reg26 : nullptr;
+ qpoints->pReadBarrierMarkReg27 = is_active ? art_quick_read_barrier_mark_reg27 : nullptr;
+ qpoints->pReadBarrierMarkReg28 = is_active ? art_quick_read_barrier_mark_reg28 : nullptr;
+ qpoints->pReadBarrierMarkReg29 = is_active ? art_quick_read_barrier_mark_reg29 : nullptr;
// Check that array switch cases are at appropriate offsets from the introspection entrypoint.
DCHECK_ALIGNED(art_quick_read_barrier_mark_introspection, 512u);
@@ -128,7 +128,7 @@
DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET, gc_roots_diff);
// The register 16, i.e. IP0, is reserved, so there is no art_quick_read_barrier_mark_reg16.
// We're using the entry to hold a pointer to the introspection entrypoint instead.
- qpoints->pReadBarrierMarkReg16 = is_marking ? art_quick_read_barrier_mark_introspection : nullptr;
+ qpoints->pReadBarrierMarkReg16 = is_active ? art_quick_read_barrier_mark_introspection : nullptr;
}
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
@@ -188,7 +188,7 @@
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
qpoints->pReadBarrierMarkReg16 = nullptr; // IP0 is used as a temp by the asm stub.
- UpdateReadBarrierEntrypoints(qpoints, /*is_marking*/ false);
+ UpdateReadBarrierEntrypoints(qpoints, /*is_active*/ false);
qpoints->pReadBarrierSlow = artReadBarrierSlow;
qpoints->pReadBarrierForRootSlow = artReadBarrierForRootSlow;
};
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 434e33c..9978da5 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -86,68 +86,68 @@
extern "C" int64_t __divdi3(int64_t, int64_t);
extern "C" int64_t __moddi3(int64_t, int64_t);
-void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_marking) {
- qpoints->pReadBarrierMarkReg01 = is_marking ? art_quick_read_barrier_mark_reg01 : nullptr;
+void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) {
+ qpoints->pReadBarrierMarkReg01 = is_active ? art_quick_read_barrier_mark_reg01 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg01),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg02 = is_marking ? art_quick_read_barrier_mark_reg02 : nullptr;
+ qpoints->pReadBarrierMarkReg02 = is_active ? art_quick_read_barrier_mark_reg02 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg02),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg03 = is_marking ? art_quick_read_barrier_mark_reg03 : nullptr;
+ qpoints->pReadBarrierMarkReg03 = is_active ? art_quick_read_barrier_mark_reg03 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg03),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg04 = is_marking ? art_quick_read_barrier_mark_reg04 : nullptr;
+ qpoints->pReadBarrierMarkReg04 = is_active ? art_quick_read_barrier_mark_reg04 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg04),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg05 = is_marking ? art_quick_read_barrier_mark_reg05 : nullptr;
+ qpoints->pReadBarrierMarkReg05 = is_active ? art_quick_read_barrier_mark_reg05 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg05),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg06 = is_marking ? art_quick_read_barrier_mark_reg06 : nullptr;
+ qpoints->pReadBarrierMarkReg06 = is_active ? art_quick_read_barrier_mark_reg06 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg06),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg07 = is_marking ? art_quick_read_barrier_mark_reg07 : nullptr;
+ qpoints->pReadBarrierMarkReg07 = is_active ? art_quick_read_barrier_mark_reg07 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg07),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg08 = is_marking ? art_quick_read_barrier_mark_reg08 : nullptr;
+ qpoints->pReadBarrierMarkReg08 = is_active ? art_quick_read_barrier_mark_reg08 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg08),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg09 = is_marking ? art_quick_read_barrier_mark_reg09 : nullptr;
+ qpoints->pReadBarrierMarkReg09 = is_active ? art_quick_read_barrier_mark_reg09 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg09),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg10 = is_marking ? art_quick_read_barrier_mark_reg10 : nullptr;
+ qpoints->pReadBarrierMarkReg10 = is_active ? art_quick_read_barrier_mark_reg10 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg10),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg11 = is_marking ? art_quick_read_barrier_mark_reg11 : nullptr;
+ qpoints->pReadBarrierMarkReg11 = is_active ? art_quick_read_barrier_mark_reg11 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg11),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg12 = is_marking ? art_quick_read_barrier_mark_reg12 : nullptr;
+ qpoints->pReadBarrierMarkReg12 = is_active ? art_quick_read_barrier_mark_reg12 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg12),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg13 = is_marking ? art_quick_read_barrier_mark_reg13 : nullptr;
+ qpoints->pReadBarrierMarkReg13 = is_active ? art_quick_read_barrier_mark_reg13 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg13),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg14 = is_marking ? art_quick_read_barrier_mark_reg14 : nullptr;
+ qpoints->pReadBarrierMarkReg14 = is_active ? art_quick_read_barrier_mark_reg14 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg14),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg17 = is_marking ? art_quick_read_barrier_mark_reg17 : nullptr;
+ qpoints->pReadBarrierMarkReg17 = is_active ? art_quick_read_barrier_mark_reg17 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg17),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg18 = is_marking ? art_quick_read_barrier_mark_reg18 : nullptr;
+ qpoints->pReadBarrierMarkReg18 = is_active ? art_quick_read_barrier_mark_reg18 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg18),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg19 = is_marking ? art_quick_read_barrier_mark_reg19 : nullptr;
+ qpoints->pReadBarrierMarkReg19 = is_active ? art_quick_read_barrier_mark_reg19 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg19),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg20 = is_marking ? art_quick_read_barrier_mark_reg20 : nullptr;
+ qpoints->pReadBarrierMarkReg20 = is_active ? art_quick_read_barrier_mark_reg20 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg20),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg21 = is_marking ? art_quick_read_barrier_mark_reg21 : nullptr;
+ qpoints->pReadBarrierMarkReg21 = is_active ? art_quick_read_barrier_mark_reg21 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg21),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg22 = is_marking ? art_quick_read_barrier_mark_reg22 : nullptr;
+ qpoints->pReadBarrierMarkReg22 = is_active ? art_quick_read_barrier_mark_reg22 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg22),
"Non-direct C stub marked direct.");
- qpoints->pReadBarrierMarkReg29 = is_marking ? art_quick_read_barrier_mark_reg29 : nullptr;
+ qpoints->pReadBarrierMarkReg29 = is_active ? art_quick_read_barrier_mark_reg29 : nullptr;
static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg29),
"Non-direct C stub marked direct.");
}
@@ -160,7 +160,7 @@
jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
// Alloc
- ResetQuickAllocEntryPoints(qpoints, /*is_marking*/ false);
+ ResetQuickAllocEntryPoints(qpoints, /*is_active*/ false);
// Cast
qpoints->pInstanceofNonTrivial = artInstanceOfFromCode;
@@ -412,7 +412,7 @@
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
static_assert(IsDirectEntrypoint(kQuickReadBarrierJni), "Direct C stub not marked direct.");
- UpdateReadBarrierEntrypoints(qpoints, /*is_marking*/ false);
+ UpdateReadBarrierEntrypoints(qpoints, /*is_active*/ false);
// Cannot use the following registers to pass arguments:
// 0(ZERO), 1(AT), 16(S0), 17(S1), 24(T8), 25(T9), 26(K0), 27(K1), 28(GP), 29(SP), 31(RA).
// Note that there are 30 entry points only: 00 for register 1(AT), ..., 29 for register 30(S8).
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 61a3a04..7bbcbf0 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -421,7 +421,7 @@
SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP
.endm
-.macro RESTORE_SAVE_EVERYTHING_FRAME
+.macro RESTORE_SAVE_EVERYTHING_FRAME restore_a0=1
addiu $sp, $sp, ARG_SLOT_SIZE # remove argument slots on the stack
.cfi_adjust_cfa_offset -ARG_SLOT_SIZE
@@ -490,8 +490,10 @@
.cfi_restore 6
lw $a1, 160($sp)
.cfi_restore 5
+ .if \restore_a0
lw $a0, 156($sp)
.cfi_restore 4
+ .endif
lw $v1, 152($sp)
.cfi_restore 3
lw $v0, 148($sp)
@@ -507,16 +509,26 @@
.endm
/*
- * Macro that set calls through to artDeliverPendingExceptionFromCode, where the pending
- * exception is Thread::Current()->exception_
+ * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending
+ * exception is Thread::Current()->exception_ when the runtime method frame is ready.
+ * Requires $gp properly set up.
*/
-.macro DELIVER_PENDING_EXCEPTION
- SETUP_SAVE_ALL_CALLEE_SAVES_FRAME # save callee saves for throw
+.macro DELIVER_PENDING_EXCEPTION_FRAME_READY
la $t9, artDeliverPendingExceptionFromCode
jalr $zero, $t9 # artDeliverPendingExceptionFromCode(Thread*)
move $a0, rSELF # pass Thread::Current
.endm
+ /*
+ * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending
+ * exception is Thread::Current()->exception_.
+ * Requires $gp properly set up.
+ */
+.macro DELIVER_PENDING_EXCEPTION
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME # save callee saves for throw
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+.endm
+
.macro RETURN_IF_NO_EXCEPTION
lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
RESTORE_SAVE_REFS_ONLY_FRAME
@@ -1660,30 +1672,51 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+// Macro for string and type resolution and initialization.
+// $a0 is both input and output.
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint
+ .extern \entrypoint
+ENTRY_NO_GP \name
+ SETUP_SAVE_EVERYTHING_FRAME # Save everything in case of GC.
+ move $s2, $gp # Preserve $gp across the call for exception delivery.
+ la $t9, \entrypoint
+ jalr $t9 # (uint32_t index, Thread*)
+ move $a1, rSELF # Pass Thread::Current (in delay slot).
+ beqz $v0, 1f # Success?
+ move $a0, $v0 # Move result to $a0 (in delay slot).
+ RESTORE_SAVE_EVERYTHING_FRAME 0 # Restore everything except $a0.
+ jalr $zero, $ra # Return on success.
+ nop
+1:
+ move $gp, $s2
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+END \name
+.endm
+
/*
* Entry from managed code to resolve a string, this stub will allocate a String and deliver an
* exception on error. On success the String is returned. A0 holds the string index. The fast
* path check for hit in strings cache has already been performed.
*/
-ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
/*
* Entry from managed code when uninitialized static storage, this stub will run the class
* initializer and deliver the exception on error. On success the static storage base is
* returned.
*/
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
/*
* Entry from managed code when dex cache misses for a type_idx.
*/
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
/*
* Entry from managed code when type_idx needs to be checked for access and dex cache may also
* miss.
*/
-ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
/*
* Called by managed code when the value in rSUSPEND has been decremented to 0.
@@ -1854,7 +1887,8 @@
nop
2:
- lw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)
+ lw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)
+ move $gp, $s3 # restore $gp from $s3
# This will create a new save-all frame, required by the runtime.
DELIVER_PENDING_EXCEPTION
END art_quick_generic_jni_trampoline
@@ -2213,8 +2247,32 @@
*/
.macro READ_BARRIER_MARK_REG name, reg
ENTRY \name
- /* TODO: optimizations: mark bit, forwarding. */
- addiu $sp, $sp, -160 # includes 16 bytes of space for argument registers a0-a3
+ // Null check so that we can load the lock word.
+ bnez \reg, .Lnot_null_\name
+ nop
+.Lret_rb_\name:
+ jalr $zero, $ra
+ nop
+.Lnot_null_\name:
+ // Check lock word for mark bit, if marked return.
+ lw $t9, MIRROR_OBJECT_LOCK_WORD_OFFSET(\reg)
+ .set push
+ .set noat
+ sll $at, $t9, 31 - LOCK_WORD_MARK_BIT_SHIFT # Move mark bit to sign bit.
+ bltz $at, .Lret_rb_\name
+#if (LOCK_WORD_STATE_SHIFT != 30) || (LOCK_WORD_STATE_FORWARDING_ADDRESS != 3)
+ // The below code depends on the lock word state being in the highest bits
+ // and the "forwarding address" state having all bits set.
+#error "Unexpected lock word state shift or forwarding address state value."
+#endif
+ // Test that both the forwarding state bits are 1.
+ sll $at, $t9, 1
+ and $at, $at, $t9 # Sign bit = 1 IFF both bits are 1.
+ bltz $at, .Lret_forwarding_address\name
+ nop
+ .set pop
+
+ addiu $sp, $sp, -160 # Includes 16 bytes of space for argument registers a0-a3.
.cfi_adjust_cfa_offset 160
sw $ra, 156($sp)
@@ -2319,6 +2377,12 @@
jalr $zero, $ra
addiu $sp, $sp, 160
.cfi_adjust_cfa_offset -160
+
+.Lret_forwarding_address\name:
+ jalr $zero, $ra
+ // Shift left by the forwarding address shift. This clears out the state bits since they are
+ // in the top 2 bits of the lock word.
+ sll \reg, $t9, LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT
END \name
.endm
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
index f8242ae..763d93e 100644
--- a/runtime/arch/mips64/entrypoints_init_mips64.cc
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -86,27 +86,27 @@
extern "C" int64_t __moddi3(int64_t, int64_t);
// No read barrier entrypoints for marking registers.
-void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_marking) {
- qpoints->pReadBarrierMarkReg01 = is_marking ? art_quick_read_barrier_mark_reg01 : nullptr;
- qpoints->pReadBarrierMarkReg02 = is_marking ? art_quick_read_barrier_mark_reg02 : nullptr;
- qpoints->pReadBarrierMarkReg03 = is_marking ? art_quick_read_barrier_mark_reg03 : nullptr;
- qpoints->pReadBarrierMarkReg04 = is_marking ? art_quick_read_barrier_mark_reg04 : nullptr;
- qpoints->pReadBarrierMarkReg05 = is_marking ? art_quick_read_barrier_mark_reg05 : nullptr;
- qpoints->pReadBarrierMarkReg06 = is_marking ? art_quick_read_barrier_mark_reg06 : nullptr;
- qpoints->pReadBarrierMarkReg07 = is_marking ? art_quick_read_barrier_mark_reg07 : nullptr;
- qpoints->pReadBarrierMarkReg08 = is_marking ? art_quick_read_barrier_mark_reg08 : nullptr;
- qpoints->pReadBarrierMarkReg09 = is_marking ? art_quick_read_barrier_mark_reg09 : nullptr;
- qpoints->pReadBarrierMarkReg10 = is_marking ? art_quick_read_barrier_mark_reg10 : nullptr;
- qpoints->pReadBarrierMarkReg11 = is_marking ? art_quick_read_barrier_mark_reg11 : nullptr;
- qpoints->pReadBarrierMarkReg12 = is_marking ? art_quick_read_barrier_mark_reg12 : nullptr;
- qpoints->pReadBarrierMarkReg13 = is_marking ? art_quick_read_barrier_mark_reg13 : nullptr;
- qpoints->pReadBarrierMarkReg17 = is_marking ? art_quick_read_barrier_mark_reg17 : nullptr;
- qpoints->pReadBarrierMarkReg18 = is_marking ? art_quick_read_barrier_mark_reg18 : nullptr;
- qpoints->pReadBarrierMarkReg19 = is_marking ? art_quick_read_barrier_mark_reg19 : nullptr;
- qpoints->pReadBarrierMarkReg20 = is_marking ? art_quick_read_barrier_mark_reg20 : nullptr;
- qpoints->pReadBarrierMarkReg21 = is_marking ? art_quick_read_barrier_mark_reg21 : nullptr;
- qpoints->pReadBarrierMarkReg22 = is_marking ? art_quick_read_barrier_mark_reg22 : nullptr;
- qpoints->pReadBarrierMarkReg29 = is_marking ? art_quick_read_barrier_mark_reg29 : nullptr;
+void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) {
+ qpoints->pReadBarrierMarkReg01 = is_active ? art_quick_read_barrier_mark_reg01 : nullptr;
+ qpoints->pReadBarrierMarkReg02 = is_active ? art_quick_read_barrier_mark_reg02 : nullptr;
+ qpoints->pReadBarrierMarkReg03 = is_active ? art_quick_read_barrier_mark_reg03 : nullptr;
+ qpoints->pReadBarrierMarkReg04 = is_active ? art_quick_read_barrier_mark_reg04 : nullptr;
+ qpoints->pReadBarrierMarkReg05 = is_active ? art_quick_read_barrier_mark_reg05 : nullptr;
+ qpoints->pReadBarrierMarkReg06 = is_active ? art_quick_read_barrier_mark_reg06 : nullptr;
+ qpoints->pReadBarrierMarkReg07 = is_active ? art_quick_read_barrier_mark_reg07 : nullptr;
+ qpoints->pReadBarrierMarkReg08 = is_active ? art_quick_read_barrier_mark_reg08 : nullptr;
+ qpoints->pReadBarrierMarkReg09 = is_active ? art_quick_read_barrier_mark_reg09 : nullptr;
+ qpoints->pReadBarrierMarkReg10 = is_active ? art_quick_read_barrier_mark_reg10 : nullptr;
+ qpoints->pReadBarrierMarkReg11 = is_active ? art_quick_read_barrier_mark_reg11 : nullptr;
+ qpoints->pReadBarrierMarkReg12 = is_active ? art_quick_read_barrier_mark_reg12 : nullptr;
+ qpoints->pReadBarrierMarkReg13 = is_active ? art_quick_read_barrier_mark_reg13 : nullptr;
+ qpoints->pReadBarrierMarkReg17 = is_active ? art_quick_read_barrier_mark_reg17 : nullptr;
+ qpoints->pReadBarrierMarkReg18 = is_active ? art_quick_read_barrier_mark_reg18 : nullptr;
+ qpoints->pReadBarrierMarkReg19 = is_active ? art_quick_read_barrier_mark_reg19 : nullptr;
+ qpoints->pReadBarrierMarkReg20 = is_active ? art_quick_read_barrier_mark_reg20 : nullptr;
+ qpoints->pReadBarrierMarkReg21 = is_active ? art_quick_read_barrier_mark_reg21 : nullptr;
+ qpoints->pReadBarrierMarkReg22 = is_active ? art_quick_read_barrier_mark_reg22 : nullptr;
+ qpoints->pReadBarrierMarkReg29 = is_active ? art_quick_read_barrier_mark_reg29 : nullptr;
}
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
@@ -168,7 +168,7 @@
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
- UpdateReadBarrierEntrypoints(qpoints, /*is_marking*/ false);
+ UpdateReadBarrierEntrypoints(qpoints, /*is_active*/ false);
// Cannot use the following registers to pass arguments:
// 0(ZERO), 1(AT), 15(T3), 16(S0), 17(S1), 24(T8), 25(T9), 26(K0), 27(K1), 28(GP), 29(SP), 31(RA).
// Note that there are 30 entry points only: 00 for register 1(AT), ..., 29 for register 30(S8).
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 24caa0e..8f713a1 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -447,7 +447,7 @@
SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP
.endm
-.macro RESTORE_SAVE_EVERYTHING_FRAME
+.macro RESTORE_SAVE_EVERYTHING_FRAME restore_a0=1
// Restore FP registers.
l.d $f31, 264($sp)
l.d $f30, 256($sp)
@@ -530,8 +530,10 @@
.cfi_restore 6
ld $a1, 304($sp)
.cfi_restore 5
+ .if \restore_a0
ld $a0, 296($sp)
.cfi_restore 4
+ .endif
ld $v1, 288($sp)
.cfi_restore 3
ld $v0, 280($sp)
@@ -547,16 +549,24 @@
.endm
/*
- * Macro that set calls through to artDeliverPendingExceptionFromCode,
- * where the pending
- * exception is Thread::Current()->exception_
+ * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending
+ * exception is Thread::Current()->exception_ when the runtime method frame is ready.
+ * Requires $gp properly set up.
+ */
+.macro DELIVER_PENDING_EXCEPTION_FRAME_READY
+ dla $t9, artDeliverPendingExceptionFromCode
+ jalr $zero, $t9 # artDeliverPendingExceptionFromCode(Thread*)
+ move $a0, rSELF # pass Thread::Current
+.endm
+
+ /*
+ * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending
+ * exception is Thread::Current()->exception_.
*/
.macro DELIVER_PENDING_EXCEPTION
SETUP_GP
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME # save callee saves for throw
- dla $t9, artDeliverPendingExceptionFromCode
- jalr $zero, $t9 # artDeliverPendingExceptionFromCode(Thread*)
- move $a0, rSELF # pass Thread::Current
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
.endm
.macro RETURN_IF_NO_EXCEPTION
@@ -1615,30 +1625,48 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+// Macro for string and type resolution and initialization.
+// $a0 is both input and output.
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint
+ .extern \entrypoint
+ENTRY_NO_GP \name
+ SETUP_SAVE_EVERYTHING_FRAME # Save everything in case of GC.
+ dla $t9, \entrypoint
+ jalr $t9 # (uint32_t index, Thread*)
+ move $a1, rSELF # Pass Thread::Current (in delay slot).
+ beqz $v0, 1f # Success?
+ move $a0, $v0 # Move result to $a0 (in delay slot).
+ RESTORE_SAVE_EVERYTHING_FRAME 0 # Restore everything except $a0.
+ jic $ra, 0 # Return on success.
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+END \name
+.endm
+
/*
* Entry from managed code to resolve a string, this stub will allocate a String and deliver an
* exception on error. On success the String is returned. A0 holds the string index. The fast
* path check for hit in strings cache has already been performed.
*/
-ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
/*
* Entry from managed code when uninitialized static storage, this stub will run the class
* initializer and deliver the exception on error. On success the static storage base is
* returned.
*/
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
/*
* Entry from managed code when dex cache misses for a type_idx.
*/
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
/*
* Entry from managed code when type_idx needs to be checked for access and dex cache may also
* miss.
*/
-ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
/*
* Called by managed code when the value in rSUSPEND has been decremented to 0.
@@ -2067,7 +2095,29 @@
*/
.macro READ_BARRIER_MARK_REG name, reg
ENTRY \name
- /* TODO: optimizations: mark bit, forwarding. */
+ // Null check so that we can load the lock word.
+ bnezc \reg, .Lnot_null_\name
+ nop
+.Lret_rb_\name:
+ jic $ra, 0
+.Lnot_null_\name:
+ // Check lock word for mark bit, if marked return.
+ lw $t9, MIRROR_OBJECT_LOCK_WORD_OFFSET(\reg)
+ .set push
+ .set noat
+ sll $at, $t9, 31 - LOCK_WORD_MARK_BIT_SHIFT # Move mark bit to sign bit.
+ bltzc $at, .Lret_rb_\name
+#if (LOCK_WORD_STATE_SHIFT != 30) || (LOCK_WORD_STATE_FORWARDING_ADDRESS != 3)
+ // The below code depends on the lock word state being in the highest bits
+ // and the "forwarding address" state having all bits set.
+#error "Unexpected lock word state shift or forwarding address state value."
+#endif
+ // Test that both the forwarding state bits are 1.
+ sll $at, $t9, 1
+ and $at, $at, $t9 # Sign bit = 1 IFF both bits are 1.
+ bltzc $at, .Lret_forwarding_address\name
+ .set pop
+
daddiu $sp, $sp, -320
.cfi_adjust_cfa_offset 320
@@ -2202,6 +2252,13 @@
jalr $zero, $ra
daddiu $sp, $sp, 320
.cfi_adjust_cfa_offset -320
+
+.Lret_forwarding_address\name:
+ // Shift left by the forwarding address shift. This clears out the state bits since they are
+ // in the top 2 bits of the lock word.
+ sll \reg, $t9, LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT
+ jalr $zero, $ra
+ dext \reg, \reg, 0, 32 # Make sure the address is zero-extended.
END \name
.endm
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index 9cd4a3e..102faf1 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -44,14 +44,14 @@
extern "C" mirror::Object* art_quick_read_barrier_slow(mirror::Object*, mirror::Object*, uint32_t);
extern "C" mirror::Object* art_quick_read_barrier_for_root_slow(GcRoot<mirror::Object>*);
-void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_marking) {
- qpoints->pReadBarrierMarkReg00 = is_marking ? art_quick_read_barrier_mark_reg00 : nullptr;
- qpoints->pReadBarrierMarkReg01 = is_marking ? art_quick_read_barrier_mark_reg01 : nullptr;
- qpoints->pReadBarrierMarkReg02 = is_marking ? art_quick_read_barrier_mark_reg02 : nullptr;
- qpoints->pReadBarrierMarkReg03 = is_marking ? art_quick_read_barrier_mark_reg03 : nullptr;
- qpoints->pReadBarrierMarkReg05 = is_marking ? art_quick_read_barrier_mark_reg05 : nullptr;
- qpoints->pReadBarrierMarkReg06 = is_marking ? art_quick_read_barrier_mark_reg06 : nullptr;
- qpoints->pReadBarrierMarkReg07 = is_marking ? art_quick_read_barrier_mark_reg07 : nullptr;
+void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) {
+ qpoints->pReadBarrierMarkReg00 = is_active ? art_quick_read_barrier_mark_reg00 : nullptr;
+ qpoints->pReadBarrierMarkReg01 = is_active ? art_quick_read_barrier_mark_reg01 : nullptr;
+ qpoints->pReadBarrierMarkReg02 = is_active ? art_quick_read_barrier_mark_reg02 : nullptr;
+ qpoints->pReadBarrierMarkReg03 = is_active ? art_quick_read_barrier_mark_reg03 : nullptr;
+ qpoints->pReadBarrierMarkReg05 = is_active ? art_quick_read_barrier_mark_reg05 : nullptr;
+ qpoints->pReadBarrierMarkReg06 = is_active ? art_quick_read_barrier_mark_reg06 : nullptr;
+ qpoints->pReadBarrierMarkReg07 = is_active ? art_quick_read_barrier_mark_reg07 : nullptr;
}
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
@@ -97,7 +97,7 @@
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
- UpdateReadBarrierEntrypoints(qpoints, /*is_marking*/ false);
+ UpdateReadBarrierEntrypoints(qpoints, /*is_active*/ false);
qpoints->pReadBarrierMarkReg04 = nullptr; // Cannot use register 4 (ESP) to pass arguments.
// x86 has only 8 core registers.
qpoints->pReadBarrierMarkReg08 = nullptr;
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index a326b4e..1e56e8a 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -55,22 +55,22 @@
extern "C" mirror::Object* art_quick_read_barrier_slow(mirror::Object*, mirror::Object*, uint32_t);
extern "C" mirror::Object* art_quick_read_barrier_for_root_slow(GcRoot<mirror::Object>*);
-void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_marking) {
- qpoints->pReadBarrierMarkReg00 = is_marking ? art_quick_read_barrier_mark_reg00 : nullptr;
- qpoints->pReadBarrierMarkReg01 = is_marking ? art_quick_read_barrier_mark_reg01 : nullptr;
- qpoints->pReadBarrierMarkReg02 = is_marking ? art_quick_read_barrier_mark_reg02 : nullptr;
- qpoints->pReadBarrierMarkReg03 = is_marking ? art_quick_read_barrier_mark_reg03 : nullptr;
- qpoints->pReadBarrierMarkReg05 = is_marking ? art_quick_read_barrier_mark_reg05 : nullptr;
- qpoints->pReadBarrierMarkReg06 = is_marking ? art_quick_read_barrier_mark_reg06 : nullptr;
- qpoints->pReadBarrierMarkReg07 = is_marking ? art_quick_read_barrier_mark_reg07 : nullptr;
- qpoints->pReadBarrierMarkReg08 = is_marking ? art_quick_read_barrier_mark_reg08 : nullptr;
- qpoints->pReadBarrierMarkReg09 = is_marking ? art_quick_read_barrier_mark_reg09 : nullptr;
- qpoints->pReadBarrierMarkReg10 = is_marking ? art_quick_read_barrier_mark_reg10 : nullptr;
- qpoints->pReadBarrierMarkReg11 = is_marking ? art_quick_read_barrier_mark_reg11 : nullptr;
- qpoints->pReadBarrierMarkReg12 = is_marking ? art_quick_read_barrier_mark_reg12 : nullptr;
- qpoints->pReadBarrierMarkReg13 = is_marking ? art_quick_read_barrier_mark_reg13 : nullptr;
- qpoints->pReadBarrierMarkReg14 = is_marking ? art_quick_read_barrier_mark_reg14 : nullptr;
- qpoints->pReadBarrierMarkReg15 = is_marking ? art_quick_read_barrier_mark_reg15 : nullptr;
+void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) {
+ qpoints->pReadBarrierMarkReg00 = is_active ? art_quick_read_barrier_mark_reg00 : nullptr;
+ qpoints->pReadBarrierMarkReg01 = is_active ? art_quick_read_barrier_mark_reg01 : nullptr;
+ qpoints->pReadBarrierMarkReg02 = is_active ? art_quick_read_barrier_mark_reg02 : nullptr;
+ qpoints->pReadBarrierMarkReg03 = is_active ? art_quick_read_barrier_mark_reg03 : nullptr;
+ qpoints->pReadBarrierMarkReg05 = is_active ? art_quick_read_barrier_mark_reg05 : nullptr;
+ qpoints->pReadBarrierMarkReg06 = is_active ? art_quick_read_barrier_mark_reg06 : nullptr;
+ qpoints->pReadBarrierMarkReg07 = is_active ? art_quick_read_barrier_mark_reg07 : nullptr;
+ qpoints->pReadBarrierMarkReg08 = is_active ? art_quick_read_barrier_mark_reg08 : nullptr;
+ qpoints->pReadBarrierMarkReg09 = is_active ? art_quick_read_barrier_mark_reg09 : nullptr;
+ qpoints->pReadBarrierMarkReg10 = is_active ? art_quick_read_barrier_mark_reg10 : nullptr;
+ qpoints->pReadBarrierMarkReg11 = is_active ? art_quick_read_barrier_mark_reg11 : nullptr;
+ qpoints->pReadBarrierMarkReg12 = is_active ? art_quick_read_barrier_mark_reg12 : nullptr;
+ qpoints->pReadBarrierMarkReg13 = is_active ? art_quick_read_barrier_mark_reg13 : nullptr;
+ qpoints->pReadBarrierMarkReg14 = is_active ? art_quick_read_barrier_mark_reg14 : nullptr;
+ qpoints->pReadBarrierMarkReg15 = is_active ? art_quick_read_barrier_mark_reg15 : nullptr;
}
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
@@ -119,7 +119,7 @@
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
- UpdateReadBarrierEntrypoints(qpoints, /*is_marking*/ false);
+ UpdateReadBarrierEntrypoints(qpoints, /*is_active*/ false);
qpoints->pReadBarrierMarkReg04 = nullptr; // Cannot use register 4 (RSP) to pass arguments.
// x86-64 has only 16 core registers.
qpoints->pReadBarrierMarkReg16 = nullptr;
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 935fd81..136ed12 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -33,6 +33,7 @@
template <bool kCount>
const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
+ // Every name should have the same width and end with a space. Abbreviate if necessary:
"Misc ",
"SwitchTbl ",
"SlowPaths ",
@@ -49,6 +50,7 @@
"Successors ",
"Dominated ",
"Instruction ",
+ "CtorFenceIns ",
"InvokeInputs ",
"PhiInputs ",
"LoopInfo ",
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index c39429c..60b6ea8 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -59,6 +59,7 @@
kArenaAllocSuccessors,
kArenaAllocDominated,
kArenaAllocInstruction,
+ kArenaAllocConstructorFenceInputs,
kArenaAllocInvokeInputs,
kArenaAllocPhiInputs,
kArenaAllocLoopInfo,
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 2414b5f..03ae63a 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -373,19 +373,19 @@
bool IsSharedHeld(const Thread* self) const;
// Assert the current thread has shared access to the ReaderWriterMutex.
- void AssertSharedHeld(const Thread* self) ASSERT_SHARED_CAPABILITY(this) {
+ ALWAYS_INLINE void AssertSharedHeld(const Thread* self) ASSERT_SHARED_CAPABILITY(this) {
if (kDebugLocking && (gAborting == 0)) {
// TODO: we can only assert this well when self != null.
CHECK(IsSharedHeld(self) || self == nullptr) << *this;
}
}
- void AssertReaderHeld(const Thread* self) ASSERT_SHARED_CAPABILITY(this) {
+ ALWAYS_INLINE void AssertReaderHeld(const Thread* self) ASSERT_SHARED_CAPABILITY(this) {
AssertSharedHeld(self);
}
// Assert the current thread doesn't hold this ReaderWriterMutex either in shared or exclusive
// mode.
- void AssertNotHeld(const Thread* self) ASSERT_SHARED_CAPABILITY(!this) {
+ ALWAYS_INLINE void AssertNotHeld(const Thread* self) ASSERT_SHARED_CAPABILITY(!this) {
if (kDebugLocking && (gAborting == 0)) {
CHECK(!IsSharedHeld(self)) << *this;
}
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 7736f3d..94b2615 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -123,7 +123,7 @@
// To generate a base64 encoded Dex file (such as kGoodTestDex, below)
// from Smali files, use:
//
-// smali -o classes.dex class1.smali [class2.smali ...]
+// smali assemble -o classes.dex class1.smali [class2.smali ...]
// base64 classes.dex >classes.dex.base64
// For reference.
@@ -1461,7 +1461,7 @@
// To generate a base64 encoded Dex file version 037 from Smali files, use:
//
-// smali --api-level 24 -o classes.dex class1.smali [class2.smali ...]
+// smali assemble --api 24 -o classes.dex class1.smali [class2.smali ...]
// base64 classes.dex >classes.dex.base64
// Dex file version 037 generated from:
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 355d7b3..6b96567 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -58,18 +58,13 @@
}
}
-constexpr Runtime::CalleeSaveType kInitEntrypointSaveType =
- // TODO: Change allocation entrypoints on MIPS and MIPS64 to kSaveEverything.
- (kRuntimeISA == kMips || kRuntimeISA == kMips64) ? Runtime::kSaveRefsOnly
- : Runtime::kSaveEverything;
-
extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Called to ensure static storage base is initialized for direct static field reads and writes.
// A class may be accessing another class' fields when it doesn't have access, as access has been
// given by inheritance.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveEverything);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false);
@@ -83,7 +78,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
// Called when method->dex_cache_resolved_types_[] misses.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveEverything);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false);
@@ -98,7 +93,7 @@
// Called when caller isn't guaranteed to have access to a type and the dex cache may be
// unpopulated.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveEverything);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true);
@@ -111,7 +106,7 @@
extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveEverything);
ArtMethod* caller = caller_and_outer.caller;
mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx));
if (LIKELY(result != nullptr)) {
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index cd30d9d..c3dd21f 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -51,6 +51,7 @@
static constexpr size_t kCardSize = 1 << kCardShift;
static constexpr uint8_t kCardClean = 0x0;
static constexpr uint8_t kCardDirty = 0x70;
+ static constexpr uint8_t kCardAged = kCardDirty - 1;
static CardTable* Create(const uint8_t* heap_begin, size_t heap_capacity);
~CardTable();
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index d5c36bf..3503973 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -152,7 +152,8 @@
inline mirror::Object* ConcurrentCopying::MarkFromReadBarrier(mirror::Object* from_ref) {
mirror::Object* ret;
- if (from_ref == nullptr) {
+ // We can get here before marking starts since we gray immune objects before the marking phase.
+ if (from_ref == nullptr || !Thread::Current()->GetIsGcMarking()) {
return from_ref;
}
// TODO: Consider removing this check when we are done investigating slow paths. b/30162165
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index e27c1ec..a450a75 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -77,6 +77,7 @@
mark_stack_lock_("concurrent copying mark stack lock", kMarkSweepMarkStackLock),
thread_running_gc_(nullptr),
is_marking_(false),
+ is_using_read_barrier_entrypoints_(false),
is_active_(false),
is_asserting_to_space_invariant_(false),
region_space_bitmap_(nullptr),
@@ -163,6 +164,15 @@
ReaderMutexLock mu(self, *Locks::mutator_lock_);
InitializePhase();
}
+ if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
+ // Switch to read barrier mark entrypoints before we gray the objects. This is required in case
+ // a mutator sees a gray bit and dispatches on the entrpoint. (b/37876887).
+ ActivateReadBarrierEntrypoints();
+ // Gray dirty immune objects concurrently to reduce GC pause times. We re-process gray cards in
+ // the pause.
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ GrayAllDirtyImmuneObjects();
+ }
FlipThreadRoots();
{
ReaderMutexLock mu(self, *Locks::mutator_lock_);
@@ -192,6 +202,59 @@
thread_running_gc_ = nullptr;
}
+class ConcurrentCopying::ActivateReadBarrierEntrypointsCheckpoint : public Closure {
+ public:
+ explicit ActivateReadBarrierEntrypointsCheckpoint(ConcurrentCopying* concurrent_copying)
+ : concurrent_copying_(concurrent_copying) {}
+
+ void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
+ // Note: self is not necessarily equal to thread since thread may be suspended.
+ Thread* self = Thread::Current();
+ DCHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc)
+ << thread->GetState() << " thread " << thread << " self " << self;
+ // Switch to the read barrier entrypoints.
+ thread->SetReadBarrierEntrypoints();
+ // If thread is a running mutator, then act on behalf of the garbage collector.
+ // See the code in ThreadList::RunCheckpoint.
+ concurrent_copying_->GetBarrier().Pass(self);
+ }
+
+ private:
+ ConcurrentCopying* const concurrent_copying_;
+};
+
+class ConcurrentCopying::ActivateReadBarrierEntrypointsCallback : public Closure {
+ public:
+ explicit ActivateReadBarrierEntrypointsCallback(ConcurrentCopying* concurrent_copying)
+ : concurrent_copying_(concurrent_copying) {}
+
+ void Run(Thread* self ATTRIBUTE_UNUSED) OVERRIDE REQUIRES(Locks::thread_list_lock_) {
+ // This needs to run under the thread_list_lock_ critical section in ThreadList::RunCheckpoint()
+ // to avoid a race with ThreadList::Register().
+ CHECK(!concurrent_copying_->is_using_read_barrier_entrypoints_);
+ concurrent_copying_->is_using_read_barrier_entrypoints_ = true;
+ }
+
+ private:
+ ConcurrentCopying* const concurrent_copying_;
+};
+
+void ConcurrentCopying::ActivateReadBarrierEntrypoints() {
+ Thread* const self = Thread::Current();
+ ActivateReadBarrierEntrypointsCheckpoint checkpoint(this);
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
+ gc_barrier_->Init(self, 0);
+ ActivateReadBarrierEntrypointsCallback callback(this);
+ const size_t barrier_count = thread_list->RunCheckpoint(&checkpoint, &callback);
+ // If there are no threads to wait which implies that all the checkpoint functions are finished,
+ // then no need to release the mutator lock.
+ if (barrier_count == 0) {
+ return;
+ }
+ ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+ gc_barrier_->Increment(self, barrier_count);
+}
+
void ConcurrentCopying::BindBitmaps() {
Thread* self = Thread::Current();
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
@@ -352,9 +415,12 @@
if (kVerifyNoMissingCardMarks) {
cc->VerifyNoMissingCardMarks();
}
- CHECK(thread == self);
+ CHECK_EQ(thread, self);
Locks::mutator_lock_->AssertExclusiveHeld(self);
- cc->region_space_->SetFromSpace(cc->rb_table_, cc->force_evacuate_all_);
+ {
+ TimingLogger::ScopedTiming split2("(Paused)SetFromSpace", cc->GetTimings());
+ cc->region_space_->SetFromSpace(cc->rb_table_, cc->force_evacuate_all_);
+ }
cc->SwapStacks();
if (ConcurrentCopying::kEnableFromSpaceAccountingCheck) {
cc->RecordLiveStackFreezeSize(self);
@@ -368,11 +434,11 @@
}
if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
CHECK(Runtime::Current()->IsAotCompiler());
- TimingLogger::ScopedTiming split2("(Paused)VisitTransactionRoots", cc->GetTimings());
+ TimingLogger::ScopedTiming split3("(Paused)VisitTransactionRoots", cc->GetTimings());
Runtime::Current()->VisitTransactionRoots(cc);
}
if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
- cc->GrayAllDirtyImmuneObjects();
+ cc->GrayAllNewlyDirtyImmuneObjects();
if (kIsDebugBuild) {
// Check that all non-gray immune objects only refernce immune objects.
cc->VerifyGrayImmuneObjects();
@@ -519,8 +585,8 @@
void ConcurrentCopying::VerifyNoMissingCardMarkCallback(mirror::Object* obj, void* arg) {
auto* collector = reinterpret_cast<ConcurrentCopying*>(arg);
- // Objects not on dirty cards should never have references to newly allocated regions.
- if (!collector->heap_->GetCardTable()->IsDirty(obj)) {
+ // Objects not on dirty or aged cards should never have references to newly allocated regions.
+ if (collector->heap_->GetCardTable()->GetCard(obj) == gc::accounting::CardTable::kCardClean) {
VerifyNoMissingCardMarkVisitor visitor(collector, /*holder*/ obj);
obj->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
visitor,
@@ -583,53 +649,100 @@
}
}
+template <bool kConcurrent>
class ConcurrentCopying::GrayImmuneObjectVisitor {
public:
- explicit GrayImmuneObjectVisitor() {}
+ explicit GrayImmuneObjectVisitor(Thread* self) : self_(self) {}
ALWAYS_INLINE void operator()(mirror::Object* obj) const REQUIRES_SHARED(Locks::mutator_lock_) {
- if (kUseBakerReadBarrier) {
- if (kIsDebugBuild) {
- Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+ if (kUseBakerReadBarrier && obj->GetReadBarrierState() == ReadBarrier::WhiteState()) {
+ if (kConcurrent) {
+ Locks::mutator_lock_->AssertSharedHeld(self_);
+ obj->AtomicSetReadBarrierState(ReadBarrier::WhiteState(), ReadBarrier::GrayState());
+ // Mod union table VisitObjects may visit the same object multiple times so we can't check
+ // the result of the atomic set.
+ } else {
+ Locks::mutator_lock_->AssertExclusiveHeld(self_);
+ obj->SetReadBarrierState(ReadBarrier::GrayState());
}
- obj->SetReadBarrierState(ReadBarrier::GrayState());
}
}
static void Callback(mirror::Object* obj, void* arg) REQUIRES_SHARED(Locks::mutator_lock_) {
- reinterpret_cast<GrayImmuneObjectVisitor*>(arg)->operator()(obj);
+ reinterpret_cast<GrayImmuneObjectVisitor<kConcurrent>*>(arg)->operator()(obj);
}
+
+ private:
+ Thread* const self_;
};
void ConcurrentCopying::GrayAllDirtyImmuneObjects() {
- TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings());
- gc::Heap* const heap = Runtime::Current()->GetHeap();
- accounting::CardTable* const card_table = heap->GetCardTable();
- WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ TimingLogger::ScopedTiming split("GrayAllDirtyImmuneObjects", GetTimings());
+ accounting::CardTable* const card_table = heap_->GetCardTable();
+ Thread* const self = Thread::Current();
+ using VisitorType = GrayImmuneObjectVisitor</* kIsConcurrent */ true>;
+ VisitorType visitor(self);
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
for (space::ContinuousSpace* space : immune_spaces_.GetSpaces()) {
DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
- GrayImmuneObjectVisitor visitor;
- accounting::ModUnionTable* table = heap->FindModUnionTableFromSpace(space);
+ accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
// Mark all the objects on dirty cards since these may point to objects in other space.
// Once these are marked, the GC will eventually clear them later.
// Table is non null for boot image and zygote spaces. It is only null for application image
// spaces.
if (table != nullptr) {
- // TODO: Consider adding precleaning outside the pause.
table->ProcessCards();
- table->VisitObjects(GrayImmuneObjectVisitor::Callback, &visitor);
- // Since the cards are recorded in the mod-union table and this is paused, we can clear
- // the cards for the space (to madvise).
+ table->VisitObjects(&VisitorType::Callback, &visitor);
+ // Don't clear cards here since we need to rescan in the pause. If we cleared the cards here,
+ // there would be races with the mutator marking new cards.
+ } else {
+ // Keep cards aged if we don't have a mod-union table since we may need to scan them in future
+ // GCs. This case is for app images.
+ card_table->ModifyCardsAtomic(
+ space->Begin(),
+ space->End(),
+ [](uint8_t card) {
+ return (card != gc::accounting::CardTable::kCardClean)
+ ? gc::accounting::CardTable::kCardAged
+ : card;
+ },
+ /* card modified visitor */ VoidFunctor());
+ card_table->Scan</* kClearCard */ false>(space->GetMarkBitmap(),
+ space->Begin(),
+ space->End(),
+ visitor,
+ gc::accounting::CardTable::kCardAged);
+ }
+ }
+}
+
+void ConcurrentCopying::GrayAllNewlyDirtyImmuneObjects() {
+ TimingLogger::ScopedTiming split("(Paused)GrayAllNewlyDirtyImmuneObjects", GetTimings());
+ accounting::CardTable* const card_table = heap_->GetCardTable();
+ using VisitorType = GrayImmuneObjectVisitor</* kIsConcurrent */ false>;
+ Thread* const self = Thread::Current();
+ VisitorType visitor(self);
+ WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ for (space::ContinuousSpace* space : immune_spaces_.GetSpaces()) {
+ DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
+ accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
+
+ // Don't need to scan aged cards since we did these before the pause. Note that scanning cards
+ // also handles the mod-union table cards.
+ card_table->Scan</* kClearCard */ false>(space->GetMarkBitmap(),
+ space->Begin(),
+ space->End(),
+ visitor,
+ gc::accounting::CardTable::kCardDirty);
+ if (table != nullptr) {
+ // Add the cards to the mod-union table so that we can clear cards to save RAM.
+ table->ProcessCards();
TimingLogger::ScopedTiming split2("(Paused)ClearCards", GetTimings());
card_table->ClearCardRange(space->Begin(),
AlignDown(space->End(), accounting::CardTable::kCardSize));
- } else {
- // TODO: Consider having a mark bitmap for app image spaces and avoid scanning during the
- // pause because app image spaces are all dirty pages anyways.
- card_table->Scan<false>(space->GetMarkBitmap(), space->Begin(), space->End(), visitor);
}
}
- // Since all of the objects that may point to other spaces are marked, we can avoid all the read
+ // Since all of the objects that may point to other spaces are gray, we can avoid all the read
// barriers in the immune spaces.
updated_all_immune_objects_.StoreRelaxed(true);
}
@@ -658,6 +771,7 @@
ALWAYS_INLINE void operator()(mirror::Object* obj) const REQUIRES_SHARED(Locks::mutator_lock_) {
if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
+ // Only need to scan gray objects.
if (obj->GetReadBarrierState() == ReadBarrier::GrayState()) {
collector_->ScanImmuneObject(obj);
// Done scanning the object, go back to white.
@@ -707,6 +821,7 @@
if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects && table != nullptr) {
table->VisitObjects(ImmuneSpaceScanObjVisitor::Callback, &visitor);
} else {
+ // TODO: Scan only the aged cards.
live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
reinterpret_cast<uintptr_t>(space->Limit()),
visitor);
@@ -876,6 +991,12 @@
// to avoid a race with ThreadList::Register().
CHECK(concurrent_copying_->is_marking_);
concurrent_copying_->is_marking_ = false;
+ if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
+ CHECK(concurrent_copying_->is_using_read_barrier_entrypoints_);
+ concurrent_copying_->is_using_read_barrier_entrypoints_ = false;
+ } else {
+ CHECK(!concurrent_copying_->is_using_read_barrier_entrypoints_);
+ }
}
private:
@@ -1621,25 +1742,29 @@
Thread* const self = Thread::Current();
WriterMutexLock rmu(self, *Locks::heap_bitmap_lock_);
space::LargeObjectSpace* const los = heap_->GetLargeObjectsSpace();
- // Pick the current live bitmap (mark bitmap if swapped).
- accounting::LargeObjectBitmap* const live_bitmap = los->GetLiveBitmap();
- accounting::LargeObjectBitmap* const mark_bitmap = los->GetMarkBitmap();
- // Walk through all of the objects and explicitly mark the zygote ones so they don't get swept.
- std::pair<uint8_t*, uint8_t*> range = los->GetBeginEndAtomic();
- live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(range.first),
- reinterpret_cast<uintptr_t>(range.second),
- [mark_bitmap, los, self](mirror::Object* obj)
- REQUIRES(Locks::heap_bitmap_lock_)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (los->IsZygoteLargeObject(self, obj)) {
- mark_bitmap->Set(obj);
- }
- });
+ if (los != nullptr) {
+ // Pick the current live bitmap (mark bitmap if swapped).
+ accounting::LargeObjectBitmap* const live_bitmap = los->GetLiveBitmap();
+ accounting::LargeObjectBitmap* const mark_bitmap = los->GetMarkBitmap();
+ // Walk through all of the objects and explicitly mark the zygote ones so they don't get swept.
+ std::pair<uint8_t*, uint8_t*> range = los->GetBeginEndAtomic();
+ live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(range.first),
+ reinterpret_cast<uintptr_t>(range.second),
+ [mark_bitmap, los, self](mirror::Object* obj)
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (los->IsZygoteLargeObject(self, obj)) {
+ mark_bitmap->Set(obj);
+ }
+ });
+ }
}
void ConcurrentCopying::SweepLargeObjects(bool swap_bitmaps) {
TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings());
- RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+ if (heap_->GetLargeObjectsSpace() != nullptr) {
+ RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+ }
}
void ConcurrentCopying::ReclaimPhase() {
@@ -1888,7 +2013,6 @@
heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
accounting::LargeObjectBitmap* los_bitmap =
heap_mark_bitmap_->GetLargeObjectBitmap(ref);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
bool is_los = mark_bitmap == nullptr;
if ((!is_los && mark_bitmap->Test(ref)) ||
(is_los && los_bitmap->Test(ref))) {
@@ -2392,7 +2516,6 @@
heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
accounting::LargeObjectBitmap* los_bitmap =
heap_mark_bitmap_->GetLargeObjectBitmap(ref);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
bool is_los = mark_bitmap == nullptr;
if (!is_los && mark_bitmap->Test(ref)) {
// Already marked.
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 37b6a2c..c09e0eb 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -118,6 +118,11 @@
bool IsMarking() const {
return is_marking_;
}
+ // We may want to use read barrier entrypoints before is_marking_ is true since concurrent graying
+ // creates a small window where we might dispatch on these entrypoints.
+ bool IsUsingReadBarrierEntrypoints() const {
+ return is_using_read_barrier_entrypoints_;
+ }
bool IsActive() const {
return is_active_;
}
@@ -165,6 +170,9 @@
void GrayAllDirtyImmuneObjects()
REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
+ void GrayAllNewlyDirtyImmuneObjects()
+ REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_);
void VerifyGrayImmuneObjects()
REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
@@ -252,6 +260,8 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
void DumpPerformanceInfo(std::ostream& os) OVERRIDE REQUIRES(!rb_slow_path_histogram_lock_);
+ // Set the read barrier mark entrypoints to non-null.
+ void ActivateReadBarrierEntrypoints();
space::RegionSpace* region_space_; // The underlying region space.
std::unique_ptr<Barrier> gc_barrier_;
@@ -268,6 +278,8 @@
GUARDED_BY(mark_stack_lock_);
Thread* thread_running_gc_;
bool is_marking_; // True while marking is ongoing.
+ // True while we might dispatch on the read barrier entrypoints.
+ bool is_using_read_barrier_entrypoints_;
bool is_active_; // True while the collection is ongoing.
bool is_asserting_to_space_invariant_; // True while asserting the to-space invariant.
ImmuneSpaces immune_spaces_;
@@ -330,6 +342,8 @@
// ObjPtr since the GC may transition to suspended and runnable between phases.
mirror::Class* java_lang_Object_;
+ class ActivateReadBarrierEntrypointsCallback;
+ class ActivateReadBarrierEntrypointsCheckpoint;
class AssertToSpaceInvariantFieldVisitor;
class AssertToSpaceInvariantObjectVisitor;
class AssertToSpaceInvariantRefsVisitor;
@@ -339,7 +353,7 @@
class DisableMarkingCheckpoint;
class DisableWeakRefAccessCallback;
class FlipCallback;
- class GrayImmuneObjectVisitor;
+ template <bool kConcurrent> class GrayImmuneObjectVisitor;
class ImmuneSpaceScanObjVisitor;
class LostCopyVisitor;
class RefFieldsVisitor;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index bd4f99b..298336a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -150,8 +150,13 @@
static uint8_t* const kPreferredAllocSpaceBegin =
reinterpret_cast<uint8_t*>(300 * MB - Heap::kDefaultNonMovingSpaceCapacity);
#else
-// For 32-bit, use 0x20000000 because asan reserves 0x04000000 - 0x20000000.
+#ifdef __ANDROID__
+// For 32-bit Android, use 0x20000000 because asan reserves 0x04000000 - 0x20000000.
static uint8_t* const kPreferredAllocSpaceBegin = reinterpret_cast<uint8_t*>(0x20000000);
+#else
+// For 32-bit host, use 0x40000000 because asan uses most of the space below this.
+static uint8_t* const kPreferredAllocSpaceBegin = reinterpret_cast<uint8_t*>(0x40000000);
+#endif
#endif
static inline bool CareAboutPauseTimes() {
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index c8d256f..07aeb66 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -20,7 +20,7 @@
struct ProfileSaverOptions {
public:
- static constexpr uint32_t kMinSavePeriodMs = 20 * 1000; // 20 seconds
+ static constexpr uint32_t kMinSavePeriodMs = 40 * 1000; // 40 seconds
static constexpr uint32_t kSaveResolvedClassesDelayMs = 5 * 1000; // 5 seconds
// Minimum number of JIT samples during launch to include a method into the profile.
static constexpr uint32_t kStartupMethodSamples = 1;
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index c3a94b9..4c00317 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1078,9 +1078,156 @@
jint* extension_count_ptr,
jvmtiExtensionFunctionInfo** extensions) {
ENSURE_VALID_ENV(env);
- // We do not have any extension functions.
- *extension_count_ptr = 0;
- *extensions = nullptr;
+ ENSURE_NON_NULL(extension_count_ptr);
+ ENSURE_NON_NULL(extensions);
+
+ std::vector<jvmtiExtensionFunctionInfo> ext_vector;
+
+ // Holders for allocated values.
+ std::vector<JvmtiUniquePtr<char[]>> char_buffers;
+ std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers;
+ std::vector<JvmtiUniquePtr<jvmtiError[]>> error_buffers;
+
+ // Add a helper struct that takes an arbitrary const char*. add_extension will use Allocate
+ // appropriately.
+ struct CParamInfo {
+ const char* name;
+ jvmtiParamKind kind;
+ jvmtiParamTypes base_type;
+ jboolean null_ok;
+ };
+
+ auto add_extension = [&](jvmtiExtensionFunction func,
+ const char* id,
+ const char* short_description,
+ jint param_count,
+ const std::vector<CParamInfo>& params,
+ jint error_count,
+ const std::vector<jvmtiError>& errors) {
+ jvmtiExtensionFunctionInfo func_info;
+ jvmtiError error;
+
+ func_info.func = func;
+
+ JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error);
+ if (id_ptr == nullptr) {
+ return error;
+ }
+ func_info.id = id_ptr.get();
+ char_buffers.push_back(std::move(id_ptr));
+
+ JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error);
+ if (descr == nullptr) {
+ return error;
+ }
+ func_info.short_description = descr.get();
+ char_buffers.push_back(std::move(descr));
+
+ func_info.param_count = param_count;
+ if (param_count > 0) {
+ JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr =
+ AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, param_count, &error);
+ if (params_ptr == nullptr) {
+ return error;
+ }
+ func_info.params = params_ptr.get();
+ param_buffers.push_back(std::move(params_ptr));
+
+ for (jint i = 0; i != param_count; ++i) {
+ JvmtiUniquePtr<char[]> param_name = CopyString(env, params[i].name, &error);
+ if (param_name == nullptr) {
+ return error;
+ }
+ func_info.params[i].name = param_name.get();
+ char_buffers.push_back(std::move(param_name));
+
+ func_info.params[i].kind = params[i].kind;
+ func_info.params[i].base_type = params[i].base_type;
+ func_info.params[i].null_ok = params[i].null_ok;
+ }
+ } else {
+ func_info.params = nullptr;
+ }
+
+ func_info.error_count = error_count;
+ if (error_count > 0) {
+ JvmtiUniquePtr<jvmtiError[]> errors_ptr =
+ AllocJvmtiUniquePtr<jvmtiError[]>(env, error_count, &error);
+ if (errors_ptr == nullptr) {
+ return error;
+ }
+ func_info.errors = errors_ptr.get();
+ error_buffers.push_back(std::move(errors_ptr));
+
+ for (jint i = 0; i != error_count; ++i) {
+ func_info.errors[i] = errors[i];
+ }
+ } else {
+ func_info.errors = nullptr;
+ }
+
+ ext_vector.push_back(func_info);
+
+ return ERR(NONE);
+ };
+
+ jvmtiError error;
+
+ // Heap extensions.
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetObjectHeapId),
+ "com.android.art.heap.get_object_heap_id",
+ "Retrieve the heap id of the the object tagged with the given argument. An "
+ "arbitrary object is chosen if multiple objects exist with the same tag.",
+ 2,
+ { // NOLINT [whitespace/braces] [4]
+ { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false},
+ { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false}
+ },
+ 1,
+ { JVMTI_ERROR_NOT_FOUND });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetHeapName),
+ "com.android.art.heap.get_heap_name",
+ "Retrieve the name of the heap with the given id.",
+ 2,
+ { // NOLINT [whitespace/braces] [4]
+ { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false},
+ { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false}
+ },
+ 1,
+ { JVMTI_ERROR_ILLEGAL_ARGUMENT });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
+ // Copy into output buffer.
+
+ *extension_count_ptr = ext_vector.size();
+ JvmtiUniquePtr<jvmtiExtensionFunctionInfo[]> out_data =
+ AllocJvmtiUniquePtr<jvmtiExtensionFunctionInfo[]>(env, ext_vector.size(), &error);
+ if (out_data == nullptr) {
+ return error;
+ }
+ memcpy(out_data.get(),
+ ext_vector.data(),
+ ext_vector.size() * sizeof(jvmtiExtensionFunctionInfo));
+ *extensions = out_data.release();
+
+ // Release all the buffer holders, we're OK now.
+ for (auto& holder : char_buffers) {
+ holder.release();
+ }
+ for (auto& holder : param_buffers) {
+ holder.release();
+ }
+ for (auto& holder : error_buffers) {
+ holder.release();
+ }
return ERR(NONE);
}
diff --git a/runtime/openjdkjvmti/jvmti_weak_table-inl.h b/runtime/openjdkjvmti/jvmti_weak_table-inl.h
index f67fffc..64ab3e7 100644
--- a/runtime/openjdkjvmti/jvmti_weak_table-inl.h
+++ b/runtime/openjdkjvmti/jvmti_weak_table-inl.h
@@ -384,6 +384,23 @@
return ERR(NONE);
}
+template <typename T>
+art::mirror::Object* JvmtiWeakTable<T>::Find(T tag) {
+ art::Thread* self = art::Thread::Current();
+ art::MutexLock mu(self, allow_disallow_lock_);
+ Wait(self);
+
+ for (auto& pair : tagged_objects_) {
+ if (tag == pair.second) {
+ art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>();
+ if (obj != nullptr) {
+ return obj;
+ }
+ }
+ }
+ return nullptr;
+}
+
} // namespace openjdkjvmti
#endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_
diff --git a/runtime/openjdkjvmti/jvmti_weak_table.h b/runtime/openjdkjvmti/jvmti_weak_table.h
index eeea75a..a6fd247 100644
--- a/runtime/openjdkjvmti/jvmti_weak_table.h
+++ b/runtime/openjdkjvmti/jvmti_weak_table.h
@@ -116,6 +116,10 @@
void Unlock() RELEASE(allow_disallow_lock_);
void AssertLocked() ASSERT_CAPABILITY(allow_disallow_lock_);
+ art::mirror::Object* Find(T tag)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ REQUIRES(!allow_disallow_lock_);
+
protected:
// Should HandleNullSweep be called when Sweep detects the release of an object?
virtual bool DoesHandleNullOnSweep() {
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 7fc5104..9b4dcaa 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -1400,4 +1400,95 @@
return ERR(NONE);
}
+
+static constexpr jint kHeapIdDefault = 0;
+static constexpr jint kHeapIdImage = 1;
+static constexpr jint kHeapIdZygote = 2;
+static constexpr jint kHeapIdApp = 3;
+
+jvmtiError HeapExtensions::GetObjectHeapId(jvmtiEnv* env, jlong tag, jint* heap_id, ...) {
+ if (heap_id == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::Thread* self = art::Thread::Current();
+
+ auto work = [&]() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ ObjectTagTable* tag_table = ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get();
+ art::ObjPtr<art::mirror::Object> obj = tag_table->Find(tag);
+ if (obj == nullptr) {
+ return ERR(NOT_FOUND);
+ }
+
+ art::gc::Heap* const heap = art::Runtime::Current()->GetHeap();
+ const art::gc::space::ContinuousSpace* const space =
+ heap->FindContinuousSpaceFromObject(obj, true);
+ jint heap_type = kHeapIdApp;
+ if (space != nullptr) {
+ if (space->IsZygoteSpace()) {
+ heap_type = kHeapIdZygote;
+ } else if (space->IsImageSpace() && heap->ObjectIsInBootImageSpace(obj)) {
+ // Only count objects in the boot image as HPROF_HEAP_IMAGE, this leaves app image objects
+ // as HPROF_HEAP_APP. b/35762934
+ heap_type = kHeapIdImage;
+ }
+ } else {
+ const auto* los = heap->GetLargeObjectsSpace();
+ if (los->Contains(obj.Ptr()) && los->IsZygoteLargeObject(self, obj.Ptr())) {
+ heap_type = kHeapIdZygote;
+ }
+ }
+ *heap_id = heap_type;
+ return ERR(NONE);
+ };
+
+ if (!art::Locks::mutator_lock_->IsSharedHeld(self)) {
+ if (!self->IsThreadSuspensionAllowable()) {
+ return ERR(INTERNAL);
+ }
+ art::ScopedObjectAccess soa(self);
+ return work();
+ } else {
+ // We cannot use SOA in this case. We might be holding the lock, but may not be in the
+ // runnable state (e.g., during GC).
+ art::Locks::mutator_lock_->AssertSharedHeld(self);
+ // TODO: Investigate why ASSERT_SHARED_CAPABILITY doesn't work.
+ auto annotalysis_workaround = [&]() NO_THREAD_SAFETY_ANALYSIS {
+ return work();
+ };
+ return annotalysis_workaround();
+ }
+}
+
+static jvmtiError CopyStringAndReturn(jvmtiEnv* env, const char* in, char** out) {
+ jvmtiError error;
+ JvmtiUniquePtr<char[]> param_name = CopyString(env, in, &error);
+ if (param_name == nullptr) {
+ return error;
+ }
+ *out = param_name.release();
+ return ERR(NONE);
+}
+
+static constexpr const char* kHeapIdDefaultName = "default";
+static constexpr const char* kHeapIdImageName = "image";
+static constexpr const char* kHeapIdZygoteName = "zygote";
+static constexpr const char* kHeapIdAppName = "app";
+
+jvmtiError HeapExtensions::GetHeapName(jvmtiEnv* env, jint heap_id, char** heap_name, ...) {
+ switch (heap_id) {
+ case kHeapIdDefault:
+ return CopyStringAndReturn(env, kHeapIdDefaultName, heap_name);
+ case kHeapIdImage:
+ return CopyStringAndReturn(env, kHeapIdImageName, heap_name);
+ case kHeapIdZygote:
+ return CopyStringAndReturn(env, kHeapIdZygoteName, heap_name);
+ case kHeapIdApp:
+ return CopyStringAndReturn(env, kHeapIdAppName, heap_name);
+
+ default:
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_heap.h b/runtime/openjdkjvmti/ti_heap.h
index dccecb4..b4b71ba 100644
--- a/runtime/openjdkjvmti/ti_heap.h
+++ b/runtime/openjdkjvmti/ti_heap.h
@@ -56,6 +56,12 @@
ObjectTagTable* tags_;
};
+class HeapExtensions {
+ public:
+ static jvmtiError JNICALL GetObjectHeapId(jvmtiEnv* env, jlong tag, jint* heap_id, ...);
+ static jvmtiError JNICALL GetHeapName(jvmtiEnv* env, jint heap_id, char** heap_name, ...);
+};
+
} // namespace openjdkjvmti
#endif // ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index eb068b3..b1acec6 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -336,6 +336,16 @@
jit_->DeleteThreadPool();
}
+ // Make sure our internal threads are dead before we start tearing down things they're using.
+ Dbg::StopJdwp();
+ delete signal_catcher_;
+
+ // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
+ {
+ ScopedTrace trace2("Delete thread list");
+ thread_list_->ShutDown();
+ }
+
// TODO Maybe do some locking.
for (auto& agent : agents_) {
agent.Unload();
@@ -346,15 +356,9 @@
plugin.Unload();
}
- // Make sure our internal threads are dead before we start tearing down things they're using.
- Dbg::StopJdwp();
- delete signal_catcher_;
+ // Finally delete the thread list.
+ delete thread_list_;
- // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
- {
- ScopedTrace trace2("Delete thread list");
- delete thread_list_;
- }
// Delete the JIT after thread list to ensure that there is no remaining threads which could be
// accessing the instrumentation when we delete it.
if (jit_ != nullptr) {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 62a616b..653a9bd 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -129,12 +129,12 @@
}
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints);
-void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_marking);
+void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active);
void Thread::SetIsGcMarkingAndUpdateEntrypoints(bool is_marking) {
CHECK(kUseReadBarrier);
tls32_.is_gc_marking = is_marking;
- UpdateReadBarrierEntrypoints(&tlsPtr_.quick_entrypoints, is_marking);
+ UpdateReadBarrierEntrypoints(&tlsPtr_.quick_entrypoints, /* is_active */ is_marking);
ResetQuickAllocEntryPointsForThread(is_marking);
}
@@ -3604,4 +3604,9 @@
return peer;
}
+void Thread::SetReadBarrierEntrypoints() {
+ // Make sure entrypoints aren't null.
+ UpdateReadBarrierEntrypoints(&tlsPtr_.quick_entrypoints, /* is_active*/ true);
+}
+
} // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 5251012..6abde5b 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1180,6 +1180,9 @@
return false;
}
+ // Set to the read barrier marking entrypoints to be non-null.
+ void SetReadBarrierEntrypoints();
+
static jobject CreateCompileTimePeer(JNIEnv* env,
const char* name,
bool as_daemon,
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 2e0d866..b63eaa4 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -73,12 +73,17 @@
unregistering_count_(0),
suspend_all_historam_("suspend all histogram", 16, 64),
long_suspend_(false),
+ shut_down_(false),
thread_suspend_timeout_ns_(thread_suspend_timeout_ns),
empty_checkpoint_barrier_(new Barrier(0)) {
CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1, 0U)));
}
ThreadList::~ThreadList() {
+ CHECK(shut_down_);
+}
+
+void ThreadList::ShutDown() {
ScopedTrace trace(__PRETTY_FUNCTION__);
// Detach the current thread if necessary. If we failed to start, there might not be any threads.
// We need to detach the current thread here in case there's another thread waiting to join with
@@ -102,6 +107,8 @@
// TODO: there's an unaddressed race here where a thread may attach during shutdown, see
// Thread::Init.
SuspendAllDaemonThreadsForShutdown();
+
+ shut_down_ = true;
}
bool ThreadList::Contains(Thread* thread) {
@@ -1362,6 +1369,7 @@
void ThreadList::Register(Thread* self) {
DCHECK_EQ(self, Thread::Current());
+ CHECK(!shut_down_);
if (VLOG_IS_ON(threads)) {
std::ostringstream oss;
@@ -1387,13 +1395,14 @@
CHECK(!Contains(self));
list_.push_back(self);
if (kUseReadBarrier) {
+ gc::collector::ConcurrentCopying* const cc =
+ Runtime::Current()->GetHeap()->ConcurrentCopyingCollector();
// Initialize according to the state of the CC collector.
- bool is_gc_marking =
- Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->IsMarking();
- self->SetIsGcMarkingAndUpdateEntrypoints(is_gc_marking);
- bool weak_ref_access_enabled =
- Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->IsWeakRefAccessEnabled();
- self->SetWeakRefAccessEnabled(weak_ref_access_enabled);
+ self->SetIsGcMarkingAndUpdateEntrypoints(cc->IsMarking());
+ if (cc->IsUsingReadBarrierEntrypoints()) {
+ self->SetReadBarrierEntrypoints();
+ }
+ self->SetWeakRefAccessEnabled(cc->IsWeakRefAccessEnabled());
}
}
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 70917eb..14bef5e 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -50,6 +50,8 @@
explicit ThreadList(uint64_t thread_suspend_timeout_ns);
~ThreadList();
+ void ShutDown();
+
void DumpForSigQuit(std::ostream& os)
REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_);
// For thread suspend timeout dumps.
@@ -219,6 +221,10 @@
// Whether or not the current thread suspension is long.
bool long_suspend_;
+ // Whether the shutdown function has been called. This is checked in the destructor. It is an
+ // error to destroy a ThreadList instance without first calling ShutDown().
+ bool shut_down_;
+
// Thread suspension timeout in nanoseconds.
const uint64_t thread_suspend_timeout_ns_;
diff --git a/test/121-modifiers/info.txt b/test/121-modifiers/info.txt
index 129aee8..335df53 100644
--- a/test/121-modifiers/info.txt
+++ b/test/121-modifiers/info.txt
@@ -14,5 +14,5 @@
mv NonInf.out classes/NonInf.class
mv Main.class A.class A\$B.class A\$C.class classes/
dx --debug --dex --output=classes.dex classes
-baksmali classes.dex
+baksmali disassemble classes.dex
mv out/*.smali smali/
diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java
index 330aa74..a538f52 100644
--- a/test/476-checker-ctor-memory-barrier/src/Main.java
+++ b/test/476-checker-ctor-memory-barrier/src/Main.java
@@ -17,8 +17,8 @@
// TODO: Add more tests after we can inline functions with calls.
class ClassWithoutFinals {
- /// CHECK-START: void ClassWithoutFinals.<init>() register (after)
- /// CHECK-NOT: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void ClassWithoutFinals.<init>() inliner (after)
+ /// CHECK-NOT: ConstructorFence
public ClassWithoutFinals() {}
}
@@ -33,17 +33,40 @@
// should not inline this constructor
}
- /// CHECK-START: void ClassWithFinals.<init>() register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void ClassWithFinals.<init>() inliner (after)
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
+
+ /*
+ * Check that the correct assembly instructions are selected for a Store/Store fence.
+ *
+ * - ARM variants: DMB ISHST (store-store fence for inner shareable domain)
+ * - Intel variants: no-op (store-store does not need a fence).
+ */
+
+ /// CHECK-START-ARM64: void ClassWithFinals.<init>() disassembly (after)
+ /// CHECK: ConstructorFence
+ /// CHECK-NEXT: dmb ishst
+
+ /// CHECK-START-ARM: void ClassWithFinals.<init>() disassembly (after)
+ /// CHECK: ConstructorFence
+ /// CHECK-NEXT: dmb ishst
+
+ /// CHECK-START-X86_64: void ClassWithFinals.<init>() disassembly (after)
+ /// CHECK: ConstructorFence
+ /// CHECK-NOT: {{[slm]}}fence
+
+ /// CHECK-START-X86: void ClassWithFinals.<init>() disassembly (after)
+ /// CHECK: ConstructorFence
+ /// CHECK-NOT: {{[slm]}}fence
public ClassWithFinals() {
// Exactly one constructor barrier.
x = 0;
}
- /// CHECK-START: void ClassWithFinals.<init>(int) register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void ClassWithFinals.<init>(int) inliner (after)
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
public ClassWithFinals(int x) {
// This should have exactly two barriers:
@@ -55,11 +78,11 @@
}
class InheritFromClassWithFinals extends ClassWithFinals {
- /// CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void InheritFromClassWithFinals.<init>() inliner (after)
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
- /// CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
+ /// CHECK-START: void InheritFromClassWithFinals.<init>() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
public InheritFromClassWithFinals() {
// Should inline the super constructor.
@@ -67,23 +90,23 @@
// Exactly one constructor barrier here.
}
- /// CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
+ /// CHECK-START: void InheritFromClassWithFinals.<init>(boolean) inliner (after)
/// CHECK: InvokeStaticOrDirect
- /// CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
- /// CHECK-NOT: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void InheritFromClassWithFinals.<init>(boolean) inliner (after)
+ /// CHECK-NOT: ConstructorFence
public InheritFromClassWithFinals(boolean cond) {
super(cond);
// should not inline the super constructor
}
- /// CHECK-START: void InheritFromClassWithFinals.<init>(int) register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK-NOT: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void InheritFromClassWithFinals.<init>(int) inliner (after)
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
+ /// CHECK-NOT: ConstructorFence
/// CHECK: ReturnVoid
- /// CHECK-START: void InheritFromClassWithFinals.<init>(int) register (after)
+ /// CHECK-START: void InheritFromClassWithFinals.<init>(int) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
public InheritFromClassWithFinals(int unused) {
// Should inline the super constructor and insert a memory barrier.
@@ -96,21 +119,21 @@
class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals {
final int y;
- /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() inliner (after)
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
- /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
+ /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
public HaveFinalsAndInheritFromClassWithFinals() {
// Should inline the super constructor and keep the memory barrier.
y = 0;
}
- /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(boolean) register (after)
+ /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(boolean) inliner (after)
/// CHECK: InvokeStaticOrDirect
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
public HaveFinalsAndInheritFromClassWithFinals(boolean cond) {
super(cond);
@@ -118,15 +141,15 @@
y = 0;
}
- /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(int) register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(int) inliner (after)
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
- /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(int) register (after)
+ /// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(int) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
public HaveFinalsAndInheritFromClassWithFinals(int unused) {
// Should inline the super constructor and keep keep both memory barriers.
@@ -141,55 +164,55 @@
public class Main {
- /// CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
+ /// CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() inliner (after)
/// CHECK: InvokeStaticOrDirect
- /// CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
- /// CHECK-NOT: MemoryBarrier kind:StoreStore
+ /// CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() inliner (after)
+ /// CHECK-NOT: ConstructorFence
public static ClassWithFinals noInlineNoConstructorBarrier() {
return new ClassWithFinals(false);
// should not inline the constructor
}
- /// CHECK-START: void Main.inlineNew() register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void Main.inlineNew() inliner (after)
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
- /// CHECK-START: void Main.inlineNew() register (after)
+ /// CHECK-START: void Main.inlineNew() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
public static void inlineNew() {
new ClassWithFinals();
}
- /// CHECK-START: void Main.inlineNew1() register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void Main.inlineNew1() inliner (after)
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
- /// CHECK-START: void Main.inlineNew1() register (after)
+ /// CHECK-START: void Main.inlineNew1() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
public static void inlineNew1() {
new InheritFromClassWithFinals();
}
- /// CHECK-START: void Main.inlineNew2() register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void Main.inlineNew2() inliner (after)
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
- /// CHECK-START: void Main.inlineNew2() register (after)
+ /// CHECK-START: void Main.inlineNew2() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
public static void inlineNew2() {
new HaveFinalsAndInheritFromClassWithFinals();
}
- /// CHECK-START: void Main.inlineNew3() register (after)
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
- /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-START: void Main.inlineNew3() inliner (after)
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
+ /// CHECK: ConstructorFence
/// CHECK-NEXT: ReturnVoid
- /// CHECK-START: void Main.inlineNew3() register (after)
+ /// CHECK-START: void Main.inlineNew3() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
public static void inlineNew3() {
new HaveFinalsAndInheritFromClassWithFinals();
diff --git a/test/530-checker-lse-ctor-fences/expected.txt b/test/530-checker-lse-ctor-fences/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/530-checker-lse-ctor-fences/expected.txt
diff --git a/test/530-checker-lse-ctor-fences/info.txt b/test/530-checker-lse-ctor-fences/info.txt
new file mode 100644
index 0000000..ccc7b47
--- /dev/null
+++ b/test/530-checker-lse-ctor-fences/info.txt
@@ -0,0 +1 @@
+Checker test for testing load-store elimination with final fields (constructor fences).
diff --git a/test/530-checker-lse-ctor-fences/src/Main.java b/test/530-checker-lse-ctor-fences/src/Main.java
new file mode 100644
index 0000000..7755875
--- /dev/null
+++ b/test/530-checker-lse-ctor-fences/src/Main.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This base class has a single final field;
+// the constructor should have one fence.
+class Circle {
+ Circle(double radius) {
+ this.radius = radius;
+ }
+ public double getRadius() {
+ return radius;
+ }
+ public double getArea() {
+ return radius * radius * Math.PI;
+ }
+
+ public double getCircumference() {
+ return 2 * Math.PI * radius;
+ }
+
+ private final double radius;
+}
+
+// This subclass adds an extra final field;
+// there should be an extra constructor fence added
+// (for a total of 2 after inlining).
+class Ellipse extends Circle {
+ Ellipse(double vertex, double covertex) {
+ super(vertex);
+
+ this.covertex = covertex;
+ }
+
+ public double getVertex() {
+ return getRadius();
+ }
+
+ public double getCovertex() {
+ return covertex;
+ }
+
+ @Override
+ public double getArea() {
+ return getRadius() * covertex * Math.PI;
+ }
+
+ private final double covertex;
+}
+
+class CalcCircleAreaOrCircumference {
+ public static final int TYPE_AREA = 0;
+ public static final int TYPE_CIRCUMFERENCE = 1;
+
+ double value;
+
+ public CalcCircleAreaOrCircumference(int type) {
+ this.type = type;
+ }
+
+ final int type;
+}
+
+public class Main {
+
+ /// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (before)
+ /// CHECK: NewInstance
+ /// CHECK: InstanceFieldSet
+ /// CHECK: ConstructorFence
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (after)
+ /// CHECK-NOT: NewInstance
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: ConstructorFence
+ /// CHECK-NOT: InstanceFieldGet
+
+ // Make sure the constructor fence gets eliminated when the allocation is eliminated.
+ static double calcCircleArea(double radius) {
+ return new Circle(radius).getArea();
+ }
+
+ /// CHECK-START: double Main.calcEllipseArea(double, double) load_store_elimination (before)
+ /// CHECK: NewInstance
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: ConstructorFence
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: double Main.calcEllipseArea(double, double) load_store_elimination (after)
+ /// CHECK-NOT: NewInstance
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: ConstructorFence
+ /// CHECK-NOT: InstanceFieldGet
+
+ // Multiple constructor fences can accumulate through inheritance, make sure
+ // they are all eliminated when the allocation is eliminated.
+ static double calcEllipseArea(double vertex, double covertex) {
+ return new Ellipse(vertex, covertex).getArea();
+ }
+
+ /// CHECK-START: double Main.calcCircleAreaOrCircumference(double, boolean) load_store_elimination (before)
+ /// CHECK: NewInstance
+ /// CHECK: InstanceFieldSet
+ /// CHECK: ConstructorFence
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: double Main.calcCircleAreaOrCircumference(double, boolean) load_store_elimination (after)
+ /// CHECK: NewInstance
+ /// CHECK-NOT: ConstructorFence
+
+ //
+ // The object allocation will not be eliminated by LSE because of aliased stores.
+ // However the object is still a singleton, so it never escapes the current thread.
+ // There should not be a constructor fence here after LSE.
+ static double calcCircleAreaOrCircumference(double radius, boolean area_or_circumference) {
+ CalcCircleAreaOrCircumference calc =
+ new CalcCircleAreaOrCircumference(
+ area_or_circumference ? CalcCircleAreaOrCircumference.TYPE_AREA :
+ CalcCircleAreaOrCircumference.TYPE_CIRCUMFERENCE);
+
+ if (area_or_circumference) {
+ // Area
+ calc.value = Math.PI * Math.PI * radius;
+ } else {
+ // Circumference
+ calc.value = 2 * Math.PI * radius;
+ }
+
+ return calc.value;
+ }
+
+ /// CHECK-START: Circle Main.makeCircle(double) load_store_elimination (after)
+ /// CHECK: NewInstance
+ /// CHECK: ConstructorFence
+
+ // The object allocation is considered a singleton by LSE,
+ // but we cannot eliminate the new because it is returned.
+ //
+ // The constructor fence must also not be removed because the object could escape the
+ // current thread (in the caller).
+ static Circle makeCircle(double radius) {
+ return new Circle(radius);
+ }
+
+ static void assertIntEquals(int result, int expected) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ static void assertFloatEquals(float result, float expected) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ static void assertDoubleEquals(double result, double expected) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ static void assertInstanceOf(Object result, Class<?> expected) {
+ if (result.getClass() != expected) {
+ throw new Error("Expected type: " + expected + ", found : " + result.getClass());
+ }
+ }
+
+ public static void main(String[] args) {
+ assertDoubleEquals(Math.PI * Math.PI * Math.PI, calcCircleArea(Math.PI));
+ assertDoubleEquals(Math.PI * Math.PI * Math.PI, calcEllipseArea(Math.PI, Math.PI));
+ assertDoubleEquals(2 * Math.PI * Math.PI, calcCircleAreaOrCircumference(Math.PI, false));
+ assertInstanceOf(makeCircle(Math.PI), Circle.class);
+ }
+
+ static boolean sFlag;
+}
diff --git a/test/530-checker-lse2/src/Main.java b/test/530-checker-lse2/src/Main.java
index 0fe3d87..491a9a1 100644
--- a/test/530-checker-lse2/src/Main.java
+++ b/test/530-checker-lse2/src/Main.java
@@ -76,16 +76,27 @@
/// CHECK-DAG: Deoptimize
/// CHECK-DAG: Deoptimize
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
+ /// CHECK-DAG: ConstructorFence
/// CHECK-DAG: NewInstance
/// CHECK-DAG: NewInstance
/// CHECK-DAG: NewInstance
@@ -95,9 +106,14 @@
/// CHECK-DAG: Deoptimize
/// CHECK-DAG: Deoptimize
/// CHECK-NOT: NewInstance
+ /// CHECK-NOT: ConstructorFence
private float testMethod() {
{
+ // Each of the "new" statements here will initialize an object with final fields,
+ // which after inlining will also retain a constructor fence.
+ //
+ // After LSE we remove the 'new-instance' and the associated constructor fence.
int lI0 = (-1456058746 << mI);
mD = ((double)(int)(double) mD);
for (int i0 = 56 - 1; i0 >= 0; i0--) {
diff --git a/test/551-checker-shifter-operand/build b/test/551-checker-shifter-operand/build
index a78021f..027a0ea 100644
--- a/test/551-checker-shifter-operand/build
+++ b/test/551-checker-shifter-operand/build
@@ -168,7 +168,7 @@
if [ "${HAS_SMALI}" = "true" ]; then
# Compile Smali classes
- ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
+ ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
# Don't bother with dexmerger if we provide our own main function in a smali file.
if [ ${SKIP_DX_MERGER} = "false" ]; then
diff --git a/test/569-checker-pattern-replacement/src/Main.java b/test/569-checker-pattern-replacement/src/Main.java
index 345e9fd..26d87b1 100644
--- a/test/569-checker-pattern-replacement/src/Main.java
+++ b/test/569-checker-pattern-replacement/src/Main.java
@@ -331,7 +331,7 @@
/// CHECK-START: double Main.constructBase() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructBase() {
@@ -347,7 +347,7 @@
/// CHECK-START: double Main.constructBase(int) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructBase(int) inliner (after)
/// CHECK-DAG: <<Value:i\d+>> ParameterValue
@@ -371,7 +371,7 @@
/// CHECK-START: double Main.constructBaseWith0() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructBaseWith0() {
@@ -387,7 +387,7 @@
/// CHECK-START: java.lang.String Main.constructBase(java.lang.String) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: java.lang.String Main.constructBase(java.lang.String) inliner (after)
/// CHECK-DAG: <<Value:l\d+>> ParameterValue
@@ -411,7 +411,7 @@
/// CHECK-START: java.lang.String Main.constructBaseWithNullString() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: java.lang.String Main.constructBaseWithNullString() inliner (after)
/// CHECK-NOT: InstanceFieldSet
@@ -431,7 +431,7 @@
/// CHECK-START: double Main.constructBase(double, java.lang.Object) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructBase(double, java.lang.Object) inliner (after)
/// CHECK-DAG: <<DValue:d\d+>> ParameterValue
@@ -460,7 +460,7 @@
/// CHECK-START: double Main.constructBase(int, double, java.lang.Object) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructBase(int, double, java.lang.Object) inliner (after)
/// CHECK-DAG: <<IValue:i\d+>> ParameterValue
@@ -493,7 +493,7 @@
/// CHECK-START: double Main.constructBaseWith0DoubleNull(double) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructBaseWith0DoubleNull(double) inliner (after)
/// CHECK-DAG: <<DValue:d\d+>> ParameterValue
@@ -543,7 +543,7 @@
/// CHECK-START: double Main.constructBase(double) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructBase(double) inliner (after)
/// CHECK-DAG: <<Value:d\d+>> ParameterValue
@@ -567,7 +567,7 @@
/// CHECK-START: double Main.constructBaseWith0d() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructBaseWith0d() {
@@ -605,7 +605,7 @@
/// CHECK-START: double Main.constructBase(int, long) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructBase(int, long) inliner (after)
/// CHECK-DAG: <<IValue:i\d+>> ParameterValue
@@ -628,7 +628,7 @@
/// CHECK-START: double Main.constructDerived() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructDerived() {
@@ -644,7 +644,7 @@
/// CHECK-START: double Main.constructDerived(int) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructDerived(int) inliner (after)
/// CHECK-DAG: <<Value:i\d+>> ParameterValue
@@ -668,7 +668,7 @@
/// CHECK-START: double Main.constructDerivedWith0() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructDerivedWith0() {
@@ -684,7 +684,7 @@
/// CHECK-START: java.lang.String Main.constructDerived(java.lang.String) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: java.lang.String Main.constructDerived(java.lang.String) inliner (after)
/// CHECK-NOT: InstanceFieldSet
@@ -702,7 +702,7 @@
/// CHECK-START: double Main.constructDerived(double) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructDerived(double) inliner (after)
/// CHECK-DAG: <<Value:d\d+>> ParameterValue
@@ -726,7 +726,7 @@
/// CHECK-START: double Main.constructDerivedWith0d() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructDerivedWith0d() {
@@ -744,7 +744,7 @@
/// CHECK-START: double Main.constructDerived(int, double, java.lang.Object) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructDerived(int, double, java.lang.Object) inliner (after)
/// CHECK-DAG: <<DValue:d\d+>> ParameterValue
@@ -794,7 +794,7 @@
/// CHECK-START: double Main.constructDerived(float) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructDerived(float) inliner (after)
/// CHECK-DAG: <<Value:f\d+>> ParameterValue
@@ -821,7 +821,7 @@
/// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, float) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, float) inliner (after)
/// CHECK-DAG: <<IValue:i\d+>> ParameterValue
@@ -852,7 +852,7 @@
/// CHECK-START: int Main.constructBaseWithFinalField() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static int constructBaseWithFinalField() {
@@ -873,7 +873,7 @@
/// CHECK-DAG: <<Value:i\d+>> ParameterValue
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
- /// CHECK-DAG: MemoryBarrier
+ /// CHECK-DAG: ConstructorFence
/// CHECK-START: int Main.constructBaseWithFinalField(int) inliner (after)
/// CHECK-DAG: InstanceFieldSet
@@ -892,7 +892,7 @@
/// CHECK-START: int Main.constructBaseWithFinalFieldWith0() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static int constructBaseWithFinalFieldWith0() {
@@ -907,7 +907,7 @@
/// CHECK-START: double Main.constructDerivedWithFinalField() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructDerivedWithFinalField() {
@@ -928,7 +928,7 @@
/// CHECK-DAG: <<Value:i\d+>> ParameterValue
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
- /// CHECK-DAG: MemoryBarrier
+ /// CHECK-DAG: ConstructorFence
/// CHECK-START: double Main.constructDerivedWithFinalField(int) inliner (after)
/// CHECK-DAG: InstanceFieldSet
@@ -947,7 +947,7 @@
/// CHECK-START: double Main.constructDerivedWithFinalFieldWith0() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructDerivedWithFinalFieldWith0() {
@@ -968,7 +968,7 @@
/// CHECK-DAG: <<Value:d\d+>> ParameterValue
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
- /// CHECK-DAG: MemoryBarrier
+ /// CHECK-DAG: ConstructorFence
/// CHECK-START: double Main.constructDerivedWithFinalField(double) inliner (after)
/// CHECK-DAG: InstanceFieldSet
@@ -987,7 +987,7 @@
/// CHECK-START: double Main.constructDerivedWithFinalFieldWith0d() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructDerivedWithFinalFieldWith0d() {
@@ -1009,7 +1009,7 @@
/// CHECK-DAG: <<Value:d\d+>> ParameterValue
/// CHECK-DAG: <<Obj:l\d+>> NewInstance
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
- /// CHECK-DAG: MemoryBarrier
+ /// CHECK-DAG: ConstructorFence
/// CHECK-START: double Main.constructDerivedWithFinalField(int, double) inliner (after)
/// CHECK-DAG: InstanceFieldSet
@@ -1017,8 +1017,8 @@
/// CHECK-NOT: InstanceFieldSet
/// CHECK-START: double Main.constructDerivedWithFinalField(int, double) inliner (after)
- /// CHECK-DAG: MemoryBarrier
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-DAG: ConstructorFence
+ /// CHECK-NOT: ConstructorFence
public static double constructDerivedWithFinalField(int intValue, double doubleValue) {
DerivedWithFinalField d = new DerivedWithFinalField(intValue, doubleValue);
@@ -1034,7 +1034,7 @@
/// CHECK-START: double Main.constructDerivedWithFinalFieldWith0And0d() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static double constructDerivedWithFinalFieldWith0And0d() {
@@ -1049,7 +1049,7 @@
/// CHECK-START: int Main.constructDerivedInSecondDex() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static int constructDerivedInSecondDex() {
@@ -1070,7 +1070,7 @@
/// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedInSecondDex.<init>
/// CHECK-START: int Main.constructDerivedInSecondDex(int) inliner (after)
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static int constructDerivedInSecondDex(int intValue) {
@@ -1091,7 +1091,7 @@
/// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedInSecondDex.<init>
/// CHECK-START: int Main.constructDerivedInSecondDexWith0() inliner (after)
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static int constructDerivedInSecondDexWith0() {
@@ -1107,7 +1107,7 @@
/// CHECK-START: int Main.constructDerivedInSecondDex(long) inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
- /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: ConstructorFence
/// CHECK-NOT: InstanceFieldSet
public static int constructDerivedInSecondDex(long dummy) {
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index d1f36ed..520e7c3 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -341,6 +341,16 @@
}
}
+ // Bug b/37768917: potential dynamic BCE vs. loop optimizations
+ // case should be deal with correctly (used to DCHECK fail).
+ private static void arrayInTripCount(int[] a, byte[] b, int n) {
+ for (int k = 0; k < n; k++) {
+ for (int i = 0, u = a[0]; i < u; i++) {
+ b[i] += 2;
+ }
+ }
+ }
+
public static void main(String[] args) {
expectEquals(10, earlyExitFirst(-1));
for (int i = 0; i <= 10; i++) {
@@ -436,6 +446,13 @@
expectEquals(dd[i], 1);
}
+ xx[0] = 10;
+ byte[] bt = new byte[10];
+ arrayInTripCount(xx, bt, 20);
+ for (int i = 0; i < bt.length; i++) {
+ expectEquals(40, bt[i]);
+ }
+
System.out.println("passed");
}
diff --git a/test/648-inline-caches-unresolved/expected.txt b/test/648-inline-caches-unresolved/expected.txt
new file mode 100644
index 0000000..4e6a438
--- /dev/null
+++ b/test/648-inline-caches-unresolved/expected.txt
@@ -0,0 +1 @@
+Subclass
diff --git a/test/648-inline-caches-unresolved/info.txt b/test/648-inline-caches-unresolved/info.txt
new file mode 100644
index 0000000..8fc6042
--- /dev/null
+++ b/test/648-inline-caches-unresolved/info.txt
@@ -0,0 +1 @@
+Test for inlining with inline cache into an unresolved method.
diff --git a/test/648-inline-caches-unresolved/profile b/test/648-inline-caches-unresolved/profile
new file mode 100644
index 0000000..92c0a41
--- /dev/null
+++ b/test/648-inline-caches-unresolved/profile
@@ -0,0 +1 @@
+LMain;->inlineMonomorphicUnresolvedSuper(Ljava/lang/Object;)Ljava/lang/String;+LSubclass;
diff --git a/test/648-inline-caches-unresolved/run b/test/648-inline-caches-unresolved/run
new file mode 100644
index 0000000..fb70d22
--- /dev/null
+++ b/test/648-inline-caches-unresolved/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} $@ --secondary --profile
diff --git a/test/648-inline-caches-unresolved/src-dex2oat-unresolved/UnresolvedSuperClass.java b/test/648-inline-caches-unresolved/src-dex2oat-unresolved/UnresolvedSuperClass.java
new file mode 100644
index 0000000..dd3be00
--- /dev/null
+++ b/test/648-inline-caches-unresolved/src-dex2oat-unresolved/UnresolvedSuperClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class UnresolvedSuperClass {
+ public void superMethod() {
+ System.out.println("UnresolvedClass.superMethod()");
+ }
+}
diff --git a/test/648-inline-caches-unresolved/src/Main.java b/test/648-inline-caches-unresolved/src/Main.java
new file mode 100644
index 0000000..4e8aeec
--- /dev/null
+++ b/test/648-inline-caches-unresolved/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main extends UnresolvedSuperClass {
+ public static String inlineMonomorphicUnresolvedSuper(Object o) {
+ return o.toString();
+ }
+
+ public static void main(String[] args) {
+ System.out.println(inlineMonomorphicUnresolvedSuper(new Subclass()));
+ }
+}
+
+class Subclass {
+ public String toString() {
+ return "Subclass";
+ }
+}
diff --git a/test/912-classes/src/art/Test912Art.java b/test/912-classes/src/art/Test912Art.java
index 6da3cad..a1e7ff2 100644
--- a/test/912-classes/src/art/Test912Art.java
+++ b/test/912-classes/src/art/Test912Art.java
@@ -39,7 +39,7 @@
// run in configurations where dex2oat didn't verify the class itself. So explicitly
// check whether the class has been already loaded, and skip then.
// TODO: Add multiple configurations to the run script once that becomes easier to do.
- if (hasJit() && !isLoadedClass("art.Test912Art$ClassD")) {
+ if (hasJit() && !isLoadedClass("Lart/Test912Art$ClassD;")) {
testClassEventsJit();
}
}
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 702b247..b128d1c 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -385,3 +385,10 @@
5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
---
+
+default
+image
+zygote
+app
+
+3
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index e319f7d..f39c5f1 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -817,5 +817,192 @@
return result;
}
+using GetObjectHeapId = jvmtiError(*)(jvmtiEnv*, jlong, jint*, ...);
+static GetObjectHeapId gGetObjectHeapIdFn = nullptr;
+
+using GetHeapName = jvmtiError(*)(jvmtiEnv*, jint, char**, ...);
+static GetHeapName gGetHeapNameFn = nullptr;
+
+static void FreeExtensionFunctionInfo(jvmtiExtensionFunctionInfo* extensions, jint count) {
+ for (size_t i = 0; i != static_cast<size_t>(count); ++i) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].id));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].short_description));
+ for (size_t j = 0; j != static_cast<size_t>(extensions[i].param_count); ++j) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].params[j].name));
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].params));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].errors));
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test913_checkForExtensionApis(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ jint extension_count;
+ jvmtiExtensionFunctionInfo* extensions;
+ jvmtiError result = jvmti_env->GetExtensionFunctions(&extension_count, &extensions);
+ if (JvmtiErrorToException(env, jvmti_env, result)) {
+ return;
+ }
+
+ for (size_t i = 0; i != static_cast<size_t>(extension_count); ++i) {
+ if (strcmp("com.android.art.heap.get_object_heap_id", extensions[i].id) == 0) {
+ CHECK(gGetObjectHeapIdFn == nullptr);
+ gGetObjectHeapIdFn = reinterpret_cast<GetObjectHeapId>(extensions[i].func);
+
+ CHECK_EQ(extensions[i].param_count, 2);
+
+ CHECK_EQ(strcmp("tag", extensions[i].params[0].name), 0);
+ CHECK_EQ(extensions[i].params[0].base_type, JVMTI_TYPE_JLONG);
+ CHECK_EQ(extensions[i].params[0].kind, JVMTI_KIND_IN);
+
+ CHECK_EQ(strcmp("heap_id", extensions[i].params[1].name), 0);
+ CHECK_EQ(extensions[i].params[1].base_type, JVMTI_TYPE_JINT);
+ CHECK_EQ(extensions[i].params[1].kind, JVMTI_KIND_OUT);
+ CHECK_EQ(extensions[i].params[1].null_ok, false);
+
+ CHECK_EQ(extensions[i].error_count, 1);
+ CHECK(extensions[i].errors != nullptr);
+ CHECK(extensions[i].errors[0] == JVMTI_ERROR_NOT_FOUND);
+
+ continue;
+ }
+
+ if (strcmp("com.android.art.heap.get_heap_name", extensions[i].id) == 0) {
+ CHECK(gGetHeapNameFn == nullptr);
+ gGetHeapNameFn = reinterpret_cast<GetHeapName>(extensions[i].func);
+
+ CHECK_EQ(extensions[i].param_count, 2);
+
+ CHECK_EQ(strcmp("heap_id", extensions[i].params[0].name), 0);
+ CHECK_EQ(extensions[i].params[0].base_type, JVMTI_TYPE_JINT);
+ CHECK_EQ(extensions[i].params[0].kind, JVMTI_KIND_IN);
+
+ CHECK_EQ(strcmp("heap_name", extensions[i].params[1].name), 0);
+ CHECK_EQ(extensions[i].params[1].base_type, JVMTI_TYPE_CCHAR);
+ CHECK_EQ(extensions[i].params[1].kind, JVMTI_KIND_ALLOC_BUF);
+ CHECK_EQ(extensions[i].params[1].null_ok, false);
+
+ CHECK_EQ(extensions[i].error_count, 1);
+ CHECK(extensions[i].errors != nullptr);
+ CHECK(extensions[i].errors[0] == JVMTI_ERROR_ILLEGAL_ARGUMENT);
+ }
+ }
+
+ CHECK(gGetObjectHeapIdFn != nullptr);
+ CHECK(gGetHeapNameFn != nullptr);
+
+ FreeExtensionFunctionInfo(extensions, extension_count);
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_art_Test913_getObjectHeapId(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
+ CHECK(gGetObjectHeapIdFn != nullptr);
+ jint heap_id;
+ jvmtiError result = gGetObjectHeapIdFn(jvmti_env, tag, &heap_id);
+ JvmtiErrorToException(env, jvmti_env, result);
+ return heap_id;
+}
+
+extern "C" JNIEXPORT jstring JNICALL Java_art_Test913_getHeapName(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_id) {
+ CHECK(gGetHeapNameFn != nullptr);
+ char* heap_name;
+ jvmtiError result = gGetHeapNameFn(jvmti_env, heap_id, &heap_name);
+ if (JvmtiErrorToException(env, jvmti_env, result)) {
+ return nullptr;
+ }
+ jstring ret = env->NewStringUTF(heap_name);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(heap_name));
+ return ret;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test913_checkGetObjectHeapIdInCallback(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag, jint heap_id) {
+ CHECK(gGetObjectHeapIdFn != nullptr);
+
+ {
+ struct GetObjectHeapIdCallbacks {
+ static jint JNICALL FollowReferencesCallback(
+ jvmtiHeapReferenceKind reference_kind ATTRIBUTE_UNUSED,
+ const jvmtiHeapReferenceInfo* reference_info ATTRIBUTE_UNUSED,
+ jlong class_tag ATTRIBUTE_UNUSED,
+ jlong referrer_class_tag ATTRIBUTE_UNUSED,
+ jlong size ATTRIBUTE_UNUSED,
+ jlong* tag_ptr,
+ jlong* referrer_tag_ptr ATTRIBUTE_UNUSED,
+ jint length ATTRIBUTE_UNUSED,
+ void* user_data) {
+ if (*tag_ptr != 0) {
+ GetObjectHeapIdCallbacks* p = reinterpret_cast<GetObjectHeapIdCallbacks*>(user_data);
+ if (*tag_ptr == p->check_callback_tag) {
+ jint tag_heap_id;
+ jvmtiError result = gGetObjectHeapIdFn(jvmti_env, *tag_ptr, &tag_heap_id);
+ CHECK_EQ(result, JVMTI_ERROR_NONE);
+ CHECK_EQ(tag_heap_id, p->check_callback_id);
+ return JVMTI_VISIT_ABORT;
+ }
+ }
+
+ return JVMTI_VISIT_OBJECTS; // Continue visiting.
+ }
+
+ jlong check_callback_tag;
+ jint check_callback_id;
+ };
+
+ jvmtiHeapCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
+ callbacks.heap_reference_callback = GetObjectHeapIdCallbacks::FollowReferencesCallback;
+
+ GetObjectHeapIdCallbacks ffc;
+ ffc.check_callback_tag = tag;
+ ffc.check_callback_id = heap_id;
+
+ jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, nullptr, &callbacks, &ffc);
+ if (JvmtiErrorToException(env, jvmti_env, ret)) {
+ return;
+ }
+ }
+
+ {
+ struct GetObjectHeapIdCallbacks {
+ static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
+ jlong size ATTRIBUTE_UNUSED,
+ jlong* tag_ptr,
+ jint length ATTRIBUTE_UNUSED,
+ void* user_data) {
+ if (*tag_ptr != 0) {
+ GetObjectHeapIdCallbacks* p = reinterpret_cast<GetObjectHeapIdCallbacks*>(user_data);
+ if (*tag_ptr == p->check_callback_tag) {
+ jint tag_heap_id;
+ jvmtiError result = gGetObjectHeapIdFn(jvmti_env, *tag_ptr, &tag_heap_id);
+ CHECK_EQ(result, JVMTI_ERROR_NONE);
+ CHECK_EQ(tag_heap_id, p->check_callback_id);
+ return JVMTI_VISIT_ABORT;
+ }
+ }
+
+ return 0; // Continue visiting.
+ }
+
+ jlong check_callback_tag;
+ jint check_callback_id;
+ };
+
+ jvmtiHeapCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
+ callbacks.heap_iteration_callback = GetObjectHeapIdCallbacks::HeapIterationCallback;
+
+ GetObjectHeapIdCallbacks ffc;
+ ffc.check_callback_tag = tag;
+ ffc.check_callback_id = heap_id;
+
+ jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc);
+ if (JvmtiErrorToException(env, jvmti_env, ret)) {
+ return;
+ }
+ }
+}
+
} // namespace Test913Heaps
} // namespace art
diff --git a/test/913-heaps/src/art/Test913.java b/test/913-heaps/src/art/Test913.java
index 8800b1a..6694aad 100644
--- a/test/913-heaps/src/art/Test913.java
+++ b/test/913-heaps/src/art/Test913.java
@@ -16,6 +16,9 @@
package art;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -44,6 +47,8 @@
};
t.start();
cdl1.await();
+
+ doExtensionTests();
}
public static void runFollowReferences() throws Exception {
@@ -215,6 +220,50 @@
System.out.println(getTag(floatObject));
}
+ static ArrayList<Object> extensionTestHolder;
+
+ private static void doExtensionTests() {
+ checkForExtensionApis();
+
+ extensionTestHolder = new ArrayList<>();
+ System.out.println();
+
+ try {
+ getHeapName(-1);
+ System.out.println("Expected failure for -1");
+ } catch (Exception e) {
+ }
+ System.out.println(getHeapName(0));
+ System.out.println(getHeapName(1));
+ System.out.println(getHeapName(2));
+ System.out.println(getHeapName(3));
+ try {
+ getHeapName(4);
+ System.out.println("Expected failure for -1");
+ } catch (Exception e) {
+ }
+
+ System.out.println();
+
+ setTag(Object.class, 100000);
+ int objectClassHeapId = getObjectHeapId(100000);
+ int objClassExpectedHeapId = hasImage() ? 1 : 3;
+ if (objectClassHeapId != objClassExpectedHeapId) {
+ throw new RuntimeException("Expected object class in heap " + objClassExpectedHeapId +
+ " but received " + objectClassHeapId);
+ }
+
+ A a = new A();
+ extensionTestHolder.add(a);
+ setTag(a, 100001);
+ System.out.println(getObjectHeapId(100001));
+
+ checkGetObjectHeapIdInCallback(100000, objClassExpectedHeapId);
+ checkGetObjectHeapIdInCallback(100001, 3);
+
+ extensionTestHolder = null;
+ }
+
private static void runGc() {
clearStats();
forceGarbageCollection();
@@ -233,6 +282,24 @@
System.out.println((s > 0) + " " + (f > 0));
}
+ private static boolean hasImage() {
+ try {
+ int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
+ BufferedReader reader = new BufferedReader(new FileReader("/proc/" + pid + "/maps"));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.endsWith(".art")) {
+ reader.close();
+ return true;
+ }
+ }
+ reader.close();
+ return false;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private static class TestConfig {
private Class<?> klass = null;
private int heapFilter = 0;
@@ -642,6 +709,11 @@
private static native int getGcFinishes();
private static native void forceGarbageCollection();
+ private static native void checkForExtensionApis();
+ private static native int getObjectHeapId(long tag);
+ private static native String getHeapName(int heapId);
+ private static native void checkGetObjectHeapIdInCallback(long tag, int heapId);
+
public static native String[] followReferences(int heapFilter, Class<?> klassFilter,
Object initialObject, int stopAfter, int followSet, Object jniRef);
public static native String[] followReferencesString(Object initialObject);
diff --git a/test/etc/default-build b/test/etc/default-build
index 744c38b..0508b85 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -82,9 +82,9 @@
JACK_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
declare -A SMALI_EXPERIMENTAL_ARGS
-SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"
-SMALI_EXPERIMENTAL_ARGS["method-handles"]="--api-level 26"
-SMALI_EXPERIMENTAL_ARGS["agents"]="--api-level 26"
+SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api 24"
+SMALI_EXPERIMENTAL_ARGS["method-handles"]="--api 26"
+SMALI_EXPERIMENTAL_ARGS["agents"]="--api 26"
declare -A JAVAC_EXPERIMENTAL_ARGS
JAVAC_EXPERIMENTAL_ARGS["default-methods"]="-source 1.8 -target 1.8"
@@ -275,7 +275,7 @@
if [ "${HAS_SMALI}" = "true" -a ${NEED_DEX} = "true" ]; then
# Compile Smali classes
- ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
+ ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
# Don't bother with dexmerger if we provide our own main function in a smali file.
if [ ${SKIP_DX_MERGER} = "false" ]; then
@@ -287,7 +287,7 @@
if [ "${HAS_SMALI_MULTIDEX}" = "true" -a ${NEED_DEX} = "true" ]; then
# Compile Smali classes
- ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'`
+ ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'`
# Don't bother with dexmerger if we provide our own main function in a smali file.
if [ ${HAS_SRC_MULTIDEX} = "true" ]; then
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index bb99e1c..f750556 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -564,6 +564,11 @@
profman_cmdline="${ANDROID_ROOT}/bin/profman \
--apk=$DEX_LOCATION/$TEST_NAME.jar \
--dex-location=$DEX_LOCATION/$TEST_NAME.jar"
+ if [ -f $DEX_LOCATION/$TEST_NAME-ex.jar ]; then
+ profman_cmdline="${profman_cmdline} \
+ --apk=$DEX_LOCATION/$TEST_NAME-ex.jar \
+ --dex-location=$DEX_LOCATION/$TEST_NAME-ex.jar"
+ fi
COMPILE_FLAGS="${COMPILE_FLAGS} --profile-file=$DEX_LOCATION/$TEST_NAME.prof"
FLAGS="${FLAGS} -Xcompiler-option --profile-file=$DEX_LOCATION/$TEST_NAME.prof"
if [ "$PROFILE" = "y" ]; then
diff --git a/test/knownfailures.json b/test/knownfailures.json
index ea810db..0a7089a 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -328,11 +328,6 @@
"variant": "interpreter | optimizing | regalloc_gc | jit"
},
{
- "tests": ["912-classes"],
- "bug": "http://b/36344364",
- "variant": "no-dex2oat | relocate-npatchoat"
- },
- {
"tests": ["476-clinit-inline-static-invoke",
"496-checker-inlining-class-loader",
"508-referrer-method",
diff --git a/test/run-test b/test/run-test
index f60f766..933a7fe 100755
--- a/test/run-test
+++ b/test/run-test
@@ -46,7 +46,7 @@
export DEX_LOCATION=/data/run-test/${test_dir}
export NEED_DEX="true"
export USE_JACK="true"
-export SMALI_ARGS="--experimental"
+export SMALI_ARGS=""
# If dx was not set by the environment variable, assume it is in the path.
if [ -z "$DX" ]; then