diff options
Diffstat (limited to 'compiler')
43 files changed, 2389 insertions, 804 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 25dfb0a192..c6662c2181 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -59,7 +59,8 @@ LIBART_COMPILER_SRC_FILES := \ dex/frontend.cc \ dex/mir_graph.cc \ dex/mir_analysis.cc \ - dex/verified_methods_data.cc \ + dex/verified_method.cc \ + dex/verification_results.cc \ dex/vreg_analysis.cc \ dex/ssa_transformation.cc \ driver/compiler_driver.cc \ diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 3368132a0e..ff8fea0f88 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -176,8 +176,7 @@ Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) { if (!kEnableCheckCastEllision || !PerformOptimizations()) { return inst; } - MethodReference referrer(&GetDexFile(), unit_.GetDexMethodIndex()); - if (!driver_.IsSafeCast(referrer, dex_pc)) { + if (!driver_.IsSafeCast(&unit_, dex_pc)) { return inst; } // Ok, this is a safe cast. Since the "check-cast" instruction size is 2 code @@ -272,15 +271,16 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, } // namespace optimizer } // namespace art -extern "C" void ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, +extern "C" void ArtCompileDEX(art::CompilerDriver& driver, const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file, art::DexToDexCompilationLevel dex_to_dex_compilation_level) { if (dex_to_dex_compilation_level != art::kDontDexToDexCompile) { art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(), - dex_file, code_item, class_def_idx, method_idx, access_flags); - art::optimizer::DexCompiler dex_compiler(compiler, unit, dex_to_dex_compilation_level); + dex_file, code_item, class_def_idx, method_idx, access_flags, + driver.GetVerifiedMethod(&dex_file, method_idx)); + art::optimizer::DexCompiler dex_compiler(driver, unit, dex_to_dex_compilation_level); dex_compiler.Compile(); } } diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 364a8bc55b..f5bb85a910 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -141,25 +141,24 @@ CompilationUnit::CompilationUnit(ArenaPool* pool) CompilationUnit::~CompilationUnit() { } +// TODO: Add a cumulative version of logging, and combine with dex2oat --dump-timing void CompilationUnit::StartTimingSplit(const char* label) { - if (compiler_driver->GetDumpPasses()) { + if (enable_debug & (1 << kDebugTimings)) { timings.StartSplit(label); } } void CompilationUnit::NewTimingSplit(const char* label) { - if (compiler_driver->GetDumpPasses()) { + if (enable_debug & (1 << kDebugTimings)) { timings.NewSplit(label); } } void CompilationUnit::EndTiming() { - if (compiler_driver->GetDumpPasses()) { + if (enable_debug & (1 << kDebugTimings)) { timings.EndSplit(); - if (enable_debug & (1 << kDebugTimings)) { - LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file); - LOG(INFO) << Dumpable<TimingLogger>(timings); - } + LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file); + LOG(INFO) << Dumpable<TimingLogger>(timings); } } @@ -317,9 +316,6 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, } cu.EndTiming(); - compiler.GetTimingsLogger().Start(); - compiler.GetTimingsLogger().AddLogger(cu.timings); - compiler.GetTimingsLogger().End(); return result; } diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index 8eb6684d7f..8ce12067ee 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -18,12 +18,7 @@ #define ART_COMPILER_DEX_FRONTEND_H_ #include "dex_file.h" -#include "dex_instruction.h" - - - - - +#include "invoke_type.h" namespace llvm { class Module; @@ -82,9 +77,6 @@ enum debugControlVector { kDebugTimings }; -class DexFileToMethodInlinerMap; -class CompilerDriver; - class LLVMInfo { public: LLVMInfo(); @@ -113,8 +105,8 @@ class LLVMInfo { UniquePtr<art::llvm::IRBuilder> ir_builder_; }; -struct CompilationUnit; -struct BasicBlock; +struct CompiledMethod; +class CompilerDriver; } // namespace art diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index 75883b7bd6..9e83210012 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -380,9 +380,6 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; } - if (mir->meta.throw_insn != NULL) { - mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; - } // Use side effect to note range check completed. (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); // Establish value number for loaded register. Note use of memory version. @@ -421,9 +418,6 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; } - if (mir->meta.throw_insn != NULL) { - mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; - } // Use side effect to note range check completed. (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); // Rev the memory version @@ -447,9 +441,6 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } else { null_checked_.insert(base); } - if (mir->meta.throw_insn != NULL) { - mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; - } uint16_t field_ref = mir->dalvikInsn.vC; uint16_t memory_version = GetMemoryVersion(base, field_ref); if (opcode == Instruction::IGET_WIDE) { @@ -479,9 +470,6 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } else { null_checked_.insert(base); } - if (mir->meta.throw_insn != NULL) { - mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; - } uint16_t field_ref = mir->dalvikInsn.vC; AdvanceMemoryVersion(base, field_ref); } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 8d1653fc6c..9dbb3417a3 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -126,9 +126,6 @@ BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, bottom_block->terminated_by_return = orig_block->terminated_by_return; orig_block->terminated_by_return = false; - /* Add it to the quick lookup cache */ - dex_pc_to_block_map_.Put(bottom_block->start_offset, bottom_block->id); - /* Handle the taken path */ bottom_block->taken = orig_block->taken; if (bottom_block->taken != NullBasicBlockId) { @@ -177,19 +174,29 @@ BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, } // Associate dex instructions in the bottom block with the new container. - MIR* p = bottom_block->first_mir_insn; - while (p != NULL) { + DCHECK(insn != nullptr); + DCHECK(insn != orig_block->first_mir_insn); + DCHECK(insn == bottom_block->first_mir_insn); + DCHECK_EQ(insn->offset, bottom_block->start_offset); + DCHECK(static_cast<int>(insn->dalvikInsn.opcode) == kMirOpCheck || + !IsPseudoMirOp(insn->dalvikInsn.opcode)); + DCHECK_EQ(dex_pc_to_block_map_.Get(insn->offset), orig_block->id); + MIR* p = insn; + dex_pc_to_block_map_.Put(p->offset, bottom_block->id); + while (p != bottom_block->last_mir_insn) { + p = p->next; + DCHECK(p != nullptr); int opcode = p->dalvikInsn.opcode; /* * Some messiness here to ensure that we only enter real opcodes and only the * first half of a potentially throwing instruction that has been split into - * CHECK and work portions. The 2nd half of a split operation will have a non-null - * throw_insn pointer that refers to the 1st half. + * CHECK and work portions. Since the 2nd half of a split operation is always + * the first in a BasicBlock, we can't hit it here. */ - if ((opcode == kMirOpCheck) || (!IsPseudoMirOp(opcode) && (p->meta.throw_insn == NULL))) { + if ((opcode == kMirOpCheck) || !IsPseudoMirOp(opcode)) { + DCHECK_EQ(dex_pc_to_block_map_.Get(p->offset), orig_block->id); dex_pc_to_block_map_.Put(p->offset, bottom_block->id); } - p = (p == bottom_block->last_mir_insn) ? NULL : p->next; } return bottom_block; @@ -508,7 +515,6 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffse static_cast<Instruction::Code>(kMirOpCheck); // Associate the two halves insn->meta.throw_insn = new_insn; - new_insn->meta.throw_insn = insn; AppendMIR(new_block, new_insn); return new_block; } @@ -523,7 +529,8 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ current_offset_ = 0; // TODO: will need to snapshot stack image and use that as the mir context identification. m_units_.push_back(new DexCompilationUnit(cu_, class_loader, Runtime::Current()->GetClassLinker(), - dex_file, current_code_item_, class_def_idx, method_idx, access_flags)); + dex_file, current_code_item_, class_def_idx, method_idx, access_flags, + cu_->compiler_driver->GetVerifiedMethod(&dex_file, method_idx))); const uint16_t* code_ptr = current_code_item_->insns_; const uint16_t* code_end = current_code_item_->insns_ + current_code_item_->insns_size_in_code_units_; @@ -973,7 +980,7 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { str.append(StringPrintf(", #%d", insn.vB)); break; case Instruction::k51l: // Add one wide immediate - str.append(StringPrintf(", #%lld", insn.vB_wide)); + str.append(StringPrintf(", #%" PRId64, insn.vB_wide)); break; case Instruction::k21c: // One register, one string/type/method index case Instruction::k31c: @@ -1026,7 +1033,7 @@ std::string MIRGraph::GetSSANameWithConst(int ssa_reg, bool singles_only) { } if (IsConst(reg_location_[ssa_reg])) { if (!singles_only && reg_location_[ssa_reg].wide) { - return StringPrintf("v%d_%d#0x%llx", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), + return StringPrintf("v%d_%d#0x%" PRIx64, SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), ConstantValueWide(reg_location_[ssa_reg])); } else { return StringPrintf("v%d_%d#0x%x", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index b68e6997ae..4666d1e47a 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -253,8 +253,10 @@ struct MIR { union { // Incoming edges for phi node. BasicBlockId* phi_incoming; - // Establish link between two halves of throwing instructions. + // Establish link from check instruction (kMirOpCheck) to the actual throwing instruction. MIR* throw_insn; + // Fused cmp branch condition. + ConditionCode ccode; } meta; }; diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index ee9f28e184..0d53d4cba2 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -259,7 +259,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { if ((ccode != kCondNv) && (mir->ssa_rep->defs[0] == mir_next->ssa_rep->uses[0]) && (GetSSAUseCount(mir->ssa_rep->defs[0]) == 1)) { - mir_next->dalvikInsn.arg[0] = ccode; + mir_next->meta.ccode = ccode; switch (opcode) { case Instruction::CMPL_FLOAT: mir_next->dalvikInsn.opcode = @@ -323,9 +323,10 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { break; } // Is this the select pattern? - // TODO: flesh out support for Mips and X86. NOTE: llvm's select op doesn't quite work here. + // TODO: flesh out support for Mips. NOTE: llvm's select op doesn't quite work here. // TUNING: expand to support IF_xx compare & branches - if (!(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) && + if ((cu_->compiler_backend != kPortable) && + (cu_->instruction_set == kThumb2 || cu_->instruction_set == kX86) && ((mir->dalvikInsn.opcode == Instruction::IF_EQZ) || (mir->dalvikInsn.opcode == Instruction::IF_NEZ))) { BasicBlock* ft = GetBasicBlock(bb->fall_through); @@ -391,6 +392,11 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { } } if (const_form) { + /* + * TODO: If both constants are the same value, then instead of generating + * a select, we should simply generate a const bytecode. This should be + * considered after inlining which can lead to CFG of this form. + */ // "true" set val in vB mir->dalvikInsn.vB = if_true->dalvikInsn.vB; // "false" set val in vC diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 2bc579a675..32673db41e 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_DEX_QUICK_ARM_CODEGEN_ARM_H_ #define ART_COMPILER_DEX_QUICK_ARM_CODEGEN_ARM_H_ +#include "arm_lir.h" #include "dex/compiler_internals.h" namespace art { @@ -94,9 +95,9 @@ class ArmMir2Lir : public Mir2Lir { RegLocation rl_src, int scale, bool card_mark); void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift); - void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenMulLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAndLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, @@ -110,9 +111,9 @@ class ArmMir2Lir : public Mir2Lir { bool GenInlinedPeek(CallInfo* info, OpSize size); bool GenInlinedPoke(CallInfo* info, OpSize size); void GenNegLong(RegLocation rl_dest, RegLocation rl_src); - void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenOrLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenSubLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind); RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); @@ -191,10 +192,12 @@ class ArmMir2Lir : public Mir2Lir { MIR* SpecialIPut(BasicBlock** bb, MIR* mir, OpSize size, bool long_or_double, bool is_object); MIR* SpecialIdentity(MIR* mir); LIR* LoadFPConstantValue(int r_dest, int value); - bool BadOverlap(RegLocation rl_src, RegLocation rl_dest); void ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir); void InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir); void AssignDataOffsets(); + RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, bool check_zero); + RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div); }; } // namespace art diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc index 1a9d9c5e70..46542e118c 100644 --- a/compiler/dex/quick/arm/fp_arm.cc +++ b/compiler/dex/quick/arm/fp_arm.cc @@ -209,7 +209,7 @@ void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, NewLIR2(kThumb2Vcmps, rl_src1.low_reg, rl_src2.low_reg); } NewLIR0(kThumb2Fmstat); - ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]); + ConditionCode ccode = mir->meta.ccode; switch (ccode) { case kCondEq: case kCondNe: diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 86ae75e29b..150794e75d 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -228,7 +228,7 @@ void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0); RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2); // Normalize such that if either operand is constant, src2 will be constant. - ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]); + ConditionCode ccode = mir->meta.ccode; if (rl_src1.is_const) { std::swap(rl_src1, rl_src2); ccode = FlipComparisonOrder(ccode); @@ -444,6 +444,17 @@ LIR* ArmMir2Lir::GenRegMemCheck(ConditionCode c_code, return NULL; } +RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, bool check_zero) { + LOG(FATAL) << "Unexpected use of GenDivRem for Arm"; + return rl_dest; +} + +RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) { + LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm"; + return rl_dest; +} + RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit, bool is_div) { RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); @@ -783,20 +794,8 @@ void ArmMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { StoreValueWide(rl_dest, rl_result); } - - /* - * Check to see if a result pair has a misaligned overlap with an operand pair. This - * is not usual for dx to generate, but it is legal (for now). In a future rev of - * dex, we'll want to make this case illegal. - */ -bool ArmMir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest) { - DCHECK(rl_src.wide); - DCHECK(rl_dest.wide); - return (abs(mir_graph_->SRegToVReg(rl_src.s_reg_low) - mir_graph_->SRegToVReg(rl_dest.s_reg_low)) == 1); -} - -void ArmMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { +void ArmMir2Lir::GenMulLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { /* * To pull off inline multiply, we have a worst-case requirement of 8 temporary * registers. Normally for Arm, we get 5. We can get to 6 by including @@ -868,27 +867,27 @@ void ArmMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, UnmarkTemp(rARM_LR); } -void ArmMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, +void ArmMir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenAddLong for Arm"; } -void ArmMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, +void ArmMir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenSubLong for Arm"; } -void ArmMir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1, +void ArmMir2Lir::GenAndLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenAndLong for Arm"; } -void ArmMir2Lir::GenOrLong(RegLocation rl_dest, RegLocation rl_src1, +void ArmMir2Lir::GenOrLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenOrLong for Arm"; } -void ArmMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1, +void ArmMir2Lir::GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of genXoLong for Arm"; } diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 759104150d..ceec7d50ce 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -14,10 +14,12 @@ * limitations under the License. */ +#include "codegen_arm.h" + +#include <inttypes.h> + #include <string> -#include "arm_lir.h" -#include "codegen_arm.h" #include "dex/compiler_internals.h" #include "dex/quick/mir_to_lir-inl.h" @@ -407,9 +409,8 @@ std::string ArmMir2Lir::BuildInsnString(const char* fmt, LIR* lir, unsigned char strcpy(tbuf, cc_names[operand]); break; case 't': - snprintf(tbuf, arraysize(tbuf), "0x%08x (L%p)", - reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + - (operand << 1), + snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)", + reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1), lir->target); break; case 'u': { diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 29554c0977..7f19ea1eb9 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -21,7 +21,8 @@ #include "mir_to_lir-inl.h" #include "dex/quick/dex_file_method_inliner.h" #include "dex/quick/dex_file_to_method_inliner_map.h" -#include "dex/verified_methods_data.h" +#include "dex/verification_results.h" +#include "dex/verified_method.h" #include "verifier/dex_gc_map.h" #include "verifier/method_verifier.h" @@ -34,7 +35,7 @@ template <typename It> void DumpMappingTable(const char* table_name, const char* descriptor, const char* name, const Signature& signature, uint32_t size, It first) { if (size != 0) { - std::string line(StringPrintf("\n %s %s%s_%s_table[%zu] = {", table_name, + std::string line(StringPrintf("\n %s %s%s_%s_table[%u] = {", table_name, descriptor, name, signature.ToString().c_str(), size)); std::replace(line.begin(), line.end(), ';', '_'); LOG(INFO) << line; @@ -234,8 +235,8 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) { lir, base_addr)); std::string op_operands(BuildInsnString(GetTargetInstFmt(lir->opcode), lir, base_addr)); - LOG(INFO) << StringPrintf("%05x: %-9s%s%s", - reinterpret_cast<unsigned int>(base_addr + offset), + LOG(INFO) << StringPrintf("%5p: %-9s%s%s", + base_addr + offset, op_name.c_str(), op_operands.c_str(), lir->flags.is_nop ? "(nop)" : ""); } @@ -440,6 +441,20 @@ void Mir2Lir::InstallLiteralPools() { PushPointer(code_buffer_, &id); data_lir = NEXT_LIR(data_lir); } + // Push class literals. + data_lir = class_literal_list_; + while (data_lir != NULL) { + uint32_t target = data_lir->operands[0]; + cu_->compiler_driver->AddClassPatch(cu_->dex_file, + cu_->class_def_idx, + cu_->method_idx, + target, + code_buffer_.size()); + const DexFile::TypeId& id = cu_->dex_file->GetTypeId(target); + // unique value based on target to ensure code deduplication works + PushPointer(code_buffer_, &id); + data_lir = NEXT_LIR(data_lir); + } } /* Write the switch tables to the output stream */ @@ -749,10 +764,10 @@ void Mir2Lir::CreateNativeGcMap() { } } MethodReference method_ref(cu_->dex_file, cu_->method_idx); - const std::vector<uint8_t>* gc_map_raw = - cu_->compiler_driver->GetVerifiedMethodsData()->GetDexGcMap(method_ref); - verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[0]); - DCHECK_EQ(gc_map_raw->size(), dex_gc_map.RawSize()); + const std::vector<uint8_t>& gc_map_raw = + mir_graph_->GetCurrentDexCompilationUnit()->GetVerifiedMethod()->GetDexGcMap(); + verifier::DexPcToReferenceMap dex_gc_map(&(gc_map_raw)[0]); + DCHECK_EQ(gc_map_raw.size(), dex_gc_map.RawSize()); // Compute native offset to references size. NativePcToReferenceMapBuilder native_gc_map_builder(&native_gc_map_, mapping_table.PcToDexSize(), @@ -772,6 +787,7 @@ int Mir2Lir::AssignLiteralOffset(CodeOffset offset) { offset = AssignLiteralOffsetCommon(literal_list_, offset); offset = AssignLiteralPointerOffsetCommon(code_literal_list_, offset); offset = AssignLiteralPointerOffsetCommon(method_literal_list_, offset); + offset = AssignLiteralPointerOffsetCommon(class_literal_list_, offset); return offset; } @@ -960,6 +976,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena : Backend(arena), literal_list_(NULL), method_literal_list_(NULL), + class_literal_list_(NULL), code_literal_list_(NULL), first_fixup_(NULL), cu_(cu), @@ -1121,4 +1138,28 @@ void Mir2Lir::InsertLIRAfter(LIR* current_lir, LIR* new_lir) { new_lir->next->prev = new_lir; } +bool Mir2Lir::IsPowerOfTwo(uint64_t x) { + return (x & (x - 1)) == 0; +} + +// Returns the index of the lowest set bit in 'x'. +int32_t Mir2Lir::LowestSetBit(uint64_t x) { + int bit_posn = 0; + while ((x & 0xf) == 0) { + bit_posn += 4; + x >>= 4; + } + while ((x & 1) == 0) { + bit_posn++; + x >>= 1; + } + return bit_posn; +} + +bool Mir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest) { + DCHECK(rl_src.wide); + DCHECK(rl_dest.wide); + return (abs(mir_graph_->SRegToVReg(rl_src.s_reg_low) - mir_graph_->SRegToVReg(rl_dest.s_reg_low)) == 1); +} + } // namespace art diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 3bd0298a22..522bacb223 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -19,6 +19,7 @@ #include "dex/quick/mir_to_lir-inl.h" #include "entrypoints/quick/quick_entrypoints.h" #include "mirror/array.h" +#include "mirror/object-inl.h" #include "verifier/method_verifier.h" namespace art { @@ -206,13 +207,43 @@ void Mir2Lir::GenNewArray(uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src) { FlushAllRegs(); /* Everything to home location */ ThreadOffset func_offset(-1); - if (cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, *cu_->dex_file, + const DexFile* dex_file = cu_->dex_file; + CompilerDriver* driver = cu_->compiler_driver; + if (cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, *dex_file, type_idx)) { - func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocArray); + bool is_type_initialized; // Ignored as an array does not have an initializer. + bool use_direct_type_ptr; + uintptr_t direct_type_ptr; + if (kEmbedClassInCode && + driver->CanEmbedTypeInCode(*dex_file, type_idx, + &is_type_initialized, &use_direct_type_ptr, &direct_type_ptr)) { + // The fast path. + if (!use_direct_type_ptr) { + // Use the literal pool and a PC-relative load from a data word. + LIR* data_target = ScanLiteralPool(class_literal_list_, type_idx, 0); + if (data_target == nullptr) { + data_target = AddWordData(&class_literal_list_, type_idx); + } + LIR* load_pc_rel = OpPcRelLoad(TargetReg(kArg0), data_target); + AppendLIR(load_pc_rel); + func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocArrayResolved); + CallRuntimeHelperRegMethodRegLocation(func_offset, TargetReg(kArg0), rl_src, true); + } else { + // Use the direct pointer. + func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocArrayResolved); + CallRuntimeHelperImmMethodRegLocation(func_offset, direct_type_ptr, rl_src, true); + } + } else { + // The slow path. + DCHECK_EQ(func_offset.Int32Value(), -1); + func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocArray); + CallRuntimeHelperImmMethodRegLocation(func_offset, type_idx, rl_src, true); + } + DCHECK_NE(func_offset.Int32Value(), -1); } else { func_offset= QUICK_ENTRYPOINT_OFFSET(pAllocArrayWithAccessCheck); + CallRuntimeHelperImmMethodRegLocation(func_offset, type_idx, rl_src, true); } - CallRuntimeHelperImmMethodRegLocation(func_offset, type_idx, rl_src, true); RegLocation rl_result = GetReturn(false); StoreValue(rl_dest, rl_result); } @@ -883,13 +914,53 @@ void Mir2Lir::GenNewInstance(uint32_t type_idx, RegLocation rl_dest) { // alloc will always check for resolution, do we also need to verify // access because the verifier was unable to? ThreadOffset func_offset(-1); - if (cu_->compiler_driver->CanAccessInstantiableTypeWithoutChecks( - cu_->method_idx, *cu_->dex_file, type_idx)) { - func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocObject); + const DexFile* dex_file = cu_->dex_file; + CompilerDriver* driver = cu_->compiler_driver; + if (driver->CanAccessInstantiableTypeWithoutChecks( + cu_->method_idx, *dex_file, type_idx)) { + bool is_type_initialized; + bool use_direct_type_ptr; + uintptr_t direct_type_ptr; + if (kEmbedClassInCode && + driver->CanEmbedTypeInCode(*dex_file, type_idx, + &is_type_initialized, &use_direct_type_ptr, &direct_type_ptr)) { + // The fast path. + if (!use_direct_type_ptr) { + // Use the literal pool and a PC-relative load from a data word. + LIR* data_target = ScanLiteralPool(class_literal_list_, type_idx, 0); + if (data_target == nullptr) { + data_target = AddWordData(&class_literal_list_, type_idx); + } + LIR* load_pc_rel = OpPcRelLoad(TargetReg(kArg0), data_target); + AppendLIR(load_pc_rel); + if (!is_type_initialized) { + func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocObjectResolved); + CallRuntimeHelperRegMethod(func_offset, TargetReg(kArg0), true); + } else { + func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocObjectInitialized); + CallRuntimeHelperRegMethod(func_offset, TargetReg(kArg0), true); + } + } else { + // Use the direct pointer. + if (!is_type_initialized) { + func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocObjectResolved); + CallRuntimeHelperImmMethod(func_offset, direct_type_ptr, true); + } else { + func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocObjectInitialized); + CallRuntimeHelperImmMethod(func_offset, direct_type_ptr, true); + } + } + } else { + // The slow path. + DCHECK_EQ(func_offset.Int32Value(), -1); + func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocObject); + CallRuntimeHelperImmMethod(func_offset, type_idx, true); + } + DCHECK_NE(func_offset.Int32Value(), -1); } else { func_offset = QUICK_ENTRYPOINT_OFFSET(pAllocObjectWithAccessCheck); + CallRuntimeHelperImmMethod(func_offset, type_idx, true); } - CallRuntimeHelperImmMethod(func_offset, type_idx, true); RegLocation rl_result = GetReturn(false); StoreValue(rl_dest, rl_result); } @@ -1092,8 +1163,7 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ // Note: currently type_known_final is unused, as optimizing will only improve the performance // of the exception throw path. DexCompilationUnit* cu = mir_graph_->GetCurrentDexCompilationUnit(); - const MethodReference mr(cu->GetDexFile(), cu->GetDexMethodIndex()); - if (!needs_access_check && cu_->compiler_driver->IsSafeCast(mr, insn_idx)) { + if (!needs_access_check && cu_->compiler_driver->IsSafeCast(cu, insn_idx)) { // Verifier type analysis proved this check cast would never cause an exception. return; } @@ -1341,6 +1411,9 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, } rl_result = GenDivRem(rl_dest, rl_src1.low_reg, rl_src2.low_reg, op == kOpDiv); done = true; + } else if (cu_->instruction_set == kX86) { + rl_result = GenDivRem(rl_dest, rl_src1, rl_src2, op == kOpDiv, check_zero); + done = true; } else if (cu_->instruction_set == kThumb2) { if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) { // Use ARM SDIV instruction for division. For remainder we also need to @@ -1382,30 +1455,12 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, * or produce corresponding Thumb instructions directly. */ -static bool IsPowerOfTwo(int x) { - return (x & (x - 1)) == 0; -} - // Returns true if no more than two bits are set in 'x'. static bool IsPopCountLE2(unsigned int x) { x &= x - 1; return (x & (x - 1)) == 0; } -// Returns the index of the lowest set bit in 'x'. -static int32_t LowestSetBit(uint32_t x) { - int bit_posn = 0; - while ((x & 0xf) == 0) { - bit_posn += 4; - x >>= 4; - } - while ((x & 1) == 0) { - bit_posn++; - x >>= 1; - } - return bit_posn; -} - // Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit' // and store the result in 'rl_dest'. bool Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div, @@ -1609,6 +1664,9 @@ void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, Re rl_src = LoadValue(rl_src, kCoreReg); rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div); done = true; + } else if (cu_->instruction_set == kX86) { + rl_result = GenDivRemLit(rl_dest, rl_src, lit, is_div); + done = true; } else if (cu_->instruction_set == kThumb2) { if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) { // Use ARM SDIV instruction for division. For remainder we also need to @@ -1677,7 +1735,7 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, case Instruction::ADD_LONG: case Instruction::ADD_LONG_2ADDR: if (cu_->instruction_set != kThumb2) { - GenAddLong(rl_dest, rl_src1, rl_src2); + GenAddLong(opcode, rl_dest, rl_src1, rl_src2); return; } first_op = kOpAdd; @@ -1686,7 +1744,7 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, case Instruction::SUB_LONG: case Instruction::SUB_LONG_2ADDR: if (cu_->instruction_set != kThumb2) { - GenSubLong(rl_dest, rl_src1, rl_src2); + GenSubLong(opcode, rl_dest, rl_src1, rl_src2); return; } first_op = kOpSub; @@ -1694,8 +1752,8 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, break; case Instruction::MUL_LONG: case Instruction::MUL_LONG_2ADDR: - if (cu_->instruction_set == kThumb2) { - GenMulLong(rl_dest, rl_src1, rl_src2); + if (cu_->instruction_set != kMips) { + GenMulLong(opcode, rl_dest, rl_src1, rl_src2); return; } else { call_out = true; @@ -1721,7 +1779,7 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, case Instruction::AND_LONG_2ADDR: case Instruction::AND_LONG: if (cu_->instruction_set == kX86) { - return GenAndLong(rl_dest, rl_src1, rl_src2); + return GenAndLong(opcode, rl_dest, rl_src1, rl_src2); } first_op = kOpAnd; second_op = kOpAnd; @@ -1729,7 +1787,7 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, case Instruction::OR_LONG: case Instruction::OR_LONG_2ADDR: if (cu_->instruction_set == kX86) { - GenOrLong(rl_dest, rl_src1, rl_src2); + GenOrLong(opcode, rl_dest, rl_src1, rl_src2); return; } first_op = kOpOr; @@ -1738,7 +1796,7 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, case Instruction::XOR_LONG: case Instruction::XOR_LONG_2ADDR: if (cu_->instruction_set == kX86) { - GenXorLong(rl_dest, rl_src1, rl_src2); + GenXorLong(opcode, rl_dest, rl_src1, rl_src2); return; } first_op = kOpXor; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index d942a24a18..4bc1a37d81 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -142,6 +142,30 @@ void Mir2Lir::CallRuntimeHelperImmMethod(ThreadOffset helper_offset, int arg0, b CallHelper(r_tgt, helper_offset, safepoint_pc); } +void Mir2Lir::CallRuntimeHelperRegMethod(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { + int r_tgt = CallHelperSetup(helper_offset); + DCHECK_NE(TargetReg(kArg1), arg0); + if (TargetReg(kArg0) != arg0) { + OpRegCopy(TargetReg(kArg0), arg0); + } + LoadCurrMethodDirect(TargetReg(kArg1)); + ClobberCallerSave(); + CallHelper(r_tgt, helper_offset, safepoint_pc); +} + +void Mir2Lir::CallRuntimeHelperRegMethodRegLocation(ThreadOffset helper_offset, int arg0, + RegLocation arg2, bool safepoint_pc) { + int r_tgt = CallHelperSetup(helper_offset); + DCHECK_NE(TargetReg(kArg1), arg0); + if (TargetReg(kArg0) != arg0) { + OpRegCopy(TargetReg(kArg0), arg0); + } + LoadCurrMethodDirect(TargetReg(kArg1)); + LoadValueDirectFixed(arg2, TargetReg(kArg2)); + ClobberCallerSave(); + CallHelper(r_tgt, helper_offset, safepoint_pc); +} + void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset helper_offset, RegLocation arg0, RegLocation arg1, bool safepoint_pc) { int r_tgt = CallHelperSetup(helper_offset); diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc index 8f2f6adba8..65582ddefd 100644 --- a/compiler/dex/quick/gen_loadstore.cc +++ b/compiler/dex/quick/gen_loadstore.cc @@ -294,6 +294,53 @@ void Mir2Lir::StoreValueWide(RegLocation rl_dest, RegLocation rl_src) { } } +void Mir2Lir::StoreFinalValueWide(RegLocation rl_dest, RegLocation rl_src) { + DCHECK_EQ(IsFpReg(rl_src.low_reg), IsFpReg(rl_src.high_reg)); + DCHECK(rl_dest.wide); + DCHECK(rl_src.wide); + DCHECK_EQ(rl_src.location, kLocPhysReg); + + if (rl_dest.location == kLocPhysReg) { + OpRegCopyWide(rl_dest.low_reg, rl_dest.high_reg, rl_src.low_reg, rl_src.high_reg); + } else { + // Just re-assign the registers. Dest gets Src's regs. + rl_dest.low_reg = rl_src.low_reg; + rl_dest.high_reg = rl_src.high_reg; + rl_dest.location = kLocPhysReg; + Clobber(rl_src.low_reg); + Clobber(rl_src.high_reg); + } + + // Dest is now live and dirty (until/if we flush it to home location). + MarkLive(rl_dest.low_reg, rl_dest.s_reg_low); + + // Does this wide value live in two registers (or one vector one)? + if (rl_dest.low_reg != rl_dest.high_reg) { + MarkLive(rl_dest.high_reg, GetSRegHi(rl_dest.s_reg_low)); + MarkDirty(rl_dest); + MarkPair(rl_dest.low_reg, rl_dest.high_reg); + } else { + // This must be an x86 vector register value, + DCHECK(IsFpReg(rl_dest.low_reg) && (cu_->instruction_set == kX86)); + MarkDirty(rl_dest); + } + + ResetDefLocWide(rl_dest); + if ((IsDirty(rl_dest.low_reg) || + IsDirty(rl_dest.high_reg)) && + (oat_live_out(rl_dest.s_reg_low) || + oat_live_out(GetSRegHi(rl_dest.s_reg_low)))) { + LIR *def_start = last_lir_insn_; + DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1), + mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low))); + StoreBaseDispWide(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), + rl_dest.low_reg, rl_dest.high_reg); + MarkClean(rl_dest); + LIR *def_end = last_lir_insn_; + MarkDefWide(rl_dest, def_start, def_end); + } +} + /* Utilities to load the current Method* */ void Mir2Lir::LoadCurrMethodDirect(int r_tgt) { LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt); @@ -303,4 +350,47 @@ RegLocation Mir2Lir::LoadCurrMethod() { return LoadValue(mir_graph_->GetMethodLoc(), kCoreReg); } +RegLocation Mir2Lir::ForceTemp(RegLocation loc) { + DCHECK(!loc.wide); + DCHECK(loc.location == kLocPhysReg); + DCHECK(!IsFpReg(loc.low_reg)); + DCHECK(!IsFpReg(loc.high_reg)); + if (IsTemp(loc.low_reg)) { + Clobber(loc.low_reg); + } else { + int temp_low = AllocTemp(); + OpRegCopy(temp_low, loc.low_reg); + loc.low_reg = temp_low; + } + + // Ensure that this doesn't represent the original SR any more. + loc.s_reg_low = INVALID_SREG; + return loc; +} + +RegLocation Mir2Lir::ForceTempWide(RegLocation loc) { + DCHECK(loc.wide); + DCHECK(loc.location == kLocPhysReg); + DCHECK(!IsFpReg(loc.low_reg)); + DCHECK(!IsFpReg(loc.high_reg)); + if (IsTemp(loc.low_reg)) { + Clobber(loc.low_reg); + } else { + int temp_low = AllocTemp(); + OpRegCopy(temp_low, loc.low_reg); + loc.low_reg = temp_low; + } + if (IsTemp(loc.high_reg)) { + Clobber(loc.high_reg); + } else { + int temp_high = AllocTemp(); + OpRegCopy(temp_high, loc.high_reg); + loc.high_reg = temp_high; + } + + // Ensure that this doesn't represent the original SR any more. + loc.s_reg_low = INVALID_SREG; + return loc; +} + } // namespace art diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index a5a14d5c0e..aca93f51d3 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -94,9 +94,9 @@ class MipsMir2Lir : public Mir2Lir { RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark); void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift); - void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenMulLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAndLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, @@ -110,9 +110,9 @@ class MipsMir2Lir : public Mir2Lir { bool GenInlinedPeek(CallInfo* info, OpSize size); bool GenInlinedPoke(CallInfo* info, OpSize size); void GenNegLong(RegLocation rl_dest, RegLocation rl_src); - void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenOrLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenSubLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind); RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); @@ -175,6 +175,9 @@ class MipsMir2Lir : public Mir2Lir { private: void ConvertShortToLongBranch(LIR* lir); + RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, bool check_zero); + RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div); }; } // namespace art diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 180d56c782..013041a9a5 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -250,6 +250,17 @@ RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit, return rl_result; } +RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, bool check_zero) { + LOG(FATAL) << "Unexpected use of GenDivRem for Mips"; + return rl_dest; +} + +RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) { + LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips"; + return rl_dest; +} + void MipsMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) { LOG(FATAL) << "Unexpected use of OpLea for Arm"; } @@ -356,13 +367,13 @@ LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) { return NULL; } -void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { +void MipsMir2Lir::GenMulLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenMulLong for Mips"; } -void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { +void MipsMir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); @@ -383,8 +394,8 @@ void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, StoreValueWide(rl_dest, rl_result); } -void MipsMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { +void MipsMir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); @@ -425,18 +436,19 @@ void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { StoreValueWide(rl_dest, rl_result); } -void MipsMir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1, +void MipsMir2Lir::GenAndLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenAndLong for Mips"; } -void MipsMir2Lir::GenOrLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { +void MipsMir2Lir::GenOrLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenOrLong for Mips"; } -void MipsMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { +void MipsMir2Lir::GenXorLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { LOG(FATAL) << "Unexpected use of GenXorLong for Mips"; } diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index 1aee06c89a..b744adcd97 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -15,12 +15,15 @@ */ #include "codegen_mips.h" + +#include <inttypes.h> + +#include <string> + #include "dex/compiler_internals.h" #include "dex/quick/mir_to_lir-inl.h" #include "mips_lir.h" -#include <string> - namespace art { static int core_regs[] = {r_ZERO, r_AT, r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, @@ -203,9 +206,9 @@ std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned cha snprintf(tbuf, arraysize(tbuf), "%d", operand*2); break; case 't': - snprintf(tbuf, arraysize(tbuf), "0x%08x (L%p)", - reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 2), - lir->target); + snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)", + reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1), + lir->target); break; case 'T': snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2); diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 6281eff873..1f4122d7a3 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -342,8 +342,8 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list bool is_safe = is_null; // Always safe to store null. if (!is_safe) { // Check safety from verifier type information. - const MethodReference mr(cu_->dex_file, cu_->method_idx); - is_safe = cu_->compiler_driver->IsSafeCast(mr, mir->offset); + const DexCompilationUnit* unit = mir_graph_->GetCurrentDexCompilationUnit(); + is_safe = cu_->compiler_driver->IsSafeCast(unit, mir->offset); } if (is_null || is_safe) { // Store of constant null doesn't require an assignability test and can be generated inline @@ -762,11 +762,13 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { // Combine check and work halves of throwing instruction. MIR* work_half = mir->meta.throw_insn; mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode; + mir->meta = work_half->meta; // Whatever the work_half had, we need to copy it. opcode = work_half->dalvikInsn.opcode; SSARepresentation* ssa_rep = work_half->ssa_rep; work_half->ssa_rep = mir->ssa_rep; mir->ssa_rep = ssa_rep; work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpCheckPart2); + work_half->meta.throw_insn = mir; } if (opcode >= kMirOpFirst) { diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index c157327109..bcd0eb190e 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -529,6 +529,9 @@ class Mir2Lir : public Backend { bool safepoint_pc); void CallRuntimeHelperImmMethod(ThreadOffset helper_offset, int arg0, bool safepoint_pc); + void CallRuntimeHelperRegMethod(ThreadOffset helper_offset, int arg0, bool safepoint_pc); + void CallRuntimeHelperRegMethodRegLocation(ThreadOffset helper_offset, int arg0, + RegLocation arg2, bool safepoint_pc); void CallRuntimeHelperRegLocationRegLocation(ThreadOffset helper_offset, RegLocation arg0, RegLocation arg1, bool safepoint_pc); @@ -627,6 +630,18 @@ class Mir2Lir : public Backend { */ void StoreValueWide(RegLocation rl_dest, RegLocation rl_src); + /** + * @brief Used to do the final store in a wide destination as per bytecode semantics. + * @see StoreValueWide + * @param rl_dest The destination dalvik register location. + * @param rl_src The source register location. It must be kLocPhysReg + * + * This is used for x86 two operand computations, where we have computed the correct + * register values that now need to be properly registered. This is used to avoid an + * extra pair of register copies that would result if StoreValueWide was called. + */ + void StoreFinalValueWide(RegLocation rl_dest, RegLocation rl_src); + // Shared by all targets - implemented in mir_to_lir.cc. void CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list); void HandleExtendedMethodMIR(BasicBlock* bb, MIR* mir); @@ -695,11 +710,14 @@ class Mir2Lir : public Backend { // Required for target - Dalvik-level generators. virtual void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; - virtual void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, + virtual void GenMulLong(Instruction::Code, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; - virtual void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, + virtual void GenAddLong(Instruction::Code, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; - virtual void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, + virtual void GenAndLong(Instruction::Code, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; virtual void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, @@ -727,11 +745,14 @@ class Mir2Lir : public Backend { virtual bool GenInlinedPeek(CallInfo* info, OpSize size) = 0; virtual bool GenInlinedPoke(CallInfo* info, OpSize size) = 0; virtual void GenNegLong(RegLocation rl_dest, RegLocation rl_src) = 0; - virtual void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, + virtual void GenOrLong(Instruction::Code, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; - virtual void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, + virtual void GenSubLong(Instruction::Code, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; - virtual void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, + virtual void GenXorLong(Instruction::Code, + RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; virtual LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind) = 0; @@ -739,6 +760,25 @@ class Mir2Lir : public Backend { bool is_div) = 0; virtual RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div) = 0; + /* + * @brief Generate an integer div or rem operation by a literal. + * @param rl_dest Destination Location. + * @param rl_src1 Numerator Location. + * @param rl_src2 Divisor Location. + * @param is_div 'true' if this is a division, 'false' for a remainder. + * @param check_zero 'true' if an exception should be generated if the divisor is 0. + */ + virtual RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, bool check_zero) = 0; + /* + * @brief Generate an integer div or rem operation by a literal. + * @param rl_dest Destination Location. + * @param rl_src Numerator Location. + * @param lit Divisor. + * @param is_div 'true' if this is a division, 'false' for a remainder. + */ + virtual RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, + int lit, bool is_div) = 0; virtual void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; @@ -758,7 +798,14 @@ class Mir2Lir : public Backend { virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) = 0; virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) = 0; + + /** + * @brief Lowers the kMirOpSelect MIR into LIR. + * @param bb The basic block in which the MIR is from. + * @param mir The MIR whose opcode is kMirOpSelect. + */ virtual void GenSelect(BasicBlock* bb, MIR* mir) = 0; + virtual void GenMemBarrier(MemBarrierKind barrier_kind) = 0; virtual void GenMoveException(RegLocation rl_dest) = 0; virtual void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, @@ -835,6 +882,43 @@ class Mir2Lir : public Backend { CompilationUnit* GetCompilationUnit() { return cu_; } + /* + * @brief Returns the index of the lowest set bit in 'x'. + * @param x Value to be examined. + * @returns The bit number of the lowest bit set in the value. + */ + int32_t LowestSetBit(uint64_t x); + /* + * @brief Is this value a power of two? + * @param x Value to be examined. + * @returns 'true' if only 1 bit is set in the value. + */ + bool IsPowerOfTwo(uint64_t x); + /* + * @brief Do these SRs overlap? + * @param rl_op1 One RegLocation + * @param rl_op2 The other RegLocation + * @return 'true' if the VR pairs overlap + * + * Check to see if a result pair has a misaligned overlap with an operand pair. This + * is not usual for dx to generate, but it is legal (for now). In a future rev of + * dex, we'll want to make this case illegal. + */ + bool BadOverlap(RegLocation rl_op1, RegLocation rl_op2); + + /* + * @brief Force a location (in a register) into a temporary register + * @param loc location of result + * @returns update location + */ + RegLocation ForceTemp(RegLocation loc); + + /* + * @brief Force a wide location (in registers) into temporary registers + * @param loc location of result + * @returns update location + */ + RegLocation ForceTempWide(RegLocation loc); private: void GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest, @@ -855,6 +939,7 @@ class Mir2Lir : public Backend { // TODO: add accessors for these. LIR* literal_list_; // Constants. LIR* method_literal_list_; // Method literals requiring patching. + LIR* class_literal_list_; // Class literals requiring patching. LIR* code_literal_list_; // Code literals requiring patching. LIR* first_fixup_; // Doubly-linked list of LIR nodes requiring fixups. diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 1dcff652ba..c29d6c422a 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -211,6 +211,8 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, #undef SHIFT_ENCODING_MAP { kX86Cmc, kNullary, NO_OPERAND, { 0, 0, 0xF5, 0, 0, 0, 0, 0}, "Cmc", "" }, + { kX86Shld32RRI, kRegRegImmRev, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { 0, 0, 0x0F, 0xA4, 0, 0, 0, 1}, "Shld32", "!0r,!1r,!2d" }, + { kX86Shrd32RRI, kRegRegImmRev, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { 0, 0, 0x0F, 0xAC, 0, 0, 0, 1}, "Shrd32", "!0r,!1r,!2d" }, { kX86Test8RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1}, "Test8RI", "!0r,!1d" }, { kX86Test8MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1}, "Test8MI", "[!0r+!1d],!2d" }, @@ -242,12 +244,13 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, UNARY_ENCODING_MAP(Not, 0x2, IS_STORE, 0, R, kReg, IS_UNARY_OP | REG_DEF0_USE0, M, kMem, IS_BINARY_OP | REG_USE0, A, kArray, IS_QUAD_OP | REG_USE01, 0, 0, 0, 0, "", "", ""), UNARY_ENCODING_MAP(Neg, 0x3, IS_STORE, SETS_CCODES, R, kReg, IS_UNARY_OP | REG_DEF0_USE0, M, kMem, IS_BINARY_OP | REG_USE0, A, kArray, IS_QUAD_OP | REG_USE01, 0, 0, 0, 0, "", "", ""), - UNARY_ENCODING_MAP(Mul, 0x4, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEA, REG_DEFAD_USEA, "ax,al,", "dx:ax,ax,", "edx:eax,eax,"), - UNARY_ENCODING_MAP(Imul, 0x5, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEA, REG_DEFAD_USEA, "ax,al,", "dx:ax,ax,", "edx:eax,eax,"), - UNARY_ENCODING_MAP(Divmod, 0x6, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"), - UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"), + UNARY_ENCODING_MAP(Mul, 0x4, 0, SETS_CCODES, DaR, kReg, IS_UNARY_OP | REG_USE0, DaM, kMem, IS_BINARY_OP | REG_USE0, DaA, kArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEA, REG_DEFAD_USEA, "ax,al,", "dx:ax,ax,", "edx:eax,eax,"), + UNARY_ENCODING_MAP(Imul, 0x5, 0, SETS_CCODES, DaR, kReg, IS_UNARY_OP | REG_USE0, DaM, kMem, IS_BINARY_OP | REG_USE0, DaA, kArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEA, REG_DEFAD_USEA, "ax,al,", "dx:ax,ax,", "edx:eax,eax,"), + UNARY_ENCODING_MAP(Divmod, 0x6, 0, SETS_CCODES, DaR, kReg, IS_UNARY_OP | REG_USE0, DaM, kMem, IS_BINARY_OP | REG_USE0, DaA, kArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"), + UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kReg, IS_UNARY_OP | REG_USE0, DaM, kMem, IS_BINARY_OP | REG_USE0, DaA, kArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"), #undef UNARY_ENCODING_MAP + { kx86Cdq32Da, kRegOpcode, NO_OPERAND | REG_DEFAD_USEA, { 0, 0, 0x99, 0, 0, 0, 0, 0 }, "Cdq", "" }, { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" }, { kX86Push32R, kRegOpcode, IS_UNARY_OP | REG_USE0 | REG_USE_SP | REG_DEF_SP | IS_STORE, { 0, 0, 0x50, 0, 0, 0, 0, 0 }, "Push32R", "!0r" }, { kX86Pop32R, kRegOpcode, IS_UNARY_OP | REG_DEF0 | REG_USE_SP | REG_DEF_SP | IS_LOAD, { 0, 0, 0x58, 0, 0, 0, 0, 0 }, "Pop32R", "!0r" }, @@ -287,6 +290,7 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, EXT_0F_ENCODING_MAP(Subss, 0xF3, 0x5C, REG_DEF0), EXT_0F_ENCODING_MAP(Divsd, 0xF2, 0x5E, REG_DEF0), EXT_0F_ENCODING_MAP(Divss, 0xF3, 0x5E, REG_DEF0), + EXT_0F_ENCODING_MAP(Punpckldq, 0x66, 0x62, REG_DEF0), { kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1 }, "PsrlqRI", "!0r,!1d" }, { kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1 }, "PsllqRI", "!0r,!1d" }, @@ -421,6 +425,7 @@ int X86Mir2Lir::GetInsnSize(LIR* lir) { case kThreadImm: // lir operands - 0: disp, 1: imm return ComputeSize(entry, 0, 0x12345678, false); // displacement size is always 32bit case kRegRegImm: // lir operands - 0: reg, 1: reg, 2: imm + case kRegRegImmRev: return ComputeSize(entry, 0, 0, false); case kRegMemImm: // lir operands - 0: reg, 1: base, 2: disp, 3: imm return ComputeSize(entry, lir->operands[1], lir->operands[2], false); @@ -641,7 +646,6 @@ void X86Mir2Lir::EmitOpMem(const X86EncodingMap* entry, uint8_t base, int disp) DCHECK_NE(0x0F, entry->skeleton.opcode); DCHECK_EQ(0, entry->skeleton.extra_opcode1); DCHECK_EQ(0, entry->skeleton.extra_opcode2); - DCHECK_NE(rX86_SP, base); EmitModrmDisp(entry->skeleton.modrm_opcode, base, disp); DCHECK_EQ(0, entry->skeleton.ax_opcode); DCHECK_EQ(0, entry->skeleton.immediate_bytes); @@ -754,6 +758,22 @@ void X86Mir2Lir::EmitRegRegImm(const X86EncodingMap* entry, EmitImm(entry, imm); } +void X86Mir2Lir::EmitRegRegImmRev(const X86EncodingMap* entry, + uint8_t reg1, uint8_t reg2, int32_t imm) { + EmitRegRegImm(entry, reg2, reg1, imm); +} + +void X86Mir2Lir::EmitRegMemImm(const X86EncodingMap* entry, + uint8_t reg, uint8_t base, int disp, int32_t imm) { + EmitPrefixAndOpcode(entry); + DCHECK(!X86_FPREG(reg)); + DCHECK_LT(reg, 8); + EmitModrmDisp(reg, base, disp); + DCHECK_EQ(0, entry->skeleton.modrm_opcode); + DCHECK_EQ(0, entry->skeleton.ax_opcode); + EmitImm(entry, imm); +} + void X86Mir2Lir::EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) { if (entry->skeleton.prefix1 != 0) { code_buffer_.push_back(entry->skeleton.prefix1); @@ -1185,9 +1205,16 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kRegRegStore: // lir operands - 0: reg2, 1: reg1 EmitRegReg(entry, lir->operands[1], lir->operands[0]); break; + case kRegRegImmRev: + EmitRegRegImmRev(entry, lir->operands[0], lir->operands[1], lir->operands[2]); + break; case kRegRegImm: EmitRegRegImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]); break; + case kRegMemImm: + EmitRegMemImm(entry, lir->operands[0], lir->operands[1], lir->operands[2], + lir->operands[3]); + break; case kRegImm: // lir operands - 0: reg, 1: immediate EmitRegImm(entry, lir->operands[0], lir->operands[1]); break; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 816f2d0c5c..6280b646f5 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -94,9 +94,9 @@ class X86Mir2Lir : public Mir2Lir { RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark); void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift); - void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenMulLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenAndLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, @@ -110,9 +110,9 @@ class X86Mir2Lir : public Mir2Lir { bool GenInlinedPeek(CallInfo* info, OpSize size); bool GenInlinedPoke(CallInfo* info, OpSize size); void GenNegLong(RegLocation rl_dest, RegLocation rl_src); - void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenOrLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenSubLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind); LIR* GenMemImmedCheck(ConditionCode c_code, int base, int offset, int check_value, @@ -136,6 +136,49 @@ class X86Mir2Lir : public Mir2Lir { void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); + /* + * @brief Generate a two address long operation with a constant value + * @param rl_dest location of result + * @param rl_src constant source operand + * @param op Opcode to be generated + */ + void GenLongImm(RegLocation rl_dest, RegLocation rl_src, Instruction::Code op); + /* + * @brief Generate a three address long operation with a constant value + * @param rl_dest location of result + * @param rl_src1 source operand + * @param rl_src2 constant source operand + * @param op Opcode to be generated + */ + void GenLongLongImm(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, Instruction::Code op); + + /** + * @brief Generate a long arithmetic operation. + * @param rl_dest The destination. + * @param rl_src1 First operand. + * @param rl_src2 Second operand. + * @param op The DEX opcode for the operation. + * @param is_commutative The sources can be swapped if needed. + */ + void GenLongArith(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, Instruction::Code op, bool is_commutative); + + /** + * @brief Generate a two operand long arithmetic operation. + * @param rl_dest The destination. + * @param rl_src Second operand. + * @param op The DEX opcode for the operation. + */ + void GenLongArith(RegLocation rl_dest, RegLocation rl_src, Instruction::Code op); + + /** + * @brief Generate a long operation. + * @param rl_dest The destination. Must be in a register + * @param rl_src The other operand. May be in a register or in memory. + * @param op The DEX opcode for the operation. + */ + void GenLongRegOrMemOp(RegLocation rl_dest, RegLocation rl_src, Instruction::Code op); // Single operation generators. LIR* OpUnconditionalBranch(LIR* target); @@ -202,6 +245,8 @@ class X86Mir2Lir : public Mir2Lir { void EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int disp); void EmitRegReg(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2); void EmitRegRegImm(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, int32_t imm); + void EmitRegRegImmRev(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, int32_t imm); + void EmitRegMemImm(const X86EncodingMap* entry, uint8_t reg1, uint8_t base, int disp, int32_t imm); void EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm); void EmitThreadImm(const X86EncodingMap* entry, int disp, int imm); void EmitMovRegImm(const X86EncodingMap* entry, uint8_t reg, int imm); @@ -230,6 +275,96 @@ class X86Mir2Lir : public Mir2Lir { int64_t val, ConditionCode ccode); void OpVectorRegCopyWide(uint8_t fp_reg, uint8_t low_reg, uint8_t high_reg); void GenConstWide(RegLocation rl_dest, int64_t value); + + /* + * @brief Return the correct x86 opcode for the Dex operation + * @param op Dex opcode for the operation + * @param loc Register location of the operand + * @param is_high_op 'true' if this is an operation on the high word + * @param value Immediate value for the operation. Used for byte variants + * @returns the correct x86 opcode to perform the operation + */ + X86OpCode GetOpcode(Instruction::Code op, RegLocation loc, bool is_high_op, int32_t value); + + /* + * @brief Return the correct x86 opcode for the Dex operation + * @param op Dex opcode for the operation + * @param dest location of the destination. May be register or memory. + * @param rhs Location for the rhs of the operation. May be in register or memory. + * @param is_high_op 'true' if this is an operation on the high word + * @returns the correct x86 opcode to perform the operation + * @note at most one location may refer to memory + */ + X86OpCode GetOpcode(Instruction::Code op, RegLocation dest, RegLocation rhs, + bool is_high_op); + + /* + * @brief Is this operation a no-op for this opcode and value + * @param op Dex opcode for the operation + * @param value Immediate value for the operation. + * @returns 'true' if the operation will have no effect + */ + bool IsNoOp(Instruction::Code op, int32_t value); + + /* + * @brief Dump a RegLocation using printf + * @param loc Register location to dump + */ + static void DumpRegLocation(RegLocation loc); + + /** + * @brief Calculate magic number and shift for a given divisor + * @param divisor divisor number for calculation + * @param magic hold calculated magic number + * @param shift hold calculated shift + */ + void CalculateMagicAndShift(int divisor, int& magic, int& shift); + + /* + * @brief Generate an integer div or rem operation. + * @param rl_dest Destination Location. + * @param rl_src1 Numerator Location. + * @param rl_src2 Divisor Location. + * @param is_div 'true' if this is a division, 'false' for a remainder. + * @param check_zero 'true' if an exception should be generated if the divisor is 0. + */ + RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, bool check_zero); + + /* + * @brief Generate an integer div or rem operation by a literal. + * @param rl_dest Destination Location. + * @param rl_src Numerator Location. + * @param lit Divisor. + * @param is_div 'true' if this is a division, 'false' for a remainder. + */ + RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src, int lit, bool is_div); + + /* + * Generate code to implement long shift operations. + * @param opcode The DEX opcode to specify the shift type. + * @param rl_dest The destination. + * @param rl_src The value to be shifted. + * @param shift_amount How much to shift. + * @returns the RegLocation of the result. + */ + RegLocation GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src, int shift_amount); + /* + * Generate an imul of a register by a constant or a better sequence. + * @param dest Destination Register. + * @param src Source Register. + * @param val Constant multiplier. + */ + void GenImulRegImm(int dest, int src, int val); + /* + * Generate an imul of a memory location by a constant or a better sequence. + * @param dest Destination Register. + * @param sreg Symbolic register. + * @param displacement Displacement on stack of Symbolic Register. + * @param val Constant multiplier. + */ + void GenImulMemImm(int dest, int sreg, int displacement, int val); }; } // namespace art diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc index 6272498065..006fe76f1b 100644 --- a/compiler/dex/quick/x86/fp_x86.cc +++ b/compiler/dex/quick/x86/fp_x86.cc @@ -303,7 +303,7 @@ void X86Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, rl_src2 = LoadValue(rl_src2, kFPReg); NewLIR2(kX86UcomissRR, rl_src1.low_reg, rl_src2.low_reg); } - ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]); + ConditionCode ccode = mir->meta.ccode; switch (ccode) { case kCondEq: if (!gt_bias) { diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 01479a9021..e665f700ba 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -180,14 +180,104 @@ void X86Mir2Lir::OpRegCopyWide(int dest_lo, int dest_hi, } void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { - UNIMPLEMENTED(FATAL) << "Need codegen for GenSelect"; + RegLocation rl_result; + RegLocation rl_src = mir_graph_->GetSrc(mir, 0); + RegLocation rl_dest = mir_graph_->GetDest(mir); + rl_src = LoadValue(rl_src, kCoreReg); + + // The kMirOpSelect has two variants, one for constants and one for moves. + const bool is_constant_case = (mir->ssa_rep->num_uses == 1); + + if (is_constant_case) { + int true_val = mir->dalvikInsn.vB; + int false_val = mir->dalvikInsn.vC; + rl_result = EvalLoc(rl_dest, kCoreReg, true); + + /* + * 1) When the true case is zero and result_reg is not same as src_reg: + * xor result_reg, result_reg + * cmp $0, src_reg + * mov t1, $false_case + * cmovnz result_reg, t1 + * 2) When the false case is zero and result_reg is not same as src_reg: + * xor result_reg, result_reg + * cmp $0, src_reg + * mov t1, $true_case + * cmovz result_reg, t1 + * 3) All other cases (we do compare first to set eflags): + * cmp $0, src_reg + * mov result_reg, $true_case + * mov t1, $false_case + * cmovnz result_reg, t1 + */ + const bool result_reg_same_as_src = (rl_src.location == kLocPhysReg && rl_src.low_reg == rl_result.low_reg); + const bool true_zero_case = (true_val == 0 && false_val != 0 && !result_reg_same_as_src); + const bool false_zero_case = (false_val == 0 && true_val != 0 && !result_reg_same_as_src); + const bool catch_all_case = !(true_zero_case || false_zero_case); + + if (true_zero_case || false_zero_case) { + OpRegReg(kOpXor, rl_result.low_reg, rl_result.low_reg); + } + + if (true_zero_case || false_zero_case || catch_all_case) { + OpRegImm(kOpCmp, rl_src.low_reg, 0); + } + + if (catch_all_case) { + OpRegImm(kOpMov, rl_result.low_reg, true_val); + } + + if (true_zero_case || false_zero_case || catch_all_case) { + int immediateForTemp = false_zero_case ? true_val : false_val; + int temp1_reg = AllocTemp(); + OpRegImm(kOpMov, temp1_reg, immediateForTemp); + + ConditionCode cc = false_zero_case ? kCondEq : kCondNe; + OpCondRegReg(kOpCmov, cc, rl_result.low_reg, temp1_reg); + + FreeTemp(temp1_reg); + } + } else { + RegLocation rl_true = mir_graph_->GetSrc(mir, 1); + RegLocation rl_false = mir_graph_->GetSrc(mir, 2); + rl_true = LoadValue(rl_true, kCoreReg); + rl_false = LoadValue(rl_false, kCoreReg); + rl_result = EvalLoc(rl_dest, kCoreReg, true); + + /* + * 1) When true case is already in place: + * cmp $0, src_reg + * cmovnz result_reg, false_reg + * 2) When false case is already in place: + * cmp $0, src_reg + * cmovz result_reg, true_reg + * 3) When neither cases are in place: + * cmp $0, src_reg + * mov result_reg, true_reg + * cmovnz result_reg, false_reg + */ + + // kMirOpSelect is generated just for conditional cases when comparison is done with zero. + OpRegImm(kOpCmp, rl_src.low_reg, 0); + + if (rl_result.low_reg == rl_true.low_reg) { + OpCondRegReg(kOpCmov, kCondNe, rl_result.low_reg, rl_false.low_reg); + } else if (rl_result.low_reg == rl_false.low_reg) { + OpCondRegReg(kOpCmov, kCondEq, rl_result.low_reg, rl_true.low_reg); + } else { + OpRegCopy(rl_result.low_reg, rl_true.low_reg); + OpCondRegReg(kOpCmov, kCondNe, rl_result.low_reg, rl_false.low_reg); + } + } + + StoreValue(rl_dest, rl_result); } void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { LIR* taken = &block_label_list_[bb->taken]; RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0); RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2); - ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]); + ConditionCode ccode = mir->meta.ccode; if (rl_src1.is_const) { std::swap(rl_src1, rl_src2); @@ -284,18 +374,261 @@ void X86Mir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, OpCmpImmBranch(ccode, low_reg, val_lo, taken); } +void X86Mir2Lir::CalculateMagicAndShift(int divisor, int& magic, int& shift) { + // It does not make sense to calculate magic and shift for zero divisor. + DCHECK_NE(divisor, 0); + + /* According to H.S.Warren's Hacker's Delight Chapter 10 and + * T,Grablund, P.L.Montogomery's Division by invariant integers using multiplication. + * The magic number M and shift S can be calculated in the following way: + * Let nc be the most positive value of numerator(n) such that nc = kd - 1, + * where divisor(d) >=2. + * Let nc be the most negative value of numerator(n) such that nc = kd + 1, + * where divisor(d) <= -2. + * Thus nc can be calculated like: + * nc = 2^31 + 2^31 % d - 1, where d >= 2 + * nc = -2^31 + (2^31 + 1) % d, where d >= 2. + * + * So the shift p is the smallest p satisfying + * 2^p > nc * (d - 2^p % d), where d >= 2 + * 2^p > nc * (d + 2^p % d), where d <= -2. + * + * the magic number M is calcuated by + * M = (2^p + d - 2^p % d) / d, where d >= 2 + * M = (2^p - d - 2^p % d) / d, where d <= -2. + * + * Notice that p is always bigger than or equal to 32, so we just return 32-p as + * the shift number S. + */ + + int32_t p = 31; + const uint32_t two31 = 0x80000000U; + + // Initialize the computations. + uint32_t abs_d = (divisor >= 0) ? divisor : -divisor; + uint32_t tmp = two31 + (static_cast<uint32_t>(divisor) >> 31); + uint32_t abs_nc = tmp - 1 - tmp % abs_d; + uint32_t quotient1 = two31 / abs_nc; + uint32_t remainder1 = two31 % abs_nc; + uint32_t quotient2 = two31 / abs_d; + uint32_t remainder2 = two31 % abs_d; + + /* + * To avoid handling both positive and negative divisor, Hacker's Delight + * introduces a method to handle these 2 cases together to avoid duplication. + */ + uint32_t delta; + do { + p++; + quotient1 = 2 * quotient1; + remainder1 = 2 * remainder1; + if (remainder1 >= abs_nc) { + quotient1++; + remainder1 = remainder1 - abs_nc; + } + quotient2 = 2 * quotient2; + remainder2 = 2 * remainder2; + if (remainder2 >= abs_d) { + quotient2++; + remainder2 = remainder2 - abs_d; + } + delta = abs_d - remainder2; + } while (quotient1 < delta || (quotient1 == delta && remainder1 == 0)); + + magic = (divisor > 0) ? (quotient2 + 1) : (-quotient2 - 1); + shift = p - 32; +} + RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div) { LOG(FATAL) << "Unexpected use of GenDivRemLit for x86"; return rl_dest; } +RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src, + int imm, bool is_div) { + // Use a multiply (and fixup) to perform an int div/rem by a constant. + + // We have to use fixed registers, so flush all the temps. + FlushAllRegs(); + LockCallTemps(); // Prepare for explicit register usage. + + // Assume that the result will be in EDX. + RegLocation rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, + r2, INVALID_REG, INVALID_SREG, INVALID_SREG}; + + // handle 0x80000000 / -1 special case. + LIR *minint_branch = 0; + if (imm == -1) { + if (is_div) { + LoadValueDirectFixed(rl_src, r0); + OpRegImm(kOpCmp, r0, 0x80000000); + minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondEq); + + // for x != MIN_INT, x / -1 == -x. + NewLIR1(kX86Neg32R, r0); + + LIR* branch_around = NewLIR1(kX86Jmp8, 0); + // The target for cmp/jmp above. + minint_branch->target = NewLIR0(kPseudoTargetLabel); + // EAX already contains the right value (0x80000000), + branch_around->target = NewLIR0(kPseudoTargetLabel); + } else { + // x % -1 == 0. + LoadConstantNoClobber(r0, 0); + } + // For this case, return the result in EAX. + rl_result.low_reg = r0; + } else { + DCHECK(imm <= -2 || imm >= 2); + // Use H.S.Warren's Hacker's Delight Chapter 10 and + // T,Grablund, P.L.Montogomery's Division by invariant integers using multiplication. + int magic, shift; + CalculateMagicAndShift(imm, magic, shift); + + /* + * For imm >= 2, + * int(n/imm) = floor(n/imm) = floor(M*n/2^S), while n > 0 + * int(n/imm) = ceil(n/imm) = floor(M*n/2^S) +1, while n < 0. + * For imm <= -2, + * int(n/imm) = ceil(n/imm) = floor(M*n/2^S) +1 , while n > 0 + * int(n/imm) = floor(n/imm) = floor(M*n/2^S), while n < 0. + * We implement this algorithm in the following way: + * 1. multiply magic number m and numerator n, get the higher 32bit result in EDX + * 2. if imm > 0 and magic < 0, add numerator to EDX + * if imm < 0 and magic > 0, sub numerator from EDX + * 3. if S !=0, SAR S bits for EDX + * 4. add 1 to EDX if EDX < 0 + * 5. Thus, EDX is the quotient + */ + + // Numerator into EAX. + int numerator_reg = -1; + if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) { + // We will need the value later. + if (rl_src.location == kLocPhysReg) { + // We can use it directly. + DCHECK(rl_src.low_reg != r0 && rl_src.low_reg != r2); + numerator_reg = rl_src.low_reg; + } else { + LoadValueDirectFixed(rl_src, r1); + numerator_reg = r1; + } + OpRegCopy(r0, numerator_reg); + } else { + // Only need this once. Just put it into EAX. + LoadValueDirectFixed(rl_src, r0); + } + + // EDX = magic. + LoadConstantNoClobber(r2, magic); + + // EDX:EAX = magic & dividend. + NewLIR1(kX86Imul32DaR, r2); + + if (imm > 0 && magic < 0) { + // Add numerator to EDX. + DCHECK_NE(numerator_reg, -1); + NewLIR2(kX86Add32RR, r2, numerator_reg); + } else if (imm < 0 && magic > 0) { + DCHECK_NE(numerator_reg, -1); + NewLIR2(kX86Sub32RR, r2, numerator_reg); + } + + // Do we need the shift? + if (shift != 0) { + // Shift EDX by 'shift' bits. + NewLIR2(kX86Sar32RI, r2, shift); + } + + // Add 1 to EDX if EDX < 0. + + // Move EDX to EAX. + OpRegCopy(r0, r2); + + // Move sign bit to bit 0, zeroing the rest. + NewLIR2(kX86Shr32RI, r2, 31); + + // EDX = EDX + EAX. + NewLIR2(kX86Add32RR, r2, r0); + + // Quotient is in EDX. + if (!is_div) { + // We need to compute the remainder. + // Remainder is divisor - (quotient * imm). + DCHECK_NE(numerator_reg, -1); + OpRegCopy(r0, numerator_reg); + + // EAX = numerator * imm. + OpRegRegImm(kOpMul, r2, r2, imm); + + // EDX -= EAX. + NewLIR2(kX86Sub32RR, r0, r2); + + // For this case, return the result in EAX. + rl_result.low_reg = r0; + } + } + + return rl_result; +} + RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div) { LOG(FATAL) << "Unexpected use of GenDivRem for x86"; return rl_dest; } +RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, bool check_zero) { + // We have to use fixed registers, so flush all the temps. + FlushAllRegs(); + LockCallTemps(); // Prepare for explicit register usage. + + // Load LHS into EAX. + LoadValueDirectFixed(rl_src1, r0); + + // Load RHS into EBX. + LoadValueDirectFixed(rl_src2, r1); + + // Copy LHS sign bit into EDX. + NewLIR0(kx86Cdq32Da); + + if (check_zero) { + // Handle division by zero case. + GenImmedCheck(kCondEq, r1, 0, kThrowDivZero); + } + + // Have to catch 0x80000000/-1 case, or we will get an exception! + OpRegImm(kOpCmp, r1, -1); + LIR *minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe); + + // RHS is -1. + OpRegImm(kOpCmp, r0, 0x80000000); + LIR * minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe); + + // In 0x80000000/-1 case. + if (!is_div) { + // For DIV, EAX is already right. For REM, we need EDX 0. + LoadConstantNoClobber(r2, 0); + } + LIR* done = NewLIR1(kX86Jmp8, 0); + + // Expected case. + minus_one_branch->target = NewLIR0(kPseudoTargetLabel); + minint_branch->target = minus_one_branch->target; + NewLIR1(kX86Idivmod32DaR, r1); + done->target = NewLIR0(kPseudoTargetLabel); + + // Result is in EAX for div and EDX for rem. + RegLocation rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, + r0, INVALID_REG, INVALID_SREG, INVALID_SREG}; + if (!is_div) { + rl_result.low_reg = r2; + } + return rl_result; +} + bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) { DCHECK_EQ(cu_->instruction_set, kX86); @@ -512,100 +845,353 @@ LIR* X86Mir2Lir::OpIT(ConditionCode cond, const char* guide) { return NULL; } -void X86Mir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { - LOG(FATAL) << "Unexpected use of GenX86Long for x86"; +void X86Mir2Lir::GenImulRegImm(int dest, int src, int val) { + switch (val) { + case 0: + NewLIR2(kX86Xor32RR, dest, dest); + break; + case 1: + OpRegCopy(dest, src); + break; + default: + OpRegRegImm(kOpMul, dest, src, val); + break; + } } -void X86Mir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { - // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart - // enough. - FlushAllRegs(); - LockCallTemps(); // Prepare for explicit register usage - LoadValueDirectWideFixed(rl_src1, r0, r1); - LoadValueDirectWideFixed(rl_src2, r2, r3); - // Compute (r1:r0) = (r1:r0) + (r2:r3) - OpRegReg(kOpAdd, r0, r2); // r0 = r0 + r2 - OpRegReg(kOpAdc, r1, r3); // r1 = r1 + r3 + CF - RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1, - INVALID_SREG, INVALID_SREG}; - StoreValueWide(rl_dest, rl_result); + +void X86Mir2Lir::GenImulMemImm(int dest, int sreg, int displacement, int val) { + LIR *m; + switch (val) { + case 0: + NewLIR2(kX86Xor32RR, dest, dest); + break; + case 1: + LoadBaseDisp(rX86_SP, displacement, dest, kWord, sreg); + break; + default: + m = NewLIR4(IS_SIMM8(val) ? kX86Imul32RMI8 : kX86Imul32RMI, dest, rX86_SP, + displacement, val); + AnnotateDalvikRegAccess(m, displacement >> 2, true /* is_load */, true /* is_64bit */); + break; + } } -void X86Mir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, +void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { - // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart - // enough. + if (rl_src1.is_const) { + std::swap(rl_src1, rl_src2); + } + // Are we multiplying by a constant? + if (rl_src2.is_const) { + // Do special compare/branch against simple const operand + int64_t val = mir_graph_->ConstantValueWide(rl_src2); + if (val == 0) { + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + OpRegReg(kOpXor, rl_result.low_reg, rl_result.low_reg); + OpRegReg(kOpXor, rl_result.high_reg, rl_result.high_reg); + StoreValueWide(rl_dest, rl_result); + return; + } else if (val == 1) { + rl_src1 = EvalLocWide(rl_src1, kCoreReg, true); + StoreValueWide(rl_dest, rl_src1); + return; + } else if (val == 2) { + GenAddLong(Instruction::ADD_LONG, rl_dest, rl_src1, rl_src1); + return; + } else if (IsPowerOfTwo(val)) { + int shift_amount = LowestSetBit(val); + if (!BadOverlap(rl_src1, rl_dest)) { + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + RegLocation rl_result = GenShiftImmOpLong(Instruction::SHL_LONG, rl_dest, + rl_src1, shift_amount); + StoreValueWide(rl_dest, rl_result); + return; + } + } + + // Okay, just bite the bullet and do it. + int32_t val_lo = Low32Bits(val); + int32_t val_hi = High32Bits(val); + FlushAllRegs(); + LockCallTemps(); // Prepare for explicit register usage. + rl_src1 = UpdateLocWide(rl_src1); + bool src1_in_reg = rl_src1.location == kLocPhysReg; + int displacement = SRegOffset(rl_src1.s_reg_low); + + // ECX <- 1H * 2L + // EAX <- 1L * 2H + if (src1_in_reg) { + GenImulRegImm(r1, rl_src1.high_reg, val_lo); + GenImulRegImm(r0, rl_src1.low_reg, val_hi); + } else { + GenImulMemImm(r1, GetSRegHi(rl_src1.s_reg_low), displacement + HIWORD_OFFSET, val_lo); + GenImulMemImm(r0, rl_src1.s_reg_low, displacement + LOWORD_OFFSET, val_hi); + } + + // ECX <- ECX + EAX (2H * 1L) + (1H * 2L) + NewLIR2(kX86Add32RR, r1, r0); + + // EAX <- 2L + LoadConstantNoClobber(r0, val_lo); + + // EDX:EAX <- 2L * 1L (double precision) + if (src1_in_reg) { + NewLIR1(kX86Mul32DaR, rl_src1.low_reg); + } else { + LIR *m = NewLIR2(kX86Mul32DaM, rX86_SP, displacement + LOWORD_OFFSET); + AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, + true /* is_load */, true /* is_64bit */); + } + + // EDX <- EDX + ECX (add high words) + NewLIR2(kX86Add32RR, r2, r1); + + // Result is EDX:EAX + RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r2, + INVALID_SREG, INVALID_SREG}; + StoreValueWide(rl_dest, rl_result); + return; + } + + // Nope. Do it the hard way FlushAllRegs(); - LockCallTemps(); // Prepare for explicit register usage - LoadValueDirectWideFixed(rl_src1, r0, r1); - LoadValueDirectWideFixed(rl_src2, r2, r3); - // Compute (r1:r0) = (r1:r0) + (r2:r3) - OpRegReg(kOpSub, r0, r2); // r0 = r0 - r2 - OpRegReg(kOpSbc, r1, r3); // r1 = r1 - r3 - CF - RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1, - INVALID_SREG, INVALID_SREG}; + LockCallTemps(); // Prepare for explicit register usage. + rl_src1 = UpdateLocWide(rl_src1); + rl_src2 = UpdateLocWide(rl_src2); + + // At this point, the VRs are in their home locations. + bool src1_in_reg = rl_src1.location == kLocPhysReg; + bool src2_in_reg = rl_src2.location == kLocPhysReg; + + // ECX <- 1H + if (src1_in_reg) { + NewLIR2(kX86Mov32RR, r1, rl_src1.high_reg); + } else { + LoadBaseDisp(rX86_SP, SRegOffset(rl_src1.s_reg_low) + HIWORD_OFFSET, r1, + kWord, GetSRegHi(rl_src1.s_reg_low)); + } + + // EAX <- 2H + if (src2_in_reg) { + NewLIR2(kX86Mov32RR, r0, rl_src2.high_reg); + } else { + LoadBaseDisp(rX86_SP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, r0, + kWord, GetSRegHi(rl_src2.s_reg_low)); + } + + // EAX <- EAX * 1L (2H * 1L) + if (src1_in_reg) { + NewLIR2(kX86Imul32RR, r0, rl_src1.low_reg); + } else { + int displacement = SRegOffset(rl_src1.s_reg_low); + LIR *m = NewLIR3(kX86Imul32RM, r0, rX86_SP, displacement + LOWORD_OFFSET); + AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, + true /* is_load */, true /* is_64bit */); + } + + // ECX <- ECX * 2L (1H * 2L) + if (src2_in_reg) { + NewLIR2(kX86Imul32RR, r1, rl_src2.low_reg); + } else { + int displacement = SRegOffset(rl_src2.s_reg_low); + LIR *m = NewLIR3(kX86Imul32RM, r1, rX86_SP, displacement + LOWORD_OFFSET); + AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, + true /* is_load */, true /* is_64bit */); + } + + // ECX <- ECX + EAX (2H * 1L) + (1H * 2L) + NewLIR2(kX86Add32RR, r1, r0); + + // EAX <- 2L + if (src2_in_reg) { + NewLIR2(kX86Mov32RR, r0, rl_src2.low_reg); + } else { + LoadBaseDisp(rX86_SP, SRegOffset(rl_src2.s_reg_low) + LOWORD_OFFSET, r0, + kWord, rl_src2.s_reg_low); + } + + // EDX:EAX <- 2L * 1L (double precision) + if (src1_in_reg) { + NewLIR1(kX86Mul32DaR, rl_src1.low_reg); + } else { + int displacement = SRegOffset(rl_src1.s_reg_low); + LIR *m = NewLIR2(kX86Mul32DaM, rX86_SP, displacement + LOWORD_OFFSET); + AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, + true /* is_load */, true /* is_64bit */); + } + + // EDX <- EDX + ECX (add high words) + NewLIR2(kX86Add32RR, r2, r1); + + // Result is EDX:EAX + RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r2, + INVALID_SREG, INVALID_SREG}; StoreValueWide(rl_dest, rl_result); } -void X86Mir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { - // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart - // enough. - FlushAllRegs(); - LockCallTemps(); // Prepare for explicit register usage - LoadValueDirectWideFixed(rl_src1, r0, r1); - LoadValueDirectWideFixed(rl_src2, r2, r3); - // Compute (r1:r0) = (r1:r0) & (r2:r3) - OpRegReg(kOpAnd, r0, r2); // r0 = r0 & r2 - OpRegReg(kOpAnd, r1, r3); // r1 = r1 & r3 - RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1, - INVALID_SREG, INVALID_SREG}; - StoreValueWide(rl_dest, rl_result); +void X86Mir2Lir::GenLongRegOrMemOp(RegLocation rl_dest, RegLocation rl_src, + Instruction::Code op) { + DCHECK_EQ(rl_dest.location, kLocPhysReg); + X86OpCode x86op = GetOpcode(op, rl_dest, rl_src, false); + if (rl_src.location == kLocPhysReg) { + // Both operands are in registers. + if (rl_dest.low_reg == rl_src.high_reg) { + // The registers are the same, so we would clobber it before the use. + int temp_reg = AllocTemp(); + OpRegCopy(temp_reg, rl_dest.low_reg); + rl_src.high_reg = temp_reg; + } + NewLIR2(x86op, rl_dest.low_reg, rl_src.low_reg); + + x86op = GetOpcode(op, rl_dest, rl_src, true); + NewLIR2(x86op, rl_dest.high_reg, rl_src.high_reg); + FreeTemp(rl_src.low_reg); + FreeTemp(rl_src.high_reg); + return; + } + + // RHS is in memory. + DCHECK((rl_src.location == kLocDalvikFrame) || + (rl_src.location == kLocCompilerTemp)); + int rBase = TargetReg(kSp); + int displacement = SRegOffset(rl_src.s_reg_low); + + LIR *lir = NewLIR3(x86op, rl_dest.low_reg, rBase, displacement + LOWORD_OFFSET); + AnnotateDalvikRegAccess(lir, (displacement + LOWORD_OFFSET) >> 2, + true /* is_load */, true /* is64bit */); + x86op = GetOpcode(op, rl_dest, rl_src, true); + lir = NewLIR3(x86op, rl_dest.high_reg, rBase, displacement + HIWORD_OFFSET); + AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2, + true /* is_load */, true /* is64bit */); +} + +void X86Mir2Lir::GenLongArith(RegLocation rl_dest, RegLocation rl_src, Instruction::Code op) { + rl_dest = UpdateLocWide(rl_dest); + if (rl_dest.location == kLocPhysReg) { + // Ensure we are in a register pair + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + + rl_src = UpdateLocWide(rl_src); + GenLongRegOrMemOp(rl_result, rl_src, op); + StoreFinalValueWide(rl_dest, rl_result); + return; + } + + // It wasn't in registers, so it better be in memory. + DCHECK((rl_dest.location == kLocDalvikFrame) || + (rl_dest.location == kLocCompilerTemp)); + rl_src = LoadValueWide(rl_src, kCoreReg); + + // Operate directly into memory. + X86OpCode x86op = GetOpcode(op, rl_dest, rl_src, false); + int rBase = TargetReg(kSp); + int displacement = SRegOffset(rl_dest.s_reg_low); + + LIR *lir = NewLIR3(x86op, rBase, displacement + LOWORD_OFFSET, rl_src.low_reg); + AnnotateDalvikRegAccess(lir, (displacement + LOWORD_OFFSET) >> 2, + false /* is_load */, true /* is64bit */); + x86op = GetOpcode(op, rl_dest, rl_src, true); + lir = NewLIR3(x86op, rBase, displacement + HIWORD_OFFSET, rl_src.high_reg); + AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2, + false /* is_load */, true /* is64bit */); + FreeTemp(rl_src.low_reg); + FreeTemp(rl_src.high_reg); +} + +void X86Mir2Lir::GenLongArith(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, Instruction::Code op, + bool is_commutative) { + // Is this really a 2 operand operation? + switch (op) { + case Instruction::ADD_LONG_2ADDR: + case Instruction::SUB_LONG_2ADDR: + case Instruction::AND_LONG_2ADDR: + case Instruction::OR_LONG_2ADDR: + case Instruction::XOR_LONG_2ADDR: + GenLongArith(rl_dest, rl_src2, op); + return; + default: + break; + } + + if (rl_dest.location == kLocPhysReg) { + RegLocation rl_result = LoadValueWide(rl_src1, kCoreReg); + + // We are about to clobber the LHS, so it needs to be a temp. + rl_result = ForceTempWide(rl_result); + + // Perform the operation using the RHS. + rl_src2 = UpdateLocWide(rl_src2); + GenLongRegOrMemOp(rl_result, rl_src2, op); + + // And now record that the result is in the temp. + StoreFinalValueWide(rl_dest, rl_result); + return; + } + + // It wasn't in registers, so it better be in memory. + DCHECK((rl_dest.location == kLocDalvikFrame) || + (rl_dest.location == kLocCompilerTemp)); + rl_src1 = UpdateLocWide(rl_src1); + rl_src2 = UpdateLocWide(rl_src2); + + // Get one of the source operands into temporary register. + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + if (IsTemp(rl_src1.low_reg) && IsTemp(rl_src1.high_reg)) { + GenLongRegOrMemOp(rl_src1, rl_src2, op); + } else if (is_commutative) { + rl_src2 = LoadValueWide(rl_src2, kCoreReg); + // We need at least one of them to be a temporary. + if (!(IsTemp(rl_src2.low_reg) && IsTemp(rl_src2.high_reg))) { + rl_src1 = ForceTempWide(rl_src1); + } + GenLongRegOrMemOp(rl_src1, rl_src2, op); + } else { + // Need LHS to be the temp. + rl_src1 = ForceTempWide(rl_src1); + GenLongRegOrMemOp(rl_src1, rl_src2, op); + } + + StoreFinalValueWide(rl_dest, rl_src1); +} + +void X86Mir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { + GenLongArith(rl_dest, rl_src1, rl_src2, opcode, true); +} + +void X86Mir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { + GenLongArith(rl_dest, rl_src1, rl_src2, opcode, false); +} + +void X86Mir2Lir::GenAndLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { + GenLongArith(rl_dest, rl_src1, rl_src2, opcode, true); } -void X86Mir2Lir::GenOrLong(RegLocation rl_dest, +void X86Mir2Lir::GenOrLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { - // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart - // enough. - FlushAllRegs(); - LockCallTemps(); // Prepare for explicit register usage - LoadValueDirectWideFixed(rl_src1, r0, r1); - LoadValueDirectWideFixed(rl_src2, r2, r3); - // Compute (r1:r0) = (r1:r0) | (r2:r3) - OpRegReg(kOpOr, r0, r2); // r0 = r0 | r2 - OpRegReg(kOpOr, r1, r3); // r1 = r1 | r3 - RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1, - INVALID_SREG, INVALID_SREG}; - StoreValueWide(rl_dest, rl_result); + GenLongArith(rl_dest, rl_src1, rl_src2, opcode, true); } -void X86Mir2Lir::GenXorLong(RegLocation rl_dest, +void X86Mir2Lir::GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { - // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart - // enough. - FlushAllRegs(); - LockCallTemps(); // Prepare for explicit register usage - LoadValueDirectWideFixed(rl_src1, r0, r1); - LoadValueDirectWideFixed(rl_src2, r2, r3); - // Compute (r1:r0) = (r1:r0) ^ (r2:r3) - OpRegReg(kOpXor, r0, r2); // r0 = r0 ^ r2 - OpRegReg(kOpXor, r1, r3); // r1 = r1 ^ r3 - RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1, - INVALID_SREG, INVALID_SREG}; - StoreValueWide(rl_dest, rl_result); + GenLongArith(rl_dest, rl_src1, rl_src2, opcode, true); } void X86Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { - FlushAllRegs(); - LockCallTemps(); // Prepare for explicit register usage - LoadValueDirectWideFixed(rl_src, r0, r1); - // Compute (r1:r0) = -(r1:r0) - OpRegReg(kOpNeg, r0, r0); // r0 = -r0 - OpRegImm(kOpAdc, r1, 0); // r1 = r1 + CF - OpRegReg(kOpNeg, r1, r1); // r1 = -r1 - RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, kVectorNotUsed, r0, r1, - INVALID_SREG, INVALID_SREG}; + rl_src = LoadValueWide(rl_src, kCoreReg); + RegLocation rl_result = ForceTempWide(rl_src); + if (rl_dest.low_reg == rl_src.high_reg) { + // The registers are the same, so we would clobber it before the use. + int temp_reg = AllocTemp(); + OpRegCopy(temp_reg, rl_result.low_reg); + rl_result.high_reg = temp_reg; + } + OpRegReg(kOpNeg, rl_result.low_reg, rl_result.low_reg); // rLow = -rLow + OpRegImm(kOpAdc, rl_result.high_reg, 0); // rHigh = rHigh + CF + OpRegReg(kOpNeg, rl_result.high_reg, rl_result.high_reg); // rHigh = -rHigh StoreValueWide(rl_dest, rl_result); } @@ -740,16 +1326,328 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } } +RegLocation X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src, int shift_amount) { + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + switch (opcode) { + case Instruction::SHL_LONG: + case Instruction::SHL_LONG_2ADDR: + DCHECK_NE(shift_amount, 1); // Prevent a double store from happening. + if (shift_amount == 32) { + OpRegCopy(rl_result.high_reg, rl_src.low_reg); + LoadConstant(rl_result.low_reg, 0); + } else if (shift_amount > 31) { + OpRegCopy(rl_result.high_reg, rl_src.low_reg); + FreeTemp(rl_src.high_reg); + NewLIR2(kX86Sal32RI, rl_result.high_reg, shift_amount - 32); + LoadConstant(rl_result.low_reg, 0); + } else { + OpRegCopy(rl_result.low_reg, rl_src.low_reg); + OpRegCopy(rl_result.high_reg, rl_src.high_reg); + NewLIR3(kX86Shld32RRI, rl_result.high_reg, rl_result.low_reg, shift_amount); + NewLIR2(kX86Sal32RI, rl_result.low_reg, shift_amount); + } + break; + case Instruction::SHR_LONG: + case Instruction::SHR_LONG_2ADDR: + if (shift_amount == 32) { + OpRegCopy(rl_result.low_reg, rl_src.high_reg); + OpRegCopy(rl_result.high_reg, rl_src.high_reg); + NewLIR2(kX86Sar32RI, rl_result.high_reg, 31); + } else if (shift_amount > 31) { + OpRegCopy(rl_result.low_reg, rl_src.high_reg); + OpRegCopy(rl_result.high_reg, rl_src.high_reg); + NewLIR2(kX86Sar32RI, rl_result.low_reg, shift_amount - 32); + NewLIR2(kX86Sar32RI, rl_result.high_reg, 31); + } else { + OpRegCopy(rl_result.low_reg, rl_src.low_reg); + OpRegCopy(rl_result.high_reg, rl_src.high_reg); + NewLIR3(kX86Shrd32RRI, rl_result.low_reg, rl_result.high_reg, shift_amount); + NewLIR2(kX86Sar32RI, rl_result.high_reg, shift_amount); + } + break; + case Instruction::USHR_LONG: + case Instruction::USHR_LONG_2ADDR: + if (shift_amount == 32) { + OpRegCopy(rl_result.low_reg, rl_src.high_reg); + LoadConstant(rl_result.high_reg, 0); + } else if (shift_amount > 31) { + OpRegCopy(rl_result.low_reg, rl_src.high_reg); + NewLIR2(kX86Shr32RI, rl_result.low_reg, shift_amount - 32); + LoadConstant(rl_result.high_reg, 0); + } else { + OpRegCopy(rl_result.low_reg, rl_src.low_reg); + OpRegCopy(rl_result.high_reg, rl_src.high_reg); + NewLIR3(kX86Shrd32RRI, rl_result.low_reg, rl_result.high_reg, shift_amount); + NewLIR2(kX86Shr32RI, rl_result.high_reg, shift_amount); + } + break; + default: + LOG(FATAL) << "Unexpected case"; + } + return rl_result; +} + void X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_shift) { - // Default implementation is just to ignore the constant case. - GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); + RegLocation rl_src, RegLocation rl_shift) { + // Per spec, we only care about low 6 bits of shift amount. + int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f; + if (shift_amount == 0) { + rl_src = LoadValueWide(rl_src, kCoreReg); + StoreValueWide(rl_dest, rl_src); + return; + } else if (shift_amount == 1 && + (opcode == Instruction::SHL_LONG || opcode == Instruction::SHL_LONG_2ADDR)) { + // Need to handle this here to avoid calling StoreValueWide twice. + GenAddLong(Instruction::ADD_LONG, rl_dest, rl_src, rl_src); + return; + } + if (BadOverlap(rl_src, rl_dest)) { + GenShiftOpLong(opcode, rl_dest, rl_src, rl_shift); + return; + } + rl_src = LoadValueWide(rl_src, kCoreReg); + RegLocation rl_result = GenShiftImmOpLong(opcode, rl_dest, rl_src, shift_amount); + StoreValueWide(rl_dest, rl_result); } void X86Mir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { - // Default - bail to non-const handler. - GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2); + switch (opcode) { + case Instruction::ADD_LONG: + case Instruction::AND_LONG: + case Instruction::OR_LONG: + case Instruction::XOR_LONG: + if (rl_src2.is_const) { + GenLongLongImm(rl_dest, rl_src1, rl_src2, opcode); + } else { + DCHECK(rl_src1.is_const); + GenLongLongImm(rl_dest, rl_src2, rl_src1, opcode); + } + break; + case Instruction::SUB_LONG: + case Instruction::SUB_LONG_2ADDR: + if (rl_src2.is_const) { + GenLongLongImm(rl_dest, rl_src1, rl_src2, opcode); + } else { + GenSubLong(opcode, rl_dest, rl_src1, rl_src2); + } + break; + case Instruction::ADD_LONG_2ADDR: + case Instruction::OR_LONG_2ADDR: + case Instruction::XOR_LONG_2ADDR: + case Instruction::AND_LONG_2ADDR: + if (rl_src2.is_const) { + GenLongImm(rl_dest, rl_src2, opcode); + } else { + DCHECK(rl_src1.is_const); + GenLongLongImm(rl_dest, rl_src2, rl_src1, opcode); + } + break; + default: + // Default - bail to non-const handler. + GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2); + break; + } +} + +bool X86Mir2Lir::IsNoOp(Instruction::Code op, int32_t value) { + switch (op) { + case Instruction::AND_LONG_2ADDR: + case Instruction::AND_LONG: + return value == -1; + case Instruction::OR_LONG: + case Instruction::OR_LONG_2ADDR: + case Instruction::XOR_LONG: + case Instruction::XOR_LONG_2ADDR: + return value == 0; + default: + return false; + } +} + +X86OpCode X86Mir2Lir::GetOpcode(Instruction::Code op, RegLocation dest, RegLocation rhs, + bool is_high_op) { + bool rhs_in_mem = rhs.location != kLocPhysReg; + bool dest_in_mem = dest.location != kLocPhysReg; + DCHECK(!rhs_in_mem || !dest_in_mem); + switch (op) { + case Instruction::ADD_LONG: + case Instruction::ADD_LONG_2ADDR: + if (dest_in_mem) { + return is_high_op ? kX86Adc32MR : kX86Add32MR; + } else if (rhs_in_mem) { + return is_high_op ? kX86Adc32RM : kX86Add32RM; + } + return is_high_op ? kX86Adc32RR : kX86Add32RR; + case Instruction::SUB_LONG: + case Instruction::SUB_LONG_2ADDR: + if (dest_in_mem) { + return is_high_op ? kX86Sbb32MR : kX86Sub32MR; + } else if (rhs_in_mem) { + return is_high_op ? kX86Sbb32RM : kX86Sub32RM; + } + return is_high_op ? kX86Sbb32RR : kX86Sub32RR; + case Instruction::AND_LONG_2ADDR: + case Instruction::AND_LONG: + if (dest_in_mem) { + return kX86And32MR; + } + return rhs_in_mem ? kX86And32RM : kX86And32RR; + case Instruction::OR_LONG: + case Instruction::OR_LONG_2ADDR: + if (dest_in_mem) { + return kX86Or32MR; + } + return rhs_in_mem ? kX86Or32RM : kX86Or32RR; + case Instruction::XOR_LONG: + case Instruction::XOR_LONG_2ADDR: + if (dest_in_mem) { + return kX86Xor32MR; + } + return rhs_in_mem ? kX86Xor32RM : kX86Xor32RR; + default: + LOG(FATAL) << "Unexpected opcode: " << op; + return kX86Add32RR; + } +} + +X86OpCode X86Mir2Lir::GetOpcode(Instruction::Code op, RegLocation loc, bool is_high_op, + int32_t value) { + bool in_mem = loc.location != kLocPhysReg; + bool byte_imm = IS_SIMM8(value); + DCHECK(in_mem || !IsFpReg(loc.low_reg)); + switch (op) { + case Instruction::ADD_LONG: + case Instruction::ADD_LONG_2ADDR: + if (byte_imm) { + if (in_mem) { + return is_high_op ? kX86Adc32MI8 : kX86Add32MI8; + } + return is_high_op ? kX86Adc32RI8 : kX86Add32RI8; + } + if (in_mem) { + return is_high_op ? kX86Adc32MI : kX86Add32MI; + } + return is_high_op ? kX86Adc32RI : kX86Add32RI; + case Instruction::SUB_LONG: + case Instruction::SUB_LONG_2ADDR: + if (byte_imm) { + if (in_mem) { + return is_high_op ? kX86Sbb32MI8 : kX86Sub32MI8; + } + return is_high_op ? kX86Sbb32RI8 : kX86Sub32RI8; + } + if (in_mem) { + return is_high_op ? kX86Sbb32MI : kX86Sub32MI; + } + return is_high_op ? kX86Sbb32RI : kX86Sub32RI; + case Instruction::AND_LONG_2ADDR: + case Instruction::AND_LONG: + if (byte_imm) { + return in_mem ? kX86And32MI8 : kX86And32RI8; + } + return in_mem ? kX86And32MI : kX86And32RI; + case Instruction::OR_LONG: + case Instruction::OR_LONG_2ADDR: + if (byte_imm) { + return in_mem ? kX86Or32MI8 : kX86Or32RI8; + } + return in_mem ? kX86Or32MI : kX86Or32RI; + case Instruction::XOR_LONG: + case Instruction::XOR_LONG_2ADDR: + if (byte_imm) { + return in_mem ? kX86Xor32MI8 : kX86Xor32RI8; + } + return in_mem ? kX86Xor32MI : kX86Xor32RI; + default: + LOG(FATAL) << "Unexpected opcode: " << op; + return kX86Add32MI; + } +} + +void X86Mir2Lir::GenLongImm(RegLocation rl_dest, RegLocation rl_src, Instruction::Code op) { + DCHECK(rl_src.is_const); + int64_t val = mir_graph_->ConstantValueWide(rl_src); + int32_t val_lo = Low32Bits(val); + int32_t val_hi = High32Bits(val); + rl_dest = UpdateLocWide(rl_dest); + + // Can we just do this into memory? + if ((rl_dest.location == kLocDalvikFrame) || + (rl_dest.location == kLocCompilerTemp)) { + int rBase = TargetReg(kSp); + int displacement = SRegOffset(rl_dest.s_reg_low); + + if (!IsNoOp(op, val_lo)) { + X86OpCode x86op = GetOpcode(op, rl_dest, false, val_lo); + LIR *lir = NewLIR3(x86op, rBase, displacement + LOWORD_OFFSET, val_lo); + AnnotateDalvikRegAccess(lir, (displacement + LOWORD_OFFSET) >> 2, + false /* is_load */, true /* is64bit */); + } + if (!IsNoOp(op, val_hi)) { + X86OpCode x86op = GetOpcode(op, rl_dest, true, val_hi); + LIR *lir = NewLIR3(x86op, rBase, displacement + HIWORD_OFFSET, val_hi); + AnnotateDalvikRegAccess(lir, (displacement + HIWORD_OFFSET) >> 2, + false /* is_load */, true /* is64bit */); + } + return; + } + + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + DCHECK_EQ(rl_result.location, kLocPhysReg); + DCHECK(!IsFpReg(rl_result.low_reg)); + + if (!IsNoOp(op, val_lo)) { + X86OpCode x86op = GetOpcode(op, rl_result, false, val_lo); + NewLIR2(x86op, rl_result.low_reg, val_lo); + } + if (!IsNoOp(op, val_hi)) { + X86OpCode x86op = GetOpcode(op, rl_result, true, val_hi); + NewLIR2(x86op, rl_result.high_reg, val_hi); + } + StoreValueWide(rl_dest, rl_result); +} + +void X86Mir2Lir::GenLongLongImm(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, Instruction::Code op) { + DCHECK(rl_src2.is_const); + int64_t val = mir_graph_->ConstantValueWide(rl_src2); + int32_t val_lo = Low32Bits(val); + int32_t val_hi = High32Bits(val); + rl_dest = UpdateLocWide(rl_dest); + rl_src1 = UpdateLocWide(rl_src1); + + // Can we do this directly into the destination registers? + if (rl_dest.location == kLocPhysReg && rl_src1.location == kLocPhysReg && + rl_dest.low_reg == rl_src1.low_reg && rl_dest.high_reg == rl_src1.high_reg && + !IsFpReg(rl_dest.low_reg)) { + if (!IsNoOp(op, val_lo)) { + X86OpCode x86op = GetOpcode(op, rl_dest, false, val_lo); + NewLIR2(x86op, rl_dest.low_reg, val_lo); + } + if (!IsNoOp(op, val_hi)) { + X86OpCode x86op = GetOpcode(op, rl_dest, true, val_hi); + NewLIR2(x86op, rl_dest.high_reg, val_hi); + } + return; + } + + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + DCHECK_EQ(rl_src1.location, kLocPhysReg); + + // We need the values to be in a temporary + RegLocation rl_result = ForceTempWide(rl_src1); + if (!IsNoOp(op, val_lo)) { + X86OpCode x86op = GetOpcode(op, rl_result, false, val_lo); + NewLIR2(x86op, rl_result.low_reg, val_lo); + } + if (!IsNoOp(op, val_hi)) { + X86OpCode x86op = GetOpcode(op, rl_result, true, val_hi); + NewLIR2(x86op, rl_result.high_reg, val_hi); + } + + StoreFinalValueWide(rl_dest, rl_result); } } // namespace art diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 5c993c5ac5..f22354859c 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -243,9 +243,9 @@ std::string X86Mir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char } break; case 't': - buf += StringPrintf("0x%08x (L%p)", - reinterpret_cast<uintptr_t>(base_addr) - + lir->offset + operand, lir->target); + buf += StringPrintf("0x%08" PRIxPTR " (L%p)", + reinterpret_cast<uintptr_t>(base_addr) + lir->offset + operand, + lir->target); break; default: buf += StringPrintf("DecodeError '%c'", fmt[i]); @@ -679,31 +679,24 @@ RegLocation X86Mir2Lir::EvalLocWide(RegLocation loc, int reg_class, bool update) } DCHECK_NE(loc.s_reg_low, INVALID_SREG); - if (IsFpReg(loc.low_reg) && reg_class != kCoreReg) { - // Need a wide vector register. - low_reg = AllocTypedTemp(true, reg_class); - loc.low_reg = low_reg; - loc.high_reg = low_reg; // Play nice with existing code. - loc.vec_len = kVectorLength8; - if (update) { - loc.location = kLocPhysReg; - MarkLive(loc.low_reg, loc.s_reg_low); - } - DCHECK(IsFpReg(loc.low_reg)); - } else { - DCHECK_NE(GetSRegHi(loc.s_reg_low), INVALID_SREG); + DCHECK_NE(GetSRegHi(loc.s_reg_low), INVALID_SREG); - new_regs = AllocTypedTempPair(loc.fp, reg_class); - loc.low_reg = new_regs & 0xff; - loc.high_reg = (new_regs >> 8) & 0xff; + new_regs = AllocTypedTempPair(loc.fp, reg_class); + loc.low_reg = new_regs & 0xff; + loc.high_reg = (new_regs >> 8) & 0xff; + if (loc.low_reg == loc.high_reg) { + DCHECK(IsFpReg(loc.low_reg)); + loc.vec_len = kVectorLength8; + } else { MarkPair(loc.low_reg, loc.high_reg); - if (update) { - loc.location = kLocPhysReg; - MarkLive(loc.low_reg, loc.s_reg_low); + } + if (update) { + loc.location = kLocPhysReg; + MarkLive(loc.low_reg, loc.s_reg_low); + if (loc.low_reg != loc.high_reg) { MarkLive(loc.high_reg, GetSRegHi(loc.s_reg_low)); } - DCHECK(!IsFpReg(loc.low_reg) || ((loc.low_reg & 0x1) == 0)); } return loc; } @@ -796,4 +789,23 @@ void X86Mir2Lir::GenConstWide(RegLocation rl_dest, int64_t value) { // Just use the standard code to do the generation. Mir2Lir::GenConstWide(rl_dest, value); } + +// TODO: Merge with existing RegLocation dumper in vreg_analysis.cc +void X86Mir2Lir::DumpRegLocation(RegLocation loc) { + LOG(INFO) << "location: " << loc.location << ',' + << (loc.wide ? " w" : " ") + << (loc.defined ? " D" : " ") + << (loc.is_const ? " c" : " ") + << (loc.fp ? " F" : " ") + << (loc.core ? " C" : " ") + << (loc.ref ? " r" : " ") + << (loc.high_word ? " h" : " ") + << (loc.home ? " H" : " ") + << " vec_len: " << loc.vec_len + << ", low: " << static_cast<int>(loc.low_reg) + << ", high: " << static_cast<int>(loc.high_reg) + << ", s_reg: " << loc.s_reg_low + << ", orig: " << loc.orig_sreg; +} + } // namespace art diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index 91c39fa682..97c04dc65d 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -141,7 +141,14 @@ LIR* X86Mir2Lir::OpRegImm(OpKind op, int r_dest_src1, int value) { case kOpSub: opcode = byte_imm ? kX86Sub32RI8 : kX86Sub32RI; break; case kOpXor: opcode = byte_imm ? kX86Xor32RI8 : kX86Xor32RI; break; case kOpCmp: opcode = byte_imm ? kX86Cmp32RI8 : kX86Cmp32RI; break; - case kOpMov: return LoadConstantNoClobber(r_dest_src1, value); + case kOpMov: + /* + * Moving the constant zero into register can be specialized as an xor of the register. + * However, that sets eflags while the move does not. For that reason here, always do + * the move and if caller is flexible, they should be calling LoadConstantNoClobber instead. + */ + opcode = kX86Mov32RI; + break; case kOpMul: opcode = byte_imm ? kX86Imul32RRI8 : kX86Imul32RRI; return NewLIR3(opcode, r_dest_src1, r_dest_src1, value); @@ -346,8 +353,7 @@ LIR* X86Mir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value) { if (val_hi != 0) { r_dest_hi = AllocTempDouble(); LoadConstantNoClobber(r_dest_hi, val_hi); - NewLIR2(kX86PsllqRI, r_dest_hi, 32); - NewLIR2(kX86OrpsRR, r_dest_lo, r_dest_hi); + NewLIR2(kX86PunpckldqRR, r_dest_lo, r_dest_hi); FreeTemp(r_dest_hi); } } @@ -435,15 +441,37 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(int rBase, int r_index, int scale, displacement + LOWORD_OFFSET); } else { if (rBase == r_dest) { - load2 = NewLIR5(opcode, r_dest_hi, rBase, r_index, scale, - displacement + HIWORD_OFFSET); - load = NewLIR5(opcode, r_dest, rBase, r_index, scale, - displacement + LOWORD_OFFSET); + if (r_dest_hi == r_index) { + // We can't use either register for the first load. + int temp = AllocTemp(); + load2 = NewLIR5(opcode, temp, rBase, r_index, scale, + displacement + HIWORD_OFFSET); + load = NewLIR5(opcode, r_dest, rBase, r_index, scale, + displacement + LOWORD_OFFSET); + OpRegCopy(r_dest_hi, temp); + FreeTemp(temp); + } else { + load2 = NewLIR5(opcode, r_dest_hi, rBase, r_index, scale, + displacement + HIWORD_OFFSET); + load = NewLIR5(opcode, r_dest, rBase, r_index, scale, + displacement + LOWORD_OFFSET); + } } else { - load = NewLIR5(opcode, r_dest, rBase, r_index, scale, - displacement + LOWORD_OFFSET); - load2 = NewLIR5(opcode, r_dest_hi, rBase, r_index, scale, - displacement + HIWORD_OFFSET); + if (r_dest == r_index) { + // We can't use either register for the first load. + int temp = AllocTemp(); + load = NewLIR5(opcode, temp, rBase, r_index, scale, + displacement + LOWORD_OFFSET); + load2 = NewLIR5(opcode, r_dest_hi, rBase, r_index, scale, + displacement + HIWORD_OFFSET); + OpRegCopy(r_dest, temp); + FreeTemp(temp); + } else { + load = NewLIR5(opcode, r_dest, rBase, r_index, scale, + displacement + LOWORD_OFFSET); + load2 = NewLIR5(opcode, r_dest_hi, rBase, r_index, scale, + displacement + HIWORD_OFFSET); + } } } } @@ -572,8 +600,7 @@ void X86Mir2Lir::OpVectorRegCopyWide(uint8_t fp_reg, uint8_t low_reg, uint8_t hi NewLIR2(kX86MovdxrRR, fp_reg, low_reg); int tmp_reg = AllocTempDouble(); NewLIR2(kX86MovdxrRR, tmp_reg, high_reg); - NewLIR2(kX86PsllqRI, tmp_reg, 32); - NewLIR2(kX86OrpsRR, fp_reg, tmp_reg); + NewLIR2(kX86PunpckldqRR, fp_reg, tmp_reg); FreeTemp(tmp_reg); } diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 1488f5d557..7f35d061b5 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -304,6 +304,8 @@ enum X86OpCode { BinaryShiftOpCode(kX86Sar), #undef BinaryShiftOpcode kX86Cmc, + kX86Shld32RRI, + kX86Shrd32RRI, #define UnaryOpcode(opcode, reg, mem, array) \ opcode ## 8 ## reg, opcode ## 8 ## mem, opcode ## 8 ## array, \ opcode ## 16 ## reg, opcode ## 16 ## mem, opcode ## 16 ## array, \ @@ -316,6 +318,7 @@ enum X86OpCode { UnaryOpcode(kX86Imul, DaR, DaM, DaA), UnaryOpcode(kX86Divmod, DaR, DaM, DaA), UnaryOpcode(kX86Idivmod, DaR, DaM, DaA), + kx86Cdq32Da, kX86Bswap32R, kX86Push32R, kX86Pop32R, #undef UnaryOpcode @@ -349,6 +352,7 @@ enum X86OpCode { Binary0fOpCode(kX86Subss), // float subtract Binary0fOpCode(kX86Divsd), // double divide Binary0fOpCode(kX86Divss), // float divide + Binary0fOpCode(kX86Punpckldq), // Interleave low-order double words kX86PsrlqRI, // right shift of floating point registers kX86PsllqRI, // left shift of floating point registers kX86SqrtsdRR, // sqrt of floating point register @@ -397,6 +401,7 @@ enum X86EncodingKind { kRegImm, kMemImm, kArrayImm, kThreadImm, // RI, MI, AI and TI instruction kinds. kRegRegImm, kRegMemImm, kRegArrayImm, // RRI, RMI and RAI instruction kinds. kMovRegImm, // Shorter form move RI. + kRegRegImmRev, // RRI with first reg in r/m kShiftRegImm, kShiftMemImm, kShiftArrayImm, // Shift opcode with immediate. kShiftRegCl, kShiftMemCl, kShiftArrayCl, // Shift opcode with register CL. kRegRegReg, kRegRegMem, kRegRegArray, // RRR, RRM, RRA instruction kinds. diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc new file mode 100644 index 0000000000..edccec55ba --- /dev/null +++ b/compiler/dex/verification_results.cc @@ -0,0 +1,110 @@ +/* + * 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/stl_util.h" +#include "base/mutex.h" +#include "base/mutex-inl.h" +#include "thread.h" +#include "thread-inl.h" +#include "verified_method.h" +#include "verifier/method_verifier.h" +#include "verifier/method_verifier-inl.h" + +namespace art { + +VerificationResults::VerificationResults() + : verified_methods_lock_("compiler verified methods lock"), + verified_methods_(), + rejected_classes_lock_("compiler rejected classes lock"), + rejected_classes_() { +} + +VerificationResults::~VerificationResults() { + Thread* self = Thread::Current(); + { + WriterMutexLock mu(self, verified_methods_lock_); + STLDeleteValues(&verified_methods_); + } +} + +bool VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) { + MethodReference ref = method_verifier->GetMethodReference(); + bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags()); + // TODO: Check also for virtual/interface invokes when DEX-to-DEX supports devirtualization. + if (!compile && !method_verifier->HasCheckCasts()) { + return true; + } + + const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile); + if (verified_method == nullptr) { + DCHECK(method_verifier->HasFailures()); + return false; + } + + WriterMutexLock mu(Thread::Current(), verified_methods_lock_); + auto it = verified_methods_.find(ref); + if (it != verified_methods_.end()) { + // TODO: Investigate why are we doing the work again for this method and try to avoid it. + LOG(WARNING) << "Method processed more than once: " + << PrettyMethod(ref.dex_method_index, *ref.dex_file); + DCHECK_EQ(it->second->GetDevirtMap().size(), verified_method->GetDevirtMap().size()); + DCHECK_EQ(it->second->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size()); + DCHECK_EQ(it->second->GetDexGcMap().size(), verified_method->GetDexGcMap().size()); + delete it->second; + verified_methods_.erase(it); + } + verified_methods_.Put(ref, verified_method); + DCHECK(verified_methods_.find(ref) != verified_methods_.end()); + return true; +} + +const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) { + ReaderMutexLock mu(Thread::Current(), verified_methods_lock_); + auto it = verified_methods_.find(ref); + return (it != verified_methods_.end()) ? it->second : nullptr; +} + +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& method_ref, + const uint32_t access_flags) { +#ifdef ART_SEA_IR_MODE + bool use_sea = Runtime::Current()->IsSeaIRMode(); + use_sea = use_sea && (std::string::npos != PrettyMethod( + method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci")); + if (use_sea) return true; +#endif + // Don't compile class initializers, ever. + if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { + return false; + } + return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly); +} + +} // namespace art diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h new file mode 100644 index 0000000000..2eb07131ce --- /dev/null +++ b/compiler/dex/verification_results.h @@ -0,0 +1,70 @@ +/* + * 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 <vector> + +#include "base/macros.h" +#include "base/mutex.h" +#include "class_reference.h" +#include "method_reference.h" +#include "safe_map.h" + +namespace art { + +namespace verifier { +class MethodVerifier; +} // namespace verifier + +class VerifiedMethod; + +class VerificationResults { + public: + VerificationResults(); + ~VerificationResults(); + + bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(verified_methods_lock_); + + const VerifiedMethod* GetVerifiedMethod(MethodReference ref) + LOCKS_EXCLUDED(verified_methods_lock_); + + void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); + bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); + + static bool IsCandidateForCompilation(MethodReference& method_ref, + const uint32_t access_flags); + + private: + // Verified methods. + typedef SafeMap<MethodReference, const VerifiedMethod*, + MethodReferenceComparator> VerifiedMethodMap; + ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + VerifiedMethodMap verified_methods_; + + // Rejected classes. + ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_); +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_VERIFICATION_RESULTS_H_ diff --git a/compiler/dex/verified_methods_data.cc b/compiler/dex/verified_method.cc index e6c4ddab06..0f812a49cd 100644 --- a/compiler/dex/verified_methods_data.cc +++ b/compiler/dex/verified_method.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * 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. @@ -14,6 +14,12 @@ * limitations under the License. */ +#include "verified_method.h" + +#include <algorithm> +#include <vector> + +#include "base/logging.h" #include "base/stl_util.h" #include "dex_file.h" #include "dex_instruction.h" @@ -28,7 +34,7 @@ #include "mirror/dex_cache-inl.h" #include "mirror/object.h" #include "mirror/object-inl.h" -#include "verified_methods_data.h" +#include "UniquePtr.h" #include "verifier/dex_gc_map.h" #include "verifier/method_verifier.h" #include "verifier/method_verifier-inl.h" @@ -37,150 +43,58 @@ namespace art { -VerifiedMethodsData::VerifiedMethodsData() - : dex_gc_maps_lock_("compiler GC maps lock"), - dex_gc_maps_(), - safecast_map_lock_("compiler Cast Elision lock"), - safecast_map_(), - devirt_maps_lock_("compiler Devirtualization lock"), - devirt_maps_(), - rejected_classes_lock_("compiler rejected classes lock"), - rejected_classes_() { -} - -VerifiedMethodsData::~VerifiedMethodsData() { - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, dex_gc_maps_lock_); - STLDeleteValues(&dex_gc_maps_); - } - { - WriterMutexLock mu(self, safecast_map_lock_); - STLDeleteValues(&safecast_map_); - } - { - WriterMutexLock mu(self, devirt_maps_lock_); - STLDeleteValues(&devirt_maps_); - } -} - -bool VerifiedMethodsData::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) { - MethodReference ref = method_verifier->GetMethodReference(); - bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags()); +const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier, + bool compile) { + UniquePtr<VerifiedMethod> verified_method(new VerifiedMethod); if (compile) { - /* Generate a register map and add it to the method. */ - const std::vector<uint8_t>* dex_gc_map = GenerateGcMap(method_verifier); - if (dex_gc_map == NULL) { - DCHECK(method_verifier->HasFailures()); - return false; // Not a real failure, but a failure to encode + /* Generate a register map. */ + if (!verified_method->GenerateGcMap(method_verifier)) { + CHECK(method_verifier->HasFailures()); + return nullptr; // Not a real failure, but a failure to encode. } if (kIsDebugBuild) { - VerifyGcMap(method_verifier, *dex_gc_map); + VerifyGcMap(method_verifier, verified_method->dex_gc_map_); } - SetDexGcMap(ref, dex_gc_map); // TODO: move this out when DEX-to-DEX supports devirtualization. if (method_verifier->HasVirtualOrInterfaceInvokes()) { - PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(method_verifier); - if (pc_to_concrete_method != NULL) { - SetDevirtMap(ref, pc_to_concrete_method); - } + verified_method->GenerateDevirtMap(method_verifier); } } if (method_verifier->HasCheckCasts()) { - MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet(method_verifier); - if (method_to_safe_casts != NULL) { - SetSafeCastMap(ref, method_to_safe_casts); - } - } - return true; -} - -const std::vector<uint8_t>* VerifiedMethodsData::GetDexGcMap(MethodReference ref) { - ReaderMutexLock mu(Thread::Current(), dex_gc_maps_lock_); - DexGcMapTable::const_iterator it = dex_gc_maps_.find(ref); - CHECK(it != dex_gc_maps_.end()) - << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file); - CHECK(it->second != NULL); - return it->second; -} - -const MethodReference* VerifiedMethodsData::GetDevirtMap(const MethodReference& ref, - uint32_t dex_pc) { - ReaderMutexLock mu(Thread::Current(), devirt_maps_lock_); - DevirtualizationMapTable::const_iterator it = devirt_maps_.find(ref); - if (it == devirt_maps_.end()) { - return NULL; - } - - // Look up the PC in the map, get the concrete method to execute and return its reference. - PcToConcreteMethodMap::const_iterator pc_to_concrete_method = it->second->find(dex_pc); - if (pc_to_concrete_method != it->second->end()) { - return &(pc_to_concrete_method->second); - } else { - return NULL; - } -} - -bool VerifiedMethodsData::IsSafeCast(MethodReference ref, uint32_t pc) { - ReaderMutexLock mu(Thread::Current(), safecast_map_lock_); - SafeCastMap::const_iterator it = safecast_map_.find(ref); - if (it == safecast_map_.end()) { - return false; + verified_method->GenerateSafeCastSet(method_verifier); } - - // Look up the cast address in the set of safe casts - // Use binary_search for lookup in the sorted vector. - return std::binary_search(it->second->begin(), it->second->end(), pc); -} - -void VerifiedMethodsData::AddRejectedClass(ClassReference ref) { - { - WriterMutexLock mu(Thread::Current(), rejected_classes_lock_); - rejected_classes_.insert(ref); - } - DCHECK(IsClassRejected(ref)); + return verified_method.release(); } -bool VerifiedMethodsData::IsClassRejected(ClassReference ref) { - ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_); - return (rejected_classes_.find(ref) != rejected_classes_.end()); +const MethodReference* VerifiedMethod::GetDevirtTarget(uint32_t dex_pc) const { + auto it = devirt_map_.find(dex_pc); + return (it != devirt_map_.end()) ? &it->second : nullptr; } -bool VerifiedMethodsData::IsCandidateForCompilation(MethodReference& method_ref, - const uint32_t access_flags) { -#ifdef ART_SEA_IR_MODE - bool use_sea = Runtime::Current()->IsSeaIRMode(); - use_sea = use_sea && (std::string::npos != PrettyMethod( - method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci")); - if (use_sea) return true; -#endif - // Don't compile class initializers, ever. - if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { - return false; - } - return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly); +bool VerifiedMethod::IsSafeCast(uint32_t pc) const { + return std::binary_search(safe_cast_set_.begin(), safe_cast_set_.end(), pc); } -const std::vector<uint8_t>* VerifiedMethodsData::GenerateGcMap( - verifier::MethodVerifier* method_verifier) { +bool VerifiedMethod::GenerateGcMap(verifier::MethodVerifier* method_verifier) { + DCHECK(dex_gc_map_.empty()); size_t num_entries, ref_bitmap_bits, pc_bits; ComputeGcMapSizes(method_verifier, &num_entries, &ref_bitmap_bits, &pc_bits); - // There's a single byte to encode the size of each bitmap + // There's a single byte to encode the size of each bitmap. if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) { // TODO: either a better GC map format or per method failures method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers"; - return NULL; + return false; } size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8; - // There are 2 bytes to encode the number of entries + // There are 2 bytes to encode the number of entries. if (num_entries >= 65536) { - // TODO: either a better GC map format or per method failures + // TODO: Either a better GC map format or per method failures. method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with " << num_entries << " entries"; - return NULL; + return false; } size_t pc_bytes; verifier::RegisterMapFormat format; @@ -191,45 +105,39 @@ const std::vector<uint8_t>* VerifiedMethodsData::GenerateGcMap( format = verifier::kRegMapFormatCompact16; pc_bytes = 2; } else { - // TODO: either a better GC map format or per method failures + // TODO: Either a better GC map format or per method failures. method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with " << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)"; - return NULL; + return false; } size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4; - std::vector<uint8_t>* table = new std::vector<uint8_t>; - if (table == NULL) { - method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD) - << "Failed to encode GC map (size=" << table_size << ")"; - return NULL; - } - table->reserve(table_size); - // Write table header - table->push_back(format | ((ref_bitmap_bytes & ~0xFF) >> 5)); - table->push_back(ref_bitmap_bytes & 0xFF); - table->push_back(num_entries & 0xFF); - table->push_back((num_entries >> 8) & 0xFF); - // Write table data + dex_gc_map_.reserve(table_size); + // Write table header. + dex_gc_map_.push_back(format | ((ref_bitmap_bytes & ~0xFF) >> 5)); + dex_gc_map_.push_back(ref_bitmap_bytes & 0xFF); + dex_gc_map_.push_back(num_entries & 0xFF); + dex_gc_map_.push_back((num_entries >> 8) & 0xFF); + // Write table data. const DexFile::CodeItem* code_item = method_verifier->CodeItem(); for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) { if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) { - table->push_back(i & 0xFF); + dex_gc_map_.push_back(i & 0xFF); if (pc_bytes == 2) { - table->push_back((i >> 8) & 0xFF); + dex_gc_map_.push_back((i >> 8) & 0xFF); } verifier::RegisterLine* line = method_verifier->GetRegLine(i); - line->WriteReferenceBitMap(*table, ref_bitmap_bytes); + line->WriteReferenceBitMap(dex_gc_map_, ref_bitmap_bytes); } } - DCHECK_EQ(table->size(), table_size); - return table; + DCHECK_EQ(dex_gc_map_.size(), table_size); + return true; } -void VerifiedMethodsData::VerifyGcMap(verifier::MethodVerifier* method_verifier, - const std::vector<uint8_t>& data) { +void VerifiedMethod::VerifyGcMap(verifier::MethodVerifier* method_verifier, + const std::vector<uint8_t>& data) { // Check that for every GC point there is a map entry, there aren't entries for non-GC points, - // that the table data is well formed and all references are marked (or not) in the bitmap + // that the table data is well formed and all references are marked (or not) in the bitmap. verifier::DexPcToReferenceMap map(&data[0]); DCHECK_EQ(data.size(), map.RawSize()); size_t map_index = 0; @@ -237,30 +145,30 @@ void VerifiedMethodsData::VerifyGcMap(verifier::MethodVerifier* method_verifier, for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) { const uint8_t* reg_bitmap = map.FindBitMap(i, false); if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) { - CHECK_LT(map_index, map.NumEntries()); - CHECK_EQ(map.GetDexPc(map_index), i); - CHECK_EQ(map.GetBitMap(map_index), reg_bitmap); + DCHECK_LT(map_index, map.NumEntries()); + DCHECK_EQ(map.GetDexPc(map_index), i); + DCHECK_EQ(map.GetBitMap(map_index), reg_bitmap); map_index++; verifier::RegisterLine* line = method_verifier->GetRegLine(i); for (size_t j = 0; j < code_item->registers_size_; j++) { if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) { - CHECK_LT(j / 8, map.RegWidth()); - CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1); + DCHECK_LT(j / 8, map.RegWidth()); + DCHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1); } else if ((j / 8) < map.RegWidth()) { - CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0); + DCHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0); } else { - // If a register doesn't contain a reference then the bitmap may be shorter than the line + // If a register doesn't contain a reference then the bitmap may be shorter than the line. } } } else { - CHECK(reg_bitmap == NULL); + DCHECK(reg_bitmap == NULL); } } } -void VerifiedMethodsData::ComputeGcMapSizes(verifier::MethodVerifier* method_verifier, - size_t* gc_points, size_t* ref_bitmap_bits, - size_t* log2_max_gc_pc) { +void VerifiedMethod::ComputeGcMapSizes(verifier::MethodVerifier* method_verifier, + size_t* gc_points, size_t* ref_bitmap_bits, + size_t* log2_max_gc_pc) { size_t local_gc_points = 0; size_t max_insn = 0; size_t max_ref_reg = -1; @@ -274,7 +182,7 @@ void VerifiedMethodsData::ComputeGcMapSizes(verifier::MethodVerifier* method_ver } } *gc_points = local_gc_points; - *ref_bitmap_bits = max_ref_reg + 1; // if max register is 0 we need 1 bit to encode (ie +1) + *ref_bitmap_bits = max_ref_reg + 1; // If max register is 0 we need 1 bit to encode (ie +1). size_t i = 0; while ((1U << i) <= max_insn) { i++; @@ -282,92 +190,13 @@ void VerifiedMethodsData::ComputeGcMapSizes(verifier::MethodVerifier* method_ver *log2_max_gc_pc = i; } -void VerifiedMethodsData::SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* gc_map) { - DCHECK(Runtime::Current()->IsCompiler()); - { - WriterMutexLock mu(Thread::Current(), dex_gc_maps_lock_); - DexGcMapTable::iterator it = dex_gc_maps_.find(ref); - if (it != dex_gc_maps_.end()) { - delete it->second; - dex_gc_maps_.erase(it); - } - dex_gc_maps_.Put(ref, gc_map); - } - DCHECK(GetDexGcMap(ref) != NULL); -} - -VerifiedMethodsData::MethodSafeCastSet* VerifiedMethodsData::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 NULL; - } - UniquePtr<MethodSafeCastSet> mscs; - 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) || (code == Instruction::APUT_OBJECT)) { - uint32_t dex_pc = inst->GetDexPc(code_item->insns_); - const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); - bool is_safe_cast = false; - if (code == Instruction::CHECK_CAST) { - const verifier::RegType& reg_type(line->GetRegisterType(inst->VRegA_21c())); - const verifier::RegType& cast_type = - method_verifier->ResolveCheckedClass(inst->VRegB_21c()); - is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type); - } else { - const verifier::RegType& array_type(line->GetRegisterType(inst->VRegB_23x())); - // We only know its safe to assign to an array if the array type is precise. For example, - // an Object[] can have any type of object stored in it, but it may also be assigned a - // String[] in which case the stores need to be of Strings. - if (array_type.IsPreciseReference()) { - const verifier::RegType& value_type(line->GetRegisterType(inst->VRegA_23x())); - const verifier::RegType& component_type = method_verifier->GetRegTypeCache() - ->GetComponentType(array_type, method_verifier->GetClassLoader()); - is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type); - } - } - if (is_safe_cast) { - if (mscs.get() == nullptr) { - mscs.reset(new MethodSafeCastSet()); - } else { - DCHECK_LT(mscs->back(), dex_pc); // Verify ordering for push_back() to the sorted vector. - } - mscs->push_back(dex_pc); - } - } - } - return mscs.release(); -} - -void VerifiedMethodsData::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) { - WriterMutexLock mu(Thread::Current(), safecast_map_lock_); - SafeCastMap::iterator it = safecast_map_.find(ref); - if (it != safecast_map_.end()) { - delete it->second; - safecast_map_.erase(it); - } - safecast_map_.Put(ref, cast_set); - DCHECK(safecast_map_.find(ref) != safecast_map_.end()); -} - -VerifiedMethodsData::PcToConcreteMethodMap* VerifiedMethodsData::GenerateDevirtMap( - verifier::MethodVerifier* method_verifier) { +void VerifiedMethod::GenerateDevirtMap(verifier::MethodVerifier* method_verifier) { // It is risky to rely on reg_types for sharpening in cases of soft // verification, we might end up sharpening to a wrong implementation. Just abort. if (method_verifier->HasFailures()) { - return NULL; + return; } - UniquePtr<PcToConcreteMethodMap> pc_to_concrete_method_map; const DexFile::CodeItem* code_item = method_verifier->CodeItem(); const uint16_t* insns = code_item->insns_; const Instruction* inst = Instruction::At(insns); @@ -426,29 +255,58 @@ VerifiedMethodsData::PcToConcreteMethodMap* VerifiedMethodsData::GenerateDevirtM concrete_method->GetDeclaringClass()->IsFinal()) { // If we knew exactly the class being dispatched upon, or if the target method cannot be // overridden record the target to be used in the compiler driver. - if (pc_to_concrete_method_map.get() == NULL) { - pc_to_concrete_method_map.reset(new PcToConcreteMethodMap()); - } MethodReference concrete_ref( concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(), concrete_method->GetDexMethodIndex()); - pc_to_concrete_method_map->Put(dex_pc, concrete_ref); + devirt_map_.Put(dex_pc, concrete_ref); } } - return pc_to_concrete_method_map.release(); } -void VerifiedMethodsData::SetDevirtMap(MethodReference ref, - const PcToConcreteMethodMap* devirt_map) { - WriterMutexLock mu(Thread::Current(), devirt_maps_lock_); - DevirtualizationMapTable::iterator it = devirt_maps_.find(ref); - if (it != devirt_maps_.end()) { - delete it->second; - devirt_maps_.erase(it); +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_); - devirt_maps_.Put(ref, devirt_map); - DCHECK(devirt_maps_.find(ref) != devirt_maps_.end()); + for (; inst < end; inst = inst->Next()) { + Instruction::Code code = inst->Opcode(); + if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) { + uint32_t dex_pc = inst->GetDexPc(code_item->insns_); + const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); + bool is_safe_cast = false; + if (code == Instruction::CHECK_CAST) { + const verifier::RegType& reg_type(line->GetRegisterType(inst->VRegA_21c())); + const verifier::RegType& cast_type = + method_verifier->ResolveCheckedClass(inst->VRegB_21c()); + is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type); + } else { + const verifier::RegType& array_type(line->GetRegisterType(inst->VRegB_23x())); + // We only know its safe to assign to an array if the array type is precise. For example, + // an Object[] can have any type of object stored in it, but it may also be assigned a + // String[] in which case the stores need to be of Strings. + if (array_type.IsPreciseReference()) { + const verifier::RegType& value_type(line->GetRegisterType(inst->VRegA_23x())); + const verifier::RegType& component_type = method_verifier->GetRegTypeCache() + ->GetComponentType(array_type, method_verifier->GetClassLoader()); + is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type); + } + } + if (is_safe_cast) { + // 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); + } + } + } } } // namespace art diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h new file mode 100644 index 0000000000..aa0e72a5ca --- /dev/null +++ b/compiler/dex/verified_method.h @@ -0,0 +1,98 @@ +/* + * 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 "method_reference.h" +#include "safe_map.h" + +namespace art { + +namespace verifier { +class MethodVerifier; +} // namespace verifier + +class VerifiedMethod { + public: + // 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; + + // Devirtualization map type maps dex offset to concrete method reference. + typedef SafeMap<uint32_t, MethodReference> DevirtualizationMap; + + static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier, bool compile) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ~VerifiedMethod() = default; + + const std::vector<uint8_t>& GetDexGcMap() const { + return dex_gc_map_; + } + + const DevirtualizationMap& GetDevirtMap() const { + return devirt_map_; + } + + const SafeCastSet& GetSafeCastSet() const { + return safe_cast_set_; + } + + // Returns the devirtualization target method, or nullptr if none. + const MethodReference* GetDevirtTarget(uint32_t dex_pc) const; + + // 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; + + private: + VerifiedMethod() = default; + + /* + * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of + * verification). For type-precise determination we have all the data we need, so we just need to + * encode it in some clever fashion. + * Stores the data in dex_gc_map_, returns true on success and false on failure. + */ + bool GenerateGcMap(verifier::MethodVerifier* method_verifier); + + // Verify that the GC map associated with method_ is well formed. + static void VerifyGcMap(verifier::MethodVerifier* method_verifier, + const std::vector<uint8_t>& data); + + // Compute sizes for GC map data. + static void ComputeGcMapSizes(verifier::MethodVerifier* method_verifier, + size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc); + + // Generate devirtualizaion map into devirt_map_. + void GenerateDevirtMap(verifier::MethodVerifier* method_verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Generate safe case set into safe_cast_set_. + void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + std::vector<uint8_t> dex_gc_map_; + DevirtualizationMap devirt_map_; + SafeCastSet safe_cast_set_; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_VERIFIED_METHOD_H_ diff --git a/compiler/dex/verified_methods_data.h b/compiler/dex/verified_methods_data.h deleted file mode 100644 index d495dff7d9..0000000000 --- a/compiler/dex/verified_methods_data.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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_VERIFIED_METHODS_DATA_H_ -#define ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_ - -#include <stdint.h> -#include <set> -#include <vector> - -#include "base/macros.h" -#include "base/mutex.h" -#include "class_reference.h" -#include "method_reference.h" -#include "safe_map.h" - -namespace art { - -namespace verifier { -class MethodVerifier; -} // namespace verifier - -class VerifiedMethodsData { - public: - VerifiedMethodsData(); - ~VerifiedMethodsData(); - - bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - LOCKS_EXCLUDED(dex_gc_maps_lock_, devirt_maps_lock_, safecast_map_lock_); - - const std::vector<uint8_t>* GetDexGcMap(MethodReference ref) - LOCKS_EXCLUDED(dex_gc_maps_lock_); - - const MethodReference* GetDevirtMap(const MethodReference& ref, uint32_t dex_pc) - LOCKS_EXCLUDED(devirt_maps_lock_); - - // 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(MethodReference ref, uint32_t pc) LOCKS_EXCLUDED(safecast_map_lock_); - - void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); - bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); - - static bool IsCandidateForCompilation(MethodReference& method_ref, - const uint32_t access_flags); - - private: - /* - * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of - * verification). For type-precise determination we have all the data we need, so we just need to - * encode it in some clever fashion. - * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure. - */ - const std::vector<uint8_t>* GenerateGcMap(verifier::MethodVerifier* method_verifier); - - // Verify that the GC map associated with method_ is well formed - void VerifyGcMap(verifier::MethodVerifier* method_verifier, const std::vector<uint8_t>& data); - - // Compute sizes for GC map data - void ComputeGcMapSizes(verifier::MethodVerifier* method_verifier, - size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc); - - // All the GC maps that the verifier has created - typedef SafeMap<const MethodReference, const std::vector<uint8_t>*, - MethodReferenceComparator> DexGcMapTable; - ReaderWriterMutex dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - DexGcMapTable dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_); - void SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* dex_gc_map) - LOCKS_EXCLUDED(dex_gc_maps_lock_); - - // Cast elision types. - // 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> MethodSafeCastSet; - typedef SafeMap<MethodReference, const MethodSafeCastSet*, - MethodReferenceComparator> SafeCastMap; - MethodSafeCastSet* GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* mscs) - LOCKS_EXCLUDED(safecast_map_lock_); - ReaderWriterMutex safecast_map_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - SafeCastMap safecast_map_ GUARDED_BY(safecast_map_lock_); - - // Devirtualization map. - typedef SafeMap<uint32_t, MethodReference> PcToConcreteMethodMap; - typedef SafeMap<MethodReference, const PcToConcreteMethodMap*, - MethodReferenceComparator> DevirtualizationMapTable; - PcToConcreteMethodMap* GenerateDevirtMap(verifier::MethodVerifier* method_verifier) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ReaderWriterMutex devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - DevirtualizationMapTable devirt_maps_ GUARDED_BY(devirt_maps_lock_); - void SetDevirtMap(MethodReference ref, const PcToConcreteMethodMap* pc_method_map) - LOCKS_EXCLUDED(devirt_maps_lock_); - - // Rejected classes - typedef std::set<ClassReference> RejectedClassesTable; - ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - RejectedClassesTable rejected_classes_ GUARDED_BY(rejected_classes_lock_); -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 714dc4cb5e..d504a4e704 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -27,7 +27,8 @@ #include "class_linker.h" #include "dex_compilation_unit.h" #include "dex_file-inl.h" -#include "dex/verified_methods_data.h" +#include "dex/verification_results.h" +#include "dex/verified_method.h" #include "jni_internal.h" #include "object_utils.h" #include "runtime.h" @@ -336,13 +337,13 @@ extern "C" art::CompiledMethod* ArtQuickJniCompileMethod(art::CompilerDriver& co extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver, std::string const& filename); -CompilerDriver::CompilerDriver(VerifiedMethodsData* verified_methods_data, +CompilerDriver::CompilerDriver(VerificationResults* verification_results, DexFileToMethodInlinerMap* method_inliner_map, CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, - bool dump_stats, bool dump_passes, CumulativeLogger* timer) - : verified_methods_data_(verified_methods_data), + bool dump_stats) + : verification_results_(verification_results), method_inliner_map_(method_inliner_map), compiler_backend_(compiler_backend), instruction_set_(instruction_set), @@ -356,8 +357,6 @@ CompilerDriver::CompilerDriver(VerifiedMethodsData* verified_methods_data, start_ns_(0), stats_(new AOTCompilationStats), dump_stats_(dump_stats), - dump_passes_(dump_passes), - timings_logger_(timer), compiler_library_(NULL), compiler_(NULL), compiler_context_(NULL), @@ -441,6 +440,10 @@ CompilerDriver::~CompilerDriver() { MutexLock mu(self, compiled_methods_lock_); STLDeleteElements(&methods_to_patch_); } + { + MutexLock mu(self, compiled_methods_lock_); + STLDeleteElements(&classes_to_patch_); + } CHECK_PTHREAD_CALL(pthread_key_delete, (tls_key_), "delete tls key"); typedef void (*UninitCompilerContextFn)(CompilerDriver&); UninitCompilerContextFn uninit_compiler_context; @@ -908,6 +911,51 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id return result; } +bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx, + bool* is_type_initialized, bool* use_direct_type_ptr, + uintptr_t* direct_type_ptr) { + ScopedObjectAccess soa(Thread::Current()); + mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); + mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); + if (resolved_class == nullptr) { + return false; + } + const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot(); + if (compiling_boot) { + // boot -> boot class pointers. + // True if the class is in the image at boot compiling time. + const bool is_image_class = IsImage() && IsImageClass( + dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_)); + // True if pc relative load works. + const bool support_boot_image_fixup = GetSupportBootImageFixup(); + if (is_image_class && support_boot_image_fixup) { + *is_type_initialized = resolved_class->IsInitialized(); + *use_direct_type_ptr = false; + *direct_type_ptr = 0; + return true; + } else { + return false; + } + } else { + // True if the class is in the image at app compiling time. + const bool class_in_image = + Runtime::Current()->GetHeap()->FindSpaceFromObject(resolved_class, false)->IsImageSpace(); + if (class_in_image) { + // boot -> app class pointers. + *is_type_initialized = resolved_class->IsInitialized(); + *use_direct_type_ptr = true; + *direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_class); + return true; + } else { + // app -> app class pointers. + // Give up because app does not have an image and class + // isn't created at compile time. TODO: implement this + // if/when each app gets an image. + return false; + } + } +} + static mirror::Class* ComputeCompilingMethodsClass(ScopedObjectAccess& soa, SirtRef<mirror::DexCache>& dex_cache, const DexCompilationUnit* mUnit) @@ -960,21 +1008,8 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi ComputeCompilingMethodsClass(soa, dex_cache, mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); - bool access_ok = referrer_class->CanAccess(fields_class) && - referrer_class->CanAccessMember(fields_class, - resolved_field->GetAccessFlags()); - if (!access_ok) { - // The referring class can't access the resolved field, this may occur as a result of a - // protected field being made public by a sub-class. Resort to the dex file to determine - // the correct class for the access check. - const DexFile& dex_file = *referrer_class->GetDexCache()->GetDexFile(); - mirror::Class* dex_fields_class = mUnit->GetClassLinker()->ResolveType(dex_file, - dex_file.GetFieldId(field_idx).class_idx_, - referrer_class); - access_ok = referrer_class->CanAccess(dex_fields_class) && - referrer_class->CanAccessMember(dex_fields_class, - resolved_field->GetAccessFlags()); - } + bool access_ok = + referrer_class->CanAccessResolvedField<false>(fields_class, resolved_field, field_idx); bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() && fields_class != referrer_class; if (access_ok && !is_write_to_final_from_wrong_class) { @@ -1020,23 +1055,8 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila stats_->ResolvedLocalStaticField(); return true; // fast path } else { - bool access_ok = referrer_class->CanAccess(fields_class) && - referrer_class->CanAccessMember(fields_class, - resolved_field->GetAccessFlags()); - if (!access_ok) { - // The referring class can't access the resolved field, this may occur as a result of a - // protected field being made public by a sub-class. Resort to the dex file to determine - // the correct class for the access check. Don't change the field's class as that is - // used to identify the SSB. - const DexFile& dex_file = *referrer_class->GetDexCache()->GetDexFile(); - mirror::Class* dex_fields_class = - mUnit->GetClassLinker()->ResolveType(dex_file, - dex_file.GetFieldId(field_idx).class_idx_, - referrer_class); - access_ok = referrer_class->CanAccess(dex_fields_class) && - referrer_class->CanAccessMember(dex_fields_class, - resolved_field->GetAccessFlags()); - } + bool access_ok = + referrer_class->CanAccessResolvedField<false>(fields_class, resolved_field, field_idx); bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal(); if (access_ok && !is_write_to_final_from_wrong_class) { // We have the resolved field, we must make it into a index for the referrer @@ -1219,20 +1239,8 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui bool icce = resolved_method->CheckIncompatibleClassChange(*invoke_type); if (referrer_class != NULL && !icce) { mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - if (!referrer_class->CanAccess(methods_class) || - !referrer_class->CanAccessMember(methods_class, - resolved_method->GetAccessFlags())) { - // The referring class can't access the resolved method, this may occur as a result of a - // protected method being made public by implementing an interface that re-declares the - // method public. Resort to the dex file to determine the correct class for the access - // check. - uint16_t class_idx = - target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_; - methods_class = mUnit->GetClassLinker()->ResolveType(*target_method->dex_file, - class_idx, referrer_class); - } - if (referrer_class->CanAccess(methods_class) && - referrer_class->CanAccessMember(methods_class, resolved_method->GetAccessFlags())) { + if (referrer_class->CanAccessResolvedMethod<false>(methods_class, resolved_method, + target_method->dex_method_index)) { const bool enableFinalBasedSharpening = enable_devirtualization; // Sharpen a virtual call into a direct call when the target is known not to have been // overridden (ie is final). @@ -1265,9 +1273,9 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui if (enableVerifierBasedSharpening && (*invoke_type == kVirtual || *invoke_type == kInterface)) { // Did the verifier record a more precise invoke target based on its type information? - const MethodReference caller_method(mUnit->GetDexFile(), mUnit->GetDexMethodIndex()); + DCHECK(mUnit->GetVerifiedMethod() != nullptr); const MethodReference* devirt_map_target = - verified_methods_data_->GetDevirtMap(caller_method, dex_pc); + mUnit->GetVerifiedMethod()->GetDevirtTarget(dex_pc); if (devirt_map_target != NULL) { SirtRef<mirror::DexCache> target_dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file)); SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); @@ -1314,8 +1322,15 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui return false; // Incomplete knowledge needs slow path. } -bool CompilerDriver::IsSafeCast(const MethodReference& mr, uint32_t dex_pc) { - bool result = verified_methods_data_->IsSafeCast(mr, dex_pc); +const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file, + uint32_t method_idx) const { + MethodReference ref(dex_file, method_idx); + return verification_results_->GetVerifiedMethod(ref); +} + +bool CompilerDriver::IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc) { + DCHECK(mUnit->GetVerifiedMethod() != nullptr); + bool result = mUnit->GetVerifiedMethod()->IsSafeCast(dex_pc); if (result) { stats_->SafeCast(); } else { @@ -1333,13 +1348,13 @@ void CompilerDriver::AddCodePatch(const DexFile* dex_file, InvokeType target_invoke_type, size_t literal_offset) { MutexLock mu(Thread::Current(), compiled_methods_lock_); - code_to_patch_.push_back(new PatchInformation(dex_file, - referrer_class_def_idx, - referrer_method_idx, - referrer_invoke_type, - target_method_idx, - target_invoke_type, - literal_offset)); + code_to_patch_.push_back(new CallPatchInformation(dex_file, + referrer_class_def_idx, + referrer_method_idx, + referrer_invoke_type, + target_method_idx, + target_invoke_type, + literal_offset)); } void CompilerDriver::AddMethodPatch(const DexFile* dex_file, uint16_t referrer_class_def_idx, @@ -1349,13 +1364,25 @@ void CompilerDriver::AddMethodPatch(const DexFile* dex_file, InvokeType target_invoke_type, size_t literal_offset) { MutexLock mu(Thread::Current(), compiled_methods_lock_); - methods_to_patch_.push_back(new PatchInformation(dex_file, - referrer_class_def_idx, - referrer_method_idx, - referrer_invoke_type, - target_method_idx, - target_invoke_type, - literal_offset)); + methods_to_patch_.push_back(new CallPatchInformation(dex_file, + referrer_class_def_idx, + referrer_method_idx, + referrer_invoke_type, + target_method_idx, + target_invoke_type, + literal_offset)); +} +void CompilerDriver::AddClassPatch(const DexFile* dex_file, + uint16_t referrer_class_def_idx, + uint32_t referrer_method_idx, + uint32_t target_type_idx, + size_t literal_offset) { + MutexLock mu(Thread::Current(), compiled_methods_lock_); + classes_to_patch_.push_back(new TypePatchInformation(dex_file, + referrer_class_def_idx, + referrer_method_idx, + target_type_idx, + literal_offset)); } class ParallelCompilationManager { @@ -2249,7 +2276,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz } ClassReference ref(&dex_file, class_def_index); // Skip compiling classes with generic verifier failures since they will still fail at runtime - if (manager->GetCompiler()->verified_methods_data_->IsClassRejected(ref)) { + if (manager->GetCompiler()->verification_results_->IsClassRejected(ref)) { return; } const byte* class_data = dex_file.GetClassData(class_def); @@ -2332,7 +2359,7 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t } else if ((access_flags & kAccAbstract) != 0) { } else { MethodReference method_ref(&dex_file, method_idx); - bool compile = VerifiedMethodsData::IsCandidateForCompilation(method_ref, access_flags); + bool compile = VerificationResults::IsCandidateForCompilation(method_ref, access_flags); if (compile) { CompilerFn compiler = compiler_; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index aabdf2f9f6..a8110e71d7 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -22,7 +22,6 @@ #include <vector> #include "base/mutex.h" -#include "base/timing_logger.h" #include "class_reference.h" #include "compiled_class.h" #include "compiled_method.h" @@ -45,7 +44,8 @@ class DexCompilationUnit; class DexFileToMethodInlinerMap; class OatWriter; class TimingLogger; -class VerifiedMethodsData; +class VerificationResults; +class VerifiedMethod; enum CompilerBackend { kQuick, @@ -93,13 +93,12 @@ class CompilerDriver { // enabled. "image_classes" lets the compiler know what classes it // can assume will be in the image, with NULL implying all available // classes. - explicit CompilerDriver(VerifiedMethodsData* verified_methods_data, + explicit CompilerDriver(VerificationResults* verification_results, DexFileToMethodInlinerMap* method_inliner_map, CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, - size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer); + size_t thread_count, bool dump_stats); ~CompilerDriver(); @@ -111,8 +110,8 @@ class CompilerDriver { void CompileOne(const mirror::ArtMethod* method, TimingLogger& timings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - VerifiedMethodsData* GetVerifiedMethodsData() const { - return verified_methods_data_; + VerificationResults* GetVerificationResults() const { + return verification_results_; } DexFileToMethodInlinerMap* GetMethodInlinerMap() const { @@ -191,6 +190,10 @@ class CompilerDriver { uint32_t type_idx) LOCKS_EXCLUDED(Locks::mutator_lock_); + bool CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx, + bool* is_type_initialized, bool* use_direct_type_ptr, + uintptr_t* direct_type_ptr); + // Can we fast path instance field access? Computes field's offset and volatility. bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, int* field_offset, bool* is_volatile) @@ -211,7 +214,8 @@ class CompilerDriver { uintptr_t* direct_code, uintptr_t* direct_method) LOCKS_EXCLUDED(Locks::mutator_lock_); - bool IsSafeCast(const MethodReference& mr, uint32_t dex_pc); + const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const; + bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc); // Record patch information for later fix up. void AddCodePatch(const DexFile* dex_file, @@ -230,6 +234,12 @@ class CompilerDriver { InvokeType target_invoke_type, size_t literal_offset) LOCKS_EXCLUDED(compiled_methods_lock_); + void AddClassPatch(const DexFile* dex_file, + uint16_t referrer_class_def_idx, + uint32_t referrer_method_idx, + uint32_t target_method_idx, + size_t literal_offset) + LOCKS_EXCLUDED(compiled_methods_lock_); void SetBitcodeFileName(std::string const& filename); @@ -269,14 +279,8 @@ class CompilerDriver { return thread_count_; } - bool GetDumpPasses() const { - return dump_passes_; - } - - CumulativeLogger& GetTimingsLogger() const { - return *timings_logger_; - } - + class CallPatchInformation; + class TypePatchInformation; class PatchInformation { public: const DexFile& GetDexFile() const { @@ -288,55 +292,127 @@ class CompilerDriver { uint32_t GetReferrerMethodIdx() const { return referrer_method_idx_; } - InvokeType GetReferrerInvokeType() const { - return referrer_invoke_type_; + size_t GetLiteralOffset() const { + return literal_offset_; } - uint32_t GetTargetMethodIdx() const { - return target_method_idx_; + + virtual bool IsCall() const { + return false; } - InvokeType GetTargetInvokeType() const { - return target_invoke_type_; + virtual bool IsType() const { + return false; } - size_t GetLiteralOffset() const {; - return literal_offset_; + virtual const CallPatchInformation* AsCall() const { + LOG(FATAL) << "Unreachable"; + return nullptr; + } + virtual const TypePatchInformation* AsType() const { + LOG(FATAL) << "Unreachable"; + return nullptr; } - private: + protected: PatchInformation(const DexFile* dex_file, uint16_t referrer_class_def_idx, uint32_t referrer_method_idx, - InvokeType referrer_invoke_type, - uint32_t target_method_idx, - InvokeType target_invoke_type, size_t literal_offset) : dex_file_(dex_file), referrer_class_def_idx_(referrer_class_def_idx), referrer_method_idx_(referrer_method_idx), - referrer_invoke_type_(referrer_invoke_type), - target_method_idx_(target_method_idx), - target_invoke_type_(target_invoke_type), literal_offset_(literal_offset) { CHECK(dex_file_ != NULL); } + virtual ~PatchInformation() {} const DexFile* const dex_file_; const uint16_t referrer_class_def_idx_; const uint32_t referrer_method_idx_; + const size_t literal_offset_; + + friend class CompilerDriver; + }; + + class CallPatchInformation : public PatchInformation { + public: + InvokeType GetReferrerInvokeType() const { + return referrer_invoke_type_; + } + uint32_t GetTargetMethodIdx() const { + return target_method_idx_; + } + InvokeType GetTargetInvokeType() const { + return target_invoke_type_; + } + + const CallPatchInformation* AsCall() const { + return this; + } + bool IsCall() const { + return true; + } + + private: + CallPatchInformation(const DexFile* dex_file, + uint16_t referrer_class_def_idx, + uint32_t referrer_method_idx, + InvokeType referrer_invoke_type, + uint32_t target_method_idx, + InvokeType target_invoke_type, + size_t literal_offset) + : PatchInformation(dex_file, referrer_class_def_idx, + referrer_method_idx, literal_offset), + referrer_invoke_type_(referrer_invoke_type), + target_method_idx_(target_method_idx), + target_invoke_type_(target_invoke_type) { + } + const InvokeType referrer_invoke_type_; const uint32_t target_method_idx_; const InvokeType target_invoke_type_; - const size_t literal_offset_; friend class CompilerDriver; - DISALLOW_COPY_AND_ASSIGN(PatchInformation); + DISALLOW_COPY_AND_ASSIGN(CallPatchInformation); }; - const std::vector<const PatchInformation*>& GetCodeToPatch() const { + class TypePatchInformation : public PatchInformation { + public: + uint32_t GetTargetTypeIdx() const { + return target_type_idx_; + } + + bool IsType() const { + return true; + } + const TypePatchInformation* AsType() const { + return this; + } + + private: + TypePatchInformation(const DexFile* dex_file, + uint16_t referrer_class_def_idx, + uint32_t referrer_method_idx, + uint32_t target_type_idx, + size_t literal_offset) + : PatchInformation(dex_file, referrer_class_def_idx, + referrer_method_idx, literal_offset), + target_type_idx_(target_type_idx) { + } + + const uint32_t target_type_idx_; + + friend class CompilerDriver; + DISALLOW_COPY_AND_ASSIGN(TypePatchInformation); + }; + + const std::vector<const CallPatchInformation*>& GetCodeToPatch() const { return code_to_patch_; } - const std::vector<const PatchInformation*>& GetMethodsToPatch() const { + const std::vector<const CallPatchInformation*>& GetMethodsToPatch() const { return methods_to_patch_; } + const std::vector<const TypePatchInformation*>& GetClassesToPatch() const { + return classes_to_patch_; + } // Checks if class specified by type_idx is one of the image_classes_ bool IsImageClass(const char* descriptor) const; @@ -408,10 +484,11 @@ class CompilerDriver { static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index) LOCKS_EXCLUDED(Locks::mutator_lock_); - std::vector<const PatchInformation*> code_to_patch_; - std::vector<const PatchInformation*> methods_to_patch_; + std::vector<const CallPatchInformation*> code_to_patch_; + std::vector<const CallPatchInformation*> methods_to_patch_; + std::vector<const TypePatchInformation*> classes_to_patch_; - VerifiedMethodsData* verified_methods_data_; + VerificationResults* verification_results_; DexFileToMethodInlinerMap* method_inliner_map_; CompilerBackend compiler_backend_; @@ -446,9 +523,6 @@ class CompilerDriver { UniquePtr<AOTCompilationStats> stats_; bool dump_stats_; - const bool dump_passes_; - - CumulativeLogger* const timings_logger_; typedef void (*CompilerCallbackFn)(CompilerDriver& driver); typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver); diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc index c441d09ab2..840b0adf49 100644 --- a/compiler/driver/dex_compilation_unit.cc +++ b/compiler/driver/dex_compilation_unit.cc @@ -31,7 +31,8 @@ DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu) code_item_(cu->code_item), class_def_idx_(cu->class_def_idx), dex_method_idx_(cu->method_idx), - access_flags_(cu->access_flags) { + access_flags_(cu->access_flags), + verified_method_(cu_->compiler_driver->GetVerifiedMethod(cu->dex_file, cu->method_idx)) { } DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu, @@ -41,7 +42,8 @@ DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu, const DexFile::CodeItem* code_item, uint16_t class_def_idx, uint32_t method_idx, - uint32_t access_flags) + uint32_t access_flags, + const VerifiedMethod* verified_method) : cu_(cu), class_loader_(class_loader), class_linker_(class_linker), @@ -49,7 +51,8 @@ DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu, code_item_(code_item), class_def_idx_(class_def_idx), dex_method_idx_(method_idx), - access_flags_(access_flags) { + access_flags_(access_flags), + verified_method_(verified_method) { } const std::string& DexCompilationUnit::GetSymbol() { diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 3df50ffec6..84f57991c3 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -29,6 +29,7 @@ class DexCache; } // namespace mirror class ClassLinker; struct CompilationUnit; +class VerifiedMethod; class DexCompilationUnit { public: @@ -36,7 +37,8 @@ class DexCompilationUnit { DexCompilationUnit(CompilationUnit* cu, jobject class_loader, ClassLinker* class_linker, const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint16_t class_def_idx, uint32_t method_idx, uint32_t access_flags); + uint16_t class_def_idx, uint32_t method_idx, uint32_t access_flags, + const VerifiedMethod* verified_method); CompilationUnit* GetCompilationUnit() const { return cu_; @@ -96,6 +98,10 @@ class DexCompilationUnit { return ((access_flags_ & kAccSynchronized) != 0); } + const VerifiedMethod* GetVerifiedMethod() const { + return verified_method_; + } + const std::string& GetSymbol(); private: @@ -111,6 +117,7 @@ class DexCompilationUnit { const uint16_t class_def_idx_; const uint32_t dex_method_idx_; const uint32_t access_flags_; + const VerifiedMethod* const verified_method_; std::string symbol_; }; diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h index 0ef4185431..dbc986a6e2 100644 --- a/compiler/elf_writer.h +++ b/compiler/elf_writer.h @@ -44,7 +44,7 @@ class ElfWriter { size_t& oat_data_offset); // Returns runtime oat_data runtime address for an opened ElfFile. - static llvm::ELF::Elf32_Addr GetOatDataAddress(ElfFile* elf_file); + static ::llvm::ELF::Elf32_Addr GetOatDataAddress(ElfFile* elf_file); protected: ElfWriter(const CompilerDriver& driver, File* elf_file); diff --git a/compiler/elf_writer_mclinker.h b/compiler/elf_writer_mclinker.h index fa7e9caa19..8ee7231f79 100644 --- a/compiler/elf_writer_mclinker.h +++ b/compiler/elf_writer_mclinker.h @@ -69,7 +69,7 @@ class ElfWriterMclinker : public ElfWriter { void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); uint32_t FixupCompiledCodeOffset(ElfFile& elf_file, - llvm::ELF::Elf32_Addr oatdata_address, + ::llvm::ELF::Elf32_Addr oatdata_address, const CompiledCode& compiled_code); #endif diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 556dec25ad..09bb70cd2f 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -734,7 +734,7 @@ void ImageWriter::FixupFields(const Object* orig, } } -static ArtMethod* GetTargetMethod(const CompilerDriver::PatchInformation* patch) +static ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); @@ -757,15 +757,34 @@ static ArtMethod* GetTargetMethod(const CompilerDriver::PatchInformation* patch) return method; } +static Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, class_linker->FindDexCache(patch->GetDexFile())); + SirtRef<mirror::ClassLoader> class_loader(self, nullptr); + Class* klass = class_linker->ResolveType(patch->GetDexFile(), + patch->GetTargetTypeIdx(), + dex_cache, + class_loader); + CHECK(klass != NULL) + << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx(); + CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass) + << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " " + << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " " + << PrettyClass(klass); + return klass; +} + void ImageWriter::PatchOatCodeAndMethods() { Thread* self = Thread::Current(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); - typedef std::vector<const CompilerDriver::PatchInformation*> Patches; - const Patches& code_to_patch = compiler_driver_.GetCodeToPatch(); + typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches; + const CallPatches& code_to_patch = compiler_driver_.GetCodeToPatch(); for (size_t i = 0; i < code_to_patch.size(); i++) { - const CompilerDriver::PatchInformation* patch = code_to_patch[i]; + const CompilerDriver::CallPatchInformation* patch = code_to_patch[i]; ArtMethod* target = GetTargetMethod(patch); uint32_t code = reinterpret_cast<uint32_t>(class_linker->GetOatCodeFor(target)); uint32_t code_base = reinterpret_cast<uint32_t>(&oat_file_->GetOatHeader()); @@ -773,13 +792,21 @@ void ImageWriter::PatchOatCodeAndMethods() { SetPatchLocation(patch, reinterpret_cast<uint32_t>(GetOatAddress(code_offset))); } - const Patches& methods_to_patch = compiler_driver_.GetMethodsToPatch(); + const CallPatches& methods_to_patch = compiler_driver_.GetMethodsToPatch(); for (size_t i = 0; i < methods_to_patch.size(); i++) { - const CompilerDriver::PatchInformation* patch = methods_to_patch[i]; + const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i]; ArtMethod* target = GetTargetMethod(patch); SetPatchLocation(patch, reinterpret_cast<uint32_t>(GetImageAddress(target))); } + const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch = + compiler_driver_.GetClassesToPatch(); + for (size_t i = 0; i < classes_to_patch.size(); i++) { + const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i]; + Class* target = GetTargetType(patch); + SetPatchLocation(patch, reinterpret_cast<uint32_t>(GetImageAddress(target))); + } + // Update the image header with the new checksum after patching ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin()); image_header->SetOatChecksum(oat_file_->GetOatHeader().GetChecksum()); @@ -796,13 +823,26 @@ void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uint32_t>(oat_code) & ~0x1); uint32_t* patch_location = reinterpret_cast<uint32_t*>(base + patch->GetLiteralOffset()); if (kIsDebugBuild) { - const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); - uint32_t expected = reinterpret_cast<uint32_t>(&id); - uint32_t actual = *patch_location; - CHECK(actual == expected || actual == value) << std::hex - << "actual=" << actual - << "expected=" << expected - << "value=" << value; + if (patch->IsCall()) { + const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall(); + const DexFile::MethodId& id = cpatch->GetDexFile().GetMethodId(cpatch->GetTargetMethodIdx()); + uint32_t expected = reinterpret_cast<uint32_t>(&id); + uint32_t actual = *patch_location; + CHECK(actual == expected || actual == value) << std::hex + << "actual=" << actual + << "expected=" << expected + << "value=" << value; + } + if (patch->IsType()) { + const CompilerDriver::TypePatchInformation* tpatch = patch->AsType(); + const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx()); + uint32_t expected = reinterpret_cast<uint32_t>(&id); + uint32_t actual = *patch_location; + CHECK(actual == expected || actual == value) << std::hex + << "actual=" << actual + << "expected=" << expected + << "value=" << value; + } } *patch_location = value; oat_header.UpdateChecksum(patch_location, sizeof(value)); diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc index 35d1ecd783..94408bb39c 100644 --- a/compiler/llvm/compiler_llvm.cc +++ b/compiler/llvm/compiler_llvm.cc @@ -20,7 +20,8 @@ #include "base/stl_util.h" #include "class_linker.h" #include "compiled_method.h" -#include "dex/verified_methods_data.h" +#include "dex/verification_results.h" +#include "dex/verified_method.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" #include "globals.h" @@ -153,11 +154,9 @@ CompileDexMethod(DexCompilationUnit* dex_compilation_unit, InvokeType invoke_typ cunit->Materialize(); - MethodReference mref(dex_compilation_unit->GetDexFile(), - dex_compilation_unit->GetDexMethodIndex()); return new CompiledMethod(*compiler_driver_, compiler_driver_->GetInstructionSet(), cunit->GetElfObject(), - *compiler_driver_->GetVerifiedMethodsData()->GetDexGcMap(mref), + dex_compilation_unit->GetVerifiedMethod()->GetDexGcMap(), cunit->GetDexCompilationUnit()->GetSymbol()); } @@ -214,7 +213,7 @@ extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, art::DexCompilationUnit dex_compilation_unit( NULL, class_loader, class_linker, dex_file, code_item, - class_def_idx, method_idx, access_flags); + class_def_idx, method_idx, access_flags, driver.GetVerifiedMethod(&dex_file, method_idx)); art::llvm::CompilerLLVM* compiler_llvm = ContextOf(driver); art::CompiledMethod* result = compiler_llvm->CompileDexMethod(&dex_compilation_unit, invoke_type); return result; @@ -226,8 +225,8 @@ extern "C" art::CompiledMethod* ArtLLVMJniCompileMethod(art::CompilerDriver& dri art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker(); art::DexCompilationUnit dex_compilation_unit( - NULL, NULL, class_linker, dex_file, NULL, - 0, method_idx, access_flags); + nullptr, nullptr, class_linker, dex_file, nullptr, + 0, method_idx, access_flags, nullptr); art::llvm::CompilerLLVM* compiler_llvm = ContextOf(driver); art::CompiledMethod* result = compiler_llvm->CompileNativeMethod(&dex_compilation_unit); diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 12d821239f..fc454127c3 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -79,14 +79,13 @@ TEST_F(OatTest, WriteRead) { InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86; InstructionSetFeatures insn_features; - verified_methods_data_.reset(new VerifiedMethodsData); + verification_results_.reset(new VerificationResults); method_inliner_map_.reset(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr); - callbacks_.Reset(verified_methods_data_.get(), method_inliner_map_.get()); - CumulativeLogger timer("Compilation times"); - compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(), + callbacks_.Reset(verification_results_.get(), method_inliner_map_.get()); + compiler_driver_.reset(new CompilerDriver(verification_results_.get(), method_inliner_map_.get(), compiler_backend, insn_set, - insn_features, false, NULL, 2, true, true, &timer)); + insn_features, false, NULL, 2, true)); jobject class_loader = NULL; if (kCompile) { TimingLogger timings("OatTest::WriteRead", false, false); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 199a2b8d58..7a902d86d6 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -23,7 +23,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "dex_file-inl.h" -#include "dex/verified_methods_data.h" +#include "dex/verification_results.h" #include "gc/space/space.h" #include "mirror/art_method-inl.h" #include "mirror/array.h" @@ -218,7 +218,7 @@ size_t OatWriter::InitOatClasses(size_t offset) { mirror::Class::Status status; if (compiled_class != NULL) { status = compiled_class->GetStatus(); - } else if (compiler_driver_->GetVerifiedMethodsData()->IsClassRejected(class_ref)) { + } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { status = mirror::Class::kStatusError; } else { status = mirror::Class::kStatusNotReady; @@ -433,7 +433,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, mirror::Class::Status status; if (compiled_class != NULL) { status = compiled_class->GetStatus(); - } else if (compiler_driver_->GetVerifiedMethodsData()->IsClassRejected(class_ref)) { + } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { status = mirror::Class::kStatusError; } else { status = mirror::Class::kStatusNotReady; |