upload android base code part3
This commit is contained in:
parent
71b83c22f1
commit
b9e30e05b1
15122 changed files with 2089659 additions and 0 deletions
405
android/art/compiler/dex/dex_to_dex_compiler.cc
Normal file
405
android/art/compiler/dex/dex_to_dex_compiler.cc
Normal file
|
@ -0,0 +1,405 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "dex_to_dex_compiler.h"
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
|
||||
#include "art_field-inl.h"
|
||||
#include "art_method-inl.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mutex.h"
|
||||
#include "bytecode_utils.h"
|
||||
#include "compiled_method.h"
|
||||
#include "dex_file-inl.h"
|
||||
#include "dex_instruction-inl.h"
|
||||
#include "driver/compiler_driver.h"
|
||||
#include "driver/dex_compilation_unit.h"
|
||||
#include "mirror/dex_cache.h"
|
||||
#include "quicken_info.h"
|
||||
#include "thread-current-inl.h"
|
||||
|
||||
namespace art {
|
||||
namespace optimizer {
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
// Controls quickening activation.
|
||||
const bool kEnableQuickening = true;
|
||||
// Control check-cast elision.
|
||||
const bool kEnableCheckCastEllision = true;
|
||||
|
||||
struct QuickenedInfo {
|
||||
QuickenedInfo(uint32_t pc, uint16_t index) : dex_pc(pc), dex_member_index(index) {}
|
||||
|
||||
uint32_t dex_pc;
|
||||
uint16_t dex_member_index;
|
||||
};
|
||||
|
||||
class DexCompiler {
|
||||
public:
|
||||
DexCompiler(art::CompilerDriver& compiler,
|
||||
const DexCompilationUnit& unit,
|
||||
DexToDexCompilationLevel dex_to_dex_compilation_level)
|
||||
: driver_(compiler),
|
||||
unit_(unit),
|
||||
dex_to_dex_compilation_level_(dex_to_dex_compilation_level) {}
|
||||
|
||||
~DexCompiler() {}
|
||||
|
||||
void Compile();
|
||||
|
||||
const std::vector<QuickenedInfo>& GetQuickenedInfo() const {
|
||||
return quickened_info_;
|
||||
}
|
||||
|
||||
private:
|
||||
const DexFile& GetDexFile() const {
|
||||
return *unit_.GetDexFile();
|
||||
}
|
||||
|
||||
// Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
|
||||
// a barrier is required.
|
||||
void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
|
||||
|
||||
// Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In
|
||||
// this case, returns the second NOP instruction pointer. Otherwise, returns
|
||||
// the given "inst".
|
||||
Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc);
|
||||
|
||||
// Compiles a field access into a quick field access.
|
||||
// The field index is replaced by an offset within an Object where we can read
|
||||
// from / write to this field. Therefore, this does not involve any resolution
|
||||
// at runtime.
|
||||
// Since the field index is encoded with 16 bits, we can replace it only if the
|
||||
// field offset can be encoded with 16 bits too.
|
||||
void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc,
|
||||
Instruction::Code new_opcode, bool is_put);
|
||||
|
||||
// Compiles a virtual method invocation into a quick virtual method invocation.
|
||||
// The method index is replaced by the vtable index where the corresponding
|
||||
// Executable can be found. Therefore, this does not involve any resolution
|
||||
// at runtime.
|
||||
// Since the method index is encoded with 16 bits, we can replace it only if the
|
||||
// vtable index can be encoded with 16 bits too.
|
||||
void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
|
||||
Instruction::Code new_opcode, bool is_range);
|
||||
|
||||
CompilerDriver& driver_;
|
||||
const DexCompilationUnit& unit_;
|
||||
const DexToDexCompilationLevel dex_to_dex_compilation_level_;
|
||||
|
||||
// Filled by the compiler when quickening, in order to encode that information
|
||||
// in the .oat file. The runtime will use that information to get to the original
|
||||
// opcodes.
|
||||
std::vector<QuickenedInfo> quickened_info_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DexCompiler);
|
||||
};
|
||||
|
||||
void DexCompiler::Compile() {
|
||||
DCHECK_EQ(dex_to_dex_compilation_level_, DexToDexCompilationLevel::kOptimize);
|
||||
for (CodeItemIterator it(*unit_.GetCodeItem()); !it.Done(); it.Advance()) {
|
||||
Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
|
||||
const uint32_t dex_pc = it.CurrentDexPc();
|
||||
switch (inst->Opcode()) {
|
||||
case Instruction::RETURN_VOID:
|
||||
CompileReturnVoid(inst, dex_pc);
|
||||
break;
|
||||
|
||||
case Instruction::CHECK_CAST:
|
||||
inst = CompileCheckCast(inst, dex_pc);
|
||||
if (inst->Opcode() == Instruction::NOP) {
|
||||
// We turned the CHECK_CAST into two NOPs, avoid visiting the second NOP twice since this
|
||||
// would add 2 quickening info entries.
|
||||
it.Advance();
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction::IGET:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
|
||||
break;
|
||||
|
||||
case Instruction::IGET_WIDE:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
|
||||
break;
|
||||
|
||||
case Instruction::IGET_OBJECT:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
|
||||
break;
|
||||
|
||||
case Instruction::IGET_BOOLEAN:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN_QUICK, false);
|
||||
break;
|
||||
|
||||
case Instruction::IGET_BYTE:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE_QUICK, false);
|
||||
break;
|
||||
|
||||
case Instruction::IGET_CHAR:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR_QUICK, false);
|
||||
break;
|
||||
|
||||
case Instruction::IGET_SHORT:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT_QUICK, false);
|
||||
break;
|
||||
|
||||
case Instruction::IPUT:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
|
||||
break;
|
||||
|
||||
case Instruction::IPUT_BOOLEAN:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN_QUICK, true);
|
||||
break;
|
||||
|
||||
case Instruction::IPUT_BYTE:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE_QUICK, true);
|
||||
break;
|
||||
|
||||
case Instruction::IPUT_CHAR:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR_QUICK, true);
|
||||
break;
|
||||
|
||||
case Instruction::IPUT_SHORT:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT_QUICK, true);
|
||||
break;
|
||||
|
||||
case Instruction::IPUT_WIDE:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
|
||||
break;
|
||||
|
||||
case Instruction::IPUT_OBJECT:
|
||||
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true);
|
||||
break;
|
||||
|
||||
case Instruction::INVOKE_VIRTUAL:
|
||||
CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false);
|
||||
break;
|
||||
|
||||
case Instruction::INVOKE_VIRTUAL_RANGE:
|
||||
CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
|
||||
break;
|
||||
|
||||
case Instruction::NOP:
|
||||
// We need to differentiate between check cast inserted NOP and normal NOP, put an invalid
|
||||
// index in the map for normal nops. This should be rare in real code.
|
||||
quickened_info_.push_back(QuickenedInfo(dex_pc, DexFile::kDexNoIndex16));
|
||||
break;
|
||||
|
||||
default:
|
||||
DCHECK(!inst->IsQuickened());
|
||||
// Nothing to do.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
|
||||
DCHECK_EQ(inst->Opcode(), Instruction::RETURN_VOID);
|
||||
if (unit_.IsConstructor()) {
|
||||
// Are we compiling a non clinit constructor which needs a barrier ?
|
||||
if (!unit_.IsStatic() &&
|
||||
driver_.RequiresConstructorBarrier(Thread::Current(), unit_.GetDexFile(),
|
||||
unit_.GetClassDefIndex())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Replace RETURN_VOID by RETURN_VOID_NO_BARRIER.
|
||||
VLOG(compiler) << "Replacing " << Instruction::Name(inst->Opcode())
|
||||
<< " by " << Instruction::Name(Instruction::RETURN_VOID_NO_BARRIER)
|
||||
<< " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
|
||||
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
|
||||
inst->SetOpcode(Instruction::RETURN_VOID_NO_BARRIER);
|
||||
}
|
||||
|
||||
Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
|
||||
if (!kEnableCheckCastEllision) {
|
||||
return inst;
|
||||
}
|
||||
if (!driver_.IsSafeCast(&unit_, dex_pc)) {
|
||||
return inst;
|
||||
}
|
||||
// Ok, this is a safe cast. Since the "check-cast" instruction size is 2 code
|
||||
// units and a "nop" instruction size is 1 code unit, we need to replace it by
|
||||
// 2 consecutive NOP instructions.
|
||||
// Because the caller loops over instructions by calling Instruction::Next onto
|
||||
// the current instruction, we need to return the 2nd NOP instruction. Indeed,
|
||||
// its next instruction is the former check-cast's next instruction.
|
||||
VLOG(compiler) << "Removing " << Instruction::Name(inst->Opcode())
|
||||
<< " by replacing it with 2 NOPs at dex pc "
|
||||
<< StringPrintf("0x%x", dex_pc) << " in method "
|
||||
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
|
||||
quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegA_21c()));
|
||||
quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegB_21c()));
|
||||
// We are modifying 4 consecutive bytes.
|
||||
inst->SetOpcode(Instruction::NOP);
|
||||
inst->SetVRegA_10x(0u); // keep compliant with verifier.
|
||||
// Get to next instruction which is the second half of check-cast and replace
|
||||
// it by a NOP.
|
||||
inst = const_cast<Instruction*>(inst->Next());
|
||||
inst->SetOpcode(Instruction::NOP);
|
||||
inst->SetVRegA_10x(0u); // keep compliant with verifier.
|
||||
return inst;
|
||||
}
|
||||
|
||||
void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
|
||||
uint32_t dex_pc,
|
||||
Instruction::Code new_opcode,
|
||||
bool is_put) {
|
||||
if (!kEnableQuickening) {
|
||||
return;
|
||||
}
|
||||
uint32_t field_idx = inst->VRegC_22c();
|
||||
MemberOffset field_offset(0u);
|
||||
bool is_volatile;
|
||||
bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, is_put,
|
||||
&field_offset, &is_volatile);
|
||||
if (fast_path && !is_volatile && IsUint<16>(field_offset.Int32Value())) {
|
||||
VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
|
||||
<< " to " << Instruction::Name(new_opcode)
|
||||
<< " by replacing field index " << field_idx
|
||||
<< " by field offset " << field_offset.Int32Value()
|
||||
<< " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
|
||||
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
|
||||
// We are modifying 4 consecutive bytes.
|
||||
inst->SetOpcode(new_opcode);
|
||||
// Replace field index by field offset.
|
||||
inst->SetVRegC_22c(static_cast<uint16_t>(field_offset.Int32Value()));
|
||||
quickened_info_.push_back(QuickenedInfo(dex_pc, field_idx));
|
||||
}
|
||||
}
|
||||
|
||||
void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
|
||||
Instruction::Code new_opcode, bool is_range) {
|
||||
if (!kEnableQuickening) {
|
||||
return;
|
||||
}
|
||||
uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
|
||||
ScopedObjectAccess soa(Thread::Current());
|
||||
|
||||
ClassLinker* class_linker = unit_.GetClassLinker();
|
||||
ArtMethod* resolved_method =
|
||||
class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
|
||||
GetDexFile(),
|
||||
method_idx,
|
||||
unit_.GetDexCache(),
|
||||
unit_.GetClassLoader(),
|
||||
/* referrer */ nullptr,
|
||||
kVirtual);
|
||||
|
||||
if (UNLIKELY(resolved_method == nullptr)) {
|
||||
// Clean up any exception left by type resolution.
|
||||
soa.Self()->ClearException();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t vtable_idx = resolved_method->GetMethodIndex();
|
||||
DCHECK(IsUint<16>(vtable_idx));
|
||||
VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
|
||||
<< "(" << GetDexFile().PrettyMethod(method_idx, true) << ")"
|
||||
<< " to " << Instruction::Name(new_opcode)
|
||||
<< " by replacing method index " << method_idx
|
||||
<< " by vtable index " << vtable_idx
|
||||
<< " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
|
||||
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
|
||||
// We are modifying 4 consecutive bytes.
|
||||
inst->SetOpcode(new_opcode);
|
||||
// Replace method index by vtable index.
|
||||
if (is_range) {
|
||||
inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
|
||||
} else {
|
||||
inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
|
||||
}
|
||||
quickened_info_.push_back(QuickenedInfo(dex_pc, method_idx));
|
||||
}
|
||||
|
||||
CompiledMethod* ArtCompileDEX(
|
||||
CompilerDriver* driver,
|
||||
const DexFile::CodeItem* code_item,
|
||||
uint32_t access_flags,
|
||||
InvokeType invoke_type ATTRIBUTE_UNUSED,
|
||||
uint16_t class_def_idx,
|
||||
uint32_t method_idx,
|
||||
Handle<mirror::ClassLoader> class_loader,
|
||||
const DexFile& dex_file,
|
||||
DexToDexCompilationLevel dex_to_dex_compilation_level) {
|
||||
DCHECK(driver != nullptr);
|
||||
if (dex_to_dex_compilation_level != DexToDexCompilationLevel::kDontDexToDexCompile) {
|
||||
ScopedObjectAccess soa(Thread::Current());
|
||||
StackHandleScope<1> hs(soa.Self());
|
||||
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
|
||||
art::DexCompilationUnit unit(
|
||||
class_loader,
|
||||
class_linker,
|
||||
dex_file,
|
||||
code_item,
|
||||
class_def_idx,
|
||||
method_idx,
|
||||
access_flags,
|
||||
driver->GetVerifiedMethod(&dex_file, method_idx),
|
||||
hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)));
|
||||
art::optimizer::DexCompiler dex_compiler(*driver, unit, dex_to_dex_compilation_level);
|
||||
dex_compiler.Compile();
|
||||
if (dex_compiler.GetQuickenedInfo().empty()) {
|
||||
// No need to create a CompiledMethod if there are no quickened opcodes.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a `CompiledMethod`, with the quickened information in the vmap table.
|
||||
if (kIsDebugBuild) {
|
||||
// Double check that the counts line up with the size of the quicken info.
|
||||
size_t quicken_count = 0;
|
||||
for (CodeItemIterator it(*code_item); !it.Done(); it.Advance()) {
|
||||
if (QuickenInfoTable::NeedsIndexForInstruction(&it.CurrentInstruction())) {
|
||||
++quicken_count;
|
||||
}
|
||||
}
|
||||
CHECK_EQ(quicken_count, dex_compiler.GetQuickenedInfo().size());
|
||||
}
|
||||
std::vector<uint8_t> quicken_data;
|
||||
for (QuickenedInfo info : dex_compiler.GetQuickenedInfo()) {
|
||||
// Dex pc is not serialized, only used for checking the instructions. Since we access the
|
||||
// array based on the index of the quickened instruction, the indexes must line up perfectly.
|
||||
// The reader side uses the NeedsIndexForInstruction function too.
|
||||
const Instruction* inst = Instruction::At(code_item->insns_ + info.dex_pc);
|
||||
CHECK(QuickenInfoTable::NeedsIndexForInstruction(inst)) << inst->Opcode();
|
||||
// Add the index.
|
||||
quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 0));
|
||||
quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 8));
|
||||
}
|
||||
InstructionSet instruction_set = driver->GetInstructionSet();
|
||||
if (instruction_set == kThumb2) {
|
||||
// Don't use the thumb2 instruction set to avoid the one off code delta.
|
||||
instruction_set = kArm;
|
||||
}
|
||||
return CompiledMethod::SwapAllocCompiledMethod(
|
||||
driver,
|
||||
instruction_set,
|
||||
ArrayRef<const uint8_t>(), // no code
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
ArrayRef<const uint8_t>(), // method_info
|
||||
ArrayRef<const uint8_t>(quicken_data), // vmap_table
|
||||
ArrayRef<const uint8_t>(), // cfi data
|
||||
ArrayRef<const LinkerPatch>());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace optimizer
|
||||
|
||||
} // namespace art
|
55
android/art/compiler/dex/dex_to_dex_compiler.h
Normal file
55
android/art/compiler/dex/dex_to_dex_compiler.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
|
||||
#define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
|
||||
|
||||
#include "dex_file.h"
|
||||
#include "handle.h"
|
||||
#include "invoke_type.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
class CompiledMethod;
|
||||
class CompilerDriver;
|
||||
|
||||
namespace mirror {
|
||||
class ClassLoader;
|
||||
} // namespace mirror
|
||||
|
||||
namespace optimizer {
|
||||
|
||||
enum class DexToDexCompilationLevel {
|
||||
kDontDexToDexCompile, // Only meaning wrt image time interpretation.
|
||||
kOptimize // Perform peep-hole optimizations.
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const DexToDexCompilationLevel& rhs);
|
||||
|
||||
CompiledMethod* ArtCompileDEX(CompilerDriver* driver,
|
||||
const DexFile::CodeItem* code_item,
|
||||
uint32_t access_flags,
|
||||
InvokeType invoke_type,
|
||||
uint16_t class_def_idx,
|
||||
uint32_t method_idx,
|
||||
Handle<mirror::ClassLoader> class_loader,
|
||||
const DexFile& dex_file,
|
||||
DexToDexCompilationLevel dex_to_dex_compilation_level);
|
||||
|
||||
} // namespace optimizer
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
|
136
android/art/compiler/dex/dex_to_dex_decompiler_test.cc
Normal file
136
android/art/compiler/dex/dex_to_dex_decompiler_test.cc
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "dex_to_dex_decompiler.h"
|
||||
|
||||
#include "class_linker.h"
|
||||
#include "compiler/common_compiler_test.h"
|
||||
#include "compiler/compiled_method.h"
|
||||
#include "compiler/driver/compiler_options.h"
|
||||
#include "compiler/driver/compiler_driver.h"
|
||||
#include "compiler_callbacks.h"
|
||||
#include "dex_file.h"
|
||||
#include "handle_scope-inl.h"
|
||||
#include "verifier/method_verifier-inl.h"
|
||||
#include "mirror/class_loader.h"
|
||||
#include "runtime.h"
|
||||
#include "scoped_thread_state_change-inl.h"
|
||||
#include "thread.h"
|
||||
#include "verifier/method_verifier-inl.h"
|
||||
#include "verifier/verifier_deps.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
class DexToDexDecompilerTest : public CommonCompilerTest {
|
||||
public:
|
||||
void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
|
||||
TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
|
||||
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
|
||||
compiler_options_->boot_image_ = false;
|
||||
compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken);
|
||||
// Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
|
||||
// the results for all the dex files, not just the results for the current dex file.
|
||||
Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
|
||||
new verifier::VerifierDeps(GetDexFiles(class_loader)));
|
||||
compiler_driver_->SetDexFilesForOatFile(GetDexFiles(class_loader));
|
||||
compiler_driver_->CompileAll(class_loader, GetDexFiles(class_loader), &timings);
|
||||
}
|
||||
|
||||
void RunTest(const char* dex_name) {
|
||||
Thread* self = Thread::Current();
|
||||
// First load the original dex file.
|
||||
jobject original_class_loader;
|
||||
{
|
||||
ScopedObjectAccess soa(self);
|
||||
original_class_loader = LoadDex(dex_name);
|
||||
}
|
||||
const DexFile* original_dex_file = GetDexFiles(original_class_loader)[0];
|
||||
|
||||
// Load the dex file again and make it writable to quicken them.
|
||||
jobject class_loader;
|
||||
const DexFile* updated_dex_file = nullptr;
|
||||
{
|
||||
ScopedObjectAccess soa(self);
|
||||
class_loader = LoadDex(dex_name);
|
||||
updated_dex_file = GetDexFiles(class_loader)[0];
|
||||
Runtime::Current()->GetClassLinker()->RegisterDexFile(
|
||||
*updated_dex_file, soa.Decode<mirror::ClassLoader>(class_loader).Ptr());
|
||||
}
|
||||
// The dex files should be identical.
|
||||
int cmp = memcmp(original_dex_file->Begin(),
|
||||
updated_dex_file->Begin(),
|
||||
updated_dex_file->Size());
|
||||
ASSERT_EQ(0, cmp);
|
||||
|
||||
updated_dex_file->EnableWrite();
|
||||
CompileAll(class_loader);
|
||||
// The dex files should be different after quickening.
|
||||
cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size());
|
||||
ASSERT_NE(0, cmp);
|
||||
|
||||
// Unquicken the dex file.
|
||||
for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) {
|
||||
const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i);
|
||||
const uint8_t* class_data = updated_dex_file->GetClassData(class_def);
|
||||
if (class_data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
ClassDataItemIterator it(*updated_dex_file, class_data);
|
||||
it.SkipAllFields();
|
||||
|
||||
// Unquicken each method.
|
||||
while (it.HasNextDirectMethod()) {
|
||||
uint32_t method_idx = it.GetMemberIndex();
|
||||
CompiledMethod* compiled_method =
|
||||
compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
|
||||
ArrayRef<const uint8_t> table;
|
||||
if (compiled_method != nullptr) {
|
||||
table = compiled_method->GetVmapTable();
|
||||
}
|
||||
optimizer::ArtDecompileDEX(
|
||||
*it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
|
||||
it.Next();
|
||||
}
|
||||
while (it.HasNextVirtualMethod()) {
|
||||
uint32_t method_idx = it.GetMemberIndex();
|
||||
CompiledMethod* compiled_method =
|
||||
compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
|
||||
ArrayRef<const uint8_t> table;
|
||||
if (compiled_method != nullptr) {
|
||||
table = compiled_method->GetVmapTable();
|
||||
}
|
||||
optimizer::ArtDecompileDEX(
|
||||
*it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
|
||||
it.Next();
|
||||
}
|
||||
DCHECK(!it.HasNext());
|
||||
}
|
||||
|
||||
// Make sure after unquickening we go back to the same contents as the original dex file.
|
||||
cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size());
|
||||
ASSERT_EQ(0, cmp);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DexToDexDecompilerTest, VerifierDeps) {
|
||||
RunTest("VerifierDeps");
|
||||
}
|
||||
|
||||
TEST_F(DexToDexDecompilerTest, DexToDexDecompiler) {
|
||||
RunTest("DexToDexDecompiler");
|
||||
}
|
||||
|
||||
} // namespace art
|
734
android/art/compiler/dex/inline_method_analyser.cc
Normal file
734
android/art/compiler/dex/inline_method_analyser.cc
Normal file
|
@ -0,0 +1,734 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "inline_method_analyser.h"
|
||||
|
||||
#include "art_field-inl.h"
|
||||
#include "art_method-inl.h"
|
||||
#include "base/enums.h"
|
||||
#include "class_linker-inl.h"
|
||||
#include "dex_file-inl.h"
|
||||
#include "dex_instruction.h"
|
||||
#include "dex_instruction-inl.h"
|
||||
#include "dex_instruction_utils.h"
|
||||
#include "mirror/class-inl.h"
|
||||
#include "mirror/dex_cache-inl.h"
|
||||
|
||||
/*
|
||||
* NOTE: This code is part of the quick compiler. It lives in the runtime
|
||||
* only to allow the debugger to check whether a method has been inlined.
|
||||
*/
|
||||
|
||||
namespace art {
|
||||
|
||||
namespace { // anonymous namespace
|
||||
|
||||
// Helper class for matching a pattern.
|
||||
class Matcher {
|
||||
public:
|
||||
// Match function type.
|
||||
typedef bool MatchFn(Matcher* matcher);
|
||||
|
||||
template <size_t size>
|
||||
static bool Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]);
|
||||
|
||||
// Match and advance.
|
||||
|
||||
static bool Mark(Matcher* matcher);
|
||||
|
||||
template <bool (Matcher::*Fn)()>
|
||||
static bool Required(Matcher* matcher);
|
||||
|
||||
template <bool (Matcher::*Fn)()>
|
||||
static bool Repeated(Matcher* matcher); // On match, returns to the mark.
|
||||
|
||||
// Match an individual instruction.
|
||||
|
||||
template <Instruction::Code opcode> bool Opcode();
|
||||
bool Const0();
|
||||
bool IPutOnThis();
|
||||
|
||||
private:
|
||||
explicit Matcher(const DexFile::CodeItem* code_item)
|
||||
: code_item_(code_item),
|
||||
instruction_(Instruction::At(code_item->insns_)),
|
||||
pos_(0u),
|
||||
mark_(0u) { }
|
||||
|
||||
static bool DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size);
|
||||
|
||||
const DexFile::CodeItem* const code_item_;
|
||||
const Instruction* instruction_;
|
||||
size_t pos_;
|
||||
size_t mark_;
|
||||
};
|
||||
|
||||
template <size_t size>
|
||||
bool Matcher::Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]) {
|
||||
return DoMatch(code_item, pattern, size);
|
||||
}
|
||||
|
||||
bool Matcher::Mark(Matcher* matcher) {
|
||||
matcher->pos_ += 1u; // Advance to the next match function before marking.
|
||||
matcher->mark_ = matcher->pos_;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool (Matcher::*Fn)()>
|
||||
bool Matcher::Required(Matcher* matcher) {
|
||||
if (!(matcher->*Fn)()) {
|
||||
return false;
|
||||
}
|
||||
matcher->pos_ += 1u;
|
||||
matcher->instruction_ = matcher->instruction_->Next();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool (Matcher::*Fn)()>
|
||||
bool Matcher::Repeated(Matcher* matcher) {
|
||||
if (!(matcher->*Fn)()) {
|
||||
// Didn't match optional instruction, try the next match function.
|
||||
matcher->pos_ += 1u;
|
||||
return true;
|
||||
}
|
||||
matcher->pos_ = matcher->mark_;
|
||||
matcher->instruction_ = matcher->instruction_->Next();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <Instruction::Code opcode>
|
||||
bool Matcher::Opcode() {
|
||||
return instruction_->Opcode() == opcode;
|
||||
}
|
||||
|
||||
// Match const 0.
|
||||
bool Matcher::Const0() {
|
||||
return IsInstructionDirectConst(instruction_->Opcode()) &&
|
||||
(instruction_->Opcode() == Instruction::CONST_WIDE ? instruction_->VRegB_51l() == 0
|
||||
: instruction_->VRegB() == 0);
|
||||
}
|
||||
|
||||
bool Matcher::IPutOnThis() {
|
||||
DCHECK_NE(code_item_->ins_size_, 0u);
|
||||
return IsInstructionIPut(instruction_->Opcode()) &&
|
||||
instruction_->VRegB_22c() == code_item_->registers_size_ - code_item_->ins_size_;
|
||||
}
|
||||
|
||||
bool Matcher::DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size) {
|
||||
Matcher matcher(code_item);
|
||||
while (matcher.pos_ != size) {
|
||||
if (!pattern[matcher.pos_](&matcher)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Used for a single invoke in a constructor. In that situation, the method verifier makes
|
||||
// sure we invoke a constructor either in the same class or superclass with at least "this".
|
||||
ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_direct)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||||
DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
|
||||
DCHECK_EQ(invoke_direct->VRegC_35c(),
|
||||
method->GetCodeItem()->registers_size_ - method->GetCodeItem()->ins_size_);
|
||||
uint32_t method_index = invoke_direct->VRegB_35c();
|
||||
ArtMethod* target_method = Runtime::Current()->GetClassLinker()->LookupResolvedMethod(
|
||||
method_index, method->GetDexCache(), method->GetClassLoader());
|
||||
if (kIsDebugBuild && target_method != nullptr) {
|
||||
CHECK(!target_method->IsStatic());
|
||||
CHECK(target_method->IsConstructor());
|
||||
CHECK(target_method->GetDeclaringClass() == method->GetDeclaringClass() ||
|
||||
target_method->GetDeclaringClass() == method->GetDeclaringClass()->GetSuperClass());
|
||||
}
|
||||
return target_method;
|
||||
}
|
||||
|
||||
// Return the forwarded arguments and check that all remaining arguments are zero.
|
||||
// If the check fails, return static_cast<size_t>(-1).
|
||||
size_t CountForwardedConstructorArguments(const DexFile::CodeItem* code_item,
|
||||
const Instruction* invoke_direct,
|
||||
uint16_t zero_vreg_mask) {
|
||||
DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
|
||||
size_t number_of_args = invoke_direct->VRegA_35c();
|
||||
DCHECK_NE(number_of_args, 0u);
|
||||
uint32_t args[Instruction::kMaxVarArgRegs];
|
||||
invoke_direct->GetVarArgs(args);
|
||||
uint16_t this_vreg = args[0];
|
||||
DCHECK_EQ(this_vreg, code_item->registers_size_ - code_item->ins_size_); // Checked by verifier.
|
||||
size_t forwarded = 1u;
|
||||
while (forwarded < number_of_args &&
|
||||
args[forwarded] == this_vreg + forwarded &&
|
||||
(zero_vreg_mask & (1u << args[forwarded])) == 0) {
|
||||
++forwarded;
|
||||
}
|
||||
for (size_t i = forwarded; i != number_of_args; ++i) {
|
||||
if ((zero_vreg_mask & (1u << args[i])) == 0) {
|
||||
return static_cast<size_t>(-1);
|
||||
}
|
||||
}
|
||||
return forwarded;
|
||||
}
|
||||
|
||||
uint16_t GetZeroVRegMask(const Instruction* const0) {
|
||||
DCHECK(IsInstructionDirectConst(const0->Opcode()));
|
||||
DCHECK((const0->Opcode() == Instruction::CONST_WIDE) ? const0->VRegB_51l() == 0u
|
||||
: const0->VRegB() == 0);
|
||||
uint16_t base_mask = IsInstructionConstWide(const0->Opcode()) ? 3u : 1u;
|
||||
return base_mask << const0->VRegA();
|
||||
}
|
||||
|
||||
// We limit the number of IPUTs storing parameters. There can be any number
|
||||
// of IPUTs that store the value 0 as they are useless in a constructor as
|
||||
// the object always starts zero-initialized. We also eliminate all but the
|
||||
// last store to any field as they are not observable; not even if the field
|
||||
// is volatile as no reference to the object can escape from a constructor
|
||||
// with this pattern.
|
||||
static constexpr size_t kMaxConstructorIPuts = 3u;
|
||||
|
||||
struct ConstructorIPutData {
|
||||
ConstructorIPutData() : field_index(DexFile::kDexNoIndex16), arg(0u) { }
|
||||
|
||||
uint16_t field_index;
|
||||
uint16_t arg;
|
||||
};
|
||||
|
||||
bool RecordConstructorIPut(ArtMethod* method,
|
||||
const Instruction* new_iput,
|
||||
uint16_t this_vreg,
|
||||
uint16_t zero_vreg_mask,
|
||||
/*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts])
|
||||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||||
DCHECK(IsInstructionIPut(new_iput->Opcode()));
|
||||
uint32_t field_index = new_iput->VRegC_22c();
|
||||
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
||||
ArtField* field = class_linker->LookupResolvedField(field_index, method, /* is_static */ false);
|
||||
if (UNLIKELY(field == nullptr)) {
|
||||
return false;
|
||||
}
|
||||
// Remove previous IPUT to the same field, if any. Different field indexes may refer
|
||||
// to the same field, so we need to compare resolved fields from the dex cache.
|
||||
for (size_t old_pos = 0; old_pos != arraysize(iputs); ++old_pos) {
|
||||
if (iputs[old_pos].field_index == DexFile::kDexNoIndex16) {
|
||||
break;
|
||||
}
|
||||
ArtField* f = class_linker->LookupResolvedField(iputs[old_pos].field_index,
|
||||
method,
|
||||
/* is_static */ false);
|
||||
DCHECK(f != nullptr);
|
||||
if (f == field) {
|
||||
auto back_it = std::copy(iputs + old_pos + 1, iputs + arraysize(iputs), iputs + old_pos);
|
||||
*back_it = ConstructorIPutData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the stored value isn't zero, record the IPUT.
|
||||
if ((zero_vreg_mask & (1u << new_iput->VRegA_22c())) == 0u) {
|
||||
size_t new_pos = 0;
|
||||
while (new_pos != arraysize(iputs) && iputs[new_pos].field_index != DexFile::kDexNoIndex16) {
|
||||
++new_pos;
|
||||
}
|
||||
if (new_pos == arraysize(iputs)) {
|
||||
return false; // Exceeded capacity of the output array.
|
||||
}
|
||||
iputs[new_pos].field_index = field_index;
|
||||
iputs[new_pos].arg = new_iput->VRegA_22c() - this_vreg;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DoAnalyseConstructor(const DexFile::CodeItem* code_item,
|
||||
ArtMethod* method,
|
||||
/*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts])
|
||||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||||
// On entry we should not have any IPUTs yet.
|
||||
DCHECK_EQ(0, std::count_if(
|
||||
iputs,
|
||||
iputs + arraysize(iputs),
|
||||
[](const ConstructorIPutData& iput_data) {
|
||||
return iput_data.field_index != DexFile::kDexNoIndex16;
|
||||
}));
|
||||
|
||||
// Limit the maximum number of code units we're willing to match.
|
||||
static constexpr size_t kMaxCodeUnits = 16u;
|
||||
|
||||
// Limit the number of registers that the constructor may use to 16.
|
||||
// Given that IPUTs must use low 16 registers and we do not match MOVEs,
|
||||
// this is a reasonable limitation.
|
||||
static constexpr size_t kMaxVRegs = 16u;
|
||||
|
||||
// We try to match a constructor that calls another constructor (either in
|
||||
// superclass or in the same class) with the same parameters, or with some
|
||||
// parameters truncated (allowed only for calls to superclass constructor)
|
||||
// or with extra parameters with value 0 (with any type, including null).
|
||||
// This call can be followed by optional IPUTs on "this" storing either one
|
||||
// of the parameters or 0 and the code must then finish with RETURN_VOID.
|
||||
// The called constructor must be either java.lang.Object.<init>() or it
|
||||
// must also match the same pattern.
|
||||
static Matcher::MatchFn* const kConstructorPattern[] = {
|
||||
&Matcher::Mark,
|
||||
&Matcher::Repeated<&Matcher::Const0>,
|
||||
&Matcher::Required<&Matcher::Opcode<Instruction::INVOKE_DIRECT>>,
|
||||
&Matcher::Mark,
|
||||
&Matcher::Repeated<&Matcher::Const0>,
|
||||
&Matcher::Repeated<&Matcher::IPutOnThis>,
|
||||
&Matcher::Required<&Matcher::Opcode<Instruction::RETURN_VOID>>,
|
||||
};
|
||||
|
||||
DCHECK(method != nullptr);
|
||||
DCHECK(!method->IsStatic());
|
||||
DCHECK(method->IsConstructor());
|
||||
DCHECK(code_item != nullptr);
|
||||
if (!method->GetDeclaringClass()->IsVerified() ||
|
||||
code_item->insns_size_in_code_units_ > kMaxCodeUnits ||
|
||||
code_item->registers_size_ > kMaxVRegs ||
|
||||
!Matcher::Match(code_item, kConstructorPattern)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the invoke, prevent a few odd cases and collect IPUTs.
|
||||
uint16_t this_vreg = code_item->registers_size_ - code_item->ins_size_;
|
||||
uint16_t zero_vreg_mask = 0u;
|
||||
for (const Instruction* instruction = Instruction::At(code_item->insns_);
|
||||
instruction->Opcode() != Instruction::RETURN_VOID;
|
||||
instruction = instruction->Next()) {
|
||||
if (instruction->Opcode() == Instruction::INVOKE_DIRECT) {
|
||||
ArtMethod* target_method = GetTargetConstructor(method, instruction);
|
||||
if (target_method == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// We allow forwarding constructors only if they pass more arguments
|
||||
// to prevent infinite recursion.
|
||||
if (target_method->GetDeclaringClass() == method->GetDeclaringClass() &&
|
||||
instruction->VRegA_35c() <= code_item->ins_size_) {
|
||||
return false;
|
||||
}
|
||||
size_t forwarded = CountForwardedConstructorArguments(code_item, instruction, zero_vreg_mask);
|
||||
if (forwarded == static_cast<size_t>(-1)) {
|
||||
return false;
|
||||
}
|
||||
if (target_method->GetDeclaringClass()->IsObjectClass()) {
|
||||
DCHECK_EQ(Instruction::At(target_method->GetCodeItem()->insns_)->Opcode(),
|
||||
Instruction::RETURN_VOID);
|
||||
} else {
|
||||
const DexFile::CodeItem* target_code_item = target_method->GetCodeItem();
|
||||
if (target_code_item == nullptr) {
|
||||
return false; // Native constructor?
|
||||
}
|
||||
if (!DoAnalyseConstructor(target_code_item, target_method, iputs)) {
|
||||
return false;
|
||||
}
|
||||
// Prune IPUTs with zero input.
|
||||
auto kept_end = std::remove_if(
|
||||
iputs,
|
||||
iputs + arraysize(iputs),
|
||||
[forwarded](const ConstructorIPutData& iput_data) {
|
||||
return iput_data.arg >= forwarded;
|
||||
});
|
||||
std::fill(kept_end, iputs + arraysize(iputs), ConstructorIPutData());
|
||||
// If we have any IPUTs from the call, check that the target method is in the same
|
||||
// dex file (compare DexCache references), otherwise field_indexes would be bogus.
|
||||
if (iputs[0].field_index != DexFile::kDexNoIndex16 &&
|
||||
target_method->GetDexCache() != method->GetDexCache()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (IsInstructionDirectConst(instruction->Opcode())) {
|
||||
zero_vreg_mask |= GetZeroVRegMask(instruction);
|
||||
if ((zero_vreg_mask & (1u << this_vreg)) != 0u) {
|
||||
return false; // Overwriting `this` is unsupported.
|
||||
}
|
||||
} else {
|
||||
DCHECK(IsInstructionIPut(instruction->Opcode()));
|
||||
DCHECK_EQ(instruction->VRegB_22c(), this_vreg);
|
||||
if (!RecordConstructorIPut(method, instruction, this_vreg, zero_vreg_mask, iputs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool AnalyseConstructor(const DexFile::CodeItem* code_item,
|
||||
ArtMethod* method,
|
||||
InlineMethod* result)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_) {
|
||||
ConstructorIPutData iputs[kMaxConstructorIPuts];
|
||||
if (!DoAnalyseConstructor(code_item, method, iputs)) {
|
||||
return false;
|
||||
}
|
||||
static_assert(kMaxConstructorIPuts == 3, "Unexpected limit"); // Code below depends on this.
|
||||
DCHECK(iputs[0].field_index != DexFile::kDexNoIndex16 ||
|
||||
iputs[1].field_index == DexFile::kDexNoIndex16);
|
||||
DCHECK(iputs[1].field_index != DexFile::kDexNoIndex16 ||
|
||||
iputs[2].field_index == DexFile::kDexNoIndex16);
|
||||
|
||||
#define STORE_IPUT(n) \
|
||||
do { \
|
||||
result->d.constructor_data.iput##n##_field_index = iputs[n].field_index; \
|
||||
result->d.constructor_data.iput##n##_arg = iputs[n].arg; \
|
||||
} while (false)
|
||||
|
||||
STORE_IPUT(0);
|
||||
STORE_IPUT(1);
|
||||
STORE_IPUT(2);
|
||||
#undef STORE_IPUT
|
||||
|
||||
result->opcode = kInlineOpConstructor;
|
||||
result->d.constructor_data.reserved = 0u;
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET), "iget type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE), "iget_wide type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT),
|
||||
"iget_object type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN),
|
||||
"iget_boolean type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE), "iget_byte type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR), "iget_char type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT), "iget_short type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT), "iput type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE), "iput_wide type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT),
|
||||
"iput_object type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN),
|
||||
"iput_boolean type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE), "iput_byte type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR), "iput_char type");
|
||||
static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT), "iput_short type");
|
||||
static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET) ==
|
||||
InlineMethodAnalyser::IPutVariant(Instruction::IPUT), "iget/iput variant");
|
||||
static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) ==
|
||||
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), "iget/iput_wide variant");
|
||||
static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) ==
|
||||
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), "iget/iput_object variant");
|
||||
static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) ==
|
||||
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), "iget/iput_boolean variant");
|
||||
static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) ==
|
||||
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), "iget/iput_byte variant");
|
||||
static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) ==
|
||||
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), "iget/iput_char variant");
|
||||
static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
|
||||
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant");
|
||||
|
||||
bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
|
||||
const DexFile::CodeItem* code_item = method->GetCodeItem();
|
||||
if (code_item == nullptr) {
|
||||
// Native or abstract.
|
||||
return false;
|
||||
}
|
||||
return AnalyseMethodCode(code_item,
|
||||
MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
|
||||
method->IsStatic(),
|
||||
method,
|
||||
result);
|
||||
}
|
||||
|
||||
bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item,
|
||||
const MethodReference& method_ref,
|
||||
bool is_static,
|
||||
ArtMethod* method,
|
||||
InlineMethod* result) {
|
||||
// We currently support only plain return or 2-instruction methods.
|
||||
|
||||
DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
|
||||
const Instruction* instruction = Instruction::At(code_item->insns_);
|
||||
Instruction::Code opcode = instruction->Opcode();
|
||||
|
||||
switch (opcode) {
|
||||
case Instruction::RETURN_VOID:
|
||||
if (result != nullptr) {
|
||||
result->opcode = kInlineOpNop;
|
||||
result->d.data = 0u;
|
||||
}
|
||||
return true;
|
||||
case Instruction::RETURN:
|
||||
case Instruction::RETURN_OBJECT:
|
||||
case Instruction::RETURN_WIDE:
|
||||
return AnalyseReturnMethod(code_item, result);
|
||||
case Instruction::CONST:
|
||||
case Instruction::CONST_4:
|
||||
case Instruction::CONST_16:
|
||||
case Instruction::CONST_HIGH16:
|
||||
// TODO: Support wide constants (RETURN_WIDE).
|
||||
if (AnalyseConstMethod(code_item, result)) {
|
||||
return true;
|
||||
}
|
||||
FALLTHROUGH_INTENDED;
|
||||
case Instruction::CONST_WIDE:
|
||||
case Instruction::CONST_WIDE_16:
|
||||
case Instruction::CONST_WIDE_32:
|
||||
case Instruction::CONST_WIDE_HIGH16:
|
||||
case Instruction::INVOKE_DIRECT:
|
||||
if (method != nullptr && !method->IsStatic() && method->IsConstructor()) {
|
||||
return AnalyseConstructor(code_item, method, result);
|
||||
}
|
||||
return false;
|
||||
case Instruction::IGET:
|
||||
case Instruction::IGET_OBJECT:
|
||||
case Instruction::IGET_BOOLEAN:
|
||||
case Instruction::IGET_BYTE:
|
||||
case Instruction::IGET_CHAR:
|
||||
case Instruction::IGET_SHORT:
|
||||
case Instruction::IGET_WIDE:
|
||||
// TODO: Add handling for JIT.
|
||||
// case Instruction::IGET_QUICK:
|
||||
// case Instruction::IGET_WIDE_QUICK:
|
||||
// case Instruction::IGET_OBJECT_QUICK:
|
||||
return AnalyseIGetMethod(code_item, method_ref, is_static, method, result);
|
||||
case Instruction::IPUT:
|
||||
case Instruction::IPUT_OBJECT:
|
||||
case Instruction::IPUT_BOOLEAN:
|
||||
case Instruction::IPUT_BYTE:
|
||||
case Instruction::IPUT_CHAR:
|
||||
case Instruction::IPUT_SHORT:
|
||||
case Instruction::IPUT_WIDE:
|
||||
// TODO: Add handling for JIT.
|
||||
// case Instruction::IPUT_QUICK:
|
||||
// case Instruction::IPUT_WIDE_QUICK:
|
||||
// case Instruction::IPUT_OBJECT_QUICK:
|
||||
return AnalyseIPutMethod(code_item, method_ref, is_static, method, result);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) {
|
||||
const DexFile::MethodId& method_id = ref.dex_file->GetMethodId(ref.dex_method_index);
|
||||
const char* method_name = ref.dex_file->GetMethodName(method_id);
|
||||
// javac names synthetic accessors "access$nnn",
|
||||
// jack names them "-getN", "-putN", "-wrapN".
|
||||
return strncmp(method_name, "access$", strlen("access$")) == 0 ||
|
||||
strncmp(method_name, "-", strlen("-")) == 0;
|
||||
}
|
||||
|
||||
bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item,
|
||||
InlineMethod* result) {
|
||||
const Instruction* return_instruction = Instruction::At(code_item->insns_);
|
||||
Instruction::Code return_opcode = return_instruction->Opcode();
|
||||
uint32_t reg = return_instruction->VRegA_11x();
|
||||
uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
|
||||
DCHECK_GE(reg, arg_start);
|
||||
DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg,
|
||||
code_item->registers_size_);
|
||||
|
||||
if (result != nullptr) {
|
||||
result->opcode = kInlineOpReturnArg;
|
||||
InlineReturnArgData* data = &result->d.return_data;
|
||||
data->arg = reg - arg_start;
|
||||
data->is_wide = (return_opcode == Instruction::RETURN_WIDE) ? 1u : 0u;
|
||||
data->is_object = (return_opcode == Instruction::RETURN_OBJECT) ? 1u : 0u;
|
||||
data->reserved = 0u;
|
||||
data->reserved2 = 0u;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item,
|
||||
InlineMethod* result) {
|
||||
const Instruction* instruction = Instruction::At(code_item->insns_);
|
||||
const Instruction* return_instruction = instruction->Next();
|
||||
Instruction::Code return_opcode = return_instruction->Opcode();
|
||||
if (return_opcode != Instruction::RETURN &&
|
||||
return_opcode != Instruction::RETURN_OBJECT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t return_reg = return_instruction->VRegA_11x();
|
||||
DCHECK_LT(return_reg, code_item->registers_size_);
|
||||
|
||||
int32_t const_value = instruction->VRegB();
|
||||
if (instruction->Opcode() == Instruction::CONST_HIGH16) {
|
||||
const_value <<= 16;
|
||||
}
|
||||
DCHECK_LT(instruction->VRegA(), code_item->registers_size_);
|
||||
if (instruction->VRegA() != return_reg) {
|
||||
return false; // Not returning the value set by const?
|
||||
}
|
||||
if (return_opcode == Instruction::RETURN_OBJECT && const_value != 0) {
|
||||
return false; // Returning non-null reference constant?
|
||||
}
|
||||
if (result != nullptr) {
|
||||
result->opcode = kInlineOpNonWideConst;
|
||||
result->d.data = static_cast<uint64_t>(const_value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item,
|
||||
const MethodReference& method_ref,
|
||||
bool is_static,
|
||||
ArtMethod* method,
|
||||
InlineMethod* result) {
|
||||
const Instruction* instruction = Instruction::At(code_item->insns_);
|
||||
Instruction::Code opcode = instruction->Opcode();
|
||||
DCHECK(IsInstructionIGet(opcode));
|
||||
|
||||
const Instruction* return_instruction = instruction->Next();
|
||||
Instruction::Code return_opcode = return_instruction->Opcode();
|
||||
if (!(return_opcode == Instruction::RETURN_WIDE && opcode == Instruction::IGET_WIDE) &&
|
||||
!(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT) &&
|
||||
!(return_opcode == Instruction::RETURN && opcode != Instruction::IGET_WIDE &&
|
||||
opcode != Instruction::IGET_OBJECT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t return_reg = return_instruction->VRegA_11x();
|
||||
DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
|
||||
code_item->registers_size_);
|
||||
|
||||
uint32_t dst_reg = instruction->VRegA_22c();
|
||||
uint32_t object_reg = instruction->VRegB_22c();
|
||||
uint32_t field_idx = instruction->VRegC_22c();
|
||||
uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
|
||||
DCHECK_GE(object_reg, arg_start);
|
||||
DCHECK_LT(object_reg, code_item->registers_size_);
|
||||
uint32_t object_arg = object_reg - arg_start;
|
||||
|
||||
DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->registers_size_);
|
||||
if (dst_reg != return_reg) {
|
||||
return false; // Not returning the value retrieved by IGET?
|
||||
}
|
||||
|
||||
if (is_static || object_arg != 0u) {
|
||||
// TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
|
||||
// Allow synthetic accessors. We don't care about losing their stack frame in NPE.
|
||||
if (!IsSyntheticAccessor(method_ref)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// InlineIGetIPutData::object_arg is only 4 bits wide.
|
||||
static constexpr uint16_t kMaxObjectArg = 15u;
|
||||
if (object_arg > kMaxObjectArg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result != nullptr) {
|
||||
InlineIGetIPutData* data = &result->d.ifield_data;
|
||||
if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) {
|
||||
return false;
|
||||
}
|
||||
result->opcode = kInlineOpIGet;
|
||||
data->op_variant = IGetVariant(opcode);
|
||||
data->method_is_static = is_static ? 1u : 0u;
|
||||
data->object_arg = object_arg; // Allow IGET on any register, not just "this".
|
||||
data->src_arg = 0u;
|
||||
data->return_arg_plus1 = 0u;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item,
|
||||
const MethodReference& method_ref,
|
||||
bool is_static,
|
||||
ArtMethod* method,
|
||||
InlineMethod* result) {
|
||||
const Instruction* instruction = Instruction::At(code_item->insns_);
|
||||
Instruction::Code opcode = instruction->Opcode();
|
||||
DCHECK(IsInstructionIPut(opcode));
|
||||
|
||||
const Instruction* return_instruction = instruction->Next();
|
||||
Instruction::Code return_opcode = return_instruction->Opcode();
|
||||
uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
|
||||
uint16_t return_arg_plus1 = 0u;
|
||||
if (return_opcode != Instruction::RETURN_VOID) {
|
||||
if (return_opcode != Instruction::RETURN &&
|
||||
return_opcode != Instruction::RETURN_OBJECT &&
|
||||
return_opcode != Instruction::RETURN_WIDE) {
|
||||
return false;
|
||||
}
|
||||
// Returning an argument.
|
||||
uint32_t return_reg = return_instruction->VRegA_11x();
|
||||
DCHECK_GE(return_reg, arg_start);
|
||||
DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1u : return_reg,
|
||||
code_item->registers_size_);
|
||||
return_arg_plus1 = return_reg - arg_start + 1u;
|
||||
}
|
||||
|
||||
uint32_t src_reg = instruction->VRegA_22c();
|
||||
uint32_t object_reg = instruction->VRegB_22c();
|
||||
uint32_t field_idx = instruction->VRegC_22c();
|
||||
DCHECK_GE(object_reg, arg_start);
|
||||
DCHECK_LT(object_reg, code_item->registers_size_);
|
||||
DCHECK_GE(src_reg, arg_start);
|
||||
DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->registers_size_);
|
||||
uint32_t object_arg = object_reg - arg_start;
|
||||
uint32_t src_arg = src_reg - arg_start;
|
||||
|
||||
if (is_static || object_arg != 0u) {
|
||||
// TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
|
||||
// Allow synthetic accessors. We don't care about losing their stack frame in NPE.
|
||||
if (!IsSyntheticAccessor(method_ref)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// InlineIGetIPutData::object_arg/src_arg/return_arg_plus1 are each only 4 bits wide.
|
||||
static constexpr uint16_t kMaxObjectArg = 15u;
|
||||
static constexpr uint16_t kMaxSrcArg = 15u;
|
||||
static constexpr uint16_t kMaxReturnArgPlus1 = 15u;
|
||||
if (object_arg > kMaxObjectArg || src_arg > kMaxSrcArg || return_arg_plus1 > kMaxReturnArgPlus1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result != nullptr) {
|
||||
InlineIGetIPutData* data = &result->d.ifield_data;
|
||||
if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) {
|
||||
return false;
|
||||
}
|
||||
result->opcode = kInlineOpIPut;
|
||||
data->op_variant = IPutVariant(opcode);
|
||||
data->method_is_static = is_static ? 1u : 0u;
|
||||
data->object_arg = object_arg; // Allow IPUT on any register, not just "this".
|
||||
data->src_arg = src_arg;
|
||||
data->return_arg_plus1 = return_arg_plus1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method,
|
||||
uint32_t field_idx,
|
||||
bool is_put,
|
||||
InlineIGetIPutData* result) {
|
||||
if (method == nullptr) {
|
||||
return false;
|
||||
}
|
||||
ObjPtr<mirror::DexCache> dex_cache = method->GetDexCache();
|
||||
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
|
||||
ArtField* field = class_linker->LookupResolvedField(field_idx, method, /* is_static */ false);
|
||||
if (field == nullptr || field->IsStatic()) {
|
||||
return false;
|
||||
}
|
||||
ObjPtr<mirror::Class> method_class = method->GetDeclaringClass();
|
||||
ObjPtr<mirror::Class> field_class = field->GetDeclaringClass();
|
||||
if (!method_class->CanAccessResolvedField(field_class, field, dex_cache, field_idx) ||
|
||||
(is_put && field->IsFinal() && method_class != field_class)) {
|
||||
return false;
|
||||
}
|
||||
DCHECK_GE(field->GetOffset().Int32Value(), 0);
|
||||
// Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451.
|
||||
uint32_t field_offset = field->GetOffset().Uint32Value();
|
||||
bool is_volatile = field->IsVolatile();
|
||||
result->field_idx = field_idx;
|
||||
result->field_offset = field_offset;
|
||||
result->is_volatile = is_volatile ? 1u : 0u;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace art
|
156
android/art/compiler/dex/inline_method_analyser.h
Normal file
156
android/art/compiler/dex/inline_method_analyser.h
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ART_COMPILER_DEX_INLINE_METHOD_ANALYSER_H_
|
||||
#define ART_COMPILER_DEX_INLINE_METHOD_ANALYSER_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/mutex.h"
|
||||
#include "dex_file.h"
|
||||
#include "dex_instruction.h"
|
||||
#include "method_reference.h"
|
||||
|
||||
/*
|
||||
* NOTE: This code is part of the quick compiler. It lives in the runtime
|
||||
* only to allow the debugger to check whether a method has been inlined.
|
||||
*/
|
||||
|
||||
namespace art {
|
||||
|
||||
namespace verifier {
|
||||
class MethodVerifier;
|
||||
} // namespace verifier
|
||||
class ArtMethod;
|
||||
|
||||
enum InlineMethodOpcode : uint16_t {
|
||||
kInlineOpNop,
|
||||
kInlineOpReturnArg,
|
||||
kInlineOpNonWideConst,
|
||||
kInlineOpIGet,
|
||||
kInlineOpIPut,
|
||||
kInlineOpConstructor,
|
||||
};
|
||||
|
||||
struct InlineIGetIPutData {
|
||||
// The op_variant below is DexMemAccessType but the runtime doesn't know that enumeration.
|
||||
uint16_t op_variant : 3;
|
||||
uint16_t method_is_static : 1;
|
||||
uint16_t object_arg : 4;
|
||||
uint16_t src_arg : 4; // iput only
|
||||
uint16_t return_arg_plus1 : 4; // iput only, method argument to return + 1, 0 = return void.
|
||||
uint16_t field_idx;
|
||||
uint32_t is_volatile : 1;
|
||||
uint32_t field_offset : 31;
|
||||
};
|
||||
static_assert(sizeof(InlineIGetIPutData) == sizeof(uint64_t), "Invalid size of InlineIGetIPutData");
|
||||
|
||||
struct InlineReturnArgData {
|
||||
uint16_t arg;
|
||||
uint16_t is_wide : 1;
|
||||
uint16_t is_object : 1;
|
||||
uint16_t reserved : 14;
|
||||
uint32_t reserved2;
|
||||
};
|
||||
static_assert(sizeof(InlineReturnArgData) == sizeof(uint64_t),
|
||||
"Invalid size of InlineReturnArgData");
|
||||
|
||||
struct InlineConstructorData {
|
||||
// There can be up to 3 IPUTs, unused fields are marked with kNoDexIndex16.
|
||||
uint16_t iput0_field_index;
|
||||
uint16_t iput1_field_index;
|
||||
uint16_t iput2_field_index;
|
||||
uint16_t iput0_arg : 4;
|
||||
uint16_t iput1_arg : 4;
|
||||
uint16_t iput2_arg : 4;
|
||||
uint16_t reserved : 4;
|
||||
};
|
||||
static_assert(sizeof(InlineConstructorData) == sizeof(uint64_t),
|
||||
"Invalid size of InlineConstructorData");
|
||||
|
||||
struct InlineMethod {
|
||||
InlineMethodOpcode opcode;
|
||||
union {
|
||||
uint64_t data;
|
||||
InlineIGetIPutData ifield_data;
|
||||
InlineReturnArgData return_data;
|
||||
InlineConstructorData constructor_data;
|
||||
} d;
|
||||
};
|
||||
|
||||
class InlineMethodAnalyser {
|
||||
public:
|
||||
/**
|
||||
* Analyse method code to determine if the method is a candidate for inlining.
|
||||
* If it is, record the inlining data.
|
||||
*
|
||||
* @return true if the method is a candidate for inlining, false otherwise.
|
||||
*/
|
||||
static bool AnalyseMethodCode(ArtMethod* method, InlineMethod* result)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_);
|
||||
|
||||
static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
|
||||
return Instruction::IGET <= opcode && opcode <= Instruction::IGET_SHORT;
|
||||
}
|
||||
|
||||
static constexpr bool IsInstructionIPut(Instruction::Code opcode) {
|
||||
return Instruction::IPUT <= opcode && opcode <= Instruction::IPUT_SHORT;
|
||||
}
|
||||
|
||||
static constexpr uint16_t IGetVariant(Instruction::Code opcode) {
|
||||
return opcode - Instruction::IGET;
|
||||
}
|
||||
|
||||
static constexpr uint16_t IPutVariant(Instruction::Code opcode) {
|
||||
return opcode - Instruction::IPUT;
|
||||
}
|
||||
|
||||
// Determines whether the method is a synthetic accessor (method name starts with "access$").
|
||||
static bool IsSyntheticAccessor(MethodReference ref);
|
||||
|
||||
private:
|
||||
static bool AnalyseMethodCode(const DexFile::CodeItem* code_item,
|
||||
const MethodReference& method_ref,
|
||||
bool is_static,
|
||||
ArtMethod* method,
|
||||
InlineMethod* result)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_);
|
||||
static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
|
||||
static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
|
||||
static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item,
|
||||
const MethodReference& method_ref,
|
||||
bool is_static,
|
||||
ArtMethod* method,
|
||||
InlineMethod* result)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_);
|
||||
static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item,
|
||||
const MethodReference& method_ref,
|
||||
bool is_static,
|
||||
ArtMethod* method,
|
||||
InlineMethod* result)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_);
|
||||
|
||||
// Can we fast path instance field access in a verified accessor?
|
||||
// If yes, computes field's offset and volatility and whether the method is static or not.
|
||||
static bool ComputeSpecialAccessorInfo(ArtMethod* method,
|
||||
uint32_t field_idx,
|
||||
bool is_put,
|
||||
InlineIGetIPutData* result)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_);
|
||||
};
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_COMPILER_DEX_INLINE_METHOD_ANALYSER_H_
|
50
android/art/compiler/dex/quick_compiler_callbacks.cc
Normal file
50
android/art/compiler/dex/quick_compiler_callbacks.cc
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "quick_compiler_callbacks.h"
|
||||
|
||||
#include "driver/compiler_driver.h"
|
||||
#include "verifier/method_verifier-inl.h"
|
||||
#include "verification_results.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
void QuickCompilerCallbacks::MethodVerified(verifier::MethodVerifier* verifier) {
|
||||
if (verification_results_ != nullptr) {
|
||||
verification_results_->ProcessVerifiedMethod(verifier);
|
||||
}
|
||||
}
|
||||
|
||||
void QuickCompilerCallbacks::ClassRejected(ClassReference ref) {
|
||||
if (verification_results_ != nullptr) {
|
||||
verification_results_->AddRejectedClass(ref);
|
||||
}
|
||||
}
|
||||
|
||||
bool QuickCompilerCallbacks::CanAssumeVerified(ClassReference ref) {
|
||||
// If we don't have class unloading enabled in the compiler, we will never see class that were
|
||||
// previously verified. Return false to avoid overhead from the lookup in the compiler driver.
|
||||
if (!does_class_unloading_) {
|
||||
return false;
|
||||
}
|
||||
DCHECK(compiler_driver_ != nullptr);
|
||||
// In the case of the quicken filter: avoiding verification of quickened instructions, which the
|
||||
// verifier doesn't currently support.
|
||||
// In the case of the verify filter, avoiding verifiying twice.
|
||||
return compiler_driver_->CanAssumeVerified(ref);
|
||||
}
|
||||
|
||||
} // namespace art
|
75
android/art/compiler/dex/quick_compiler_callbacks.h
Normal file
75
android/art/compiler/dex/quick_compiler_callbacks.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ART_COMPILER_DEX_QUICK_COMPILER_CALLBACKS_H_
|
||||
#define ART_COMPILER_DEX_QUICK_COMPILER_CALLBACKS_H_
|
||||
|
||||
#include "compiler_callbacks.h"
|
||||
#include "verifier/verifier_deps.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
class CompilerDriver;
|
||||
class VerificationResults;
|
||||
|
||||
class QuickCompilerCallbacks FINAL : public CompilerCallbacks {
|
||||
public:
|
||||
explicit QuickCompilerCallbacks(CompilerCallbacks::CallbackMode mode)
|
||||
: CompilerCallbacks(mode) {}
|
||||
|
||||
~QuickCompilerCallbacks() { }
|
||||
|
||||
void MethodVerified(verifier::MethodVerifier* verifier)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
|
||||
|
||||
void ClassRejected(ClassReference ref) OVERRIDE;
|
||||
|
||||
// We are running in an environment where we can call patchoat safely so we should.
|
||||
bool IsRelocationPossible() OVERRIDE {
|
||||
return true;
|
||||
}
|
||||
|
||||
verifier::VerifierDeps* GetVerifierDeps() const OVERRIDE {
|
||||
return verifier_deps_.get();
|
||||
}
|
||||
|
||||
void SetVerifierDeps(verifier::VerifierDeps* deps) OVERRIDE {
|
||||
verifier_deps_.reset(deps);
|
||||
}
|
||||
|
||||
void SetVerificationResults(VerificationResults* verification_results) {
|
||||
verification_results_ = verification_results;
|
||||
}
|
||||
|
||||
bool CanAssumeVerified(ClassReference ref) OVERRIDE;
|
||||
|
||||
void SetDoesClassUnloading(bool does_class_unloading, CompilerDriver* compiler_driver)
|
||||
OVERRIDE {
|
||||
does_class_unloading_ = does_class_unloading;
|
||||
compiler_driver_ = compiler_driver;
|
||||
DCHECK(!does_class_unloading || compiler_driver_ != nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
VerificationResults* verification_results_ = nullptr;
|
||||
bool does_class_unloading_ = false;
|
||||
CompilerDriver* compiler_driver_ = nullptr;
|
||||
std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
|
||||
};
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_COMPILER_DEX_QUICK_COMPILER_CALLBACKS_H_
|
176
android/art/compiler/dex/verification_results.cc
Normal file
176
android/art/compiler/dex/verification_results.cc
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "verification_results.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/mutex-inl.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "driver/compiler_driver.h"
|
||||
#include "driver/compiler_options.h"
|
||||
#include "runtime.h"
|
||||
#include "thread.h"
|
||||
#include "thread-current-inl.h"
|
||||
#include "utils/atomic_dex_ref_map-inl.h"
|
||||
#include "verified_method.h"
|
||||
#include "verifier/method_verifier-inl.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
VerificationResults::VerificationResults(const CompilerOptions* compiler_options)
|
||||
: compiler_options_(compiler_options),
|
||||
verified_methods_lock_("compiler verified methods lock"),
|
||||
rejected_classes_lock_("compiler rejected classes lock") {}
|
||||
|
||||
VerificationResults::~VerificationResults() {
|
||||
WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
|
||||
STLDeleteValues(&verified_methods_);
|
||||
atomic_verified_methods_.Visit([](const DexFileReference& ref ATTRIBUTE_UNUSED,
|
||||
const VerifiedMethod* method) {
|
||||
delete method;
|
||||
});
|
||||
}
|
||||
|
||||
void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
|
||||
DCHECK(method_verifier != nullptr);
|
||||
if (!compiler_options_->IsAnyCompilationEnabled()) {
|
||||
// Verified methods are only required for quickening and compilation.
|
||||
return;
|
||||
}
|
||||
MethodReference ref = method_verifier->GetMethodReference();
|
||||
std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier));
|
||||
if (verified_method == nullptr) {
|
||||
// We'll punt this later.
|
||||
return;
|
||||
}
|
||||
AtomicMap::InsertResult result = atomic_verified_methods_.Insert(
|
||||
DexFileReference(ref.dex_file, ref.dex_method_index),
|
||||
/*expected*/ nullptr,
|
||||
verified_method.get());
|
||||
const VerifiedMethod* existing = nullptr;
|
||||
bool inserted;
|
||||
if (result != AtomicMap::kInsertResultInvalidDexFile) {
|
||||
inserted = (result == AtomicMap::kInsertResultSuccess);
|
||||
if (!inserted) {
|
||||
// Rare case.
|
||||
CHECK(atomic_verified_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index),
|
||||
&existing));
|
||||
CHECK_NE(verified_method.get(), existing);
|
||||
}
|
||||
} else {
|
||||
WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
|
||||
auto it = verified_methods_.find(ref);
|
||||
inserted = it == verified_methods_.end();
|
||||
if (inserted) {
|
||||
verified_methods_.Put(ref, verified_method.get());
|
||||
DCHECK(verified_methods_.find(ref) != verified_methods_.end());
|
||||
} else {
|
||||
existing = it->second;
|
||||
}
|
||||
}
|
||||
if (inserted) {
|
||||
// Successfully added, release the unique_ptr since we no longer have ownership.
|
||||
DCHECK_EQ(GetVerifiedMethod(ref), verified_method.get());
|
||||
verified_method.release();
|
||||
} else {
|
||||
// TODO: Investigate why are we doing the work again for this method and try to avoid it.
|
||||
LOG(WARNING) << "Method processed more than once: " << ref.PrettyMethod();
|
||||
if (!Runtime::Current()->UseJitCompilation()) {
|
||||
if (kIsDebugBuild) {
|
||||
auto ex_set = existing->GetSafeCastSet();
|
||||
auto ve_set = verified_method->GetSafeCastSet();
|
||||
CHECK_EQ(ex_set == nullptr, ve_set == nullptr);
|
||||
CHECK((ex_set == nullptr) || (ex_set->size() == ve_set->size()));
|
||||
}
|
||||
}
|
||||
// Let the unique_ptr delete the new verified method since there was already an existing one
|
||||
// registered. It is unsafe to replace the existing one since the JIT may be using it to
|
||||
// generate a native GC map.
|
||||
}
|
||||
}
|
||||
|
||||
const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
|
||||
const VerifiedMethod* ret = nullptr;
|
||||
DCHECK(compiler_options_->IsAnyCompilationEnabled());
|
||||
if (atomic_verified_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index), &ret)) {
|
||||
return ret;
|
||||
}
|
||||
ReaderMutexLock mu(Thread::Current(), verified_methods_lock_);
|
||||
auto it = verified_methods_.find(ref);
|
||||
return (it != verified_methods_.end()) ? it->second : nullptr;
|
||||
}
|
||||
|
||||
void VerificationResults::CreateVerifiedMethodFor(MethodReference ref) {
|
||||
// This method should only be called for classes verified at compile time,
|
||||
// which have no verifier error, nor has methods that we know will throw
|
||||
// at runtime.
|
||||
std::unique_ptr<VerifiedMethod> verified_method = std::make_unique<VerifiedMethod>(
|
||||
/* encountered_error_types */ 0, /* has_runtime_throw */ false);
|
||||
if (atomic_verified_methods_.Insert(DexFileReference(ref.dex_file, ref.dex_method_index),
|
||||
/*expected*/ nullptr,
|
||||
verified_method.get()) ==
|
||||
AtomicMap::InsertResult::kInsertResultSuccess) {
|
||||
verified_method.release();
|
||||
}
|
||||
}
|
||||
|
||||
void VerificationResults::AddRejectedClass(ClassReference ref) {
|
||||
{
|
||||
WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
|
||||
rejected_classes_.insert(ref);
|
||||
}
|
||||
DCHECK(IsClassRejected(ref));
|
||||
}
|
||||
|
||||
bool VerificationResults::IsClassRejected(ClassReference ref) {
|
||||
ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_);
|
||||
return (rejected_classes_.find(ref) != rejected_classes_.end());
|
||||
}
|
||||
|
||||
bool VerificationResults::IsCandidateForCompilation(MethodReference&,
|
||||
const uint32_t access_flags) {
|
||||
if (!compiler_options_->IsAotCompilationEnabled()) {
|
||||
return false;
|
||||
}
|
||||
// Don't compile class initializers unless kEverything.
|
||||
if ((compiler_options_->GetCompilerFilter() != CompilerFilter::kEverything) &&
|
||||
((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VerificationResults::AddDexFile(const DexFile* dex_file) {
|
||||
atomic_verified_methods_.AddDexFile(dex_file, dex_file->NumMethodIds());
|
||||
WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
|
||||
// There can be some verified methods that are already registered for the dex_file since we set
|
||||
// up well known classes earlier. Remove these and put them in the array so that we don't
|
||||
// accidentally miss seeing them.
|
||||
for (auto it = verified_methods_.begin(); it != verified_methods_.end(); ) {
|
||||
MethodReference ref = it->first;
|
||||
if (ref.dex_file == dex_file) {
|
||||
CHECK(atomic_verified_methods_.Insert(DexFileReference(ref.dex_file, ref.dex_method_index),
|
||||
nullptr,
|
||||
it->second) ==
|
||||
AtomicMap::kInsertResultSuccess);
|
||||
it = verified_methods_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace art
|
90
android/art/compiler/dex/verification_results.h
Normal file
90
android/art/compiler/dex/verification_results.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
|
||||
#define ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <set>
|
||||
|
||||
#include "base/dchecked_vector.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/mutex.h"
|
||||
#include "class_reference.h"
|
||||
#include "method_reference.h"
|
||||
#include "safe_map.h"
|
||||
#include "utils/atomic_dex_ref_map.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
namespace verifier {
|
||||
class MethodVerifier;
|
||||
class VerifierDepsTest;
|
||||
} // namespace verifier
|
||||
|
||||
class CompilerOptions;
|
||||
class VerifiedMethod;
|
||||
|
||||
// Used by CompilerCallbacks to track verification information from the Runtime.
|
||||
class VerificationResults {
|
||||
public:
|
||||
explicit VerificationResults(const CompilerOptions* compiler_options);
|
||||
~VerificationResults();
|
||||
|
||||
void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_)
|
||||
REQUIRES(!verified_methods_lock_);
|
||||
|
||||
void CreateVerifiedMethodFor(MethodReference ref)
|
||||
REQUIRES(!verified_methods_lock_);
|
||||
|
||||
const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
|
||||
REQUIRES(!verified_methods_lock_);
|
||||
|
||||
void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
|
||||
bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
|
||||
|
||||
bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags);
|
||||
|
||||
// Add a dex file to enable using the atomic map.
|
||||
void AddDexFile(const DexFile* dex_file) REQUIRES(!verified_methods_lock_);
|
||||
|
||||
private:
|
||||
// Verified methods. The method array is fixed to avoid needing a lock to extend it.
|
||||
using AtomicMap = AtomicDexRefMap<const VerifiedMethod*>;
|
||||
using VerifiedMethodMap = SafeMap<MethodReference,
|
||||
const VerifiedMethod*,
|
||||
MethodReferenceComparator>;
|
||||
|
||||
VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
|
||||
const CompilerOptions* const compiler_options_;
|
||||
|
||||
// Dex2oat can add dex files to atomic_verified_methods_ to avoid locking when calling
|
||||
// GetVerifiedMethod.
|
||||
AtomicMap atomic_verified_methods_;
|
||||
|
||||
ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
|
||||
|
||||
// Rejected classes.
|
||||
ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
|
||||
std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
|
||||
|
||||
friend class verifier::VerifierDepsTest;
|
||||
};
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
|
112
android/art/compiler/dex/verified_method.cc
Normal file
112
android/art/compiler/dex/verified_method.cc
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "verified_method.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "dex_file.h"
|
||||
#include "dex_instruction-inl.h"
|
||||
#include "runtime.h"
|
||||
#include "verifier/method_verifier-inl.h"
|
||||
#include "verifier/reg_type-inl.h"
|
||||
#include "verifier/register_line-inl.h"
|
||||
#include "verifier/verifier_deps.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw)
|
||||
: encountered_error_types_(encountered_error_types),
|
||||
has_runtime_throw_(has_runtime_throw) {
|
||||
}
|
||||
|
||||
const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier) {
|
||||
DCHECK(Runtime::Current()->IsAotCompiler());
|
||||
std::unique_ptr<VerifiedMethod> verified_method(
|
||||
new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(),
|
||||
method_verifier->HasInstructionThatWillThrow()));
|
||||
|
||||
if (method_verifier->HasCheckCasts()) {
|
||||
verified_method->GenerateSafeCastSet(method_verifier);
|
||||
}
|
||||
|
||||
return verified_method.release();
|
||||
}
|
||||
|
||||
bool VerifiedMethod::IsSafeCast(uint32_t pc) const {
|
||||
if (safe_cast_set_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return std::binary_search(safe_cast_set_->begin(), safe_cast_set_->end(), pc);
|
||||
}
|
||||
|
||||
void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) {
|
||||
/*
|
||||
* Walks over the method code and adds any cast instructions in which
|
||||
* the type cast is implicit to a set, which is used in the code generation
|
||||
* to elide these casts.
|
||||
*/
|
||||
if (method_verifier->HasFailures()) {
|
||||
return;
|
||||
}
|
||||
const DexFile::CodeItem* code_item = method_verifier->CodeItem();
|
||||
const Instruction* inst = Instruction::At(code_item->insns_);
|
||||
const Instruction* end = Instruction::At(code_item->insns_ +
|
||||
code_item->insns_size_in_code_units_);
|
||||
|
||||
for (; inst < end; inst = inst->Next()) {
|
||||
Instruction::Code code = inst->Opcode();
|
||||
if (code == Instruction::CHECK_CAST) {
|
||||
uint32_t dex_pc = inst->GetDexPc(code_item->insns_);
|
||||
if (!method_verifier->GetInstructionFlags(dex_pc).IsVisited()) {
|
||||
// Do not attempt to quicken this instruction, it's unreachable anyway.
|
||||
continue;
|
||||
}
|
||||
const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
|
||||
const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
|
||||
inst->VRegA_21c()));
|
||||
const verifier::RegType& cast_type =
|
||||
method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c()));
|
||||
// Pass null for the method verifier to not record the VerifierDeps dependency
|
||||
// if the types are not assignable.
|
||||
if (cast_type.IsStrictlyAssignableFrom(reg_type, /* method_verifier */ nullptr)) {
|
||||
// The types are assignable, we record that dependency in the VerifierDeps so
|
||||
// that if this changes after OTA, we will re-verify again.
|
||||
// We check if reg_type has a class, as the verifier may have inferred it's
|
||||
// 'null'.
|
||||
if (reg_type.HasClass()) {
|
||||
DCHECK(cast_type.HasClass());
|
||||
verifier::VerifierDeps::MaybeRecordAssignability(method_verifier->GetDexFile(),
|
||||
cast_type.GetClass(),
|
||||
reg_type.GetClass(),
|
||||
/* strict */ true,
|
||||
/* assignable */ true);
|
||||
}
|
||||
if (safe_cast_set_ == nullptr) {
|
||||
safe_cast_set_.reset(new SafeCastSet());
|
||||
}
|
||||
// Verify ordering for push_back() to the sorted vector.
|
||||
DCHECK(safe_cast_set_->empty() || safe_cast_set_->back() < dex_pc);
|
||||
safe_cast_set_->push_back(dex_pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
DCHECK(safe_cast_set_ == nullptr || !safe_cast_set_->empty());
|
||||
}
|
||||
|
||||
} // namespace art
|
80
android/art/compiler/dex/verified_method.h
Normal file
80
android/art/compiler/dex/verified_method.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ART_COMPILER_DEX_VERIFIED_METHOD_H_
|
||||
#define ART_COMPILER_DEX_VERIFIED_METHOD_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/mutex.h"
|
||||
#include "dex_file.h"
|
||||
#include "method_reference.h"
|
||||
#include "safe_map.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
namespace verifier {
|
||||
class MethodVerifier;
|
||||
} // namespace verifier
|
||||
|
||||
class VerifiedMethod {
|
||||
public:
|
||||
VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw);
|
||||
|
||||
// Cast elision set type.
|
||||
// Since we're adding the dex PCs to the set in increasing order, a sorted vector
|
||||
// is better for performance (not just memory usage), especially for large sets.
|
||||
typedef std::vector<uint32_t> SafeCastSet;
|
||||
|
||||
static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_);
|
||||
~VerifiedMethod() = default;
|
||||
|
||||
const SafeCastSet* GetSafeCastSet() const {
|
||||
return safe_cast_set_.get();
|
||||
}
|
||||
|
||||
// Returns true if the cast can statically be verified to be redundant
|
||||
// by using the check-cast elision peephole optimization in the verifier.
|
||||
bool IsSafeCast(uint32_t pc) const;
|
||||
|
||||
// Returns true if there were any errors during verification.
|
||||
bool HasVerificationFailures() const {
|
||||
return encountered_error_types_ != 0;
|
||||
}
|
||||
|
||||
uint32_t GetEncounteredVerificationFailures() const {
|
||||
return encountered_error_types_;
|
||||
}
|
||||
|
||||
bool HasRuntimeThrow() const {
|
||||
return has_runtime_throw_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Generate safe case set into safe_cast_set_.
|
||||
void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
|
||||
REQUIRES_SHARED(Locks::mutator_lock_);
|
||||
|
||||
std::unique_ptr<SafeCastSet> safe_cast_set_;
|
||||
|
||||
const uint32_t encountered_error_types_;
|
||||
const bool has_runtime_throw_;
|
||||
};
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_COMPILER_DEX_VERIFIED_METHOD_H_
|
Loading…
Add table
Add a link
Reference in a new issue