summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/compiler.h11
-rw-r--r--compiler/driver/compiler_driver.cc45
-rw-r--r--compiler/driver/compiler_driver.h3
-rw-r--r--compiler/dwarf/debug_frame_opcode_writer.h23
-rw-r--r--compiler/jit/jit_compiler.cc98
-rw-r--r--compiler/jni/jni_cfi_test.cc1
-rw-r--r--compiler/optimizing/code_generator.cc24
-rw-r--r--compiler/optimizing/code_generator.h22
-rw-r--r--compiler/optimizing/optimizing_cfi_test.cc151
-rw-r--r--compiler/optimizing/optimizing_cfi_test_expected.inc275
-rw-r--r--compiler/optimizing/optimizing_compiler.cc346
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc27
-rw-r--r--compiler/utils/arm/assembler_thumb2.h2
-rw-r--r--compiler/utils/assembler.cc9
-rw-r--r--compiler/utils/assembler.h59
-rw-r--r--compiler/utils/mips/assembler_mips.cc60
-rw-r--r--compiler/utils/mips/assembler_mips.h5
-rw-r--r--runtime/class_linker.cc56
-rw-r--r--runtime/class_linker.h3
-rw-r--r--runtime/class_linker_test.cc2
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc50
-rw-r--r--runtime/jit/jit.cc11
-rw-r--r--runtime/jit/jit_code_cache.cc78
-rw-r--r--runtime/jit/jit_code_cache.h5
-rw-r--r--runtime/jit/jit_instrumentation.cc82
-rw-r--r--runtime/jit/jit_instrumentation.h55
-rw-r--r--runtime/mirror/class-inl.h9
-rw-r--r--runtime/mirror/class.cc17
-rw-r--r--runtime/mirror/class.h11
-rw-r--r--runtime/thread_pool.cc5
-rw-r--r--runtime/thread_pool.h3
-rw-r--r--test/008-exceptions/expected.txt17
-rw-r--r--test/008-exceptions/src/Main.java51
-rwxr-xr-xtools/buildbot-build.sh2
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=