summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64.cc10
-rw-r--r--compiler/optimizing/code_generator_arm.cc132
-rw-r--r--compiler/optimizing/code_generator_arm64.cc113
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc111
-rw-r--r--compiler/optimizing/code_generator_x86.cc30
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc39
-rw-r--r--profman/profman.cc11
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S49
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S80
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S82
-rw-r--r--runtime/arch/mips64/quick_entrypoints_mips64.S81
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S54
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S45
-rw-r--r--runtime/entrypoints/quick/quick_dexcache_entrypoints.cc17
-rw-r--r--runtime/gc/heap.cc16
-rw-r--r--runtime/hprof/hprof.cc27
-rw-r--r--runtime/interpreter/unstarted_runtime.cc104
-rw-r--r--runtime/interpreter/unstarted_runtime_test.cc95
-rw-r--r--runtime/jit/profile_compilation_info.cc18
-rw-r--r--runtime/jit/profile_compilation_info.h3
-rw-r--r--runtime/mirror/string.h5
-rw-r--r--runtime/openjdkjvmti/ti_thread.cc4
-rw-r--r--test/912-classes/classes.cc185
-rw-r--r--test/912-classes/src/Main.java56
-rw-r--r--test/Android.run-test.mk1
-rw-r--r--tools/ahat/src/InstanceUtils.java21
-rw-r--r--tools/ahat/test-dump/Main.java2
-rw-r--r--tools/ahat/test/InstanceUtilsTest.java70
-rwxr-xr-xtools/run-libcore-tests.sh2
29 files changed, 1066 insertions, 397 deletions
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 79e1785e91..9ddf200237 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -31,9 +31,7 @@ namespace linker {
namespace {
inline bool IsAdrpPatch(const LinkerPatch& patch) {
- LinkerPatch::Type type = patch.GetType();
- return
- (type == LinkerPatch::Type::kStringRelative || type == LinkerPatch::Type::kDexCacheArray) &&
+ return (patch.IsPcRelative() && patch.GetType() != LinkerPatch::Type::kCallRelative) &&
patch.LiteralOffset() == patch.PcInsnOffset();
}
@@ -214,11 +212,11 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative ||
patch.GetType() == LinkerPatch::Type::kTypeRelative) << patch.GetType();
} else {
- // With the read barrier (non-Baker) enabled, it could be kDexCacheArray in the
- // HLoadString::LoadKind::kDexCachePcRelative case of VisitLoadString().
+ // With the read barrier (non-Baker) enabled, it could be kStringBssEntry or kTypeBssEntry.
DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative ||
patch.GetType() == LinkerPatch::Type::kTypeRelative ||
- patch.GetType() == LinkerPatch::Type::kDexCacheArray) << patch.GetType();
+ patch.GetType() == LinkerPatch::Type::kStringBssEntry ||
+ patch.GetType() == LinkerPatch::Type::kTypeBssEntry) << patch.GetType();
}
shift = 0u; // No shift for ADD.
} else {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 20cdae3619..759a951d6b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -367,22 +367,37 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM {
class LoadClassSlowPathARM : public SlowPathCodeARM {
public:
- LoadClassSlowPathARM(HLoadClass* cls,
- HInstruction* at,
- uint32_t dex_pc,
- bool do_clinit)
+ LoadClassSlowPathARM(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
: SlowPathCodeARM(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
+ Location out = locations->Out();
+ constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
+ // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
+ DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
+ bool is_load_class_bss_entry =
+ (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
+ Register entry_address = kNoRegister;
+ if (is_load_class_bss_entry && call_saves_everything_except_r0) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
+ // the kSaveEverything call.
+ bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
+ entry_address = temp_is_r0 ? out.AsRegister<Register>() : temp;
+ DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
+ if (temp_is_r0) {
+ __ mov(entry_address, ShifterOperand(temp));
+ }
+ }
dex::TypeIndex type_index = cls_->GetTypeIndex();
__ LoadImmediate(calling_convention.GetRegisterAt(0), type_index.index_);
QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
@@ -394,30 +409,31 @@ class LoadClassSlowPathARM : public SlowPathCodeARM {
CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
}
+ // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
+ if (is_load_class_bss_entry) {
+ if (call_saves_everything_except_r0) {
+ // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
+ __ str(R0, Address(entry_address));
+ } else {
+ // For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ Register temp = IP;
+ CodeGeneratorARM::PcRelativePatchInfo* labels =
+ arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
+ __ BindTrackedLabel(&labels->movw_label);
+ __ movw(temp, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->movt_label);
+ __ movt(temp, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->add_pc_label);
+ __ add(temp, temp, ShifterOperand(PC));
+ __ str(R0, Address(temp));
+ }
+ }
// Move the class to the desired location.
- Location out = locations->Out();
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
}
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.
- CodeGeneratorARM::PcRelativePatchInfo* labels =
- arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
- __ BindTrackedLabel(&labels->movw_label);
- __ movw(IP, /* placeholder */ 0u);
- __ BindTrackedLabel(&labels->movt_label);
- __ movt(IP, /* placeholder */ 0u);
- __ BindTrackedLabel(&labels->add_pc_label);
- __ add(IP, IP, ShifterOperand(PC));
- __ str(locations->Out().AsRegister<Register>(), Address(IP));
- }
__ b(GetExitLabel());
}
@@ -441,12 +457,13 @@ class LoadStringSlowPathARM : public SlowPathCodeARM {
explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCodeARM(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>();
- Register temp = locations->GetTemp(0).AsRegister<Register>();
constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
@@ -455,12 +472,16 @@ class LoadStringSlowPathARM : public SlowPathCodeARM {
InvokeRuntimeCallingConvention calling_convention;
// In the unlucky case that the `temp` is R0, we preserve the address in `out` across
- // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call).
- bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
- Register entry_address = temp_is_r0 ? out : temp;
- DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
- if (call_saves_everything_except_r0 && temp_is_r0) {
- __ mov(entry_address, ShifterOperand(temp));
+ // the kSaveEverything call.
+ Register entry_address = kNoRegister;
+ if (call_saves_everything_except_r0) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
+ entry_address = temp_is_r0 ? out : temp;
+ DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
+ if (temp_is_r0) {
+ __ mov(entry_address, ShifterOperand(temp));
+ }
}
__ LoadImmediate(calling_convention.GetRegisterAt(0), string_index.index_);
@@ -473,15 +494,16 @@ class LoadStringSlowPathARM : public SlowPathCodeARM {
__ str(R0, Address(entry_address));
} else {
// For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ Register temp = IP;
CodeGeneratorARM::PcRelativePatchInfo* labels =
arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
__ BindTrackedLabel(&labels->movw_label);
- __ movw(entry_address, /* placeholder */ 0u);
+ __ movw(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->movt_label);
- __ movt(entry_address, /* placeholder */ 0u);
+ __ movt(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->add_pc_label);
- __ add(entry_address, entry_address, ShifterOperand(PC));
- __ str(R0, Address(entry_address));
+ __ add(temp, temp, ShifterOperand(PC));
+ __ str(R0, Address(temp));
}
arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
@@ -624,6 +646,10 @@ class ArraySetSlowPathARM : public SlowPathCodeARM {
// probably still be a from-space reference (unless it gets updated by
// another thread, or if another thread installed another object
// reference (different from `ref`) in `obj.field`).
+//
+// If `entrypoint` is a valid location it is assumed to already be
+// holding the entrypoint. The case where the entrypoint is passed in
+// is for the GcRoot read barrier.
class ReadBarrierMarkSlowPathARM : public SlowPathCodeARM {
public:
ReadBarrierMarkSlowPathARM(HInstruction* instruction,
@@ -5755,6 +5781,7 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
cls,
Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Location::RegisterLocation(R0));
+ DCHECK_EQ(calling_convention.GetRegisterAt(0), R0);
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -5772,6 +5799,22 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
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.
+ // Note that IP may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we request a different temp.
+ locations->AddTemp(Location::RequiresRegister());
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
+ // that the the kPrimNot result register is the same as the first argument register.
+ 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
@@ -5834,15 +5877,18 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE
break;
}
case HLoadClass::LoadKind::kBssEntry: {
+ Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
+ ? locations->GetTemp(0).AsRegister<Register>()
+ : out;
CodeGeneratorARM::PcRelativePatchInfo* labels =
codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
__ BindTrackedLabel(&labels->movw_label);
- __ movw(out, /* placeholder */ 0u);
+ __ movw(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->movt_label);
- __ movt(out, /* placeholder */ 0u);
+ __ movt(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->add_pc_label);
- __ add(out, out, ShifterOperand(PC));
- GenerateGcRootFieldLoad(cls, out_loc, out, 0, kCompilerReadBarrierOption);
+ __ add(temp, temp, ShifterOperand(PC));
+ GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
generate_null_check = true;
break;
}
@@ -5851,7 +5897,7 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE
cls->GetTypeIndex(),
cls->GetClass()));
// /* GcRoot<mirror::Class> */ out = *out
- GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod:
@@ -5938,9 +5984,9 @@ void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything, including temps.
- // Note that IP may theoretically be clobbered by saving/restoring the live register
- // (only one thanks to the custom calling convention), so we request a different temp.
+ // Rely on the pResolveString and marking to save everything we need, including temps.
+ // Note that IP may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we request a different temp.
locations->AddTemp(Location::RequiresRegister());
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
@@ -5991,7 +6037,9 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_S
}
case HLoadString::LoadKind::kBssEntry: {
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
- Register temp = locations->GetTemp(0).AsRegister<Register>();
+ Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
+ ? locations->GetTemp(0).AsRegister<Register>()
+ : out;
CodeGeneratorARM::PcRelativePatchInfo* labels =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
__ BindTrackedLabel(&labels->movw_label);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 598be4715b..27b42536ef 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -275,15 +275,37 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
LoadClassSlowPathARM64(HLoadClass* cls,
HInstruction* at,
uint32_t dex_pc,
- bool do_clinit)
- : SlowPathCodeARM64(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ bool do_clinit,
+ vixl::aarch64::Register bss_entry_temp = vixl::aarch64::Register(),
+ vixl::aarch64::Label* bss_entry_adrp_label = nullptr)
+ : SlowPathCodeARM64(at),
+ cls_(cls),
+ dex_pc_(dex_pc),
+ do_clinit_(do_clinit),
+ bss_entry_temp_(bss_entry_temp),
+ bss_entry_adrp_label_(bss_entry_adrp_label) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
+ Location out = locations->Out();
+ constexpr bool call_saves_everything_except_r0_ip0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+ // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the page address of
+ // the entry which is in a scratch register. Make sure it's not used for saving/restoring
+ // registers. Exclude the scratch register also for non-Baker read barrier for simplicity.
+ DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
+ bool is_load_class_bss_entry =
+ (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
+ UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler());
+ if (is_load_class_bss_entry) {
+ // This temp is a scratch register.
+ DCHECK(bss_entry_temp_.IsValid());
+ temps.Exclude(bss_entry_temp_);
+ }
+
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
@@ -300,7 +322,6 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
}
// 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();
@@ -308,25 +329,23 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
}
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) {
+ if (is_load_class_bss_entry) {
DCHECK(out.IsValid());
- UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler());
- Register temp = temps.AcquireX();
const DexFile& dex_file = cls_->GetDexFile();
- // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
- // kSaveEverything and use a temporary for the ADRP in the fast path, so that we
- // can avoid the ADRP here.
- vixl::aarch64::Label* adrp_label =
- arm64_codegen->NewBssEntryTypePatch(dex_file, type_index);
- arm64_codegen->EmitAdrpPlaceholder(adrp_label, temp);
+ if (call_saves_everything_except_r0_ip0) {
+ // The class entry page address was preserved in bss_entry_temp_ thanks to kSaveEverything.
+ } else {
+ // For non-Baker read barrier, we need to re-calculate the address of the class entry page.
+ bss_entry_adrp_label_ = arm64_codegen->NewBssEntryTypePatch(dex_file, type_index);
+ arm64_codegen->EmitAdrpPlaceholder(bss_entry_adrp_label_, bss_entry_temp_);
+ }
vixl::aarch64::Label* strp_label =
- arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, adrp_label);
+ arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label_);
{
SingleEmissionCheckScope guard(arm64_codegen->GetVIXLAssembler());
__ Bind(strp_label);
__ str(RegisterFrom(locations->Out(), Primitive::kPrimNot),
- MemOperand(temp, /* offset placeholder */ 0));
+ MemOperand(bss_entry_temp_, /* offset placeholder */ 0));
}
}
__ B(GetExitLabel());
@@ -344,6 +363,10 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
// Whether to initialize the class.
const bool do_clinit_;
+ // For HLoadClass/kBssEntry, the temp register and the label of the ADRP where it was loaded.
+ vixl::aarch64::Register bss_entry_temp_;
+ vixl::aarch64::Label* bss_entry_adrp_label_;
+
DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64);
};
@@ -619,8 +642,10 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) {
// probably still be a from-space reference (unless it gets updated by
// another thread, or if another thread installed another object
// reference (different from `ref`) in `obj.field`).
-// If entrypoint is a valid location it is assumed to already be holding the entrypoint. The case
-// where the entrypoint is passed in is for the GcRoot read barrier.
+//
+// If `entrypoint` is a valid location it is assumed to already be
+// holding the entrypoint. The case where the entrypoint is passed in
+// is for the GcRoot read barrier.
class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 {
public:
ReadBarrierMarkSlowPathARM64(HInstruction* instruction,
@@ -4393,6 +4418,7 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
cls,
LocationFrom(calling_convention.GetRegisterAt(0)),
LocationFrom(vixl::aarch64::x0));
+ DCHECK(calling_convention.GetRegisterAt(0).Is(vixl::aarch64::x0));
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -4410,6 +4436,22 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (cls->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution or initialization and marking to save everything we need.
+ // Note that IP0 may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we shall use IP1.
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
+ DCHECK_EQ(calling_convention.GetRegisterAt(0).GetCode(),
+ RegisterFrom(calling_convention.GetReturnLocation(Primitive::kPrimNot),
+ Primitive::kPrimNot).GetCode());
+ 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
@@ -4424,6 +4466,8 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA
Location out_loc = cls->GetLocations()->Out();
Register out = OutputRegister(cls);
+ Register bss_entry_temp;
+ vixl::aarch64::Label* bss_entry_adrp_label = nullptr;
const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
? kWithoutReadBarrier
@@ -4473,18 +4517,23 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA
// Add ADRP with its PC-relative Class .bss entry patch.
const DexFile& dex_file = cls->GetDexFile();
dex::TypeIndex type_index = cls->GetTypeIndex();
- vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index);
- codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
+ // We can go to slow path even with non-zero reference and in that case marking
+ // can clobber IP0, so we need to use IP1 which shall be preserved.
+ bss_entry_temp = ip1;
+ UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
+ temps.Exclude(bss_entry_temp);
+ bss_entry_adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index);
+ codegen_->EmitAdrpPlaceholder(bss_entry_adrp_label, bss_entry_temp);
// Add LDR with its PC-relative Class patch.
vixl::aarch64::Label* ldr_label =
- codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label);
+ codegen_->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label);
// /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */
GenerateGcRootFieldLoad(cls,
- cls->GetLocations()->Out(),
- out.X(),
- /* placeholder */ 0u,
+ out_loc,
+ bss_entry_temp,
+ /* offset placeholder */ 0u,
ldr_label,
- kCompilerReadBarrierOption);
+ read_barrier_option);
generate_null_check = true;
break;
}
@@ -4497,7 +4546,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA
out.X(),
/* offset */ 0,
/* fixup_label */ nullptr,
- kCompilerReadBarrierOption);
+ read_barrier_option);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod:
@@ -4506,10 +4555,11 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA
UNREACHABLE();
}
- if (generate_null_check || cls->MustGenerateClinitCheck()) {
+ bool do_clinit = cls->MustGenerateClinitCheck();
+ if (generate_null_check || do_clinit) {
DCHECK(cls->CanCallRuntime());
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
- cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ cls, cls, cls->GetDexPc(), do_clinit, bss_entry_temp, bss_entry_adrp_label);
codegen_->AddSlowPath(slow_path);
if (generate_null_check) {
__ Cbz(out, slow_path->GetEntryLabel());
@@ -4577,7 +4627,9 @@ void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything, including temps.
+ // Rely on the pResolveString and marking to save everything we need.
+ // Note that IP0 may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we shall use IP1.
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
@@ -4628,8 +4680,11 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD
const DexFile& dex_file = load->GetDexFile();
const dex::StringIndex string_index = load->GetStringIndex();
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+ // We could use IP0 as the marking shall not clobber IP0 if the reference is null and
+ // that's when we need the slow path. But let's not rely on such details and use IP1.
+ Register temp = ip1;
UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
- Register temp = temps.AcquireX();
+ temps.Exclude(temp);
vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index);
codegen_->EmitAdrpPlaceholder(adrp_label, temp);
// Add LDR with its PC-relative String patch.
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index e18960872e..5c4ca5bc17 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -400,12 +400,30 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
+ Location out = locations->Out();
+ constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
+ DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
+ bool is_load_class_bss_entry =
+ (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
+ vixl32::Register entry_address;
+ if (is_load_class_bss_entry && call_saves_everything_except_r0) {
+ vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+ // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
+ // the kSaveEverything call.
+ bool temp_is_r0 = temp.Is(calling_convention.GetRegisterAt(0));
+ entry_address = temp_is_r0 ? RegisterFrom(out) : temp;
+ DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
+ if (temp_is_r0) {
+ __ Mov(entry_address, temp);
+ }
+ }
dex::TypeIndex type_index = cls_->GetTypeIndex();
__ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
@@ -417,27 +435,28 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
}
+ // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
+ if (is_load_class_bss_entry) {
+ if (call_saves_everything_except_r0) {
+ // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
+ __ Str(r0, MemOperand(entry_address));
+ } else {
+ // For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ UseScratchRegisterScope temps(
+ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+ arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
+ arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
+ __ Str(r0, MemOperand(temp));
+ }
+ }
// Move the class to the desired location.
- Location out = locations->Out();
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
arm_codegen->Move32(locations->Out(), LocationFrom(r0));
}
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.
- UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
- vixl32::Register temp = temps.Acquire();
- CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
- arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
- arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
- __ Str(OutputRegister(cls_), MemOperand(temp));
- }
__ B(GetExitLabel());
}
@@ -462,12 +481,13 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
: SlowPathCodeARMVIXL(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();
vixl32::Register out = OutputRegister(load);
- vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
@@ -476,12 +496,16 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
InvokeRuntimeCallingConventionARMVIXL calling_convention;
// In the unlucky case that the `temp` is R0, we preserve the address in `out` across
- // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call).
- bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
- vixl32::Register entry_address = temp_is_r0 ? out : temp;
- DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
- if (call_saves_everything_except_r0 && temp_is_r0) {
- __ Mov(entry_address, temp);
+ // the kSaveEverything call.
+ vixl32::Register entry_address;
+ if (call_saves_everything_except_r0) {
+ vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+ bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
+ entry_address = temp_is_r0 ? out : temp;
+ DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
+ if (temp_is_r0) {
+ __ Mov(entry_address, temp);
+ }
}
__ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
@@ -494,10 +518,13 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
__ Str(r0, MemOperand(entry_address));
} else {
// For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ UseScratchRegisterScope temps(
+ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
- arm_codegen->EmitMovwMovtPlaceholder(labels, out);
- __ Str(r0, MemOperand(entry_address));
+ arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
+ __ Str(r0, MemOperand(temp));
}
arm_codegen->Move32(locations->Out(), LocationFrom(r0));
@@ -5832,6 +5859,7 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
cls,
LocationFrom(calling_convention.GetRegisterAt(0)),
LocationFrom(r0));
+ DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -5849,6 +5877,22 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
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.
+ // Note that IP may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we request a different temp.
+ locations->AddTemp(Location::RequiresRegister());
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
+ // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
+ // that the the kPrimNot result register is the same as the first argument register.
+ 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
@@ -5906,10 +5950,13 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_
break;
}
case HLoadClass::LoadKind::kBssEntry: {
+ vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
+ ? RegisterFrom(locations->GetTemp(0))
+ : out;
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
- codegen_->EmitMovwMovtPlaceholder(labels, out);
- GenerateGcRootFieldLoad(cls, out_loc, out, 0, kCompilerReadBarrierOption);
+ codegen_->EmitMovwMovtPlaceholder(labels, temp);
+ GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
generate_null_check = true;
break;
}
@@ -5918,7 +5965,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_
cls->GetTypeIndex(),
cls->GetClass()));
// /* GcRoot<mirror::Class> */ out = *out
- GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod:
@@ -6012,9 +6059,9 @@ void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything, including temps.
- // Note that IP may theoretically be clobbered by saving/restoring the live register
- // (only one thanks to the custom calling convention), so we request a different temp.
+ // Rely on the pResolveString and marking to save everything we need, including temps.
+ // Note that IP may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we request a different temp.
locations->AddTemp(Location::RequiresRegister());
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConventionARMVIXL calling_convention;
@@ -6059,7 +6106,9 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE
}
case HLoadString::LoadKind::kBssEntry: {
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
- vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+ vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
+ ? RegisterFrom(locations->GetTemp(0))
+ : out;
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
codegen_->EmitMovwMovtPlaceholder(labels, temp);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 137b55423b..09612c8dbf 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6057,6 +6057,7 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
cls,
Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Location::RegisterLocation(EAX));
+ DCHECK_EQ(calling_convention.GetRegisterAt(0), EAX);
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -6076,6 +6077,17 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution and/or initialization to save everything.
+ 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.
+ }
+ }
}
Label* CodeGeneratorX86::NewJitRootClassPatch(const DexFile& dex_file,
@@ -6158,7 +6170,7 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE
Label* fixup_label = codegen_->NewJitRootClassPatch(
cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass());
// /* GcRoot<mirror::Class> */ out = *address
- GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, read_barrier_option);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod:
@@ -6250,7 +6262,7 @@ void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything.
+ // Rely on the pResolveString to save everything.
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
@@ -7136,9 +7148,10 @@ void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
//
- // root = *address;
- // if (Thread::Current()->GetIsGcMarking()) {
- // root = ReadBarrier::Mark(root)
+ // root = obj.field;
+ // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+ // if (temp != null) {
+ // root = temp(root)
// }
// /* GcRoot<mirror::Object> */ root = *address
@@ -7159,8 +7172,11 @@ void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(
instruction, root, /* unpoison_ref_before_marking */ false);
codegen_->AddSlowPath(slow_path);
- __ fs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86PointerSize>().Int32Value()),
- Immediate(0));
+ // Test the entrypoint (`Thread::Current()->pReadBarrierMarkReg ## root.reg()`).
+ const int32_t entry_point_offset =
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(root.reg());
+ __ fs()->cmpl(Address::Absolute(entry_point_offset), Immediate(0));
+ // The entrypoint is null when the GC is not marking.
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
} else {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index c5367ce86e..0879992e32 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -245,9 +245,8 @@ class LoadClassSlowPathX86_64 : public SlowPathCode {
SaveLiveRegisters(codegen, locations);
- InvokeRuntimeCallingConvention calling_convention;
- __ movl(CpuRegister(calling_convention.GetRegisterAt(0)),
- Immediate(cls_->GetTypeIndex().index_));
+ // Custom calling convention: RAX serves as both input and output.
+ __ movl(CpuRegister(RAX), Immediate(cls_->GetTypeIndex().index_));
x86_64_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType,
instruction_,
dex_pc_,
@@ -5456,10 +5455,10 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(
void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
HLoadClass::LoadKind load_kind = cls->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
- InvokeRuntimeCallingConvention calling_convention;
+ // Custom calling convention: RAX serves as both input and output.
CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(RAX),
Location::RegisterLocation(RAX));
return;
}
@@ -5478,6 +5477,17 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution and/or initialization to save everything.
+ // Custom calling convention: RAX serves as both input and output.
+ RegisterSet caller_saves = RegisterSet::Empty();
+ caller_saves.Add(Location::RegisterLocation(RAX));
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
Label* CodeGeneratorX86_64::NewJitRootClassPatch(const DexFile& dex_file,
@@ -5553,7 +5563,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S
Label* fixup_label =
codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass());
// /* GcRoot<mirror::Class> */ out = *address
- GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, read_barrier_option);
break;
}
default:
@@ -5629,7 +5639,7 @@ void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything.
+ // Rely on the pResolveString to save everything.
// Custom calling convention: RAX serves as both input and output.
RegisterSet caller_saves = RegisterSet::Empty();
caller_saves.Add(Location::RegisterLocation(RAX));
@@ -6501,9 +6511,10 @@ void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
//
- // root = *address;
- // if (Thread::Current()->GetIsGcMarking()) {
- // root = ReadBarrier::Mark(root)
+ // root = obj.field;
+ // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+ // if (temp != null) {
+ // root = temp(root)
// }
// /* GcRoot<mirror::Object> */ root = *address
@@ -6524,9 +6535,11 @@ void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(
instruction, root, /* unpoison_ref_before_marking */ false);
codegen_->AddSlowPath(slow_path);
- __ gs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86_64PointerSize>().Int32Value(),
- /* no_rip */ true),
- Immediate(0));
+ // Test the `Thread::Current()->pReadBarrierMarkReg ## root.reg()` entrypoint.
+ const int32_t entry_point_offset =
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(root.reg());
+ __ gs()->cmpl(Address::Absolute(entry_point_offset, /* no_rip */ true), Immediate(0));
+ // The entrypoint is null when the GC is not marking.
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
} else {
diff --git a/profman/profman.cc b/profman/profman.cc
index ffebb6a2ea..b0cbed1ef9 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -248,8 +248,11 @@ class ProfMan FINAL {
return result;
}
- int DumpOneProfile(const std::string& banner, const std::string& filename, int fd,
- const std::vector<const DexFile*>* dex_files, std::string* dump) {
+ int DumpOneProfile(const std::string& banner,
+ const std::string& filename,
+ int fd,
+ const std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::string* dump) {
if (!filename.empty()) {
fd = open(filename.c_str(), O_RDWR);
if (fd < 0) {
@@ -277,7 +280,7 @@ class ProfMan FINAL {
// Open apk/zip files and and read dex files.
MemMap::Init(); // for ZipArchive::OpenFromFd
- std::vector<const DexFile*> dex_files;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
assert(dex_locations_.size() == apks_fd_.size());
static constexpr bool kVerifyChecksum = true;
for (size_t i = 0; i < dex_locations_.size(); ++i) {
@@ -293,7 +296,7 @@ class ProfMan FINAL {
continue;
}
for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) {
- dex_files.push_back(dex_file.release());
+ dex_files.emplace_back(std::move(dex_file));
}
}
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index a443a4060d..cfe8406fbf 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -965,9 +965,27 @@ ENTRY \name
END \name
.endm
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+// Macro for string and type resolution and initialization.
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint
+ .extern \entrypoint
+ENTRY \name
+ SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC
+ mov r1, r9 @ pass Thread::Current
+ bl \entrypoint @ (uint32_t index, Thread*)
+ cbz r0, 1f @ If result is null, deliver the OOME.
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
+ bx lr
+ .cfi_restore_state
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+END \name
+.endm
+
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
/*
* Called by managed code to resolve a static field and load a non-wide value.
@@ -1066,27 +1084,6 @@ ENTRY art_quick_set64_static
DELIVER_PENDING_EXCEPTION
END art_quick_set64_static
- /*
- * Entry from managed code to resolve a string, this stub will
- * check the dex cache for a matching string (the fast path), and if not found,
- * it will allocate a String and deliver an exception on error.
- * On success the String is returned. R0 holds the string index.
- */
-
-ENTRY art_quick_resolve_string
- SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC
- mov r1, r9 @ pass Thread::Current
- bl artResolveStringFromCode @ (uint32_t type_idx, Thread*)
- cbz r0, 1f @ If result is null, deliver the OOME.
- .cfi_remember_state
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
- bx lr
- .cfi_restore_state
-1:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END art_quick_resolve_string
-
-
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS
// Comment out allocators that have arm specific asm.
@@ -2057,7 +2054,9 @@ ENTRY \name
beq .Lret_forwarding_address\name
.Lslow_rb_\name:
- // Save IP: the kSaveEverything entrypoint art_quick_resolve_string makes a tail call here.
+ // Save IP: The kSaveEverything entrypoint art_quick_resolve_string used to
+ // make a tail call here. Currently, it serves only for stack alignment but
+ // we may reintroduce kSaveEverything calls here in the future.
push {r0-r4, r9, ip, lr} @ save return address, core caller-save registers and ip
.cfi_adjust_cfa_offset 32
.cfi_rel_offset r0, 0
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 219d8b447a..bfbe4816ba 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1553,6 +1553,24 @@ ENTRY \name
END \name
.endm
+// Macro for string and type resolution and initialization.
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint
+ .extern \entrypoint
+ENTRY \name
+ SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl
+ mov x1, xSELF // pass Thread::Current
+ bl \entrypoint // (int32_t index, Thread* self)
+ cbz w0, 1f // If result is null, deliver the OOME.
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
+ ret // return
+ .cfi_restore_state
+ .cfi_def_cfa_offset FRAME_SIZE_SAVE_EVERYTHING // workaround for clang bug: 31975598
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+END \name
+.endm
+
.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
cbz w0, 1f // result zero branch over
ret // return
@@ -1571,10 +1589,11 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode,
* 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
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-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, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
@@ -1604,27 +1623,6 @@ THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCompiledCod
THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
- /*
- * Entry from managed code to resolve a string, this stub will
- * check the dex cache for a matching string (the fast path), and if not found,
- * it will allocate a String and deliver an exception on error.
- * On success the String is returned. R0 holds the string index.
- */
-
-ENTRY art_quick_resolve_string
- SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl
- mov x1, xSELF // pass Thread::Current
- bl artResolveStringFromCode // (int32_t string_idx, Thread* self)
- cbz w0, 1f // If result is null, deliver the OOME.
- .cfi_remember_state
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
- ret // return
- .cfi_restore_state
- .cfi_def_cfa_offset FRAME_SIZE_SAVE_EVERYTHING // workaround for clang bug: 31975598
-1:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END art_quick_resolve_string
-
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS
// Comment out allocators that have arm64 specific asm.
@@ -2380,13 +2378,6 @@ END art_quick_indexof
ENTRY \name
// Reference is null, no work to do at all.
cbz \wreg, .Lret_rb_\name
- /*
- * Allocate 46 stack slots * 8 = 368 bytes:
- * - 20 slots for core registers X0-X19
- * - 24 slots for floating-point registers D0-D7 and D16-D31
- * - 1 slot for return address register XLR
- * - 1 padding slot for 16-byte stack alignment
- */
// Use wIP0 as temp and check the mark bit of the reference. wIP0 is not used by the compiler.
ldr wIP0, [\xreg, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
tbz wIP0, #LOCK_WORD_MARK_BIT_SHIFT, .Lnot_marked_rb_\name
@@ -2398,10 +2389,15 @@ ENTRY \name
cmp wzr, wIP0, lsr #30
beq .Lret_forwarding_address\name
.Lslow_rb_\name:
- // We must not clobber IP0 since art_quick_resolve_string makes a tail call here and relies on
- // IP0 being restored.
+ /*
+ * Allocate 44 stack slots * 8 = 352 bytes:
+ * - 20 slots for core registers X0-15, X17-X19, LR
+ * - 24 slots for floating-point registers D0-D7 and D16-D31
+ */
+ // We must not clobber IP1 since code emitted for HLoadClass and HLoadString
+ // relies on IP1 being preserved.
// Save all potentially live caller-save core registers.
- SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 368
+ SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 352
SAVE_TWO_REGS x2, x3, 16
SAVE_TWO_REGS x4, x5, 32
SAVE_TWO_REGS x6, x7, 48
@@ -2409,8 +2405,8 @@ ENTRY \name
SAVE_TWO_REGS x10, x11, 80
SAVE_TWO_REGS x12, x13, 96
SAVE_TWO_REGS x14, x15, 112
- SAVE_TWO_REGS x16, x17, 128
- SAVE_TWO_REGS x18, x19, 144
+ SAVE_TWO_REGS x17, x18, 128 // Skip x16, i.e. IP0.
+ SAVE_TWO_REGS x19, xLR, 144 // Save also return address.
// Save all potentially live caller-save floating-point registers.
stp d0, d1, [sp, #160]
stp d2, d3, [sp, #176]
@@ -2424,9 +2420,6 @@ ENTRY \name
stp d26, d27, [sp, #304]
stp d28, d29, [sp, #320]
stp d30, d31, [sp, #336]
- // Save return address.
- // (sp + #352 is a padding slot)
- SAVE_REG xLR, 360
.ifnc \wreg, w0
mov w0, \wreg // Pass arg1 - obj from `wreg`
@@ -2446,8 +2439,8 @@ ENTRY \name
POP_REGS_NE x10, x11, 80, \xreg
POP_REGS_NE x12, x13, 96, \xreg
POP_REGS_NE x14, x15, 112, \xreg
- POP_REGS_NE x16, x17, 128, \xreg
- POP_REGS_NE x18, x19, 144, \xreg
+ POP_REGS_NE x17, x18, 128, \xreg
+ POP_REGS_NE x19, xLR, 144, \xreg // Restore also return address.
// Restore floating-point registers.
ldp d0, d1, [sp, #160]
ldp d2, d3, [sp, #176]
@@ -2461,9 +2454,8 @@ ENTRY \name
ldp d26, d27, [sp, #304]
ldp d28, d29, [sp, #320]
ldp d30, d31, [sp, #336]
- // Restore return address and remove padding.
- RESTORE_REG xLR, 360
- DECREASE_FRAME 368
+ // Remove frame and return.
+ DECREASE_FRAME 352
ret
.Lret_forwarding_address\name:
mvn wIP0, wIP0
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 663cb6c62f..10b690ac4a 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1576,9 +1576,87 @@ END \name
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+// A hand-written override for:
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
+.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name
+ENTRY \c_name
+ # Fast path rosalloc allocation
+ # a0: type
+ # s1: Thread::Current
+ # -----------------------------
+ # t1: object size
+ # t2: rosalloc run
+ # t3: thread stack top offset
+ # t4: thread stack bottom offset
+ # v0: free list head
+ #
+ # t5, t6 : temps
+ lw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation
+ lw $t4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # stack has any room left.
+ bgeu $t3, $t4, .Lslow_path_\c_name
+
+ lw $t1, MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET($a0) # Load object size (t1).
+ li $t5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local
+ # allocation. Also does the
+ # initialized and finalizable checks.
+ bgtu $t1, $t5, .Lslow_path_\c_name
+
+ # Compute the rosalloc bracket index from the size. Since the size is already aligned we can
+ # combine the two shifts together.
+ srl $t1, $t1, (ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT)
+
+ addu $t2, $t1, $s1
+ lw $t2, (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)($t2) # Load rosalloc run (t2).
+
+ # Load the free list head (v0).
+ # NOTE: this will be the return val.
+ lw $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
+ beqz $v0, .Lslow_path_\c_name
+ nop
+
+ # Load the next pointer of the head and update the list head with the next pointer.
+ lw $t5, ROSALLOC_SLOT_NEXT_OFFSET($v0)
+ sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
+
+ # Store the class pointer in the header. This also overwrites the first pointer. The offsets are
+ # asserted to match.
+
+#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
+#error "Class pointer needs to overwrite next pointer."
+#endif
+
+ POISON_HEAP_REF $a0
+ sw $a0, MIRROR_OBJECT_CLASS_OFFSET($v0)
+
+ # Push the new object onto the thread local allocation stack and increment the thread local
+ # allocation stack top.
+ sw $v0, 0($t3)
+ addiu $t3, $t3, COMPRESSED_REFERENCE_SIZE
+ sw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)
+
+ # Decrement the size of the free list.
+ lw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
+ addiu $t5, $t5, -1
+ sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
+
+ sync # Fence.
+
+ jalr $zero, $ra
+ nop
+
+ .Lslow_path_\c_name:
+ SETUP_SAVE_REFS_ONLY_FRAME
+ la $t9, \cxx_name
+ jalr $t9
+ move $a1, $s1 # Pass self as argument.
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END \c_name
+.endm
+
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 5fee575331..fff6d256b1 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1533,8 +1533,85 @@ END \name
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
+// A hand-written override for:
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
+.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name
+ENTRY \c_name
+ # Fast path rosalloc allocation
+ # a0: type
+ # s1: Thread::Current
+ # -----------------------------
+ # t1: object size
+ # t2: rosalloc run
+ # t3: thread stack top offset
+ # a4: thread stack bottom offset
+ # v0: free list head
+ #
+ # a5, a6 : temps
+ ld $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation stack
+ ld $a4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # has any room left.
+ bgeuc $t3, $a4, .Lslow_path_\c_name
+
+ lwu $t1, MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET($a0) # Load object size (t1).
+ li $a5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local
+ # allocation. Also does the initialized
+ # and finalizable checks.
+ bltuc $a5, $t1, .Lslow_path_\c_name
+
+ # Compute the rosalloc bracket index from the size. Since the size is already aligned we can
+ # combine the two shifts together.
+ dsrl $t1, $t1, (ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT)
+
+ daddu $t2, $t1, $s1
+ ld $t2, (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)($t2) # Load rosalloc run (t2).
+
+ # Load the free list head (v0).
+ # NOTE: this will be the return val.
+ ld $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
+ beqzc $v0, .Lslow_path_\c_name
+
+ # Load the next pointer of the head and update the list head with the next pointer.
+ ld $a5, ROSALLOC_SLOT_NEXT_OFFSET($v0)
+ sd $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
+
+ # Store the class pointer in the header. This also overwrites the first pointer. The offsets are
+ # asserted to match.
+
+#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
+#error "Class pointer needs to overwrite next pointer."
+#endif
+
+ POISON_HEAP_REF $a0
+ sw $a0, MIRROR_OBJECT_CLASS_OFFSET($v0)
+
+ # Push the new object onto the thread local allocation stack and increment the thread local
+ # allocation stack top.
+ sd $v0, 0($t3)
+ daddiu $t3, $t3, COMPRESSED_REFERENCE_SIZE
+ sd $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)
+
+ # Decrement the size of the free list.
+ lw $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
+ addiu $a5, $a5, -1
+ sw $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
+
+ sync # Fence.
+
+ jalr $zero, $ra
+ .cpreturn # Restore gp from t8 in branch delay slot.
+
+.Lslow_path_\c_name:
+ SETUP_SAVE_REFS_ONLY_FRAME
+ jal \cxx_name
+ move $a1 ,$s1 # Pass self as argument.
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END \c_name
+.endm
+
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc
+
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 76615e843b..8c907e0790 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -922,6 +922,31 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
END_FUNCTION VAR(c_name)
END_MACRO
+// Macro for string and type resolution and initialization.
+MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name)
+ DEFINE_FUNCTION VAR(c_name)
+ SETUP_SAVE_EVERYTHING_FRAME ebx, ebx // save ref containing registers for GC
+ // Outgoing argument set up
+ subl MACRO_LITERAL(8), %esp // push padding
+ CFI_ADJUST_CFA_OFFSET(8)
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH eax // pass arg1
+ call CALLVAR(cxx_name) // cxx_name(arg1, Thread*)
+ addl MACRO_LITERAL(16), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-16)
+ testl %eax, %eax // If result is null, deliver the OOME.
+ jz 1f
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX // restore frame up to return address
+ ret // return
+ CFI_RESTORE_STATE
+ CFI_DEF_CFA(esp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+ END_FUNCTION VAR(c_name)
+END_MACRO
+
MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
testl %eax, %eax // eax == 0 ?
jz 1f // if eax == 0 goto 1
@@ -1245,31 +1270,10 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr
GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
-DEFINE_FUNCTION art_quick_resolve_string
- SETUP_SAVE_EVERYTHING_FRAME ebx, ebx
- // Outgoing argument set up
- subl LITERAL(8), %esp // push padding
- CFI_ADJUST_CFA_OFFSET(8)
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH eax // pass arg1
- call SYMBOL(artResolveStringFromCode)
- addl LITERAL(16), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-16)
- testl %eax, %eax // If result is null, deliver the OOME.
- jz 1f
- CFI_REMEMBER_STATE
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX
- ret
- CFI_RESTORE_STATE
- CFI_DEF_CFA(esp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598
-1:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END_FUNCTION art_quick_resolve_string
-
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-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_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index a1ae858735..f1be52eeb6 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -950,6 +950,26 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
END_FUNCTION VAR(c_name)
END_MACRO
+// Macro for string and type resolution and initialization.
+MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name)
+ DEFINE_FUNCTION VAR(c_name)
+ SETUP_SAVE_EVERYTHING_FRAME // save everything for GC
+ // Outgoing argument set up
+ movl %eax, %edi // pass string index
+ movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
+ call CALLVAR(cxx_name) // cxx_name(arg0, Thread*)
+ testl %eax, %eax // If result is null, deliver the OOME.
+ jz 1f
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX // restore frame up to return address
+ ret
+ CFI_RESTORE_STATE
+ CFI_DEF_CFA(rsp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+ END_FUNCTION VAR(c_name)
+END_MACRO
+
MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
testq %rax, %rax // rax == 0 ?
jz 1f // if rax == 0 goto 1
@@ -1270,27 +1290,10 @@ DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab
ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedRegionTLAB
END_FUNCTION art_quick_alloc_object_initialized_region_tlab
-DEFINE_FUNCTION art_quick_resolve_string
- SETUP_SAVE_EVERYTHING_FRAME
- // Outgoing argument set up
- movl %eax, %edi // pass string index
- movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
- call SYMBOL(artResolveStringFromCode) // artResolveStringFromCode(arg0, Thread*)
-
- testl %eax, %eax // If result is null, deliver the OOME.
- jz 1f
- CFI_REMEMBER_STATE
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX // restore frame up to return address
- ret
- CFI_RESTORE_STATE
- CFI_DEF_CFA(rsp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598
-1:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END_FUNCTION art_quick_resolve_string
-
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-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_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 5b1b2871c2..699cf91c70 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -53,13 +53,18 @@ static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Lock
}
}
+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, Runtime::kSaveRefsOnly);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false);
@@ -73,7 +78,7 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s
REQUIRES_SHARED(Locks::mutator_lock_) {
// Called when method->dex_cache_resolved_types_[] misses.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false);
@@ -88,7 +93,7 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type
// 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, Runtime::kSaveRefsOnly);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true);
@@ -101,11 +106,7 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type
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,
- // TODO: Change art_quick_resolve_string on MIPS and MIPS64 to kSaveEverything.
- (kRuntimeISA == kMips || kRuntimeISA == kMips64) ? Runtime::kSaveRefsOnly
- : Runtime::kSaveEverything);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
ArtMethod* caller = caller_and_outer.caller;
mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx));
if (LIKELY(result != nullptr)) {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 051f3f7b00..0a45fcedae 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3887,13 +3887,15 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) {
// blocking watermark. Ensure that only one of those threads runs the
// blocking GC. The rest of the threads should instead wait for the
// blocking GC to complete.
- if (native_blocking_gc_in_progress_) {
- do {
- native_blocking_gc_cond_->Wait(self);
- } while (native_blocking_gcs_finished_ == initial_gcs_finished);
- } else {
- native_blocking_gc_in_progress_ = true;
- run_gc = true;
+ if (native_blocking_gcs_finished_ == initial_gcs_finished) {
+ if (native_blocking_gc_in_progress_) {
+ do {
+ native_blocking_gc_cond_->Wait(self);
+ } while (native_blocking_gcs_finished_ == initial_gcs_finished);
+ } else {
+ native_blocking_gc_in_progress_ = true;
+ run_gc = true;
+ }
}
}
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 3d3ad593b3..133502e6a3 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -224,12 +224,6 @@ class EndianOutput {
HandleU1List(values, count);
length_ += count;
}
- void AddU1AsU2List(const uint8_t* values, size_t count) {
- HandleU1AsU2List(values, count);
- // Array of char from compressed String (8-bit) is added as 16-bit blocks
- int ceil_count_to_even = count + ((count & 1) ? 1 : 0);
- length_ += ceil_count_to_even * sizeof(uint8_t);
- }
void AddU2List(const uint16_t* values, size_t count) {
HandleU2List(values, count);
length_ += count * sizeof(uint16_t);
@@ -1277,7 +1271,7 @@ void Hprof::DumpHeapClass(mirror::Class* klass) {
HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), nullptr);
__ AddU1(t);
}
- // Add native value character array for strings.
+ // Add native value character array for strings / byte array for compressed strings.
if (klass->IsStringClass()) {
__ AddStringId(LookupStringId("value"));
__ AddU1(hprof_basic_object);
@@ -1359,8 +1353,16 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) {
case hprof_basic_short:
__ AddU2(f->GetShort(obj));
break;
- case hprof_basic_float:
case hprof_basic_int:
+ if (mirror::kUseStringCompression &&
+ klass->IsStringClass() &&
+ f->GetOffset().SizeValue() == mirror::String::CountOffset().SizeValue()) {
+ // Store the string length instead of the raw count field with compression flag.
+ __ AddU4(obj->AsString()->GetLength());
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case hprof_basic_float:
case hprof_basic_object:
__ AddU4(f->Get32(obj));
break;
@@ -1397,16 +1399,15 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) {
CHECK_EQ(obj->IsString(), string_value != nullptr);
if (string_value != nullptr) {
mirror::String* s = obj->AsString();
- // Compressed string's (8-bit) length is ceil(length/2) in 16-bit blocks
- int length_in_16_bit = (s->IsCompressed()) ? ((s->GetLength() + 1) / 2) : s->GetLength();
__ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
__ AddObjectId(string_value);
__ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
- __ AddU4(length_in_16_bit);
- __ AddU1(hprof_basic_char);
+ __ AddU4(s->GetLength());
if (s->IsCompressed()) {
- __ AddU1AsU2List(s->GetValueCompressed(), s->GetLength());
+ __ AddU1(hprof_basic_byte);
+ __ AddU1List(s->GetValueCompressed(), s->GetLength());
} else {
+ __ AddU1(hprof_basic_char);
__ AddU2List(s->GetValue(), s->GetLength());
}
}
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 371e2f1e65..545cc1ad42 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <cmath>
+#include <initializer_list>
#include <limits>
#include <locale>
#include <unordered_map>
@@ -883,43 +884,74 @@ void UnstartedRuntime::UnstartedSystemGetPropertyWithDefault(
GetSystemProperty(self, shadow_frame, result, arg_offset, true);
}
-void UnstartedRuntime::UnstartedThreadLocalGet(
- Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
- std::string caller(ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod()));
- bool ok = false;
- if (caller == "void java.lang.FloatingDecimal.developLongDigits(int, long, long)" ||
- caller == "java.lang.String java.lang.FloatingDecimal.toJavaFormatString()") {
- // Allocate non-threadlocal buffer.
- result->SetL(mirror::CharArray::Alloc(self, 26));
- ok = true;
- } else if (caller ==
- "java.lang.FloatingDecimal java.lang.FloatingDecimal.getThreadLocalInstance()") {
- // Allocate new object.
- StackHandleScope<2> hs(self);
- Handle<mirror::Class> h_real_to_string_class(hs.NewHandle(
- shadow_frame->GetLink()->GetMethod()->GetDeclaringClass()));
- Handle<mirror::Object> h_real_to_string_obj(hs.NewHandle(
- h_real_to_string_class->AllocObject(self)));
- if (h_real_to_string_obj.Get() != nullptr) {
- auto* cl = Runtime::Current()->GetClassLinker();
- ArtMethod* init_method = h_real_to_string_class->FindDirectMethod(
- "<init>", "()V", cl->GetImagePointerSize());
- if (init_method == nullptr) {
- h_real_to_string_class->DumpClass(LOG_STREAM(FATAL), mirror::Class::kDumpClassFullDetail);
- } else {
- JValue invoke_result;
- EnterInterpreterFromInvoke(self, init_method, h_real_to_string_obj.Get(), nullptr,
- nullptr);
- if (!self->IsExceptionPending()) {
- result->SetL(h_real_to_string_obj.Get());
- ok = true;
- }
+static std::string GetImmediateCaller(ShadowFrame* shadow_frame)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame->GetLink() == nullptr) {
+ return "<no caller>";
+ }
+ return ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod());
+}
+
+static bool CheckCallers(ShadowFrame* shadow_frame,
+ std::initializer_list<std::string> allowed_call_stack)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (const std::string& allowed_caller : allowed_call_stack) {
+ if (shadow_frame->GetLink() == nullptr) {
+ return false;
+ }
+
+ std::string found_caller = ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod());
+ if (allowed_caller != found_caller) {
+ return false;
+ }
+
+ shadow_frame = shadow_frame->GetLink();
+ }
+ return true;
+}
+
+static ObjPtr<mirror::Object> CreateInstanceOf(Thread* self, const char* class_descriptor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Find the requested class.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::Class> klass =
+ class_linker->FindClass(self, class_descriptor, ScopedNullHandle<mirror::ClassLoader>());
+ if (klass == nullptr) {
+ AbortTransactionOrFail(self, "Could not load class %s", class_descriptor);
+ return nullptr;
+ }
+
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Class> h_class(hs.NewHandle(klass));
+ Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
+ if (h_obj.Get() != nullptr) {
+ ArtMethod* init_method = h_class->FindDirectMethod(
+ "<init>", "()V", class_linker->GetImagePointerSize());
+ if (init_method == nullptr) {
+ AbortTransactionOrFail(self, "Could not find <init> for %s", class_descriptor);
+ return nullptr;
+ } else {
+ JValue invoke_result;
+ EnterInterpreterFromInvoke(self, init_method, h_obj.Get(), nullptr, nullptr);
+ if (!self->IsExceptionPending()) {
+ return h_obj.Get();
}
+ AbortTransactionOrFail(self, "Could not run <init> for %s", class_descriptor);
}
}
+ AbortTransactionOrFail(self, "Could not allocate instance of %s", class_descriptor);
+ return nullptr;
+}
- if (!ok) {
- AbortTransactionOrFail(self, "Could not create RealToString object");
+void UnstartedRuntime::UnstartedThreadLocalGet(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
+ if (CheckCallers(shadow_frame, { "sun.misc.FloatingDecimal$BinaryToASCIIBuffer "
+ "sun.misc.FloatingDecimal.getBinaryToASCIIBuffer()" })) {
+ result->SetL(CreateInstanceOf(self, "Lsun/misc/FloatingDecimal$BinaryToASCIIBuffer;"));
+ } else {
+ AbortTransactionOrFail(self,
+ "ThreadLocal.get() does not support %s",
+ GetImmediateCaller(shadow_frame).c_str());
}
}
@@ -1252,12 +1284,12 @@ void UnstartedRuntime::UnstartedReferenceGetReferent(
// initialization of other classes, so will *use* the value.
void UnstartedRuntime::UnstartedRuntimeAvailableProcessors(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
- std::string caller(ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod()));
- if (caller == "void java.util.concurrent.SynchronousQueue.<clinit>()") {
+ if (CheckCallers(shadow_frame, { "void java.util.concurrent.SynchronousQueue.<clinit>()" })) {
// SynchronousQueue really only separates between single- and multiprocessor case. Return
// 8 as a conservative upper approximation.
result->SetI(8);
- } else if (caller == "void java.util.concurrent.ConcurrentHashMap.<clinit>()") {
+ } else if (CheckCallers(shadow_frame,
+ { "void java.util.concurrent.ConcurrentHashMap.<clinit>()" })) {
// ConcurrentHashMap uses it for striding. 8 still seems an OK general value, as it's likely
// a good upper bound.
// TODO: Consider resetting in the zygote?
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index ae55f4c2ef..31be587e9c 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -944,5 +944,100 @@ TEST_F(UnstartedRuntimeTest, GetDeclaringClass) {
ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
}
+TEST_F(UnstartedRuntimeTest, ThreadLocalGet) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ JValue result;
+ ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ StackHandleScope<1> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ // Positive test. See that We get something for float conversion.
+ {
+ Handle<mirror::Class> floating_decimal = hs.NewHandle(
+ class_linker->FindClass(self,
+ "Lsun/misc/FloatingDecimal;",
+ ScopedNullHandle<mirror::ClassLoader>()));
+ ASSERT_TRUE(floating_decimal.Get() != nullptr);
+ ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true));
+
+ ArtMethod* caller_method = floating_decimal->FindDeclaredDirectMethod(
+ "getBinaryToASCIIBuffer",
+ "()Lsun/misc/FloatingDecimal$BinaryToASCIIBuffer;",
+ class_linker->GetImagePointerSize());
+ // floating_decimal->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
+ ASSERT_TRUE(caller_method != nullptr);
+ ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0);
+ shadow_frame->SetLink(caller_frame);
+
+ UnstartedThreadLocalGet(self, shadow_frame, &result, 0);
+ EXPECT_TRUE(result.GetL() != nullptr);
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ ShadowFrame::DeleteDeoptimizedFrame(caller_frame);
+ }
+
+ // Negative test.
+ PrepareForAborts();
+
+ {
+ // Just use a method in Class.
+ ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
+ ArtMethod* caller_method =
+ &*class_class->GetDeclaredMethods(class_linker->GetImagePointerSize()).begin();
+ ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0);
+ shadow_frame->SetLink(caller_frame);
+
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedThreadLocalGet(self, shadow_frame, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ self->ClearException();
+
+ ShadowFrame::DeleteDeoptimizedFrame(caller_frame);
+ }
+
+ ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
+TEST_F(UnstartedRuntimeTest, FloatConversion) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ StackHandleScope<1> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::Class> double_class = hs.NewHandle(
+ class_linker->FindClass(self,
+ "Ljava/lang/Double;",
+ ScopedNullHandle<mirror::ClassLoader>()));
+ ASSERT_TRUE(double_class.Get() != nullptr);
+ ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true));
+
+ ArtMethod* method = double_class->FindDeclaredDirectMethod("toString",
+ "(D)Ljava/lang/String;",
+ class_linker->GetImagePointerSize());
+ ASSERT_TRUE(method != nullptr);
+
+ // create instruction data for invoke-direct {v0, v1} of method with fake index
+ uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
+ const Instruction* inst = Instruction::At(inst_data);
+
+ JValue result;
+ ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, method, 0);
+ shadow_frame->SetVRegDouble(0, 1.23);
+ interpreter::DoCall<false, false>(method, self, *shadow_frame, inst, inst_data[0], &result);
+ ObjPtr<mirror::String> string_result = reinterpret_cast<mirror::String*>(result.GetL());
+ ASSERT_TRUE(string_result != nullptr);
+
+ std::string mod_utf = string_result->ToModifiedUtf8();
+ EXPECT_EQ("1.23", mod_utf);
+
+ ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
} // namespace interpreter
} // namespace art
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 1405c40096..9ba2d1a355 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -597,6 +597,24 @@ uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
return total;
}
+// Produce a non-owning vector from a vector.
+template<typename T>
+const std::vector<T*>* MakeNonOwningVector(const std::vector<std::unique_ptr<T>>* owning_vector) {
+ auto non_owning_vector = new std::vector<T*>();
+ for (auto& element : *owning_vector) {
+ non_owning_vector->push_back(element.get());
+ }
+ return non_owning_vector;
+}
+
+std::string ProfileCompilationInfo::DumpInfo(
+ const std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ bool print_full_dex_location) const {
+ std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
+ MakeNonOwningVector(dex_files));
+ return DumpInfo(non_owning_dex_files.get(), print_full_dex_location);
+}
+
std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location) const {
std::ostringstream os;
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index f8061bcfd8..b1587c0070 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
#define ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
+#include <memory>
#include <set>
#include <vector>
@@ -72,6 +73,8 @@ class ProfileCompilationInfo {
// If dex_files is not null then the method indices will be resolved to their
// names.
// This is intended for testing and debugging.
+ std::string DumpInfo(const std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ bool print_full_dex_location = true) const;
std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location = true) const;
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 95b6c3e76b..409c6c2896 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -241,8 +241,9 @@ class MANAGED String FINAL : public Object {
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
- // First bit (uppermost/leftmost) is taken out for Compressed/Uncompressed flag
- // [0] Uncompressed: string uses 16-bit memory | [1] Compressed: 8-bit memory
+
+ // If string compression is enabled, count_ holds the StringCompressionFlag in the
+ // least significant bit and the length in the remaining bits, length = count_ >> 1.
int32_t count_;
uint32_t hash_code_;
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index 00d4144415..f8f8fa6b25 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -78,7 +78,9 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime
if (art::kIsDebugBuild) {
std::string name;
self->GetThreadName(name);
- if (name != "Signal Catcher" && !android::base::StartsWith(name, "Jit thread pool")) {
+ if (name != "JDWP" &&
+ name != "Signal Catcher" &&
+ !android::base::StartsWith(name, "Jit thread pool")) {
LOG(FATAL) << "Unexpected thread before start: " << name;
}
}
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index d13436ebf6..e659ea3bfb 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -17,9 +17,14 @@
#include <stdio.h>
#include "base/macros.h"
+#include "class_linker.h"
#include "jni.h"
+#include "mirror/class_loader.h"
#include "openjdkjvmti/jvmti.h"
+#include "runtime.h"
#include "ScopedLocalRef.h"
+#include "ScopedUtfChars.h"
+#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "ti-agent/common_helper.h"
@@ -278,69 +283,11 @@ static std::string GetClassName(jvmtiEnv* jenv, JNIEnv* jni_env, jclass klass) {
return tmp;
}
-static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) {
- jvmtiThreadInfo info;
- jvmtiError result = jenv->GetThreadInfo(thread, &info);
- if (result != JVMTI_ERROR_NONE) {
- if (jni_env != nullptr) {
- JvmtiErrorToException(jni_env, result);
- } else {
- printf("Failed to get thread name.\n");
- }
- return "";
- }
-
- std::string tmp(info.name);
- jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name));
- jni_env->DeleteLocalRef(info.context_class_loader);
- jni_env->DeleteLocalRef(info.thread_group);
-
- return tmp;
-}
-
-static std::string GetThreadName(Thread* thread) {
- std::string tmp;
- thread->GetThreadName(tmp);
- return tmp;
-}
-
-static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
- JNIEnv* jni_env,
- jthread thread,
- jclass klass) {
- std::string name = GetClassName(jenv, jni_env, klass);
- if (name == "") {
- return;
- }
- std::string thread_name = GetThreadName(jenv, jni_env, thread);
- if (thread_name == "") {
- return;
- }
- std::string cur_thread_name = GetThreadName(Thread::Current());
- printf("Prepare: %s on %s (cur=%s)\n",
- name.c_str(),
- thread_name.c_str(),
- cur_thread_name.c_str());
-}
-
-static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
- JNIEnv* jni_env,
- jthread thread,
- jclass klass) {
- std::string name = GetClassName(jenv, jni_env, klass);
- if (name == "") {
- return;
- }
- std::string thread_name = GetThreadName(jenv, jni_env, thread);
- if (thread_name == "") {
- return;
- }
- printf("Load: %s on %s\n", name.c_str(), thread_name.c_str());
-}
-
-extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadEvents(
- JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
- if (b == JNI_FALSE) {
+static void EnableEvents(JNIEnv* env,
+ jboolean enable,
+ decltype(jvmtiEventCallbacks().ClassLoad) class_load,
+ decltype(jvmtiEventCallbacks().ClassPrepare) class_prepare) {
+ if (enable == JNI_FALSE) {
jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
JVMTI_EVENT_CLASS_LOAD,
nullptr);
@@ -356,8 +303,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadEvents(
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
- callbacks.ClassLoad = ClassLoadCallback;
- callbacks.ClassPrepare = ClassPrepareCallback;
+ callbacks.ClassLoad = class_load;
+ callbacks.ClassPrepare = class_prepare;
jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
if (JvmtiErrorToException(env, ret)) {
return;
@@ -375,5 +322,113 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadEvents(
JvmtiErrorToException(env, ret);
}
+class ClassLoadPreparePrinter {
+ public:
+ static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
+ JNIEnv* jni_env,
+ jthread thread,
+ jclass klass) {
+ std::string name = GetClassName(jenv, jni_env, klass);
+ if (name == "") {
+ return;
+ }
+ std::string thread_name = GetThreadName(jenv, jni_env, thread);
+ if (thread_name == "") {
+ return;
+ }
+ printf("Load: %s on %s\n", name.c_str(), thread_name.c_str());
+ }
+
+ static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
+ JNIEnv* jni_env,
+ jthread thread,
+ jclass klass) {
+ std::string name = GetClassName(jenv, jni_env, klass);
+ if (name == "") {
+ return;
+ }
+ std::string thread_name = GetThreadName(jenv, jni_env, thread);
+ if (thread_name == "") {
+ return;
+ }
+ std::string cur_thread_name = GetThreadName(Thread::Current());
+ printf("Prepare: %s on %s (cur=%s)\n",
+ name.c_str(),
+ thread_name.c_str(),
+ cur_thread_name.c_str());
+ }
+
+ private:
+ static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) {
+ jvmtiThreadInfo info;
+ jvmtiError result = jenv->GetThreadInfo(thread, &info);
+ if (result != JVMTI_ERROR_NONE) {
+ if (jni_env != nullptr) {
+ JvmtiErrorToException(jni_env, result);
+ } else {
+ printf("Failed to get thread name.\n");
+ }
+ return "";
+ }
+
+ std::string tmp(info.name);
+ jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name));
+ jni_env->DeleteLocalRef(info.context_class_loader);
+ jni_env->DeleteLocalRef(info.thread_group);
+
+ return tmp;
+ }
+
+ static std::string GetThreadName(Thread* thread) {
+ std::string tmp;
+ thread->GetThreadName(tmp);
+ return tmp;
+ }
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadPreparePrintEvents(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean enable) {
+ EnableEvents(env,
+ enable,
+ ClassLoadPreparePrinter::ClassLoadCallback,
+ ClassLoadPreparePrinter::ClassPrepareCallback);
+}
+
+struct ClassLoadSeen {
+ static void JNICALL ClassLoadSeenCallback(jvmtiEnv* jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED,
+ jthread thread ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED) {
+ saw_event = true;
+ }
+
+ static bool saw_event;
+};
+bool ClassLoadSeen::saw_event = false;
+
+extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadSeenEvents(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
+ EnableEvents(env, b, ClassLoadSeen::ClassLoadSeenCallback, nullptr);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hadLoadEvent(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED) {
+ return ClassLoadSeen::saw_event ? JNI_TRUE : JNI_FALSE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isLoadedClass(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring class_name) {
+ ScopedUtfChars name(env, class_name);
+ ScopedObjectAccess soa(Thread::Current());
+ Runtime* current = Runtime::Current();
+ ClassLinker* class_linker = current->GetClassLinker();
+ bool found =
+ class_linker->LookupClass(
+ soa.Self(),
+ name.c_str(),
+ soa.Decode<mirror::ClassLoader>(current->GetSystemClassLoader())) != nullptr;
+ return found ? JNI_TRUE : JNI_FALSE;
+}
+
} // namespace Test912Classes
} // namespace art
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 6ad23a4869..e3aceb9a5f 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -219,6 +219,15 @@ public class Main {
}
final ClassLoader boot = cl;
+ // The JIT may deeply inline and load some classes. Preload these for test determinism.
+ final String PRELOAD_FOR_JIT[] = {
+ "java.nio.charset.CoderMalfunctionError",
+ "java.util.NoSuchElementException"
+ };
+ for (String s : PRELOAD_FOR_JIT) {
+ Class.forName(s);
+ }
+
Runnable r = new Runnable() {
@Override
public void run() {
@@ -238,7 +247,7 @@ public class Main {
ensureJitCompiled(Main.class, "testClassEvents");
- enableClassLoadEvents(true);
+ enableClassLoadPreparePrintEvents(true);
ClassLoader cl1 = create(boot, DEX1, DEX2);
System.out.println("B, false");
@@ -270,7 +279,37 @@ public class Main {
t.start();
t.join();
- enableClassLoadEvents(false);
+ enableClassLoadPreparePrintEvents(false);
+
+ // Note: the JIT part of this test is about the JIT pulling in a class not yet touched by
+ // anything else in the system. This could be the verifier or the interpreter. We
+ // block the interpreter by calling ensureJitCompiled. The verifier, however, must
+ // 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("Main$ClassD")) {
+ testClassEventsJit();
+ }
+ }
+
+ private static void testClassEventsJit() throws Exception {
+ enableClassLoadSeenEvents(true);
+
+ testClassEventsJitImpl();
+
+ enableClassLoadSeenEvents(false);
+
+ if (!hadLoadEvent()) {
+ throw new RuntimeException("Did not get expected load event.");
+ }
+ }
+
+ private static void testClassEventsJitImpl() throws Exception {
+ ensureJitCompiled(Main.class, "testClassEventsJitImpl");
+
+ if (ClassD.x != 1) {
+ throw new RuntimeException("Unexpected value");
+ }
}
private static void printClassLoaderClasses(ClassLoader cl) {
@@ -335,9 +374,14 @@ public class Main {
private static native int[] getClassVersion(Class<?> c);
- private static native void enableClassLoadEvents(boolean b);
+ private static native void enableClassLoadPreparePrintEvents(boolean b);
+
+ private static native void ensureJitCompiled(Class<?> c, String name);
- private static native void ensureJitCompiled(Class c, String name);
+ private static native boolean hasJit();
+ private static native boolean isLoadedClass(String name);
+ private static native void enableClassLoadSeenEvents(boolean b);
+ private static native boolean hadLoadEvent();
private static class TestForNonInit {
public static double dummy = Math.random(); // So it can't be compile-time initialized.
@@ -361,6 +405,10 @@ public class Main {
public abstract static class ClassC implements InfA, InfC {
}
+ public static class ClassD {
+ static int x = 1;
+ }
+
private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar";
private static final String DEX2 = System.getenv("DEX_LOCATION") + "/912-classes-ex.jar";
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index b937c931e7..1938b92db8 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -540,7 +540,6 @@ TEST_ART_BROKEN_JIT_RUN_TESTS := \
629-vdex-speed \
904-object-allocation \
906-iterate-heap \
- 912-classes \
ifneq (,$(filter jit,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index 94934a2831..a062afdaef 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -26,6 +26,7 @@ import com.android.tools.perflib.heap.RootObj;
import com.android.tools.perflib.heap.Type;
import java.awt.image.BufferedImage;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -87,22 +88,27 @@ class InstanceUtils {
// is a char[], use that directly as the value, otherwise use the value
// field of the string object. The field accesses for count and offset
// later on will work okay regardless of what type the inst object is.
- Object value = inst;
- if (isInstanceOfClass(inst, "java.lang.String")) {
- value = getField(inst, "value");
- }
+ boolean isString = isInstanceOfClass(inst, "java.lang.String");
+ Object value = isString ? getField(inst, "value") : inst;
if (!(value instanceof ArrayInstance)) {
return null;
}
ArrayInstance chars = (ArrayInstance) value;
+ int numChars = chars.getLength();
+ int offset = getIntField(inst, "offset", 0);
+ int count = getIntField(inst, "count", numChars);
+
+ // With string compression enabled, the array type can be BYTE but in that case
+ // offset must be 0 and count must match numChars.
+ if (isString && (chars.getArrayType() == Type.BYTE) && (offset == 0) && (count == numChars)) {
+ int length = (0 <= maxChars && maxChars < numChars) ? maxChars : numChars;
+ return new String(chars.asRawByteArray(/* offset */ 0, length), StandardCharsets.US_ASCII);
+ }
if (chars.getArrayType() != Type.CHAR) {
return null;
}
-
- int numChars = chars.getLength();
- int count = getIntField(inst, "count", numChars);
if (count == 0) {
return "";
}
@@ -110,7 +116,6 @@ class InstanceUtils {
count = maxChars;
}
- int offset = getIntField(inst, "offset", 0);
int end = offset + count - 1;
if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
return new String(chars.asCharArray(offset, count));
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
index e08df67b13..587d9defef 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/test-dump/Main.java
@@ -45,6 +45,8 @@ public class Main {
// class and reading the desired field.
public static class DumpedStuff {
public String basicString = "hello, world";
+ public String nonAscii = "Sigma (\u01a9) is not ASCII";
+ public String embeddedZero = "embedded\0..."; // Non-ASCII for string compression purposes.
public char[] charArray = "char thing".toCharArray();
public String nullString = null;
public Object anObject = new Object();
diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java
index ec77e70da1..fe2706d7d4 100644
--- a/tools/ahat/test/InstanceUtilsTest.java
+++ b/tools/ahat/test/InstanceUtilsTest.java
@@ -37,6 +37,20 @@ public class InstanceUtilsTest {
}
@Test
+ public void asStringNonAscii() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("nonAscii");
+ assertEquals("Sigma (\u01a9) is not ASCII", InstanceUtils.asString(str));
+ }
+
+ @Test
+ public void asStringEmbeddedZero() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("embeddedZero");
+ assertEquals("embedded\0...", InstanceUtils.asString(str));
+ }
+
+ @Test
public void asStringCharArray() throws IOException {
TestDump dump = TestDump.getTestDump();
Instance str = (Instance)dump.getDumpedThing("charArray");
@@ -51,6 +65,20 @@ public class InstanceUtilsTest {
}
@Test
+ public void asStringTruncatedNonAscii() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("nonAscii");
+ assertEquals("Sigma (\u01a9)", InstanceUtils.asString(str, 9));
+ }
+
+ @Test
+ public void asStringTruncatedEmbeddedZero() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("embeddedZero");
+ assertEquals("embed", InstanceUtils.asString(str, 5));
+ }
+
+ @Test
public void asStringCharArrayTruncated() throws IOException {
TestDump dump = TestDump.getTestDump();
Instance str = (Instance)dump.getDumpedThing("charArray");
@@ -65,6 +93,20 @@ public class InstanceUtilsTest {
}
@Test
+ public void asStringExactMaxNonAscii() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("nonAscii");
+ assertEquals("Sigma (\u01a9) is not ASCII", InstanceUtils.asString(str, 22));
+ }
+
+ @Test
+ public void asStringExactMaxEmbeddedZero() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("embeddedZero");
+ assertEquals("embedded\0...", InstanceUtils.asString(str, 12));
+ }
+
+ @Test
public void asStringCharArrayExactMax() throws IOException {
TestDump dump = TestDump.getTestDump();
Instance str = (Instance)dump.getDumpedThing("charArray");
@@ -79,6 +121,20 @@ public class InstanceUtilsTest {
}
@Test
+ public void asStringNotTruncatedNonAscii() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("nonAscii");
+ assertEquals("Sigma (\u01a9) is not ASCII", InstanceUtils.asString(str, 50));
+ }
+
+ @Test
+ public void asStringNotTruncatedEmbeddedZero() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("embeddedZero");
+ assertEquals("embedded\0...", InstanceUtils.asString(str, 50));
+ }
+
+ @Test
public void asStringCharArrayNotTruncated() throws IOException {
TestDump dump = TestDump.getTestDump();
Instance str = (Instance)dump.getDumpedThing("charArray");
@@ -93,6 +149,20 @@ public class InstanceUtilsTest {
}
@Test
+ public void asStringNegativeMaxNonAscii() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("nonAscii");
+ assertEquals("Sigma (\u01a9) is not ASCII", InstanceUtils.asString(str, -3));
+ }
+
+ @Test
+ public void asStringNegativeMaxEmbeddedZero() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("embeddedZero");
+ assertEquals("embedded\0...", InstanceUtils.asString(str, -3));
+ }
+
+ @Test
public void asStringCharArrayNegativeMax() throws IOException {
TestDump dump = TestDump.getTestDump();
Instance str = (Instance)dump.getDumpedThing("charArray");
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 41faa69c31..729a3e5ac4 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -123,7 +123,7 @@ done
vogar_args="$vogar_args --timeout 480"
# Use Jack with "1.8" configuration.
-vogar_args="$vogar_args --toolchain jack --language JN"
+vogar_args="$vogar_args --toolchain jack --language JO"
# JIT settings.
if $use_jit; then