diff options
34 files changed, 1104 insertions, 514 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/dwarf/debug_frame_opcode_writer.h b/compiler/dwarf/debug_frame_opcode_writer.h index 60241f722c..5a9964178a 100644 --- a/compiler/dwarf/debug_frame_opcode_writer.h +++ b/compiler/dwarf/debug_frame_opcode_writer.h @@ -282,7 +282,12 @@ class DebugFrameOpCodeWriter : private Writer<Vector> { bool IsEnabled() const { return enabled_; } - void SetEnabled(bool value) { enabled_ = value; } + void SetEnabled(bool value) { + enabled_ = value; + if (enabled_ && opcodes_.capacity() == 0u) { + opcodes_.reserve(kDefaultCapacity); + } + } int GetCurrentPC() const { return current_pc_; } @@ -292,24 +297,24 @@ class DebugFrameOpCodeWriter : private Writer<Vector> { using Writer<Vector>::data; - DebugFrameOpCodeWriter(bool enabled = true, - const typename Vector::allocator_type& alloc = - typename Vector::allocator_type()) + explicit DebugFrameOpCodeWriter(bool enabled = true, + const typename Vector::allocator_type& alloc = + typename Vector::allocator_type()) : Writer<Vector>(&opcodes_), - enabled_(enabled), + enabled_(false), opcodes_(alloc), current_cfa_offset_(0), current_pc_(0), uses_dwarf3_features_(false) { - if (enabled) { - // Best guess based on couple of observed outputs. - opcodes_.reserve(16); - } + SetEnabled(enabled); } virtual ~DebugFrameOpCodeWriter() { } protected: + // Best guess based on couple of observed outputs. + static constexpr size_t kDefaultCapacity = 32u; + int FactorDataOffset(int offset) const { DCHECK_EQ(offset % kDataAlignmentFactor, 0); return offset / kDataAlignmentFactor; 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/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc index 0bfe8a276a..8832c84ac7 100644 --- a/compiler/jni/jni_cfi_test.cc +++ b/compiler/jni/jni_cfi_test.cc @@ -51,6 +51,7 @@ class JNICFITest : public CFITest { // Assemble the method. std::unique_ptr<Assembler> jni_asm(Assembler::Create(isa)); + jni_asm->cfi().SetEnabled(true); jni_asm->BuildFrame(frame_size, mr_conv->MethodRegister(), callee_save_regs, mr_conv->EntrySpills()); jni_asm->IncreaseFrameSize(32); 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/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index 05c6b2ca57..34f1fe5949 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -23,6 +23,8 @@ #include "optimizing/code_generator.h" #include "optimizing/optimizing_unit_test.h" #include "utils/assembler.h" +#include "utils/arm/assembler_thumb2.h" +#include "utils/mips/assembler_mips.h" #include "optimizing/optimizing_cfi_test_expected.inc" @@ -36,52 +38,62 @@ class OptimizingCFITest : public CFITest { // Enable this flag to generate the expected outputs. static constexpr bool kGenerateExpected = false; - void TestImpl(InstructionSet isa, const char* isa_str, - const std::vector<uint8_t>& expected_asm, - const std::vector<uint8_t>& expected_cfi) { + OptimizingCFITest() + : pool_(), + allocator_(&pool_), + opts_(), + isa_features_(), + graph_(nullptr), + code_gen_(), + blocks_(allocator_.Adapter()) {} + + void SetUpFrame(InstructionSet isa) { // Setup simple context. - ArenaPool pool; - ArenaAllocator allocator(&pool); - CompilerOptions opts; - std::unique_ptr<const InstructionSetFeatures> isa_features; std::string error; - isa_features.reset(InstructionSetFeatures::FromVariant(isa, "default", &error)); - HGraph* graph = CreateGraph(&allocator); + isa_features_.reset(InstructionSetFeatures::FromVariant(isa, "default", &error)); + graph_ = CreateGraph(&allocator_); // Generate simple frame with some spills. - std::unique_ptr<CodeGenerator> code_gen( - CodeGenerator::Create(graph, isa, *isa_features.get(), opts)); + code_gen_.reset(CodeGenerator::Create(graph_, isa, *isa_features_, opts_)); + code_gen_->GetAssembler()->cfi().SetEnabled(true); const int frame_size = 64; int core_reg = 0; int fp_reg = 0; for (int i = 0; i < 2; i++) { // Two registers of each kind. for (; core_reg < 32; core_reg++) { - if (code_gen->IsCoreCalleeSaveRegister(core_reg)) { + if (code_gen_->IsCoreCalleeSaveRegister(core_reg)) { auto location = Location::RegisterLocation(core_reg); - code_gen->AddAllocatedRegister(location); + code_gen_->AddAllocatedRegister(location); core_reg++; break; } } for (; fp_reg < 32; fp_reg++) { - if (code_gen->IsFloatingPointCalleeSaveRegister(fp_reg)) { + if (code_gen_->IsFloatingPointCalleeSaveRegister(fp_reg)) { auto location = Location::FpuRegisterLocation(fp_reg); - code_gen->AddAllocatedRegister(location); + code_gen_->AddAllocatedRegister(location); fp_reg++; break; } } } - ArenaVector<HBasicBlock*> blocks(allocator.Adapter()); - code_gen->block_order_ = &blocks; - code_gen->ComputeSpillMask(); - code_gen->SetFrameSize(frame_size); - code_gen->GenerateFrameEntry(); - code_gen->GenerateFrameExit(); + code_gen_->block_order_ = &blocks_; + code_gen_->ComputeSpillMask(); + code_gen_->SetFrameSize(frame_size); + code_gen_->GenerateFrameEntry(); + } + + void Finish() { + code_gen_->GenerateFrameExit(); + code_gen_->Finalize(&code_allocator_); + } + + void Check(InstructionSet isa, + const char* isa_str, + const std::vector<uint8_t>& expected_asm, + const std::vector<uint8_t>& expected_cfi) { // Get the outputs. - InternalCodeAllocator code_allocator; - code_gen->Finalize(&code_allocator); - const std::vector<uint8_t>& actual_asm = code_allocator.GetMemory(); - Assembler* opt_asm = code_gen->GetAssembler(); + const std::vector<uint8_t>& actual_asm = code_allocator_.GetMemory(); + Assembler* opt_asm = code_gen_->GetAssembler(); const std::vector<uint8_t>& actual_cfi = *(opt_asm->cfi().data()); if (kGenerateExpected) { @@ -92,6 +104,19 @@ class OptimizingCFITest : public CFITest { } } + void TestImpl(InstructionSet isa, const char* + isa_str, + const std::vector<uint8_t>& expected_asm, + const std::vector<uint8_t>& expected_cfi) { + SetUpFrame(isa); + Finish(); + Check(isa, isa_str, expected_asm, expected_cfi); + } + + CodeGenerator* GetCodeGenerator() { + return code_gen_.get(); + } + private: class InternalCodeAllocator : public CodeAllocator { public: @@ -109,21 +134,83 @@ class OptimizingCFITest : public CFITest { DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator); }; + + ArenaPool pool_; + ArenaAllocator allocator_; + CompilerOptions opts_; + std::unique_ptr<const InstructionSetFeatures> isa_features_; + HGraph* graph_; + std::unique_ptr<CodeGenerator> code_gen_; + ArenaVector<HBasicBlock*> blocks_; + InternalCodeAllocator code_allocator_; }; -#define TEST_ISA(isa) \ - TEST_F(OptimizingCFITest, isa) { \ - std::vector<uint8_t> expected_asm(expected_asm_##isa, \ - expected_asm_##isa + arraysize(expected_asm_##isa)); \ - std::vector<uint8_t> expected_cfi(expected_cfi_##isa, \ - expected_cfi_##isa + arraysize(expected_cfi_##isa)); \ - TestImpl(isa, #isa, expected_asm, expected_cfi); \ +#define TEST_ISA(isa) \ + TEST_F(OptimizingCFITest, isa) { \ + std::vector<uint8_t> expected_asm( \ + expected_asm_##isa, \ + expected_asm_##isa + arraysize(expected_asm_##isa)); \ + std::vector<uint8_t> expected_cfi( \ + expected_cfi_##isa, \ + expected_cfi_##isa + arraysize(expected_cfi_##isa)); \ + TestImpl(isa, #isa, expected_asm, expected_cfi); \ } TEST_ISA(kThumb2) TEST_ISA(kArm64) TEST_ISA(kX86) TEST_ISA(kX86_64) +TEST_ISA(kMips) +TEST_ISA(kMips64) + +TEST_F(OptimizingCFITest, kThumb2Adjust) { + std::vector<uint8_t> expected_asm( + expected_asm_kThumb2_adjust, + expected_asm_kThumb2_adjust + arraysize(expected_asm_kThumb2_adjust)); + std::vector<uint8_t> expected_cfi( + expected_cfi_kThumb2_adjust, + expected_cfi_kThumb2_adjust + arraysize(expected_cfi_kThumb2_adjust)); + SetUpFrame(kThumb2); +#define __ down_cast<arm::Thumb2Assembler*>(GetCodeGenerator()->GetAssembler())-> + Label target; + __ CompareAndBranchIfZero(arm::R0, &target); + // Push the target out of range of CBZ. + for (size_t i = 0; i != 65; ++i) { + __ ldr(arm::R0, arm::Address(arm::R0)); + } + __ Bind(&target); +#undef __ + Finish(); + Check(kThumb2, "kThumb2_adjust", expected_asm, expected_cfi); +} + +TEST_F(OptimizingCFITest, kMipsAdjust) { + // One NOP in delay slot, 1 << 15 NOPS have size 1 << 17 which exceeds 18-bit signed maximum. + static constexpr size_t kNumNops = 1u + (1u << 15); + std::vector<uint8_t> expected_asm( + expected_asm_kMips_adjust_head, + expected_asm_kMips_adjust_head + arraysize(expected_asm_kMips_adjust_head)); + expected_asm.resize(expected_asm.size() + kNumNops * 4u, 0u); + expected_asm.insert( + expected_asm.end(), + expected_asm_kMips_adjust_tail, + expected_asm_kMips_adjust_tail + arraysize(expected_asm_kMips_adjust_tail)); + std::vector<uint8_t> expected_cfi( + expected_cfi_kMips_adjust, + expected_cfi_kMips_adjust + arraysize(expected_cfi_kMips_adjust)); + SetUpFrame(kMips); +#define __ down_cast<mips::MipsAssembler*>(GetCodeGenerator()->GetAssembler())-> + mips::MipsLabel target; + __ Beqz(mips::A0, &target); + // Push the target out of range of BEQZ. + for (size_t i = 0; i != kNumNops; ++i) { + __ Nop(); + } + __ Bind(&target); +#undef __ + Finish(); + Check(kMips, "kMips_adjust", expected_asm, expected_cfi); +} #endif // __ANDROID__ diff --git a/compiler/optimizing/optimizing_cfi_test_expected.inc b/compiler/optimizing/optimizing_cfi_test_expected.inc index 2c2c55f295..4571ebf2d4 100644 --- a/compiler/optimizing/optimizing_cfi_test_expected.inc +++ b/compiler/optimizing/optimizing_cfi_test_expected.inc @@ -138,3 +138,278 @@ static constexpr uint8_t expected_cfi_kX86_64[] = { // 0x0000002c: ret // 0x0000002d: .cfi_restore_state // 0x0000002d: .cfi_def_cfa_offset: 64 + +static constexpr uint8_t expected_asm_kMips[] = { + 0xE4, 0xFF, 0xBD, 0x27, 0x18, 0x00, 0xBF, 0xAF, 0x14, 0x00, 0xB1, 0xAF, + 0x10, 0x00, 0xB0, 0xAF, 0x08, 0x00, 0xB6, 0xE7, 0x0C, 0x00, 0xB7, 0xE7, + 0x00, 0x00, 0xB4, 0xE7, 0x04, 0x00, 0xB5, 0xE7, 0xDC, 0xFF, 0xBD, 0x27, + 0x00, 0x00, 0xA4, 0xAF, 0x24, 0x00, 0xBD, 0x27, 0x00, 0x00, 0xB4, 0xC7, + 0x04, 0x00, 0xB5, 0xC7, 0x08, 0x00, 0xB6, 0xC7, 0x0C, 0x00, 0xB7, 0xC7, + 0x10, 0x00, 0xB0, 0x8F, 0x14, 0x00, 0xB1, 0x8F, 0x18, 0x00, 0xBF, 0x8F, + 0x1C, 0x00, 0xBD, 0x27, 0x09, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, +}; +static constexpr uint8_t expected_cfi_kMips[] = { + 0x44, 0x0E, 0x1C, 0x44, 0x9F, 0x01, 0x44, 0x91, 0x02, 0x44, 0x90, 0x03, + 0x54, 0x0E, 0x40, 0x44, 0x0A, 0x44, 0x0E, 0x1C, 0x54, 0xD0, 0x44, 0xD1, + 0x44, 0xDF, 0x44, 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40, +}; +// 0x00000000: addiu r29, r29, -28 +// 0x00000004: .cfi_def_cfa_offset: 28 +// 0x00000004: sw r31, +24(r29) +// 0x00000008: .cfi_offset: r31 at cfa-4 +// 0x00000008: sw r17, +20(r29) +// 0x0000000c: .cfi_offset: r17 at cfa-8 +// 0x0000000c: sw r16, +16(r29) +// 0x00000010: .cfi_offset: r16 at cfa-12 +// 0x00000010: swc1 f22, +8(r29) +// 0x00000014: swc1 f23, +12(r29) +// 0x00000018: swc1 f20, +0(r29) +// 0x0000001c: swc1 f21, +4(r29) +// 0x00000020: addiu r29, r29, -36 +// 0x00000024: .cfi_def_cfa_offset: 64 +// 0x00000024: sw r4, +0(r29) +// 0x00000028: .cfi_remember_state +// 0x00000028: addiu r29, r29, 36 +// 0x0000002c: .cfi_def_cfa_offset: 28 +// 0x0000002c: lwc1 f20, +0(r29) +// 0x00000030: lwc1 f21, +4(r29) +// 0x00000034: lwc1 f22, +8(r29) +// 0x00000038: lwc1 f23, +12(r29) +// 0x0000003c: lw r16, +16(r29) +// 0x00000040: .cfi_restore: r16 +// 0x00000040: lw r17, +20(r29) +// 0x00000044: .cfi_restore: r17 +// 0x00000044: lw r31, +24(r29) +// 0x00000048: .cfi_restore: r31 +// 0x00000048: addiu r29, r29, 28 +// 0x0000004c: .cfi_def_cfa_offset: 0 +// 0x0000004c: jr r31 +// 0x00000050: nop +// 0x00000054: .cfi_restore_state +// 0x00000054: .cfi_def_cfa_offset: 64 + +static constexpr uint8_t expected_asm_kMips64[] = { + 0xD8, 0xFF, 0xBD, 0x67, 0x20, 0x00, 0xBF, 0xFF, 0x18, 0x00, 0xB1, 0xFF, + 0x10, 0x00, 0xB0, 0xFF, 0x08, 0x00, 0xB9, 0xF7, 0x00, 0x00, 0xB8, 0xF7, + 0xE8, 0xFF, 0xBD, 0x67, 0x00, 0x00, 0xA4, 0xFF, 0x18, 0x00, 0xBD, 0x67, + 0x00, 0x00, 0xB8, 0xD7, 0x08, 0x00, 0xB9, 0xD7, 0x10, 0x00, 0xB0, 0xDF, + 0x18, 0x00, 0xB1, 0xDF, 0x20, 0x00, 0xBF, 0xDF, 0x28, 0x00, 0xBD, 0x67, + 0x09, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, +}; +static constexpr uint8_t expected_cfi_kMips64[] = { + 0x44, 0x0E, 0x28, 0x44, 0x9F, 0x02, 0x44, 0x91, 0x04, 0x44, 0x90, 0x06, + 0x4C, 0x0E, 0x40, 0x44, 0x0A, 0x44, 0x0E, 0x28, 0x4C, 0xD0, 0x44, 0xD1, + 0x44, 0xDF, 0x44, 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40, +}; +// 0x00000000: daddiu r29, r29, -40 +// 0x00000004: .cfi_def_cfa_offset: 40 +// 0x00000004: sd r31, +32(r29) +// 0x00000008: .cfi_offset: r31 at cfa-8 +// 0x00000008: sd r17, +24(r29) +// 0x0000000c: .cfi_offset: r17 at cfa-16 +// 0x0000000c: sd r16, +16(r29) +// 0x00000010: .cfi_offset: r16 at cfa-24 +// 0x00000010: sdc1 f25, +8(r29) +// 0x00000014: sdc1 f24, +0(r29) +// 0x00000018: daddiu r29, r29, -24 +// 0x0000001c: .cfi_def_cfa_offset: 64 +// 0x0000001c: sd r4, +0(r29) +// 0x00000020: .cfi_remember_state +// 0x00000020: daddiu r29, r29, 24 +// 0x00000024: .cfi_def_cfa_offset: 40 +// 0x00000024: ldc1 f24, +0(r29) +// 0x00000028: ldc1 f25, +8(r29) +// 0x0000002c: ld r16, +16(r29) +// 0x00000030: .cfi_restore: r16 +// 0x00000030: ld r17, +24(r29) +// 0x00000034: .cfi_restore: r17 +// 0x00000034: ld r31, +32(r29) +// 0x00000038: .cfi_restore: r31 +// 0x00000038: daddiu r29, r29, 40 +// 0x0000003c: .cfi_def_cfa_offset: 0 +// 0x0000003c: jr r31 +// 0x00000040: nop +// 0x00000044: .cfi_restore_state +// 0x00000044: .cfi_def_cfa_offset: 64 + +static constexpr uint8_t expected_asm_kThumb2_adjust[] = { + 0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x90, 0x00, 0x28, + 0x40, 0xD0, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, + 0x0B, 0xB0, 0xBD, 0xEC, 0x02, 0x8A, 0x60, 0xBD, +}; +static constexpr uint8_t expected_cfi_kThumb2_adjust[] = { + 0x42, 0x0E, 0x0C, 0x85, 0x03, 0x86, 0x02, 0x8E, 0x01, 0x44, 0x0E, 0x14, + 0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x02, 0x88, 0x0A, + 0x42, 0x0E, 0x14, 0x44, 0x0E, 0x0C, 0x06, 0x50, 0x06, 0x51, 0x42, 0x0B, + 0x0E, 0x40, +}; +// 0x00000000: push {r5, r6, lr} +// 0x00000002: .cfi_def_cfa_offset: 12 +// 0x00000002: .cfi_offset: r5 at cfa-12 +// 0x00000002: .cfi_offset: r6 at cfa-8 +// 0x00000002: .cfi_offset: r14 at cfa-4 +// 0x00000002: vpush.f32 {s16-s17} +// 0x00000006: .cfi_def_cfa_offset: 20 +// 0x00000006: .cfi_offset_extended: r80 at cfa-20 +// 0x00000006: .cfi_offset_extended: r81 at cfa-16 +// 0x00000006: sub sp, sp, #44 +// 0x00000008: .cfi_def_cfa_offset: 64 +// 0x00000008: str r0, [sp, #0] +// 0x0000000a: cmp r0, #0 +// 0x0000000c: beq +128 (0x00000090) +// 0x0000000e: ldr r0, [r0, #0] +// 0x00000010: ldr r0, [r0, #0] +// 0x00000012: ldr r0, [r0, #0] +// 0x00000014: ldr r0, [r0, #0] +// 0x00000016: ldr r0, [r0, #0] +// 0x00000018: ldr r0, [r0, #0] +// 0x0000001a: ldr r0, [r0, #0] +// 0x0000001c: ldr r0, [r0, #0] +// 0x0000001e: ldr r0, [r0, #0] +// 0x00000020: ldr r0, [r0, #0] +// 0x00000022: ldr r0, [r0, #0] +// 0x00000024: ldr r0, [r0, #0] +// 0x00000026: ldr r0, [r0, #0] +// 0x00000028: ldr r0, [r0, #0] +// 0x0000002a: ldr r0, [r0, #0] +// 0x0000002c: ldr r0, [r0, #0] +// 0x0000002e: ldr r0, [r0, #0] +// 0x00000030: ldr r0, [r0, #0] +// 0x00000032: ldr r0, [r0, #0] +// 0x00000034: ldr r0, [r0, #0] +// 0x00000036: ldr r0, [r0, #0] +// 0x00000038: ldr r0, [r0, #0] +// 0x0000003a: ldr r0, [r0, #0] +// 0x0000003c: ldr r0, [r0, #0] +// 0x0000003e: ldr r0, [r0, #0] +// 0x00000040: ldr r0, [r0, #0] +// 0x00000042: ldr r0, [r0, #0] +// 0x00000044: ldr r0, [r0, #0] +// 0x00000046: ldr r0, [r0, #0] +// 0x00000048: ldr r0, [r0, #0] +// 0x0000004a: ldr r0, [r0, #0] +// 0x0000004c: ldr r0, [r0, #0] +// 0x0000004e: ldr r0, [r0, #0] +// 0x00000050: ldr r0, [r0, #0] +// 0x00000052: ldr r0, [r0, #0] +// 0x00000054: ldr r0, [r0, #0] +// 0x00000056: ldr r0, [r0, #0] +// 0x00000058: ldr r0, [r0, #0] +// 0x0000005a: ldr r0, [r0, #0] +// 0x0000005c: ldr r0, [r0, #0] +// 0x0000005e: ldr r0, [r0, #0] +// 0x00000060: ldr r0, [r0, #0] +// 0x00000062: ldr r0, [r0, #0] +// 0x00000064: ldr r0, [r0, #0] +// 0x00000066: ldr r0, [r0, #0] +// 0x00000068: ldr r0, [r0, #0] +// 0x0000006a: ldr r0, [r0, #0] +// 0x0000006c: ldr r0, [r0, #0] +// 0x0000006e: ldr r0, [r0, #0] +// 0x00000070: ldr r0, [r0, #0] +// 0x00000072: ldr r0, [r0, #0] +// 0x00000074: ldr r0, [r0, #0] +// 0x00000076: ldr r0, [r0, #0] +// 0x00000078: ldr r0, [r0, #0] +// 0x0000007a: ldr r0, [r0, #0] +// 0x0000007c: ldr r0, [r0, #0] +// 0x0000007e: ldr r0, [r0, #0] +// 0x00000080: ldr r0, [r0, #0] +// 0x00000082: ldr r0, [r0, #0] +// 0x00000084: ldr r0, [r0, #0] +// 0x00000086: ldr r0, [r0, #0] +// 0x00000088: ldr r0, [r0, #0] +// 0x0000008a: ldr r0, [r0, #0] +// 0x0000008c: ldr r0, [r0, #0] +// 0x0000008e: ldr r0, [r0, #0] +// 0x00000090: .cfi_remember_state +// 0x00000090: add sp, sp, #44 +// 0x00000092: .cfi_def_cfa_offset: 20 +// 0x00000092: vpop.f32 {s16-s17} +// 0x00000096: .cfi_def_cfa_offset: 12 +// 0x00000096: .cfi_restore_extended: r80 +// 0x00000096: .cfi_restore_extended: r81 +// 0x00000096: pop {r5, r6, pc} +// 0x00000098: .cfi_restore_state +// 0x00000098: .cfi_def_cfa_offset: 64 + +static constexpr uint8_t expected_asm_kMips_adjust_head[] = { + 0xE4, 0xFF, 0xBD, 0x27, 0x18, 0x00, 0xBF, 0xAF, 0x14, 0x00, 0xB1, 0xAF, + 0x10, 0x00, 0xB0, 0xAF, 0x08, 0x00, 0xB6, 0xE7, 0x0C, 0x00, 0xB7, 0xE7, + 0x00, 0x00, 0xB4, 0xE7, 0x04, 0x00, 0xB5, 0xE7, 0xDC, 0xFF, 0xBD, 0x27, + 0x00, 0x00, 0xA4, 0xAF, 0x08, 0x00, 0x04, 0x14, 0xFC, 0xFF, 0xBD, 0x27, + 0x00, 0x00, 0xBF, 0xAF, 0x00, 0x00, 0x10, 0x04, 0x02, 0x00, 0x01, 0x3C, + 0x18, 0x00, 0x21, 0x34, 0x21, 0x08, 0x3F, 0x00, 0x00, 0x00, 0xBF, 0x8F, + 0x09, 0x00, 0x20, 0x00, 0x04, 0x00, 0xBD, 0x27, +}; +static constexpr uint8_t expected_asm_kMips_adjust_tail[] = { + 0x24, 0x00, 0xBD, 0x27, 0x00, 0x00, 0xB4, 0xC7, 0x04, 0x00, 0xB5, 0xC7, + 0x08, 0x00, 0xB6, 0xC7, 0x0C, 0x00, 0xB7, 0xC7, 0x10, 0x00, 0xB0, 0x8F, + 0x14, 0x00, 0xB1, 0x8F, 0x18, 0x00, 0xBF, 0x8F, 0x1C, 0x00, 0xBD, 0x27, + 0x09, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, +}; +static constexpr uint8_t expected_cfi_kMips_adjust[] = { + 0x44, 0x0E, 0x1C, 0x44, 0x9F, 0x01, 0x44, 0x91, 0x02, 0x44, 0x90, 0x03, + 0x54, 0x0E, 0x40, 0x4C, 0x0E, 0x44, 0x60, 0x0E, 0x40, 0x04, 0x04, 0x00, + 0x02, 0x00, 0x0A, 0x44, 0x0E, 0x1C, 0x54, 0xD0, 0x44, 0xD1, 0x44, 0xDF, + 0x44, 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40, +}; +// 0x00000000: addiu r29, r29, -28 +// 0x00000004: .cfi_def_cfa_offset: 28 +// 0x00000004: sw r31, +24(r29) +// 0x00000008: .cfi_offset: r31 at cfa-4 +// 0x00000008: sw r17, +20(r29) +// 0x0000000c: .cfi_offset: r17 at cfa-8 +// 0x0000000c: sw r16, +16(r29) +// 0x00000010: .cfi_offset: r16 at cfa-12 +// 0x00000010: swc1 f22, +8(r29) +// 0x00000014: swc1 f23, +12(r29) +// 0x00000018: swc1 f20, +0(r29) +// 0x0000001c: swc1 f21, +4(r29) +// 0x00000020: addiu r29, r29, -36 +// 0x00000024: .cfi_def_cfa_offset: 64 +// 0x00000024: sw r4, +0(r29) +// 0x00000028: bne r0, r4, 0x0000004c ; +36 +// 0x0000002c: addiu r29, r29, -4 +// 0x00000030: .cfi_def_cfa_offset: 68 +// 0x00000030: sw r31, +0(r29) +// 0x00000034: bltzal r0, 0x00000038 ; +4 +// 0x00000038: lui r1, 0x20000 +// 0x0000003c: ori r1, r1, 24 +// 0x00000040: addu r1, r1, r31 +// 0x00000044: lw r31, +0(r29) +// 0x00000048: jr r1 +// 0x0000004c: addiu r29, r29, 4 +// 0x00000050: .cfi_def_cfa_offset: 64 +// 0x00000050: nop +// ... +// 0x00020050: nop +// 0x00020054: .cfi_remember_state +// 0x00020054: addiu r29, r29, 36 +// 0x00020058: .cfi_def_cfa_offset: 28 +// 0x00020058: lwc1 f20, +0(r29) +// 0x0002005c: lwc1 f21, +4(r29) +// 0x00020060: lwc1 f22, +8(r29) +// 0x00020064: lwc1 f23, +12(r29) +// 0x00020068: lw r16, +16(r29) +// 0x0002006c: .cfi_restore: r16 +// 0x0002006c: lw r17, +20(r29) +// 0x00020070: .cfi_restore: r17 +// 0x00020070: lw r31, +24(r29) +// 0x00020074: .cfi_restore: r31 +// 0x00020074: addiu r29, r29, 28 +// 0x00020078: .cfi_def_cfa_offset: 0 +// 0x00020078: jr r31 +// 0x0002007c: nop +// 0x00020080: .cfi_restore_state +// 0x00020080: .cfi_def_cfa_offset: 64 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/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index fb3aa1ea85..297cc54e29 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -282,6 +282,32 @@ void Thumb2Assembler::EmitJumpTables() { } } +void Thumb2Assembler::PatchCFI() { + if (cfi().NumberOfDelayedAdvancePCs() == 0u) { + return; + } + + typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC; + const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC(); + const std::vector<uint8_t>& old_stream = data.first; + const std::vector<DelayedAdvancePC>& advances = data.second; + + // Refill our data buffer with patched opcodes. + cfi().ReserveCFIStream(old_stream.size() + advances.size() + 16); + size_t stream_pos = 0; + for (const DelayedAdvancePC& advance : advances) { + DCHECK_GE(advance.stream_pos, stream_pos); + // Copy old data up to the point where advance was issued. + cfi().AppendRawData(old_stream, stream_pos, advance.stream_pos); + stream_pos = advance.stream_pos; + // Insert the advance command with its final offset. + size_t final_pc = GetAdjustedPosition(advance.pc); + cfi().AdvancePC(final_pc); + } + // Copy the final segment if any. + cfi().AppendRawData(old_stream, stream_pos, old_stream.size()); +} + inline int16_t Thumb2Assembler::BEncoding16(int32_t offset, Condition cond) { DCHECK_ALIGNED(offset, 2); int16_t encoding = B15 | B14; @@ -463,6 +489,7 @@ void Thumb2Assembler::FinalizeCode() { EmitLiterals(); FinalizeTrackedLabels(); EmitJumpTables(); + PatchCFI(); } bool Thumb2Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) { diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index 38fd244087..e18361300a 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -44,6 +44,7 @@ class Thumb2Assembler FINAL : public ArmAssembler { last_position_adjustment_(0u), last_old_position_(0u), last_fixup_id_(0u) { + cfi().DelayEmittingAdvancePCs(); } virtual ~Thumb2Assembler() { @@ -792,6 +793,7 @@ class Thumb2Assembler FINAL : public ArmAssembler { void EmitFixups(uint32_t adjusted_code_size); void EmitLiterals(); void EmitJumpTables(); + void PatchCFI(); static int16_t BEncoding16(int32_t offset, Condition cond); static int32_t BEncoding32(int32_t offset, Condition cond); diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc index b01b0fe4e0..f784d2c3f8 100644 --- a/compiler/utils/assembler.cc +++ b/compiler/utils/assembler.cc @@ -38,6 +38,7 @@ #ifdef ART_ENABLE_CODEGEN_x86_64 #include "x86_64/assembler_x86_64.h" #endif +#include "base/casts.h" #include "globals.h" #include "memory_region.h" @@ -119,7 +120,13 @@ void AssemblerBuffer::ExtendCapacity(size_t min_capacity) { } void DebugFrameOpCodeWriterForAssembler::ImplicitlyAdvancePC() { - this->AdvancePC(assembler_->CodeSize()); + uint32_t pc = dchecked_integral_cast<uint32_t>(assembler_->CodeSize()); + if (delay_emitting_advance_pc_) { + uint32_t stream_pos = dchecked_integral_cast<uint32_t>(opcodes_.size()); + delayed_advance_pcs_.push_back(DelayedAdvancePC {stream_pos, pc}); + } else { + AdvancePC(pc); + } } Assembler* Assembler::Create(InstructionSet instruction_set, diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index dfe6babb25..1dbc142391 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -271,16 +271,71 @@ class AssemblerBuffer { class DebugFrameOpCodeWriterForAssembler FINAL : public dwarf::DebugFrameOpCodeWriter<> { public: + struct DelayedAdvancePC { + uint32_t stream_pos; + uint32_t pc; + }; + // This method is called the by the opcode writers. virtual void ImplicitlyAdvancePC() FINAL; explicit DebugFrameOpCodeWriterForAssembler(Assembler* buffer) - : dwarf::DebugFrameOpCodeWriter<>(), - assembler_(buffer) { + : dwarf::DebugFrameOpCodeWriter<>(false /* enabled */), + assembler_(buffer), + delay_emitting_advance_pc_(false), + delayed_advance_pcs_() { + } + + ~DebugFrameOpCodeWriterForAssembler() { + DCHECK(delayed_advance_pcs_.empty()); + } + + // Tell the writer to delay emitting advance PC info. + // The assembler must explicitly process all the delayed advances. + void DelayEmittingAdvancePCs() { + delay_emitting_advance_pc_ = true; + } + + // Override the last delayed PC. The new PC can be out of order. + void OverrideDelayedPC(size_t pc) { + DCHECK(delay_emitting_advance_pc_); + DCHECK(!delayed_advance_pcs_.empty()); + delayed_advance_pcs_.back().pc = pc; + } + + // Return the number of delayed advance PC entries. + size_t NumberOfDelayedAdvancePCs() const { + return delayed_advance_pcs_.size(); + } + + // Release the CFI stream and advance PC infos so that the assembler can patch it. + std::pair<std::vector<uint8_t>, std::vector<DelayedAdvancePC>> + ReleaseStreamAndPrepareForDelayedAdvancePC() { + DCHECK(delay_emitting_advance_pc_); + delay_emitting_advance_pc_ = false; + std::pair<std::vector<uint8_t>, std::vector<DelayedAdvancePC>> result; + result.first.swap(opcodes_); + result.second.swap(delayed_advance_pcs_); + return result; + } + + // Reserve space for the CFI stream. + void ReserveCFIStream(size_t capacity) { + opcodes_.reserve(capacity); + } + + // Append raw data to the CFI stream. + void AppendRawData(const std::vector<uint8_t>& raw_data, size_t first, size_t last) { + DCHECK_LE(0u, first); + DCHECK_LE(first, last); + DCHECK_LE(last, raw_data.size()); + opcodes_.insert(opcodes_.end(), raw_data.begin() + first, raw_data.begin() + last); } private: Assembler* assembler_; + bool delay_emitting_advance_pc_; + std::vector<DelayedAdvancePC> delayed_advance_pcs_; }; class Assembler { diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index 6f35e9ef59..aee64120a8 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -43,8 +43,60 @@ void MipsAssembler::FinalizeCode() { } void MipsAssembler::FinalizeInstructions(const MemoryRegion& region) { + size_t number_of_delayed_adjust_pcs = cfi().NumberOfDelayedAdvancePCs(); EmitBranches(); Assembler::FinalizeInstructions(region); + PatchCFI(number_of_delayed_adjust_pcs); +} + +void MipsAssembler::PatchCFI(size_t number_of_delayed_adjust_pcs) { + if (cfi().NumberOfDelayedAdvancePCs() == 0u) { + DCHECK_EQ(number_of_delayed_adjust_pcs, 0u); + return; + } + + typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC; + const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC(); + const std::vector<uint8_t>& old_stream = data.first; + const std::vector<DelayedAdvancePC>& advances = data.second; + + // PCs recorded before EmitBranches() need to be adjusted. + // PCs recorded during EmitBranches() are already adjusted. + // Both ranges are separately sorted but they may overlap. + if (kIsDebugBuild) { + auto cmp = [](const DelayedAdvancePC& lhs, const DelayedAdvancePC& rhs) { + return lhs.pc < rhs.pc; + }; + CHECK(std::is_sorted(advances.begin(), advances.begin() + number_of_delayed_adjust_pcs, cmp)); + CHECK(std::is_sorted(advances.begin() + number_of_delayed_adjust_pcs, advances.end(), cmp)); + } + + // Append initial CFI data if any. + size_t size = advances.size(); + DCHECK_NE(size, 0u); + cfi().AppendRawData(old_stream, 0u, advances[0].stream_pos); + // Emit PC adjustments interleaved with the old CFI stream. + size_t adjust_pos = 0u; + size_t late_emit_pos = number_of_delayed_adjust_pcs; + while (adjust_pos != number_of_delayed_adjust_pcs || late_emit_pos != size) { + size_t adjusted_pc = (adjust_pos != number_of_delayed_adjust_pcs) + ? GetAdjustedPosition(advances[adjust_pos].pc) + : static_cast<size_t>(-1); + size_t late_emit_pc = (late_emit_pos != size) + ? advances[late_emit_pos].pc + : static_cast<size_t>(-1); + size_t advance_pc = std::min(adjusted_pc, late_emit_pc); + DCHECK_NE(advance_pc, static_cast<size_t>(-1)); + size_t entry = (adjusted_pc <= late_emit_pc) ? adjust_pos : late_emit_pos; + if (adjusted_pc <= late_emit_pc) { + ++adjust_pos; + } else { + ++late_emit_pos; + } + cfi().AdvancePC(advance_pc); + size_t end_pos = (entry + 1u == size) ? old_stream.size() : advances[entry + 1u].stream_pos; + cfi().AppendRawData(old_stream, advances[entry].stream_pos, end_pos); + } } void MipsAssembler::EmitBranches() { @@ -1770,6 +1822,7 @@ void MipsAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, const std::vector<ManagedRegister>& callee_save_regs, const ManagedRegisterEntrySpills& entry_spills) { CHECK_ALIGNED(frame_size, kStackAlignment); + DCHECK(!overwriting_); // Increase frame to required size. IncreaseFrameSize(frame_size); @@ -1811,6 +1864,7 @@ void MipsAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, void MipsAssembler::RemoveFrame(size_t frame_size, const std::vector<ManagedRegister>& callee_save_regs) { CHECK_ALIGNED(frame_size, kStackAlignment); + DCHECK(!overwriting_); cfi_.RememberState(); // Pop callee saves and return address. @@ -1840,12 +1894,18 @@ void MipsAssembler::IncreaseFrameSize(size_t adjust) { CHECK_ALIGNED(adjust, kFramePointerSize); Addiu32(SP, SP, -adjust); cfi_.AdjustCFAOffset(adjust); + if (overwriting_) { + cfi_.OverrideDelayedPC(overwrite_location_); + } } void MipsAssembler::DecreaseFrameSize(size_t adjust) { CHECK_ALIGNED(adjust, kFramePointerSize); Addiu32(SP, SP, adjust); cfi_.AdjustCFAOffset(-adjust); + if (overwriting_) { + cfi_.OverrideDelayedPC(overwrite_location_); + } } void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index aa187b812b..4038c1f1c4 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -94,7 +94,9 @@ class MipsAssembler FINAL : public Assembler { last_position_adjustment_(0), last_old_position_(0), last_branch_id_(0), - isa_features_(instruction_set_features) {} + isa_features_(instruction_set_features) { + cfi().DelayEmittingAdvancePCs(); + } virtual ~MipsAssembler() { for (auto& branch : branches_) { @@ -599,6 +601,7 @@ class MipsAssembler FINAL : public Assembler { void PromoteBranches(); void EmitBranch(Branch* branch); void EmitBranches(); + void PatchCFI(size_t number_of_delayed_adjust_pcs); // Emits exception block. void EmitExceptionPoll(MipsExceptionSlowPath* exception); 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/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= |