diff options
89 files changed, 1139 insertions, 371 deletions
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 1c1cc95ab1..f7957d402f 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -65,6 +65,7 @@ static bool ExpectedPairLayout(Location location) { static constexpr int kCurrentMethodStackOffset = 0; static constexpr size_t kArmInstrMaxSizeInBytes = 4u; +static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7; #ifdef __ #error "ARM Codegen VIXL macro-assembler macro already defined." @@ -657,6 +658,7 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, compiler_options, stats), block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this), @@ -675,9 +677,44 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d15); } +void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) { + uint32_t num_entries = switch_instr_->GetNumEntries(); + DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold); + + // We are about to use the assembler to place literals directly. Make sure we have enough + // underlying code buffer and we have generated the jump table with right size. + codegen->GetVIXLAssembler()->GetBuffer().Align(); + AssemblerAccurateScope aas(codegen->GetVIXLAssembler(), + num_entries * sizeof(int32_t), + CodeBufferCheckScope::kMaximumSize); + // TODO(VIXL): Check that using lower case bind is fine here. + codegen->GetVIXLAssembler()->bind(&table_start_); + const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors(); + for (uint32_t i = 0; i < num_entries; i++) { + vixl32::Label* target_label = codegen->GetLabelOf(successors[i]); + DCHECK(target_label->IsBound()); + int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation(); + // When doing BX to address we need to have lower bit set to 1 in T32. + if (codegen->GetVIXLAssembler()->IsUsingT32()) { + jump_offset++; + } + DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min()); + DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max()); + vixl32::Literal<int32_t> literal(jump_offset); + codegen->GetVIXLAssembler()->place(&literal); + } +} + +void CodeGeneratorARMVIXL::EmitJumpTables() { + for (auto&& jump_table : jump_tables_) { + jump_table->EmitTable(this); + } +} + #define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) { + EmitJumpTables(); GetAssembler()->FinalizeCode(); CodeGenerator::Finalize(allocator); } @@ -1253,6 +1290,14 @@ void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) { __ Bind(&false_target); } +void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) { + new (GetGraph()->GetArena()) LocationSummary(info); +} + +void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) { + // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile. +} + void CodeGeneratorARMVIXL::GenerateNop() { __ Nop(); } @@ -2495,7 +2540,12 @@ void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) { locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } else { - TODO_VIXL32(FATAL); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the former. + locations->SetOut(LocationFrom(r0)); } break; } @@ -2532,7 +2582,13 @@ void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) { } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1)); } else { - TODO_VIXL32(FATAL); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs))); + DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs))); + DCHECK(r0.Is(OutputRegister(div))); + + codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc()); + CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); } break; } @@ -2561,6 +2617,140 @@ void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) { } } +void LocationsBuilderARMVIXL::VisitRem(HRem* rem) { + Primitive::Type type = rem->GetResultType(); + + // Most remainders are implemented in the runtime. + LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly; + if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) { + // sdiv will be replaced by other instruction sequence. + call_kind = LocationSummary::kNoCall; + } else if ((rem->GetResultType() == Primitive::kPrimInt) + && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + // Have hardware divide instruction for int, do it with three instructions. + call_kind = LocationSummary::kNoCall; + } + + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); + + switch (type) { + case Primitive::kPrimInt: { + if (rem->InputAt(1)->IsConstant()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant())); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue(); + if (value == 1 || value == 0 || value == -1) { + // No temp register required. + } else { + locations->AddTemp(Location::RequiresRegister()); + if (!IsPowerOfTwo(AbsOrMin(value))) { + locations->AddTemp(Location::RequiresRegister()); + } + } + } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + } else { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the latter. + locations->SetOut(LocationFrom(r1)); + } + break; + } + case Primitive::kPrimLong: { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom( + calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); + locations->SetInAt(1, LocationFrom( + calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); + // The runtime helper puts the output in R2,R3. + locations->SetOut(LocationFrom(r2, r3)); + break; + } + case Primitive::kPrimFloat: { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1))); + locations->SetOut(LocationFrom(s0)); + break; + } + + case Primitive::kPrimDouble: { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom( + calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1))); + locations->SetInAt(1, LocationFrom( + calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3))); + locations->SetOut(LocationFrom(s0, s1)); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << type; + } +} + +void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) { + LocationSummary* locations = rem->GetLocations(); + Location second = locations->InAt(1); + + Primitive::Type type = rem->GetResultType(); + switch (type) { + case Primitive::kPrimInt: { + vixl32::Register reg1 = InputRegisterAt(rem, 0); + vixl32::Register out_reg = OutputRegister(rem); + if (second.IsConstant()) { + GenerateDivRemConstantIntegral(rem); + } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + vixl32::Register reg2 = RegisterFrom(second); + vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); + + // temp = reg1 / reg2 (integer division) + // dest = reg1 - temp * reg2 + __ Sdiv(temp, reg1, reg2); + __ Mls(out_reg, temp, reg2, reg1); + } else { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + DCHECK(reg1.Is(calling_convention.GetRegisterAt(0))); + DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1))); + DCHECK(out_reg.Is(r1)); + + codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc()); + CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); + } + break; + } + + case Primitive::kPrimLong: { + codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc()); + CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>(); + break; + } + + case Primitive::kPrimFloat: { + codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc()); + CheckEntrypointTypes<kQuickFmodf, float, float, float>(); + break; + } + + case Primitive::kPrimDouble: { + codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc()); + CheckEntrypointTypes<kQuickFmod, double, double, double>(); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << type; + } +} + + void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) { // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/ LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() @@ -5257,6 +5447,24 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ Bind(type_check_slow_path->GetExitLabel()); } +void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); +} + +void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) { + codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject, + instruction, + instruction->GetDexPc()); + if (instruction->IsEnter()) { + CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); + } else { + CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); + } +} + void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } @@ -5659,6 +5867,103 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location __ Blx(lr); } +void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { + // Nothing to do, this should be removed during prepare for register allocator. + LOG(FATAL) << "Unreachable"; +} + +void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { + // Nothing to do, this should be removed during prepare for register allocator. + LOG(FATAL) << "Unreachable"; +} + +// Simple implementation of packed switch - generate cascaded compare/jumps. +void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold && + codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) { + locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. + if (switch_instr->GetStartValue() != 0) { + locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias. + } + } +} + +// TODO(VIXL): Investigate and reach the parity with old arm codegen. +void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) { + int32_t lower_bound = switch_instr->GetStartValue(); + uint32_t num_entries = switch_instr->GetNumEntries(); + LocationSummary* locations = switch_instr->GetLocations(); + vixl32::Register value_reg = InputRegisterAt(switch_instr, 0); + HBasicBlock* default_block = switch_instr->GetDefaultBlock(); + + if (num_entries <= kPackedSwitchCompareJumpThreshold || + !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) { + // Create a series of compare/jumps. + UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + vixl32::Register temp_reg = temps.Acquire(); + // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store + // the immediate, because IP is used as the destination register. For the other + // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant, + // and they can be encoded in the instruction without making use of IP register. + __ Adds(temp_reg, value_reg, -lower_bound); + + const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); + // Jump to successors[0] if value == lower_bound. + __ B(eq, codegen_->GetLabelOf(successors[0])); + int32_t last_index = 0; + for (; num_entries - last_index > 2; last_index += 2) { + __ Adds(temp_reg, temp_reg, -2); + // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. + __ B(lo, codegen_->GetLabelOf(successors[last_index + 1])); + // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. + __ B(eq, codegen_->GetLabelOf(successors[last_index + 2])); + } + if (num_entries - last_index == 2) { + // The last missing case_value. + __ Cmp(temp_reg, 1); + __ B(eq, codegen_->GetLabelOf(successors[last_index + 1])); + } + + // And the default for any other value. + if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { + __ B(codegen_->GetLabelOf(default_block)); + } + } else { + // Create a table lookup. + vixl32::Register table_base = RegisterFrom(locations->GetTemp(0)); + + JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr); + + // Remove the bias. + vixl32::Register key_reg; + if (lower_bound != 0) { + key_reg = RegisterFrom(locations->GetTemp(1)); + __ Sub(key_reg, value_reg, lower_bound); + } else { + key_reg = value_reg; + } + + // Check whether the value is in the table, jump to default block if not. + __ Cmp(key_reg, num_entries - 1); + __ B(hi, codegen_->GetLabelOf(default_block)); + + UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + vixl32::Register jump_offset = temps.Acquire(); + + // Load jump offset from the table. + __ Adr(table_base, jump_table->GetTableStartLabel()); + __ Ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2)); + + // Jump to target block by branching to table_base(pc related) + offset. + vixl32::Register target_address = table_base; + __ Add(target_address, table_base, jump_offset); + __ Bx(target_address); + } +} + // Copy the result of a call into the given target. void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) { if (!trg.IsValid()) { @@ -5687,6 +5992,17 @@ void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type } } +void LocationsBuilderARMVIXL::VisitClassTableGet( + HClassTableGet* instruction ATTRIBUTE_UNUSED) { + TODO_VIXL32(FATAL); +} + +void InstructionCodeGeneratorARMVIXL::VisitClassTableGet( + HClassTableGet* instruction ATTRIBUTE_UNUSED) { + TODO_VIXL32(FATAL); +} + + #undef __ #undef QUICK_ENTRY_POINT #undef TODO_VIXL32 diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 2ccc30ff5f..ccd866c367 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -114,7 +114,9 @@ class LoadClassSlowPathARMVIXL; M(BelowOrEqual) \ M(BooleanNot) \ M(BoundsCheck) \ + M(BoundType) \ M(CheckCast) \ + M(ClassTableGet) \ M(ClearException) \ M(ClinitCheck) \ M(Compare) \ @@ -145,7 +147,9 @@ class LoadClassSlowPathARMVIXL; M(LoadString) \ M(LongConstant) \ M(MemoryBarrier) \ + M(MonitorOperation) \ M(Mul) \ + M(NativeDebugInfo) \ M(Neg) \ M(NewArray) \ M(NewInstance) \ @@ -154,9 +158,11 @@ class LoadClassSlowPathARMVIXL; M(NullCheck) \ M(NullConstant) \ M(Or) \ + M(PackedSwitch) \ M(ParallelMove) \ M(ParameterValue) \ M(Phi) \ + M(Rem) \ M(Return) \ M(ReturnVoid) \ M(Ror) \ @@ -181,17 +187,27 @@ class LoadClassSlowPathARMVIXL; #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ M(ArmDexCacheArraysBase) \ M(BitwiseNegatedRight) \ - M(BoundType) \ - M(ClassTableGet) \ M(IntermediateAddress) \ - M(MonitorOperation) \ M(MultiplyAccumulate) \ - M(NativeDebugInfo) \ - M(PackedSwitch) \ - M(Rem) \ class CodeGeneratorARMVIXL; +class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> { + public: + explicit JumpTableARMVIXL(HPackedSwitch* switch_instr) + : switch_instr_(switch_instr), table_start_() {} + + vixl::aarch32::Label* GetTableStartLabel() { return &table_start_; } + + void EmitTable(CodeGeneratorARMVIXL* codegen); + + private: + HPackedSwitch* const switch_instr_; + vixl::aarch32::Label table_start_; + + DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL); +}; + class InvokeRuntimeCallingConventionARMVIXL : public CallingConvention<vixl::aarch32::Register, vixl::aarch32::SRegister> { public: @@ -488,10 +504,16 @@ class CodeGeneratorARMVIXL : public CodeGenerator { return block_entry_label->GetLocation(); } + JumpTableARMVIXL* CreateJumpTable(HPackedSwitch* switch_instr) { + jump_tables_.emplace_back(new (GetGraph()->GetArena()) JumpTableARMVIXL(switch_instr)); + return jump_tables_.back().get(); + } + HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } + void EmitJumpTables(); void GenerateMemoryBarrier(MemBarrierKind kind); void Finalize(CodeAllocator* allocator) OVERRIDE; void SetupBlockedRegisters() const OVERRIDE; @@ -673,6 +695,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArenaDeque<vixl::aarch32::Label> block_labels_; // Indexed by block id. vixl::aarch32::Label frame_entry_label_; + ArenaVector<std::unique_ptr<JumpTableARMVIXL>> jump_tables_; LocationsBuilderARMVIXL location_builder_; InstructionCodeGeneratorARMVIXL instruction_visitor_; ParallelMoveResolverARMVIXL move_resolver_; diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index eb2d18dd88..f0086fb202 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -120,17 +120,17 @@ void LICM::Run() { } DCHECK(!loop_info->IsIrreducible()); - // We can move an instruction that can throw only if it is the first - // throwing instruction in the loop. Note that the first potentially - // throwing instruction encountered that is not hoisted stops this - // optimization. Non-throwing instruction can still be hoisted. - bool found_first_non_hoisted_throwing_instruction_in_loop = !inner->IsLoopHeader(); + // We can move an instruction that can throw only as long as it is the first visible + // instruction (throw or write) in the loop. Note that the first potentially visible + // instruction that is not hoisted stops this optimization. Non-throwing instructions, + // on the other hand, can still be hoisted. + bool found_first_non_hoisted_visible_instruction_in_loop = !inner->IsLoopHeader(); for (HInstructionIterator inst_it(inner->GetInstructions()); !inst_it.Done(); inst_it.Advance()) { HInstruction* instruction = inst_it.Current(); if (instruction->CanBeMoved() - && (!instruction->CanThrow() || !found_first_non_hoisted_throwing_instruction_in_loop) + && (!instruction->CanThrow() || !found_first_non_hoisted_visible_instruction_in_loop) && !instruction->GetSideEffects().MayDependOn(loop_effects) && InputsAreDefinedBeforeLoop(instruction)) { // We need to update the environment if the instruction has a loop header @@ -142,10 +142,10 @@ void LICM::Run() { } instruction->MoveBefore(pre_header->GetLastInstruction()); MaybeRecordStat(MethodCompilationStat::kLoopInvariantMoved); - } else if (instruction->CanThrow()) { - // If `instruction` can throw, we cannot move further instructions - // that can throw as well. - found_first_non_hoisted_throwing_instruction_in_loop = true; + } else if (instruction->CanThrow() || instruction->DoesAnyWrite()) { + // If `instruction` can do something visible (throw or write), + // we cannot move further instructions that can throw. + found_first_non_hoisted_visible_instruction_in_loop = true; } } } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 6f84cdcc4f..7a930cce71 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -167,24 +167,13 @@ class PassObserver : public ValueObject { LOG(INFO) << "TIMINGS " << GetMethodName(); LOG(INFO) << Dumpable<TimingLogger>(timing_logger_); } - if (visualizer_enabled_) { - MutexLock mu(Thread::Current(), visualizer_dump_mutex_); - *visualizer_output_ << visualizer_oss_.str(); - // The destructor of `visualizer_output_` is normally - // responsible for flushing (and closing) the stream, but it - // won't be invoked during fast exits in non-debug mode -- see - // art::Dex2Oat::~Dex2Oat, which explicitly abandons some - // objects (such as the compiler driver) in non-debug mode, to - // avoid the cost of destructing them. Therefore we explicitly - // flush the stream here to prevent truncated CFG visualizer - // files. - visualizer_output_->flush(); - } + DCHECK(visualizer_oss_.str().empty()); } - void DumpDisassembly() const { + void DumpDisassembly() REQUIRES(!visualizer_dump_mutex_) { if (visualizer_enabled_) { visualizer_.DumpGraphWithDisassembly(); + FlushVisualizer(); } } @@ -199,24 +188,34 @@ class PassObserver : public ValueObject { } private: - void StartPass(const char* pass_name) { + void StartPass(const char* pass_name) REQUIRES(!visualizer_dump_mutex_) { VLOG(compiler) << "Starting pass: " << pass_name; // Dump graph first, then start timer. if (visualizer_enabled_) { visualizer_.DumpGraph(pass_name, /* is_after_pass */ false, graph_in_bad_state_); + FlushVisualizer(); } if (timing_logger_enabled_) { timing_logger_.StartTiming(pass_name); } } - void EndPass(const char* pass_name) { + void FlushVisualizer() REQUIRES(!visualizer_dump_mutex_) { + MutexLock mu(Thread::Current(), visualizer_dump_mutex_); + *visualizer_output_ << visualizer_oss_.str(); + visualizer_output_->flush(); + visualizer_oss_.str(""); + visualizer_oss_.clear(); + } + + void EndPass(const char* pass_name) REQUIRES(!visualizer_dump_mutex_) { // Pause timer first, then dump graph. if (timing_logger_enabled_) { timing_logger_.EndTiming(); } if (visualizer_enabled_) { visualizer_.DumpGraph(pass_name, /* is_after_pass */ true, graph_in_bad_state_); + FlushVisualizer(); } // Validate the HGraph if running in debug mode. diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 1180bdeabb..c37c72b237 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -517,7 +517,8 @@ class Dex2Oat FINAL { thread_count_(sysconf(_SC_NPROCESSORS_CONF)), start_ns_(NanoTime()), oat_fd_(-1), - vdex_fd_(-1), + input_vdex_fd_(-1), + output_vdex_fd_(-1), zip_fd_(-1), image_base_(0U), image_classes_zip_filename_(nullptr), @@ -590,8 +591,13 @@ class Dex2Oat FINAL { ParseUintOption(option, "--zip-fd", &zip_fd_, Usage); } - void ParseVdexFd(const StringPiece& option) { - ParseUintOption(option, "--vdex-fd", &vdex_fd_, Usage); + void ParseInputVdexFd(const StringPiece& option) { + // Note that the input vdex fd might be -1. + ParseIntOption(option, "--input-vdex-fd", &input_vdex_fd_, Usage); + } + + void ParseOutputVdexFd(const StringPiece& option) { + ParseUintOption(option, "--output-vdex-fd", &output_vdex_fd_, Usage); } void ParseOatFd(const StringPiece& option) { @@ -707,9 +713,9 @@ class Dex2Oat FINAL { Usage("--oat-file should not be used with --oat-fd"); } - if ((vdex_fd_ == -1) != (oat_fd_ == -1)) { + if ((output_vdex_fd_ == -1) != (oat_fd_ == -1)) { Usage("VDEX and OAT output must be specified either with one --oat-filename " - "or with --oat-fd and --vdex-fd file descriptors"); + "or with --oat-fd and --output-vdex-fd file descriptors"); } if (!parser_options->oat_symbols.empty() && oat_fd_ != -1) { @@ -720,8 +726,8 @@ class Dex2Oat FINAL { Usage("--oat-symbols should not be used with --host"); } - if (vdex_fd_ != -1 && !image_filenames_.empty()) { - Usage("--vdex-fd should not be used with --image"); + if (output_vdex_fd_ != -1 && !image_filenames_.empty()) { + Usage("--output-vdex-fd should not be used with --image"); } if (oat_fd_ != -1 && !image_filenames_.empty()) { @@ -1114,8 +1120,10 @@ class Dex2Oat FINAL { ParseZipFd(option); } else if (option.starts_with("--zip-location=")) { zip_location_ = option.substr(strlen("--zip-location=")).data(); - } else if (option.starts_with("--vdex-fd=")) { - ParseVdexFd(option); + } else if (option.starts_with("--input-vdex-fd=")) { + ParseInputVdexFd(option); + } else if (option.starts_with("--output-vdex-fd=")) { + ParseOutputVdexFd(option); } else if (option.starts_with("--oat-file=")) { oat_filenames_.push_back(option.substr(strlen("--oat-file=")).data()); } else if (option.starts_with("--oat-symbols=")) { @@ -1258,7 +1266,7 @@ class Dex2Oat FINAL { } oat_files_.push_back(std::move(oat_file)); - DCHECK_EQ(vdex_fd_, -1); + DCHECK_EQ(output_vdex_fd_, -1); std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex"); std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str())); if (vdex_file.get() == nullptr) { @@ -1284,9 +1292,9 @@ class Dex2Oat FINAL { } oat_files_.push_back(std::move(oat_file)); - DCHECK_NE(vdex_fd_, -1); + DCHECK_NE(output_vdex_fd_, -1); std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex"); - std::unique_ptr<File> vdex_file(new File(vdex_fd_, vdex_location, /* check_usage */ true)); + std::unique_ptr<File> vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true)); if (vdex_file.get() == nullptr) { PLOG(ERROR) << "Failed to create vdex file: " << vdex_location; return false; @@ -2576,7 +2584,8 @@ class Dex2Oat FINAL { std::vector<const char*> oat_filenames_; std::vector<const char*> oat_unstripped_; int oat_fd_; - int vdex_fd_; + int input_vdex_fd_; + int output_vdex_fd_; std::vector<const char*> dex_filenames_; std::vector<const char*> dex_locations_; int zip_fd_; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 6d45dad28f..c8875f49e2 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1891,7 +1891,7 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { boot_class_table_.VisitRoots(buffered_visitor); // If tracing is enabled, then mark all the class loaders to prevent unloading. - if (tracing_enabled) { + if ((flags & kVisitRootFlagClassLoader) != 0 || tracing_enabled) { for (const ClassLoaderData& data : class_loaders_) { GcRoot<mirror::Object> root(GcRoot<mirror::Object>(self->DecodeJObject(data.weak_root))); root.VisitRoot(visitor, RootInfo(kRootVMInternal)); diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index d921900933..e18a955251 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -181,7 +181,6 @@ void AllocRecordObjectMap::DisallowNewAllocationRecords() { } void AllocRecordObjectMap::BroadcastForNewAllocationRecords() { - CHECK(kUseReadBarrier); new_record_condition_.Broadcast(Thread::Current()); } @@ -291,6 +290,9 @@ void AllocRecordObjectMap::RecordAllocation(Thread* self, // Wait for GC's sweeping to complete and allow new records while (UNLIKELY((!kUseReadBarrier && !allow_new_record_) || (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) { + // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the + // presence of threads blocking for weak ref access. + self->CheckEmptyCheckpoint(); new_record_condition_.WaitHoldingLocks(self); } diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h index c8b2b89702..90cff6a8c4 100644 --- a/runtime/gc/allocation_record.h +++ b/runtime/gc/allocation_record.h @@ -261,7 +261,6 @@ class AllocRecordObjectMap { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::alloc_tracker_lock_); void BroadcastForNewAllocationRecords() - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::alloc_tracker_lock_); // TODO: Is there a better way to hide the entries_'s type? diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 2e72adadd6..8353b26a9a 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -514,26 +514,6 @@ void ConcurrentCopying::RecordLiveStackFreezeSize(Thread* self) { live_stack_freeze_size_ = heap_->GetLiveStack()->Size(); } -class EmptyCheckpoint : public Closure { - public: - explicit EmptyCheckpoint(ConcurrentCopying* concurrent_copying) - : concurrent_copying_(concurrent_copying) { - } - - virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS { - // Note: self is not necessarily equal to thread since thread may be suspended. - Thread* self = Thread::Current(); - CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc) - << thread->GetState() << " thread " << thread << " self " << self; - // If thread is a running mutator, then act on behalf of the garbage collector. - // See the code in ThreadList::RunCheckpoint. - concurrent_copying_->GetBarrier().Pass(self); - } - - private: - ConcurrentCopying* const concurrent_copying_; -}; - // Used to visit objects in the immune spaces. inline void ConcurrentCopying::ScanImmuneObject(mirror::Object* obj) { DCHECK(obj != nullptr); @@ -835,10 +815,10 @@ void ConcurrentCopying::ProcessFalseGrayStack() { void ConcurrentCopying::IssueEmptyCheckpoint() { Thread* self = Thread::Current(); - EmptyCheckpoint check_point(this); ThreadList* thread_list = Runtime::Current()->GetThreadList(); - gc_barrier_->Init(self, 0); - size_t barrier_count = thread_list->RunCheckpoint(&check_point); + Barrier* barrier = thread_list->EmptyCheckpointBarrier(); + barrier->Init(self, 0); + size_t barrier_count = thread_list->RunEmptyCheckpoint(); // If there are no threads to wait which implys that all the checkpoint functions are finished, // then no need to release the mutator lock. if (barrier_count == 0) { @@ -848,7 +828,7 @@ void ConcurrentCopying::IssueEmptyCheckpoint() { Locks::mutator_lock_->SharedUnlock(self); { ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); - gc_barrier_->Increment(self, barrier_count); + barrier->Increment(self, barrier_count); } Locks::mutator_lock_->SharedLock(self); } @@ -1253,6 +1233,10 @@ bool ConcurrentCopying::ProcessMarkStackOnce() { } gc_mark_stack_->Reset(); } else if (mark_stack_mode == kMarkStackModeShared) { + // Do an empty checkpoint to avoid a race with a mutator preempted in the middle of a read + // barrier but before pushing onto the mark stack. b/32508093. Note the weak ref access is + // disabled at this point. + IssueEmptyCheckpoint(); // Process the shared GC mark stack with a lock. { MutexLock mu(self, mark_stack_lock_); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 7b73e43ad2..673a97e82d 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -608,8 +608,7 @@ void MarkSweep::MarkNonThreadRoots() { void MarkSweep::MarkConcurrentRoots(VisitRootFlags flags) { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Visit all runtime roots and clear dirty flags. - Runtime::Current()->VisitConcurrentRoots( - this, static_cast<VisitRootFlags>(flags | kVisitRootFlagNonMoving)); + Runtime::Current()->VisitConcurrentRoots(this, flags); } class MarkSweep::DelayReferenceReferentVisitor { diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 19c2e9aaba..a94cb279cf 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -98,7 +98,7 @@ class MarkSweep : public GarbageCollector { REQUIRES(!mark_stack_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void MarkConcurrentRoots(VisitRootFlags flags) + virtual void MarkConcurrentRoots(VisitRootFlags flags) REQUIRES(Locks::heap_bitmap_lock_) REQUIRES(!mark_stack_lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc index bb7e854ea1..a2dbe3f7a0 100644 --- a/runtime/gc/collector/sticky_mark_sweep.cc +++ b/runtime/gc/collector/sticky_mark_sweep.cc @@ -56,6 +56,19 @@ void StickyMarkSweep::MarkReachableObjects() { RecursiveMarkDirtyObjects(false, accounting::CardTable::kCardDirty - 1); } +void StickyMarkSweep::MarkConcurrentRoots(VisitRootFlags flags) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); + // Visit all runtime roots and clear dirty flags including class loader. This is done to prevent + // incorrect class unloading since the GC does not card mark when storing store the class during + // object allocation. Doing this for each allocation would be slow. + // Since the card is not dirty, it means the object may not get scanned. This can cause class + // unloading to occur even though the class and class loader are reachable through the object's + // class. + Runtime::Current()->VisitConcurrentRoots( + this, + static_cast<VisitRootFlags>(flags | kVisitRootFlagClassLoader)); +} + void StickyMarkSweep::Sweep(bool swap_bitmaps ATTRIBUTE_UNUSED) { SweepArray(GetHeap()->GetLiveStack(), false); } diff --git a/runtime/gc/collector/sticky_mark_sweep.h b/runtime/gc/collector/sticky_mark_sweep.h index 100ca64ee4..45f912f63a 100644 --- a/runtime/gc/collector/sticky_mark_sweep.h +++ b/runtime/gc/collector/sticky_mark_sweep.h @@ -33,6 +33,12 @@ class StickyMarkSweep FINAL : public PartialMarkSweep { StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = ""); ~StickyMarkSweep() {} + virtual void MarkConcurrentRoots(VisitRootFlags flags) + OVERRIDE + REQUIRES(Locks::heap_bitmap_lock_) + REQUIRES(!mark_stack_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + protected: // Bind the live bits to the mark bits of bitmaps for all spaces, all spaces other than the // alloc space will be marked as immune. diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 19760afed7..ddc38526bd 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -4065,7 +4065,6 @@ void Heap::DisallowNewAllocationRecords() const { } void Heap::BroadcastForNewAllocationRecords() const { - CHECK(kUseReadBarrier); // Always broadcast without checking IsAllocTrackingEnabled() because IsAllocTrackingEnabled() may // be set to false while some threads are waiting for system weak access in // AllocRecordObjectMap::RecordAllocation() and we may fail to wake them up. b/27467554. diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index e8eb69e35c..0c671d269d 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -797,7 +797,6 @@ class Heap { REQUIRES(!Locks::alloc_tracker_lock_); void BroadcastForNewAllocationRecords() const - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::alloc_tracker_lock_); void DisableGCForShutdown() REQUIRES(!*gc_complete_lock_); diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 798ecd3d87..2cde7d5731 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -55,7 +55,6 @@ void ReferenceProcessor::DisableSlowPath(Thread* self) { } void ReferenceProcessor::BroadcastForSlowPath(Thread* self) { - CHECK(kUseReadBarrier); MutexLock mu(self, *Locks::reference_processor_lock_); condition_.Broadcast(self); } @@ -99,6 +98,9 @@ ObjPtr<mirror::Object> ReferenceProcessor::GetReferent(Thread* self, } } } + // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the + // presence of threads blocking for weak ref access. + self->CheckEmptyCheckpoint(); condition_.WaitHoldingLocks(self); } return reference->GetReferent(); @@ -270,6 +272,9 @@ bool ReferenceProcessor::MakeCircularListIfUnenqueued( // Wait untul we are done processing reference. while ((!kUseReadBarrier && SlowPathEnabled()) || (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) { + // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the + // presence of threads blocking for weak ref access. + self->CheckEmptyCheckpoint(); condition_.WaitHoldingLocks(self); } // At this point, since the sentinel of the reference is live, it is guaranteed to not be diff --git a/runtime/gc/system_weak.h b/runtime/gc/system_weak.h index 887059b57f..e5cddfc6f9 100644 --- a/runtime/gc/system_weak.h +++ b/runtime/gc/system_weak.h @@ -30,7 +30,8 @@ class AbstractSystemWeakHolder { virtual void Allow() REQUIRES_SHARED(Locks::mutator_lock_) = 0; virtual void Disallow() REQUIRES_SHARED(Locks::mutator_lock_) = 0; - virtual void Broadcast() REQUIRES_SHARED(Locks::mutator_lock_) = 0; + // See Runtime::BroadcastForNewSystemWeaks for the broadcast_for_checkpoint definition. + virtual void Broadcast(bool broadcast_for_checkpoint) = 0; virtual void Sweep(IsMarkedVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) = 0; }; @@ -61,10 +62,8 @@ class SystemWeakHolder : public AbstractSystemWeakHolder { allow_new_system_weak_ = false; } - void Broadcast() OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) + void Broadcast(bool broadcast_for_checkpoint ATTRIBUTE_UNUSED) OVERRIDE REQUIRES(!allow_disallow_lock_) { - CHECK(kUseReadBarrier); MutexLock mu(Thread::Current(), allow_disallow_lock_); new_weak_condition_.Broadcast(Thread::Current()); } @@ -75,10 +74,15 @@ class SystemWeakHolder : public AbstractSystemWeakHolder { } protected: - void Wait(Thread* self) REQUIRES_SHARED(allow_disallow_lock_) { + void Wait(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(allow_disallow_lock_) { // Wait for GC's sweeping to complete and allow new records while (UNLIKELY((!kUseReadBarrier && !allow_new_system_weak_) || (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) { + // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the + // presence of threads blocking for weak ref access. + self->CheckEmptyCheckpoint(); new_weak_condition_.WaitHoldingLocks(self); } } diff --git a/runtime/gc/system_weak_test.cc b/runtime/gc/system_weak_test.cc index af8a444903..9b601c0753 100644 --- a/runtime/gc/system_weak_test.cc +++ b/runtime/gc/system_weak_test.cc @@ -58,12 +58,14 @@ struct CountingSystemWeakHolder : public SystemWeakHolder { disallow_count_++; } - void Broadcast() OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) + void Broadcast(bool broadcast_for_checkpoint) OVERRIDE REQUIRES(!allow_disallow_lock_) { - SystemWeakHolder::Broadcast(); + SystemWeakHolder::Broadcast(broadcast_for_checkpoint); - allow_count_++; + if (!broadcast_for_checkpoint) { + // Don't count the broadcasts for running checkpoints. + allow_count_++; + } } void Sweep(IsMarkedVisitor* visitor) OVERRIDE diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index 2c95fe942e..f13ff8c9bc 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -142,6 +142,10 @@ DEFINE_CHECK_EQ(static_cast<int32_t>(ROSALLOC_SLOT_NEXT_OFFSET), (static_cast<in DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_SUSPEND_REQUEST), (static_cast<int32_t>((art::kSuspendRequest)))) #define THREAD_CHECKPOINT_REQUEST 2 DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_CHECKPOINT_REQUEST), (static_cast<int32_t>((art::kCheckpointRequest)))) +#define THREAD_EMPTY_CHECKPOINT_REQUEST 4 +DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_EMPTY_CHECKPOINT_REQUEST), (static_cast<int32_t>((art::kEmptyCheckpointRequest)))) +#define THREAD_SUSPEND_OR_CHECKPOINT_REQUEST 7 +DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), (static_cast<int32_t>((art::kSuspendRequest | art::kCheckpointRequest | art::kEmptyCheckpointRequest)))) #define JIT_CHECK_OSR (-1) DEFINE_CHECK_EQ(static_cast<int16_t>(JIT_CHECK_OSR), (static_cast<int16_t>((art::jit::kJitCheckForOSR)))) #define JIT_HOTNESS_DISABLE (-2) diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index d885226bb0..9c05d3c574 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -188,7 +188,6 @@ void InternTable::AddImagesStringsToTable(const std::vector<gc::space::ImageSpac } void InternTable::BroadcastForNewInterns() { - CHECK(kUseReadBarrier); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::intern_table_lock_); weak_intern_condition_.Broadcast(self); diff --git a/runtime/intern_table.h b/runtime/intern_table.h index acb20671ab..f661d9ffe5 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -112,7 +112,7 @@ class InternTable { void DumpForSigQuit(std::ostream& os) const REQUIRES(!Locks::intern_table_lock_); - void BroadcastForNewInterns() REQUIRES_SHARED(Locks::mutator_lock_); + void BroadcastForNewInterns(); // Adds all of the resolved image strings from the image spaces into the intern table. The // advantage of doing this is preventing expensive DexFile::FindStringId calls. Sets diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S index 62e573a7db..cd32ea2e28 100644 --- a/runtime/interpreter/mterp/arm/footer.S +++ b/runtime/interpreter/mterp/arm/footer.S @@ -156,7 +156,7 @@ MterpCommonTakenBranch: REFRESH_IBASE add r2, rINST, rINST @ r2<- byte offset FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST bne .L_suspend_request_pending GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_return.S b/runtime/interpreter/mterp/arm/op_return.S index 1888373256..f9c0f0fa19 100644 --- a/runtime/interpreter/mterp/arm/op_return.S +++ b/runtime/interpreter/mterp/arm/op_return.S @@ -8,7 +8,7 @@ bl MterpThreadFenceForConstructor ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA GET_VREG r0, r2 @ r0<- vAA diff --git a/runtime/interpreter/mterp/arm/op_return_void.S b/runtime/interpreter/mterp/arm/op_return_void.S index cbea2bf6a0..a91ccb31f5 100644 --- a/runtime/interpreter/mterp/arm/op_return_void.S +++ b/runtime/interpreter/mterp/arm/op_return_void.S @@ -2,7 +2,7 @@ bl MterpThreadFenceForConstructor ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r0, #0 mov r1, #0 diff --git a/runtime/interpreter/mterp/arm/op_return_void_no_barrier.S b/runtime/interpreter/mterp/arm/op_return_void_no_barrier.S index 2dde7aeefc..b953f4c7fd 100644 --- a/runtime/interpreter/mterp/arm/op_return_void_no_barrier.S +++ b/runtime/interpreter/mterp/arm/op_return_void_no_barrier.S @@ -1,6 +1,6 @@ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r0, #0 mov r1, #0 diff --git a/runtime/interpreter/mterp/arm/op_return_wide.S b/runtime/interpreter/mterp/arm/op_return_wide.S index ceae878fa4..df582c0831 100644 --- a/runtime/interpreter/mterp/arm/op_return_wide.S +++ b/runtime/interpreter/mterp/arm/op_return_wide.S @@ -6,7 +6,7 @@ bl MterpThreadFenceForConstructor ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[AA] diff --git a/runtime/interpreter/mterp/arm64/footer.S b/runtime/interpreter/mterp/arm64/footer.S index 7628ed3c47..ada0326649 100644 --- a/runtime/interpreter/mterp/arm64/footer.S +++ b/runtime/interpreter/mterp/arm64/footer.S @@ -141,7 +141,7 @@ MterpCommonTakenBranchNoFlags: add w2, wINST, wINST // w2<- byte offset FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST REFRESH_IBASE - ands lr, lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L_suspend_request_pending GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction @@ -215,7 +215,7 @@ MterpCommonTakenBranchNoFlags: */ MterpCheckSuspendAndContinue: ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh xIBASE - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne check1 GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction @@ -270,7 +270,7 @@ MterpReturn: ldr lr, [xSELF, #THREAD_FLAGS_OFFSET] str x0, [x2] mov x0, xSELF - ands lr, lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.eq check2 bl MterpSuspendCheck // (self) check2: diff --git a/runtime/interpreter/mterp/arm64/op_return.S b/runtime/interpreter/mterp/arm64/op_return.S index 28630eed48..9f125c7fef 100644 --- a/runtime/interpreter/mterp/arm64/op_return.S +++ b/runtime/interpreter/mterp/arm64/op_return.S @@ -8,7 +8,7 @@ bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L${opcode}_check .L${opcode}_return: lsr w2, wINST, #8 // r2<- AA diff --git a/runtime/interpreter/mterp/arm64/op_return_void.S b/runtime/interpreter/mterp/arm64/op_return_void.S index 3a5aa56162..b2530062e8 100644 --- a/runtime/interpreter/mterp/arm64/op_return_void.S +++ b/runtime/interpreter/mterp/arm64/op_return_void.S @@ -2,7 +2,7 @@ bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L${opcode}_check .L${opcode}_return: mov x0, #0 diff --git a/runtime/interpreter/mterp/arm64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/arm64/op_return_void_no_barrier.S index 1e0695374d..c817169070 100644 --- a/runtime/interpreter/mterp/arm64/op_return_void_no_barrier.S +++ b/runtime/interpreter/mterp/arm64/op_return_void_no_barrier.S @@ -1,6 +1,6 @@ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L${opcode}_check .L${opcode}_return: mov x0, #0 diff --git a/runtime/interpreter/mterp/arm64/op_return_wide.S b/runtime/interpreter/mterp/arm64/op_return_wide.S index c6e1d9da80..c47661cd54 100644 --- a/runtime/interpreter/mterp/arm64/op_return_wide.S +++ b/runtime/interpreter/mterp/arm64/op_return_wide.S @@ -7,7 +7,7 @@ bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L${opcode}_check .L${opcode}_return: lsr w2, wINST, #8 // w2<- AA diff --git a/runtime/interpreter/mterp/mips/footer.S b/runtime/interpreter/mterp/mips/footer.S index 1363751566..9909dfeb47 100644 --- a/runtime/interpreter/mterp/mips/footer.S +++ b/runtime/interpreter/mterp/mips/footer.S @@ -151,7 +151,7 @@ MterpCommonTakenBranchNoFlags: REFRESH_IBASE() addu a2, rINST, rINST # a2<- byte offset FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST bnez ra, .L_suspend_request_pending GET_INST_OPCODE(t0) # extract opcode from rINST GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_return.S b/runtime/interpreter/mterp/mips/op_return.S index 894ae18de0..44b93958d2 100644 --- a/runtime/interpreter/mterp/mips/op_return.S +++ b/runtime/interpreter/mterp/mips/op_return.S @@ -8,7 +8,7 @@ JAL(MterpThreadFenceForConstructor) lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: diff --git a/runtime/interpreter/mterp/mips/op_return_void.S b/runtime/interpreter/mterp/mips/op_return_void.S index 35c1326306..1f616ea198 100644 --- a/runtime/interpreter/mterp/mips/op_return_void.S +++ b/runtime/interpreter/mterp/mips/op_return_void.S @@ -2,7 +2,7 @@ JAL(MterpThreadFenceForConstructor) lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: diff --git a/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S b/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S index 56968b5fc4..e670c2867f 100644 --- a/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S +++ b/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S @@ -1,6 +1,6 @@ lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: diff --git a/runtime/interpreter/mterp/mips/op_return_wide.S b/runtime/interpreter/mterp/mips/op_return_wide.S index 91d62bf550..f0f679dbde 100644 --- a/runtime/interpreter/mterp/mips/op_return_wide.S +++ b/runtime/interpreter/mterp/mips/op_return_wide.S @@ -6,7 +6,7 @@ JAL(MterpThreadFenceForConstructor) lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S index 40631626b6..64772c804d 100644 --- a/runtime/interpreter/mterp/mips64/footer.S +++ b/runtime/interpreter/mterp/mips64/footer.S @@ -108,7 +108,7 @@ MterpCommonTakenBranchNoFlags: REFRESH_IBASE daddu a2, rINST, rINST # a2<- byte offset FETCH_ADVANCE_INST_RB a2 # update rPC, load rINST - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST bnezc ra, .L_suspend_request_pending GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -225,7 +225,7 @@ MterpReturn: lw ra, THREAD_FLAGS_OFFSET(rSELF) sd a0, 0(a2) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, check2 jal MterpSuspendCheck # (self) check2: diff --git a/runtime/interpreter/mterp/mips64/op_return.S b/runtime/interpreter/mterp/mips64/op_return.S index b10c03f072..edd795f561 100644 --- a/runtime/interpreter/mterp/mips64/op_return.S +++ b/runtime/interpreter/mterp/mips64/op_return.S @@ -10,7 +10,7 @@ jal MterpThreadFenceForConstructor lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: diff --git a/runtime/interpreter/mterp/mips64/op_return_void.S b/runtime/interpreter/mterp/mips64/op_return_void.S index 05253aea05..f6eee915a5 100644 --- a/runtime/interpreter/mterp/mips64/op_return_void.S +++ b/runtime/interpreter/mterp/mips64/op_return_void.S @@ -3,7 +3,7 @@ jal MterpThreadFenceForConstructor lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: diff --git a/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S index f67e811c70..4e9b640226 100644 --- a/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S +++ b/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S @@ -1,7 +1,7 @@ .extern MterpSuspendCheck lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: diff --git a/runtime/interpreter/mterp/mips64/op_return_wide.S b/runtime/interpreter/mterp/mips64/op_return_wide.S index 544e02794d..91ca1fae59 100644 --- a/runtime/interpreter/mterp/mips64/op_return_wide.S +++ b/runtime/interpreter/mterp/mips64/op_return_wide.S @@ -8,7 +8,7 @@ jal MterpThreadFenceForConstructor lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 46d5af179f..2bd47bbfcf 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -564,6 +564,8 @@ extern "C" void MterpLogSuspendFallback(Thread* self, ShadowFrame* shadow_frame, LOG(INFO) << "Checkpoint fallback: " << inst->Opcode(inst_data); } else if (flags & kSuspendRequest) { LOG(INFO) << "Suspend fallback: " << inst->Opcode(inst_data); + } else if (flags & kEmptyCheckpointRequest) { + LOG(INFO) << "Empty checkpoint fallback: " << inst->Opcode(inst_data); } } diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 78a90af54f..4d540d768b 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -619,7 +619,7 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpThreadFenceForConstructor ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r0, #0 mov r1, #0 @@ -639,7 +639,7 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpThreadFenceForConstructor ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA GET_VREG r0, r2 @ r0<- vAA @@ -658,7 +658,7 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpThreadFenceForConstructor ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[AA] @@ -680,7 +680,7 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpThreadFenceForConstructor ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA GET_VREG r0, r2 @ r0<- vAA @@ -3149,7 +3149,7 @@ artMterpAsmInstructionStart = .L_op_nop /* File: arm/op_return_void_no_barrier.S */ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST blne MterpSuspendCheck @ (self) mov r0, #0 mov r1, #0 @@ -11989,7 +11989,7 @@ MterpCommonTakenBranch: REFRESH_IBASE add r2, rINST, rINST @ r2<- byte offset FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST bne .L_suspend_request_pending GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index dafcc3ef6a..42f8c1b08d 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -616,7 +616,7 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .Lop_return_void_check .Lop_return_void_return: mov x0, #0 @@ -639,7 +639,7 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .Lop_return_check .Lop_return_return: lsr w2, wINST, #8 // r2<- AA @@ -662,7 +662,7 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .Lop_return_wide_check .Lop_return_wide_return: lsr w2, wINST, #8 // w2<- AA @@ -687,7 +687,7 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpThreadFenceForConstructor ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .Lop_return_object_check .Lop_return_object_return: lsr w2, wINST, #8 // r2<- AA @@ -3033,7 +3033,7 @@ artMterpAsmInstructionStart = .L_op_nop /* File: arm64/op_return_void_no_barrier.S */ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov x0, xSELF - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .Lop_return_void_no_barrier_check .Lop_return_void_no_barrier_return: mov x0, #0 @@ -7082,7 +7082,7 @@ MterpCommonTakenBranchNoFlags: add w2, wINST, wINST // w2<- byte offset FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST REFRESH_IBASE - ands lr, lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne .L_suspend_request_pending GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction @@ -7156,7 +7156,7 @@ MterpCommonTakenBranchNoFlags: */ MterpCheckSuspendAndContinue: ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh xIBASE - ands w7, w7, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands w7, w7, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne check1 GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction @@ -7211,7 +7211,7 @@ MterpReturn: ldr lr, [xSELF, #THREAD_FLAGS_OFFSET] str x0, [x2] mov x0, xSELF - ands lr, lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + ands lr, lr, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.eq check2 bl MterpSuspendCheck // (self) check2: diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index d3b91e2686..e154e6c6b2 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -1040,7 +1040,7 @@ artMterpAsmInstructionStart = .L_op_nop JAL(MterpThreadFenceForConstructor) lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: @@ -1062,7 +1062,7 @@ artMterpAsmInstructionStart = .L_op_nop JAL(MterpThreadFenceForConstructor) lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: @@ -1083,7 +1083,7 @@ artMterpAsmInstructionStart = .L_op_nop JAL(MterpThreadFenceForConstructor) lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: @@ -1107,7 +1107,7 @@ artMterpAsmInstructionStart = .L_op_nop JAL(MterpThreadFenceForConstructor) lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: @@ -3524,7 +3524,7 @@ artMterpAsmInstructionStart = .L_op_nop /* File: mips/op_return_void_no_barrier.S */ lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqz ra, 1f JAL(MterpSuspendCheck) # (self) 1: @@ -12651,7 +12651,7 @@ MterpCommonTakenBranchNoFlags: REFRESH_IBASE() addu a2, rINST, rINST # a2<- byte offset FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST bnez ra, .L_suspend_request_pending GET_INST_OPCODE(t0) # extract opcode from rINST GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 143aeb034c..037787f6b4 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -637,7 +637,7 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpThreadFenceForConstructor lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: @@ -659,7 +659,7 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpThreadFenceForConstructor lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: @@ -681,7 +681,7 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpThreadFenceForConstructor lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: @@ -705,7 +705,7 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpThreadFenceForConstructor lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: @@ -3121,7 +3121,7 @@ artMterpAsmInstructionStart = .L_op_nop .extern MterpSuspendCheck lw ra, THREAD_FLAGS_OFFSET(rSELF) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, 1f jal MterpSuspendCheck # (self) 1: @@ -12179,7 +12179,7 @@ MterpCommonTakenBranchNoFlags: REFRESH_IBASE daddu a2, rINST, rINST # a2<- byte offset FETCH_ADVANCE_INST_RB a2 # update rPC, load rINST - and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST bnezc ra, .L_suspend_request_pending GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -12296,7 +12296,7 @@ MterpReturn: lw ra, THREAD_FLAGS_OFFSET(rSELF) sd a0, 0(a2) move a0, rSELF - and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + and ra, ra, THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beqzc ra, check2 jal MterpSuspendCheck # (self) check2: diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index d676fdab96..695d1e4973 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -612,7 +612,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movl rSELF, %eax - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) @@ -634,7 +634,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movl rSELF, %eax - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) @@ -654,7 +654,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movl rSELF, %eax - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) @@ -677,7 +677,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movl rSELF, %eax - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) @@ -3104,7 +3104,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .L_op_return_void_no_barrier: /* 0x73 */ /* File: x86/op_return_void_no_barrier.S */ movl rSELF, %eax - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) @@ -12678,7 +12678,7 @@ MterpCommonTakenBranch: je .L_add_batch # counted down to zero - report .L_resume_backward_branch: movl rSELF, %eax - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) leal (rPC, rINST, 2), rPC FETCH_INST jnz .L_suspend_request_pending diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index df88499a62..2eab58c053 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -587,7 +587,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movq rSELF, OUT_ARG0 - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: @@ -607,7 +607,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movq rSELF, OUT_ARG0 - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: @@ -625,7 +625,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movq rSELF, OUT_ARG0 - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: @@ -646,7 +646,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movq rSELF, OUT_ARG0 - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: @@ -2972,7 +2972,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop .L_op_return_void_no_barrier: /* 0x73 */ /* File: x86_64/op_return_void_no_barrier.S */ movq rSELF, OUT_ARG0 - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: @@ -11915,7 +11915,7 @@ MterpCommonTakenBranch: je .L_add_batch # counted down to zero - report .L_resume_backward_branch: movq rSELF, %rax - testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%rax) + testl $(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%rax) REFRESH_IBASE leaq (rPC, rINSTq, 2), rPC FETCH_INST diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index e8c8ca8d79..088cb127dc 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/footer.S @@ -167,7 +167,7 @@ MterpCommonTakenBranch: je .L_add_batch # counted down to zero - report .L_resume_backward_branch: movl rSELF, %eax - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) leal (rPC, rINST, 2), rPC FETCH_INST jnz .L_suspend_request_pending diff --git a/runtime/interpreter/mterp/x86/op_return.S b/runtime/interpreter/mterp/x86/op_return.S index 8e3cfad380..a8ebbed64e 100644 --- a/runtime/interpreter/mterp/x86/op_return.S +++ b/runtime/interpreter/mterp/x86/op_return.S @@ -7,7 +7,7 @@ .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movl rSELF, %eax - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) diff --git a/runtime/interpreter/mterp/x86/op_return_void.S b/runtime/interpreter/mterp/x86/op_return_void.S index a14a4f6394..d9eddf39f2 100644 --- a/runtime/interpreter/mterp/x86/op_return_void.S +++ b/runtime/interpreter/mterp/x86/op_return_void.S @@ -1,7 +1,7 @@ .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movl rSELF, %eax - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) diff --git a/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S index 1d0e93331b..2fbda6bfe9 100644 --- a/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S +++ b/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S @@ -1,5 +1,5 @@ movl rSELF, %eax - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) diff --git a/runtime/interpreter/mterp/x86/op_return_wide.S b/runtime/interpreter/mterp/x86/op_return_wide.S index 7d1850a962..5fff626200 100644 --- a/runtime/interpreter/mterp/x86/op_return_wide.S +++ b/runtime/interpreter/mterp/x86/op_return_wide.S @@ -5,7 +5,7 @@ .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movl rSELF, %eax - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) jz 1f movl %eax, OUT_ARG0(%esp) call SYMBOL(MterpSuspendCheck) diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S index f78f163576..ed5e5eabfa 100644 --- a/runtime/interpreter/mterp/x86_64/footer.S +++ b/runtime/interpreter/mterp/x86_64/footer.S @@ -151,7 +151,7 @@ MterpCommonTakenBranch: je .L_add_batch # counted down to zero - report .L_resume_backward_branch: movq rSELF, %rax - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%rax) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%rax) REFRESH_IBASE leaq (rPC, rINSTq, 2), rPC FETCH_INST diff --git a/runtime/interpreter/mterp/x86_64/op_return.S b/runtime/interpreter/mterp/x86_64/op_return.S index 07e0e5357c..8cb6cbaee1 100644 --- a/runtime/interpreter/mterp/x86_64/op_return.S +++ b/runtime/interpreter/mterp/x86_64/op_return.S @@ -7,7 +7,7 @@ .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movq rSELF, OUT_ARG0 - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: diff --git a/runtime/interpreter/mterp/x86_64/op_return_void.S b/runtime/interpreter/mterp/x86_64/op_return_void.S index 6a12df318b..ba68e7e444 100644 --- a/runtime/interpreter/mterp/x86_64/op_return_void.S +++ b/runtime/interpreter/mterp/x86_64/op_return_void.S @@ -1,7 +1,7 @@ .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movq rSELF, OUT_ARG0 - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: diff --git a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S index 822b2e85e6..6799da1dbd 100644 --- a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S +++ b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S @@ -1,5 +1,5 @@ movq rSELF, OUT_ARG0 - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: diff --git a/runtime/interpreter/mterp/x86_64/op_return_wide.S b/runtime/interpreter/mterp/x86_64/op_return_wide.S index 288eb96f8c..d6d6d1bf5e 100644 --- a/runtime/interpreter/mterp/x86_64/op_return_wide.S +++ b/runtime/interpreter/mterp/x86_64/op_return_wide.S @@ -5,7 +5,7 @@ .extern MterpThreadFenceForConstructor call SYMBOL(MterpThreadFenceForConstructor) movq rSELF, OUT_ARG0 - testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0) jz 1f call SYMBOL(MterpSuspendCheck) 1: diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index 8e76aeb7cd..caf705a9cb 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -562,6 +562,9 @@ jweak JavaVMExt::AddWeakGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) { } MutexLock mu(self, *Locks::jni_weak_globals_lock_); while (UNLIKELY(!MayAccessWeakGlobals(self))) { + // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the + // presence of threads blocking for weak ref access. + self->CheckEmptyCheckpoint(); weak_globals_add_condition_.WaitHoldingLocks(self); } IndirectRef ref = weak_globals_.Add(kIRTFirstSegment, obj); @@ -648,7 +651,6 @@ void JavaVMExt::AllowNewWeakGlobals() { } void JavaVMExt::BroadcastForNewWeakGlobals() { - CHECK(kUseReadBarrier); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::jni_weak_globals_lock_); weak_globals_add_condition_.Broadcast(self); @@ -694,6 +696,9 @@ ObjPtr<mirror::Object> JavaVMExt::DecodeWeakGlobalLocked(Thread* self, IndirectR Locks::jni_weak_globals_lock_->AssertHeld(self); } while (UNLIKELY(!MayAccessWeakGlobals(self))) { + // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the + // presence of threads blocking for weak ref access. + self->CheckEmptyCheckpoint(); weak_globals_add_condition_.WaitHoldingLocks(self); } return weak_globals_.Get(ref); @@ -716,6 +721,9 @@ bool JavaVMExt::IsWeakGlobalCleared(Thread* self, IndirectRef ref) { DCHECK_EQ(IndirectReferenceTable::GetIndirectRefKind(ref), kWeakGlobal); MutexLock mu(self, *Locks::jni_weak_globals_lock_); while (UNLIKELY(!MayAccessWeakGlobals(self))) { + // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the + // presence of threads blocking for weak ref access. + self->CheckEmptyCheckpoint(); weak_globals_add_condition_.WaitHoldingLocks(self); } // When just checking a weak ref has been cleared, avoid triggering the read barrier in decode diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h index 9e37f1178c..7374920f2b 100644 --- a/runtime/java_vm_ext.h +++ b/runtime/java_vm_ext.h @@ -136,7 +136,6 @@ class JavaVMExt : public JavaVM { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::jni_weak_globals_lock_); void BroadcastForNewWeakGlobals() - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::jni_weak_globals_lock_); jobject AddGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index 0b26f9bd0c..fdca078bc7 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -152,7 +152,7 @@ class ProfileCompilationInfo { uint8_t* Get() { return storage_.get(); } private: - std::unique_ptr<uint8_t> storage_; + std::unique_ptr<uint8_t[]> storage_; uint8_t* ptr_current_; uint8_t* ptr_end_; }; diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index f1adc32ce2..3c22d7f656 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -141,7 +141,14 @@ bool IsParameterTypeConvertible(ObjPtr<mirror::Class> from, ObjPtr<mirror::Class } Primitive::Type unboxed_type; if (GetUnboxedPrimitiveType(from, &unboxed_type)) { - return Primitive::IsWidenable(unboxed_type, to_primitive); + if (unboxed_type == to_primitive) { + // Straightforward unboxing conversion such as Boolean => boolean. + return true; + } else { + // Check if widening operations for numeric primitives would work, + // such as Byte => byte => long. + return Primitive::IsWidenable(unboxed_type, to_primitive); + } } } diff --git a/runtime/monitor.cc b/runtime/monitor.cc index f5831670fa..e7de7e64c6 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1330,7 +1330,6 @@ void MonitorList::AllowNewMonitors() { } void MonitorList::BroadcastForNewMonitors() { - CHECK(kUseReadBarrier); Thread* self = Thread::Current(); MutexLock mu(self, monitor_list_lock_); monitor_add_condition_.Broadcast(self); @@ -1341,6 +1340,9 @@ void MonitorList::Add(Monitor* m) { MutexLock mu(self, monitor_list_lock_); while (UNLIKELY((!kUseReadBarrier && !allow_new_monitors_) || (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) { + // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the + // presence of threads blocking for weak ref access. + self->CheckEmptyCheckpoint(); monitor_add_condition_.WaitHoldingLocks(self); } list_.push_front(m); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index ff00451343..0679360a15 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -595,7 +595,7 @@ OatFileAssistant::GenerateOatFile(std::string* error_msg) { std::vector<std::string> args; args.push_back("--dex-file=" + dex_location_); - args.push_back("--vdex-fd=" + std::to_string(vdex_file->Fd())); + args.push_back("--output-vdex-fd=" + std::to_string(vdex_file->Fd())); args.push_back("--oat-fd=" + std::to_string(oat_file->Fd())); args.push_back("--oat-location=" + oat_file_name); diff --git a/runtime/primitive.h b/runtime/primitive.h index 7cc47ad79b..a0edaee6fe 100644 --- a/runtime/primitive.h +++ b/runtime/primitive.h @@ -162,7 +162,7 @@ class Primitive { } // Return true if |type| is an numeric type. - static bool IsNumericType(Type type) { + static constexpr bool IsNumericType(Type type) { switch (type) { case Primitive::Type::kPrimNot: return false; case Primitive::Type::kPrimBoolean: return false; @@ -177,13 +177,16 @@ class Primitive { } } - // Returns true if |from| and |to| are the same or a widening conversion exists between them. + // Returns true if it is possible to widen type |from| to type |to|. Both |from| and + // |to| should be numeric primitive types. static bool IsWidenable(Type from, Type to) { static_assert(Primitive::Type::kPrimByte < Primitive::Type::kPrimShort, "Bad ordering"); static_assert(Primitive::Type::kPrimShort < Primitive::Type::kPrimInt, "Bad ordering"); static_assert(Primitive::Type::kPrimInt < Primitive::Type::kPrimLong, "Bad ordering"); static_assert(Primitive::Type::kPrimLong < Primitive::Type::kPrimFloat, "Bad ordering"); static_assert(Primitive::Type::kPrimFloat < Primitive::Type::kPrimDouble, "Bad ordering"); + // Widening is only applicable between numeric types, like byte + // and int. Non-numeric types, such as boolean, cannot be widened. return IsNumericType(from) && IsNumericType(to) && from <= to; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index ca65c2bea3..6d849c446b 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1761,10 +1761,10 @@ void Runtime::AllowNewSystemWeaks() { } } -void Runtime::BroadcastForNewSystemWeaks() { +void Runtime::BroadcastForNewSystemWeaks(bool broadcast_for_checkpoint) { // This is used for the read barrier case that uses the thread-local - // Thread::GetWeakRefAccessEnabled() flag. - CHECK(kUseReadBarrier); + // Thread::GetWeakRefAccessEnabled() flag and the checkpoint while weak ref access is disabled + // (see ThreadList::RunCheckpoint). monitor_list_->BroadcastForNewMonitors(); intern_table_->BroadcastForNewInterns(); java_vm_->BroadcastForNewWeakGlobals(); @@ -1772,7 +1772,7 @@ void Runtime::BroadcastForNewSystemWeaks() { // All other generic system-weak holders. for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) { - holder->Broadcast(); + holder->Broadcast(broadcast_for_checkpoint); } } diff --git a/runtime/runtime.h b/runtime/runtime.h index 6abe682d4b..6806180de5 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -107,9 +107,7 @@ enum VisitRootFlags : uint8_t { kVisitRootFlagStartLoggingNewRoots = 0x4, kVisitRootFlagStopLoggingNewRoots = 0x8, kVisitRootFlagClearRootLog = 0x10, - // Non moving means we can have optimizations where we don't visit some roots if they are - // definitely reachable from another location. E.g. ArtMethod and ArtField roots. - kVisitRootFlagNonMoving = 0x20, + kVisitRootFlagClassLoader = 0x20, }; class Runtime { @@ -321,7 +319,10 @@ class Runtime { void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); void AllowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); - void BroadcastForNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); + // broadcast_for_checkpoint is true when we broadcast for making blocking threads to respond to + // checkpoint requests. It's false when we broadcast to unblock blocking threads after system weak + // access is reenabled. + void BroadcastForNewSystemWeaks(bool broadcast_for_checkpoint = false); // Visit all the roots. If only_dirty is true then non-dirty roots won't be visited. If // clean_dirty is true then dirty roots will be marked as non-dirty after visiting. diff --git a/runtime/stack.h b/runtime/stack.h index 8a446ecfd4..992bda5a1e 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -469,14 +469,21 @@ struct ShadowFrameDeleter { } }; -class JavaFrameRootInfo : public RootInfo { +class JavaFrameRootInfo FINAL : public RootInfo { public: JavaFrameRootInfo(uint32_t thread_id, const StackVisitor* stack_visitor, size_t vreg) : RootInfo(kRootJavaFrame, thread_id), stack_visitor_(stack_visitor), vreg_(vreg) { } - virtual void Describe(std::ostream& os) const OVERRIDE + void Describe(std::ostream& os) const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); + size_t GetVReg() const { + return vreg_; + } + const StackVisitor* GetVisitor() const { + return stack_visitor_; + } + private: const StackVisitor* const stack_visitor_; const size_t vreg_; @@ -623,7 +630,7 @@ class StackVisitor { return num_frames_; } - size_t GetFrameDepth() REQUIRES_SHARED(Locks::mutator_lock_) { + size_t GetFrameDepth() const REQUIRES_SHARED(Locks::mutator_lock_) { return cur_depth_; } diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 5fa9353207..c92305f373 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -72,6 +72,19 @@ inline void Thread::CheckSuspend() { RunCheckpointFunction(); } else if (ReadFlag(kSuspendRequest)) { FullSuspendCheck(); + } else if (ReadFlag(kEmptyCheckpointRequest)) { + RunEmptyCheckpoint(); + } else { + break; + } + } +} + +inline void Thread::CheckEmptyCheckpoint() { + DCHECK_EQ(Thread::Current(), this); + for (;;) { + if (ReadFlag(kEmptyCheckpointRequest)) { + RunEmptyCheckpoint(); } else { break; } @@ -145,8 +158,13 @@ inline void Thread::TransitionToSuspendedAndRunCheckpoints(ThreadState new_state RunCheckpointFunction(); continue; } + if (UNLIKELY((old_state_and_flags.as_struct.flags & kEmptyCheckpointRequest) != 0)) { + RunEmptyCheckpoint(); + continue; + } // Change the state but keep the current flags (kCheckpointRequest is clear). DCHECK_EQ((old_state_and_flags.as_struct.flags & kCheckpointRequest), 0); + DCHECK_EQ((old_state_and_flags.as_struct.flags & kEmptyCheckpointRequest), 0); new_state_and_flags.as_struct.flags = old_state_and_flags.as_struct.flags; new_state_and_flags.as_struct.state = new_state; @@ -163,7 +181,8 @@ inline void Thread::TransitionToSuspendedAndRunCheckpoints(ThreadState new_state inline void Thread::PassActiveSuspendBarriers() { while (true) { uint16_t current_flags = tls32_.state_and_flags.as_struct.flags; - if (LIKELY((current_flags & (kCheckpointRequest | kActiveSuspendBarrier)) == 0)) { + if (LIKELY((current_flags & + (kCheckpointRequest | kEmptyCheckpointRequest | kActiveSuspendBarrier)) == 0)) { break; } else if ((current_flags & kActiveSuspendBarrier) != 0) { PassActiveSuspendBarriers(this); @@ -211,7 +230,8 @@ inline ThreadState Thread::TransitionFromSuspendedToRunnable() { } } else if ((old_state_and_flags.as_struct.flags & kActiveSuspendBarrier) != 0) { PassActiveSuspendBarriers(this); - } else if ((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0) { + } else if ((old_state_and_flags.as_struct.flags & + (kCheckpointRequest | kEmptyCheckpointRequest)) != 0) { // Impossible LOG(FATAL) << "Transitioning to runnable with checkpoint flag, " << " flags=" << old_state_and_flags.as_struct.flags diff --git a/runtime/thread.cc b/runtime/thread.cc index 23c077ceab..b99df26706 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1155,6 +1155,12 @@ void Thread::RunCheckpointFunction() { } while (!done); } +void Thread::RunEmptyCheckpoint() { + DCHECK_EQ(Thread::Current(), this); + AtomicClearFlag(kEmptyCheckpointRequest); + Runtime::Current()->GetThreadList()->EmptyCheckpointBarrier()->Pass(this); +} + bool Thread::RequestCheckpoint(Closure* function) { union StateAndFlags old_state_and_flags; old_state_and_flags.as_int = tls32_.state_and_flags.as_int; @@ -1182,6 +1188,28 @@ bool Thread::RequestCheckpoint(Closure* function) { return success; } +bool Thread::RequestEmptyCheckpoint() { + union StateAndFlags old_state_and_flags; + old_state_and_flags.as_int = tls32_.state_and_flags.as_int; + if (old_state_and_flags.as_struct.state != kRunnable) { + // If it's not runnable, we don't need to do anything because it won't be in the middle of a + // heap access (eg. the read barrier). + return false; + } + + // We must be runnable to request a checkpoint. + DCHECK_EQ(old_state_and_flags.as_struct.state, kRunnable); + union StateAndFlags new_state_and_flags; + new_state_and_flags.as_int = old_state_and_flags.as_int; + new_state_and_flags.as_struct.flags |= kEmptyCheckpointRequest; + bool success = tls32_.state_and_flags.as_atomic_int.CompareExchangeStrongSequentiallyConsistent( + old_state_and_flags.as_int, new_state_and_flags.as_int); + if (success) { + TriggerSuspend(); + } + return success; +} + class BarrierClosure : public Closure { public: explicit BarrierClosure(Closure* wrapped) : wrapped_(wrapped), barrier_(0) {} @@ -1841,7 +1869,8 @@ Thread::~Thread() { tlsPtr_.jni_env = nullptr; } CHECK_NE(GetState(), kRunnable); - CHECK_NE(ReadFlag(kCheckpointRequest), true); + CHECK(!ReadFlag(kCheckpointRequest)); + CHECK(!ReadFlag(kEmptyCheckpointRequest)); CHECK(tlsPtr_.checkpoint_function == nullptr); CHECK_EQ(checkpoint_overflow_.size(), 0u); CHECK(tlsPtr_.flip_function == nullptr); diff --git a/runtime/thread.h b/runtime/thread.h index faa77e1581..b2983cc6de 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -105,7 +105,8 @@ enum ThreadFlag { kSuspendRequest = 1, // If set implies that suspend_count_ > 0 and the Thread should enter the // safepoint handler. kCheckpointRequest = 2, // Request that the thread do some checkpoint work and then continue. - kActiveSuspendBarrier = 4 // Register that at least 1 suspend barrier needs to be passed. + kEmptyCheckpointRequest = 4, // Request that the thread do empty checkpoint and then continue. + kActiveSuspendBarrier = 8, // Register that at least 1 suspend barrier needs to be passed. }; enum class StackedShadowFrameType { @@ -171,6 +172,9 @@ class Thread { // Process pending thread suspension request and handle if pending. void CheckSuspend() REQUIRES_SHARED(Locks::mutator_lock_); + // Process a pending empty checkpoint if pending. + void CheckEmptyCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_); + static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts, mirror::Object* thread_peer) REQUIRES(Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_) @@ -239,6 +243,8 @@ class Thread { REQUIRES(Locks::thread_suspend_count_lock_); void RequestSynchronousCheckpoint(Closure* function) REQUIRES(!Locks::thread_suspend_count_lock_, !Locks::thread_list_lock_); + bool RequestEmptyCheckpoint() + REQUIRES(Locks::thread_suspend_count_lock_); void SetFlipFunction(Closure* function); Closure* GetFlipFunction(); @@ -1214,6 +1220,7 @@ class Thread { REQUIRES(Locks::thread_suspend_count_lock_); void RunCheckpointFunction(); + void RunEmptyCheckpoint(); bool PassActiveSuspendBarriers(Thread* self) REQUIRES(!Locks::thread_suspend_count_lock_); diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 8a3bb15463..27fb37a662 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -32,6 +32,7 @@ #include "base/timing_logger.h" #include "debugger.h" #include "gc/collector/concurrent_copying.h" +#include "gc/reference_processor.h" #include "jni_internal.h" #include "lock_word.h" #include "monitor.h" @@ -68,7 +69,8 @@ ThreadList::ThreadList() debug_suspend_all_count_(0), unregistering_count_(0), suspend_all_historam_("suspend all histogram", 16, 64), - long_suspend_(false) { + long_suspend_(false), + empty_checkpoint_barrier_(new Barrier(0)) { CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1, 0U))); } @@ -373,6 +375,43 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback return count; } +size_t ThreadList::RunEmptyCheckpoint() { + Thread* self = Thread::Current(); + Locks::mutator_lock_->AssertNotExclusiveHeld(self); + Locks::thread_list_lock_->AssertNotHeld(self); + Locks::thread_suspend_count_lock_->AssertNotHeld(self); + + size_t count = 0; + { + MutexLock mu(self, *Locks::thread_list_lock_); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); + for (Thread* thread : list_) { + if (thread != self) { + while (true) { + if (thread->RequestEmptyCheckpoint()) { + // This thread will run an empty checkpoint (decrement the empty checkpoint barrier) + // some time in the near future. + ++count; + break; + } + if (thread->GetState() != kRunnable) { + // It's seen suspended, we are done because it must not be in the middle of a mutator + // heap access. + break; + } + } + } + } + } + + // Wake up the threads blocking for weak ref access so that they will respond to the empty + // checkpoint request. Otherwise we will hang as they are blocking in the kRunnable state. + Runtime::Current()->GetHeap()->GetReferenceProcessor()->BroadcastForSlowPath(self); + Runtime::Current()->BroadcastForNewSystemWeaks(/*broadcast_for_checkpoint*/true); + + return count; +} + // Request that a checkpoint function be run on all active (non-suspended) // threads. Returns the number of successful requests. size_t ThreadList::RunCheckpointOnRunnableThreads(Closure* checkpoint_function) { diff --git a/runtime/thread_list.h b/runtime/thread_list.h index b455e31e4c..133d43029a 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_THREAD_LIST_H_ #define ART_RUNTIME_THREAD_LIST_H_ +#include "barrier.h" #include "base/histogram.h" #include "base/mutex.h" #include "base/value_object.h" @@ -100,6 +101,14 @@ class ThreadList { size_t RunCheckpoint(Closure* checkpoint_function, Closure* callback = nullptr) REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); + // Run an empty checkpoint on threads. Wait until threads pass the next suspend point or are + // suspended. This is used to ensure that the threads finish or aren't in the middle of an + // in-flight mutator heap access (eg. a read barrier.) Runnable threads will respond by + // decrementing the empty checkpoint barrier count. This works even when the weak ref access is + // disabled. Only one concurrent use is currently supported. + size_t RunEmptyCheckpoint() + REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); + size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function) REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); @@ -158,6 +167,10 @@ class ThreadList { void DumpNativeStacks(std::ostream& os) REQUIRES(!Locks::thread_list_lock_); + Barrier* EmptyCheckpointBarrier() { + return empty_checkpoint_barrier_.get(); + } + private: uint32_t AllocThreadId(Thread* self); void ReleaseThreadId(Thread* self, uint32_t id) REQUIRES(!Locks::allocated_thread_ids_lock_); @@ -203,6 +216,8 @@ class ThreadList { // Whether or not the current thread suspension is long. bool long_suspend_; + std::unique_ptr<Barrier> empty_checkpoint_barrier_; + friend class Thread; DISALLOW_COPY_AND_ASSIGN(ThreadList); diff --git a/runtime/utils.h b/runtime/utils.h index 21df839131..1e9805790a 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -279,24 +279,34 @@ static inline const void* EntryPointToCodePointer(const void* entry_point) { using UsageFn = void (*)(const char*, ...); template <typename T> -static void ParseUintOption(const StringPiece& option, +static void ParseIntOption(const StringPiece& option, const std::string& option_name, T* out, - UsageFn Usage, + UsageFn usage, bool is_long_option = true) { std::string option_prefix = option_name + (is_long_option ? "=" : ""); DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix; const char* value_string = option.substr(option_prefix.size()).data(); int64_t parsed_integer_value = 0; if (!ParseInt(value_string, &parsed_integer_value)) { - Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string); - } - if (parsed_integer_value < 0) { - Usage("%s passed a negative value %d", option_name.c_str(), parsed_integer_value); + usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string); } *out = dchecked_integral_cast<T>(parsed_integer_value); } +template <typename T> +static void ParseUintOption(const StringPiece& option, + const std::string& option_name, + T* out, + UsageFn usage, + bool is_long_option = true) { + ParseIntOption(option, option_name, out, usage, is_long_option); + if (*out < 0) { + usage("%s passed a negative value %d", option_name.c_str(), *out); + *out = 0; + } +} + void ParseDouble(const std::string& option, char after_char, double min, diff --git a/test/039-join-main/src/Main.java b/test/039-join-main/src/Main.java index 2373221d71..60791e4450 100644 --- a/test/039-join-main/src/Main.java +++ b/test/039-join-main/src/Main.java @@ -14,35 +14,48 @@ * limitations under the License. */ +import java.util.concurrent.CountDownLatch; + /** * Make sure that a sub-thread can join the main thread. */ public class Main { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { Thread t; + CountDownLatch waitLatch = new CountDownLatch(1); + CountDownLatch progressLatch = new CountDownLatch(1); - t = new Thread(new JoinMainSub(Thread.currentThread()), "Joiner"); + t = new Thread(new JoinMainSub(Thread.currentThread(), waitLatch, progressLatch), "Joiner"); System.out.print("Starting thread '" + t.getName() + "'\n"); t.start(); - try { Thread.sleep(1000); } - catch (InterruptedException ie) {} - + waitLatch.await(); System.out.print("JoinMain starter returning\n"); + progressLatch.countDown(); + + // Keep the thread alive a little longer, giving the other thread a chance to join on a + // live thread (though that isn't critically important for the test). + Thread.currentThread().sleep(500); } } class JoinMainSub implements Runnable { private Thread mJoinMe; + private CountDownLatch waitLatch; + private CountDownLatch progressLatch; - public JoinMainSub(Thread joinMe) { + public JoinMainSub(Thread joinMe, CountDownLatch waitLatch, CountDownLatch progressLatch) { mJoinMe = joinMe; + this.waitLatch = waitLatch; + this.progressLatch = progressLatch; } public void run() { System.out.print("@ JoinMainSub running\n"); try { + waitLatch.countDown(); + progressLatch.await(); mJoinMe.join(); System.out.print("@ JoinMainSub successfully joined main\n"); } catch (InterruptedException ie) { diff --git a/test/141-class-unload/expected.txt b/test/141-class-unload/expected.txt index 2b77b29490..0a03ecb3a4 100644 --- a/test/141-class-unload/expected.txt +++ b/test/141-class-unload/expected.txt @@ -21,3 +21,4 @@ JNI_OnLoad called class null false test JNI_OnUnload called Number of loaded unload-ex maps 0 +Too small false diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java index f9b6180ab1..2a6e9442e8 100644 --- a/test/141-class-unload/src/Main.java +++ b/test/141-class-unload/src/Main.java @@ -47,6 +47,8 @@ public class Main { stressTest(constructor); // Test that the oat files are unloaded. testOatFilesUnloaded(getPid()); + // Test that objects keep class loader live for sticky GC. + testStickyUnload(constructor); } catch (Exception e) { e.printStackTrace(); } @@ -161,6 +163,30 @@ public class Main { return intHolder; } + private static Object allocObjectInOtherClassLoader(Constructor<?> constructor) + throws Exception { + ClassLoader loader = (ClassLoader) constructor.newInstance( + DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); + return loader.loadClass("IntHolder").newInstance(); + } + + // Regression test for public issue 227182. + private static void testStickyUnload(Constructor<?> constructor) throws Exception { + String s = ""; + for (int i = 0; i < 10; ++i) { + s = ""; + // The object is the only thing preventing the class loader from being unloaded. + Object o = allocObjectInOtherClassLoader(constructor); + for (int j = 0; j < 1000; ++j) { + s += j + " "; + } + // Make sure the object still has a valid class (hasn't been incorrectly unloaded). + s += o.getClass().getName(); + o = null; + } + System.out.println("Too small " + (s.length() < 1000)); + } + private static WeakReference<Class> setUpUnloadClassWeak(Constructor<?> constructor) throws Exception { return new WeakReference<Class>(setUpUnloadClass(constructor)); diff --git a/test/625-checker-licm-regressions/expected.txt b/test/625-checker-licm-regressions/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/625-checker-licm-regressions/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/625-checker-licm-regressions/info.txt b/test/625-checker-licm-regressions/info.txt new file mode 100644 index 0000000000..10480df32c --- /dev/null +++ b/test/625-checker-licm-regressions/info.txt @@ -0,0 +1 @@ +Regression tests on LICM. diff --git a/test/625-checker-licm-regressions/src/Main.java b/test/625-checker-licm-regressions/src/Main.java new file mode 100644 index 0000000000..cc1e07cabf --- /dev/null +++ b/test/625-checker-licm-regressions/src/Main.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 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. + */ + +/** + * Regression tests for LICM. + */ +public class Main { + + static int sA; + + // + // We cannot hoist the null check (can throw) above the field + // assignment (has write side effects) because that would result + // in throwing an exception before the assignment is done. + // + /// CHECK-START: void Main.foo(int[]) licm (before) + /// CHECK-DAG: LoadClass loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: StaticFieldSet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: NullCheck loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayLength loop:<<Loop>> outer_loop:none + // + /// CHECK-START: void Main.foo(int[]) licm (after) + /// CHECK-DAG: LoadClass loop:none + /// CHECK-DAG: StaticFieldSet loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: NullCheck loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArrayLength loop:<<Loop>> outer_loop:none + // + /// CHECK-START: void Main.foo(int[]) licm (after) + /// CHECK-NOT: LoadClass loop:{{B\d+}} outer_loop:none + static void foo(int[] arr) { + int j = 0; + do { + sA = 1; + } while (j < arr.length); + } + + public static void main(String[] args) { + sA = 0; + try { + foo(null); + } catch (Exception e) { + } + expectEquals(1, sA); + + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/902-hello-transformation/expected.txt b/test/902-hello-transformation/expected.txt index e86e814cab..a826f932d8 100644 --- a/test/902-hello-transformation/expected.txt +++ b/test/902-hello-transformation/expected.txt @@ -1,3 +1,3 @@ -Hello +hello modifying class 'Transform' Goodbye diff --git a/test/902-hello-transformation/src/Transform.java b/test/902-hello-transformation/src/Transform.java index dc0a0c4f04..8e8af355da 100644 --- a/test/902-hello-transformation/src/Transform.java +++ b/test/902-hello-transformation/src/Transform.java @@ -16,6 +16,13 @@ class Transform { public void sayHi() { - System.out.println("Hello"); + // Use lower 'h' to make sure the string will have a different string id + // than the transformation (the transformation code is the same except + // the actual printed String, which was making the test inacurately passing + // in JIT mode when loading the string from the dex cache, as the string ids + // of the two different strings were the same). + // We know the string ids will be different because lexicographically: + // "Goodbye" < "LTransform;" < "hello". + System.out.println("hello"); } } diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index dc6e67d194..d1ddbae8a9 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -3,96 +3,90 @@ true true root@root --(stack-local)--> 1@1000 [size=16, length=-1] root@root --(stack-local)--> 3000@0 [size=132, length=-1] root@root --(thread)--> 3000@0 [size=132, length=-1] +0@0 --(array-element@0)--> 1@1000 [size=16, length=-1] +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=132, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] 1@1000 --(class)--> 1000@0 [size=123, length=-1] -1@1000 --(field@8)--> 2@1000 [size=16, length=-1] 1@1000 --(field@12)--> 3@1001 [size=24, length=-1] -0@0 --(array-element@0)--> 1@1000 [size=16, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +2001@0 --(interface)--> 2000@0 [size=132, length=-1] 2@1000 --(class)--> 1000@0 [size=123, length=-1] 3@1001 --(class)--> 1001@0 [size=123, length=-1] 3@1001 --(field@16)--> 4@1000 [size=16, length=-1] 3@1001 --(field@20)--> 5@1002 [size=32, length=-1] -1001@0 --(superclass)--> 1000@0 [size=123, length=-1] 4@1000 --(class)--> 1000@0 [size=123, length=-1] 5@1002 --(class)--> 1002@0 [size=123, length=-1] 5@1002 --(field@24)--> 6@1000 [size=16, length=-1] 5@1002 --(field@28)--> 1@1000 [size=16, length=-1] -1002@0 --(superclass)--> 1001@0 [size=123, length=-1] -1002@0 --(interface)--> 2001@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123, length=-1] -2001@0 --(interface)--> 2000@0 [size=132, length=-1] --- root@root --(stack-local)--> 1@1000 [size=16, length=-1] -root@root --(stack-local)--> 1@1000 [size=16, length=-1] -root@root --(stack-local)--> 1@1000 [size=16, length=-1] root@root --(stack-local)--> 2@1000 [size=16, length=-1] root@root --(stack-local)--> 3000@0 [size=132, length=-1] root@root --(thread)--> 2@1000 [size=16, length=-1] root@root --(thread)--> 3000@0 [size=132, length=-1] -2@1000 --(class)--> 1000@0 [size=123, length=-1] +0@0 --(array-element@0)--> 1@1000 [size=16, length=-1] +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=132, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] 1@1000 --(class)--> 1000@0 [size=123, length=-1] -1@1000 --(field@8)--> 2@1000 [size=16, length=-1] 1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +2001@0 --(interface)--> 2000@0 [size=132, length=-1] +2@1000 --(class)--> 1000@0 [size=123, length=-1] 3@1001 --(class)--> 1001@0 [size=123, length=-1] 3@1001 --(field@16)--> 4@1000 [size=16, length=-1] 3@1001 --(field@20)--> 5@1002 [size=32, length=-1] -0@0 --(array-element@0)--> 1@1000 [size=16, length=-1] -1001@0 --(superclass)--> 1000@0 [size=123, length=-1] 4@1000 --(class)--> 1000@0 [size=123, length=-1] 5@1002 --(class)--> 1002@0 [size=123, length=-1] 5@1002 --(field@24)--> 6@1000 [size=16, length=-1] 5@1002 --(field@28)--> 1@1000 [size=16, length=-1] -1002@0 --(superclass)--> 1001@0 [size=123, length=-1] -1002@0 --(interface)--> 2001@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123, length=-1] -2001@0 --(interface)--> 2000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local)--> 1@1000 [size=16, length=-1] root@root --(stack-local)--> 1@1000 [size=16, length=-1] -root@root --(stack-local)--> 1@1000 [size=16, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] root@root --(thread)--> 3000@0 [size=132, length=-1] +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=132, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] 1@1000 --(class)--> 1000@0 [size=123, length=-1] -1@1000 --(field@8)--> 2@1000 [size=16, length=-1] 1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +2001@0 --(interface)--> 2000@0 [size=132, length=-1] 2@1000 --(class)--> 1000@0 [size=123, length=-1] 3@1001 --(class)--> 1001@0 [size=123, length=-1] 3@1001 --(field@16)--> 4@1000 [size=16, length=-1] 3@1001 --(field@20)--> 5@1002 [size=32, length=-1] -1001@0 --(superclass)--> 1000@0 [size=123, length=-1] 4@1000 --(class)--> 1000@0 [size=123, length=-1] 5@1002 --(class)--> 1002@0 [size=123, length=-1] 5@1002 --(field@24)--> 6@1000 [size=16, length=-1] 5@1002 --(field@28)--> 1@1000 [size=16, length=-1] -1002@0 --(superclass)--> 1001@0 [size=123, length=-1] -1002@0 --(interface)--> 2001@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123, length=-1] -2001@0 --(interface)--> 2000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local)--> 1@1000 [size=16, length=-1] root@root --(stack-local)--> 1@1000 [size=16, length=-1] -root@root --(stack-local)--> 1@1000 [size=16, length=-1] -root@root --(stack-local)--> 1@1000 [size=16, length=-1] -root@root --(stack-local)--> 1@1000 [size=16, length=-1] root@root --(stack-local)--> 2@1000 [size=16, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] root@root --(thread)--> 2@1000 [size=16, length=-1] root@root --(thread)--> 3000@0 [size=132, length=-1] +1001@0 --(superclass)--> 1000@0 [size=123, length=-1] +1002@0 --(interface)--> 2001@0 [size=132, length=-1] +1002@0 --(superclass)--> 1001@0 [size=123, length=-1] 1@1000 --(class)--> 1000@0 [size=123, length=-1] -1@1000 --(field@8)--> 2@1000 [size=16, length=-1] 1@1000 --(field@12)--> 3@1001 [size=24, length=-1] +1@1000 --(field@8)--> 2@1000 [size=16, length=-1] +2001@0 --(interface)--> 2000@0 [size=132, length=-1] 2@1000 --(class)--> 1000@0 [size=123, length=-1] 3@1001 --(class)--> 1001@0 [size=123, length=-1] 3@1001 --(field@16)--> 4@1000 [size=16, length=-1] 3@1001 --(field@20)--> 5@1002 [size=32, length=-1] -1001@0 --(superclass)--> 1000@0 [size=123, length=-1] 4@1000 --(class)--> 1000@0 [size=123, length=-1] 5@1002 --(class)--> 1002@0 [size=123, length=-1] 5@1002 --(field@24)--> 6@1000 [size=16, length=-1] 5@1002 --(field@28)--> 1@1000 [size=16, length=-1] -1002@0 --(superclass)--> 1001@0 [size=123, length=-1] -1002@0 --(interface)--> 2001@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123, length=-1] -2001@0 --(interface)--> 2000@0 [size=132, length=-1] --- diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index bc07fe9040..4087abdc19 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -25,8 +25,11 @@ #include "base/logging.h" #include "base/macros.h" #include "base/stringprintf.h" +#include "jit/jit.h" #include "jni.h" #include "openjdkjvmti/jvmti.h" +#include "runtime.h" +#include "thread-inl.h" #include "ti-agent/common_helper.h" #include "ti-agent/common_load.h" @@ -280,5 +283,12 @@ jint OnLoad(JavaVM* vm, return 0; } +extern "C" JNIEXPORT void JNICALL Java_Main_waitForJitCompilation(JNIEnv*, jclass) { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + jit->WaitForCompilationToFinish(Thread::Current()); + } +} + } // namespace Test913Heaps } // namespace art diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java index f463429ef4..fc00ada916 100644 --- a/test/913-heaps/src/Main.java +++ b/test/913-heaps/src/Main.java @@ -16,6 +16,8 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; public class Main { public static void main(String[] args) throws Exception { @@ -56,7 +58,6 @@ public class Main { Runtime.getRuntime().gc(); Runtime.getRuntime().gc(); - tagClasses(); setTag(Thread.currentThread(), 3000); { @@ -77,88 +78,103 @@ public class Main { } private static void doFollowReferencesTestNonRoot(ArrayList<Object> tmpStorage) { - A a = createTree(); + Verifier v = new Verifier(); + tagClasses(v); + A a = createTree(v); tmpStorage.add(a); - doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, null); - doFollowReferencesTestImpl(a, Integer.MAX_VALUE, -1, null); + v.add("0@0", "1@1000"); // tmpStorage[0] --(array-element)--> a. + + doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, null, v, null); + doFollowReferencesTestImpl(a.foo, Integer.MAX_VALUE, -1, null, v, "2@1000"); + tmpStorage.clear(); } private static void doFollowReferencesTestRoot() { - A a = createTree(); - doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, a); - doFollowReferencesTestImpl(a, Integer.MAX_VALUE, -1, a); + Verifier v = new Verifier(); + tagClasses(v); + A a = createTree(v); + + doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, a, v, null); + doFollowReferencesTestImpl(a.foo, Integer.MAX_VALUE, -1, a, v, "2@1000"); } private static void doFollowReferencesTestImpl(A root, int stopAfter, int followSet, - Object asRoot) { - String[] lines = - followReferences(0, null, root == null ? null : root.foo, stopAfter, followSet, asRoot); - // Note: sort the roots, as stack locals visit order isn't defined, so may depend on compiled - // code. Do not sort non-roots, as the order here needs to be verified (elements are - // finished before a reference is followed). The test setup (and root visit order) - // luckily ensures that this is deterministic. - - int i = 0; - ArrayList<String> rootLines = new ArrayList<>(); - while (i < lines.length) { - if (lines[i].startsWith("root")) { - rootLines.add(lines[i]); - } else { - break; - } - i++; - } - Collections.sort(rootLines); - for (String l : rootLines) { - System.out.println(l); - } + Object asRoot, Verifier v, String additionalEnabled) { + waitForJitCompilation(); // Wait to avoid JIT influence (e.g., JNI globals). - // Print the non-root lines in order. - while (i < lines.length) { - System.out.println(lines[i]); - i++; - } + String[] lines = + followReferences(0, null, root, stopAfter, followSet, asRoot); - System.out.println("---"); + v.process(lines, additionalEnabled); // TODO: Test filters. } - private static void tagClasses() { + private static void tagClasses(Verifier v) { setTag(A.class, 1000); + setTag(B.class, 1001); + v.add("1001@0", "1000@0"); // B.class --(superclass)--> A.class. + setTag(C.class, 1002); + v.add("1002@0", "1001@0"); // C.class --(superclass)--> B.class. + v.add("1002@0", "2001@0"); // C.class --(interface)--> I2.class. + setTag(I1.class, 2000); + setTag(I2.class, 2001); + v.add("2001@0", "2000@0"); // I2.class --(interface)--> I1.class. } - private static A createTree() { - A root = new A(); - setTag(root, 1); - - A foo = new A(); - setTag(foo, 2); - root.foo = foo; - - B foo2 = new B(); - setTag(foo2, 3); - root.foo2 = foo2; - - A bar = new A(); - setTag(bar, 4); - foo2.bar = bar; - - C bar2 = new C(); - setTag(bar2, 5); - foo2.bar2 = bar2; - - A baz = new A(); - setTag(baz, 6); - bar2.baz = baz; - bar2.baz2 = root; - - return root; + private static A createTree(Verifier v) { + A aInst = new A(); + setTag(aInst, 1); + String aInstStr = "1@1000"; + String aClassStr = "1000@0"; + v.add(aInstStr, aClassStr); // A -->(class) --> A.class. + + A a2Inst = new A(); + setTag(a2Inst, 2); + aInst.foo = a2Inst; + String a2InstStr = "2@1000"; + v.add(a2InstStr, aClassStr); // A2 -->(class) --> A.class. + v.add(aInstStr, a2InstStr); // A -->(field) --> A2. + + B bInst = new B(); + setTag(bInst, 3); + aInst.foo2 = bInst; + String bInstStr = "3@1001"; + String bClassStr = "1001@0"; + v.add(bInstStr, bClassStr); // B -->(class) --> B.class. + v.add(aInstStr, bInstStr); // A -->(field) --> B. + + A a3Inst = new A(); + setTag(a3Inst, 4); + bInst.bar = a3Inst; + String a3InstStr = "4@1000"; + v.add(a3InstStr, aClassStr); // A3 -->(class) --> A.class. + v.add(bInstStr, a3InstStr); // B -->(field) --> A3. + + C cInst = new C(); + setTag(cInst, 5); + bInst.bar2 = cInst; + String cInstStr = "5@1000"; + String cClassStr = "1002@0"; + v.add(cInstStr, cClassStr); // C -->(class) --> C.class. + v.add(bInstStr, cInstStr); // B -->(field) --> C. + + A a4Inst = new A(); + setTag(a4Inst, 6); + cInst.baz = a4Inst; + String a4InstStr = "6@1000"; + v.add(a4InstStr, aClassStr); // A4 -->(class) --> A.class. + v.add(cInstStr, a4InstStr); // C -->(field) --> A4. + + cInst.baz2 = aInst; + v.add(cInstStr, aInstStr); // C -->(field) --> A. + + return aInst; } public static class A { @@ -202,6 +218,165 @@ public class Main { } } + public static class Verifier { + public static class Node { + public String referrer; + + public HashSet<String> referrees = new HashSet<>(); + + public Node(String r) { + referrer = r; + } + + public boolean isRoot() { + return referrer.startsWith("root@"); + } + } + + HashMap<String, Node> nodes = new HashMap<>(); + + public Verifier() { + } + + public void add(String referrer, String referree) { + if (!nodes.containsKey(referrer)) { + nodes.put(referrer, new Node(referrer)); + } + if (referree != null) { + nodes.get(referrer).referrees.add(referree); + } + } + + public void process(String[] lines, String additionalEnabledReferrer) { + // This method isn't optimal. The loops could be merged. However, it's more readable if + // the different parts are separated. + + ArrayList<String> rootLines = new ArrayList<>(); + ArrayList<String> nonRootLines = new ArrayList<>(); + + // Check for consecutive chunks of referrers. Also ensure roots come first. + { + String currentHead = null; + boolean rootsDone = false; + HashSet<String> completedReferrers = new HashSet<>(); + for (String l : lines) { + String referrer = getReferrer(l); + + if (isRoot(referrer)) { + if (rootsDone) { + System.out.println("ERROR: Late root " + l); + print(lines); + return; + } + rootLines.add(l); + continue; + } + + rootsDone = true; + + if (currentHead == null) { + currentHead = referrer; + } else { + if (!currentHead.equals(referrer)) { + completedReferrers.add(currentHead); + currentHead = referrer; + if (completedReferrers.contains(referrer)) { + System.out.println("Non-contiguous referrer " + l); + print(lines); + return; + } + } + } + nonRootLines.add(l); + } + } + + // Sort (root order is not specified) and print the roots. + // TODO: What about extra roots? JNI and the interpreter seem to introduce those (though it + // isn't clear why a debuggable-AoT test doesn't have the same, at least for locals). + // For now, swallow duplicates, and resolve once we have the metadata for the roots. + { + Collections.sort(rootLines); + String lastRoot = null; + for (String l : rootLines) { + if (lastRoot != null && lastRoot.equals(l)) { + continue; + } + lastRoot = l; + System.out.println(l); + } + } + + // Iterate through the lines, keeping track of which referrers are visited, to ensure the + // order is acceptable. + HashSet<String> enabled = new HashSet<>(); + if (additionalEnabledReferrer != null) { + enabled.add(additionalEnabledReferrer); + } + // Always add "0@0". + enabled.add("0@0"); + + for (String l : lines) { + String referrer = getReferrer(l); + String referree = getReferree(l); + if (isRoot(referrer)) { + // For a root src, just enable the referree. + enabled.add(referree); + } else { + // Check that the referrer is enabled (may be visited). + if (!enabled.contains(referrer)) { + System.out.println("Referrer " + referrer + " not enabled: " + l); + print(lines); + return; + } + enabled.add(referree); + } + } + + // Now just sort the non-root lines and output them + Collections.sort(nonRootLines); + for (String l : nonRootLines) { + System.out.println(l); + } + + System.out.println("---"); + } + + public static boolean isRoot(String ref) { + return ref.startsWith("root@"); + } + + private static String getReferrer(String line) { + int i = line.indexOf(" --"); + if (i <= 0) { + throw new IllegalArgumentException(line); + } + int j = line.indexOf(' '); + if (i != j) { + throw new IllegalArgumentException(line); + } + return line.substring(0, i); + } + + private static String getReferree(String line) { + int i = line.indexOf("--> "); + if (i <= 0) { + throw new IllegalArgumentException(line); + } + int j = line.indexOf(' ', i + 4); + if (j < 0) { + throw new IllegalArgumentException(line); + } + return line.substring(i + 4, j); + } + + private static void print(String[] lines) { + for (String l : lines) { + System.out.println(l); + } + } + } + private static native void setupGcCallback(); private static native void enableGcTracking(boolean enable); private static native int getGcStarts(); @@ -213,4 +388,6 @@ public class Main { private static native String[] followReferences(int heapFilter, Class<?> klassFilter, Object initialObject, int stopAfter, int followSet, Object jniRef); + + private static native void waitForJitCompilation(); } diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index 3d714c9dc2..aab9f50be7 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -843,6 +843,42 @@ public class Main { long l = (long) mh.invoke(); if (l != 0) fail(); + // boolean -> Boolean + mh = MethodHandles.lookup().findStatic(Boolean.class, "parseBoolean", + MethodType.methodType(boolean.class, String.class)); + Boolean z = (Boolean) mh.invoke("True"); + if (!z.booleanValue()) fail(); + + // boolean -> int + try { + int dummy = (int) mh.invoke("True"); + fail(); + } catch (WrongMethodTypeException e) {} + + // boolean -> Integer + try { + Integer dummy = (Integer) mh.invoke("True"); + fail(); + } catch (WrongMethodTypeException e) {} + + // Boolean -> boolean + mh = MethodHandles.lookup().findStatic(Boolean.class, "valueOf", + MethodType.methodType(Boolean.class, boolean.class)); + boolean w = (boolean) mh.invoke(false); + if (w) fail(); + + // Boolean -> int + try { + int dummy = (int) mh.invoke(false); + fail(); + } catch (WrongMethodTypeException e) {} + + // Boolean -> Integer + try { + Integer dummy = (Integer) mh.invoke("True"); + fail(); + } catch (WrongMethodTypeException e) {} + System.out.println("testPrimitiveReturnValueConversions done."); } diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk index a1678473e4..845545c428 100644 --- a/test/Android.arm_vixl.mk +++ b/test/Android.arm_vixl.mk @@ -17,101 +17,56 @@ # Known broken tests for the ARM VIXL backend. TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ 003-omnibus-opcodes \ - 004-NativeAllocations \ 004-ThreadStress \ - 012-math \ - 015-switch \ - 021-string2 \ 028-array-write \ - 036-finalizer \ 037-inherit \ 042-new-instance \ 044-proxy \ - 050-sync-test \ - 051-thread \ - 068-classloader \ - 074-gc-thrash \ - 079-phantom \ 080-oom-throw \ 082-inline-execute \ 083-compiler-regressions \ - 088-monitor-verification \ 096-array-copy-concurrent-gc \ 099-vmdebug \ 103-string-append \ - 106-exceptions2 \ - 107-int-math2 \ 114-ParallelGC \ - 120-hashcode \ - 121-modifiers \ 122-npe \ - 123-compiler-regressions-mt \ 123-inline-execute2 \ 129-ThreadGetId \ - 132-daemon-locks-shutdown \ 137-cfi \ - 138-duplicate-classes-check2 \ - 141-class-unload \ 144-static-field-sigquit \ 201-built-in-except-detail-messages \ 412-new-array \ - 417-optimizing-arith-div \ 422-type-conversion \ - 426-monitor \ - 428-optimizing-arith-rem \ - 436-rem-float \ 437-inline \ 439-npe \ 442-checker-constant-folding \ - 444-checker-nce \ - 445-checker-licm \ - 447-checker-inliner3 \ - 449-checker-bce \ 450-checker-types \ 458-checker-instruct-simplification \ 458-long-to-fpu \ - 485-checker-dce-switch \ 488-checker-inline-recursive-calls \ - 508-checker-disassembly \ 510-checker-try-catch \ 515-dce-dominator \ 520-equivalent-phi \ - 522-checker-regression-monitor-exit \ - 523-checker-can-throw-regression \ 525-checker-arrays-fields1 \ 525-checker-arrays-fields2 \ - 526-checker-caller-callee-regs \ 527-checker-array-access-split \ - 530-checker-loops1 \ 530-checker-loops2 \ 530-checker-lse \ 530-checker-lse2 \ 535-regression-const-val \ 536-checker-intrinsic-optimization \ 538-checker-embed-constants \ - 543-checker-dce-trycatch \ - 546-regression-simplify-catch \ 550-checker-multiply-accumulate \ 552-checker-primitive-typeprop \ 552-checker-sharpening \ 555-UnsafeGetLong-regression \ - 558-switch \ - 560-packed-switch \ - 561-divrem \ 562-checker-no-intermediate \ 564-checker-negbitwise \ 570-checker-osr \ 570-checker-select \ - 573-checker-checkcast-regression \ 574-irreducible-and-constant-area \ - 575-checker-string-init-alias \ 580-checker-round \ - 584-checker-div-bool \ - 588-checker-irreducib-lifetime-hole \ 594-checker-array-alias \ - 597-deopt-new-string \ 602-deoptimizeable \ 700-LoadArgRegs \ - 701-easy-div-rem \ - 702-LargeBranchOffset \ 800-smali \ diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 29cec9111b..60318a41d6 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -230,40 +230,6 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES)) -# b/31385354: Roots (and thus iteration order) is non-stable between different run modes. -# Temporarily disable test for everything but default optimizing configuration -# until the test check code is generalized to allow spec-compliant output. -TEST_ART_BROKEN_B31385354_TESTS := \ - 913-heaps \ - -NON_AOT_MODES := $(filter-out optimizing,$(COMPILER_TYPES)) -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(NON_AOT_MODES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_B31385354_TESTS), \ - $(ALL_ADDRESS_SIZES)) -NON_AOT_MODES := - -NON_PREBUILD_MODES := $(filter-out prebuild,$(PREBUILD_TYPES)) -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(NON_PREBUILD_MODES), \ - $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_B31385354_TESTS), \ - $(ALL_ADDRESS_SIZES)) -NON_PREBUILD_MODES := - -NON_RELOCATE_MODES := $(filter-out relocate,$(RELOCATE_TYPES)) -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES), $(NON_RELOCATE_MODES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_B31385354_TESTS), \ - $(ALL_ADDRESS_SIZES)) -NON_RELOCATE_MODES := - -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES), $(RELOCATE_TYPES),trace,$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_B31385354_TESTS), \ - $(ALL_ADDRESS_SIZES)) - -TEST_ART_BROKEN_B31385354_TESTS := - # Disable 149-suspend-all-stress, its output is flaky (b/28988206). # Disable 577-profile-foreign-dex (b/27454772). @@ -537,8 +503,10 @@ TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := # also uses Generic JNI instead of the JNI compiler. # Test 906 iterates the heap filtering with different options. No instances should be created # between those runs to be able to have precise checks. +# Test 902 hits races with the JIT compiler. b/32821077 TEST_ART_BROKEN_JIT_RUN_TESTS := \ 137-cfi \ + 902-hello-transformation \ 904-object-allocation \ 906-iterate-heap \ diff --git a/tools/cpp-define-generator/constant_thread.def b/tools/cpp-define-generator/constant_thread.def index af5ca21f17..1364b558ec 100644 --- a/tools/cpp-define-generator/constant_thread.def +++ b/tools/cpp-define-generator/constant_thread.def @@ -25,5 +25,7 @@ DEFINE_THREAD_CONSTANT(SUSPEND_REQUEST, int32_t, art::kSuspendRequest) DEFINE_THREAD_CONSTANT(CHECKPOINT_REQUEST, int32_t, art::kCheckpointRequest) +DEFINE_THREAD_CONSTANT(EMPTY_CHECKPOINT_REQUEST, int32_t, art::kEmptyCheckpointRequest) +DEFINE_THREAD_CONSTANT(SUSPEND_OR_CHECKPOINT_REQUEST, int32_t, art::kSuspendRequest | art::kCheckpointRequest | art::kEmptyCheckpointRequest) #undef DEFINE_THREAD_CONSTANT |