OSDN Git Service

Support unresolved methods in Optimizing
authorCalin Juravle <calin@google.com>
Tue, 25 Aug 2015 14:42:32 +0000 (15:42 +0100)
committerCalin Juravle <calin@google.com>
Thu, 17 Sep 2015 11:29:51 +0000 (12:29 +0100)
Change-Id: If2da02b50d2fa668cd58f134a005f1752e7746b1

25 files changed:
compiler/optimizing/builder.cc
compiler/optimizing/code_generator.cc
compiler/optimizing/code_generator.h
compiler/optimizing/code_generator_arm.cc
compiler/optimizing/code_generator_arm.h
compiler/optimizing/code_generator_arm64.cc
compiler/optimizing/code_generator_arm64.h
compiler/optimizing/code_generator_mips64.cc
compiler/optimizing/code_generator_mips64.h
compiler/optimizing/code_generator_x86.cc
compiler/optimizing/code_generator_x86.h
compiler/optimizing/code_generator_x86_64.cc
compiler/optimizing/code_generator_x86_64.h
compiler/optimizing/graph_visualizer.cc
compiler/optimizing/inliner.cc
compiler/optimizing/nodes.h
compiler/optimizing/optimizing_compiler.cc
compiler/optimizing/optimizing_compiler_stats.h
test/529-checker-unresolved/build [new file with mode: 0644]
test/529-checker-unresolved/expected.txt [new file with mode: 0644]
test/529-checker-unresolved/info.txt [new file with mode: 0644]
test/529-checker-unresolved/run [new file with mode: 0644]
test/529-checker-unresolved/src/Main.java [new file with mode: 0644]
test/529-checker-unresolved/src/Unresolved.java [new file with mode: 0644]
test/etc/run-test-jar

index a2a0696..3663448 100644 (file)
@@ -839,11 +839,20 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
                                            &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).
index 50108a7..3c6a41d 100644 (file)
@@ -388,6 +388,31 @@ void CodeGenerator::CreateCommonInvokeLocationSummary(
   }
 }
 
+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
index d2af56a..a54dbf1 100644 (file)
@@ -169,6 +169,7 @@ class CodeGenerator {
   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;
@@ -375,9 +376,16 @@ class CodeGenerator {
   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
index c3d63b9..6f89293 100644 (file)
@@ -710,6 +710,7 @@ Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type t
       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 {
@@ -963,6 +964,21 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr
   }
 }
 
+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,
@@ -1515,6 +1531,17 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
   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.
@@ -2967,7 +2994,7 @@ void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
   __ 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);
@@ -2988,7 +3015,7 @@ void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
   __ 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);
index e44209d..91cfd00 100644 (file)
@@ -239,6 +239,7 @@ class CodeGeneratorARM : public CodeGenerator {
   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;
@@ -299,8 +300,15 @@ class CodeGeneratorARM : public CodeGenerator {
   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);
index 377eaf6..531b669 100644 (file)
@@ -716,6 +716,11 @@ void CodeGeneratorARM64::Move(HInstruction* instruction,
   }
 }
 
+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();
 
@@ -1107,6 +1112,16 @@ void CodeGeneratorARM64::StoreRelease(Primitive::Type type,
   }
 }
 
+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,
@@ -2305,6 +2320,17 @@ void InstructionCodeGeneratorARM64::VisitNullConstant(HNullConstant* constant) {
   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);
@@ -2831,11 +2857,10 @@ void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
   __ 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*>();
 }
 
@@ -2856,11 +2881,10 @@ void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction)
   __ 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*>();
 }
 
index 3211a83..576406e 100644 (file)
@@ -338,6 +338,7 @@ class CodeGeneratorARM64 : public CodeGenerator {
 
   // 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,
@@ -348,6 +349,11 @@ class CodeGeneratorARM64 : public CodeGenerator {
   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,
index 0787e49..bf0d2e2 100644 (file)
@@ -856,6 +856,11 @@ void CodeGeneratorMIPS64::Move(HInstruction* instruction,
   }
 }
 
+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();
 
@@ -973,6 +978,16 @@ void CodeGeneratorMIPS64::DumpFloatingPointRegister(std::ostream& stream, int re
   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,
@@ -2326,6 +2341,17 @@ void InstructionCodeGeneratorMIPS64::VisitNullConstant(HNullConstant* constant A
   // 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);
@@ -2775,11 +2801,10 @@ void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) {
   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*>();
 }
 
@@ -2796,11 +2821,10 @@ void InstructionCodeGeneratorMIPS64::VisitNewInstance(HNewInstance* instruction)
   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*>();
 }
 
index c754838..8511eb6 100644 (file)
@@ -283,9 +283,16 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
 
   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,
index c7ddabb..9c5ecc3 100644 (file)
@@ -47,7 +47,7 @@ static constexpr int kC2ConditionMask = 0x400;
 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:
@@ -420,12 +420,22 @@ size_t CodeGeneratorX86::RestoreFloatingPointRegister(size_t stack_index, uint32
   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);
 }
 
@@ -889,6 +899,11 @@ void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstr
   }
 }
 
+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());
 
@@ -1505,6 +1520,17 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) {
   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.
@@ -3360,11 +3386,10 @@ void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
   __ 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());
 }
 
@@ -3384,11 +3409,10 @@ void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
 
   // 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());
 }
 
index c63634d..f3307cf 100644 (file)
@@ -228,13 +228,19 @@ class CodeGeneratorX86 : public CodeGenerator {
   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);
index 82c037a..134bfed 100644 (file)
@@ -48,7 +48,7 @@ static constexpr FloatRegister kFpuCalleeSaves[] = { XMM12, XMM13, XMM14, XMM15
 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:
@@ -566,12 +566,22 @@ size_t CodeGeneratorX86_64::RestoreFloatingPointRegister(size_t stack_index, uin
   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);
 }
 
@@ -913,6 +923,11 @@ void CodeGeneratorX86_64::Move(HInstruction* instruction,
   }
 }
 
+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());
 
@@ -1686,6 +1701,17 @@ Location InvokeDexCallingConventionVisitorX86_64::GetNextLocation(Primitive::Typ
   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.
@@ -3382,11 +3408,10 @@ void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction)
   // 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());
 }
@@ -3408,11 +3433,10 @@ void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
 
   // 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());
 }
index 522f036..9b2423f 100644 (file)
@@ -228,13 +228,19 @@ class CodeGeneratorX86_64 : public CodeGenerator {
   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);
index 60a5955..d05c514 100644 (file)
@@ -380,6 +380,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
         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
index 12fd1c3..039029a 100644 (file)
@@ -173,6 +173,10 @@ static uint32_t FindMethodIndexIn(ArtMethod* method,
 }
 
 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();
@@ -194,6 +198,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) {
   }
 
   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;
index 70002ad..90ff20c 100644 (file)
@@ -1033,6 +1033,7 @@ class HLoopInformationOutwardIterator : public ValueObject {
   M(InstanceFieldSet, Instruction)                                      \
   M(InstanceOf, Instruction)                                            \
   M(IntConstant, Constant)                                              \
+  M(InvokeUnresolved, Invoke)                                           \
   M(InvokeInterface, Invoke)                                            \
   M(InvokeStaticOrDirect, Invoke)                                       \
   M(InvokeVirtual, Invoke)                                              \
@@ -3057,6 +3058,29 @@ class HInvoke : public HInstruction {
   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
index 092e3c2..a2b6131 100644 (file)
@@ -66,6 +66,7 @@
 #include "ssa_phi_elimination.h"
 #include "ssa_liveness_analysis.h"
 #include "utils/assembler.h"
+#include "verifier/method_verifier.h"
 
 namespace art {
 
@@ -835,6 +836,11 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite
   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,
@@ -845,8 +851,10 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
                                             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 {
index da5cb57..c7701b7 100644 (file)
@@ -33,6 +33,7 @@ enum MethodCompilationStat {
   kInlinedInvoke,
   kInstructionSimplifications,
   kInstructionSimplificationsArch,
+  kUnresolvedMethod,
   kNotCompiledBranchOutsideMethodCode,
   kNotCompiledCannotBuildSSA,
   kNotCompiledCantAccesType,
@@ -45,7 +46,6 @@ enum MethodCompilationStat {
   kNotCompiledSpaceFilter,
   kNotCompiledUnhandledInstruction,
   kNotCompiledUnresolvedField,
-  kNotCompiledUnresolvedMethod,
   kNotCompiledUnsupportedIsa,
   kNotCompiledVerifyAtRuntime,
   kNotOptimizedDisabled,
@@ -103,6 +103,7 @@ class OptimizingCompilerStats {
       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";
@@ -115,7 +116,6 @@ class OptimizingCompilerStats {
       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";
diff --git a/test/529-checker-unresolved/build b/test/529-checker-unresolved/build
new file mode 100644 (file)
index 0000000..8c3c4f8
--- /dev/null
@@ -0,0 +1,48 @@
+#!/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
diff --git a/test/529-checker-unresolved/expected.txt b/test/529-checker-unresolved/expected.txt
new file mode 100644 (file)
index 0000000..358048c
--- /dev/null
@@ -0,0 +1,5 @@
+UnresolvedClass.directCall()
+UnresolvedClass.staticMethod()
+UnresolvedClass.virtualMethod()
+UnresolvedClass.interfaceMethod()
+UnresolvedClass.superMethod()
diff --git a/test/529-checker-unresolved/info.txt b/test/529-checker-unresolved/info.txt
new file mode 100644 (file)
index 0000000..14efcc0
--- /dev/null
@@ -0,0 +1 @@
+Test calling of unresolved methods.
diff --git a/test/529-checker-unresolved/run b/test/529-checker-unresolved/run
new file mode 100644 (file)
index 0000000..63fdb8c
--- /dev/null
@@ -0,0 +1,18 @@
+#!/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
diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java
new file mode 100644 (file)
index 0000000..6f04797
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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());
+  }
+}
diff --git a/test/529-checker-unresolved/src/Unresolved.java b/test/529-checker-unresolved/src/Unresolved.java
new file mode 100644 (file)
index 0000000..5bf92dd
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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() { }
+}
+
index efc0bfb..fbefa07 100755 (executable)
@@ -117,6 +117,11 @@ while true; do
         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"