diff options
-rw-r--r-- | compiler/optimizing/builder.cc | 10 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 43 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 64 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 68 | ||||
-rw-r--r-- | compiler/utils/x86_64/assembler_x86_64.cc | 12 | ||||
-rw-r--r-- | compiler/utils/x86_64/assembler_x86_64.h | 1 | ||||
-rw-r--r-- | runtime/base/logging.cc | 2 | ||||
-rw-r--r-- | runtime/base/logging.h | 5 | ||||
-rw-r--r-- | runtime/base/mutex-inl.h | 8 | ||||
-rw-r--r-- | runtime/base/mutex.cc | 4 | ||||
-rw-r--r-- | runtime/base/mutex.h | 12 | ||||
-rw-r--r-- | runtime/runtime.cc | 56 | ||||
-rw-r--r-- | runtime/runtime.h | 11 | ||||
-rw-r--r-- | runtime/runtime_android.cc | 1 | ||||
-rw-r--r-- | runtime/runtime_linux.cc | 1 | ||||
-rw-r--r-- | runtime/thread-inl.h | 8 | ||||
-rw-r--r-- | runtime/thread.cc | 2 | ||||
-rw-r--r-- | runtime/thread_list.cc | 6 | ||||
-rw-r--r-- | runtime/utils.cc | 8 | ||||
-rw-r--r-- | test/422-type-conversion/src/Main.java | 70 | ||||
-rw-r--r-- | tools/art | 25 | ||||
-rw-r--r-- | tools/libcore_failures.txt | 15 |
22 files changed, 309 insertions, 123 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 9561054b69..ca72f3f242 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -1306,6 +1306,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::DOUBLE_TO_INT: { + Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimInt, dex_pc); + break; + } + + case Instruction::DOUBLE_TO_LONG: { + Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimLong, dex_pc); + break; + } + case Instruction::DOUBLE_TO_FLOAT: { Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimFloat, dex_pc); break; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 5076c85885..36af393e3b 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -44,7 +44,7 @@ static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2, R3 }; static constexpr size_t kRuntimeParameterCoreRegistersLength = arraysize(kRuntimeParameterCoreRegisters); -static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0 }; +static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1 }; static constexpr size_t kRuntimeParameterFpuRegistersLength = arraysize(kRuntimeParameterFpuRegisters); @@ -1365,9 +1365,11 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { Primitive::Type input_type = conversion->GetInputType(); DCHECK_NE(result_type, input_type); - // Float-to-long conversions invoke the runtime. + // The float-to-long and double-to-long type conversions rely on a + // call to the runtime. LocationSummary::CallKind call_kind = - (input_type == Primitive::kPrimFloat && result_type == Primitive::kPrimLong) + ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) + && result_type == Primitive::kPrimLong) ? LocationSummary::kCall : LocationSummary::kNoCall; LocationSummary* locations = @@ -1422,8 +1424,10 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + // Processing a Dex `double-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); break; default: @@ -1452,10 +1456,15 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-long' instruction. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::FpuRegisterPairLocation( + calling_convention.GetFpuRegisterAt(0), + calling_convention.GetFpuRegisterAt(1))); + locations->SetOut(Location::RegisterPairLocation(R0, R1)); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type @@ -1614,10 +1623,15 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-int' instruction. + SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); + DRegister temp_d = FromLowSToD(temp_s); + __ vmovd(temp_d, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); + __ vcvtid(temp_s, temp_d); + __ vmovrs(out.AsRegister<Register>(), temp_s); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type @@ -1643,15 +1657,16 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimFloat: // Processing a Dex `float-to-long' instruction. - // This call does not actually record PC information. codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pF2l), conversion, conversion->GetDexPc()); break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + // Processing a Dex `double-to-long' instruction. + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pD2l), + conversion, + conversion->GetDexPc()); break; default: diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2aa121d04b..2fd712f7e2 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1330,9 +1330,11 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { Primitive::Type input_type = conversion->GetInputType(); DCHECK_NE(result_type, input_type); - // Float-to-long conversions invoke the runtime. + // The float-to-long and double-to-long type conversions rely on a + // call to the runtime. LocationSummary::CallKind call_kind = - (input_type == Primitive::kPrimFloat && result_type == Primitive::kPrimLong) + ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) + && result_type == Primitive::kPrimLong) ? LocationSummary::kCall : LocationSummary::kNoCall; LocationSummary* locations = @@ -1387,8 +1389,10 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + // Processing a Dex `double-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); break; default: @@ -1411,15 +1415,27 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimFloat: { // Processing a Dex `float-to-long' instruction. InvokeRuntimeCallingConvention calling_convention; - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + // Note that on x86 floating-point parameters are passed + // through core registers (here, EAX). + locations->SetInAt(0, Location::RegisterLocation( + calling_convention.GetRegisterAt(0))); // The runtime helper puts the result in EAX, EDX. locations->SetOut(Location::RegisterPairLocation(EAX, EDX)); break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-long' instruction. + InvokeRuntimeCallingConvention calling_convention; + // Note that on x86 floating-point parameters are passed + // through core registers (here, EAX and ECX). + locations->SetInAt(0, Location::RegisterPairLocation( + calling_convention.GetRegisterAt(0), + calling_convention.GetRegisterAt(1))); + // The runtime helper puts the result in EAX, EDX. + locations->SetOut(Location::RegisterPairLocation(EAX, EDX)); + break; + } break; default: @@ -1607,10 +1623,30 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-int' instruction. + XmmRegister input = in.AsFpuRegister<XmmRegister>(); + Register output = out.AsRegister<Register>(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + Label done, nan; + + __ movl(output, Immediate(kPrimIntMax)); + // temp = int-to-double(output) + __ cvtsi2sd(temp, output); + // if input >= temp goto done + __ comisd(input, temp); + __ j(kAboveEqual, &done); + // if input == NaN goto nan + __ j(kUnordered, &nan); + // output = double-to-int-truncate(input) + __ cvttsd2si(output, input); + __ jmp(&done); + __ Bind(&nan); + // output = 0 + __ xorl(output, output); + __ Bind(&done); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type @@ -1634,13 +1670,13 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimFloat: // Processing a Dex `float-to-long' instruction. __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pF2l))); - // This call does not actually record PC information. codegen_->RecordPcInfo(conversion, conversion->GetDexPc()); break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + // Processing a Dex `double-to-long' instruction. + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pD2l))); + codegen_->RecordPcInfo(conversion, conversion->GetDexPc()); break; default: diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 5761fb1bda..39a97661c9 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1370,8 +1370,10 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + // Processing a Dex `double-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); break; default: @@ -1401,8 +1403,10 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + // Processing a Dex `double-to-long' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); break; default: @@ -1589,10 +1593,30 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-int' instruction. + XmmRegister input = in.AsFpuRegister<XmmRegister>(); + CpuRegister output = out.AsRegister<CpuRegister>(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + Label done, nan; + + __ movl(output, Immediate(kPrimIntMax)); + // temp = int-to-double(output) + __ cvtsi2sd(temp, output); + // if input >= temp goto done + __ comisd(input, temp); + __ j(kAboveEqual, &done); + // if input == NaN goto nan + __ j(kUnordered, &nan); + // output = double-to-int-truncate(input) + __ cvttsd2si(output, input); + __ jmp(&done); + __ Bind(&nan); + // output = 0 + __ xorl(output, output); + __ Bind(&done); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type @@ -1620,14 +1644,14 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver Label done, nan; __ movq(output, Immediate(kPrimLongMax)); - // temp = int-to-float(output) + // temp = long-to-float(output) __ cvtsi2ss(temp, output, true); // if input >= temp goto done __ comiss(input, temp); __ j(kAboveEqual, &done); // if input == NaN goto nan __ j(kUnordered, &nan); - // output = float-to-int-truncate(input) + // output = float-to-long-truncate(input) __ cvttss2si(output, input, true); __ jmp(&done); __ Bind(&nan); @@ -1637,10 +1661,30 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver break; } - case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type << " to " - << result_type << " not yet implemented"; + case Primitive::kPrimDouble: { + // Processing a Dex `double-to-long' instruction. + XmmRegister input = in.AsFpuRegister<XmmRegister>(); + CpuRegister output = out.AsRegister<CpuRegister>(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + Label done, nan; + + __ movq(output, Immediate(kPrimLongMax)); + // temp = long-to-double(output) + __ cvtsi2sd(temp, output, true); + // if input >= temp goto done + __ comisd(input, temp); + __ j(kAboveEqual, &done); + // if input == NaN goto nan + __ j(kUnordered, &nan); + // output = double-to-long-truncate(input) + __ cvttsd2si(output, input, true); + __ jmp(&done); + __ Bind(&nan); + // output = 0 + __ xorq(output, output); + __ Bind(&done); break; + } default: LOG(FATAL) << "Unexpected type conversion from " << input_type diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 3c21236a8a..2a6c58e128 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -683,9 +683,19 @@ void X86_64Assembler::cvttss2si(CpuRegister dst, XmmRegister src, bool is64bit) void X86_64Assembler::cvttsd2si(CpuRegister dst, XmmRegister src) { + cvttsd2si(dst, src, false); +} + + +void X86_64Assembler::cvttsd2si(CpuRegister dst, XmmRegister src, bool is64bit) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF2); - EmitOptionalRex32(dst, src); + if (is64bit) { + // Emit a REX.W prefix if the operand size is 64 bits. + EmitRex64(dst, src); + } else { + EmitOptionalRex32(dst, src); + } EmitUint8(0x0F); EmitUint8(0x2C); EmitXmmRegisterOperand(dst.LowBits(), src); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 4c2836665d..51d1de2c0f 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -342,6 +342,7 @@ class X86_64Assembler FINAL : public Assembler { void cvttss2si(CpuRegister dst, XmmRegister src); // Note: this is the r32 version. void cvttss2si(CpuRegister dst, XmmRegister src, bool is64bit); void cvttsd2si(CpuRegister dst, XmmRegister src); // Note: this is the r32 version. + void cvttsd2si(CpuRegister dst, XmmRegister src, bool is64bit); void cvtdq2pd(XmmRegister dst, XmmRegister src); diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index b781d6008c..bdc4cf6399 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -35,8 +35,6 @@ namespace art { LogVerbosity gLogVerbosity; -unsigned int gAborting = 0; - static LogSeverity gMinimumLogSeverity = INFO; static std::unique_ptr<std::string> gCmdLine; static std::unique_ptr<std::string> gProgramInvocationName; diff --git a/runtime/base/logging.h b/runtime/base/logging.h index ae83e331fd..a9cc99b085 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -55,11 +55,6 @@ struct LogVerbosity { // Global log verbosity setting, initialized by InitLogging. extern LogVerbosity gLogVerbosity; -// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive -// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown -// makes forward progress. -extern unsigned int gAborting; - // Configure logging based on ANDROID_LOG_TAGS environment variable. // We need to parse a string that looks like // diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index cb698175df..020634122e 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -97,9 +97,7 @@ inline void BaseMutex::RegisterAsLocked(Thread* self) { } } } - if (gAborting == 0) { // Avoid recursive aborts. - CHECK(!bad_mutexes_held); - } + CHECK(!bad_mutexes_held); } // Don't record monitors as they are outside the scope of analysis. They may be inspected off of // the monitor list. @@ -114,7 +112,7 @@ inline void BaseMutex::RegisterAsUnlocked(Thread* self) { return; } if (level_ != kMonitorLock) { - if (kDebugLocking && gAborting == 0) { // Avoid recursive aborts. + if (kDebugLocking) { CHECK(self->GetHeldMutex(level_) == this) << "Unlocking on unacquired mutex: " << name_; } self->SetHeldMutex(level_, NULL); @@ -178,7 +176,7 @@ inline bool Mutex::IsExclusiveHeld(const Thread* self) const { bool result = (GetExclusiveOwnerTid() == SafeGetTid(self)); if (kDebugLocking) { // Sanity debug check that if we think it is locked we have it in our held mutexes. - if (result && self != NULL && level_ != kMonitorLock && !gAborting) { + if (result && self != NULL && level_ != kMonitorLock) { CHECK_EQ(self->GetHeldMutex(level_), this); } } diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index aa2aefc318..49579886fd 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -209,9 +209,7 @@ void BaseMutex::CheckSafeToWait(Thread* self) { } } } - if (gAborting == 0) { // Avoid recursive aborts. - CHECK(!bad_mutexes_held); - } + CHECK(!bad_mutexes_held); } } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 9c93cc624d..41b5f12fdb 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -220,7 +220,7 @@ class LOCKABLE Mutex : public BaseMutex { // Assert that the Mutex is exclusively held by the current thread. void AssertExclusiveHeld(const Thread* self) { - if (kDebugLocking && (gAborting == 0)) { + if (kDebugLocking) { CHECK(IsExclusiveHeld(self)) << *this; } } @@ -228,7 +228,7 @@ class LOCKABLE Mutex : public BaseMutex { // Assert that the Mutex is not held by the current thread. void AssertNotHeldExclusive(const Thread* self) { - if (kDebugLocking && (gAborting == 0)) { + if (kDebugLocking) { CHECK(!IsExclusiveHeld(self)) << *this; } } @@ -318,7 +318,7 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Assert the current thread has exclusive access to the ReaderWriterMutex. void AssertExclusiveHeld(const Thread* self) { - if (kDebugLocking && (gAborting == 0)) { + if (kDebugLocking) { CHECK(IsExclusiveHeld(self)) << *this; } } @@ -326,7 +326,7 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Assert the current thread doesn't have exclusive access to the ReaderWriterMutex. void AssertNotExclusiveHeld(const Thread* self) { - if (kDebugLocking && (gAborting == 0)) { + if (kDebugLocking) { CHECK(!IsExclusiveHeld(self)) << *this; } } @@ -337,7 +337,7 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Assert the current thread has shared access to the ReaderWriterMutex. void AssertSharedHeld(const Thread* self) { - if (kDebugLocking && (gAborting == 0)) { + if (kDebugLocking) { // TODO: we can only assert this well when self != NULL. CHECK(IsSharedHeld(self) || self == NULL) << *this; } @@ -347,7 +347,7 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Assert the current thread doesn't hold this ReaderWriterMutex either in shared or exclusive // mode. void AssertNotHeld(const Thread* self) { - if (kDebugLocking && (gAborting == 0)) { + if (kDebugLocking) { CHECK(!IsSharedHeld(self)) << *this; } } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 078e7d24e1..e79203198e 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -126,6 +126,8 @@ namespace art { static constexpr bool kEnableJavaStackTraceHandler = false; Runtime* Runtime::instance_ = nullptr; +volatile unsigned int gAborting = 0; + Runtime::Runtime() : instruction_set_(kNone), compiler_callbacks_(nullptr), @@ -236,13 +238,8 @@ Runtime::~Runtime() { struct AbortState { void Dump(std::ostream& os) const { - if (gAborting > 1) { - os << "Runtime aborting --- recursively, so no thread-specific detail!\n"; - return; - } - gAborting++; os << "Runtime aborting...\n"; - if (Runtime::Current() == NULL) { + if (Runtime::Current() == nullptr) { os << "(Runtime does not yet exist!)\n"; return; } @@ -300,13 +297,18 @@ struct AbortState { void Runtime::Abort() { gAborting++; // set before taking any locks + if (gAborting > 1) { + LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, + "Runtime aborting --- recursively, so no thread-specific detail!\n"); + return; + } // Ensure that we don't have multiple threads trying to abort at once, // which would result in significantly worse diagnostics. MutexLock mu(Thread::Current(), *Locks::abort_lock_); // Get any pending output out of the way. - fflush(NULL); + fflush(nullptr); // Many people have difficulty distinguish aborts from crashes, // so be explicit. @@ -314,7 +316,7 @@ void Runtime::Abort() { LOG(INTERNAL_FATAL) << Dumpable<AbortState>(state); // Call the abort hook if we have one. - if (Runtime::Current() != NULL && Runtime::Current()->abort_ != NULL) { + if (Runtime::Current() != nullptr && Runtime::Current()->abort_ != nullptr) { LOG(INTERNAL_FATAL) << "Calling abort hook..."; Runtime::Current()->abort_(); // notreached @@ -342,7 +344,7 @@ void Runtime::PreZygoteFork() { } void Runtime::CallExitHook(jint status) { - if (exit_ != NULL) { + if (exit_ != nullptr) { ScopedThreadStateChange tsc(Thread::Current(), kNative); exit_(status); LOG(WARNING) << "Exit hook returned instead of exiting!"; @@ -357,14 +359,14 @@ void Runtime::SweepSystemWeaks(IsMarkedCallback* visitor, void* arg) { bool Runtime::Create(const RuntimeOptions& options, bool ignore_unrecognized) { // TODO: acquire a static mutex on Runtime to avoid racing. - if (Runtime::instance_ != NULL) { + if (Runtime::instance_ != nullptr) { return false; } - InitLogging(NULL); // Calls Locks::Init() as a side effect. + InitLogging(nullptr); // Calls Locks::Init() as a side effect. instance_ = new Runtime; if (!instance_->Init(options, ignore_unrecognized)) { delete instance_; - instance_ = NULL; + instance_ = nullptr; return false; } return true; @@ -372,7 +374,7 @@ bool Runtime::Create(const RuntimeOptions& options, bool ignore_unrecognized) { static jobject CreateSystemClassLoader() { if (Runtime::Current()->UseCompileTimeClassPath()) { - return NULL; + return nullptr; } ScopedObjectAccess soa(Thread::Current()); @@ -385,7 +387,7 @@ static jobject CreateSystemClassLoader() { mirror::ArtMethod* getSystemClassLoader = class_loader_class->FindDirectMethod("getSystemClassLoader", "()Ljava/lang/ClassLoader;"); - CHECK(getSystemClassLoader != NULL); + CHECK(getSystemClassLoader != nullptr); JValue result = InvokeWithJValues(soa, nullptr, soa.EncodeMethod(getSystemClassLoader), nullptr); JNIEnv* env = soa.Self()->GetJniEnv(); @@ -401,7 +403,7 @@ static jobject CreateSystemClassLoader() { mirror::ArtField* contextClassLoader = thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;"); - CHECK(contextClassLoader != NULL); + CHECK(contextClassLoader != nullptr); // We can't run in a transaction yet. contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), @@ -527,7 +529,7 @@ bool Runtime::InitZygote() { // Mark rootfs as being a slave so that changes from default // namespace only flow into our children. - if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) { + if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) { PLOG(WARNING) << "Failed to mount() rootfs as MS_SLAVE"; return false; } @@ -536,7 +538,7 @@ bool Runtime::InitZygote() { // bind mount storage into their respective private namespaces, which // are isolated from each other. const char* target_base = getenv("EMULATED_STORAGE_TARGET"); - if (target_base != NULL) { + if (target_base != nullptr) { if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV, "uid=0,gid=1028,mode=0751") == -1) { LOG(WARNING) << "Failed to mount tmpfs to " << target_base; @@ -893,14 +895,14 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) self->ThrowNewException(ThrowLocation(), "Ljava/lang/OutOfMemoryError;", "OutOfMemoryError thrown while trying to throw OutOfMemoryError; " "no stack trace available"); - pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>(self->GetException(NULL)); + pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>(self->GetException(nullptr)); self->ClearException(); // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class // ahead of checking the application's class loader. self->ThrowNewException(ThrowLocation(), "Ljava/lang/NoClassDefFoundError;", "Class not found using the boot class loader; no stack trace available"); - pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException(NULL)); + pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException(nullptr)); self->ClearException(); // Look for a native bridge. @@ -976,26 +978,26 @@ void Runtime::InitThreadGroups(Thread* self) { env->NewGlobalRef(env->GetStaticObjectField( WellKnownClasses::java_lang_ThreadGroup, WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup)); - CHECK(main_thread_group_ != NULL || IsCompiler()); + CHECK(main_thread_group_ != nullptr || IsCompiler()); system_thread_group_ = env->NewGlobalRef(env->GetStaticObjectField( WellKnownClasses::java_lang_ThreadGroup, WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup)); - CHECK(system_thread_group_ != NULL || IsCompiler()); + CHECK(system_thread_group_ != nullptr || IsCompiler()); } jobject Runtime::GetMainThreadGroup() const { - CHECK(main_thread_group_ != NULL || IsCompiler()); + CHECK(main_thread_group_ != nullptr || IsCompiler()); return main_thread_group_; } jobject Runtime::GetSystemThreadGroup() const { - CHECK(system_thread_group_ != NULL || IsCompiler()); + CHECK(system_thread_group_ != nullptr || IsCompiler()); return system_thread_group_; } jobject Runtime::GetSystemClassLoader() const { - CHECK(system_class_loader_ != NULL || IsCompiler()); + CHECK(system_class_loader_ != nullptr || IsCompiler()); return system_class_loader_; } @@ -1121,12 +1123,12 @@ void Runtime::BlockSignals() { bool Runtime::AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group, bool create_peer) { - return Thread::Attach(thread_name, as_daemon, thread_group, create_peer) != NULL; + return Thread::Attach(thread_name, as_daemon, thread_group, create_peer) != nullptr; } void Runtime::DetachCurrentThread() { Thread* self = Thread::Current(); - if (self == NULL) { + if (self == nullptr) { LOG(FATAL) << "attempting to detach thread that is not attached"; } if (self->HasManagedStack()) { @@ -1351,7 +1353,7 @@ void Runtime::SetCalleeSaveMethod(mirror::ArtMethod* method, CalleeSaveType type } const std::vector<const DexFile*>& Runtime::GetCompileTimeClassPath(jobject class_loader) { - if (class_loader == NULL) { + if (class_loader == nullptr) { return GetClassLinker()->GetBootClassPath(); } CHECK(UseCompileTimeClassPath()); diff --git a/runtime/runtime.h b/runtime/runtime.h index 39fd910893..e334764daa 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -71,6 +71,11 @@ class ThreadList; class Trace; class Transaction; +// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive +// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown +// makes forward progress. +extern volatile unsigned int gAborting; + typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions; // Not all combinations of flags are valid. You may not visit all roots as well as the new roots @@ -175,9 +180,9 @@ class Runtime { return instance_; } - // Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most - // callers should prefer. - [[noreturn]] static void Abort() LOCKS_EXCLUDED(Locks::abort_lock_); + // Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most callers should + // prefer. Not [[noreturn]] due to returning early in the case of recursive aborts. + static void Abort() LOCKS_EXCLUDED(Locks::abort_lock_); // Returns the "main" ThreadGroup, used when attaching user threads. jobject GetMainThreadGroup() const; diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc index 33600ddba5..33641edb50 100644 --- a/runtime/runtime_android.cc +++ b/runtime/runtime_android.cc @@ -38,7 +38,6 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex _exit(1); } handling_unexpected_signal = true; - gAborting++; // set before taking any locks MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); Runtime* runtime = Runtime::Current(); diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index 1de035c0d5..927309177a 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -284,7 +284,6 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex } handlingUnexpectedSignal = true; - gAborting++; // set before taking any locks MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); bool has_address = (signal_number == SIGILL || signal_number == SIGBUS || diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 7aed8b033c..49b7be9edd 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -83,9 +83,7 @@ inline ThreadState Thread::SetState(ThreadState new_state) { inline void Thread::AssertThreadSuspensionIsAllowable(bool check_locks) const { if (kIsDebugBuild) { - if (gAborting == 0) { - CHECK_EQ(0u, tls32_.no_thread_suspension) << tlsPtr_.last_no_thread_suspension_cause; - } + CHECK_EQ(0u, tls32_.no_thread_suspension) << tlsPtr_.last_no_thread_suspension_cause; if (check_locks) { bool bad_mutexes_held = false; for (int i = kLockLevelCount - 1; i >= 0; --i) { @@ -99,9 +97,7 @@ inline void Thread::AssertThreadSuspensionIsAllowable(bool check_locks) const { } } } - if (gAborting == 0) { - CHECK(!bad_mutexes_held); - } + CHECK(!bad_mutexes_held); } } } diff --git a/runtime/thread.cc b/runtime/thread.cc index f7c710695a..a6e5b0c18b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -742,7 +742,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { // Don't do this if we are aborting since the GC may have all the threads suspended. This will // cause ScopedObjectAccessUnchecked to deadlock. - if (gAborting == 0 && self != nullptr && thread != nullptr && thread->tlsPtr_.opeer != nullptr) { + if (self != nullptr && thread != nullptr && thread->tlsPtr_.opeer != nullptr) { ScopedObjectAccessUnchecked soa(self); priority = soa.DecodeField(WellKnownClasses::java_lang_Thread_priority) ->GetInt(thread->tlsPtr_.opeer); diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index beafcda8f2..71325a5350 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -168,9 +168,7 @@ class DumpCheckpoint FINAL : public Closure { const uint32_t kWaitTimeoutMs = 10000; bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kWaitTimeoutMs); if (timed_out) { - // Avoid a recursive abort. - LOG((kIsDebugBuild && (gAborting == 0)) ? FATAL : ERROR) - << "Unexpected time out during dump checkpoint."; + LOG(kIsDebugBuild ? FATAL : ERROR) << "Unexpected time out during dump checkpoint."; } } @@ -243,7 +241,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) { Locks::mutator_lock_->AssertNotExclusiveHeld(self); Locks::thread_list_lock_->AssertNotHeld(self); Locks::thread_suspend_count_lock_->AssertNotHeld(self); - if (kDebugLocking && gAborting == 0) { + if (kDebugLocking) { CHECK_NE(self->GetState(), kRunnable); } diff --git a/runtime/utils.cc b/runtime/utils.cc index 1211547a51..d9782f3fee 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1125,14 +1125,6 @@ void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, return; } -#if !defined(HAVE_ANDROID_OS) - if (GetTid() != tid) { - // TODO: dumping of other threads is disabled to avoid crashes during stress testing. - // b/15446488. - return; - } -#endif - std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid)); if (!backtrace->Unwind(0)) { os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n"; diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java index 91618fc341..7ce2868283 100644 --- a/test/422-type-conversion/src/Main.java +++ b/test/422-type-conversion/src/Main.java @@ -112,6 +112,12 @@ public class Main { // Generate, compile and check float-to-double Dex instructions. floatToDouble(); + // Generate, compile and check double-to-int Dex instructions. + doubleToInt(); + + // Generate, compile and check double-to-long Dex instructions. + doubleToLong(); + // Generate, compile and check double-to-float Dex instructions. doubleToFloat(); @@ -415,6 +421,58 @@ public class Main { assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$FloatToDouble(Float.NEGATIVE_INFINITY)); } + private static void doubleToInt() { + assertIntEquals(1, $opt$DoubleToInt(1D)); + assertIntEquals(0, $opt$DoubleToInt(0D)); + assertIntEquals(0, $opt$DoubleToInt(-0D)); + assertIntEquals(-1, $opt$DoubleToInt(-1D)); + assertIntEquals(51, $opt$DoubleToInt(51D)); + assertIntEquals(-51, $opt$DoubleToInt(-51D)); + assertIntEquals(0, $opt$DoubleToInt(0.5D)); + assertIntEquals(0, $opt$DoubleToInt(0.4999999D)); + assertIntEquals(0, $opt$DoubleToInt(-0.4999999D)); + assertIntEquals(0, $opt$DoubleToInt(-0.5D)); + assertIntEquals(42, $opt$DoubleToInt(42.199D)); + assertIntEquals(-42, $opt$DoubleToInt(-42.199D)); + assertIntEquals(2147483647, $opt$DoubleToInt(2147483647D)); // 2^31 - 1 + assertIntEquals(-2147483647, $opt$DoubleToInt(-2147483647D)); // -(2^31 - 1) + assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483648D)); // -(2^31) + assertIntEquals(2147483647, $opt$DoubleToInt(2147483648D)); // (2^31) + assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483649D)); // -(2^31 + 1) + assertIntEquals(2147483647, $opt$DoubleToInt(9223372036854775807D)); // 2^63 - 1 + assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775807D)); // -(2^63 - 1) + assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775808D)); // -(2^63) + assertIntEquals(0, $opt$DoubleToInt(Double.NaN)); + assertIntEquals(2147483647, $opt$DoubleToInt(Double.POSITIVE_INFINITY)); + assertIntEquals(-2147483648, $opt$DoubleToInt(Double.NEGATIVE_INFINITY)); + } + + private static void doubleToLong() { + assertLongEquals(1L, $opt$DoubleToLong(1D)); + assertLongEquals(0L, $opt$DoubleToLong(0D)); + assertLongEquals(0L, $opt$DoubleToLong(-0D)); + assertLongEquals(-1L, $opt$DoubleToLong(-1D)); + assertLongEquals(51L, $opt$DoubleToLong(51D)); + assertLongEquals(-51L, $opt$DoubleToLong(-51D)); + assertLongEquals(0L, $opt$DoubleToLong(0.5D)); + assertLongEquals(0L, $opt$DoubleToLong(0.4999999D)); + assertLongEquals(0L, $opt$DoubleToLong(-0.4999999D)); + assertLongEquals(0L, $opt$DoubleToLong(-0.5D)); + assertLongEquals(42L, $opt$DoubleToLong(42.199D)); + assertLongEquals(-42L, $opt$DoubleToLong(-42.199D)); + assertLongEquals(2147483647L, $opt$DoubleToLong(2147483647D)); // 2^31 - 1 + assertLongEquals(-2147483647L, $opt$DoubleToLong(-2147483647D)); // -(2^31 - 1) + assertLongEquals(-2147483648L, $opt$DoubleToLong(-2147483648D)); // -(2^31) + assertLongEquals(2147483648L, $opt$DoubleToLong(2147483648D)); // (2^31) + assertLongEquals(-2147483649L, $opt$DoubleToLong(-2147483649D)); // -(2^31 + 1) + assertLongEquals(9223372036854775807L, $opt$DoubleToLong(9223372036854775807D)); // 2^63 - 1 + assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775807D)); // -(2^63 - 1) + assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775808D)); // -(2^63) + assertLongEquals(0L, $opt$DoubleToLong(Double.NaN)); + assertLongEquals(9223372036854775807L, $opt$DoubleToLong(Double.POSITIVE_INFINITY)); + assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(Double.NEGATIVE_INFINITY)); + } + private static void doubleToFloat() { assertFloatEquals(1F, $opt$DoubleToFloat(1D)); assertFloatEquals(0F, $opt$DoubleToFloat(0D)); @@ -599,15 +657,21 @@ public class Main { // This method produces a float-to-int Dex instruction. static int $opt$FloatToInt(float a) { return (int)a; } + // This method produces a float-to-long Dex instruction. + static long $opt$FloatToLong(float a){ return (long)a; } + // This method produces a float-to-double Dex instruction. static double $opt$FloatToDouble(float a) { return (double)a; } + // This method produces a double-to-int Dex instruction. + static int $opt$DoubleToInt(double a){ return (int)a; } + + // This method produces a double-to-long Dex instruction. + static long $opt$DoubleToLong(double a){ return (long)a; } + // This method produces a double-to-float Dex instruction. static float $opt$DoubleToFloat(double a) { return (float)a; } - // This method produces a float-to-long Dex instruction. - static long $opt$FloatToLong(float a){ return (long)a; } - // These methods produce int-to-byte Dex instructions. static byte $opt$ShortToByte(short a) { return (byte)a; } static byte $opt$IntToByte(int a) { return (byte)a; } @@ -1,5 +1,3 @@ -#!/bin/bash -# # Copyright (C) 2011 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# This script is used on host and device. It uses a common subset +# shell dialect that should work on the host (e.g. bash), and +# Android (e.g. mksh). + function follow_links() { if [ z"$BASH_SOURCE" != z ]; then file="$BASH_SOURCE" @@ -28,7 +30,8 @@ function follow_links() { } function find_libdir() { - if [ "$(readlink "$ANDROID_ROOT/bin/$DALVIKVM")" = "dalvikvm64" ]; then + # Use realpath instead of readlink because Android does not have a readlink. + if [ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" = "$(realpath "$ANDROID_ROOT/bin/dalvikvm64")" ]; then echo "lib64" else echo "lib" @@ -70,16 +73,22 @@ done PROG_NAME="$(follow_links)" PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" ANDROID_ROOT=$PROG_DIR/.. -ANDROID_DATA=$PWD/android-data$$ LIBDIR=$(find_libdir) LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR - if [ z"$PERF" != z ]; then invoke_with="perf record -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with" fi -mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64} +DELETE_ANDROID_DATA=false +# If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own, +# and ensure we delete it at the end. +if [ "$ANDROID_DATA" = "/data" ] || [ "$ANDROID_DATA" = "" ]; then + ANDROID_DATA=$PWD/android-data$$ + mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64} + DELETE_ANDROID_DATA=true +fi + ANDROID_DATA=$ANDROID_DATA \ ANDROID_ROOT=$ANDROID_ROOT \ LD_LIBRARY_PATH=$LD_LIBRARY_PATH \ @@ -97,7 +106,9 @@ if [ z"$PERF" != z ]; then fi echo "Perf data saved in: $ANDROID_DATA/perf.data" else - rm -rf $ANDROID_DATA + if [ "$DELETE_ANDROID_DATA" = "true" ]; then + rm -rf $ANDROID_DATA + fi fi exit $EXIT_STATUS diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 8eac1d3a23..febc48c526 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -11,11 +11,26 @@ { description: "Assert.java differences between vogar and junit.", result: EXEC_FAILED, + modes: [host], name: "libcore.java.math.RunCSVTests#test_csv" }, { description: "Test is currently being updated.", result: EXEC_FAILED, name: "libcore.java.util.OldTimeZoneTest#test_getDisplayNameZILjava_util_Locale" +}, +{ + description: "Differences between vogar and cts in user directory", + result: EXEC_FAILED, + modes: [device], + name: "libcore.java.lang.SystemTest#testSystemProperties_mutable" +}, +{ + description: "Differences between vogar and cts", + result: EXEC_FAILED, + modes: [device], + names: ["libcore.java.lang.OldSystemTest#test_getProperties", + "org.apache.harmony.tests.java.lang.Process2Test#test_getErrorStream", + "org.apache.harmony.tests.java.lang.ProcessTest#test_exitValue"] } ] |