diff options
37 files changed, 433 insertions, 228 deletions
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index ac0f4ca21d..d3859ca752 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -24,7 +24,6 @@ #include "debug/dwarf/headers.h" #include "debug/elf_compilation_unit.h" #include "dex_file-inl.h" -#include "dex_file.h" #include "elf_builder.h" #include "stack_map.h" @@ -90,8 +89,9 @@ class ElfDebugLineWriter { continue; } - ArrayRef<const SrcMapElem> src_mapping_table; - std::vector<SrcMapElem> src_mapping_table_from_stack_maps; + uint32_t prologue_end = std::numeric_limits<uint32_t>::max(); + ArrayRef<const SrcMapElem> pc2dex_map; + std::vector<SrcMapElem> pc2dex_map_from_stack_maps; if (mi->IsFromOptimizingCompiler()) { // Use stack maps to create mapping table from pc to dex. const CodeInfo code_info(mi->compiled_method->GetVmapTable().data()); @@ -99,35 +99,36 @@ class ElfDebugLineWriter { for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { StackMap stack_map = code_info.GetStackMapAt(s, encoding); DCHECK(stack_map.IsValid()); - // Emit only locations where we have local-variable information. - // In particular, skip mappings inside the prologue. + const uint32_t pc = stack_map.GetNativePcOffset(encoding); + const int32_t dex = stack_map.GetDexPc(encoding); + pc2dex_map_from_stack_maps.push_back({pc, dex}); if (stack_map.HasDexRegisterMap(encoding)) { - const uint32_t pc = stack_map.GetNativePcOffset(encoding); - const int32_t dex = stack_map.GetDexPc(encoding); - src_mapping_table_from_stack_maps.push_back({pc, dex}); + // Guess that the first map with local variables is the end of prologue. + prologue_end = std::min(prologue_end, pc); } } - std::sort(src_mapping_table_from_stack_maps.begin(), - src_mapping_table_from_stack_maps.end()); - src_mapping_table = ArrayRef<const SrcMapElem>(src_mapping_table_from_stack_maps); + std::sort(pc2dex_map_from_stack_maps.begin(), + pc2dex_map_from_stack_maps.end()); + pc2dex_map = ArrayRef<const SrcMapElem>(pc2dex_map_from_stack_maps); } else { // Use the mapping table provided by the quick compiler. - src_mapping_table = mi->compiled_method->GetSrcMappingTable(); + pc2dex_map = mi->compiled_method->GetSrcMappingTable(); + prologue_end = 0; } - if (src_mapping_table.empty()) { + if (pc2dex_map.empty()) { continue; } Elf_Addr method_address = text_address + mi->low_pc; - PositionInfos position_infos; + PositionInfos dex2line_map; const DexFile* dex = mi->dex_file; - if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &position_infos)) { + if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) { continue; } - if (position_infos.empty()) { + if (dex2line_map.empty()) { continue; } @@ -184,21 +185,25 @@ class ElfDebugLineWriter { // Generate mapping opcodes from PC to Java lines. if (file_index != 0) { bool first = true; - for (SrcMapElem pc2dex : src_mapping_table) { + for (SrcMapElem pc2dex : pc2dex_map) { uint32_t pc = pc2dex.from_; int dex_pc = pc2dex.to_; // Find mapping with address with is greater than our dex pc; then go back one step. - auto ub = std::upper_bound(position_infos.begin(), position_infos.end(), dex_pc, + auto dex2line = std::upper_bound( + dex2line_map.begin(), + dex2line_map.end(), + dex_pc, [](uint32_t address, const DexFile::PositionInfo& entry) { return address < entry.address_; }); - if (ub != position_infos.begin()) { - int line = (--ub)->line_; + // Look for first valid mapping after the prologue. + if (dex2line != dex2line_map.begin() && pc >= prologue_end) { + int line = (--dex2line)->line_; if (first) { first = false; if (pc > 0) { // Assume that any preceding code is prologue. - int first_line = position_infos.front().line_; + int first_line = dex2line_map.front().line_; // Prologue is not a sensible place for a breakpoint. opcodes.NegateStmt(); opcodes.AddRow(method_address, first_line); diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index fd7f949c5d..a19b36f9cc 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -82,6 +82,8 @@ struct VariableLocation { // Get the location of given dex register (e.g. stack or machine register). // Note that the location might be different based on the current pc. // The result will cover all ranges where the variable is in scope. +// PCs corresponding to stackmap with dex register map are accurate, +// all other PCs are best-effort only. std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info, uint16_t vreg, bool is64bitValue, @@ -141,6 +143,9 @@ std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method variable_locations.back().high_pc == low_pc) { // Merge with the previous entry (extend its range). variable_locations.back().high_pc = high_pc; + } else if (!variable_locations.empty() && reg_lo == DexRegisterLocation::None()) { + // Unknown location - use the last known location as best-effort guess. + variable_locations.back().high_pc = high_pc; } else { variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi}); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 07edd97c1f..3c880c2aaa 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1276,7 +1276,7 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, } // Must be equal high, so compare the lows. codegen_->Compare32BitValue(left_low, val_low); - } else { + } else if (right.IsRegisterPair()) { Register right_high = right.AsRegisterPairHigh<Register>(); Register right_low = right.AsRegisterPairLow<Register>(); @@ -1291,6 +1291,19 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, } // Must be equal high, so compare the lows. __ cmpl(left_low, right_low); + } else { + DCHECK(right.IsDoubleStackSlot()); + __ cmpl(left_high, Address(ESP, right.GetHighStackIndex(kX86WordSize))); + if (if_cond == kCondNE) { + __ j(X86Condition(true_high_cond), true_label); + } else if (if_cond == kCondEQ) { + __ j(X86Condition(false_high_cond), false_label); + } else { + __ j(X86Condition(true_high_cond), true_label); + __ j(X86Condition(false_high_cond), false_label); + } + // Must be equal high, so compare the lows. + __ cmpl(left_low, Address(ESP, right.GetStackIndex())); } // The last comparison might be unsigned. __ j(final_condition, true_label); @@ -1593,7 +1606,7 @@ void LocationsBuilderX86::HandleCondition(HCondition* cond) { switch (cond->InputAt(0)->GetType()) { case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); + locations->SetInAt(1, Location::Any()); if (!cond->IsEmittedAtUseSite()) { locations->SetOut(Location::RequiresRegister()); } diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index ab4f6f9d28..22cefe8aa5 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -261,7 +261,8 @@ static void CreateFloatToFloat(ArenaAllocator* arena, HInvoke* invoke) { locations->SetOut(Location::SameAsFirstInput()); HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); DCHECK(static_or_direct != nullptr); - if (invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { + if (static_or_direct->HasSpecialInput() && + invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { // We need addressibility for the constant area. locations->SetInAt(1, Location::RequiresRegister()); // We need a temporary to hold the constant. @@ -276,7 +277,7 @@ static void MathAbsFP(LocationSummary* locations, Location output = locations->Out(); DCHECK(output.IsFpuRegister()); - if (locations->InAt(1).IsValid()) { + if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) { DCHECK(locations->InAt(1).IsRegister()); // We also have a constant area pointer. Register constant_area = locations->InAt(1).AsRegister<Register>(); @@ -465,7 +466,7 @@ static void GenMinMaxFP(LocationSummary* locations, // NaN handling. __ Bind(&nan); // Do we have a constant area pointer? - if (locations->InAt(2).IsValid()) { + if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) { DCHECK(locations->InAt(2).IsRegister()); Register constant_area = locations->InAt(2).AsRegister<Register>(); if (is_double) { @@ -510,7 +511,8 @@ static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { locations->SetOut(Location::SameAsFirstInput()); HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); DCHECK(static_or_direct != nullptr); - if (invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { + if (static_or_direct->HasSpecialInput() && + invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { locations->SetInAt(2, Location::RequiresRegister()); } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 552d17597d..854854f238 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -3672,6 +3672,7 @@ class HInvokeStaticOrDirect : public HInvoke { // method pointer; otherwise there may be one platform-specific special input, // such as PC-relative addressing base. uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); } + bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); } InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; } void SetOptimizedInvokeType(InvokeType invoke_type) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0631ebe374..5278d1bb05 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2620,19 +2620,37 @@ const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, return oat_class.GetOatMethod(oat_method_idx).GetQuickCode(); } -// Returns true if the method must run with interpreter, false otherwise. -static bool NeedsInterpreter(ArtMethod* method, const void* quick_code) - SHARED_REQUIRES(Locks::mutator_lock_) { +bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) { + if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) { + return false; + } + if (quick_code == nullptr) { - // No code: need interpreter. - // May return true for native code, in the case of generic JNI - // DCHECK(!method->IsNative()); return true; } - // If interpreter mode is enabled, every method (except native and proxy) must - // be run with interpreter. - return Runtime::Current()->GetInstrumentation()->InterpretOnly() && - !method->IsNative() && !method->IsProxyMethod(); + + Runtime* runtime = Runtime::Current(); + instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); + if (instr->InterpretOnly()) { + return true; + } + + if (runtime->GetClassLinker()->IsQuickToInterpreterBridge(quick_code)) { + // Doing this check avoids doing compiled/interpreter transitions. + return true; + } + + if (Dbg::IsForcedInterpreterNeededForCalling(Thread::Current(), method)) { + // Force the use of interpreter when it is required by the debugger. + return true; + } + + if (runtime->UseJit() && runtime->GetJit()->JitAtFirstUse()) { + // The force JIT uses the interpreter entry point to execute the JIT. + return true; + } + + return false; } void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { @@ -2677,15 +2695,12 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index); quick_code = oat_method.GetQuickCode(); } - const bool enter_interpreter = NeedsInterpreter(method, quick_code); - if (enter_interpreter) { + // Check whether the method is native, in which case it's generic JNI. + if (quick_code == nullptr && method->IsNative()) { + quick_code = GetQuickGenericJniStub(); + } else if (ShouldUseInterpreterEntrypoint(method, quick_code)) { // Use interpreter entry point. - // Check whether the method is native, in which case it's generic JNI. - if (quick_code == nullptr && method->IsNative()) { - quick_code = GetQuickGenericJniStub(); - } else { - quick_code = GetQuickToInterpreterBridge(); - } + quick_code = GetQuickToInterpreterBridge(); } runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code); } @@ -2716,7 +2731,8 @@ void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class } // Install entry point from interpreter. - bool enter_interpreter = NeedsInterpreter(method, method->GetEntryPointFromQuickCompiledCode()); + const void* quick_code = method->GetEntryPointFromQuickCompiledCode(); + bool enter_interpreter = ShouldUseInterpreterEntrypoint(method, quick_code); if (!method->IsInvokable()) { EnsureThrowsInvocationError(method); @@ -2728,20 +2744,18 @@ void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines // after initializing class (see ClassLinker::InitializeClass method). method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); + } else if (quick_code == nullptr && method->IsNative()) { + method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); } else if (enter_interpreter) { - if (!method->IsNative()) { - // Set entry point from compiled code if there's no code or in interpreter only mode. - method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); - } else { - method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); - } + // Set entry point from compiled code if there's no code or in interpreter only mode. + method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); } if (method->IsNative()) { // Unregistering restores the dlsym lookup stub. method->UnregisterNative(); - if (enter_interpreter) { + if (enter_interpreter || quick_code == nullptr) { // We have a native method here without code. Then it should have either the generic JNI // trampoline as entrypoint (non-static), or the resolution trampoline (static). // TODO: this doesn't handle all the cases where trampolines may be installed. diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 56a868a4d0..a9448f732c 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -592,6 +592,9 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + static bool ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) + SHARED_REQUIRES(Locks::mutator_lock_); + struct DexCacheData { // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may // not work properly. diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 0b2471b4c0..4fd3c78f44 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -27,6 +27,7 @@ #include "unstarted_runtime.h" #include "mterp/mterp.h" #include "jit/jit.h" +#include "jit/jit_code_cache.h" namespace art { namespace interpreter { @@ -293,9 +294,10 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, method, 0); } - if (UNLIKELY(Runtime::Current()->GetJit() != nullptr && - Runtime::Current()->GetJit()->JitAtFirstUse() && - method->HasAnyCompiledCode())) { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (UNLIKELY(jit != nullptr && + jit->JitAtFirstUse() && + jit->GetCodeCache()->ContainsMethod(method))) { JValue result; // Pop the shadow frame before calling into compiled code. diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 09d860140f..cbaa8173d2 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -20,6 +20,7 @@ #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" +#include "jit/jit.h" #include "mirror/array-inl.h" #include "stack.h" #include "unstarted_runtime.h" @@ -501,23 +502,6 @@ static inline bool DoCallCommon(ArtMethod* called_method, uint32_t (&arg)[kVarArgMax], uint32_t vregC) ALWAYS_INLINE; -SHARED_REQUIRES(Locks::mutator_lock_) -static inline bool NeedsInterpreter(Thread* self, ShadowFrame* new_shadow_frame) ALWAYS_INLINE; - -static inline bool NeedsInterpreter(Thread* self, ShadowFrame* new_shadow_frame) { - ArtMethod* target = new_shadow_frame->GetMethod(); - if (UNLIKELY(target->IsNative() || target->IsProxyMethod())) { - return false; - } - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - return runtime->GetInstrumentation()->IsForcedInterpretOnly() || - // Doing this check avoids doing compiled/interpreter transitions. - class_linker->IsQuickToInterpreterBridge(target->GetEntryPointFromQuickCompiledCode()) || - // Force the use of interpreter when it is required by the debugger. - Dbg::IsForcedInterpreterNeededForCalling(self, target); -} - void ArtInterpreterToCompiledCodeBridge(Thread* self, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, @@ -736,7 +720,10 @@ static inline bool DoCallCommon(ArtMethod* called_method, // Do the call now. if (LIKELY(Runtime::Current()->IsStarted())) { - if (NeedsInterpreter(self, new_shadow_frame)) { + ArtMethod* target = new_shadow_frame->GetMethod(); + if (ClassLinker::ShouldUseInterpreterEntrypoint( + target, + target->GetEntryPointFromQuickCompiledCode())) { ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result); } else { ArtInterpreterToCompiledCodeBridge(self, code_item, new_shadow_frame, result); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 31c278e748..188deb07a5 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -36,7 +36,7 @@ namespace art { namespace jit { -static constexpr bool kEnableOnStackReplacement = false; +static constexpr bool kEnableOnStackReplacement = true; JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) { auto* jit_options = new JitOptions; @@ -290,7 +290,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, } if (kRuntimeISA == kMips || kRuntimeISA == kMips64) { - VLOG(jit) << "OSR not supported on this platform"; + VLOG(jit) << "OSR not supported on this platform: " << kRuntimeISA; return false; } @@ -304,79 +304,93 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, return false; } - const OatQuickMethodHeader* osr_method = jit->GetCodeCache()->LookupOsrMethodHeader(method); - if (osr_method == nullptr) { - // No osr method yet, just return to the interpreter. - return false; - } - + // Fetch some data before looking up for an OSR method. We don't want thread + // suspension once we hold an OSR method, as the JIT code cache could delete the OSR + // method while we are being suspended. const size_t number_of_vregs = method->GetCodeItem()->registers_size_; - CodeInfo code_info = osr_method->GetOptimizedCodeInfo(); - StackMapEncoding encoding = code_info.ExtractEncoding(); - - // Find stack map starting at the target dex_pc. - StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset, encoding); - if (!stack_map.IsValid()) { - // There is no OSR stack map for this dex pc offset. Just return to the interpreter in the - // hope that the next branch has one. - return false; - } + const char* shorty = method->GetShorty(); + std::string method_name(VLOG_IS_ON(jit) ? PrettyMethod(method) : ""); + void** memory = nullptr; + size_t frame_size = 0; + ShadowFrame* shadow_frame = nullptr; + const uint8_t* native_pc = nullptr; - // We found a stack map, now fill the frame with dex register values from the interpreter's - // shadow frame. - DexRegisterMap vreg_map = - code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs); - - ShadowFrame* shadow_frame = thread->PopShadowFrame(); - - size_t frame_size = osr_method->GetFrameSizeInBytes(); - - // Allocate memory to put shadow frame values. The osr stub will copy that memory to - // stack. - // Note that we could pass the shadow frame to the stub, and let it copy the values there, - // but that is engineering complexity not worth the effort for something like OSR. - void** memory = reinterpret_cast<void**>(malloc(frame_size)); - CHECK(memory != nullptr); - memset(memory, 0, frame_size); - - // Art ABI: ArtMethod is at the bottom of the stack. - memory[0] = method; - - if (!vreg_map.IsValid()) { - // If we don't have a dex register map, then there are no live dex registers at - // this dex pc. - } else { - for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { - DexRegisterLocation::Kind location = - vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); - if (location == DexRegisterLocation::Kind::kNone) { - // Dex register is dead or unitialized. - continue; - } + { + ScopedAssertNoThreadSuspension sts(thread, "Holding OSR method"); + const OatQuickMethodHeader* osr_method = jit->GetCodeCache()->LookupOsrMethodHeader(method); + if (osr_method == nullptr) { + // No osr method yet, just return to the interpreter. + return false; + } - if (location == DexRegisterLocation::Kind::kConstant) { - // We skip constants because the compiled code knows how to handle them. - continue; - } + CodeInfo code_info = osr_method->GetOptimizedCodeInfo(); + StackMapEncoding encoding = code_info.ExtractEncoding(); - DCHECK(location == DexRegisterLocation::Kind::kInStack); + // Find stack map starting at the target dex_pc. + StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset, encoding); + if (!stack_map.IsValid()) { + // There is no OSR stack map for this dex pc offset. Just return to the interpreter in the + // hope that the next branch has one. + return false; + } - int32_t vreg_value = shadow_frame->GetVReg(vreg); - int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, - number_of_vregs, - code_info, - encoding); - DCHECK_LT(slot_offset, static_cast<int32_t>(frame_size)); - DCHECK_GT(slot_offset, 0); - (reinterpret_cast<int32_t*>(memory))[slot_offset / sizeof(int32_t)] = vreg_value; + // We found a stack map, now fill the frame with dex register values from the interpreter's + // shadow frame. + DexRegisterMap vreg_map = + code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs); + + frame_size = osr_method->GetFrameSizeInBytes(); + + // Allocate memory to put shadow frame values. The osr stub will copy that memory to + // stack. + // Note that we could pass the shadow frame to the stub, and let it copy the values there, + // but that is engineering complexity not worth the effort for something like OSR. + memory = reinterpret_cast<void**>(malloc(frame_size)); + CHECK(memory != nullptr); + memset(memory, 0, frame_size); + + // Art ABI: ArtMethod is at the bottom of the stack. + memory[0] = method; + + shadow_frame = thread->PopShadowFrame(); + if (!vreg_map.IsValid()) { + // If we don't have a dex register map, then there are no live dex registers at + // this dex pc. + } else { + for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { + DexRegisterLocation::Kind location = + vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); + if (location == DexRegisterLocation::Kind::kNone) { + // Dex register is dead or uninitialized. + continue; + } + + if (location == DexRegisterLocation::Kind::kConstant) { + // We skip constants because the compiled code knows how to handle them. + continue; + } + + DCHECK(location == DexRegisterLocation::Kind::kInStack) + << DexRegisterLocation::PrettyDescriptor(location); + + int32_t vreg_value = shadow_frame->GetVReg(vreg); + int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, + number_of_vregs, + code_info, + encoding); + DCHECK_LT(slot_offset, static_cast<int32_t>(frame_size)); + DCHECK_GT(slot_offset, 0); + (reinterpret_cast<int32_t*>(memory))[slot_offset / sizeof(int32_t)] = vreg_value; + } } + + native_pc = stack_map.GetNativePcOffset(encoding) + osr_method->GetEntryPoint(); + VLOG(jit) << "Jumping to " + << method_name + << "@" + << std::hex << reinterpret_cast<uintptr_t>(native_pc); } - const uint8_t* native_pc = stack_map.GetNativePcOffset(encoding) + osr_method->GetEntryPoint(); - VLOG(jit) << "Jumping to " - << PrettyMethod(method) - << "@" - << std::hex << reinterpret_cast<uintptr_t>(native_pc); { ManagedStack fragment; thread->PushManagedStackFragment(&fragment); @@ -384,8 +398,9 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, frame_size, native_pc, result, - method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(), + shorty, thread); + if (UNLIKELY(thread->GetException() == Thread::GetDeoptimizationException())) { thread->DeoptimizeWithDeoptimizationException(result); } @@ -393,7 +408,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, } free(memory); thread->PushShadowFrame(shadow_frame); - VLOG(jit) << "Done running OSR code for " << PrettyMethod(method); + VLOG(jit) << "Done running OSR code for " << method_name; return true; } diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index a7881ac52f..a092b9f248 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -66,6 +66,7 @@ static void EnableDebugFeatures(uint32_t debug_flags) { DEBUG_ENABLE_JNI_LOGGING = 1 << 4, DEBUG_GENERATE_DEBUG_INFO = 1 << 5, DEBUG_ALWAYS_JIT = 1 << 6, + DEBUG_NATIVE_DEBUGGABLE = 1 << 7, }; Runtime* const runtime = Runtime::Current(); @@ -117,6 +118,11 @@ static void EnableDebugFeatures(uint32_t debug_flags) { debug_flags &= ~DEBUG_ALWAYS_JIT; } + if ((debug_flags & DEBUG_NATIVE_DEBUGGABLE) != 0) { + runtime->AddCompilerOption("--native-debuggable"); + debug_flags &= ~DEBUG_NATIVE_DEBUGGABLE; + } + if (debug_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags); } diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build index faa298337c..56e87844c0 100644 --- a/test/003-omnibus-opcodes/build +++ b/test/003-omnibus-opcodes/build @@ -23,8 +23,8 @@ rm classes/UnresClass.class ${JAVAC} -d classes `find src2 -name '*.java'` if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/005-annotations/build b/test/005-annotations/build index 057b351dab..93bee507df 100644 --- a/test/005-annotations/build +++ b/test/005-annotations/build @@ -29,8 +29,8 @@ rm 'classes/android/test/anno/MissingAnnotation.class' rm 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class' if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/022-interface/build b/test/022-interface/build index 3f8915c27e..5cfc7f25b7 100644 --- a/test/022-interface/build +++ b/test/022-interface/build @@ -20,8 +20,8 @@ set -e # Use classes that are compiled with ecj that exposes an invokeinterface # issue when interfaces override methods in Object if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build index 6f50a76863..21dc66269d 100644 --- a/test/085-old-style-inner-class/build +++ b/test/085-old-style-inner-class/build @@ -23,8 +23,8 @@ mkdir classes ${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'` if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else # Suppress stderr to keep the inner class warnings out of the expected output. ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes 2>/dev/null diff --git a/test/091-override-package-private-method/build b/test/091-override-package-private-method/build index 5a340dcf6d..073a4ba9bc 100755 --- a/test/091-override-package-private-method/build +++ b/test/091-override-package-private-method/build @@ -24,14 +24,12 @@ mkdir classes-ex mv classes/OverridePackagePrivateMethodSuper.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/097-duplicate-method/build b/test/097-duplicate-method/build index a8558739de..4525549905 100644 --- a/test/097-duplicate-method/build +++ b/test/097-duplicate-method/build @@ -23,10 +23,10 @@ if [ ${USE_JACK} = "true" ]; then ${JACK} --output-jack src.jack src ${JASMIN} -d classes src/*.j - ${JILL} classes --output jasmin.jack + jar cf jasmin.jill.jar -C classes . # We set jack.import.type.policy=keep-first to consider class definitions from jasmin first. - ${JACK} --import jasmin.jack --import src.jack -D jack.import.type.policy=keep-first --output-dex . + ${JACK} --import jasmin.jill.jar --import src.jack -D jack.import.type.policy=keep-first --output-dex . else ${JAVAC} -d classes src/*.java ${JASMIN} -d classes src/*.j diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build index e772fb812f..58ac26d836 100644 --- a/test/111-unresolvable-exception/build +++ b/test/111-unresolvable-exception/build @@ -22,8 +22,8 @@ ${JAVAC} -d classes `find src -name '*.java'` rm classes/TestException.class if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/113-multidex/build b/test/113-multidex/build index 8ef5c0eb0f..4557ccd22a 100644 --- a/test/113-multidex/build +++ b/test/113-multidex/build @@ -28,14 +28,12 @@ ${JAVAC} -d classes2 `find src -name '*.java'` rm classes2/Second.class classes2/FillerA.class classes2/FillerB.class classes2/Inf*.class if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes2 --output classes2.jack + jar cf classes.jill.jar -C classes . + jar cf classes2.jill.jar -C classes2 . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . mv classes.dex classes-1.dex - ${JACK} --import classes2.jack --output-dex . + ${JACK} --import classes2.jill.jar --output-dex . mv classes.dex classes2.dex mv classes-1.dex classes.dex else diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt index b003307ab7..852ec2e5e9 100644 --- a/test/115-native-bridge/expected.txt +++ b/test/115-native-bridge/expected.txt @@ -1,4 +1,3 @@ -Code cache exists: './code_cache'. Native bridge initialized. Checking for getEnvValues. Ready for native bridge tests. diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index b70ca4ff3e..aca356b5b9 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -269,16 +269,12 @@ extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallb struct stat st; if (app_code_cache_dir != nullptr) { if (stat(app_code_cache_dir, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - printf("Code cache exists: '%s'.\n", app_code_cache_dir); - } else { + if (!S_ISDIR(st.st_mode)) { printf("Code cache is not a directory.\n"); } } else { perror("Error when stat-ing the code_cache:"); } - } else { - printf("app_code_cache_dir is null.\n"); } if (art_cbs != nullptr) { diff --git a/test/121-modifiers/build b/test/121-modifiers/build index 85b69e92a6..771dd51829 100644 --- a/test/121-modifiers/build +++ b/test/121-modifiers/build @@ -31,9 +31,9 @@ set -e # mv Main.class A.class A\$B.class A\$C.class classes/ if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack + jar cf classes.jill.jar -C classes . # Workaround b/19561685: disable sanity checks to produce a DEX file with invalid modifiers. - ${JACK} --sanity-checks off --import classes.jack --output-dex . + ${JACK} --sanity-checks off --import classes.jill.jar --output-dex . else ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build index b92ecf9382..0a340a26d6 100644 --- a/test/124-missing-classes/build +++ b/test/124-missing-classes/build @@ -27,8 +27,8 @@ rm 'classes/MissingClass.class' rm 'classes/Main$MissingInnerClass.class' if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build index b7f2118d2f..00b9ba0ac2 100644 --- a/test/126-miranda-multidex/build +++ b/test/126-miranda-multidex/build @@ -28,14 +28,12 @@ ${JAVAC} -d classes2 `find src -name '*.java'` rm classes2/Main.class classes2/MirandaAbstract.class classes2/MirandaClass*.class classes2/MirandaInterface2*.class if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes2 --output classes2.jack + jar cf classes.jill.jar -C classes . + jar cf classes2.jill.jar -C classes2 . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . mv classes.dex classes-1.dex - ${JACK} --import classes2.jack --output-dex . + ${JACK} --import classes2.jill.jar --output-dex . mv classes.dex classes2.dex mv classes-1.dex classes.dex else diff --git a/test/127-checker-secondarydex/build b/test/127-checker-secondarydex/build index 0d9f4d6291..7ce46acfed 100755 --- a/test/127-checker-secondarydex/build +++ b/test/127-checker-secondarydex/build @@ -24,14 +24,12 @@ mkdir classes-ex mv classes/Super.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt index 8db7853696..6a5618ebc6 100644 --- a/test/137-cfi/expected.txt +++ b/test/137-cfi/expected.txt @@ -1,2 +1 @@ JNI_OnLoad called -JNI_OnLoad called diff --git a/test/137-cfi/run b/test/137-cfi/run index 6f4bcfe658..8ec98c11dc 100755 --- a/test/137-cfi/run +++ b/test/137-cfi/run @@ -20,4 +20,5 @@ ${RUN} "$@" -Xcompiler-option --generate-debug-info --args --full-signatures # Test with minimal compressed debugging information. # Check only method names (parameters are omitted to save space). -${RUN} "$@" -Xcompiler-option --generate-mini-debug-info +# Temporarily disable due to bug 27172087 (leak/race in libunwind). +# ${RUN} "$@" -Xcompiler-option --generate-mini-debug-info diff --git a/test/529-checker-unresolved/build b/test/529-checker-unresolved/build index 8c3c4f8952..d85035b669 100644 --- a/test/529-checker-unresolved/build +++ b/test/529-checker-unresolved/build @@ -29,14 +29,12 @@ mv classes/UnresolvedInterface.class classes-ex mv classes/UnresolvedSuperClass.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc index c1774f3d02..4c58b39319 100644 --- a/test/570-checker-osr/osr.cc +++ b/test/570-checker-osr/osr.cc @@ -17,6 +17,7 @@ #include "art_method.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jit/profiling_info.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change.h" #include "stack_map.h" @@ -28,7 +29,8 @@ class OsrVisitor : public StackVisitor { explicit OsrVisitor(Thread* thread) SHARED_REQUIRES(Locks::mutator_lock_) : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), - in_osr_method_(false) {} + in_osr_method_(false), + in_interpreter_(false) {} bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) { ArtMethod* m = GetMethod(); @@ -44,6 +46,8 @@ class OsrVisitor : public StackVisitor { Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m); if (header != nullptr && header == GetCurrentOatQuickMethodHeader()) { in_osr_method_ = true; + } else if (IsCurrentFrameInInterpreter()) { + in_interpreter_ = true; } return false; } @@ -51,6 +55,7 @@ class OsrVisitor : public StackVisitor { } bool in_osr_method_; + bool in_interpreter_; }; extern "C" JNIEXPORT jboolean JNICALL Java_Main_ensureInOsrCode(JNIEnv*, jclass) { @@ -65,4 +70,75 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_ensureInOsrCode(JNIEnv*, jclass) return visitor.in_osr_method_; } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_ensureInInterpreter(JNIEnv*, jclass) { + if (!Runtime::Current()->UseJit()) { + // The return value is irrelevant if we're not using JIT. + return false; + } + ScopedObjectAccess soa(Thread::Current()); + OsrVisitor visitor(soa.Self()); + visitor.WalkStack(); + return visitor.in_interpreter_; +} + +class ProfilingInfoVisitor : public StackVisitor { + public: + explicit ProfilingInfoVisitor(Thread* thread) + SHARED_REQUIRES(Locks::mutator_lock_) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {} + + bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); + + if (m_name.compare("$noinline$inlineCache") == 0) { + ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true); + return false; + } + return true; + } +}; + +extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasProfilingInfo(JNIEnv*, jclass) { + if (!Runtime::Current()->UseJit()) { + return; + } + ScopedObjectAccess soa(Thread::Current()); + ProfilingInfoVisitor visitor(soa.Self()); + visitor.WalkStack(); +} + +class OsrCheckVisitor : public StackVisitor { + public: + explicit OsrCheckVisitor(Thread* thread) + SHARED_REQUIRES(Locks::mutator_lock_) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {} + + bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); + + jit::Jit* jit = Runtime::Current()->GetJit(); + if (m_name.compare("$noinline$inlineCache") == 0 && jit != nullptr) { + while (jit->GetCodeCache()->LookupOsrMethodHeader(m) == nullptr) { + // Sleep to yield to the compiler thread. + sleep(0); + // Will either ensure it's compiled or do the compilation itself. + jit->CompileMethod(m, Thread::Current(), /* osr */ true); + } + return false; + } + return true; + } +}; + +extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv*, jclass) { + if (!Runtime::Current()->UseJit()) { + return; + } + ScopedObjectAccess soa(Thread::Current()); + OsrCheckVisitor visitor(soa.Self()); + visitor.WalkStack(); +} + } // namespace art diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java index bc6612f337..828908a582 100644 --- a/test/570-checker-osr/src/Main.java +++ b/test/570-checker-osr/src/Main.java @@ -36,8 +36,8 @@ public class Main { } catch (Exception e) {} DeoptimizationController.stopDeoptimization(); - $noinline$inlineCache(new Main(), 0); - if ($noinline$inlineCache(new SubMain(), 1) != SubMain.class) { + $noinline$inlineCache(new Main(), /* isSecondInvocation */ false); + if ($noinline$inlineCache(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) { throw new Error("Unexpected return value"); } } @@ -91,29 +91,29 @@ public class Main { DeoptimizationController.startDeoptimization(); } - public static Class $noinline$inlineCache(Main m, int count) { - for (int i = 0; i < 500; ++i) { - // Warm me up. + public static Class $noinline$inlineCache(Main m, boolean isSecondInvocation) { + // If we are running in non-JIT mode, or were unlucky enough to get this method + // already JITted, just return the expected value. + if (!ensureInInterpreter()) { + return SubMain.class; } - if (count == 1) { - // Lots of back edges to trigger OSR compilation. - for (int i = 0; i < 1000; ++i) { - } - // Best effort to wait for OSR compilation. - try { - Thread.sleep(1); - } catch (Exception e) {} + + ensureHasProfilingInfo(); + + // Ensure that we have OSR code to jump to. + if (isSecondInvocation) { + ensureHasOsrCode(); } // This call will be optimized in the OSR compiled code // to check and deoptimize if m is not of type 'Main'. Main other = m.inlineCache(); - if (count == 1) { - // Jump to OSR compiled code. The second run - // of this method will have 'm' as a SubMain, and the compiled - // code we are jumping to will have wrongly optimize other as being a - // 'Main'. + // Jump to OSR compiled code. The second run + // of this method will have 'm' as a SubMain, and the compiled + // code we are jumping to will have wrongly optimize other as being a + // 'Main'. + if (isSecondInvocation) { while (!ensureInOsrCode()) {} } @@ -131,7 +131,10 @@ public class Main { public static int[] array = new int[4]; + public static native boolean ensureInInterpreter(); public static native boolean ensureInOsrCode(); + public static native void ensureHasProfilingInfo(); + public static native void ensureHasOsrCode(); public static boolean doThrow = false; } diff --git a/test/574-irreducible-and-constant-area/expected.txt b/test/574-irreducible-and-constant-area/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/574-irreducible-and-constant-area/expected.txt diff --git a/test/574-irreducible-and-constant-area/info.txt b/test/574-irreducible-and-constant-area/info.txt new file mode 100644 index 0000000000..e957a5ae6b --- /dev/null +++ b/test/574-irreducible-and-constant-area/info.txt @@ -0,0 +1,3 @@ +Regression test for intrinsics on x86, which used to wrongly assume +a HInvokeStaticOrDirect must have a special input (does not apply for irreducible +loops). diff --git a/test/574-irreducible-and-constant-area/run b/test/574-irreducible-and-constant-area/run new file mode 100755 index 0000000000..ffdbcc9cf2 --- /dev/null +++ b/test/574-irreducible-and-constant-area/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# 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. + +# Don't do relocation, as this affects this test. +exec ${RUN} "$@" --no-relocate diff --git a/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali b/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali new file mode 100644 index 0000000000..d7d43466d1 --- /dev/null +++ b/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali @@ -0,0 +1,35 @@ +# 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. + +.class public LIrreducibleLoop; + +.super Ljava/lang/Object; + +.method public static simpleLoop(I)I + .registers 5 + const/16 v0, 42 + const/16 v1, 42 + const-wide/high16 v2, 0x4000000000000000L + if-eq p0, v0, :other_loop_entry + :loop_entry + invoke-static {v1, v1}, LMain;->$inline$foo(FF)V + invoke-static {v2, v3, v2, v3}, LMain;->$inline$foo(DD)V + if-ne p0, v0, :exit + add-int v0, v0, v0 + :other_loop_entry + add-int v0, v0, v0 + goto :loop_entry + :exit + return v0 +.end method diff --git a/test/574-irreducible-and-constant-area/src/Main.java b/test/574-irreducible-and-constant-area/src/Main.java new file mode 100644 index 0000000000..3cdd92456c --- /dev/null +++ b/test/574-irreducible-and-constant-area/src/Main.java @@ -0,0 +1,41 @@ +/* + * 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. + */ + +import java.lang.reflect.Method; + +public class Main { + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("IrreducibleLoop"); + Method m = c.getMethod("simpleLoop", int.class); + Object[] arguments = { 42 }; + m.invoke(null, arguments); + } + + public static void $inline$foo(float a, float b) { + Math.abs(a); + Math.max(a, b); + Math.min(a, b); + } + + public static void $inline$foo(double a, double b) { + Math.abs(a); + Math.max(a, b); + Math.min(a, b); + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 7c71ce3c6a..364be59919 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -42,8 +42,7 @@ TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES := ifeq ($(ANDROID_COMPILE_WITH_JACK),true) TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(JACK) \ - $(JILL_JAR) + $(JACK) TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES += setup-jack-server endif @@ -72,8 +71,8 @@ $$(dmart_target): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(TARGET_JACK_CLASSPATH_DEPE DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK=$(abspath $(JACK)) \ + JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK_CLASSPATH=$(TARGET_JACK_CLASSPATH) \ - JILL_JAR=$(abspath $(JILL_JAR)) \ $(LOCAL_PATH)/run-test $$(PRIVATE_RUN_TEST_OPTIONS) --output-path $$(abspath $$(dir $$@)) $(1) $(hide) touch $$@ @@ -446,9 +445,7 @@ TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := # Known broken tests for the JIT. # CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT # also uses Generic JNI instead of the JNI compiler. -# Disable 570 while investigating OSR issues. TEST_ART_BROKEN_JIT_RUN_TESTS := \ - 570-checker-osr \ 137-cfi ifneq (,$(filter jit,$(COMPILER_TYPES))) @@ -964,8 +961,8 @@ $$(run_test_rule_name): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(HOST_OUT_EXECUTABLES DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK=$(abspath $(JACK)) \ + JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK_CLASSPATH=$$(PRIVATE_JACK_CLASSPATH) \ - JILL_JAR=$(abspath $(JILL_JAR)) \ art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(12) \ && $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \ diff --git a/test/run-test b/test/run-test index faa597e1ca..f1875d71a5 100755 --- a/test/run-test +++ b/test/run-test @@ -88,13 +88,7 @@ if [ -z "$JACK_CLASSPATH" ]; then export JACK_CLASSPATH="${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack" fi -# If JILL_JAR is not set, assume it is located in the prebuilts directory. -if [ -z "$JILL_JAR" ]; then - export JILL_JAR="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/jill.jar" -fi - export JACK="$JACK -g -cp $JACK_CLASSPATH" -export JILL="java -jar $JILL_JAR" info="info.txt" build="build" |