diff options
37 files changed, 1154 insertions, 663 deletions
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc index c09950cd5d..fe5f9a948a 100644 --- a/compiler/linker/mips/relative_patcher_mips.cc +++ b/compiler/linker/mips/relative_patcher_mips.cc @@ -49,9 +49,12 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, uint32_t target_offset) { uint32_t anchor_literal_offset = patch.PcInsnOffset(); uint32_t literal_offset = patch.LiteralOffset(); + uint32_t literal_low_offset; bool dex_cache_array = (patch.GetType() == LinkerPatch::Type::kDexCacheArray); - // Basic sanity checks. + // Perform basic sanity checks and initialize `literal_low_offset` to point + // to the instruction containing the 16 least significant bits of the + // relative address. if (is_r6) { DCHECK_GE(code->size(), 8u); DCHECK_LE(literal_offset, code->size() - 8u); @@ -61,10 +64,10 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, DCHECK_EQ((*code)[literal_offset + 1], 0x12); DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E); DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC); - // ADDIU reg, reg, offset_low + // instr reg(s), offset_low DCHECK_EQ((*code)[literal_offset + 4], 0x78); DCHECK_EQ((*code)[literal_offset + 5], 0x56); - DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x24); + literal_low_offset = literal_offset + 4; } else { DCHECK_GE(code->size(), 16u); DCHECK_LE(literal_offset, code->size() - 12u); @@ -84,36 +87,34 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, DCHECK_EQ((*code)[literal_offset + 1], 0x12); DCHECK_EQ(((*code)[literal_offset + 2] & 0xE0), 0x00); DCHECK_EQ((*code)[literal_offset + 3], 0x3C); - // ORI reg, reg, offset_low - DCHECK_EQ((*code)[literal_offset + 4], 0x78); - DCHECK_EQ((*code)[literal_offset + 5], 0x56); - DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x34); // ADDU reg, reg, reg2 - DCHECK_EQ((*code)[literal_offset + 8], 0x21); - DCHECK_EQ(((*code)[literal_offset + 9] & 0x07), 0x00); + DCHECK_EQ((*code)[literal_offset + 4], 0x21); + DCHECK_EQ(((*code)[literal_offset + 5] & 0x07), 0x00); if (dex_cache_array) { // reg2 is either RA or from HMipsComputeBaseMethodAddress. - DCHECK_EQ(((*code)[literal_offset + 10] & 0x1F), 0x1F); + DCHECK_EQ(((*code)[literal_offset + 6] & 0x1F), 0x1F); } - DCHECK_EQ(((*code)[literal_offset + 11] & 0xFC), 0x00); + DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x00); + // instr reg(s), offset_low + DCHECK_EQ((*code)[literal_offset + 8], 0x78); + DCHECK_EQ((*code)[literal_offset + 9], 0x56); + literal_low_offset = literal_offset + 8; } // Apply patch. uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset; uint32_t diff = target_offset - anchor_offset; - if (dex_cache_array) { + if (dex_cache_array && !is_r6) { diff += kDexCacheArrayLwOffset; } - if (is_r6) { - diff += (diff & 0x8000) << 1; // Account for sign extension in ADDIU. - } + diff += (diff & 0x8000) << 1; // Account for sign extension in "instr reg(s), offset_low". // LUI reg, offset_high / AUIPC reg, offset_high (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16); (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24); - // ORI reg, reg, offset_low / ADDIU reg, reg, offset_low - (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0); - (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8); + // instr reg(s), offset_low + (*code)[literal_low_offset + 0] = static_cast<uint8_t>(diff >> 0); + (*code)[literal_low_offset + 1] = static_cast<uint8_t>(diff >> 8); } } // namespace linker diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc index 4f9a3a050a..474eb73e08 100644 --- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc +++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc @@ -20,10 +20,6 @@ namespace art { namespace linker { -// We'll maximize the range of a single load instruction for dex cache array accesses -// by aligning offset -32768 with the offset of the first used element. -static constexpr uint32_t kDexCacheArrayLwOffset = 0x8000; - class Mips32r6RelativePatcherTest : public RelativePatcherTest { public: Mips32r6RelativePatcherTest() : RelativePatcherTest(kMips, "mips32r6") {} @@ -64,9 +60,6 @@ void Mips32r6RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const Link ASSERT_TRUE(result.first); uint32_t diff = target_offset - (result.second + kAnchorOffset); - if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) { - diff += kDexCacheArrayLwOffset; - } diff += (diff & 0x8000) << 1; // Account for sign extension in addiu. const uint8_t expected_code[] = { diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc index faeb92a315..b0d1294cf4 100644 --- a/compiler/linker/mips/relative_patcher_mips_test.cc +++ b/compiler/linker/mips/relative_patcher_mips_test.cc @@ -47,12 +47,12 @@ class MipsRelativePatcherTest : public RelativePatcherTest { const uint8_t MipsRelativePatcherTest::kUnpatchedPcRelativeRawCode[] = { 0x00, 0x00, 0x10, 0x04, // nal - 0x34, 0x12, 0x12, 0x3C, // lui s2, high(diff); placeholder = 0x1234 - 0x78, 0x56, 0x52, 0x36, // ori s2, s2, low(diff); placeholder = 0x5678 - 0x21, 0x90, 0x5F, 0x02, // addu s2, s2, ra + 0x34, 0x12, 0x12, 0x3C, // lui s2, high(diff); placeholder = 0x1234 + 0x21, 0x90, 0x5F, 0x02, // addu s2, s2, ra + 0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678 }; const uint32_t MipsRelativePatcherTest::kLiteralOffset = 4; // At lui (where patching starts). -const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8; // At ori (where PC+0 points). +const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8; // At addu (where PC+0 points). const ArrayRef<const uint8_t> MipsRelativePatcherTest::kUnpatchedPcRelativeCode( kUnpatchedPcRelativeRawCode); @@ -68,12 +68,13 @@ void MipsRelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPa if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) { diff += kDexCacheArrayLwOffset; } + diff += (diff & 0x8000) << 1; // Account for sign extension in addiu. const uint8_t expected_code[] = { 0x00, 0x00, 0x10, 0x04, static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x12, 0x3C, - static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x36, 0x21, 0x90, 0x5F, 0x02, + static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26, }; EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); } diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index cf824a14e3..26c8254c76 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1452,6 +1452,19 @@ static bool CoherentConstantAndType(Location constant, Primitive::Type type) { (cst->IsDoubleConstant() && type == Primitive::kPrimDouble); } +// Allocate a scratch register from the VIXL pool, querying first into +// the floating-point register pool, and then the the core register +// pool. This is essentially a reimplementation of +// vixl::aarch64::UseScratchRegisterScope::AcquireCPURegisterOfSize +// using a different allocation strategy. +static CPURegister AcquireFPOrCoreCPURegisterOfSize(vixl::aarch64::MacroAssembler* masm, + vixl::aarch64::UseScratchRegisterScope* temps, + int size_in_bits) { + return masm->GetScratchFPRegisterList()->IsEmpty() + ? CPURegister(temps->AcquireRegisterOfSize(size_in_bits)) + : CPURegister(temps->AcquireVRegisterOfSize(size_in_bits)); +} + void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Primitive::Type dst_type) { @@ -1563,8 +1576,16 @@ void CodeGeneratorARM64::MoveLocation(Location destination, // a move is blocked by a another move requiring a scratch FP // register, which would reserve D31). To prevent this issue, we // ask for a scratch register of any type (core or FP). - CPURegister temp = - temps.AcquireCPURegisterOfSize(destination.IsDoubleStackSlot() ? kXRegSize : kWRegSize); + // + // Also, we start by asking for a FP scratch register first, as the + // demand of scratch core registers is higher. This is why we + // use AcquireFPOrCoreCPURegisterOfSize instead of + // UseScratchRegisterScope::AcquireCPURegisterOfSize, which + // allocates core scratch registers first. + CPURegister temp = AcquireFPOrCoreCPURegisterOfSize( + GetVIXLAssembler(), + &temps, + (destination.IsDoubleStackSlot() ? kXRegSize : kWRegSize)); __ Ldr(temp, StackOperandFrom(source)); __ Str(temp, StackOperandFrom(destination)); } diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 76be74e921..a095970a1e 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -258,8 +258,10 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { DCHECK_NE(out.AsRegister<Register>(), AT); CodeGeneratorMIPS::PcRelativePatchInfo* info = mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - mips_codegen->EmitPcRelativeAddressPlaceholder(info, TMP, base); - __ StoreToOffset(kStoreWord, out.AsRegister<Register>(), TMP, 0); + bool reordering = __ SetReorder(false); + mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info, TMP, base); + __ StoreToOffset(kStoreWord, out.AsRegister<Register>(), TMP, /* placeholder */ 0x5678); + __ SetReorder(reordering); } __ B(GetExitLabel()); } @@ -313,8 +315,10 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { DCHECK_NE(out, AT); CodeGeneratorMIPS::PcRelativePatchInfo* info = mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index); - mips_codegen->EmitPcRelativeAddressPlaceholder(info, TMP, base); - __ StoreToOffset(kStoreWord, out, TMP, 0); + bool reordering = __ SetReorder(false); + mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info, TMP, base); + __ StoreToOffset(kStoreWord, out, TMP, /* placeholder */ 0x5678); + __ SetReorder(reordering); __ B(GetExitLabel()); } @@ -1127,16 +1131,15 @@ Literal* CodeGeneratorMIPS::DeduplicateBootImageAddressLiteral(uint32_t address) return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map); } -void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholder( - PcRelativePatchInfo* info, Register out, Register base) { - bool reordering = __ SetReorder(false); +void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, + Register out, + Register base) { if (GetInstructionSetFeatures().IsR6()) { DCHECK_EQ(base, ZERO); __ Bind(&info->high_label); __ Bind(&info->pc_rel_label); - // Add a 32-bit offset to PC. + // Add the high half of a 32-bit offset to PC. __ Auipc(out, /* placeholder */ 0x1234); - __ Addiu(out, out, /* placeholder */ 0x5678); } else { // If base is ZERO, emit NAL to obtain the actual base. if (base == ZERO) { @@ -1150,11 +1153,11 @@ void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholder( if (base == ZERO) { __ Bind(&info->pc_rel_label); } - __ Ori(out, out, /* placeholder */ 0x5678); - // Add a 32-bit offset to PC. + // Add the high half of a 32-bit offset to PC. __ Addu(out, out, (base == ZERO) ? RA : base); } - __ SetReorder(reordering); + // The immediately following instruction will add the sign-extended low half of the 32-bit + // offset to `out` (e.g. lw, jialc, addiu). } void CodeGeneratorMIPS::MarkGCCard(Register object, @@ -5159,7 +5162,8 @@ void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invo // art::PrepareForRegisterAllocation. DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); - bool has_extra_input = invoke->HasPcRelativeDexCache(); + bool is_r6 = codegen_->GetInstructionSetFeatures().IsR6(); + bool has_extra_input = invoke->HasPcRelativeDexCache() && !is_r6; IntrinsicLocationsBuilderMIPS intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { @@ -5200,12 +5204,13 @@ HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( if (kEmitCompilerReadBarrier) { UNIMPLEMENTED(FATAL) << "for read barrier"; } - // We disable PC-relative load when there is an irreducible loop, as the optimization + // We disable PC-relative load on pre-R6 when there is an irreducible loop, as the optimization // is incompatible with it. // TODO: Create as many MipsDexCacheArraysBase instructions as needed for methods // with irreducible loops. bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); - bool fallback_load = has_irreducible_loops; + bool is_r6 = GetInstructionSetFeatures().IsR6(); + bool fallback_load = has_irreducible_loops && !is_r6; switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: DCHECK(!GetCompilerOptions().GetCompilePic()); @@ -5238,10 +5243,11 @@ HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( if (kEmitCompilerReadBarrier) { UNIMPLEMENTED(FATAL) << "for read barrier"; } - // We disable pc-relative load when there is an irreducible loop, as the optimization + // We disable PC-relative load on pre-R6 when there is an irreducible loop, as the optimization // is incompatible with it. bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); - bool fallback_load = has_irreducible_loops; + bool is_r6 = GetInstructionSetFeatures().IsR6(); + bool fallback_load = has_irreducible_loops && !is_r6; switch (desired_class_load_kind) { case HLoadClass::LoadKind::kReferrersClass: fallback_load = false; @@ -5259,6 +5265,7 @@ HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); + // TODO: implement. fallback_load = true; break; case HLoadClass::LoadKind::kDexCacheViaMethod: @@ -5273,6 +5280,7 @@ HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( Register CodeGeneratorMIPS::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp) { + CHECK(!GetInstructionSetFeatures().IsR6()); CHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); if (!invoke->GetLocations()->Intrinsified()) { @@ -5301,13 +5309,13 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticO const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; - // We disable PC-relative load when there is an irreducible loop, as the optimization + // We disable PC-relative load on pre-R6 when there is an irreducible loop, as the optimization // is incompatible with it. bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); - bool fallback_load = true; + bool is_r6 = GetInstructionSetFeatures().IsR6(); + bool fallback_load = has_irreducible_loops && !is_r6; switch (dispatch_info.method_load_kind) { case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: - fallback_load = has_irreducible_loops; break; default: fallback_load = false; @@ -5325,7 +5333,8 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind(); HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation(); - Register base_reg = invoke->HasPcRelativeDexCache() + bool is_r6 = GetInstructionSetFeatures().IsR6(); + Register base_reg = (invoke->HasPcRelativeDexCache() && !is_r6) ? GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>()) : ZERO; @@ -5346,14 +5355,23 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress()); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { - HMipsDexCacheArraysBase* base = - invoke->InputAt(invoke->GetSpecialInputIndex())->AsMipsDexCacheArraysBase(); - int32_t offset = - invoke->GetDexCacheArrayOffset() - base->GetElementOffset() - kDexCacheArrayLwOffset; - __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); + case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: + if (is_r6) { + uint32_t offset = invoke->GetDexCacheArrayOffset(); + CodeGeneratorMIPS::PcRelativePatchInfo* info = + NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset); + bool reordering = __ SetReorder(false); + EmitPcRelativeAddressPlaceholderHigh(info, TMP, ZERO); + __ Lw(temp.AsRegister<Register>(), TMP, /* placeholder */ 0x5678); + __ SetReorder(reordering); + } else { + HMipsDexCacheArraysBase* base = + invoke->InputAt(invoke->GetSpecialInputIndex())->AsMipsDexCacheArraysBase(); + int32_t offset = + invoke->GetDexCacheArrayOffset() - base->GetElementOffset() - kDexCacheArrayLwOffset; + __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); + } break; - } case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); Register reg = temp.AsRegister<Register>(); @@ -5546,7 +5564,10 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); - codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); + bool reordering = __ SetReorder(false); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg); + __ Addiu(out, out, /* placeholder */ 0x5678); + __ SetReorder(reordering); break; } case HLoadClass::LoadKind::kBootImageAddress: { @@ -5562,8 +5583,10 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF case HLoadClass::LoadKind::kBssEntry: { CodeGeneratorMIPS::PcRelativePatchInfo* info = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); - codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); - __ LoadFromOffset(kLoadWord, out, out, 0); + bool reordering = __ SetReorder(false); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg); + __ LoadFromOffset(kLoadWord, out, out, /* placeholder */ 0x5678); + __ SetReorder(reordering); generate_null_check = true; break; } @@ -5678,7 +5701,10 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); - codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); + bool reordering = __ SetReorder(false); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg); + __ Addiu(out, out, /* placeholder */ 0x5678); + __ SetReorder(reordering); return; // No dex cache slow path. } case HLoadString::LoadKind::kBootImageAddress: { @@ -5694,8 +5720,10 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); - codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); - __ LoadFromOffset(kLoadWord, out, out, 0); + bool reordering = __ SetReorder(false); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg); + __ LoadFromOffset(kLoadWord, out, out, /* placeholder */ 0x5678); + __ SetReorder(reordering); SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load); codegen_->AddSlowPath(slow_path); __ Beqz(out, slow_path->GetEntryLabel()); @@ -6894,8 +6922,12 @@ void InstructionCodeGeneratorMIPS::VisitMipsDexCacheArraysBase(HMipsDexCacheArra Register reg = base->GetLocations()->Out().AsRegister<Register>(); CodeGeneratorMIPS::PcRelativePatchInfo* info = codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset()); + CHECK(!codegen_->GetInstructionSetFeatures().IsR6()); + bool reordering = __ SetReorder(false); // TODO: Reuse MipsComputeBaseMethodAddress on R2 instead of passing ZERO to force emitting NAL. - codegen_->EmitPcRelativeAddressPlaceholder(info, reg, ZERO); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, reg, ZERO); + __ Addiu(reg, reg, /* placeholder */ 0x5678); + __ SetReorder(reordering); } void LocationsBuilderMIPS::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index c8fd325999..e92eeef88f 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -463,7 +463,7 @@ class CodeGeneratorMIPS : public CodeGenerator { Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); - void EmitPcRelativeAddressPlaceholder(PcRelativePatchInfo* info, Register out, Register base); + void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, Register out, Register base); private: Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 192b4a5050..e96e3d75e1 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -3117,14 +3117,6 @@ void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad( Location root, GpuRegister obj, uint32_t offset) { - // When handling PC-relative loads, the caller calls - // EmitPcRelativeAddressPlaceholderHigh() and then GenerateGcRootFieldLoad(). - // The relative patcher expects the two methods to emit the following patchable - // sequence of instructions in this case: - // auipc reg1, 0x1234 // 0x1234 is a placeholder for offset_high. - // lwu reg2, 0x5678(reg1) // 0x5678 is a placeholder for offset_low. - // TODO: Adjust GenerateGcRootFieldLoad() and its caller when this method is - // extended (e.g. for read barriers) so as not to break the relative patcher. GpuRegister root_reg = root.AsRegister<GpuRegister>(); if (kEmitCompilerReadBarrier) { UNIMPLEMENTED(FATAL) << "for read barrier"; diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index e3f3df0ff5..763d6da6f5 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -1067,6 +1067,39 @@ TEST_F(CodegenTest, ARMVIXLParallelMoveResolver) { } #endif +#ifdef ART_ENABLE_CODEGEN_arm64 +// Regression test for b/34760542. +TEST_F(CodegenTest, ARM64ParallelMoveResolverB34760542) { + std::unique_ptr<const Arm64InstructionSetFeatures> features( + Arm64InstructionSetFeatures::FromCppDefines()); + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = CreateGraph(&allocator); + arm64::CodeGeneratorARM64 codegen(graph, *features.get(), CompilerOptions()); + + codegen.Initialize(); + + // The following ParallelMove used to fail this assertion: + // + // Assertion failed (!available->IsEmpty()) + // + // in vixl::aarch64::UseScratchRegisterScope::AcquireNextAvailable. + HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena()); + move->AddMove(Location::DoubleStackSlot(0), + Location::DoubleStackSlot(257), + Primitive::kPrimDouble, + nullptr); + move->AddMove(Location::DoubleStackSlot(257), + Location::DoubleStackSlot(0), + Primitive::kPrimDouble, + nullptr); + codegen.GetMoveResolver()->EmitNativeCode(move); + + InternalCodeAllocator code_allocator; + codegen.Finalize(&code_allocator); +} +#endif + #ifdef ART_ENABLE_CODEGEN_mips TEST_F(CodegenTest, MipsClobberRA) { std::unique_ptr<const MipsInstructionSetFeatures> features_mips( diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.cc b/compiler/optimizing/dex_cache_array_fixups_mips.cc index 04a4294c48..7734f9197d 100644 --- a/compiler/optimizing/dex_cache_array_fixups_mips.cc +++ b/compiler/optimizing/dex_cache_array_fixups_mips.cc @@ -47,7 +47,7 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { // Computing the dex cache base for PC-relative accesses will clobber RA with // the NAL instruction on R2. Take a note of this before generating the method // entry. - if (!dex_cache_array_bases_.empty() && !codegen_->GetInstructionSetFeatures().IsR6()) { + if (!dex_cache_array_bases_.empty()) { codegen_->ClobberRA(); } } @@ -92,6 +92,11 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { }; void DexCacheArrayFixups::Run() { + CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen_); + if (mips_codegen->GetInstructionSetFeatures().IsR6()) { + // Do nothing for R6 because it has PC-relative addressing. + return; + } if (graph_->HasIrreducibleLoops()) { // Do not run this optimization, as irreducible loops do not work with an instruction // that can be live-in at the irreducible loop header. diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 3973985338..5539413aad 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -57,14 +57,18 @@ static bool IsIntAndGet(HInstruction* instruction, int64_t* value) { return false; } -/** Returns b^e for b,e >= 1. */ -static int64_t IntPow(int64_t b, int64_t e) { +/** Returns b^e for b,e >= 1. Sets overflow if arithmetic wrap-around occurred. */ +static int64_t IntPow(int64_t b, int64_t e, /*out*/ bool* overflow) { DCHECK_GE(b, 1); DCHECK_GE(e, 1); int64_t pow = 1; while (e) { if (e & 1) { + int64_t oldpow = pow; pow *= b; + if (pow < oldpow) { + *overflow = true; + } } e >>= 1; b *= b; @@ -1020,20 +1024,27 @@ bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::Induct HInstruction* opb = nullptr; if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) && GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) { - // Compute f ^ m for known maximum index value m. - int64_t fpow = IntPow(f, m); if (graph != nullptr) { - DCHECK(info->operation == HInductionVarAnalysis::kMul || - info->operation == HInductionVarAnalysis::kDiv); Primitive::Type type = info->type; + // Compute f ^ m for known maximum index value m. + bool overflow = false; + int64_t fpow = IntPow(f, m, &overflow); + if (info->operation == HInductionVarAnalysis::kDiv) { + // For division, any overflow truncates to zero. + if (overflow || (type != Primitive::kPrimLong && !CanLongValueFitIntoInt(fpow))) { + fpow = 0; + } + } else if (type != Primitive::kPrimLong) { + // For multiplication, okay to truncate to required precision. + DCHECK(info->operation == HInductionVarAnalysis::kMul); + fpow = static_cast<int32_t>(fpow); + } + // Generate code. if (fpow == 0) { // Special case: repeated mul/div always yields zero. *result = graph->GetConstant(type, 0); } else { // Last value: a * f ^ m + b or a * f ^ -m + b. - if (type != Primitive::kPrimLong) { - fpow = static_cast<int32_t>(fpow); // okay to truncate - } HInstruction* e = nullptr; if (info->operation == HInductionVarAnalysis::kMul) { e = new (graph->GetArena()) HMul(type, opa, graph->GetConstant(type, fpow)); diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 91d9c56d10..1a10173ed7 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -514,6 +514,18 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMathSqrt(HInvoke* invoke) { __ Vsqrt(OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); } +void IntrinsicLocationsBuilderARMVIXL::VisitMathRint(HInvoke* invoke) { + if (features_.HasARMv8AInstructions()) { + CreateFPToFPLocations(arena_, invoke); + } +} + +void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) { + DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions()); + ArmVIXLAssembler* assembler = GetAssembler(); + __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0)); +} + void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) { CreateIntToIntLocations(arena_, invoke); } @@ -2772,7 +2784,6 @@ UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxDoubleDouble) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxFloatFloat) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinLongLong) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxLongLong) -UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRint) UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundFloat) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure. diff --git a/runtime/Android.bp b/runtime/Android.bp index 196c65e11a..540df5a554 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -186,6 +186,7 @@ cc_defaults { "reflection.cc", "runtime.cc", "runtime_callbacks.cc", + "runtime_common.cc", "runtime_options.cc", "signal_catcher.cc", "stack.cc", diff --git a/runtime/art_method.cc b/runtime/art_method.cc index ec789f51ef..61ff41742b 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -55,6 +55,17 @@ extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, const char*); +ArtMethod* ArtMethod::GetNonObsoleteMethod() { + DCHECK_EQ(kRuntimePointerSize, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + if (LIKELY(!IsObsolete())) { + return this; + } else if (IsDirect()) { + return &GetDeclaringClass()->GetDirectMethodsSlice(kRuntimePointerSize)[GetMethodIndex()]; + } else { + return GetDeclaringClass()->GetVTableEntry(GetMethodIndex(), kRuntimePointerSize); + } +} + ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) { DCHECK(!IsNative()); if (!IsAbstract()) { diff --git a/runtime/art_method.h b/runtime/art_method.h index f145d7c416..e4db2c7324 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -570,6 +570,8 @@ class ArtMethod FINAL { ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + ArtMethod* GetNonObsoleteMethod() REQUIRES_SHARED(Locks::mutator_lock_); + // May cause thread suspension due to class resolution. bool EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> params) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 65178ed066..e928344fb6 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -99,7 +99,7 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho if (UNLIKELY(resolved_type == nullptr)) { StackHandleScope<2> hs(Thread::Current()); ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); @@ -146,7 +146,7 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, if (UNLIKELY(resolved_method == nullptr)) { ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); StackHandleScope<2> hs(self); - Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache())); + Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader())); const DexFile* dex_file = h_dex_cache->GetDexFile(); resolved_method = ResolveMethod<kResolveMode>(*dex_file, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 3005c2a2f1..f11e2cba10 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -558,10 +558,8 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t } Instrumentation::InstrumentationLevel Instrumentation::GetCurrentInstrumentationLevel() const { - if (interpreter_stubs_installed_ && interpret_only_) { + if (interpreter_stubs_installed_) { return InstrumentationLevel::kInstrumentWithInterpreter; - } else if (interpreter_stubs_installed_) { - return InstrumentationLevel::kInstrumentWithInterpreterAndJit; } else if (entry_exit_stubs_installed_) { return InstrumentationLevel::kInstrumentWithInstrumentationStubs; } else { @@ -570,11 +568,8 @@ Instrumentation::InstrumentationLevel Instrumentation::GetCurrentInstrumentation } bool Instrumentation::RequiresInstrumentationInstallation(InstrumentationLevel new_level) const { - // We need to reinstall instrumentation if we go to a different level or if the current level is - // kInstrumentWithInterpreterAndJit since that level does not force all code to always use the - // interpreter and so we might have started running optimized code again. - return new_level == InstrumentationLevel::kInstrumentWithInterpreterAndJit || - GetCurrentInstrumentationLevel() != new_level; + // We need to reinstall instrumentation if we go to a different level. + return GetCurrentInstrumentationLevel() != new_level; } void Instrumentation::ConfigureStubs(const char* key, InstrumentationLevel desired_level) { @@ -605,7 +600,7 @@ void Instrumentation::ConfigureStubs(const char* key, InstrumentationLevel desir Locks::mutator_lock_->AssertExclusiveHeld(self); Locks::thread_list_lock_->AssertNotHeld(self); if (requested_level > InstrumentationLevel::kInstrumentNothing) { - if (requested_level >= InstrumentationLevel::kInstrumentWithInterpreterAndJit) { + if (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) { interpreter_stubs_installed_ = true; entry_exit_stubs_installed_ = true; } else { @@ -881,14 +876,6 @@ bool Instrumentation::ShouldNotifyMethodEnterExitEvents() const { return !deoptimization_enabled_ && !interpreter_stubs_installed_; } -// TODO we don't check deoptimization_enabled_ because currently there isn't really any support for -// multiple users of instrumentation. Since this is just a temporary state anyway pending work to -// ensure that the current_method doesn't get kept across suspend points this should be okay. -// TODO Remove once b/33630159 is resolved. -void Instrumentation::ReJitEverything(const char* key) { - ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInterpreterAndJit); -} - void Instrumentation::DeoptimizeEverything(const char* key) { CHECK(deoptimization_enabled_); ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInterpreter); diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 39606f73f6..01071a541f 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -133,9 +133,6 @@ class Instrumentation { enum class InstrumentationLevel { kInstrumentNothing, // execute without instrumentation kInstrumentWithInstrumentationStubs, // execute with instrumentation entry/exit stubs - kInstrumentWithInterpreterAndJit, // execute with interpreter initially and later the JIT - // (if it is enabled). This level is special in that it - // always requires re-instrumentation. kInstrumentWithInterpreter // execute with interpreter }; @@ -166,13 +163,6 @@ class Instrumentation { } bool ShouldNotifyMethodEnterExitEvents() const REQUIRES_SHARED(Locks::mutator_lock_); - // Executes everything with the interpreter/jit (if available). - void ReJitEverything(const char* key) - REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_) - REQUIRES(!Locks::thread_list_lock_, - !Locks::classlinker_classes_lock_, - !deoptimized_methods_lock_); - // Executes everything with interpreter. void DeoptimizeEverything(const char* key) REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_) diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 4f679f4680..4b8108accf 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -75,9 +75,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { StackVisitor::StackWalkKind::kIncludeInlinedFrames), allocator_(allocator), obsoleted_methods_(obsoleted_methods), - obsolete_maps_(obsolete_maps), - is_runtime_frame_(false) { - } + obsolete_maps_(obsolete_maps) { } ~ObsoleteMethodStackVisitor() OVERRIDE {} @@ -100,21 +98,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) { art::ArtMethod* old_method = GetMethod(); - // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt - // works through runtime methods. - bool prev_was_runtime_frame_ = is_runtime_frame_; - is_runtime_frame_ = old_method->IsRuntimeMethod(); if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) { - // The check below works since when we deoptimize we set shadow frames for all frames until a - // native/runtime transition and for those set the return PC to a function that will complete - // the deoptimization. This does leave us with the unfortunate side-effect that frames just - // below runtime frames cannot be deoptimized at the moment. - // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt - // works through runtime methods. - // TODO b/33616143 - if (!IsShadowFrame() && prev_was_runtime_frame_) { - LOG(FATAL) << "Deoptimization failed due to runtime method in stack. See b/33616143"; - } // We cannot ensure that the right dex file is used in inlined frames so we don't support // redefining them. DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition"; @@ -163,9 +147,6 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of // the redefined classes ClassExt by the caller. std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_; - // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt - // works through runtime methods. - bool is_runtime_frame_; }; jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED, @@ -509,18 +490,6 @@ void Redefiner::ClassRedefinition::FillObsoleteMethodMap( } } -// TODO It should be possible to only deoptimize the specific obsolete methods. -// TODO ReJitEverything can (sort of) fail. In certain cases it will skip deoptimizing some frames. -// If one of these frames is an obsolete method we have a problem. b/33616143 -// TODO This shouldn't be necessary once we can ensure that the current method is not kept in -// registers across suspend points. -// TODO Pending b/33630159 -void Redefiner::EnsureObsoleteMethodsAreDeoptimized() { - art::ScopedAssertNoThreadSuspension nts("Deoptimizing everything!"); - art::instrumentation::Instrumentation* i = runtime_->GetInstrumentation(); - i->ReJitEverything("libOpenJkdJvmti - Class Redefinition"); -} - bool Redefiner::ClassRedefinition::CheckClass() { // TODO Might just want to put it in a ObjPtr and NoSuspend assert. art::StackHandleScope<1> hs(driver_->self_); @@ -885,15 +854,6 @@ jvmtiError Redefiner::Run() { holder.GetOriginalDexFileBytes(counter)); counter++; } - // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have - // pointers to their ArtMethod's stashed in registers that they then use to attempt to hit the - // DexCache. (b/33630159) - // TODO This can fail (leave some methods optimized) near runtime methods (including - // quick-to-interpreter transition function). - // TODO We probably don't need this at all once we have a way to ensure that the - // current_art_method is never stashed in a (physical) register by the JIT and lost to the - // stack-walker. - EnsureObsoleteMethodsAreDeoptimized(); // TODO Verify the new Class. // TODO Shrink the obsolete method maps if possible? // TODO find appropriate class loader. diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 4492db0f56..5bcaef8971 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -243,14 +243,6 @@ class Redefiner { REQUIRES_SHARED(art::Locks::mutator_lock_); void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_); - // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have - // pointers to their ArtMethods stashed in registers that they then use to attempt to hit the - // DexCache. - void EnsureObsoleteMethodsAreDeoptimized() - REQUIRES(art::Locks::mutator_lock_) - REQUIRES(!art::Locks::thread_list_lock_, - !art::Locks::classlinker_classes_lock_); - void RecordFailure(jvmtiError result, const std::string& class_sig, const std::string& error_msg); void RecordFailure(jvmtiError result, const std::string& error_msg) { RecordFailure(result, "NO CLASS", error_msg); diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc index 0a996a9e55..495296cf7d 100644 --- a/runtime/runtime_android.cc +++ b/runtime/runtime_android.cc @@ -14,56 +14,33 @@ * limitations under the License. */ +#include "runtime.h" + #include <signal.h> -#include <string.h> -#include <sys/utsname.h> -#include <inttypes.h> -#include "base/logging.h" -#include "base/mutex.h" -#include "thread-inl.h" -#include "utils.h" +#include <cstring> -namespace art { +#include "runtime_common.h" -static constexpr bool kUseSignalHandler = false; +namespace art { struct sigaction old_action; -void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) { - static bool handling_unexpected_signal = false; - if (handling_unexpected_signal) { - LogHelper::LogLineLowStack(__FILE__, - __LINE__, - ::android::base::FATAL_WITHOUT_ABORT, - "HandleUnexpectedSignal reentered\n"); - _exit(1); - } - handling_unexpected_signal = true; - gAborting++; // set before taking any locks - MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); - Runtime* runtime = Runtime::Current(); - if (runtime != nullptr) { - // Print this out first in case DumpObject faults. - LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage(); - } +void HandleUnexpectedSignalAndroid(int signal_number, siginfo_t* info, void* raw_context) { + HandleUnexpectedSignalCommon(signal_number, info, raw_context, /* running_on_linux */ false); + // Run the old signal handler. old_action.sa_sigaction(signal_number, info, raw_context); } void Runtime::InitPlatformSignalHandlers() { - if (kUseSignalHandler) { - struct sigaction action; - memset(&action, 0, sizeof(action)); - sigemptyset(&action.sa_mask); - action.sa_sigaction = HandleUnexpectedSignal; - // Use the three-argument sa_sigaction handler. - action.sa_flags |= SA_SIGINFO; - // Use the alternate signal stack so we can catch stack overflows. - action.sa_flags |= SA_ONSTACK; - int rc = 0; - rc += sigaction(SIGSEGV, &action, &old_action); - CHECK_EQ(rc, 0); + // Enable the signal handler dumping crash information to the logcat + // when the Android root is not "/system". + const char* android_root = getenv("ANDROID_ROOT"); + if (android_root != nullptr && strcmp(android_root, "/system") != 0) { + InitPlatformSignalHandlersCommon(HandleUnexpectedSignalAndroid, + &old_action, + /* handle_timeout_signal */ false); } } diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc new file mode 100644 index 0000000000..70aff37961 --- /dev/null +++ b/runtime/runtime_common.cc @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "runtime_common.h" + +#include <signal.h> + +#include <cinttypes> +#include <iostream> +#include <sstream> +#include <string> + +#include "android-base/stringprintf.h" + +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "native_stack_dump.h" +#include "thread-inl.h" +#include "thread_list.h" + +namespace art { + +using android::base::StringPrintf; + +static constexpr bool kUseSigRTTimeout = true; +static constexpr bool kDumpNativeStackOnTimeout = true; + +const char* GetSignalName(int signal_number) { + switch (signal_number) { + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGILL: return "SIGILL"; + case SIGPIPE: return "SIGPIPE"; + case SIGSEGV: return "SIGSEGV"; +#if defined(SIGSTKFLT) + case SIGSTKFLT: return "SIGSTKFLT"; +#endif + case SIGTRAP: return "SIGTRAP"; + } + return "??"; +} + +const char* GetSignalCodeName(int signal_number, int signal_code) { + // Try the signal-specific codes... + switch (signal_number) { + case SIGILL: + switch (signal_code) { + case ILL_ILLOPC: return "ILL_ILLOPC"; + case ILL_ILLOPN: return "ILL_ILLOPN"; + case ILL_ILLADR: return "ILL_ILLADR"; + case ILL_ILLTRP: return "ILL_ILLTRP"; + case ILL_PRVOPC: return "ILL_PRVOPC"; + case ILL_PRVREG: return "ILL_PRVREG"; + case ILL_COPROC: return "ILL_COPROC"; + case ILL_BADSTK: return "ILL_BADSTK"; + } + break; + case SIGBUS: + switch (signal_code) { + case BUS_ADRALN: return "BUS_ADRALN"; + case BUS_ADRERR: return "BUS_ADRERR"; + case BUS_OBJERR: return "BUS_OBJERR"; + } + break; + case SIGFPE: + switch (signal_code) { + case FPE_INTDIV: return "FPE_INTDIV"; + case FPE_INTOVF: return "FPE_INTOVF"; + case FPE_FLTDIV: return "FPE_FLTDIV"; + case FPE_FLTOVF: return "FPE_FLTOVF"; + case FPE_FLTUND: return "FPE_FLTUND"; + case FPE_FLTRES: return "FPE_FLTRES"; + case FPE_FLTINV: return "FPE_FLTINV"; + case FPE_FLTSUB: return "FPE_FLTSUB"; + } + break; + case SIGSEGV: + switch (signal_code) { + case SEGV_MAPERR: return "SEGV_MAPERR"; + case SEGV_ACCERR: return "SEGV_ACCERR"; +#if defined(SEGV_BNDERR) + case SEGV_BNDERR: return "SEGV_BNDERR"; +#endif + } + break; + case SIGTRAP: + switch (signal_code) { + case TRAP_BRKPT: return "TRAP_BRKPT"; + case TRAP_TRACE: return "TRAP_TRACE"; + } + break; + } + // Then the other codes... + switch (signal_code) { + case SI_USER: return "SI_USER"; +#if defined(SI_KERNEL) + case SI_KERNEL: return "SI_KERNEL"; +#endif + case SI_QUEUE: return "SI_QUEUE"; + case SI_TIMER: return "SI_TIMER"; + case SI_MESGQ: return "SI_MESGQ"; + case SI_ASYNCIO: return "SI_ASYNCIO"; +#if defined(SI_SIGIO) + case SI_SIGIO: return "SI_SIGIO"; +#endif +#if defined(SI_TKILL) + case SI_TKILL: return "SI_TKILL"; +#endif + } + // Then give up... + return "?"; +} + +struct UContext { + explicit UContext(void* raw_context) + : context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) {} + + void Dump(std::ostream& os) const; + + void DumpRegister32(std::ostream& os, const char* name, uint32_t value) const; + void DumpRegister64(std::ostream& os, const char* name, uint64_t value) const; + + void DumpX86Flags(std::ostream& os, uint32_t flags) const; + + mcontext_t& context; +}; + +void UContext::Dump(std::ostream& os) const { + // TODO: support non-x86 hosts. +#if defined(__APPLE__) && defined(__i386__) + DumpRegister32(os, "eax", context->__ss.__eax); + DumpRegister32(os, "ebx", context->__ss.__ebx); + DumpRegister32(os, "ecx", context->__ss.__ecx); + DumpRegister32(os, "edx", context->__ss.__edx); + os << '\n'; + + DumpRegister32(os, "edi", context->__ss.__edi); + DumpRegister32(os, "esi", context->__ss.__esi); + DumpRegister32(os, "ebp", context->__ss.__ebp); + DumpRegister32(os, "esp", context->__ss.__esp); + os << '\n'; + + DumpRegister32(os, "eip", context->__ss.__eip); + os << " "; + DumpRegister32(os, "eflags", context->__ss.__eflags); + DumpX86Flags(os, context->__ss.__eflags); + os << '\n'; + + DumpRegister32(os, "cs", context->__ss.__cs); + DumpRegister32(os, "ds", context->__ss.__ds); + DumpRegister32(os, "es", context->__ss.__es); + DumpRegister32(os, "fs", context->__ss.__fs); + os << '\n'; + DumpRegister32(os, "gs", context->__ss.__gs); + DumpRegister32(os, "ss", context->__ss.__ss); +#elif defined(__linux__) && defined(__i386__) + DumpRegister32(os, "eax", context.gregs[REG_EAX]); + DumpRegister32(os, "ebx", context.gregs[REG_EBX]); + DumpRegister32(os, "ecx", context.gregs[REG_ECX]); + DumpRegister32(os, "edx", context.gregs[REG_EDX]); + os << '\n'; + + DumpRegister32(os, "edi", context.gregs[REG_EDI]); + DumpRegister32(os, "esi", context.gregs[REG_ESI]); + DumpRegister32(os, "ebp", context.gregs[REG_EBP]); + DumpRegister32(os, "esp", context.gregs[REG_ESP]); + os << '\n'; + + DumpRegister32(os, "eip", context.gregs[REG_EIP]); + os << " "; + DumpRegister32(os, "eflags", context.gregs[REG_EFL]); + DumpX86Flags(os, context.gregs[REG_EFL]); + os << '\n'; + + DumpRegister32(os, "cs", context.gregs[REG_CS]); + DumpRegister32(os, "ds", context.gregs[REG_DS]); + DumpRegister32(os, "es", context.gregs[REG_ES]); + DumpRegister32(os, "fs", context.gregs[REG_FS]); + os << '\n'; + DumpRegister32(os, "gs", context.gregs[REG_GS]); + DumpRegister32(os, "ss", context.gregs[REG_SS]); +#elif defined(__linux__) && defined(__x86_64__) + DumpRegister64(os, "rax", context.gregs[REG_RAX]); + DumpRegister64(os, "rbx", context.gregs[REG_RBX]); + DumpRegister64(os, "rcx", context.gregs[REG_RCX]); + DumpRegister64(os, "rdx", context.gregs[REG_RDX]); + os << '\n'; + + DumpRegister64(os, "rdi", context.gregs[REG_RDI]); + DumpRegister64(os, "rsi", context.gregs[REG_RSI]); + DumpRegister64(os, "rbp", context.gregs[REG_RBP]); + DumpRegister64(os, "rsp", context.gregs[REG_RSP]); + os << '\n'; + + DumpRegister64(os, "r8 ", context.gregs[REG_R8]); + DumpRegister64(os, "r9 ", context.gregs[REG_R9]); + DumpRegister64(os, "r10", context.gregs[REG_R10]); + DumpRegister64(os, "r11", context.gregs[REG_R11]); + os << '\n'; + + DumpRegister64(os, "r12", context.gregs[REG_R12]); + DumpRegister64(os, "r13", context.gregs[REG_R13]); + DumpRegister64(os, "r14", context.gregs[REG_R14]); + DumpRegister64(os, "r15", context.gregs[REG_R15]); + os << '\n'; + + DumpRegister64(os, "rip", context.gregs[REG_RIP]); + os << " "; + DumpRegister32(os, "eflags", context.gregs[REG_EFL]); + DumpX86Flags(os, context.gregs[REG_EFL]); + os << '\n'; + + DumpRegister32(os, "cs", (context.gregs[REG_CSGSFS]) & 0x0FFFF); + DumpRegister32(os, "gs", (context.gregs[REG_CSGSFS] >> 16) & 0x0FFFF); + DumpRegister32(os, "fs", (context.gregs[REG_CSGSFS] >> 32) & 0x0FFFF); + os << '\n'; +#else + os << "Unknown architecture/word size/OS in ucontext dump"; +#endif +} + +void UContext::DumpRegister32(std::ostream& os, const char* name, uint32_t value) const { + os << StringPrintf(" %6s: 0x%08x", name, value); +} + +void UContext::DumpRegister64(std::ostream& os, const char* name, uint64_t value) const { + os << StringPrintf(" %6s: 0x%016" PRIx64, name, value); +} + +void UContext::DumpX86Flags(std::ostream& os, uint32_t flags) const { + os << " ["; + if ((flags & (1 << 0)) != 0) { + os << " CF"; + } + if ((flags & (1 << 2)) != 0) { + os << " PF"; + } + if ((flags & (1 << 4)) != 0) { + os << " AF"; + } + if ((flags & (1 << 6)) != 0) { + os << " ZF"; + } + if ((flags & (1 << 7)) != 0) { + os << " SF"; + } + if ((flags & (1 << 8)) != 0) { + os << " TF"; + } + if ((flags & (1 << 9)) != 0) { + os << " IF"; + } + if ((flags & (1 << 10)) != 0) { + os << " DF"; + } + if ((flags & (1 << 11)) != 0) { + os << " OF"; + } + os << " ]"; +} + +int GetTimeoutSignal() { +#if defined(__APPLE__) + // Mac does not support realtime signals. + UNUSED(kUseSigRTTimeout); + return -1; +#else + return kUseSigRTTimeout ? (SIGRTMIN + 2) : -1; +#endif +} + +static bool IsTimeoutSignal(int signal_number) { + return signal_number == GetTimeoutSignal(); +} + +#if defined(__APPLE__) +// On macOS, clang complains about art::HandleUnexpectedSignalCommon's +// stack frame size being too large; disable that warning locally. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wframe-larger-than=" +#endif + +void HandleUnexpectedSignalCommon(int signal_number, + siginfo_t* info, + void* raw_context, + bool running_on_linux) { + bool handle_timeout_signal = running_on_linux; + bool dump_on_stderr = running_on_linux; + + static bool handling_unexpected_signal = false; + if (handling_unexpected_signal) { + LogHelper::LogLineLowStack(__FILE__, + __LINE__, + ::android::base::FATAL_WITHOUT_ABORT, + "HandleUnexpectedSignal reentered\n"); + if (handle_timeout_signal) { + if (IsTimeoutSignal(signal_number)) { + // Ignore a recursive timeout. + return; + } + } + _exit(1); + } + handling_unexpected_signal = true; + + gAborting++; // set before taking any locks + MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); + + bool has_address = (signal_number == SIGILL || signal_number == SIGBUS || + signal_number == SIGFPE || signal_number == SIGSEGV); + + OsInfo os_info; + const char* cmd_line = GetCmdLine(); + if (cmd_line == nullptr) { + cmd_line = "<unset>"; // Because no-one called InitLogging. + } + pid_t tid = GetTid(); + std::string thread_name(GetThreadName(tid)); + UContext thread_context(raw_context); + Backtrace thread_backtrace(raw_context); + + std::ostringstream stream; + stream << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n" + << StringPrintf("Fatal signal %d (%s), code %d (%s)", + signal_number, + GetSignalName(signal_number), + info->si_code, + GetSignalCodeName(signal_number, info->si_code)) + << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << '\n' + << "OS: " << Dumpable<OsInfo>(os_info) << '\n' + << "Cmdline: " << cmd_line << '\n' + << "Thread: " << tid << " \"" << thread_name << "\"" << '\n' + << "Registers:\n" << Dumpable<UContext>(thread_context) << '\n' + << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace) << '\n'; + if (dump_on_stderr) { + // Note: We are using cerr directly instead of LOG macros to ensure even just partial output + // makes it out. That means we lose the "dalvikvm..." prefix, but that is acceptable + // considering this is an abort situation. + std::cerr << stream.str() << std::flush; + } else { + LOG(FATAL_WITHOUT_ABORT) << stream.str() << std::flush; + } + if (kIsDebugBuild && signal_number == SIGSEGV) { + PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + } + + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + if (handle_timeout_signal && IsTimeoutSignal(signal_number)) { + // Special timeout signal. Try to dump all threads. + // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts + // are of value here. + runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); + std::cerr << std::endl; + } + + if (dump_on_stderr) { + std::cerr << "Fault message: " << runtime->GetFaultMessage() << std::endl; + } else { + LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage(); + } + } +} + +#if defined(__APPLE__) +#pragma GCC diagnostic pop +#endif + +void InitPlatformSignalHandlersCommon(void (*newact)(int, siginfo_t*, void*), + struct sigaction* oldact, + bool handle_timeout_signal) { + struct sigaction action; + memset(&action, 0, sizeof(action)); + sigemptyset(&action.sa_mask); + action.sa_sigaction = newact; + // Use the three-argument sa_sigaction handler. + action.sa_flags |= SA_SIGINFO; + // Use the alternate signal stack so we can catch stack overflows. + action.sa_flags |= SA_ONSTACK; + + int rc = 0; + rc += sigaction(SIGABRT, &action, oldact); + rc += sigaction(SIGBUS, &action, oldact); + rc += sigaction(SIGFPE, &action, oldact); + rc += sigaction(SIGILL, &action, oldact); + rc += sigaction(SIGPIPE, &action, oldact); + rc += sigaction(SIGSEGV, &action, oldact); +#if defined(SIGSTKFLT) + rc += sigaction(SIGSTKFLT, &action, oldact); +#endif + rc += sigaction(SIGTRAP, &action, oldact); + // Special dump-all timeout. + if (handle_timeout_signal && GetTimeoutSignal() != -1) { + rc += sigaction(GetTimeoutSignal(), &action, oldact); + } + CHECK_EQ(rc, 0); +} + +} // namespace art diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h new file mode 100644 index 0000000000..832b6bbf3e --- /dev/null +++ b/runtime/runtime_common.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_RUNTIME_COMMON_H_ +#define ART_RUNTIME_RUNTIME_COMMON_H_ + +// Code shared by runtime/runtime_android.cc and runtime/runtime_linux.cc. + +#if defined(__APPLE__) +// On macOS, _XOPEN_SOURCE must be defined to access ucontext +// routines, as they are considered deprecated on that platform. +#define _XOPEN_SOURCE +#endif + +#include <sys/utsname.h> +#include <ucontext.h> + +#include <iomanip> + +#include "base/dumpable.h" +#include "native_stack_dump.h" +#include "utils.h" + +namespace art { + +struct Backtrace { + public: + explicit Backtrace(void* raw_context) : raw_context_(raw_context) {} + void Dump(std::ostream& os) const { + DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_); + } + private: + // Stores the context of the signal that was unexpected and will terminate the runtime. The + // DumpNativeStack code will take care of casting it to the expected type. This is required + // as our signal handler runs on an alternate stack. + void* raw_context_; +}; + +struct OsInfo { + void Dump(std::ostream& os) const { + utsname info; + uname(&info); + // Linux 2.6.38.8-gg784 (x86_64) + // Darwin 11.4.0 (x86_64) + os << info.sysname << " " << info.release << " (" << info.machine << ")"; + } +}; + +const char* GetSignalName(int signal_number); +const char* GetSignalCodeName(int signal_number, int signal_code); + +// Return the signal number we recognize as timeout. -1 means not active/supported. +int GetTimeoutSignal(); + +void HandleUnexpectedSignalCommon(int signal_number, + siginfo_t* info, + void* raw_context, + bool running_on_linux); + +void InitPlatformSignalHandlersCommon(void (*newact)(int, siginfo_t*, void*), + struct sigaction* oldact, + bool handle_timeout_signal); + +} // namespace art + +#endif // ART_RUNTIME_RUNTIME_COMMON_H_ diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index b8894d2569..ad61cf373b 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -17,359 +17,19 @@ #include "runtime.h" #include <signal.h> -#include <string.h> -#include <sys/utsname.h> -#include <inttypes.h> #include <iostream> -#include <sstream> -#include "android-base/stringprintf.h" - -#include "base/dumpable.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "native_stack_dump.h" -#include "thread-inl.h" -#include "thread_list.h" -#include "utils.h" +#include "runtime_common.h" namespace art { -using android::base::StringPrintf; - -static constexpr bool kUseSigRTTimeout = true; -static constexpr bool kDumpNativeStackOnTimeout = true; - -struct Backtrace { - public: - explicit Backtrace(void* raw_context) : raw_context_(raw_context) {} - void Dump(std::ostream& os) const { - DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_); - } - private: - // Stores the context of the signal that was unexpected and will terminate the runtime. The - // DumpNativeStack code will take care of casting it to the expected type. This is required - // as our signal handler runs on an alternate stack. - void* raw_context_; -}; - -struct OsInfo { - void Dump(std::ostream& os) const { - utsname info; - uname(&info); - // Linux 2.6.38.8-gg784 (x86_64) - // Darwin 11.4.0 (x86_64) - os << info.sysname << " " << info.release << " (" << info.machine << ")"; - } -}; - -static const char* GetSignalName(int signal_number) { - switch (signal_number) { - case SIGABRT: return "SIGABRT"; - case SIGBUS: return "SIGBUS"; - case SIGFPE: return "SIGFPE"; - case SIGILL: return "SIGILL"; - case SIGPIPE: return "SIGPIPE"; - case SIGSEGV: return "SIGSEGV"; -#if defined(SIGSTKFLT) - case SIGSTKFLT: return "SIGSTKFLT"; -#endif - case SIGTRAP: return "SIGTRAP"; - } - return "??"; -} - -static const char* GetSignalCodeName(int signal_number, int signal_code) { - // Try the signal-specific codes... - switch (signal_number) { - case SIGILL: - switch (signal_code) { - case ILL_ILLOPC: return "ILL_ILLOPC"; - case ILL_ILLOPN: return "ILL_ILLOPN"; - case ILL_ILLADR: return "ILL_ILLADR"; - case ILL_ILLTRP: return "ILL_ILLTRP"; - case ILL_PRVOPC: return "ILL_PRVOPC"; - case ILL_PRVREG: return "ILL_PRVREG"; - case ILL_COPROC: return "ILL_COPROC"; - case ILL_BADSTK: return "ILL_BADSTK"; - } - break; - case SIGBUS: - switch (signal_code) { - case BUS_ADRALN: return "BUS_ADRALN"; - case BUS_ADRERR: return "BUS_ADRERR"; - case BUS_OBJERR: return "BUS_OBJERR"; - } - break; - case SIGFPE: - switch (signal_code) { - case FPE_INTDIV: return "FPE_INTDIV"; - case FPE_INTOVF: return "FPE_INTOVF"; - case FPE_FLTDIV: return "FPE_FLTDIV"; - case FPE_FLTOVF: return "FPE_FLTOVF"; - case FPE_FLTUND: return "FPE_FLTUND"; - case FPE_FLTRES: return "FPE_FLTRES"; - case FPE_FLTINV: return "FPE_FLTINV"; - case FPE_FLTSUB: return "FPE_FLTSUB"; - } - break; - case SIGSEGV: - switch (signal_code) { - case SEGV_MAPERR: return "SEGV_MAPERR"; - case SEGV_ACCERR: return "SEGV_ACCERR"; -#if defined(SEGV_BNDERR) - case SEGV_BNDERR: return "SEGV_BNDERR"; -#endif - } - break; - case SIGTRAP: - switch (signal_code) { - case TRAP_BRKPT: return "TRAP_BRKPT"; - case TRAP_TRACE: return "TRAP_TRACE"; - } - break; - } - // Then the other codes... - switch (signal_code) { - case SI_USER: return "SI_USER"; -#if defined(SI_KERNEL) - case SI_KERNEL: return "SI_KERNEL"; -#endif - case SI_QUEUE: return "SI_QUEUE"; - case SI_TIMER: return "SI_TIMER"; - case SI_MESGQ: return "SI_MESGQ"; - case SI_ASYNCIO: return "SI_ASYNCIO"; -#if defined(SI_SIGIO) - case SI_SIGIO: return "SI_SIGIO"; -#endif -#if defined(SI_TKILL) - case SI_TKILL: return "SI_TKILL"; -#endif - } - // Then give up... - return "?"; -} - -struct UContext { - explicit UContext(void* raw_context) : - context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) { - } - - void Dump(std::ostream& os) const { - // TODO: support non-x86 hosts (not urgent because this code doesn't run on targets). -#if defined(__APPLE__) && defined(__i386__) - DumpRegister32(os, "eax", context->__ss.__eax); - DumpRegister32(os, "ebx", context->__ss.__ebx); - DumpRegister32(os, "ecx", context->__ss.__ecx); - DumpRegister32(os, "edx", context->__ss.__edx); - os << '\n'; - - DumpRegister32(os, "edi", context->__ss.__edi); - DumpRegister32(os, "esi", context->__ss.__esi); - DumpRegister32(os, "ebp", context->__ss.__ebp); - DumpRegister32(os, "esp", context->__ss.__esp); - os << '\n'; - - DumpRegister32(os, "eip", context->__ss.__eip); - os << " "; - DumpRegister32(os, "eflags", context->__ss.__eflags); - DumpX86Flags(os, context->__ss.__eflags); - os << '\n'; - - DumpRegister32(os, "cs", context->__ss.__cs); - DumpRegister32(os, "ds", context->__ss.__ds); - DumpRegister32(os, "es", context->__ss.__es); - DumpRegister32(os, "fs", context->__ss.__fs); - os << '\n'; - DumpRegister32(os, "gs", context->__ss.__gs); - DumpRegister32(os, "ss", context->__ss.__ss); -#elif defined(__linux__) && defined(__i386__) - DumpRegister32(os, "eax", context.gregs[REG_EAX]); - DumpRegister32(os, "ebx", context.gregs[REG_EBX]); - DumpRegister32(os, "ecx", context.gregs[REG_ECX]); - DumpRegister32(os, "edx", context.gregs[REG_EDX]); - os << '\n'; - - DumpRegister32(os, "edi", context.gregs[REG_EDI]); - DumpRegister32(os, "esi", context.gregs[REG_ESI]); - DumpRegister32(os, "ebp", context.gregs[REG_EBP]); - DumpRegister32(os, "esp", context.gregs[REG_ESP]); - os << '\n'; - - DumpRegister32(os, "eip", context.gregs[REG_EIP]); - os << " "; - DumpRegister32(os, "eflags", context.gregs[REG_EFL]); - DumpX86Flags(os, context.gregs[REG_EFL]); - os << '\n'; - - DumpRegister32(os, "cs", context.gregs[REG_CS]); - DumpRegister32(os, "ds", context.gregs[REG_DS]); - DumpRegister32(os, "es", context.gregs[REG_ES]); - DumpRegister32(os, "fs", context.gregs[REG_FS]); - os << '\n'; - DumpRegister32(os, "gs", context.gregs[REG_GS]); - DumpRegister32(os, "ss", context.gregs[REG_SS]); -#elif defined(__linux__) && defined(__x86_64__) - DumpRegister64(os, "rax", context.gregs[REG_RAX]); - DumpRegister64(os, "rbx", context.gregs[REG_RBX]); - DumpRegister64(os, "rcx", context.gregs[REG_RCX]); - DumpRegister64(os, "rdx", context.gregs[REG_RDX]); - os << '\n'; - - DumpRegister64(os, "rdi", context.gregs[REG_RDI]); - DumpRegister64(os, "rsi", context.gregs[REG_RSI]); - DumpRegister64(os, "rbp", context.gregs[REG_RBP]); - DumpRegister64(os, "rsp", context.gregs[REG_RSP]); - os << '\n'; - - DumpRegister64(os, "r8 ", context.gregs[REG_R8]); - DumpRegister64(os, "r9 ", context.gregs[REG_R9]); - DumpRegister64(os, "r10", context.gregs[REG_R10]); - DumpRegister64(os, "r11", context.gregs[REG_R11]); - os << '\n'; - - DumpRegister64(os, "r12", context.gregs[REG_R12]); - DumpRegister64(os, "r13", context.gregs[REG_R13]); - DumpRegister64(os, "r14", context.gregs[REG_R14]); - DumpRegister64(os, "r15", context.gregs[REG_R15]); - os << '\n'; - - DumpRegister64(os, "rip", context.gregs[REG_RIP]); - os << " "; - DumpRegister32(os, "eflags", context.gregs[REG_EFL]); - DumpX86Flags(os, context.gregs[REG_EFL]); - os << '\n'; - - DumpRegister32(os, "cs", (context.gregs[REG_CSGSFS]) & 0x0FFFF); - DumpRegister32(os, "gs", (context.gregs[REG_CSGSFS] >> 16) & 0x0FFFF); - DumpRegister32(os, "fs", (context.gregs[REG_CSGSFS] >> 32) & 0x0FFFF); - os << '\n'; -#else - os << "Unknown architecture/word size/OS in ucontext dump"; -#endif - } - - void DumpRegister32(std::ostream& os, const char* name, uint32_t value) const { - os << StringPrintf(" %6s: 0x%08x", name, value); - } - - void DumpRegister64(std::ostream& os, const char* name, uint64_t value) const { - os << StringPrintf(" %6s: 0x%016" PRIx64, name, value); - } +void HandleUnexpectedSignalLinux(int signal_number, siginfo_t* info, void* raw_context) { + HandleUnexpectedSignalCommon(signal_number, info, raw_context, /* running_on_linux */ true); - void DumpX86Flags(std::ostream& os, uint32_t flags) const { - os << " ["; - if ((flags & (1 << 0)) != 0) { - os << " CF"; - } - if ((flags & (1 << 2)) != 0) { - os << " PF"; - } - if ((flags & (1 << 4)) != 0) { - os << " AF"; - } - if ((flags & (1 << 6)) != 0) { - os << " ZF"; - } - if ((flags & (1 << 7)) != 0) { - os << " SF"; - } - if ((flags & (1 << 8)) != 0) { - os << " TF"; - } - if ((flags & (1 << 9)) != 0) { - os << " IF"; - } - if ((flags & (1 << 10)) != 0) { - os << " DF"; - } - if ((flags & (1 << 11)) != 0) { - os << " OF"; - } - os << " ]"; - } - - mcontext_t& context; -}; - -// Return the signal number we recognize as timeout. -1 means not active/supported. -static int GetTimeoutSignal() { -#if defined(__APPLE__) - // Mac does not support realtime signals. - UNUSED(kUseSigRTTimeout); - return -1; -#else - return kUseSigRTTimeout ? (SIGRTMIN + 2) : -1; -#endif -} - -static bool IsTimeoutSignal(int signal_number) { - return signal_number == GetTimeoutSignal(); -} - -void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) { - static bool handlingUnexpectedSignal = false; - if (handlingUnexpectedSignal) { - LogHelper::LogLineLowStack(__FILE__, - __LINE__, - ::android::base::FATAL_WITHOUT_ABORT, - "HandleUnexpectedSignal reentered\n"); - if (IsTimeoutSignal(signal_number)) { - // Ignore a recursive timeout. - return; - } - _exit(1); - } - 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 || - signal_number == SIGFPE || signal_number == SIGSEGV); - - OsInfo os_info; - const char* cmd_line = GetCmdLine(); - if (cmd_line == nullptr) { - cmd_line = "<unset>"; // Because no-one called InitLogging. - } - pid_t tid = GetTid(); - std::string thread_name(GetThreadName(tid)); - UContext thread_context(raw_context); - Backtrace thread_backtrace(raw_context); - - // Note: We are using cerr directly instead of LOG macros to ensure even just partial output - // makes it out. That means we lose the "dalvikvm..." prefix, but that is acceptable - // considering this is an abort situation. - - std::cerr << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n" - << StringPrintf("Fatal signal %d (%s), code %d (%s)", - signal_number, GetSignalName(signal_number), - info->si_code, - GetSignalCodeName(signal_number, info->si_code)) - << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << std::endl - << "OS: " << Dumpable<OsInfo>(os_info) << std::endl - << "Cmdline: " << cmd_line << std::endl - << "Thread: " << tid << " \"" << thread_name << "\"" << std::endl - << "Registers:\n" << Dumpable<UContext>(thread_context) << std::endl - << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace) << std::endl; - if (kIsDebugBuild && signal_number == SIGSEGV) { - PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); - } - Runtime* runtime = Runtime::Current(); - if (runtime != nullptr) { - if (IsTimeoutSignal(signal_number)) { - // Special timeout signal. Try to dump all threads. - // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts - // are of value here. - runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); - std::cerr << std::endl; - } - std::cerr << "Fault message: " << runtime->GetFaultMessage() << std::endl; - } if (getenv("debug_db_uid") != nullptr || getenv("art_wait_for_gdb_on_crash") != nullptr) { + pid_t tid = GetTid(); + std::string thread_name(GetThreadName(tid)); std::cerr << "********************************************************\n" << "* Process " << getpid() << " thread " << tid << " \"" << thread_name << "\"" @@ -398,31 +58,9 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex void Runtime::InitPlatformSignalHandlers() { // On the host, we don't have debuggerd to dump a stack for us when something unexpected happens. - struct sigaction action; - memset(&action, 0, sizeof(action)); - sigemptyset(&action.sa_mask); - action.sa_sigaction = HandleUnexpectedSignal; - // Use the three-argument sa_sigaction handler. - action.sa_flags |= SA_SIGINFO; - // Use the alternate signal stack so we can catch stack overflows. - action.sa_flags |= SA_ONSTACK; - - int rc = 0; - rc += sigaction(SIGABRT, &action, nullptr); - rc += sigaction(SIGBUS, &action, nullptr); - rc += sigaction(SIGFPE, &action, nullptr); - rc += sigaction(SIGILL, &action, nullptr); - rc += sigaction(SIGPIPE, &action, nullptr); - rc += sigaction(SIGSEGV, &action, nullptr); -#if defined(SIGSTKFLT) - rc += sigaction(SIGSTKFLT, &action, nullptr); -#endif - rc += sigaction(SIGTRAP, &action, nullptr); - // Special dump-all timeout. - if (GetTimeoutSignal() != -1) { - rc += sigaction(GetTimeoutSignal(), &action, nullptr); - } - CHECK_EQ(rc, 0); + InitPlatformSignalHandlersCommon(HandleUnexpectedSignalLinux, + nullptr, + /* handle_timeout_signal */ true); } } // namespace art diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 01c940e9df..df8acc37a2 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -455,7 +455,6 @@ size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback, gc::collector::GarbageCollector* collector) { TimingLogger::ScopedTiming split("ThreadListFlip", collector->GetTimings()); - const uint64_t start_time = NanoTime(); Thread* self = Thread::Current(); Locks::mutator_lock_->AssertNotHeld(self); Locks::thread_list_lock_->AssertNotHeld(self); @@ -464,13 +463,17 @@ size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor, collector->GetHeap()->ThreadFlipBegin(self); // Sync with JNI critical calls. + // ThreadFlipBegin happens before we suspend all the threads, so it does not count towards the + // pause. + const uint64_t suspend_start_time = NanoTime(); SuspendAllInternal(self, self, nullptr); // Run the flip callback for the collector. Locks::mutator_lock_->ExclusiveLock(self); + suspend_all_historam_.AdjustAndAddValue(NanoTime() - suspend_start_time); flip_callback->Run(self); Locks::mutator_lock_->ExclusiveUnlock(self); - collector->RegisterPause(NanoTime() - start_time); + collector->RegisterPause(NanoTime() - suspend_start_time); // Resume runnable threads. size_t runnable_thread_count = 0; @@ -629,8 +632,9 @@ void ThreadList::SuspendAllInternal(Thread* self, MutexLock mu2(self, *Locks::thread_suspend_count_lock_); // Update global suspend all state for attaching threads. ++suspend_all_count_; - if (debug_suspend) + if (debug_suspend) { ++debug_suspend_all_count_; + } pending_threads.StoreRelaxed(list_.size() - num_ignored); // Increment everybody's suspend count (except those that should be ignored). for (const auto& thread : list_) { diff --git a/runtime/trace.cc b/runtime/trace.cc index 2add955f8e..3a9975a4e2 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -905,6 +905,9 @@ void Trace::FlushBuf() { void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, instrumentation::Instrumentation::InstrumentationEvent event, uint32_t thread_clock_diff, uint32_t wall_clock_diff) { + // Ensure we always use the non-obsolete version of the method so that entry/exit events have the + // same pointer value. + method = method->GetNonObsoleteMethod(); // Advance cur_offset_ atomically. int32_t new_offset; int32_t old_offset = 0; diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index 06f193af32..fad8a9f100 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -730,16 +730,19 @@ public class Main { Math.rint(+2.1); Assert.assertEquals(Math.rint(+0.0), +0.0d, 0.0); Assert.assertEquals(Math.rint(-0.0), -0.0d, 0.0); + Assert.assertEquals(Math.rint(+0.5), +0.0d, 0.0); // expects tie-to-even Assert.assertEquals(Math.rint(+2.0), +2.0d, 0.0); Assert.assertEquals(Math.rint(+2.1), +2.0d, 0.0); - Assert.assertEquals(Math.rint(+2.5), +2.0d, 0.0); + Assert.assertEquals(Math.rint(+2.5), +2.0d, 0.0); // expects tie-to-even Assert.assertEquals(Math.rint(+2.9), +3.0d, 0.0); Assert.assertEquals(Math.rint(+3.0), +3.0d, 0.0); + Assert.assertEquals(Math.rint(+3.5), +4.0d, 0.0); // expects tie-to-even Assert.assertEquals(Math.rint(-2.0), -2.0d, 0.0); Assert.assertEquals(Math.rint(-2.1), -2.0d, 0.0); - Assert.assertEquals(Math.rint(-2.5), -2.0d, 0.0); + Assert.assertEquals(Math.rint(-2.5), -2.0d, 0.0); // expects tie-to-even Assert.assertEquals(Math.rint(-2.9), -3.0d, 0.0); Assert.assertEquals(Math.rint(-3.0), -3.0d, 0.0); + Assert.assertEquals(Math.rint(-3.5), -4.0d, 0.0); // expects tie-to-even // 2^52 - 1.5 Assert.assertEquals(Math.rint(Double.longBitsToDouble(0x432FFFFFFFFFFFFDl)), Double.longBitsToDouble(0x432FFFFFFFFFFFFCl), 0.0); diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index a46a23c95c..bf0cbe66c1 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -52,7 +52,6 @@ public class Main { /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative /// CHECK-START-MIPS: int Main.testSimple(int) sharpening (after) - /// CHECK-NOT: MipsDexCacheArraysBase /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative /// CHECK-START-MIPS64: int Main.testSimple(int) sharpening (after) @@ -69,10 +68,6 @@ public class Main { /// CHECK: ArmDexCacheArraysBase /// CHECK-NOT: ArmDexCacheArraysBase - /// CHECK-START-MIPS: int Main.testSimple(int) dex_cache_array_fixups_mips (after) - /// CHECK: MipsDexCacheArraysBase - /// CHECK-NOT: MipsDexCacheArraysBase - /// CHECK-START-X86: int Main.testSimple(int) pc_relative_fixups_x86 (after) /// CHECK: X86ComputeBaseMethodAddress /// CHECK-NOT: X86ComputeBaseMethodAddress @@ -95,7 +90,6 @@ public class Main { /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative /// CHECK-START-MIPS: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK-NOT: MipsDexCacheArraysBase /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative @@ -120,14 +114,6 @@ public class Main { /// CHECK: ArmDexCacheArraysBase /// CHECK-NEXT: If - /// CHECK-START-MIPS: int Main.testDiamond(boolean, int) dex_cache_array_fixups_mips (after) - /// CHECK: MipsDexCacheArraysBase - /// CHECK-NOT: MipsDexCacheArraysBase - - /// CHECK-START-MIPS: int Main.testDiamond(boolean, int) dex_cache_array_fixups_mips (after) - /// CHECK: MipsDexCacheArraysBase - /// CHECK-NEXT: If - /// CHECK-START-X86: int Main.testDiamond(boolean, int) pc_relative_fixups_x86 (after) /// CHECK: X86ComputeBaseMethodAddress /// CHECK-NOT: X86ComputeBaseMethodAddress @@ -182,24 +168,6 @@ public class Main { /// CHECK: begin_block /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative - /// CHECK-START-MIPS: int Main.testLoop(int[], int) dex_cache_array_fixups_mips (before) - /// CHECK-NOT: MipsDexCacheArraysBase - - /// CHECK-START-MIPS: int Main.testLoop(int[], int) dex_cache_array_fixups_mips (after) - /// CHECK: MipsDexCacheArraysBase - /// CHECK-NOT: MipsDexCacheArraysBase - - /// CHECK-START-MIPS: int Main.testLoop(int[], int) dex_cache_array_fixups_mips (after) - /// CHECK: InvokeStaticOrDirect - /// CHECK-NOT: InvokeStaticOrDirect - - /// CHECK-START-MIPS: int Main.testLoop(int[], int) dex_cache_array_fixups_mips (after) - /// CHECK: ArrayLength - /// CHECK-NEXT: MipsDexCacheArraysBase - /// CHECK-NEXT: Goto - /// CHECK: begin_block - /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative - public static int testLoop(int[] array, int x) { // PC-relative bases used by ARM, MIPS and X86 should be pulled before the loop. for (int i : array) { @@ -228,16 +196,6 @@ public class Main { /// CHECK-NEXT: ArmDexCacheArraysBase /// CHECK-NEXT: Goto - /// CHECK-START-MIPS: int Main.testLoopWithDiamond(int[], boolean, int) dex_cache_array_fixups_mips (before) - /// CHECK-NOT: MipsDexCacheArraysBase - - /// CHECK-START-MIPS: int Main.testLoopWithDiamond(int[], boolean, int) dex_cache_array_fixups_mips (after) - /// CHECK: If - /// CHECK: begin_block - /// CHECK: ArrayLength - /// CHECK-NEXT: MipsDexCacheArraysBase - /// CHECK-NEXT: Goto - public static int testLoopWithDiamond(int[] array, boolean negate, int x) { // PC-relative bases used by ARM, MIPS and X86 should be pulled before the loop // but not outside the if. @@ -387,10 +345,6 @@ public class Main { /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) /// CHECK: LoadClass load_kind:BssEntry class_name:Other - /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getOtherClass() dex_cache_array_fixups_mips (after) - /// CHECK-DAG: MipsDexCacheArraysBase - /// CHECK-DAG: LoadClass load_kind:BssEntry class_name:Other - /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) /// CHECK: LoadClass load_kind:BssEntry class_name:Other diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java index 7cc0b8b652..7509d9b4f3 100644 --- a/test/623-checker-loop-regressions/src/Main.java +++ b/test/623-checker-loop-regressions/src/Main.java @@ -154,8 +154,8 @@ public class Main { /// CHECK-NOT: Phi // /// CHECK-START: int Main.polynomialInt() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant -45 loop:none - /// CHECK-DAG: Return [<<Int>>] loop:none + /// CHECK-DAG: <<Int:i\d+>> IntConstant -45 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none static int polynomialInt() { int x = 0; for (int i = 0; i < 10; i++) { @@ -164,6 +164,81 @@ public class Main { return x; } + // Regression test for b/34779592 (found with fuzz testing): overflow for last value + // of division truncates to zero, for multiplication it simply truncates. + // + /// CHECK-START: int Main.geoIntDivLastValue(int) loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.geoIntDivLastValue(int) loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: int Main.geoIntDivLastValue(int) instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none + static int geoIntDivLastValue(int x) { + for (int i = 0; i < 2; i++) { + x /= 1081788608; + } + return x; + } + + /// CHECK-START: int Main.geoIntMulLastValue(int) loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.geoIntMulLastValue(int) loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: int Main.geoIntMulLastValue(int) instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Int:i\d+>> IntConstant -194211840 loop:none + /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>] loop:none + /// CHECK-DAG: Return [<<Mul>>] loop:none + static int geoIntMulLastValue(int x) { + for (int i = 0; i < 2; i++) { + x *= 1081788608; + } + return x; + } + + /// CHECK-START: long Main.geoLongDivLastValue(long) loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: long Main.geoLongDivLastValue(long) loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: long Main.geoLongDivLastValue(long) instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Long:j\d+>> LongConstant 0 loop:none + /// CHECK-DAG: Return [<<Long>>] loop:none + static long geoLongDivLastValue(long x) { + for (int i = 0; i < 10; i++) { + x /= 1081788608; + } + return x; + } + + /// CHECK-START: long Main.geoLongMulLastValue(long) loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: long Main.geoLongMulLastValue(long) loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: long Main.geoLongMulLastValue(long) instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Par:j\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Long:j\d+>> LongConstant -8070450532247928832 loop:none + /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Par>>,<<Long>>] loop:none + /// CHECK-DAG: Return [<<Mul>>] loop:none + static long geoLongMulLastValue(long x) { + for (int i = 0; i < 10; i++) { + x *= 1081788608; + } + return x; + } + public static void main(String[] args) { expectEquals(10, earlyExitFirst(-1)); for (int i = 0; i <= 10; i++) { @@ -185,6 +260,42 @@ public class Main { expectEquals(-45, polynomialIntFromLong()); expectEquals(-45, polynomialInt()); + expectEquals(0, geoIntDivLastValue(0)); + expectEquals(0, geoIntDivLastValue(1)); + expectEquals(0, geoIntDivLastValue(2)); + expectEquals(0, geoIntDivLastValue(1081788608)); + expectEquals(0, geoIntDivLastValue(-1081788608)); + expectEquals(0, geoIntDivLastValue(2147483647)); + expectEquals(0, geoIntDivLastValue(-2147483648)); + + expectEquals( 0, geoIntMulLastValue(0)); + expectEquals( -194211840, geoIntMulLastValue(1)); + expectEquals( -388423680, geoIntMulLastValue(2)); + expectEquals(-1041498112, geoIntMulLastValue(1081788608)); + expectEquals( 1041498112, geoIntMulLastValue(-1081788608)); + expectEquals( 194211840, geoIntMulLastValue(2147483647)); + expectEquals( 0, geoIntMulLastValue(-2147483648)); + + expectEquals(0L, geoLongDivLastValue(0L)); + expectEquals(0L, geoLongDivLastValue(1L)); + expectEquals(0L, geoLongDivLastValue(2L)); + expectEquals(0L, geoLongDivLastValue(1081788608L)); + expectEquals(0L, geoLongDivLastValue(-1081788608L)); + expectEquals(0L, geoLongDivLastValue(2147483647L)); + expectEquals(0L, geoLongDivLastValue(-2147483648L)); + expectEquals(0L, geoLongDivLastValue(9223372036854775807L)); + expectEquals(0L, geoLongDivLastValue(-9223372036854775808L)); + + expectEquals( 0L, geoLongMulLastValue(0L)); + expectEquals(-8070450532247928832L, geoLongMulLastValue(1L)); + expectEquals( 2305843009213693952L, geoLongMulLastValue(2L)); + expectEquals( 0L, geoLongMulLastValue(1081788608L)); + expectEquals( 0L, geoLongMulLastValue(-1081788608L)); + expectEquals( 8070450532247928832L, geoLongMulLastValue(2147483647L)); + expectEquals( 0L, geoLongMulLastValue(-2147483648L)); + expectEquals( 8070450532247928832L, geoLongMulLastValue(9223372036854775807L)); + expectEquals( 0L, geoLongMulLastValue(-9223372036854775808L)); + System.out.println("passed"); } @@ -193,4 +304,10 @@ public class Main { throw new Error("Expected: " + expected + ", found: " + result); } } + + private static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } } diff --git a/test/909-attach-agent/src/Main.java b/test/909-attach-agent/src/Main.java index 8a8a087458..569b89ad7d 100644 --- a/test/909-attach-agent/src/Main.java +++ b/test/909-attach-agent/src/Main.java @@ -19,7 +19,7 @@ import java.io.IOException; public class Main { public static void main(String[] args) { - System.out.println("Hello, world!"); + System.err.println("Hello, world!"); for(String a : args) { if(a.startsWith("agent:")) { String agent = a.substring(6); @@ -30,6 +30,6 @@ public class Main { } } } - System.out.println("Goodbye!"); + System.err.println("Goodbye!"); } } diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java index 1b03200ba5..2b3296f1f2 100644 --- a/test/916-obsolete-jit/src/Main.java +++ b/test/916-obsolete-jit/src/Main.java @@ -116,37 +116,27 @@ public class Main { doTest(new Transform(), new TestWatcher()); } - // TODO Workaround to (1) inability to ensure that current_method is not put into a register by - // the JIT and/or (2) inability to deoptimize frames near runtime functions. - // TODO Fix one/both of these issues. - public static void doCall(Runnable r) { - r.run(); - } - private static boolean interpreting = true; private static boolean retry = false; public static void doTest(Transform t, TestWatcher w) { // Get the methods that need to be optimized. Method say_hi_method; - Method do_call_method; // Figure out if we can even JIT at all. final boolean has_jit = hasJit(); try { say_hi_method = Transform.class.getDeclaredMethod( "sayHi", Runnable.class, Consumer.class); - do_call_method = Main.class.getDeclaredMethod("doCall", Runnable.class); } catch (Exception e) { System.out.println("Unable to find methods!"); e.printStackTrace(); return; } // Makes sure the stack is the way we want it for the test and does the redefinition. It will - // set the retry boolean to true if we need to go around again due to a bad stack. + // set the retry boolean to true if the stack does not have a JIT-compiled sayHi entry. This can + // only happen if the method gets GC'd. Runnable do_redefinition = () -> { - if (has_jit && - (Main.isInterpretedFunction(say_hi_method, true) || - Main.isInterpretedFunction(do_call_method, false))) { + if (has_jit && Main.isInterpretedFunction(say_hi_method, true)) { // Try again. We are not running the right jitted methods/cannot redefine them now. retry = true; } else { @@ -161,7 +151,6 @@ public class Main { do { // Run ensureJitCompiled here since it might get GCd ensureJitCompiled(Transform.class, "sayHi"); - ensureJitCompiled(Main.class, "doCall"); // Clear output. w.clear(); // Try and redefine. diff --git a/test/916-obsolete-jit/src/Transform.java b/test/916-obsolete-jit/src/Transform.java index f4dcf09dc6..9c9adbc22d 100644 --- a/test/916-obsolete-jit/src/Transform.java +++ b/test/916-obsolete-jit/src/Transform.java @@ -29,13 +29,7 @@ class Transform { reporter.accept("Pre Start private method call"); Start(reporter); reporter.accept("Post Start private method call"); - // TODO Revisit with b/33616143 - // TODO Uncomment this once either b/33630159 or b/33616143 are resolved. - // r.run(); - // TODO This doCall function is a very temporary fix until we get either deoptimization near - // runtime frames working, forcing current method to be always read from the stack or both - // working. - Main.doCall(r); + r.run(); reporter.accept("Pre Finish private method call"); Finish(reporter); reporter.accept("Post Finish private method call"); diff --git a/test/919-obsolete-fields/src/Main.java b/test/919-obsolete-fields/src/Main.java index 1d893f125a..ffb9897236 100644 --- a/test/919-obsolete-fields/src/Main.java +++ b/test/919-obsolete-fields/src/Main.java @@ -120,13 +120,6 @@ public class Main { doTest(new Transform(w), w); } - // TODO Workaround to (1) inability to ensure that current_method is not put into a register by - // the JIT and/or (2) inability to deoptimize frames near runtime functions. - // TODO Fix one/both of these issues. - public static void doCall(Runnable r) { - r.run(); - } - private static boolean interpreting = true; private static boolean retry = false; diff --git a/test/919-obsolete-fields/src/Transform.java b/test/919-obsolete-fields/src/Transform.java index abd1d19b66..c8e3cbd934 100644 --- a/test/919-obsolete-fields/src/Transform.java +++ b/test/919-obsolete-fields/src/Transform.java @@ -34,12 +34,7 @@ class Transform { reporter.accept("Pre Start private method call"); Start(); reporter.accept("Post Start private method call"); - // TODO Revist with b/33616143 - // TODO Uncomment this - // r.run(); - // TODO This is a very temporary fix until we get either deoptimization near runtime frames - // working, forcing current method to be always read from the stack or both working. - Main.doCall(r); + r.run(); reporter.accept("Pre Finish private method call"); Finish(); reporter.accept("Post Finish private method call"); diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt index 05b80e78a7..cf6b5a14b5 100644 --- a/test/957-methodhandle-transforms/expected.txt +++ b/test/957-methodhandle-transforms/expected.txt @@ -59,3 +59,20 @@ a: a, b:42, c: 43 a: a, b:100, c: 99 a: a, b:8.9, c: 9.1 a: a, b:6.7, c: 7.8 +a: a, b: b, c:c, d:d +a: a, b: b, c:c, d:d +a: a, b: b, c:c, d:d +a: a+b, b: c, c: d +a: a, b: b+c, c: d +a: a, b: b, c: c+d +voidFilter +a: a, b: b, c: c +voidFilter +a: a, b: b, c: c +a: foo, b:45, c:56, d:bar +a: foo, b:56, c:57, d:bar +a: foo, b:56, c:57, d:bar +a: foo, b:45, c:46, d:bar +a: c+d ,b:c ,c:d ,d:e +c+d +a: a ,b:c ,c:d ,d:e diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java index 4035857b9a..b6bbe74b9c 100644 --- a/test/957-methodhandle-transforms/src/Main.java +++ b/test/957-methodhandle-transforms/src/Main.java @@ -38,6 +38,10 @@ public class Main { testSpreaders_primitive(); testInvokeWithArguments(); testAsCollector(); + testFilterArguments(); + testCollectArguments(); + testInsertArguments(); + testFoldArguments(); } public static void testThrowException() throws Throwable { @@ -1374,6 +1378,269 @@ public class Main { assertEquals(51, (int) target.asCollector(double[].class, 2).invoke("a", 6.7, 7.8)); } + public static String filter1(char a) { + return String.valueOf(a); + } + + public static char filter2(String b) { + return b.charAt(0); + } + + public static String badFilter1(char a, char b) { + return "bad"; + } + + public static int filterTarget(String a, char b, String c, char d) { + System.out.println("a: " + a + ", b: " + b + ", c:" + c + ", d:" + d); + return 56; + } + + public static void testFilterArguments() throws Throwable { + MethodHandle filter1 = MethodHandles.lookup().findStatic( + Main.class, "filter1", MethodType.methodType(String.class, char.class)); + MethodHandle filter2 = MethodHandles.lookup().findStatic( + Main.class, "filter2", MethodType.methodType(char.class, String.class)); + + MethodHandle target = MethodHandles.lookup().findStatic( + Main.class, "filterTarget", MethodType.methodType(int.class, + String.class, char.class, String.class, char.class)); + + // In all the cases below, the values printed will be 'a', 'b', 'c', 'd'. + + // Filter arguments [0, 1] - all other arguments are passed through + // as is. + MethodHandle adapter = MethodHandles.filterArguments( + target, 0, filter1, filter2); + assertEquals(56, (int) adapter.invokeExact('a', "bXXXX", "c", 'd')); + + // Filter arguments [1, 2]. + adapter = MethodHandles.filterArguments(target, 1, filter2, filter1); + assertEquals(56, (int) adapter.invokeExact("a", "bXXXX", 'c', 'd')); + + // Filter arguments [2, 3]. + adapter = MethodHandles.filterArguments(target, 2, filter1, filter2); + assertEquals(56, (int) adapter.invokeExact("a", 'b', 'c', "dXXXXX")); + + // Try out a few error cases : + + // The return types of the filter doesn't align with the expected argument + // type of the target. + try { + adapter = MethodHandles.filterArguments(target, 2, filter2, filter1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // There are more filters than arguments. + try { + adapter = MethodHandles.filterArguments(target, 3, filter2, filter1); + fail(); + } catch (IllegalArgumentException expected) { + } + + // We pass in an obviously bogus position. + try { + adapter = MethodHandles.filterArguments(target, -1, filter2, filter1); + fail(); + } catch (ArrayIndexOutOfBoundsException expected) { + } + + // We pass in a function that has more than one argument. + MethodHandle badFilter1 = MethodHandles.lookup().findStatic( + Main.class, "badFilter1", + MethodType.methodType(String.class, char.class, char.class)); + + try { + adapter = MethodHandles.filterArguments(target, 0, badFilter1, filter2); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + static void voidFilter(char a, char b) { + System.out.println("voidFilter"); + } + + static String filter(char a, char b) { + return String.valueOf(a) + "+" + b; + } + + static char badFilter(char a, char b) { + return 0; + } + + static int target(String a, String b, String c) { + System.out.println("a: " + a + ", b: " + b + ", c: " + c); + return 57; + } + + public static void testCollectArguments() throws Throwable { + // Test non-void filters. + MethodHandle filter = MethodHandles.lookup().findStatic( + Main.class, "filter", + MethodType.methodType(String.class, char.class, char.class)); + + MethodHandle target = MethodHandles.lookup().findStatic( + Main.class, "target", + MethodType.methodType(int.class, String.class, String.class, String.class)); + + // Filter at position 0. + MethodHandle adapter = MethodHandles.collectArguments(target, 0, filter); + assertEquals(57, (int) adapter.invokeExact('a', 'b', "c", "d")); + + // Filter at position 1. + adapter = MethodHandles.collectArguments(target, 1, filter); + assertEquals(57, (int) adapter.invokeExact("a", 'b', 'c', "d")); + + // Filter at position 2. + adapter = MethodHandles.collectArguments(target, 2, filter); + assertEquals(57, (int) adapter.invokeExact("a", "b", 'c', 'd')); + + // Test void filters. Note that we're passing in one more argument + // than usual because the filter returns nothing - we have to invoke with + // the full set of filter args and the full set of target args. + filter = MethodHandles.lookup().findStatic(Main.class, "voidFilter", + MethodType.methodType(void.class, char.class, char.class)); + adapter = MethodHandles.collectArguments(target, 0, filter); + assertEquals(57, (int) adapter.invokeExact('a', 'b', "a", "b", "c")); + + adapter = MethodHandles.collectArguments(target, 1, filter); + assertEquals(57, (int) adapter.invokeExact("a", 'a', 'b', "b", "c")); + + // Test out a few failure cases. + filter = MethodHandles.lookup().findStatic( + Main.class, "filter", + MethodType.methodType(String.class, char.class, char.class)); + + // Bogus filter position. + try { + adapter = MethodHandles.collectArguments(target, 3, filter); + fail(); + } catch (IndexOutOfBoundsException expected) { + } + + // Mismatch in filter return type. + filter = MethodHandles.lookup().findStatic( + Main.class, "badFilter", + MethodType.methodType(char.class, char.class, char.class)); + try { + adapter = MethodHandles.collectArguments(target, 0, filter); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + static int insertReceiver(String a, int b, Integer c, String d) { + System.out.println("a: " + a + ", b:" + b + ", c:" + c + ", d:" + d); + return 73; + } + + public static void testInsertArguments() throws Throwable { + MethodHandle target = MethodHandles.lookup().findStatic( + Main.class, "insertReceiver", + MethodType.methodType(int.class, + String.class, int.class, Integer.class, String.class)); + + // Basic single element array inserted at position 0. + MethodHandle adapter = MethodHandles.insertArguments( + target, 0, new Object[] { "foo" }); + assertEquals(73, (int) adapter.invokeExact(45, Integer.valueOf(56), "bar")); + + // Exercise unboxing. + adapter = MethodHandles.insertArguments( + target, 1, new Object[] { Integer.valueOf(56), 57 }); + assertEquals(73, (int) adapter.invokeExact("foo", "bar")); + + // Exercise a widening conversion. + adapter = MethodHandles.insertArguments( + target, 1, new Object[] { (short) 56, Integer.valueOf(57) }); + assertEquals(73, (int) adapter.invokeExact("foo", "bar")); + + // Insert an argument at the last position. + adapter = MethodHandles.insertArguments( + target, 3, new Object[] { "bar" }); + assertEquals(73, (int) adapter.invokeExact("foo", 45, Integer.valueOf(46))); + + // Exercise a few error cases. + + // A reference type that can't be cast to another reference type. + try { + MethodHandles.insertArguments(target, 3, new Object[] { new Object() }); + fail(); + } catch (ClassCastException expected) { + } + + // A boxed type that can't be unboxed correctly. + try { + MethodHandles.insertArguments(target, 1, new Object[] { Long.valueOf(56) }); + fail(); + } catch (ClassCastException expected) { + } + } + + public static String foldFilter(char a, char b) { + return String.valueOf(a) + "+" + b; + } + + public static void voidFoldFilter(String e, char a, char b) { + System.out.println(String.valueOf(a) + "+" + b); + } + + public static int foldTarget(String a, char b, char c, String d) { + System.out.println("a: " + a + " ,b:" + b + " ,c:" + c + " ,d:" + d); + return 89; + } + + public static void mismatchedVoidFilter(Integer a) { + } + + public static Integer mismatchedNonVoidFilter(char a, char b) { + return null; + } + + public static void testFoldArguments() throws Throwable { + // Test non-void filters. + MethodHandle filter = MethodHandles.lookup().findStatic( + Main.class, "foldFilter", + MethodType.methodType(String.class, char.class, char.class)); + + MethodHandle target = MethodHandles.lookup().findStatic( + Main.class, "foldTarget", + MethodType.methodType(int.class, String.class, + char.class, char.class, String.class)); + + // Folder with a non-void type. + MethodHandle adapter = MethodHandles.foldArguments(target, filter); + assertEquals(89, (int) adapter.invokeExact('c', 'd', "e")); + + // Folder with a void type. + filter = MethodHandles.lookup().findStatic( + Main.class, "voidFoldFilter", + MethodType.methodType(void.class, String.class, char.class, char.class)); + adapter = MethodHandles.foldArguments(target, filter); + assertEquals(89, (int) adapter.invokeExact("a", 'c', 'd', "e")); + + // Test a few erroneous cases. + + filter = MethodHandles.lookup().findStatic( + Main.class, "mismatchedVoidFilter", + MethodType.methodType(void.class, Integer.class)); + try { + adapter = MethodHandles.foldArguments(target, filter); + fail(); + } catch (IllegalArgumentException expected) { + } + + filter = MethodHandles.lookup().findStatic( + Main.class, "mismatchedNonVoidFilter", + MethodType.methodType(Integer.class, char.class, char.class)); + try { + adapter = MethodHandles.foldArguments(target, filter); + fail(); + } catch (IllegalArgumentException expected) { + } + } + public static void fail() { System.out.println("FAIL"); Thread.dumpStack(); diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 74b06b1459..1b4f19509f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -710,8 +710,10 @@ endif TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := -# Tests that check semantics for a non-debuggable app. +# 909: Tests that check semantics for a non-debuggable app. +# 137: relies on AOT code and debuggable makes us JIT always. TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS := \ + 137-cfi \ 909-attach-agent \ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 751aa95f50..186a1513ee 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -364,6 +364,8 @@ fi if [ "$HAVE_IMAGE" = "n" ]; then + # Add 5 minutes to give some time to generate the boot image. + TIME_OUT_VALUE=$((${TIME_OUT_VALUE} + 300)) DALVIKVM_BOOT_OPT="-Ximage:/system/non-existant/core.art" else DALVIKVM_BOOT_OPT="-Ximage:${BOOT_IMAGE}" |