summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/compiler.h11
-rw-r--r--compiler/driver/compiler_driver.cc45
-rw-r--r--compiler/driver/compiler_driver.h3
-rw-r--r--compiler/elf_builder.h37
-rw-r--r--compiler/jit/jit_compiler.cc98
-rw-r--r--compiler/optimizing/code_generator.cc24
-rw-r--r--compiler/optimizing/code_generator.h22
-rw-r--r--compiler/optimizing/code_generator_arm.cc26
-rw-r--r--compiler/optimizing/code_generator_utils.cc16
-rw-r--r--compiler/optimizing/code_generator_utils.h9
-rw-r--r--compiler/optimizing/code_generator_x86.cc33
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc154
-rw-r--r--compiler/optimizing/code_generator_x86_64.h6
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc2
-rw-r--r--compiler/optimizing/locations.cc37
-rw-r--r--compiler/optimizing/locations.h4
-rw-r--r--compiler/optimizing/optimizing_compiler.cc346
-rw-r--r--runtime/class_linker.cc56
-rw-r--r--runtime/class_linker.h3
-rw-r--r--runtime/class_linker_test.cc2
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc50
-rw-r--r--runtime/jit/jit.cc11
-rw-r--r--runtime/jit/jit_code_cache.cc78
-rw-r--r--runtime/jit/jit_code_cache.h5
-rw-r--r--runtime/jit/jit_instrumentation.cc82
-rw-r--r--runtime/jit/jit_instrumentation.h55
-rw-r--r--runtime/mirror/class-inl.h9
-rw-r--r--runtime/mirror/class.cc17
-rw-r--r--runtime/mirror/class.h11
-rw-r--r--runtime/thread_pool.cc5
-rw-r--r--runtime/thread_pool.h3
-rw-r--r--test/008-exceptions/expected.txt17
-rw-r--r--test/008-exceptions/src/Main.java51
-rw-r--r--test/537-checker-jump-over-jump/expected.txt0
-rw-r--r--test/537-checker-jump-over-jump/info.txt1
-rw-r--r--test/537-checker-jump-over-jump/src/Main.java44
-rwxr-xr-xtools/buildbot-build.sh2
37 files changed, 824 insertions, 551 deletions
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 8788dc1950..3a9ce1bc0e 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -22,6 +22,10 @@
namespace art {
+namespace jit {
+ class JitCodeCache;
+}
+
class ArtMethod;
class Backend;
struct CompilationUnit;
@@ -58,6 +62,13 @@ class Compiler {
uint32_t method_idx,
const DexFile& dex_file) const = 0;
+ virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED,
+ jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED,
+ ArtMethod* method ATTRIBUTE_UNUSED)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ return false;
+ }
+
virtual uintptr_t GetEntryPointOf(ArtMethod* method) const
SHARED_REQUIRES(Locks::mutator_lock_) = 0;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index d055b37ea7..527d861abe 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -559,7 +559,7 @@ static void CompileMethod(Thread* self,
}
} else if ((access_flags & kAccAbstract) != 0) {
// Abstract methods don't have code.
- } else if (Runtime::Current()->IsAotCompiler()) {
+ } else {
const VerifiedMethod* verified_method =
driver->GetVerificationResults()->GetVerifiedMethod(method_ref);
bool compile = compilation_enabled &&
@@ -598,13 +598,6 @@ static void CompileMethod(Thread* self,
? dex_to_dex_compilation_level
: optimizer::DexToDexCompilationLevel::kRequired);
}
- } else {
- // This is for the JIT compiler, which has already ensured the class is verified.
- // We can go straight to compiling.
- DCHECK(Runtime::Current()->UseJit());
- compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type,
- class_def_idx, method_idx, class_loader,
- dex_file, dex_cache);
}
if (kTimeCompileMethod) {
uint64_t duration_ns = NanoTime() - start_ns;
@@ -696,42 +689,6 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t
self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
}
-CompiledMethod* CompilerDriver::CompileArtMethod(Thread* self, ArtMethod* method) {
- DCHECK_EQ(method,
- method->GetInterfaceMethodIfProxy(
- Runtime::Current()->GetClassLinker()->GetImagePointerSize()));
- const uint32_t method_idx = method->GetDexMethodIndex();
- const uint32_t access_flags = method->GetAccessFlags();
- const InvokeType invoke_type = method->GetInvokeType();
- StackHandleScope<2> hs(self);
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- method->GetDeclaringClass()->GetClassLoader()));
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
- jobject jclass_loader = class_loader.ToJObject();
- const DexFile* dex_file = method->GetDexFile();
- const uint16_t class_def_idx = method->GetClassDefIndex();
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
- optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
- GetDexToDexCompilationLevel(self, *this, class_loader, *dex_file, class_def);
- const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
- // Go to native so that we don't block GC during compilation.
- ScopedThreadSuspension sts(self, kNative);
- CompileMethod(self,
- this,
- code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- jclass_loader,
- *dex_file,
- dex_to_dex_compilation_level,
- true,
- dex_cache);
- auto* compiled_method = GetCompiledMethod(MethodReference(dex_file, method_idx));
- return compiled_method;
-}
-
void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 4ed4dc60d2..07cd077ed1 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -119,9 +119,6 @@ class CompilerDriver {
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
- CompiledMethod* CompileArtMethod(Thread* self, ArtMethod*)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!compiled_methods_lock_) WARN_UNUSED;
-
// Compile a single Method.
void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings)
SHARED_REQUIRES(Locks::mutator_lock_)
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index bbd962fae2..4e2b15d7b0 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -266,8 +266,8 @@ class ElfBuilder FINAL {
// Writer of .dynstr .strtab and .shstrtab sections.
class StrtabSection FINAL : public Section {
public:
- StrtabSection(const std::string& name, Elf_Word flags)
- : Section(name, SHT_STRTAB, flags, nullptr, 0, 1, 0) {
+ StrtabSection(const std::string& name, Elf_Word flags, Elf_Word align)
+ : Section(name, SHT_STRTAB, flags, nullptr, 0, align, 0) {
buffer_.reserve(4 * KB);
// The first entry of strtab must be empty string.
buffer_ += '\0';
@@ -459,16 +459,8 @@ class ElfBuilder FINAL {
private:
Elf_Word GetNumBuckets() const {
const auto& symbols = symtab_->symbols_;
- if (symbols.size() < 8) {
- return 2;
- } else if (symbols.size() < 32) {
- return 4;
- } else if (symbols.size() < 256) {
- return 16;
- } else {
- // Have about 32 ids per bucket.
- return RoundUp(symbols.size()/32, 2);
- }
+ // Have about 32 ids per bucket.
+ return 1 + symbols.size()/32;
}
// from bionic
@@ -495,7 +487,7 @@ class ElfBuilder FINAL {
Elf_Word text_size, CodeOutput* text_writer,
Elf_Word bss_size)
: isa_(isa),
- dynstr_(".dynstr", SHF_ALLOC),
+ dynstr_(".dynstr", SHF_ALLOC, kPageSize),
dynsym_(".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_),
hash_(".hash", SHF_ALLOC, &dynsym_),
rodata_(".rodata", SHT_PROGBITS, SHF_ALLOC,
@@ -504,9 +496,9 @@ class ElfBuilder FINAL {
nullptr, 0, kPageSize, 0, text_size, text_writer),
bss_(".bss", bss_size),
dynamic_(".dynamic", &dynstr_),
- strtab_(".strtab", 0),
+ strtab_(".strtab", 0, kPageSize),
symtab_(".symtab", SHT_SYMTAB, 0, &strtab_),
- shstrtab_(".shstrtab", 0) {
+ shstrtab_(".shstrtab", 0, 1) {
}
~ElfBuilder() {}
@@ -606,18 +598,18 @@ class ElfBuilder FINAL {
// Create a list of all section which we want to write.
// This is the order in which they will be written.
std::vector<Section*> sections;
- sections.push_back(&dynsym_);
- sections.push_back(&dynstr_);
- sections.push_back(&hash_);
sections.push_back(&rodata_);
sections.push_back(&text_);
if (bss_.GetSize() != 0u) {
sections.push_back(&bss_);
}
+ sections.push_back(&dynstr_);
+ sections.push_back(&dynsym_);
+ sections.push_back(&hash_);
sections.push_back(&dynamic_);
if (!symtab_.IsEmpty()) {
- sections.push_back(&symtab_);
sections.push_back(&strtab_);
+ sections.push_back(&symtab_);
}
for (Section* section : other_sections_) {
sections.push_back(section);
@@ -643,7 +635,7 @@ class ElfBuilder FINAL {
// We do not know the number of headers until the final stages of write.
// It is easiest to just reserve a fixed amount of space for them.
- constexpr size_t kMaxProgramHeaders = 8;
+ constexpr size_t kMaxProgramHeaders = 16;
constexpr size_t kProgramHeadersOffset = sizeof(Elf_Ehdr);
// Layout of all sections - determine the final file offsets and addresses.
@@ -694,6 +686,11 @@ class ElfBuilder FINAL {
if (bss_.GetHeader()->sh_size != 0u) {
program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_W, bss_));
}
+ program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R, dynstr_));
+ int dynstr_dynsym_hash_size = hash_.GetHeader()->sh_offset +
+ hash_.GetHeader()->sh_size - dynstr_.GetHeader()->sh_offset;
+ program_headers.back().p_filesz = dynstr_dynsym_hash_size;
+ program_headers.back().p_memsz = dynstr_dynsym_hash_size;
program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_W, dynamic_));
program_headers.push_back(MakeProgramHeader(PT_DYNAMIC, PF_R | PF_W, dynamic_));
const Section* eh_frame = FindSection(".eh_frame");
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index d520208d32..5f4f47292b 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -189,13 +189,14 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method) {
}
// Do the compilation.
- CompiledMethod* compiled_method = nullptr;
+ JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache();
+ bool success = false;
{
TimingLogger::ScopedTiming t2("Compiling", &logger);
// If we get a request to compile a proxy method, we pass the actual Java method
// of that proxy method, as the compiler does not expect a proxy method.
ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*));
- compiled_method = compiler_driver_->CompileArtMethod(self, method_to_compile);
+ success = compiler_driver_->GetCompiler()->JitCompile(self, code_cache, method_to_compile);
}
// Trim maps to reduce memory usage.
@@ -205,105 +206,14 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method) {
runtime->GetArenaPool()->TrimMaps();
}
- // Check if we failed compiling.
- if (compiled_method == nullptr) {
- return false;
- }
-
total_time_ += NanoTime() - start_time;
- bool result = false;
- const void* code = runtime->GetClassLinker()->GetOatMethodQuickCodeFor(method);
-
- if (code != nullptr) {
- // Already have some compiled code, just use this instead of linking.
- // TODO: Fix recompilation.
- method->SetEntryPointFromQuickCompiledCode(code);
- result = true;
- } else {
- TimingLogger::ScopedTiming t2("LinkCode", &logger);
- if (AddToCodeCache(method, compiled_method)) {
- result = true;
- }
- }
-
- // Remove the compiled method to save memory.
- compiler_driver_->RemoveCompiledMethod(
- MethodReference(h_class->GetDexCache()->GetDexFile(), method->GetDexMethodIndex()));
runtime->GetJit()->AddTimingLogger(logger);
- return result;
+ return success;
}
CompilerCallbacks* JitCompiler::GetCompilerCallbacks() const {
return callbacks_.get();
}
-bool JitCompiler::AddToCodeCache(ArtMethod* method,
- const CompiledMethod* compiled_method) {
- Runtime* runtime = Runtime::Current();
- JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache();
- auto const quick_code = compiled_method->GetQuickCode();
- if (quick_code.empty()) {
- return false;
- }
- const auto code_size = quick_code.size();
- Thread* const self = Thread::Current();
- auto const mapping_table = compiled_method->GetMappingTable();
- auto const vmap_table = compiled_method->GetVmapTable();
- auto const gc_map = compiled_method->GetGcMap();
- uint8_t* mapping_table_ptr = nullptr;
- uint8_t* vmap_table_ptr = nullptr;
- uint8_t* gc_map_ptr = nullptr;
-
- if (!mapping_table.empty()) {
- // Write out pre-header stuff.
- mapping_table_ptr = code_cache->AddDataArray(
- self, mapping_table.data(), mapping_table.data() + mapping_table.size());
- if (mapping_table_ptr == nullptr) {
- return false; // Out of data cache.
- }
- }
-
- if (!vmap_table.empty()) {
- vmap_table_ptr = code_cache->AddDataArray(
- self, vmap_table.data(), vmap_table.data() + vmap_table.size());
- if (vmap_table_ptr == nullptr) {
- return false; // Out of data cache.
- }
- }
-
- if (!gc_map.empty()) {
- gc_map_ptr = code_cache->AddDataArray(
- self, gc_map.data(), gc_map.data() + gc_map.size());
- if (gc_map_ptr == nullptr) {
- return false; // Out of data cache.
- }
- }
-
- uint8_t* const code = code_cache->CommitCode(self,
- method,
- mapping_table_ptr,
- vmap_table_ptr,
- gc_map_ptr,
- compiled_method->GetFrameSizeInBytes(),
- compiled_method->GetCoreSpillMask(),
- compiled_method->GetFpSpillMask(),
- compiled_method->GetQuickCode().data(),
- compiled_method->GetQuickCode().size());
-
- if (code == nullptr) {
- return false;
- }
-
- const size_t thumb_offset = compiled_method->CodeDelta();
- const uint32_t code_offset = sizeof(OatQuickMethodHeader) + thumb_offset;
- VLOG(jit)
- << "JIT added "
- << PrettyMethod(method) << "@" << method
- << " ccache_size=" << PrettySize(code_cache->CodeCacheSize()) << ": "
- << reinterpret_cast<void*>(code + code_offset)
- << "," << reinterpret_cast<void*>(code + code_offset + code_size);
- return true;
-}
-
} // namespace jit
} // namespace art
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index a1bb5e0838..ce92470868 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -42,7 +42,7 @@
#include "compiled_method.h"
#include "dex/verified_method.h"
-#include "driver/dex_compilation_unit.h"
+#include "driver/compiler_driver.h"
#include "gc_map_builder.h"
#include "graph_visualizer.h"
#include "intrinsics.h"
@@ -787,9 +787,10 @@ CodeGenerator* CodeGenerator::Create(HGraph* graph,
}
void CodeGenerator::BuildNativeGCMap(
- ArenaVector<uint8_t>* data, const DexCompilationUnit& dex_compilation_unit) const {
+ ArenaVector<uint8_t>* data, const CompilerDriver& compiler_driver) const {
const std::vector<uint8_t>& gc_map_raw =
- dex_compilation_unit.GetVerifiedMethod()->GetDexGcMap();
+ compiler_driver.GetVerifiedMethod(&GetGraph()->GetDexFile(), GetGraph()->GetMethodIdx())
+ ->GetDexGcMap();
verifier::DexPcToReferenceMap dex_gc_map(&(gc_map_raw)[0]);
uint32_t max_native_offset = stack_map_stream_.ComputeMaxNativePcOffset();
@@ -911,19 +912,22 @@ void CodeGenerator::BuildVMapTable(ArenaVector<uint8_t>* data) const {
vmap_encoder.PushBackUnsigned(VmapTable::kAdjustedFpMarker);
}
-void CodeGenerator::BuildStackMaps(ArenaVector<uint8_t>* data) {
- uint32_t size = stack_map_stream_.PrepareForFillIn();
- data->resize(size);
- MemoryRegion region(data->data(), size);
+size_t CodeGenerator::ComputeStackMapsSize() {
+ return stack_map_stream_.PrepareForFillIn();
+}
+
+void CodeGenerator::BuildStackMaps(MemoryRegion region) {
stack_map_stream_.FillIn(region);
}
void CodeGenerator::RecordNativeDebugInfo(uint32_t dex_pc,
uintptr_t native_pc_begin,
uintptr_t native_pc_end) {
- if (src_map_ != nullptr && dex_pc != kNoDexPc && native_pc_begin != native_pc_end) {
- src_map_->push_back(SrcMapElem({static_cast<uint32_t>(native_pc_begin),
- static_cast<int32_t>(dex_pc)}));
+ if (compiler_options_.GetGenerateDebugInfo() &&
+ dex_pc != kNoDexPc &&
+ native_pc_begin != native_pc_end) {
+ src_map_.push_back(SrcMapElem({static_cast<uint32_t>(native_pc_begin),
+ static_cast<int32_t>(dex_pc)}));
}
}
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 47b6f30450..a92014dc79 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -22,6 +22,7 @@
#include "base/arena_containers.h"
#include "base/arena_object.h"
#include "base/bit_field.h"
+#include "compiled_method.h"
#include "driver/compiler_options.h"
#include "globals.h"
#include "graph_visualizer.h"
@@ -51,13 +52,9 @@ static int64_t constexpr kPrimLongMax = INT64_C(0x7fffffffffffffff);
class Assembler;
class CodeGenerator;
-class DexCompilationUnit;
+class CompilerDriver;
class LinkerPatch;
class ParallelMoveResolver;
-class SrcMapElem;
-template <class Alloc>
-class SrcMap;
-using DefaultSrcMap = SrcMap<std::allocator<SrcMapElem>>;
class CodeAllocator {
public:
@@ -284,13 +281,12 @@ class CodeGenerator {
slow_paths_.push_back(slow_path);
}
- void SetSrcMap(DefaultSrcMap* src_map) { src_map_ = src_map; }
-
void BuildMappingTable(ArenaVector<uint8_t>* vector) const;
void BuildVMapTable(ArenaVector<uint8_t>* vector) const;
void BuildNativeGCMap(
- ArenaVector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
- void BuildStackMaps(ArenaVector<uint8_t>* vector);
+ ArenaVector<uint8_t>* vector, const CompilerDriver& compiler_driver) const;
+ void BuildStackMaps(MemoryRegion region);
+ size_t ComputeStackMapsSize();
bool IsBaseline() const {
return is_baseline_;
@@ -446,6 +442,10 @@ class CodeGenerator {
// Copy the result of a call into the given target.
virtual void MoveFromReturnRegister(Location trg, Primitive::Type type) = 0;
+ const ArenaVector<SrcMapElem>& GetSrcMappingTable() const {
+ return src_map_;
+ }
+
protected:
// Method patch info used for recording locations of required linker patches and
// target methods. The target method can be used for various purposes, whether for
@@ -488,7 +488,7 @@ class CodeGenerator {
stats_(stats),
graph_(graph),
compiler_options_(compiler_options),
- src_map_(nullptr),
+ src_map_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
slow_paths_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
current_block_index_(0),
is_leaf_(true),
@@ -602,7 +602,7 @@ class CodeGenerator {
const CompilerOptions& compiler_options_;
// Native to dex_pc map used for native debugging/profiling tools.
- DefaultSrcMap* src_map_;
+ ArenaVector<SrcMapElem> src_map_;
ArenaVector<SlowPathCode*> slow_paths_;
// The current block index in `block_order_` of the block
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 3dc3b7fba0..6d05293277 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1300,20 +1300,29 @@ void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instructio
DCHECK_EQ(cond_value, 0);
}
} else {
- if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) {
- // Condition has been materialized, compare the output to 0
+ // Can we optimize the jump if we know that the next block is the true case?
+ HCondition* condition = cond->AsCondition();
+ bool can_jump_to_false = CanReverseCondition(always_true_target, false_target, condition);
+ if (condition == nullptr || condition->NeedsMaterialization()) {
+ // Condition has been materialized, compare the output to 0.
DCHECK(instruction->GetLocations()->InAt(0).IsRegister());
+ if (can_jump_to_false) {
+ __ CompareAndBranchIfZero(instruction->GetLocations()->InAt(0).AsRegister<Register>(),
+ false_target);
+ return;
+ }
__ CompareAndBranchIfNonZero(instruction->GetLocations()->InAt(0).AsRegister<Register>(),
true_target);
} else {
// Condition has not been materialized, use its inputs as the
// comparison and its condition as the branch condition.
- Primitive::Type type =
- cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt;
+ Primitive::Type type = (condition != nullptr)
+ ? cond->InputAt(0)->GetType()
+ : Primitive::kPrimInt;
// Is this a long or FP comparison that has been folded into the HCondition?
if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
// Generate the comparison directly.
- GenerateCompareTestAndBranch(instruction->AsIf(), cond->AsCondition(),
+ GenerateCompareTestAndBranch(instruction->AsIf(), condition,
true_target, false_target, always_true_target);
return;
}
@@ -1328,7 +1337,12 @@ void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instructio
DCHECK(right.IsConstant());
GenerateCompareWithImmediate(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
}
- __ b(true_target, ARMCondition(cond->AsCondition()->GetCondition()));
+ if (can_jump_to_false) {
+ __ b(false_target, ARMCondition(condition->GetOppositeCondition()));
+ return;
+ }
+
+ __ b(true_target, ARMCondition(condition->GetCondition()));
}
}
if (false_target != nullptr) {
diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc
index 921c1d86c2..bf354e7ee2 100644
--- a/compiler/optimizing/code_generator_utils.cc
+++ b/compiler/optimizing/code_generator_utils.cc
@@ -15,6 +15,7 @@
*/
#include "code_generator_utils.h"
+#include "nodes.h"
#include "base/logging.h"
@@ -94,4 +95,19 @@ void CalculateMagicAndShiftForDivRem(int64_t divisor, bool is_long,
*shift = is_long ? p - 64 : p - 32;
}
+// Is it valid to reverse the condition? Uses the values supplied to
+// GenerateTestAndBranch() in instruction generators.
+bool CanReverseCondition(Label* always_true_target,
+ Label* false_target,
+ HCondition* condition) {
+ // 'always_true_target' is null when the 'true' path is to the next
+ // block to be generated. Check the type of the condition to ensure that
+ // FP conditions are not swapped. This is for future fusing of HCompare and
+ // HCondition.
+ // Note: If the condition is nullptr, then it is always okay to reverse.
+ return always_true_target == nullptr && false_target != nullptr &&
+ (condition == nullptr ||
+ !Primitive::IsFloatingPointType(condition->InputAt(0)->GetType()));
+}
+
} // namespace art
diff --git a/compiler/optimizing/code_generator_utils.h b/compiler/optimizing/code_generator_utils.h
index 59b495c2c9..628eee8885 100644
--- a/compiler/optimizing/code_generator_utils.h
+++ b/compiler/optimizing/code_generator_utils.h
@@ -21,10 +21,19 @@
namespace art {
+class Label;
+class HCondition;
+
// Computes the magic number and the shift needed in the div/rem by constant algorithm, as out
// arguments `magic` and `shift`
void CalculateMagicAndShiftForDivRem(int64_t divisor, bool is_long, int64_t* magic, int* shift);
+// Is it valid to reverse the condition? Uses the values supplied to
+// GenerateTestAndBranch() in instruction generators.
+bool CanReverseCondition(Label* always_true_target,
+ Label* false_target,
+ HCondition* condition);
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_UTILS_H_
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 0df7e3b30a..0db5837b97 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1216,16 +1216,21 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio
DCHECK_EQ(cond_value, 0);
}
} else {
+ HCondition* condition = cond->AsCondition();
bool is_materialized =
- !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
+ condition == nullptr || condition->NeedsMaterialization();
// Moves do not affect the eflags register, so if the condition is
// evaluated just before the if, we don't need to evaluate it
// again. We can't use the eflags on long/FP conditions if they are
// materialized due to the complex branching.
- Primitive::Type type = cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt;
- bool eflags_set = cond->IsCondition()
- && cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction)
+ Primitive::Type type = (condition != nullptr)
+ ? cond->InputAt(0)->GetType()
+ : Primitive::kPrimInt;
+ bool eflags_set = condition != nullptr
+ && condition->IsBeforeWhenDisregardMoves(instruction)
&& (type != Primitive::kPrimLong && !Primitive::IsFloatingPointType(type));
+ // Can we optimize the jump if we know that the next block is the true case?
+ bool can_jump_to_false = CanReverseCondition(always_true_target, false_target, condition);
if (is_materialized) {
if (!eflags_set) {
// Materialized condition, compare against 0.
@@ -1235,9 +1240,17 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio
} else {
__ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
}
+ if (can_jump_to_false) {
+ __ j(kEqual, false_target);
+ return;
+ }
__ j(kNotEqual, true_target);
} else {
- __ j(X86Condition(cond->AsCondition()->GetCondition()), true_target);
+ if (can_jump_to_false) {
+ __ j(X86Condition(condition->GetOppositeCondition()), false_target);
+ return;
+ }
+ __ j(X86Condition(condition->GetCondition()), true_target);
}
} else {
// Condition has not been materialized, use its inputs as the
@@ -1247,7 +1260,7 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio
if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
// Generate the comparison directly.
GenerateCompareTestAndBranch(instruction->AsIf(),
- cond->AsCondition(),
+ condition,
true_target,
false_target,
always_true_target);
@@ -1270,7 +1283,13 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio
} else {
__ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
}
- __ j(X86Condition(cond->AsCondition()->GetCondition()), true_target);
+
+ if (can_jump_to_false) {
+ __ j(X86Condition(condition->GetOppositeCondition()), false_target);
+ return;
+ }
+
+ __ j(X86Condition(condition->GetCondition()), true_target);
}
}
if (false_target != nullptr) {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 5218d70995..ee8a299c5e 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1183,16 +1183,20 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc
DCHECK_EQ(cond_value, 0);
}
} else {
- bool is_materialized =
- !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
+ HCondition* condition = cond->AsCondition();
+ bool is_materialized = condition == nullptr || condition->NeedsMaterialization();
// Moves do not affect the eflags register, so if the condition is
// evaluated just before the if, we don't need to evaluate it
// again. We can't use the eflags on FP conditions if they are
// materialized due to the complex branching.
- Primitive::Type type = cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt;
- bool eflags_set = cond->IsCondition()
- && cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction)
+ Primitive::Type type = (condition != nullptr)
+ ? cond->InputAt(0)->GetType()
+ : Primitive::kPrimInt;
+ bool eflags_set = condition != nullptr
+ && condition->IsBeforeWhenDisregardMoves(instruction)
&& !Primitive::IsFloatingPointType(type);
+ // Can we optimize the jump if we know that the next block is the true case?
+ bool can_jump_to_false = CanReverseCondition(always_true_target, false_target, condition);
if (is_materialized) {
if (!eflags_set) {
@@ -1204,9 +1208,17 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc
__ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()),
Immediate(0));
}
+ if (can_jump_to_false) {
+ __ j(kEqual, false_target);
+ return;
+ }
__ j(kNotEqual, true_target);
} else {
- __ j(X86_64IntegerCondition(cond->AsCondition()->GetCondition()), true_target);
+ if (can_jump_to_false) {
+ __ j(X86_64IntegerCondition(condition->GetOppositeCondition()), false_target);
+ return;
+ }
+ __ j(X86_64IntegerCondition(condition->GetCondition()), true_target);
}
} else {
// Condition has not been materialized, use its inputs as the
@@ -1215,7 +1227,7 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc
// Is this a long or FP comparison that has been folded into the HCondition?
if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
// Generate the comparison directly.
- GenerateCompareTestAndBranch(instruction->AsIf(), cond->AsCondition(),
+ GenerateCompareTestAndBranch(instruction->AsIf(), condition,
true_target, false_target, always_true_target);
return;
}
@@ -1235,7 +1247,13 @@ void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruc
__ cmpl(lhs.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), rhs.GetStackIndex()));
}
- __ j(X86_64IntegerCondition(cond->AsCondition()->GetCondition()), true_target);
+
+ if (can_jump_to_false) {
+ __ j(X86_64IntegerCondition(condition->GetOppositeCondition()), false_target);
+ return;
+ }
+
+ __ j(X86_64IntegerCondition(condition->GetCondition()), true_target);
}
}
if (false_target != nullptr) {
@@ -2562,7 +2580,7 @@ void LocationsBuilderX86_64::VisitAdd(HAdd* add) {
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
// We can use a leaq or addq if the constant can fit in an immediate.
- locations->SetInAt(1, Location::RegisterOrInt32LongConstant(add->InputAt(1)));
+ locations->SetInAt(1, Location::RegisterOrInt32Constant(add->InputAt(1)));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
}
@@ -2682,7 +2700,7 @@ void LocationsBuilderX86_64::VisitSub(HSub* sub) {
}
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrInt32LongConstant(sub->InputAt(1)));
+ locations->SetInAt(1, Location::RegisterOrInt32Constant(sub->InputAt(1)));
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -3755,14 +3773,25 @@ void LocationsBuilderX86_64::HandleFieldSet(HInstruction* instruction,
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Primitive::Type field_type = field_info.GetFieldType();
+ bool is_volatile = field_info.IsVolatile();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
locations->SetInAt(0, Location::RequiresRegister());
if (Primitive::IsFloatingPointType(instruction->InputAt(1)->GetType())) {
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ if (is_volatile) {
+ // In order to satisfy the semantics of volatile, this must be a single instruction store.
+ locations->SetInAt(1, Location::FpuRegisterOrInt32Constant(instruction->InputAt(1)));
+ } else {
+ locations->SetInAt(1, Location::FpuRegisterOrConstant(instruction->InputAt(1)));
+ }
} else {
- locations->SetInAt(1, Location::RegisterOrInt32LongConstant(instruction->InputAt(1)));
+ if (is_volatile) {
+ // In order to satisfy the semantics of volatile, this must be a single instruction store.
+ locations->SetInAt(1, Location::RegisterOrInt32Constant(instruction->InputAt(1)));
+ } else {
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ }
}
if (needs_write_barrier) {
// Temporary registers for the write barrier.
@@ -3790,11 +3819,13 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
}
+ bool maybe_record_implicit_null_check_done = false;
+
switch (field_type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte: {
if (value.IsConstant()) {
- int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
+ int8_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
__ movb(Address(base, offset), Immediate(v));
} else {
__ movb(Address(base, offset), value.AsRegister<CpuRegister>());
@@ -3805,7 +3836,7 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
if (value.IsConstant()) {
- int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
+ int16_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
__ movw(Address(base, offset), Immediate(v));
} else {
__ movw(Address(base, offset), value.AsRegister<CpuRegister>());
@@ -3838,9 +3869,11 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
case Primitive::kPrimLong: {
if (value.IsConstant()) {
int64_t v = value.GetConstant()->AsLongConstant()->GetValue();
- DCHECK(IsInt<32>(v));
- int32_t v_32 = v;
- __ movq(Address(base, offset), Immediate(v_32));
+ codegen_->MoveInt64ToAddress(Address(base, offset),
+ Address(base, offset + sizeof(int32_t)),
+ v,
+ instruction);
+ maybe_record_implicit_null_check_done = true;
} else {
__ movq(Address(base, offset), value.AsRegister<CpuRegister>());
}
@@ -3848,12 +3881,28 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
}
case Primitive::kPrimFloat: {
- __ movss(Address(base, offset), value.AsFpuRegister<XmmRegister>());
+ if (value.IsConstant()) {
+ int32_t v =
+ bit_cast<int32_t, float>(value.GetConstant()->AsFloatConstant()->GetValue());
+ __ movl(Address(base, offset), Immediate(v));
+ } else {
+ __ movss(Address(base, offset), value.AsFpuRegister<XmmRegister>());
+ }
break;
}
case Primitive::kPrimDouble: {
- __ movsd(Address(base, offset), value.AsFpuRegister<XmmRegister>());
+ if (value.IsConstant()) {
+ int64_t v =
+ bit_cast<int64_t, double>(value.GetConstant()->AsDoubleConstant()->GetValue());
+ codegen_->MoveInt64ToAddress(Address(base, offset),
+ Address(base, offset + sizeof(int32_t)),
+ v,
+ instruction);
+ maybe_record_implicit_null_check_done = true;
+ } else {
+ __ movsd(Address(base, offset), value.AsFpuRegister<XmmRegister>());
+ }
break;
}
@@ -3862,7 +3911,9 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
UNREACHABLE();
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ if (!maybe_record_implicit_null_check_done) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
@@ -4170,13 +4221,9 @@ void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
may_need_runtime_call ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(
- 1, Location::RegisterOrConstant(instruction->InputAt(1)));
- locations->SetInAt(2, Location::RequiresRegister());
- if (value_type == Primitive::kPrimLong) {
- locations->SetInAt(2, Location::RegisterOrInt32LongConstant(instruction->InputAt(2)));
- } else if (value_type == Primitive::kPrimFloat || value_type == Primitive::kPrimDouble) {
- locations->SetInAt(2, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ if (Primitive::IsFloatingPointType(value_type)) {
+ locations->SetInAt(2, Location::FpuRegisterOrConstant(instruction->InputAt(2)));
} else {
locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
}
@@ -4330,13 +4377,15 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
: Address(array, index.AsRegister<CpuRegister>(), TIMES_8, offset);
if (value.IsRegister()) {
__ movq(address, value.AsRegister<CpuRegister>());
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
} else {
int64_t v = value.GetConstant()->AsLongConstant()->GetValue();
- DCHECK(IsInt<32>(v));
- int32_t v_32 = v;
- __ movq(address, Immediate(v_32));
+ Address address_high = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) +
+ offset + sizeof(int32_t))
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_8, offset + sizeof(int32_t));
+ codegen_->MoveInt64ToAddress(address, address_high, v, instruction);
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -4345,8 +4394,14 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
Address address = index.IsConstant()
? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
: Address(array, index.AsRegister<CpuRegister>(), TIMES_4, offset);
- DCHECK(value.IsFpuRegister());
- __ movss(address, value.AsFpuRegister<XmmRegister>());
+ if (value.IsFpuRegister()) {
+ __ movss(address, value.AsFpuRegister<XmmRegister>());
+ } else {
+ DCHECK(value.IsConstant());
+ int32_t v =
+ bit_cast<int32_t, float>(value.GetConstant()->AsFloatConstant()->GetValue());
+ __ movl(address, Immediate(v));
+ }
codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -4356,9 +4411,18 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
Address address = index.IsConstant()
? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + offset)
: Address(array, index.AsRegister<CpuRegister>(), TIMES_8, offset);
- DCHECK(value.IsFpuRegister());
- __ movsd(address, value.AsFpuRegister<XmmRegister>());
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ if (value.IsFpuRegister()) {
+ __ movsd(address, value.AsFpuRegister<XmmRegister>());
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ } else {
+ int64_t v =
+ bit_cast<int64_t, double>(value.GetConstant()->AsDoubleConstant()->GetValue());
+ Address address_high = index.IsConstant()
+ ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) +
+ offset + sizeof(int32_t))
+ : Address(array, index.AsRegister<CpuRegister>(), TIMES_8, offset + sizeof(int32_t));
+ codegen_->MoveInt64ToAddress(address, address_high, v, instruction);
+ }
break;
}
@@ -5564,6 +5628,24 @@ Address CodeGeneratorX86_64::LiteralCaseTable(HPackedSwitch* switch_instr) {
return Address::RIP(table_fixup);
}
+void CodeGeneratorX86_64::MoveInt64ToAddress(const Address& addr_low,
+ const Address& addr_high,
+ int64_t v,
+ HInstruction* instruction) {
+ if (IsInt<32>(v)) {
+ int32_t v_32 = v;
+ __ movq(addr_low, Immediate(v_32));
+ MaybeRecordImplicitNullCheck(instruction);
+ } else {
+ // Didn't fit in a register. Do it in pieces.
+ int32_t low_v = Low32Bits(v);
+ int32_t high_v = High32Bits(v);
+ __ movl(addr_low, Immediate(low_v));
+ MaybeRecordImplicitNullCheck(instruction);
+ __ movl(addr_high, Immediate(high_v));
+ }
+}
+
#undef __
} // namespace x86_64
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index fc485f5bb6..7a52473408 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -368,6 +368,12 @@ class CodeGeneratorX86_64 : public CodeGenerator {
// Store a 64 bit value into a DoubleStackSlot in the most efficient manner.
void Store64BitValueToStack(Location dest, int64_t value);
+ // Assign a 64 bit constant to an address.
+ void MoveInt64ToAddress(const Address& addr_low,
+ const Address& addr_high,
+ int64_t v,
+ HInstruction* instruction);
+
private:
struct PcRelativeDexCacheAccessInfo {
PcRelativeDexCacheAccessInfo(const DexFile& dex_file, uint32_t element_off)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 14c65c9aaf..a29f3ef1d1 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1605,7 +1605,7 @@ static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke)
LocationSummary::kNoCall,
kIntrinsified);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrInt32LongConstant(invoke->InputAt(1)));
+ locations->SetInAt(1, Location::RegisterOrInt32Constant(invoke->InputAt(1)));
}
static void GenPoke(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) {
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index ebdf7a2f65..1ab206f69e 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -17,6 +17,7 @@
#include "locations.h"
#include "nodes.h"
+#include "code_generator.h"
namespace art {
@@ -47,18 +48,26 @@ Location Location::RegisterOrConstant(HInstruction* instruction) {
: Location::RequiresRegister();
}
-Location Location::RegisterOrInt32LongConstant(HInstruction* instruction) {
- if (instruction->IsIntConstant() || instruction->IsNullConstant()) {
- return Location::ConstantLocation(instruction->AsConstant());
- } else if (instruction->IsLongConstant()) {
- // Does the long constant fit in a 32 bit int?
- int64_t value = instruction->AsLongConstant()->GetValue();
- return IsInt<32>(value)
- ? Location::ConstantLocation(instruction->AsConstant())
- : Location::RequiresRegister();
- } else {
- return Location::RequiresRegister();
+Location Location::RegisterOrInt32Constant(HInstruction* instruction) {
+ HConstant* constant = instruction->AsConstant();
+ if (constant != nullptr) {
+ int64_t value = CodeGenerator::GetInt64ValueOf(constant);
+ if (IsInt<32>(value)) {
+ return Location::ConstantLocation(constant);
+ }
}
+ return Location::RequiresRegister();
+}
+
+Location Location::FpuRegisterOrInt32Constant(HInstruction* instruction) {
+ HConstant* constant = instruction->AsConstant();
+ if (constant != nullptr) {
+ int64_t value = CodeGenerator::GetInt64ValueOf(constant);
+ if (IsInt<32>(value)) {
+ return Location::ConstantLocation(constant);
+ }
+ }
+ return Location::RequiresFpuRegister();
}
Location Location::ByteRegisterOrConstant(int reg, HInstruction* instruction) {
@@ -67,6 +76,12 @@ Location Location::ByteRegisterOrConstant(int reg, HInstruction* instruction) {
: Location::RegisterLocation(reg);
}
+Location Location::FpuRegisterOrConstant(HInstruction* instruction) {
+ return instruction->IsConstant()
+ ? Location::ConstantLocation(instruction->AsConstant())
+ : Location::RequiresFpuRegister();
+}
+
std::ostream& operator<<(std::ostream& os, const Location& location) {
os << location.DebugString();
if (location.IsRegister() || location.IsFpuRegister()) {
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index d014379bca..1181007666 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -354,8 +354,10 @@ class Location : public ValueObject {
}
static Location RegisterOrConstant(HInstruction* instruction);
- static Location RegisterOrInt32LongConstant(HInstruction* instruction);
+ static Location RegisterOrInt32Constant(HInstruction* instruction);
static Location ByteRegisterOrConstant(int reg, HInstruction* instruction);
+ static Location FpuRegisterOrConstant(HInstruction* instruction);
+ static Location FpuRegisterOrInt32Constant(HInstruction* instruction);
// The location of the first input to the instruction will be
// used to replace this unallocated location.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8cb2cfc816..7e3c5e602e 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -56,6 +56,7 @@
#include "inliner.h"
#include "instruction_simplifier.h"
#include "intrinsics.h"
+#include "jit/jit_code_cache.h"
#include "licm.h"
#include "jni/quick/jni_compiler.h"
#include "load_store_elimination.h"
@@ -258,15 +259,6 @@ class OptimizingCompiler FINAL : public Compiler {
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache) const OVERRIDE;
- CompiledMethod* TryCompile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache) const;
-
CompiledMethod* JniCompile(uint32_t access_flags,
uint32_t method_idx,
const DexFile& dex_file) const OVERRIDE {
@@ -291,23 +283,45 @@ class OptimizingCompiler FINAL : public Compiler {
}
}
+ bool JitCompile(Thread* self, jit::JitCodeCache* code_cache, ArtMethod* method)
+ OVERRIDE
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
private:
// Whether we should run any optimization or register allocation. If false, will
// just run the code generation after the graph was built.
const bool run_optimizations_;
- // Optimize and compile `graph`.
- CompiledMethod* CompileOptimized(HGraph* graph,
- CodeGenerator* codegen,
- CompilerDriver* driver,
- const DexCompilationUnit& dex_compilation_unit,
- PassObserver* pass_observer) const;
-
- // Just compile without doing optimizations.
- CompiledMethod* CompileBaseline(CodeGenerator* codegen,
- CompilerDriver* driver,
- const DexCompilationUnit& dex_compilation_unit,
- PassObserver* pass_observer) const;
+ // Create a 'CompiledMethod' for an optimized graph.
+ CompiledMethod* EmitOptimized(ArenaAllocator* arena,
+ CodeVectorAllocator* code_allocator,
+ CodeGenerator* codegen,
+ CompilerDriver* driver) const;
+
+ // Create a 'CompiledMethod' for a non-optimized graph.
+ CompiledMethod* EmitBaseline(ArenaAllocator* arena,
+ CodeVectorAllocator* code_allocator,
+ CodeGenerator* codegen,
+ CompilerDriver* driver) const;
+
+ // Try compiling a method and return the code generator used for
+ // compiling it.
+ // This method:
+ // 1) Builds the graph. Returns null if it failed to build it.
+ // 2) If `run_optimizations_` is set:
+ // 2.1) Transform the graph to SSA. Returns null if it failed.
+ // 2.2) Run optimizations on the graph, including register allocator.
+ // 3) Generate code with the `code_allocator` provided.
+ CodeGenerator* TryCompile(ArenaAllocator* arena,
+ CodeVectorAllocator* code_allocator,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file,
+ Handle<mirror::DexCache> dex_cache) const;
std::unique_ptr<OptimizingCompilerStats> compilation_stats_;
@@ -446,13 +460,32 @@ static void RunArchOptimizations(InstructionSet instruction_set,
}
}
+NO_INLINE // Avoid increasing caller's frame size by large stack-allocated objects.
+static void AllocateRegisters(HGraph* graph,
+ CodeGenerator* codegen,
+ PassObserver* pass_observer) {
+ PrepareForRegisterAllocation(graph).Run();
+ SsaLivenessAnalysis liveness(graph, codegen);
+ {
+ PassScope scope(SsaLivenessAnalysis::kLivenessPassName, pass_observer);
+ liveness.Analyze();
+ }
+ {
+ PassScope scope(RegisterAllocator::kRegisterAllocatorPassName, pass_observer);
+ RegisterAllocator(graph->GetArena(), codegen, liveness).AllocateRegisters();
+ }
+}
+
static void RunOptimizations(HGraph* graph,
CodeGenerator* codegen,
CompilerDriver* driver,
OptimizingCompilerStats* stats,
const DexCompilationUnit& dex_compilation_unit,
- PassObserver* pass_observer,
- StackHandleScopeCollection* handles) {
+ PassObserver* pass_observer) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScopeCollection handles(soa.Self());
+ ScopedThreadSuspension sts(soa.Self(), kNative);
+
ArenaAllocator* arena = graph->GetArena();
HDeadCodeElimination* dce1 = new (arena) HDeadCodeElimination(
graph, stats, HDeadCodeElimination::kInitialDeadCodeEliminationPassName);
@@ -469,7 +502,7 @@ static void RunOptimizations(HGraph* graph,
HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, induction);
ReferenceTypePropagation* type_propagation =
- new (arena) ReferenceTypePropagation(graph, handles);
+ new (arena) ReferenceTypePropagation(graph, &handles);
HSharpening* sharpening = new (arena) HSharpening(graph, codegen, dex_compilation_unit, driver);
InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier_after_types");
@@ -492,7 +525,7 @@ static void RunOptimizations(HGraph* graph,
RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer);
- MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
+ MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, &handles);
// TODO: Update passes incompatible with try/catch so we have the same
// pipeline for all methods.
@@ -532,6 +565,7 @@ static void RunOptimizations(HGraph* graph,
}
RunArchOptimizations(driver->GetInstructionSet(), graph, stats, pass_observer);
+ AllocateRegisters(graph, codegen, pass_observer);
}
// The stack map we generate must be 4-byte aligned on ARM. Since existing
@@ -545,22 +579,6 @@ static ArrayRef<const uint8_t> AlignVectorSize(ArenaVector<uint8_t>& vector) {
return ArrayRef<const uint8_t>(vector);
}
-NO_INLINE // Avoid increasing caller's frame size by large stack-allocated objects.
-static void AllocateRegisters(HGraph* graph,
- CodeGenerator* codegen,
- PassObserver* pass_observer) {
- PrepareForRegisterAllocation(graph).Run();
- SsaLivenessAnalysis liveness(graph, codegen);
- {
- PassScope scope(SsaLivenessAnalysis::kLivenessPassName, pass_observer);
- liveness.Analyze();
- }
- {
- PassScope scope(RegisterAllocator::kRegisterAllocatorPassName, pass_observer);
- RegisterAllocator(graph->GetArena(), codegen, liveness).AllocateRegisters();
- }
-}
-
static ArenaVector<LinkerPatch> EmitAndSortLinkerPatches(CodeGenerator* codegen) {
ArenaVector<LinkerPatch> linker_patches(codegen->GetGraph()->GetArena()->Adapter());
codegen->EmitLinkerPatches(&linker_patches);
@@ -574,74 +592,42 @@ static ArenaVector<LinkerPatch> EmitAndSortLinkerPatches(CodeGenerator* codegen)
return linker_patches;
}
-CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver,
- const DexCompilationUnit& dex_compilation_unit,
- PassObserver* pass_observer) const {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
- soa.Self()->TransitionFromRunnableToSuspended(kNative);
- RunOptimizations(graph,
- codegen,
- compiler_driver,
- compilation_stats_.get(),
- dex_compilation_unit,
- pass_observer,
- &handles);
-
- AllocateRegisters(graph, codegen, pass_observer);
-
- ArenaAllocator* arena = graph->GetArena();
- CodeVectorAllocator allocator(arena);
- DefaultSrcMap src_mapping_table;
- codegen->SetSrcMap(compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()
- ? &src_mapping_table
- : nullptr);
- codegen->CompileOptimized(&allocator);
-
+CompiledMethod* OptimizingCompiler::EmitOptimized(ArenaAllocator* arena,
+ CodeVectorAllocator* code_allocator,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver) const {
ArenaVector<LinkerPatch> linker_patches = EmitAndSortLinkerPatches(codegen);
-
ArenaVector<uint8_t> stack_map(arena->Adapter(kArenaAllocStackMaps));
- codegen->BuildStackMaps(&stack_map);
+ stack_map.resize(codegen->ComputeStackMapsSize());
+ codegen->BuildStackMaps(MemoryRegion(stack_map.data(), stack_map.size()));
MaybeRecordStat(MethodCompilationStat::kCompiledOptimized);
CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
compiler_driver,
codegen->GetInstructionSet(),
- ArrayRef<const uint8_t>(allocator.GetMemory()),
+ ArrayRef<const uint8_t>(code_allocator->GetMemory()),
// Follow Quick's behavior and set the frame size to zero if it is
// considered "empty" (see the definition of
// art::CodeGenerator::HasEmptyFrame).
codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
codegen->GetCoreSpillMask(),
codegen->GetFpuSpillMask(),
- ArrayRef<const SrcMapElem>(src_mapping_table),
+ ArrayRef<const SrcMapElem>(codegen->GetSrcMappingTable()),
ArrayRef<const uint8_t>(), // mapping_table.
ArrayRef<const uint8_t>(stack_map),
ArrayRef<const uint8_t>(), // native_gc_map.
ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()),
ArrayRef<const LinkerPatch>(linker_patches));
- pass_observer->DumpDisassembly();
- soa.Self()->TransitionFromSuspendedToRunnable();
return compiled_method;
}
-CompiledMethod* OptimizingCompiler::CompileBaseline(
+CompiledMethod* OptimizingCompiler::EmitBaseline(
+ ArenaAllocator* arena,
+ CodeVectorAllocator* code_allocator,
CodeGenerator* codegen,
- CompilerDriver* compiler_driver,
- const DexCompilationUnit& dex_compilation_unit,
- PassObserver* pass_observer) const {
- ArenaAllocator* arena = codegen->GetGraph()->GetArena();
- CodeVectorAllocator allocator(arena);
- DefaultSrcMap src_mapping_table;
- codegen->SetSrcMap(compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()
- ? &src_mapping_table
- : nullptr);
- codegen->CompileBaseline(&allocator);
-
+ CompilerDriver* compiler_driver) const {
ArenaVector<LinkerPatch> linker_patches = EmitAndSortLinkerPatches(codegen);
ArenaVector<uint8_t> mapping_table(arena->Adapter(kArenaAllocBaselineMaps));
@@ -649,37 +635,38 @@ CompiledMethod* OptimizingCompiler::CompileBaseline(
ArenaVector<uint8_t> vmap_table(arena->Adapter(kArenaAllocBaselineMaps));
codegen->BuildVMapTable(&vmap_table);
ArenaVector<uint8_t> gc_map(arena->Adapter(kArenaAllocBaselineMaps));
- codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+ codegen->BuildNativeGCMap(&gc_map, *compiler_driver);
MaybeRecordStat(MethodCompilationStat::kCompiledBaseline);
CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
compiler_driver,
codegen->GetInstructionSet(),
- ArrayRef<const uint8_t>(allocator.GetMemory()),
+ ArrayRef<const uint8_t>(code_allocator->GetMemory()),
// Follow Quick's behavior and set the frame size to zero if it is
// considered "empty" (see the definition of
// art::CodeGenerator::HasEmptyFrame).
codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
codegen->GetCoreSpillMask(),
codegen->GetFpuSpillMask(),
- ArrayRef<const SrcMapElem>(src_mapping_table),
+ ArrayRef<const SrcMapElem>(codegen->GetSrcMappingTable()),
AlignVectorSize(mapping_table),
AlignVectorSize(vmap_table),
AlignVectorSize(gc_map),
ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()),
ArrayRef<const LinkerPatch>(linker_patches));
- pass_observer->DumpDisassembly();
return compiled_method;
}
-CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache) const {
+CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
+ CodeVectorAllocator* code_allocator,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file,
+ Handle<mirror::DexCache> dex_cache) const {
std::string method_name = PrettyMethod(method_idx, dex_file);
MaybeRecordStat(MethodCompilationStat::kAttemptCompilation);
CompilerDriver* compiler_driver = GetCompilerDriver();
@@ -721,13 +708,10 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite
&& compiler_driver->RequiresConstructorBarrier(Thread::Current(),
dex_compilation_unit.GetDexFile(),
dex_compilation_unit.GetClassDefIndex());
- ArenaAllocator arena(Runtime::Current()->GetArenaPool());
- HGraph* graph = new (&arena) HGraph(
- &arena, dex_file, method_idx, requires_barrier, compiler_driver->GetInstructionSet(),
+ HGraph* graph = new (arena) HGraph(
+ arena, dex_file, method_idx, requires_barrier, compiler_driver->GetInstructionSet(),
kInvalidInvokeType, compiler_driver->GetCompilerOptions().GetDebuggable());
- bool shouldOptimize = method_name.find("$opt$reg$") != std::string::npos && run_optimizations_;
-
std::unique_ptr<CodeGenerator> codegen(
CodeGenerator::Create(graph,
instruction_set,
@@ -779,16 +763,8 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite
}
}
- bool can_allocate_registers = RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set);
-
- // `run_optimizations_` is set explicitly (either through a compiler filter
- // or the debuggable flag). If it is set, we can run baseline. Otherwise, we fall back
- // to Quick.
- bool can_use_baseline = !run_optimizations_ && builder.CanUseBaselineForStringInit();
- CompiledMethod* compiled_method = nullptr;
- if (run_optimizations_ && can_allocate_registers) {
- VLOG(compiler) << "Optimizing " << method_name;
-
+ VLOG(compiler) << "Optimizing " << method_name;
+ if (run_optimizations_) {
{
PassScope scope(SsaBuilder::kSsaBuilderPassName, &pass_observer);
if (!graph->TryBuildingSsa()) {
@@ -800,37 +776,26 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite
}
}
- compiled_method = CompileOptimized(graph,
- codegen.get(),
- compiler_driver,
- dex_compilation_unit,
- &pass_observer);
- } else if (shouldOptimize && can_allocate_registers) {
- LOG(FATAL) << "Could not allocate registers in optimizing compiler";
- UNREACHABLE();
- } else if (can_use_baseline) {
- VLOG(compiler) << "Compile baseline " << method_name;
-
- if (!run_optimizations_) {
- MaybeRecordStat(MethodCompilationStat::kNotOptimizedDisabled);
- } else if (!can_allocate_registers) {
- MaybeRecordStat(MethodCompilationStat::kNotOptimizedRegisterAllocator);
- }
-
- compiled_method = CompileBaseline(codegen.get(),
- compiler_driver,
- dex_compilation_unit,
- &pass_observer);
+ RunOptimizations(graph,
+ codegen.get(),
+ compiler_driver,
+ compilation_stats_.get(),
+ dex_compilation_unit,
+ &pass_observer);
+ codegen->CompileOptimized(code_allocator);
+ } else {
+ codegen->CompileBaseline(code_allocator);
}
+ pass_observer.DumpDisassembly();
if (kArenaAllocatorCountAllocations) {
- if (arena.BytesAllocated() > 4 * MB) {
- MemStats mem_stats(arena.GetMemStats());
+ if (arena->BytesAllocated() > 4 * MB) {
+ MemStats mem_stats(arena->GetMemStats());
LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
}
}
- return compiled_method;
+ return codegen.release();
}
static bool CanHandleVerificationFailure(const VerifiedMethod* verified_method) {
@@ -852,26 +817,37 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
Handle<mirror::DexCache> dex_cache) const {
CompilerDriver* compiler_driver = GetCompilerDriver();
CompiledMethod* method = nullptr;
- if (Runtime::Current()->IsAotCompiler()) {
- const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
- DCHECK(!verified_method->HasRuntimeThrow());
- if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)
- || CanHandleVerificationFailure(verified_method)) {
- method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
- method_idx, jclass_loader, dex_file, dex_cache);
- } else {
- if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) {
- MaybeRecordStat(MethodCompilationStat::kNotCompiledVerifyAtRuntime);
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
+ DCHECK(!verified_method->HasRuntimeThrow());
+ if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)
+ || CanHandleVerificationFailure(verified_method)) {
+ ArenaAllocator arena(Runtime::Current()->GetArenaPool());
+ CodeVectorAllocator code_allocator(&arena);
+ std::unique_ptr<CodeGenerator> codegen(
+ TryCompile(&arena,
+ &code_allocator,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ jclass_loader,
+ dex_file,
+ dex_cache));
+ if (codegen.get() != nullptr) {
+ if (run_optimizations_) {
+ method = EmitOptimized(&arena, &code_allocator, codegen.get(), compiler_driver);
} else {
- MaybeRecordStat(MethodCompilationStat::kNotCompiledClassNotVerified);
+ method = EmitBaseline(&arena, &code_allocator, codegen.get(), compiler_driver);
}
}
} else {
- // This is for the JIT compiler, which has already ensured the class is verified.
- // We can go straight to compiling.
- DCHECK(Runtime::Current()->UseJit());
- method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
- method_idx, jclass_loader, dex_file, dex_cache);
+ if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) {
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledVerifyAtRuntime);
+ } else {
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledClassNotVerified);
+ }
}
if (kIsDebugBuild &&
@@ -896,4 +872,70 @@ bool IsCompilingWithCoreImage() {
return EndsWith(image, "core.art") || EndsWith(image, "core-optimizing.art");
}
+bool OptimizingCompiler::JitCompile(Thread* self,
+ jit::JitCodeCache* code_cache,
+ ArtMethod* method) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ method->GetDeclaringClass()->GetClassLoader()));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
+
+ jobject jclass_loader = class_loader.ToJObject();
+ const DexFile* dex_file = method->GetDexFile();
+ const uint16_t class_def_idx = method->GetClassDefIndex();
+ const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
+ const uint32_t method_idx = method->GetDexMethodIndex();
+ const uint32_t access_flags = method->GetAccessFlags();
+ const InvokeType invoke_type = method->GetInvokeType();
+
+ ArenaAllocator arena(Runtime::Current()->GetArenaPool());
+ CodeVectorAllocator code_allocator(&arena);
+ std::unique_ptr<CodeGenerator> codegen;
+ {
+ // Go to native so that we don't block GC during compilation.
+ ScopedThreadSuspension sts(self, kNative);
+
+ DCHECK(run_optimizations_);
+ codegen.reset(
+ TryCompile(&arena,
+ &code_allocator,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ jclass_loader,
+ *dex_file,
+ dex_cache));
+ if (codegen.get() == nullptr) {
+ return false;
+ }
+ }
+
+ size_t stack_map_size = codegen->ComputeStackMapsSize();
+ uint8_t* stack_map_data = code_cache->ReserveData(self, stack_map_size);
+ if (stack_map_data == nullptr) {
+ return false;
+ }
+ codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size));
+ const void* code = code_cache->CommitCode(
+ self,
+ method,
+ nullptr,
+ stack_map_data,
+ nullptr,
+ codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
+ codegen->GetCoreSpillMask(),
+ codegen->GetFpuSpillMask(),
+ code_allocator.GetMemory().data(),
+ code_allocator.GetSize());
+
+ if (code == nullptr) {
+ code_cache->ClearData(self, stack_map_data);
+ return false;
+ }
+
+ return true;
+}
+
} // namespace art
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index da70456369..6053469016 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -104,12 +104,13 @@ static void ThrowNoClassDefFoundError(const char* fmt, ...) {
va_end(args);
}
-bool ClassLinker::HasInitWithString(Thread* self, const char* descriptor) {
+static bool HasInitWithString(Thread* self, ClassLinker* class_linker, const char* descriptor)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
ArtMethod* method = self->GetCurrentMethod(nullptr);
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(method != nullptr ?
method->GetDeclaringClass()->GetClassLoader() : nullptr));
- mirror::Class* exception_class = FindClass(self, descriptor, class_loader);
+ mirror::Class* exception_class = class_linker->FindClass(self, descriptor, class_loader);
if (exception_class == nullptr) {
// No exc class ~ no <init>-with-string.
@@ -119,10 +120,39 @@ bool ClassLinker::HasInitWithString(Thread* self, const char* descriptor) {
}
ArtMethod* exception_init_method = exception_class->FindDeclaredDirectMethod(
- "<init>", "(Ljava/lang/String;)V", image_pointer_size_);
+ "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
return exception_init_method != nullptr;
}
+// Helper for ThrowEarlierClassFailure. Throws the stored error.
+static void HandleEarlierVerifyError(Thread* self, ClassLinker* class_linker, mirror::Class* c)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* obj = c->GetVerifyError();
+ DCHECK(obj != nullptr);
+ self->AssertNoPendingException();
+ if (obj->IsClass()) {
+ // Previous error has been stored as class. Create a new exception of that type.
+
+ // It's possible the exception doesn't have a <init>(String).
+ std::string temp;
+ const char* descriptor = obj->AsClass()->GetDescriptor(&temp);
+
+ if (HasInitWithString(self, class_linker, descriptor)) {
+ self->ThrowNewException(descriptor, PrettyDescriptor(c).c_str());
+ } else {
+ self->ThrowNewException(descriptor, nullptr);
+ }
+ } else {
+ // Previous error has been stored as an instance. Just rethrow.
+ mirror::Class* throwable_class =
+ self->DecodeJObject(WellKnownClasses::java_lang_Throwable)->AsClass();
+ mirror::Class* error_class = obj->GetClass();
+ CHECK(throwable_class->IsAssignableFrom(error_class));
+ self->SetException(obj->AsThrowable());
+ }
+ self->AssertPendingException();
+}
+
void ClassLinker::ThrowEarlierClassFailure(mirror::Class* c) {
// The class failed to initialize on a previous attempt, so we want to throw
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
@@ -131,8 +161,11 @@ void ClassLinker::ThrowEarlierClassFailure(mirror::Class* c) {
Runtime* const runtime = Runtime::Current();
if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime.
std::string extra;
- if (c->GetVerifyErrorClass() != nullptr) {
- extra = PrettyDescriptor(c->GetVerifyErrorClass());
+ if (c->GetVerifyError() != nullptr) {
+ mirror::Class* descr_from = c->GetVerifyError()->IsClass()
+ ? c->GetVerifyError()->AsClass()
+ : c->GetVerifyError()->GetClass();
+ extra = PrettyDescriptor(descr_from);
}
LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c) << ": " << extra;
}
@@ -144,17 +177,8 @@ void ClassLinker::ThrowEarlierClassFailure(mirror::Class* c) {
mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
} else {
- if (c->GetVerifyErrorClass() != nullptr) {
- // TODO: change the verifier to store an _instance_, with a useful detail message?
- // It's possible the exception doesn't have a <init>(String).
- std::string temp;
- const char* descriptor = c->GetVerifyErrorClass()->GetDescriptor(&temp);
-
- if (HasInitWithString(self, descriptor)) {
- self->ThrowNewException(descriptor, PrettyDescriptor(c).c_str());
- } else {
- self->ThrowNewException(descriptor, nullptr);
- }
+ if (c->GetVerifyError() != nullptr) {
+ HandleEarlierVerifyError(self, this, c);
} else {
self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
PrettyDescriptor(c).c_str());
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 392efd23e2..73f9d4b864 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -854,9 +854,6 @@ class ClassLinker {
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
- bool HasInitWithString(Thread* self, const char* descriptor)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
-
bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, bool can_init_parents)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 04b890063d..2c086c59f0 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -515,7 +515,7 @@ struct ClassOffsets : public CheckOffsets<mirror::Class> {
addOffset(OFFSETOF_MEMBER(mirror::Class, sfields_), "sFields");
addOffset(OFFSETOF_MEMBER(mirror::Class, status_), "status");
addOffset(OFFSETOF_MEMBER(mirror::Class, super_class_), "superClass");
- addOffset(OFFSETOF_MEMBER(mirror::Class, verify_error_class_), "verifyErrorClass");
+ addOffset(OFFSETOF_MEMBER(mirror::Class, verify_error_), "verifyError");
addOffset(OFFSETOF_MEMBER(mirror::Class, virtual_methods_), "virtualMethods");
addOffset(OFFSETOF_MEMBER(mirror::Class, vtable_), "vtable");
};
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index e57569e140..87e29ae3c3 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -364,36 +364,34 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp,
(reinterpret_cast<uint8_t*>(sp) + callee_return_pc_offset));
ArtMethod* outer_method = *caller_sp;
ArtMethod* caller = outer_method;
-
- if (outer_method != nullptr) {
- const OatQuickMethodHeader* current_code = outer_method->GetOatQuickMethodHeader(caller_pc);
- if (current_code->IsOptimized()) {
- if (LIKELY(caller_pc != reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()))) {
- uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc);
- CodeInfo code_info = current_code->GetOptimizedCodeInfo();
- StackMapEncoding encoding = code_info.ExtractEncoding();
- StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
- DCHECK(stack_map.IsValid());
- if (stack_map.HasInlineInfo(encoding)) {
- InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
- caller = GetResolvedMethod(outer_method, inline_info, inline_info.GetDepth() - 1);
- }
- } else {
- // We're instrumenting, just use the StackVisitor which knows how to
- // handle instrumented frames.
- NthCallerVisitor visitor(Thread::Current(), 1, true);
- visitor.WalkStack();
- caller = visitor.caller;
+ if (LIKELY(caller_pc != reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()))) {
+ if (outer_method != nullptr) {
+ const OatQuickMethodHeader* current_code = outer_method->GetOatQuickMethodHeader(caller_pc);
+ if (current_code->IsOptimized()) {
+ uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc);
+ CodeInfo code_info = current_code->GetOptimizedCodeInfo();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+ DCHECK(stack_map.IsValid());
+ if (stack_map.HasInlineInfo(encoding)) {
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+ caller = GetResolvedMethod(outer_method, inline_info, inline_info.GetDepth() - 1);
+ }
}
}
- }
-
- if (kIsDebugBuild && do_caller_check) {
- // Note that do_caller_check is optional, as this method can be called by
- // stubs, and tests without a proper call stack.
+ if (kIsDebugBuild && do_caller_check) {
+ // Note that do_caller_check is optional, as this method can be called by
+ // stubs, and tests without a proper call stack.
+ NthCallerVisitor visitor(Thread::Current(), 1, true);
+ visitor.WalkStack();
+ CHECK_EQ(caller, visitor.caller);
+ }
+ } else {
+ // We're instrumenting, just use the StackVisitor which knows how to
+ // handle instrumented frames.
NthCallerVisitor visitor(Thread::Current(), 1, true);
visitor.WalkStack();
- CHECK_EQ(caller, visitor.caller);
+ caller = visitor.caller;
}
return caller;
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 5afd28e7b5..f69115159f 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -26,7 +26,6 @@
#include "jit_instrumentation.h"
#include "runtime.h"
#include "runtime_options.h"
-#include "thread_list.h"
#include "utils.h"
namespace art {
@@ -145,7 +144,7 @@ void Jit::CreateThreadPool() {
void Jit::DeleteThreadPool() {
if (instrumentation_cache_.get() != nullptr) {
- instrumentation_cache_->DeleteThreadPool();
+ instrumentation_cache_->DeleteThreadPool(Thread::Current());
}
}
@@ -164,16 +163,8 @@ Jit::~Jit() {
void Jit::CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold) {
CHECK_GT(compile_threshold, 0U);
- ScopedSuspendAll ssa(__FUNCTION__);
- // Add Jit interpreter instrumentation, tells the interpreter when to notify the jit to compile
- // something.
instrumentation_cache_.reset(
new jit::JitInstrumentationCache(compile_threshold, warmup_threshold));
- Runtime::Current()->GetInstrumentation()->AddListener(
- new jit::JitInstrumentationListener(instrumentation_cache_.get()),
- instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kBackwardBranch |
- instrumentation::Instrumentation::kInvokeVirtualOrInterface);
}
} // namespace jit
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 4c7cb1e36a..fbcba1b881 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -248,40 +248,49 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
OatQuickMethodHeader* method_header = nullptr;
uint8_t* code_ptr = nullptr;
-
- ScopedThreadSuspension sts(self, kSuspended);
- MutexLock mu(self, lock_);
- WaitForPotentialCollectionToComplete(self);
{
- ScopedCodeCacheWrite scc(code_map_.get());
- uint8_t* result = reinterpret_cast<uint8_t*>(
- mspace_memalign(code_mspace_, alignment, total_size));
- if (result == nullptr) {
- return nullptr;
+ ScopedThreadSuspension sts(self, kSuspended);
+ MutexLock mu(self, lock_);
+ WaitForPotentialCollectionToComplete(self);
+ {
+ ScopedCodeCacheWrite scc(code_map_.get());
+ uint8_t* result = reinterpret_cast<uint8_t*>(
+ mspace_memalign(code_mspace_, alignment, total_size));
+ if (result == nullptr) {
+ return nullptr;
+ }
+ code_ptr = result + header_size;
+ DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(code_ptr), alignment);
+
+ std::copy(code, code + code_size, code_ptr);
+ method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+ new (method_header) OatQuickMethodHeader(
+ (mapping_table == nullptr) ? 0 : code_ptr - mapping_table,
+ (vmap_table == nullptr) ? 0 : code_ptr - vmap_table,
+ (gc_map == nullptr) ? 0 : code_ptr - gc_map,
+ frame_size_in_bytes,
+ core_spill_mask,
+ fp_spill_mask,
+ code_size);
}
- code_ptr = result + header_size;
- DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(code_ptr), alignment);
-
- std::copy(code, code + code_size, code_ptr);
- method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
- new (method_header) OatQuickMethodHeader(
- (mapping_table == nullptr) ? 0 : code_ptr - mapping_table,
- (vmap_table == nullptr) ? 0 : code_ptr - vmap_table,
- (gc_map == nullptr) ? 0 : code_ptr - gc_map,
- frame_size_in_bytes,
- core_spill_mask,
- fp_spill_mask,
- code_size);
- }
-
- __builtin___clear_cache(reinterpret_cast<char*>(code_ptr),
- reinterpret_cast<char*>(code_ptr + code_size));
- method_code_map_.Put(code_ptr, method);
- // We have checked there was no collection in progress earlier. If we
- // were, setting the entry point of a method would be unsafe, as the collection
- // could delete it.
- DCHECK(!collection_in_progress_);
- method->SetEntryPointFromQuickCompiledCode(method_header->GetEntryPoint());
+
+ __builtin___clear_cache(reinterpret_cast<char*>(code_ptr),
+ reinterpret_cast<char*>(code_ptr + code_size));
+ method_code_map_.Put(code_ptr, method);
+ // We have checked there was no collection in progress earlier. If we
+ // were, setting the entry point of a method would be unsafe, as the collection
+ // could delete it.
+ DCHECK(!collection_in_progress_);
+ method->SetEntryPointFromQuickCompiledCode(method_header->GetEntryPoint());
+ }
+ VLOG(jit)
+ << "JIT added "
+ << PrettyMethod(method) << "@" << method
+ << " ccache_size=" << PrettySize(CodeCacheSize()) << ": "
+ << " dcache_size=" << PrettySize(DataCacheSize()) << ": "
+ << reinterpret_cast<const void*>(method_header->GetEntryPoint()) << ","
+ << reinterpret_cast<const void*>(method_header->GetEntryPoint() + method_header->code_size_);
+
return reinterpret_cast<uint8_t*>(method_header);
}
@@ -304,6 +313,11 @@ size_t JitCodeCache::NumberOfCompiledCode() {
return method_code_map_.size();
}
+void JitCodeCache::ClearData(Thread* self, void* data) {
+ MutexLock mu(self, lock_);
+ mspace_free(data_mspace_, data);
+}
+
uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
size = RoundUp(size, sizeof(void*));
uint8_t* result = nullptr;
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index e10f9629ae..afff657880 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -88,6 +88,11 @@ class JitCodeCache {
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
+ // Clear data from the data portion of the code cache.
+ void ClearData(Thread* self, void* data)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!lock_);
+
// Add a data array of size (end - begin) with the associated contents, returns null if there
// is no more room.
uint8_t* AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end)
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
index 7931306ff6..6531325de5 100644
--- a/runtime/jit/jit_instrumentation.cc
+++ b/runtime/jit/jit_instrumentation.cc
@@ -20,6 +20,7 @@
#include "jit.h"
#include "jit_code_cache.h"
#include "scoped_thread_state_change.h"
+#include "thread_list.h"
namespace art {
namespace jit {
@@ -73,16 +74,48 @@ class JitCompileTask FINAL : public Task {
JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold,
size_t warm_method_threshold)
: hot_method_threshold_(hot_method_threshold),
- warm_method_threshold_(warm_method_threshold) {
+ warm_method_threshold_(warm_method_threshold),
+ listener_(this) {
}
void JitInstrumentationCache::CreateThreadPool() {
+ // Create the thread pool before setting the instrumentation, so that
+ // when the threads stopped being suspended, they can use it directly.
+ // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
+ // is not null when we instrument.
thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
+ thread_pool_->StartWorkers(Thread::Current());
+ {
+ // Add Jit interpreter instrumentation, tells the interpreter when
+ // to notify the jit to compile something.
+ ScopedSuspendAll ssa(__FUNCTION__);
+ Runtime::Current()->GetInstrumentation()->AddListener(
+ &listener_, JitInstrumentationListener::kJitEvents);
+ }
}
-void JitInstrumentationCache::DeleteThreadPool() {
- DCHECK(Runtime::Current()->IsShuttingDown(Thread::Current()));
- thread_pool_.reset();
+void JitInstrumentationCache::DeleteThreadPool(Thread* self) {
+ DCHECK(Runtime::Current()->IsShuttingDown(self));
+ if (thread_pool_ != nullptr) {
+ // First remove the listener, to avoid having mutators enter
+ // 'AddSamples'.
+ ThreadPool* cache = nullptr;
+ {
+ ScopedSuspendAll ssa(__FUNCTION__);
+ Runtime::Current()->GetInstrumentation()->RemoveListener(
+ &listener_, JitInstrumentationListener::kJitEvents);
+ // Clear thread_pool_ field while the threads are suspended.
+ // A mutator in the 'AddSamples' method will check against it.
+ cache = thread_pool_.release();
+ }
+ cache->StopWorkers(self);
+ cache->RemoveAllTasks(self);
+ // We could just suspend all threads, but we know those threads
+ // will finish in a short period, so it's not worth adding a suspend logic
+ // here. Besides, this is only done for shutdown.
+ cache->Wait(self, false, false);
+ delete cache;
+ }
}
void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t) {
@@ -91,25 +124,32 @@ void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t
if (method->IsClassInitializer() || method->IsNative()) {
return;
}
- if (thread_pool_.get() == nullptr) {
- DCHECK(Runtime::Current()->IsShuttingDown(self));
- return;
- }
+ DCHECK(thread_pool_ != nullptr);
+
uint16_t sample_count = method->IncrementCounter();
if (sample_count == warm_method_threshold_) {
- if (ProfilingInfo::Create(self, method, /* retry_allocation */ false)) {
+ bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
+ if (success) {
VLOG(jit) << "Start profiling " << PrettyMethod(method);
- } else {
+ }
+
+ if (thread_pool_ == nullptr) {
+ // Calling ProfilingInfo::Create might put us in a suspended state, which could
+ // lead to the thread pool being deleted when we are shutting down.
+ DCHECK(Runtime::Current()->IsShuttingDown(self));
+ return;
+ }
+
+ if (!success) {
// We failed allocating. Instead of doing the collection on the Java thread, we push
// an allocation to a compiler thread, that will do the collection.
thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
- thread_pool_->StartWorkers(self);
}
}
if (sample_count == hot_method_threshold_) {
+ DCHECK(thread_pool_ != nullptr);
thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
- thread_pool_->StartWorkers(self);
}
}
@@ -118,6 +158,20 @@ JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache*
CHECK(instrumentation_cache_ != nullptr);
}
+void JitInstrumentationListener::MethodEntered(Thread* thread,
+ mirror::Object* /*this_object*/,
+ ArtMethod* method,
+ uint32_t /*dex_pc*/) {
+ instrumentation_cache_->AddSamples(thread, method, 1);
+}
+
+void JitInstrumentationListener::BackwardBranch(Thread* thread,
+ ArtMethod* method,
+ int32_t dex_pc_offset) {
+ CHECK_LE(dex_pc_offset, 0);
+ instrumentation_cache_->AddSamples(thread, method, 1);
+}
+
void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread,
mirror::Object* this_object,
ArtMethod* caller,
@@ -138,7 +192,9 @@ void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread,
}
void JitInstrumentationCache::WaitForCompilationToFinish(Thread* self) {
- thread_pool_->Wait(self, false, false);
+ if (thread_pool_ != nullptr) {
+ thread_pool_->Wait(self, false, false);
+ }
}
} // namespace jit
diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h
index 9eb464b841..1f96d59888 100644
--- a/runtime/jit/jit_instrumentation.h
+++ b/runtime/jit/jit_instrumentation.h
@@ -31,7 +31,6 @@
namespace art {
namespace mirror {
- class Class;
class Object;
class Throwable;
} // namespace mirror
@@ -42,24 +41,7 @@ class Thread;
namespace jit {
-// Keeps track of which methods are hot.
-class JitInstrumentationCache {
- public:
- JitInstrumentationCache(size_t hot_method_threshold, size_t warm_method_threshold);
- void AddSamples(Thread* self, ArtMethod* method, size_t samples)
- SHARED_REQUIRES(Locks::mutator_lock_);
- void CreateThreadPool();
- void DeleteThreadPool();
- // Wait until there is no more pending compilation tasks.
- void WaitForCompilationToFinish(Thread* self);
-
- private:
- size_t hot_method_threshold_;
- size_t warm_method_threshold_;
- std::unique_ptr<ThreadPool> thread_pool_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache);
-};
+class JitInstrumentationCache;
class JitInstrumentationListener : public instrumentation::InstrumentationListener {
public:
@@ -67,9 +49,8 @@ class JitInstrumentationListener : public instrumentation::InstrumentationListen
void MethodEntered(Thread* thread, mirror::Object* /*this_object*/,
ArtMethod* method, uint32_t /*dex_pc*/)
- OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
- instrumentation_cache_->AddSamples(thread, method, 1);
- }
+ OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
+
void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/,
ArtMethod* /*method*/, uint32_t /*dex_pc*/,
const JValue& /*return_value*/)
@@ -90,10 +71,7 @@ class JitInstrumentationListener : public instrumentation::InstrumentationListen
ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { }
void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
- OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
- CHECK_LE(dex_pc_offset, 0);
- instrumentation_cache_->AddSamples(thread, method, 1);
- }
+ OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
void InvokeVirtualOrInterface(Thread* thread,
mirror::Object* this_object,
@@ -102,12 +80,37 @@ class JitInstrumentationListener : public instrumentation::InstrumentationListen
ArtMethod* callee)
OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
+ static constexpr uint32_t kJitEvents =
+ instrumentation::Instrumentation::kMethodEntered |
+ instrumentation::Instrumentation::kBackwardBranch |
+ instrumentation::Instrumentation::kInvokeVirtualOrInterface;
+
private:
JitInstrumentationCache* const instrumentation_cache_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationListener);
};
+// Keeps track of which methods are hot.
+class JitInstrumentationCache {
+ public:
+ JitInstrumentationCache(size_t hot_method_threshold, size_t warm_method_threshold);
+ void AddSamples(Thread* self, ArtMethod* method, size_t samples)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ void CreateThreadPool();
+ void DeleteThreadPool(Thread* self);
+ // Wait until there is no more pending compilation tasks.
+ void WaitForCompilationToFinish(Thread* self);
+
+ private:
+ size_t hot_method_threshold_;
+ size_t warm_method_threshold_;
+ JitInstrumentationListener listener_;
+ std::unique_ptr<ThreadPool> thread_pool_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache);
+};
+
} // namespace jit
} // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 19ee7f4dd4..174de0e2f6 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -520,15 +520,6 @@ inline void Class::SetClinitThreadId(pid_t new_clinit_thread_id) {
}
}
-inline void Class::SetVerifyErrorClass(Class* klass) {
- CHECK(klass != nullptr) << PrettyClass(this);
- if (Runtime::Current()->IsActiveTransaction()) {
- SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass);
- } else {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass);
- }
-}
-
template<VerifyObjectFlags kVerifyFlags>
inline uint32_t Class::GetAccessFlags() {
// Check class is loaded/retired or this is java.lang.String that has a
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 9d01a1db60..77275f007b 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -57,6 +57,15 @@ void Class::VisitRoots(RootVisitor* visitor) {
java_lang_Class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
+inline void Class::SetVerifyError(mirror::Object* error) {
+ CHECK(error != nullptr) << PrettyClass(this);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_), error);
+ } else {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_), error);
+ }
+}
+
void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
Status old_status = h_this->GetStatus();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -109,7 +118,13 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
// case.
Class* exception_class = old_exception->GetClass();
if (!eiie_class->IsAssignableFrom(exception_class)) {
- h_this->SetVerifyErrorClass(exception_class);
+ // Store the exception class when this is the AoT compiler. Don't store full exceptions,
+ // as they need to be trimmed (native components are not storable in an image).
+ if (Runtime::Current()->IsAotCompiler()) {
+ h_this->SetVerifyError(exception_class);
+ } else {
+ h_this->SetVerifyError(old_exception.Get());
+ }
}
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 8219d69b6e..c4339b9230 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1015,9 +1015,9 @@ class MANAGED Class FINAL : public Object {
void SetClinitThreadId(pid_t new_clinit_thread_id) SHARED_REQUIRES(Locks::mutator_lock_);
- Class* GetVerifyErrorClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ Object* GetVerifyError() SHARED_REQUIRES(Locks::mutator_lock_) {
// DCHECK(IsErroneous());
- return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_));
+ return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_));
}
uint16_t GetDexClassDefIndex() SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -1158,7 +1158,7 @@ class MANAGED Class FINAL : public Object {
SHARED_REQUIRES(Locks::mutator_lock_);
private:
- void SetVerifyErrorClass(Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
+ void SetVerifyError(Object* klass) SHARED_REQUIRES(Locks::mutator_lock_);
template <bool throw_on_failure, bool use_referrers_cache>
bool ResolvedFieldAccessTest(Class* access_to, ArtField* field,
@@ -1230,8 +1230,9 @@ class MANAGED Class FINAL : public Object {
// check for interfaces and return null.
HeapReference<Class> super_class_;
- // If class verify fails, we must return same error on subsequent tries.
- HeapReference<Class> verify_error_class_;
+ // If class verify fails, we must return same error on subsequent tries. We may store either
+ // the class of the error, or an actual instance of Throwable here.
+ HeapReference<Object> verify_error_;
// Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass is
// copied in, and virtual methods from our class either replace those from the super or are
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 0527d3ae14..5a4dfb8cfd 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -82,6 +82,11 @@ void ThreadPool::AddTask(Thread* self, Task* task) {
}
}
+void ThreadPool::RemoveAllTasks(Thread* self) {
+ MutexLock mu(self, task_queue_lock_);
+ tasks_.clear();
+}
+
ThreadPool::ThreadPool(const char* name, size_t num_threads)
: name_(name),
task_queue_lock_("task queue lock"),
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index a2338d6fcc..6cd4ad3cdc 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -91,6 +91,9 @@ class ThreadPool {
// after running it, it is the caller's responsibility.
void AddTask(Thread* self, Task* task) REQUIRES(!task_queue_lock_);
+ // Remove all tasks in the queue.
+ void RemoveAllTasks(Thread* self) REQUIRES(!task_queue_lock_);
+
ThreadPool(const char* name, size_t num_threads);
virtual ~ThreadPool();
diff --git a/test/008-exceptions/expected.txt b/test/008-exceptions/expected.txt
index 92c79dc2a0..ea59a49677 100644
--- a/test/008-exceptions/expected.txt
+++ b/test/008-exceptions/expected.txt
@@ -1,12 +1,15 @@
Got an NPE: second throw
java.lang.NullPointerException: second throw
- at Main.catchAndRethrow(Main.java:58)
- at Main.exceptions_007(Main.java:41)
- at Main.main(Main.java:49)
+ at Main.catchAndRethrow(Main.java:77)
+ at Main.exceptions_007(Main.java:59)
+ at Main.main(Main.java:67)
Caused by: java.lang.NullPointerException: first throw
- at Main.throwNullPointerException(Main.java:65)
- at Main.catchAndRethrow(Main.java:55)
+ at Main.throwNullPointerException(Main.java:84)
+ at Main.catchAndRethrow(Main.java:74)
... 2 more
Static Init
-BadError: This is bad by convention
-BadError: This is bad by convention
+BadError: This is bad by convention: BadInit
+BadError: This is bad by convention: BadInit
+Static BadInitNoStringInit
+BadErrorNoStringInit: This is bad by convention
+BadErrorNoStringInit: This is bad by convention
diff --git a/test/008-exceptions/src/Main.java b/test/008-exceptions/src/Main.java
index 7f6d0c5956..c0502047d0 100644
--- a/test/008-exceptions/src/Main.java
+++ b/test/008-exceptions/src/Main.java
@@ -14,20 +14,38 @@
* limitations under the License.
*/
-// An exception that doesn't have a <init>(String) method.
+// An error class.
class BadError extends Error {
- public BadError() {
- super("This is bad by convention");
+ public BadError(String s) {
+ super("This is bad by convention: " + s);
}
}
-// A class that throws BadException during static initialization.
+// A class that throws BadError during static initialization.
class BadInit {
static int dummy;
static {
System.out.println("Static Init");
if (true) {
- throw new BadError();
+ throw new BadError("BadInit");
+ }
+ }
+}
+
+// An error that doesn't have a <init>(String) method.
+class BadErrorNoStringInit extends Error {
+ public BadErrorNoStringInit() {
+ super("This is bad by convention");
+ }
+}
+
+// A class that throws BadErrorNoStringInit during static initialization.
+class BadInitNoStringInit {
+ static int dummy;
+ static {
+ System.out.println("Static BadInitNoStringInit");
+ if (true) {
+ throw new BadErrorNoStringInit();
}
}
}
@@ -48,6 +66,7 @@ public class Main {
public static void main (String args[]) {
exceptions_007();
exceptionsRethrowClassInitFailure();
+ exceptionsRethrowClassInitFailureNoStringInit();
}
private static void catchAndRethrow() {
@@ -86,4 +105,26 @@ public class Main {
error.printStackTrace();
}
}
+
+ private static void exceptionsRethrowClassInitFailureNoStringInit() {
+ try {
+ try {
+ BadInitNoStringInit.dummy = 1;
+ throw new IllegalStateException("Should not reach here.");
+ } catch (BadErrorNoStringInit e) {
+ System.out.println(e);
+ }
+
+ // Check if it works a second time.
+
+ try {
+ BadInitNoStringInit.dummy = 1;
+ throw new IllegalStateException("Should not reach here.");
+ } catch (BadErrorNoStringInit e) {
+ System.out.println(e);
+ }
+ } catch (Exception error) {
+ error.printStackTrace();
+ }
+ }
}
diff --git a/test/537-checker-jump-over-jump/expected.txt b/test/537-checker-jump-over-jump/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/537-checker-jump-over-jump/expected.txt
diff --git a/test/537-checker-jump-over-jump/info.txt b/test/537-checker-jump-over-jump/info.txt
new file mode 100644
index 0000000000..aeb30bb90f
--- /dev/null
+++ b/test/537-checker-jump-over-jump/info.txt
@@ -0,0 +1 @@
+Test for X86-64 elimination of jump over jump.
diff --git a/test/537-checker-jump-over-jump/src/Main.java b/test/537-checker-jump-over-jump/src/Main.java
new file mode 100644
index 0000000000..fb666eaaea
--- /dev/null
+++ b/test/537-checker-jump-over-jump/src/Main.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+
+public class Main {
+ public static int FIBCOUNT = 64;
+ public static int[] fibs;
+
+ /// CHECK-START-X86_64: int Main.test() disassembly (after)
+ /// CHECK: If
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: jnl/ge
+ /// CHECK-NOT: jmp
+ /// CHECK: ArrayGet
+ // Checks that there is no conditional jump over a jmp. The ArrayGet is in
+ // the next block.
+ public static int test() {
+ for (int i = 1; ; i++) {
+ if (i >= FIBCOUNT) {
+ return fibs[0];
+ }
+ fibs[i] = (i + fibs[(i - 1)]);
+ }
+ }
+
+ public static void main(String[] args) {
+ fibs = new int[FIBCOUNT];
+ fibs[0] = 1;
+ test();
+ }
+}
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 631e0a0c59..047c24f8aa 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -21,7 +21,7 @@ fi
out_dir=${OUT_DIR-out}
java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES
-common_targets="vogar vogar.jar ${java_libraries_dir}/core-tests_intermediates/javalib.jar apache-harmony-jdwp-tests-hostdex ${java_libraries_dir}/jsr166-tests_intermediates/javalib.jar"
+common_targets="vogar ${java_libraries_dir}/core-tests_intermediates/javalib.jar apache-harmony-jdwp-tests-hostdex ${java_libraries_dir}/jsr166-tests_intermediates/javalib.jar"
mode="target"
j_arg="-j$(nproc)"
showcommands=