upload android base code part3

This commit is contained in:
August 2018-08-08 16:48:17 +08:00
parent 71b83c22f1
commit b9e30e05b1
15122 changed files with 2089659 additions and 0 deletions

View 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

View 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_

View 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

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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

View 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_