Merge "Refactor the inliner."
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index ac0f4ca..d3859ca 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 @@
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 @@
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 @@
// 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 fd7f949..a19b36f 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -82,6 +82,8 @@
// 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 @@
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 07edd97..3c880c2 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1276,7 +1276,7 @@
}
// 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 @@
}
// 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 @@
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 ab4f6f9..22cefe8 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -261,7 +261,8 @@
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 @@
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 @@
// 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 @@
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 552d175..854854f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3672,6 +3672,7 @@
// 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 0631ebe..5278d1b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2620,19 +2620,37 @@
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 @@
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 @@
}
// 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 @@
// 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 56a868a..a9448f7 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -592,6 +592,9 @@
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 0b2471b..4fd3c78 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 @@
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 09d8601..cbaa817 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 @@
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 @@
// 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 31c278e..188deb0 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 @@
}
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 @@
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();
+ 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;
- // 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;
- }
-
- // 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;
- }
-
- if (location == DexRegisterLocation::Kind::kConstant) {
- // We skip constants because the compiled code knows how to handle them.
- continue;
- }
-
- DCHECK(location == DexRegisterLocation::Kind::kInStack);
-
- 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;
+ {
+ 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;
}
+
+ 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;
+ }
+
+ // 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 @@
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 @@
}
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 a7881ac..a092b9f 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -66,6 +66,7 @@
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 @@
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 faa2983..56e8784 100644
--- a/test/003-omnibus-opcodes/build
+++ b/test/003-omnibus-opcodes/build
@@ -23,8 +23,8 @@
${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 057b351..93bee50 100644
--- a/test/005-annotations/build
+++ b/test/005-annotations/build
@@ -29,8 +29,8 @@
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 3f8915c..5cfc7f2 100644
--- a/test/022-interface/build
+++ b/test/022-interface/build
@@ -20,8 +20,8 @@
# 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 6f50a76..21dc662 100644
--- a/test/085-old-style-inner-class/build
+++ b/test/085-old-style-inner-class/build
@@ -23,8 +23,8 @@
${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 5a340dc..073a4ba 100755
--- a/test/091-override-package-private-method/build
+++ b/test/091-override-package-private-method/build
@@ -24,14 +24,12 @@
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 a855873..4525549 100644
--- a/test/097-duplicate-method/build
+++ b/test/097-duplicate-method/build
@@ -23,10 +23,10 @@
${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 e772fb8..58ac26d 100644
--- a/test/111-unresolvable-exception/build
+++ b/test/111-unresolvable-exception/build
@@ -22,8 +22,8 @@
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 8ef5c0e..4557ccd 100644
--- a/test/113-multidex/build
+++ b/test/113-multidex/build
@@ -28,14 +28,12 @@
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 b003307..852ec2e 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 b70ca4f..aca356b 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -269,16 +269,12 @@
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 85b69e9..771dd51 100644
--- a/test/121-modifiers/build
+++ b/test/121-modifiers/build
@@ -31,9 +31,9 @@
# 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 b92ecf9..0a340a2 100644
--- a/test/124-missing-classes/build
+++ b/test/124-missing-classes/build
@@ -27,8 +27,8 @@
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 b7f2118..00b9ba0 100644
--- a/test/126-miranda-multidex/build
+++ b/test/126-miranda-multidex/build
@@ -28,14 +28,12 @@
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 0d9f4d6..7ce46ac 100755
--- a/test/127-checker-secondarydex/build
+++ b/test/127-checker-secondarydex/build
@@ -24,14 +24,12 @@
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 8db7853..6a5618e 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 6f4bcfe..8ec98c1 100755
--- a/test/137-cfi/run
+++ b/test/137-cfi/run
@@ -20,4 +20,5 @@
# 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 8c3c4f8..d85035b 100644
--- a/test/529-checker-unresolved/build
+++ b/test/529-checker-unresolved/build
@@ -29,14 +29,12 @@
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 c1774f3..4c58b39 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 @@
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 @@
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 @@
}
bool in_osr_method_;
+ bool in_interpreter_;
};
extern "C" JNIEXPORT jboolean JNICALL Java_Main_ensureInOsrCode(JNIEnv*, jclass) {
@@ -65,4 +70,75 @@
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 bc6612f..828908a 100644
--- a/test/570-checker-osr/src/Main.java
+++ b/test/570-checker-osr/src/Main.java
@@ -36,8 +36,8 @@
} 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 @@
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 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 0000000..e69de29
--- /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 0000000..e957a5a
--- /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 0000000..ffdbcc9
--- /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 0000000..d7d4346
--- /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 0000000..3cdd924
--- /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 7c71ce3..364be59 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -42,8 +42,7 @@
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 @@
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 @@
# 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 @@
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 faa597e..f1875d7 100755
--- a/test/run-test
+++ b/test/run-test
@@ -88,13 +88,7 @@
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"