&table_index,
&direct_code,
&direct_method)) {
- VLOG(compiler) << "Did not compile "
- << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << " because a method call could not be resolved";
- MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod);
- return false;
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod);
+ HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ original_invoke_type);
+ return HandleInvoke(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ nullptr /* clinit_check */);
}
// Handle resolved methods (non string init).
}
}
+void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke) {
+ MoveConstant(invoke->GetLocations()->GetTemp(0), invoke->GetDexMethodIndex());
+
+ // Initialize to anything to silent compiler warnings.
+ QuickEntrypointEnum entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck;
+ switch (invoke->GetOriginalInvokeType()) {
+ case kStatic:
+ entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck;
+ break;
+ case kDirect:
+ entrypoint = kQuickInvokeDirectTrampolineWithAccessCheck;
+ break;
+ case kVirtual:
+ entrypoint = kQuickInvokeVirtualTrampolineWithAccessCheck;
+ break;
+ case kSuper:
+ entrypoint = kQuickInvokeSuperTrampolineWithAccessCheck;
+ break;
+ case kInterface:
+ entrypoint = kQuickInvokeInterfaceTrampolineWithAccessCheck;
+ break;
+ }
+ InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
+}
+
void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const {
// The DCHECKS below check that a register is not specified twice in
// the summary. The out location can overlap with an input, so we need
virtual void GenerateFrameExit() = 0;
virtual void Bind(HBasicBlock* block) = 0;
virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0;
+ virtual void MoveConstant(Location destination, int32_t value) = 0;
virtual Assembler* GetAssembler() = 0;
virtual const Assembler& GetAssembler() const = 0;
virtual size_t GetWordSize() const = 0;
static void CreateCommonInvokeLocationSummary(
HInvoke* invoke, InvokeDexCallingConventionVisitor* visitor);
+ void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke);
+
void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; }
DisassemblyInformation* GetDisassemblyInformation() const { return disasm_info_; }
+ virtual void InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) = 0;
+
protected:
// Method patch info used for recording locations of required linker patches and
// target methods. The target method can be used for various purposes, whether for
if (index + 1 < calling_convention.GetNumberOfRegisters()) {
DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1,
calling_convention.GetRegisterAt(index + 1));
+
return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index),
calling_convention.GetRegisterAt(index + 1));
} else {
}
}
+void CodeGeneratorARM::MoveConstant(Location location, int32_t value) {
+ DCHECK(location.IsRegister());
+ __ LoadImmediate(location.AsRegister<Register>(), value);
+}
+
+void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) {
+ InvokeRuntime(GetThreadOffset<kArmWordSize>(entrypoint).Int32Value(),
+ instruction,
+ dex_pc,
+ slow_path);
+}
+
void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset,
HInstruction* instruction,
uint32_t dex_pc,
codegen_->GenerateFrameExit();
}
+void LocationsBuilderARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ // The trampoline uses the same calling convention as dex calling conventions,
+ // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
+ // the method_idx.
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
+}
+
void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
// When we do not run baseline, explicit clinit checks triggered by static
// invokes must have been pruned by art::PrepareForRegisterAllocation.
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
instruction,
instruction->GetDexPc(),
nullptr);
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
instruction,
instruction->GetDexPc(),
nullptr);
void GenerateFrameExit() OVERRIDE;
void Bind(HBasicBlock* block) OVERRIDE;
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+ void MoveConstant(Location destination, int32_t value) OVERRIDE;
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
void Move64(Location destination, Location source);
// Generate code to invoke a runtime entry point.
- void InvokeRuntime(
- int32_t offset, HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path);
+ void InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) OVERRIDE;
+
+ void InvokeRuntime(int32_t offset,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path);
// Emit a write barrier.
void MarkGCCard(Register temp, Register card, Register object, Register value, bool can_be_null);
}
}
+void CodeGeneratorARM64::MoveConstant(Location location, int32_t value) {
+ DCHECK(location.IsRegister());
+ __ Mov(RegisterFrom(location, Primitive::kPrimInt), value);
+}
+
Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
Primitive::Type type = load->GetType();
}
}
+void CodeGeneratorARM64::InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) {
+ InvokeRuntime(GetThreadOffset<kArm64WordSize>(entrypoint).Int32Value(),
+ instruction,
+ dex_pc,
+ slow_path);
+}
+
void CodeGeneratorARM64::InvokeRuntime(int32_t entry_point_offset,
HInstruction* instruction,
uint32_t dex_pc,
UNUSED(constant);
}
+void LocationsBuilderARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ // The trampoline uses the same calling convention as dex calling conventions,
+ // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
+ // the method_idx.
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
+}
+
void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) {
InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
__ Mov(type_index, instruction->GetTypeIndex());
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(
- GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
+ instruction,
+ instruction->GetDexPc(),
+ nullptr);
CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
}
__ Mov(type_index, instruction->GetTypeIndex());
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(
- GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
+ instruction,
+ instruction->GetDexPc(),
+ nullptr);
CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
}
// Code generation helpers.
void MoveConstant(vixl::CPURegister destination, HConstant* constant);
+ void MoveConstant(Location destination, int32_t value) OVERRIDE;
// The type is optional. When specified it must be coherent with the
// locations, and is used for optimisation and debugging.
void MoveLocation(Location destination, Location source,
void StoreRelease(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
// Generate code to invoke a runtime entry point.
+ void InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) OVERRIDE;
+
void InvokeRuntime(int32_t offset,
HInstruction* instruction,
uint32_t dex_pc,
}
}
+void CodeGeneratorMIPS64::MoveConstant(Location location, int32_t value) {
+ DCHECK(location.IsRegister());
+ __ LoadConst32(location.AsRegister<GpuRegister>(), value);
+}
+
Location CodeGeneratorMIPS64::GetStackLocation(HLoadLocal* load) const {
Primitive::Type type = load->GetType();
stream << Mips64ManagedRegister::FromFpuRegister(FpuRegister(reg));
}
+void CodeGeneratorMIPS64::InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) {
+ InvokeRuntime(GetThreadOffset<kMips64WordSize>(entrypoint).Int32Value(),
+ instruction,
+ dex_pc,
+ slow_path);
+}
+
void CodeGeneratorMIPS64::InvokeRuntime(int32_t entry_point_offset,
HInstruction* instruction,
uint32_t dex_pc,
// Will be generated at use site.
}
+void LocationsBuilderMIPS64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ // The trampoline uses the same calling convention as dex calling conventions,
+ // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
+ // the method_idx.
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
+}
+
void LocationsBuilderMIPS64::HandleInvoke(HInvoke* invoke) {
InvokeDexCallingConventionVisitorMIPS64 calling_convention_visitor;
CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
LocationSummary* locations = instruction->GetLocations();
// Move an uint16_t value to a register.
__ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), instruction->GetTypeIndex());
- codegen_->InvokeRuntime(
- GetThreadOffset<kMips64WordSize>(instruction->GetEntrypoint()).Int32Value(),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
+ instruction,
+ instruction->GetDexPc(),
+ nullptr);
CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
}
LocationSummary* locations = instruction->GetLocations();
// Move an uint16_t value to a register.
__ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), instruction->GetTypeIndex());
- codegen_->InvokeRuntime(
- GetThreadOffset<kMips64WordSize>(instruction->GetEntrypoint()).Int32Value(),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
+ instruction,
+ instruction->GetDexPc(),
+ nullptr);
CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
}
void MoveLocation(Location destination, Location source, Primitive::Type type);
+ void MoveConstant(Location destination, int32_t value) OVERRIDE;
+
void SwapLocations(Location loc1, Location loc2, Primitive::Type type);
// Generate code to invoke a runtime entry point.
+ void InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) OVERRIDE;
+
void InvokeRuntime(int32_t offset,
HInstruction* instruction,
uint32_t dex_pc,
static constexpr int kFakeReturnRegister = Register(8);
#define __ down_cast<X86Assembler*>(codegen->GetAssembler())->
-#define QUICK_ENTRY_POINT(x) Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, x))
+#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86WordSize, x).Int32Value()
class NullCheckSlowPathX86 : public SlowPathCodeX86 {
public:
return GetFloatingPointSpillSlotSize();
}
-void CodeGeneratorX86::InvokeRuntime(Address entry_point,
+void CodeGeneratorX86::InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) {
+ InvokeRuntime(GetThreadOffset<kX86WordSize>(entrypoint).Int32Value(),
+ instruction,
+ dex_pc,
+ slow_path);
+}
+
+void CodeGeneratorX86::InvokeRuntime(int32_t entry_point_offset,
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
ValidateInvokeRuntime(instruction, slow_path);
- __ fs()->call(entry_point);
+ __ fs()->call(Address::Absolute(entry_point_offset));
RecordPcInfo(instruction, dex_pc, slow_path);
}
}
}
+void CodeGeneratorX86::MoveConstant(Location location, int32_t value) {
+ DCHECK(location.IsRegister());
+ __ movl(location.AsRegister<Register>(), Immediate(value));
+}
+
void InstructionCodeGeneratorX86::HandleGoto(HInstruction* got, HBasicBlock* successor) {
DCHECK(!successor->IsExitBlock());
codegen_->GenerateFrameExit();
}
+void LocationsBuilderX86::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ // The trampoline uses the same calling convention as dex calling conventions,
+ // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
+ // the method_idx.
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
+}
+
void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
// When we do not run baseline, explicit clinit checks triggered by static
// invokes must have been pruned by art::PrepareForRegisterAllocation.
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(
- Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
+ instruction,
+ instruction->GetDexPc(),
+ nullptr);
DCHECK(!codegen_->IsLeafMethod());
}
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(
- Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
+ instruction,
+ instruction->GetDexPc(),
+ nullptr);
DCHECK(!codegen_->IsLeafMethod());
}
void GenerateFrameExit() OVERRIDE;
void Bind(HBasicBlock* block) OVERRIDE;
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+ void MoveConstant(Location destination, int32_t value) OVERRIDE;
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
// Generate code to invoke a runtime entry point.
- void InvokeRuntime(Address entry_point,
+ void InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) OVERRIDE;
+
+ void InvokeRuntime(int32_t entry_point_offset,
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path);
static constexpr int kC2ConditionMask = 0x400;
#define __ down_cast<X86_64Assembler*>(codegen->GetAssembler())->
-#define QUICK_ENTRY_POINT(x) Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, x), true)
+#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, x).Int32Value()
class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
public:
return kX86_64WordSize;
}
-void CodeGeneratorX86_64::InvokeRuntime(Address entry_point,
+void CodeGeneratorX86_64::InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) {
+ InvokeRuntime(GetThreadOffset<kX86_64WordSize>(entrypoint).Int32Value(),
+ instruction,
+ dex_pc,
+ slow_path);
+}
+
+void CodeGeneratorX86_64::InvokeRuntime(int32_t entry_point_offset,
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
ValidateInvokeRuntime(instruction, slow_path);
- __ gs()->call(entry_point);
+ __ gs()->call(Address::Absolute(entry_point_offset, true));
RecordPcInfo(instruction, dex_pc, slow_path);
}
}
}
+void CodeGeneratorX86_64::MoveConstant(Location location, int32_t value) {
+ DCHECK(location.IsRegister());
+ Load64BitValue(location.AsRegister<CpuRegister>(), static_cast<int64_t>(value));
+}
+
void InstructionCodeGeneratorX86_64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
DCHECK(!successor->IsExitBlock());
return Location();
}
+void LocationsBuilderX86_64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ // The trampoline uses the same calling convention as dex calling conventions,
+ // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
+ // the method_idx.
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86_64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
+ codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
+}
+
void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
// When we do not run baseline, explicit clinit checks triggered by static
// invokes must have been pruned by art::PrepareForRegisterAllocation.
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(
- Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
+ instruction,
+ instruction->GetDexPc(),
+ nullptr);
DCHECK(!codegen_->IsLeafMethod());
}
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(
- Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true),
- instruction,
- instruction->GetDexPc(),
- nullptr);
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(),
+ instruction,
+ instruction->GetDexPc(),
+ nullptr);
DCHECK(!codegen_->IsLeafMethod());
}
void GenerateFrameExit() OVERRIDE;
void Bind(HBasicBlock* block) OVERRIDE;
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+ void MoveConstant(Location destination, int32_t value) OVERRIDE;
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
// Generate code to invoke a runtime entry point.
- void InvokeRuntime(Address entry_point,
+ void InvokeRuntime(QuickEntrypointEnum entrypoint,
+ HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path) OVERRIDE;
+
+ void InvokeRuntime(int32_t entry_point_offset,
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path);
invoke->GetDexMethodIndex(), GetGraph()->GetDexFile(), /* with_signature */ false);
}
+ void VisitInvokeUnresolved(HInvokeUnresolved* invoke) OVERRIDE {
+ VisitInvoke(invoke);
+ StartAttributeStream("invoke_type") << invoke->GetOriginalInvokeType();
+ }
+
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
VisitInvoke(invoke);
StartAttributeStream("recursive") << std::boolalpha
}
bool HInliner::TryInline(HInvoke* invoke_instruction) {
+ if (invoke_instruction->IsInvokeUnresolved()) {
+ return false; // Don't bother to move further if we know the method is unresolved.
+ }
+
uint32_t method_index = invoke_instruction->GetDexMethodIndex();
ScopedObjectAccess soa(Thread::Current());
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
}
if (resolved_method == nullptr) {
+ // TODO: Can this still happen?
// Method cannot be resolved if it is in another dex file we do not have access to.
VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, caller_dex_file);
return false;
M(InstanceFieldSet, Instruction) \
M(InstanceOf, Instruction) \
M(IntConstant, Constant) \
+ M(InvokeUnresolved, Invoke) \
M(InvokeInterface, Invoke) \
M(InvokeStaticOrDirect, Invoke) \
M(InvokeVirtual, Invoke) \
DISALLOW_COPY_AND_ASSIGN(HInvoke);
};
+class HInvokeUnresolved : public HInvoke {
+ public:
+ HInvokeUnresolved(ArenaAllocator* arena,
+ uint32_t number_of_arguments,
+ Primitive::Type return_type,
+ uint32_t dex_pc,
+ uint32_t dex_method_index,
+ InvokeType invoke_type)
+ : HInvoke(arena,
+ number_of_arguments,
+ 0u /* number_of_other_inputs */,
+ return_type,
+ dex_pc,
+ dex_method_index,
+ invoke_type) {
+ }
+
+ DECLARE_INSTRUCTION(InvokeUnresolved);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved);
+};
+
class HInvokeStaticOrDirect : public HInvoke {
public:
// Requirements of this method call regarding the class
#include "ssa_phi_elimination.h"
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
+#include "verifier/method_verifier.h"
namespace art {
return compiled_method;
}
+static bool HasOnlyUnresolvedFailures(const VerifiedMethod* verified_method) {
+ uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS;
+ return (verified_method->GetEncounteredVerificationFailures() & (~unresolved_mask)) == 0;
+}
+
CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type,
Handle<mirror::DexCache> dex_cache) const {
CompilerDriver* compiler_driver = GetCompilerDriver();
CompiledMethod* method = nullptr;
- DCHECK(!compiler_driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow());
- if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)) {
+ const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
+ DCHECK(!verified_method->HasRuntimeThrow());
+ if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)
+ || HasOnlyUnresolvedFailures(verified_method)) {
method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
method_idx, jclass_loader, dex_file, dex_cache);
} else {
kInlinedInvoke,
kInstructionSimplifications,
kInstructionSimplificationsArch,
+ kUnresolvedMethod,
kNotCompiledBranchOutsideMethodCode,
kNotCompiledCannotBuildSSA,
kNotCompiledCantAccesType,
kNotCompiledSpaceFilter,
kNotCompiledUnhandledInstruction,
kNotCompiledUnresolvedField,
- kNotCompiledUnresolvedMethod,
kNotCompiledUnsupportedIsa,
kNotCompiledVerifyAtRuntime,
kNotOptimizedDisabled,
case kInlinedInvoke : return "kInlinedInvoke";
case kInstructionSimplifications: return "kInstructionSimplifications";
case kInstructionSimplificationsArch: return "kInstructionSimplificationsArch";
+ case kUnresolvedMethod : return "kUnresolvedMethod";
case kNotCompiledBranchOutsideMethodCode: return "kNotCompiledBranchOutsideMethodCode";
case kNotCompiledCannotBuildSSA : return "kNotCompiledCannotBuildSSA";
case kNotCompiledCantAccesType : return "kNotCompiledCantAccesType";
case kNotCompiledSpaceFilter : return "kNotCompiledSpaceFilter";
case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction";
case kNotCompiledUnresolvedField : return "kNotCompiledUnresolvedField";
- case kNotCompiledUnresolvedMethod : return "kNotCompiledUnresolvedMethod";
case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";
case kNotCompiledVerifyAtRuntime : return "kNotCompiledVerifyAtRuntime";
case kNotOptimizedDisabled : return "kNotOptimizedDisabled";
--- /dev/null
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# We can't use src-ex testing infrastructure because src and src-ex are compiled
+# with javac independetely and can't share code (without reflection).
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+mkdir classes-ex
+mv classes/UnresolvedClass.class classes-ex
+mv classes/UnresolvedInterface.class classes-ex
+mv classes/UnresolvedSuperClass.class classes-ex
+
+if [ ${USE_JACK} = "true" ]; then
+ # Create .jack files from classes generated with javac.
+ ${JILL} classes --output classes.jack
+ ${JILL} classes-ex --output classes-ex.jack
+
+ # Create DEX files from .jack files.
+ ${JACK} --import classes.jack --output-dex .
+ zip $TEST_NAME.jar classes.dex
+ ${JACK} --import classes-ex.jack --output-dex .
+ zip ${TEST_NAME}-ex.jar classes.dex
+else
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+ zip $TEST_NAME.jar classes.dex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ zip ${TEST_NAME}-ex.jar classes.dex
+ fi
+fi
--- /dev/null
+UnresolvedClass.directCall()
+UnresolvedClass.staticMethod()
+UnresolvedClass.virtualMethod()
+UnresolvedClass.interfaceMethod()
+UnresolvedClass.superMethod()
--- /dev/null
+Test calling of unresolved methods.
--- /dev/null
+#!/bin/bash
+#
+# 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.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main extends UnresolvedSuperClass {
+
+ /// CHECK-START: void Main.callInvokeUnresolvedStatic() register (before)
+ /// CHECK: InvokeUnresolved invoke_type:static
+ static public void callInvokeUnresolvedStatic() {
+ UnresolvedClass.staticMethod();
+ }
+
+ /// CHECK-START: void Main.callInvokeUnresolvedVirtual(UnresolvedClass) register (before)
+ /// CHECK: InvokeUnresolved invoke_type:virtual
+ static public void callInvokeUnresolvedVirtual(UnresolvedClass c) {
+ c.virtualMethod();
+ }
+
+ /// CHECK-START: void Main.callInvokeUnresolvedInterface(UnresolvedInterface) register (before)
+ /// CHECK: InvokeUnresolved invoke_type:interface
+ static public void callInvokeUnresolvedInterface(UnresolvedInterface c) {
+ c.interfaceMethod();
+ }
+
+ static public void callInvokeUnresolvedSuper(Main c) {
+ c.superMethod();
+ }
+
+ /// CHECK-START: void Main.superMethod() register (before)
+ /// CHECK: InvokeUnresolved invoke_type:super
+ public void superMethod() {
+ super.superMethod();
+ }
+
+ /// CHECK-START: void Main.main(java.lang.String[]) register (before)
+ /// CHECK: InvokeUnresolved invoke_type:direct
+ static public void main(String[] args) {
+ UnresolvedClass c = new UnresolvedClass();
+ callInvokeUnresolvedStatic();
+ callInvokeUnresolvedVirtual(c);
+ callInvokeUnresolvedInterface(c);
+ callInvokeUnresolvedSuper(new Main());
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+interface UnresolvedInterface {
+ void interfaceMethod();
+}
+
+class UnresolvedSuperClass {
+ public void superMethod() {
+ System.out.println("UnresolvedClass.superMethod()");
+ }
+}
+
+class UnresolvedClass extends UnresolvedSuperClass implements UnresolvedInterface {
+ static public void staticMethod() {
+ System.out.println("UnresolvedClass.staticMethod()");
+ }
+
+ public UnresolvedClass() {
+ System.out.println("UnresolvedClass.directCall()");
+ }
+
+ public void virtualMethod() {
+ System.out.println("UnresolvedClass.virtualMethod()");
+ }
+
+ public void interfaceMethod() {
+ System.out.println("UnresolvedClass.interfaceMethod()");
+ }
+}
+
+final class UnresolvedFinalClass {
+ public void directMethod() {
+ System.out.println("UnresolvedFinalClass.directMethod()");
+ }
+}
+
+class UnresolvedAtRuntime {
+ public void unresolvedAtRuntime() { }
+}
+
shift
elif [ "x$1" = "x--secondary" ]; then
SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
+ # Enable cfg-append to make sure we get the dump for both dex files.
+ # (otherwise the runtime compilation of the secondary dex will overwrite
+ # the dump of the first one)
+ FLAGS="${FLAGS} -Xcompiler-option --dump-cfg-append"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --dump-cfg-append"
shift
elif [ "x$1" = "x--debug" ]; then
DEBUGGER="y"