diff options
| -rw-r--r-- | cmdline/cmdline_parser_test.cc | 4 | ||||
| -rw-r--r-- | compiler/optimizing/graph_visualizer.cc | 12 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_mips64.cc | 104 | ||||
| -rw-r--r-- | dex2oat/dex2oat.cc | 9 | ||||
| -rw-r--r-- | oatdump/oatdump.cc | 2 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 15 | ||||
| -rw-r--r-- | runtime/class_linker.h | 5 | ||||
| -rw-r--r-- | runtime/common_runtime_test.cc | 3 | ||||
| -rw-r--r-- | runtime/gc/collector/concurrent_copying.cc | 19 | ||||
| -rw-r--r-- | runtime/mirror/object-inl.h | 12 | ||||
| -rw-r--r-- | runtime/mirror/object.h | 6 | ||||
| -rw-r--r-- | runtime/parsed_options.cc | 8 | ||||
| -rw-r--r-- | runtime/thread_list.cc | 7 |
13 files changed, 169 insertions, 37 deletions
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 34fb790ecd..529143d93d 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -458,9 +458,9 @@ TEST_F(CmdlineParserTest, TestJitOptions) { } { EXPECT_SINGLE_PARSE_VALUE( - MemoryKiB(16 * KB), "-Xjitcodecacheinitialcapacity:16K", M::JITCodeCacheInitialCapacity); + MemoryKiB(16 * KB), "-Xjitinitialsize:16K", M::JITCodeCacheInitialCapacity); EXPECT_SINGLE_PARSE_VALUE( - MemoryKiB(16 * MB), "-Xjitcodecacheinitialcapacity:16M", M::JITCodeCacheInitialCapacity); + MemoryKiB(16 * MB), "-Xjitmaxsize:16M", M::JITCodeCacheMaxCapacity); } { EXPECT_SINGLE_PARSE_VALUE(12345u, "-Xjitthreshold:12345", M::JITCompileThreshold); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index af3ecb1031..d166d0061f 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -503,6 +503,18 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; } else if (instruction->IsLoadClass()) { StartAttributeStream("klass") << "unresolved"; + } else if (instruction->IsNullConstant()) { + // The NullConstant may be added to the graph during other passes that happen between + // ReferenceTypePropagation and Inliner (e.g. InstructionSimplifier). If the inliner + // doesn't run or doesn't inline anything, the NullConstant remains untyped. + // So we should check NullConstants for validity only after reference type propagation. + // + // Note: The infrastructure to properly type NullConstants everywhere is to complex to add + // for the benefits. + StartAttributeStream("klass") << "not_set"; + DCHECK(!is_after_pass_ + || !IsPass(ReferenceTypePropagation::kReferenceTypePropagationPassName)) + << " Expected a valid rti after reference type propagation"; } else { DCHECK(!is_after_pass_) << "Expected a valid rti after reference type propagation"; diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index ff843ebb1e..3654159f83 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1391,6 +1391,108 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +// boolean java.lang.String.equals(Object anObject) +void IntrinsicLocationsBuilderMIPS64::VisitStringEquals(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + + // Temporary registers to store lengths of strings and for calculations. + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringEquals(HInvoke* invoke) { + Mips64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + GpuRegister str = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister arg = locations->InAt(1).AsRegister<GpuRegister>(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + + GpuRegister temp1 = locations->GetTemp(0).AsRegister<GpuRegister>(); + GpuRegister temp2 = locations->GetTemp(1).AsRegister<GpuRegister>(); + GpuRegister temp3 = locations->GetTemp(2).AsRegister<GpuRegister>(); + + Label loop; + Label end; + Label return_true; + Label return_false; + + // Get offsets of count, value, and class fields within a string object. + const int32_t count_offset = mirror::String::CountOffset().Int32Value(); + const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); + const int32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + // If the register containing the pointer to "this", and the register + // containing the pointer to "anObject" are the same register then + // "this", and "anObject" are the same object and we can + // short-circuit the logic to a true result. + if (str == arg) { + __ LoadConst64(out, 1); + return; + } + + // Check if input is null, return false if it is. + __ Beqzc(arg, &return_false); + + // Reference equality check, return true if same reference. + __ Beqc(str, arg, &return_true); + + // Instanceof check for the argument by comparing class fields. + // All string objects must have the same type since String cannot be subclassed. + // Receiver must be a string object, so its class field is equal to all strings' class fields. + // If the argument is a string object, its class field must be equal to receiver's class field. + __ Lw(temp1, str, class_offset); + __ Lw(temp2, arg, class_offset); + __ Bnec(temp1, temp2, &return_false); + + // Load lengths of this and argument strings. + __ Lw(temp1, str, count_offset); + __ Lw(temp2, arg, count_offset); + // Check if lengths are equal, return false if they're not. + __ Bnec(temp1, temp2, &return_false); + // Return true if both strings are empty. + __ Beqzc(temp1, &return_true); + + // Don't overwrite input registers + __ Move(TMP, str); + __ Move(temp3, arg); + + // Assertions that must hold in order to compare strings 4 characters at a time. + DCHECK_ALIGNED(value_offset, 8); + static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded"); + + // Loop to compare strings 4 characters at a time starting at the beginning of the string. + // Ok to do this because strings are zero-padded to be 8-byte aligned. + __ Bind(&loop); + __ Ld(out, TMP, value_offset); + __ Ld(temp2, temp3, value_offset); + __ Bnec(out, temp2, &return_false); + __ Daddiu(TMP, TMP, 8); + __ Daddiu(temp3, temp3, 8); + __ Addiu(temp1, temp1, -4); + __ Bgtzc(temp1, &loop); + + // Return true and exit the function. + // If loop does not result in returning false, we return true. + __ Bind(&return_true); + __ LoadConst64(out, 1); + __ B(&end); + + // Return false and exit the function. + __ Bind(&return_false); + __ LoadConst64(out, 0); + __ Bind(&end); +} + static void GenerateStringIndexOf(HInvoke* invoke, Mips64Assembler* assembler, CodeGeneratorMIPS64* codegen, @@ -1586,8 +1688,6 @@ void IntrinsicCodeGeneratorMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(StringEquals) - UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 68cf6d9233..89c2a7cbdf 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1231,6 +1231,7 @@ class Dex2Oat FINAL { // Handle and ClassLoader creation needs to come after Runtime::Create jobject class_loader = nullptr; + jobject class_path_class_loader = nullptr; Thread* self = Thread::Current(); if (!boot_image_option_.empty()) { @@ -1248,10 +1249,12 @@ class Dex2Oat FINAL { key_value_store_->Put(OatHeader::kClassPathKey, OatFile::EncodeDexFileDependencies(class_path_files)); - // Then the dex files we'll compile. Thus we'll resolve the class-path first. - class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end()); + class_path_class_loader = class_linker->CreatePathClassLoader(self, + class_path_files, + nullptr); - class_loader = class_linker->CreatePathClassLoader(self, class_path_files); + // Class path loader as parent so that we'll resolve there first. + class_loader = class_linker->CreatePathClassLoader(self, dex_files_, class_path_class_loader); } driver_.reset(new CompilerDriver(compiler_options_.get(), diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index cd83de6265..94eb82b054 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -2412,7 +2412,7 @@ static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOpti // Need a class loader. // Fake that we're a compiler. - jobject class_loader = class_linker->CreatePathClassLoader(self, class_path); + jobject class_loader = class_linker->CreatePathClassLoader(self, class_path, /*parent*/nullptr); // Use the class loader while dumping. StackHandleScope<1> scope(self); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index d5a5ea6797..2dd2a83888 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -6629,7 +6629,9 @@ bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) { } } -jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) { +jobject ClassLinker::CreatePathClassLoader(Thread* self, + std::vector<const DexFile*>& dex_files, + jobject parent_loader) { // SOAAlreadyRunnable is protected, and we need something to add a global reference. // We could move the jobject to the callers, but all call-sites do this... ScopedObjectAccessUnchecked soa(self); @@ -6660,8 +6662,8 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFi for (const DexFile* dex_file : dex_files) { StackHandleScope<3> hs2(self); - // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the - // oat file but we can leave it null. + // CreatePathClassLoader is only used by gtests and dex2oat. Index 0 of h_long_array is + // supposed to be the oat file but we can leave it null. Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc( self, kDexFileIndexStart + 1)); @@ -6707,9 +6709,10 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFi mirror::Class::FindField(self, hs.NewHandle(h_path_class_loader->GetClass()), "parent", "Ljava/lang/ClassLoader;"); DCHECK(parent_field != nullptr); - mirror::Object* boot_cl = - soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self); - parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl); + mirror::Object* parent = (parent_loader != nullptr) + ? soa.Decode<mirror::ClassLoader*>(parent_loader) + : soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self); + parent_field->SetObject<false>(h_path_class_loader.Get(), parent); // Make it a global ref and return. ScopedLocalRef<jobject> local_ref( diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 5ba9652c94..29aac312c1 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -514,7 +514,10 @@ class ClassLinker { // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files. // Note: the objects are not completely set up. Do not use this outside of tests and the compiler. - jobject CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) + // If parent_loader is null then we use the boot class loader. + jobject CreatePathClassLoader(Thread* self, + std::vector<const DexFile*>& dex_files, + jobject parent_loader) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index b6b514177a..f705a50d55 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -553,7 +553,8 @@ jobject CommonRuntimeTest::LoadDex(const char* dex_name) { Thread* self = Thread::Current(); jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self, - class_path); + class_path, + nullptr); self->SetClassLoaderOverride(class_loader); return class_loader; } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 1cd7983116..bcfcb89e62 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -1080,7 +1080,7 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())))) { // Leave this Reference gray in the queue so that GetReferent() will trigger a read barrier. We // will change it to black or white later in ReferenceQueue::DequeuePendingReference(). - CHECK(to_ref->AsReference()->IsEnqueued()) << "Left unenqueued ref gray " << to_ref; + DCHECK(to_ref->AsReference()->IsEnqueued()) << "Left unenqueued ref gray " << to_ref; } else { // We may occasionally leave a Reference black or white in the queue if its referent happens to // be concurrently marked after the Scan() call above has enqueued the Reference, in which case @@ -1089,9 +1089,10 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { if (kUseBakerReadBarrier) { if (region_space_->IsInToSpace(to_ref)) { // If to-space, change from gray to white. - bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), - ReadBarrier::WhitePtr()); - CHECK(success) << "Must succeed as we won the race."; + bool success = to_ref->AtomicSetReadBarrierPointer</*kCasRelease*/true>( + ReadBarrier::GrayPtr(), + ReadBarrier::WhitePtr()); + DCHECK(success) << "Must succeed as we won the race."; DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr()); } else { // If non-moving space/unevac from space, change from gray @@ -1101,9 +1102,10 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { // indicate non-moving objects that have been marked // through. Note we'd need to change from black to white // later (concurrently). - bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), - ReadBarrier::BlackPtr()); - CHECK(success) << "Must succeed as we won the race."; + bool success = to_ref->AtomicSetReadBarrierPointer</*kCasRelease*/true>( + ReadBarrier::GrayPtr(), + ReadBarrier::BlackPtr()); + DCHECK(success) << "Must succeed as we won the race."; DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr()); } } @@ -1227,9 +1229,6 @@ class ConcurrentCopyingClearBlackPtrsVisitor { public: explicit ConcurrentCopyingClearBlackPtrsVisitor(ConcurrentCopying* cc) : collector_(cc) {} -#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER - NO_RETURN -#endif void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) SHARED_REQUIRES(Locks::heap_bitmap_lock_) { DCHECK(obj != nullptr); diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 5c12091ecb..460342807a 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -163,6 +163,7 @@ inline void Object::SetReadBarrierPointer(Object* rb_ptr) { #endif } +template<bool kCasRelease> inline bool Object::AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr) { #ifdef USE_BAKER_READ_BARRIER DCHECK(kUseBakerReadBarrier); @@ -181,10 +182,13 @@ inline bool Object::AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* static_cast<uint32_t>(reinterpret_cast<uintptr_t>(expected_rb_ptr))); new_lw = lw; new_lw.SetReadBarrierState(static_cast<uint32_t>(reinterpret_cast<uintptr_t>(rb_ptr))); - // This CAS is a CAS release so that when GC updates all the fields of an object and then - // changes the object from gray to black, the field updates (stores) will be visible (won't be - // reordered after this CAS.) - } while (!CasLockWordWeakRelease(expected_lw, new_lw)); + // ConcurrentCopying::ProcessMarkStackRef uses this with kCasRelease == true. + // If kCasRelease == true, use a CAS release so that when GC updates all the fields of + // an object and then changes the object from gray to black, the field updates (stores) will be + // visible (won't be reordered after this CAS.) + } while (!(kCasRelease ? + CasLockWordWeakRelease(expected_lw, new_lw) : + CasLockWordWeakRelaxed(expected_lw, new_lw))); return true; #elif USE_BROOKS_READ_BARRIER DCHECK(kUseBrooksReadBarrier); diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 5c6520fcab..71e704e704 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -92,13 +92,13 @@ class MANAGED LOCKABLE Object { void SetClass(Class* new_klass) SHARED_REQUIRES(Locks::mutator_lock_); Object* GetReadBarrierPointer() SHARED_REQUIRES(Locks::mutator_lock_); + #ifndef USE_BAKER_OR_BROOKS_READ_BARRIER NO_RETURN #endif void SetReadBarrierPointer(Object* rb_ptr) SHARED_REQUIRES(Locks::mutator_lock_); -#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER - NO_RETURN -#endif + + template<bool kCasRelease = false> ALWAYS_INLINE bool AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr) SHARED_REQUIRES(Locks::mutator_lock_); void AssertReadBarrierPointer() const SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 2e1fc95652..dfd783b988 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -152,10 +152,10 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .WithType<bool>() .WithValueMap({{"false", false}, {"true", true}}) .IntoKey(M::UseJIT) - .Define("-Xjitcodecacheinitialcapacity:_") + .Define("-Xjitinitialsize:_") .WithType<MemoryKiB>() .IntoKey(M::JITCodeCacheInitialCapacity) - .Define("-Xjitcodecachemaxcapacity:_") + .Define("-Xjitmaxsize:_") .WithType<MemoryKiB>() .IntoKey(M::JITCodeCacheMaxCapacity) .Define("-Xjitthreshold:_") @@ -643,7 +643,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -XX:ForegroundHeapGrowthMultiplier=doublevalue\n"); UsageMessage(stream, " -XX:LowMemoryMode\n"); UsageMessage(stream, " -Xprofile:{threadcpuclock,wallclock,dualclock}\n"); - UsageMessage(stream, " -Xjitcodecachesize:N\n"); UsageMessage(stream, " -Xjitthreshold:integervalue\n"); UsageMessage(stream, "\n"); @@ -687,6 +686,8 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n"); UsageMessage(stream, " -Xpatchoat:filename\n"); UsageMessage(stream, " -Xusejit:booleanvalue\n"); + UsageMessage(stream, " -Xjitinitialsize:N\n"); + UsageMessage(stream, " -Xjitmaxsize:N\n"); UsageMessage(stream, " -X[no]relocate\n"); UsageMessage(stream, " -X[no]dex2oat (Whether to invoke dex2oat on the application)\n"); UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n"); @@ -721,6 +722,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xjitblocking\n"); UsageMessage(stream, " -Xjitmethod:signature[,signature]* (eg Ljava/lang/String\\;replace)\n"); UsageMessage(stream, " -Xjitclass:classname[,classname]*\n"); + UsageMessage(stream, " -Xjitcodecachesize:N\n"); UsageMessage(stream, " -Xjitoffset:offset[,offset]\n"); UsageMessage(stream, " -Xjitconfig:filename\n"); UsageMessage(stream, " -Xjitcheckcg\n"); diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index b09b87fb58..a390908635 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -948,7 +948,12 @@ void ThreadList::SuspendAllForDebugger() { Locks::mutator_lock_->ExclusiveLock(self); Locks::mutator_lock_->ExclusiveUnlock(self); #endif - AssertThreadsAreSuspended(self, self, debug_thread); + // Disabled for the following race condition: + // Thread 1 calls SuspendAllForDebugger, gets preempted after pulsing the mutator lock. + // Thread 2 calls SuspendAll and SetStateUnsafe (perhaps from Dbg::Disconnected). + // Thread 1 fails assertion that all threads are suspended due to thread 2 being in a runnable + // state (from SetStateUnsafe). + // AssertThreadsAreSuspended(self, self, debug_thread); VLOG(threads) << *self << " SuspendAllForDebugger complete"; } |