diff options
Diffstat (limited to 'compiler')
49 files changed, 3070 insertions, 1370 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 6d656e63f1..e3201e7f8b 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -78,6 +78,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/code_generator_x86.cc \ optimizing/nodes.cc \ optimizing/optimizing_compiler.cc \ + optimizing/ssa_builder.cc \ trampolines/trampoline_compiler.cc \ utils/arena_allocator.cc \ utils/arena_bit_vector.cc \ diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index fdf09a50a6..8bba84a9c1 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -132,34 +132,24 @@ static inline InstructionSetFeatures ParseFeatureList(std::string str) { class CommonCompilerTest : public CommonRuntimeTest { public: - static void MakeExecutable(const std::vector<uint8_t>& code) { - CHECK_NE(code.size(), 0U); - MakeExecutable(&code[0], code.size()); - } - // Create an OatMethod based on pointers (for unit tests). OatFile::OatMethod CreateOatMethod(const void* code, const size_t frame_size_in_bytes, const uint32_t core_spill_mask, const uint32_t fp_spill_mask, - const uint8_t* mapping_table, - const uint8_t* vmap_table, const uint8_t* gc_map) { + CHECK(code != nullptr); const byte* base; - uint32_t code_offset, mapping_table_offset, vmap_table_offset, gc_map_offset; - if (mapping_table == nullptr && vmap_table == nullptr && gc_map == nullptr) { + uint32_t code_offset, gc_map_offset; + if (gc_map == nullptr) { base = reinterpret_cast<const byte*>(code); // Base of data points at code. base -= kPointerSize; // Move backward so that code_offset != 0. code_offset = kPointerSize; - mapping_table_offset = 0; - vmap_table_offset = 0; gc_map_offset = 0; } else { // TODO: 64bit support. base = nullptr; // Base of data in oat file, ie 0. code_offset = PointerToLowMemUInt32(code); - mapping_table_offset = PointerToLowMemUInt32(mapping_table); - vmap_table_offset = PointerToLowMemUInt32(vmap_table); gc_map_offset = PointerToLowMemUInt32(gc_map); } return OatFile::OatMethod(base, @@ -167,8 +157,6 @@ class CommonCompilerTest : public CommonRuntimeTest { frame_size_in_bytes, core_spill_mask, fp_spill_mask, - mapping_table_offset, - vmap_table_offset, gc_map_offset); } @@ -185,19 +173,44 @@ class CommonCompilerTest : public CommonRuntimeTest { } if (compiled_method != nullptr) { const std::vector<uint8_t>* code = compiled_method->GetQuickCode(); - if (code == nullptr) { + const void* code_ptr; + if (code != nullptr) { + uint32_t code_size = code->size(); + CHECK_NE(0u, code_size); + const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable(); + uint32_t vmap_table_offset = vmap_table.empty() ? 0u + : sizeof(OatMethodHeader) + vmap_table.size(); + const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable(); + uint32_t mapping_table_offset = mapping_table.empty() ? 0u + : sizeof(OatMethodHeader) + vmap_table.size() + mapping_table.size(); + OatMethodHeader method_header(vmap_table_offset, mapping_table_offset, code_size); + + header_code_and_maps_chunks_.push_back(std::vector<uint8_t>()); + std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back(); + size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size(); + size_t code_offset = compiled_method->AlignCode(size - code_size); + size_t padding = code_offset - (size - code_size); + chunk->reserve(padding + size); + chunk->resize(sizeof(method_header)); + memcpy(&(*chunk)[0], &method_header, sizeof(method_header)); + chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end()); + chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end()); + chunk->insert(chunk->begin(), padding, 0); + chunk->insert(chunk->end(), code->begin(), code->end()); + CHECK_EQ(padding + size, chunk->size()); + code_ptr = &(*chunk)[code_offset]; + } else { code = compiled_method->GetPortableCode(); + code_ptr = &(*code)[0]; } - MakeExecutable(*code); - const void* method_code = CompiledMethod::CodePointer(&(*code)[0], + MakeExecutable(code_ptr, code->size()); + const void* method_code = CompiledMethod::CodePointer(code_ptr, compiled_method->GetInstructionSet()); LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code; OatFile::OatMethod oat_method = CreateOatMethod(method_code, compiled_method->GetFrameSizeInBytes(), compiled_method->GetCoreSpillMask(), compiled_method->GetFpSpillMask(), - &compiled_method->GetMappingTable()[0], - &compiled_method->GetVmapTable()[0], nullptr); oat_method.LinkMethod(method); method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); @@ -211,8 +224,6 @@ class CommonCompilerTest : public CommonRuntimeTest { kStackAlignment, 0, 0, - nullptr, - nullptr, nullptr); oat_method.LinkMethod(method); method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); @@ -230,8 +241,6 @@ class CommonCompilerTest : public CommonRuntimeTest { sirt_size, callee_save_method->GetCoreSpillMask(), callee_save_method->GetFpSpillMask(), - nullptr, - nullptr, nullptr); oat_method.LinkMethod(method); method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); @@ -436,6 +445,9 @@ class CommonCompilerTest : public CommonRuntimeTest { private: UniquePtr<MemMap> image_reservation_; + + // Chunks must not move their storage after being created - use the node-based std::list. + std::list<std::vector<uint8_t> > header_code_and_maps_chunks_; }; } // namespace art diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 8a88d618cc..ba4b5c356a 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -159,8 +159,10 @@ enum AssemblerStatus { }; enum OpSize { - kWord, - kLong, + kWord, // Natural word size of target (32/64). + k32, + k64, + kReference, // Object reference; compressed on 64-bit targets. kSingle, kDouble, kUnsignedHalf, @@ -323,8 +325,6 @@ enum X86ConditionCode { std::ostream& operator<<(std::ostream& os, const X86ConditionCode& kind); enum ThrowKind { - kThrowArrayBounds, - kThrowConstantArrayBounds, kThrowNoSuchMethod, }; diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 1bf5fce989..ed2ecace36 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -181,6 +181,17 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, (cu.enable_debug & (1 << kDebugVerbose)); } + if (gVerboseMethods.size() != 0) { + cu.verbose = false; + for (size_t i = 0; i < gVerboseMethods.size(); ++i) { + if (PrettyMethod(method_idx, dex_file).find(gVerboseMethods[i]) + != std::string::npos) { + cu.verbose = true; + break; + } + } + } + /* * TODO: rework handling of optimization and debug flags. Should we split out * MIR and backend flags? Need command-line setting as well. diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 51419f4586..937e2585ef 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -42,6 +42,11 @@ void MIRGraph::DoConstantPropagation(BasicBlock* bb) { MIR* mir; for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + // Skip pass if BB has MIR without SSA representation. + if (mir->ssa_rep == NULL) { + return; + } + uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; DecodedInstruction *d_insn = &mir->dalvikInsn; diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index d0d0e6b3a7..b374ed861e 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -128,7 +128,7 @@ void ArmMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, // Load the displacement from the switch table RegStorage disp_reg = AllocTemp(); - LoadBaseIndexed(table_base, keyReg, disp_reg, 2, kWord); + LoadBaseIndexed(table_base, keyReg, disp_reg, 2, k32); // ..and go! NOTE: No instruction set switch here - must stay Thumb2 LIR* switch_branch = NewLIR1(kThumb2AddPCR, disp_reg.GetReg()); @@ -180,6 +180,7 @@ void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { */ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { FlushAllRegs(); + // FIXME: need separate LoadValues for object references. LoadValueDirectFixed(rl_src, rs_r0); // Get obj LockCallTemps(); // Prepare for explicit register usage constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15. @@ -193,7 +194,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL); } } - LoadWordDisp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2); + Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2); NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2); MarkPossibleNullPointerException(opt_flags); LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_r1, 0, NULL); @@ -219,7 +220,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { } else { // Explicit null-check as slow-path is entered using an IT. GenNullCheck(rs_r0, opt_flags); - LoadWordDisp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2); + Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2); NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2); MarkPossibleNullPointerException(opt_flags); OpRegImm(kOpCmp, rs_r1, 0); @@ -248,7 +249,7 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { LoadValueDirectFixed(rl_src, rs_r0); // Get obj LockCallTemps(); // Prepare for explicit register usage LIR* null_check_branch = nullptr; - LoadWordDisp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2); + Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2); constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15. if (kArchVariantHasGoodBranchPredictor) { if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) { @@ -259,11 +260,11 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL); } } - LoadWordDisp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); + Load32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); MarkPossibleNullPointerException(opt_flags); LoadConstantNoClobber(rs_r3, 0); LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_r1, rs_r2, NULL); - StoreWordDisp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3); + Store32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3); LIR* unlock_success_branch = OpUnconditionalBranch(NULL); LIR* slow_path_target = NewLIR0(kPseudoTargetLabel); @@ -284,14 +285,14 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { } else { // Explicit null-check as slow-path is entered using an IT. GenNullCheck(rs_r0, opt_flags); - LoadWordDisp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); // Get lock + Load32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); // Get lock MarkPossibleNullPointerException(opt_flags); - LoadWordDisp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2); + Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2); LoadConstantNoClobber(rs_r3, 0); // Is lock unheld on lock or held by us (==thread_id) on unlock? OpRegReg(kOpCmp, rs_r1, rs_r2); LIR* it = OpIT(kCondEq, "EE"); - StoreWordDisp/*eq*/(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3); + Store32Disp/*eq*/(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3); // Go expensive route - UnlockObjectFromCode(obj); LoadWordDisp/*ne*/(rs_rARM_SELF, QUICK_ENTRYPOINT_OFFSET(4, pUnlockObject).Int32Value(), rs_rARM_LR); @@ -307,9 +308,9 @@ void ArmMir2Lir::GenMoveException(RegLocation rl_dest) { int ex_offset = Thread::ExceptionOffset<4>().Int32Value(); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage reset_reg = AllocTemp(); - LoadWordDisp(rs_rARM_SELF, ex_offset, rl_result.reg); + Load32Disp(rs_rARM_SELF, ex_offset, rl_result.reg); LoadConstant(reset_reg, 0); - StoreWordDisp(rs_rARM_SELF, ex_offset, reset_reg); + Store32Disp(rs_rARM_SELF, ex_offset, reset_reg); FreeTemp(reset_reg); StoreValue(rl_dest, rl_result); } @@ -354,7 +355,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { if (!skip_overflow_check) { if (Runtime::Current()->ExplicitStackOverflowChecks()) { /* Load stack limit */ - LoadWordDisp(rs_rARM_SELF, Thread::StackEndOffset<4>().Int32Value(), rs_r12); + Load32Disp(rs_rARM_SELF, Thread::StackEndOffset<4>().Int32Value(), rs_r12); } } /* Spill core callee saves */ @@ -391,6 +392,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { ThreadOffset<4> func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowStackOverflow); // Load the entrypoint directly into the pc instead of doing a load + branch. Assumes // codegen and target are in thumb2 mode. + // NOTE: native pointer. m2l_->LoadWordDisp(rs_rARM_SELF, func_offset.Int32Value(), rs_rARM_PC); } @@ -421,7 +423,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { // a sub instruction. Otherwise we will get a temp allocation and the // code size will increase. OpRegRegImm(kOpSub, rs_r12, rs_rARM_SP, Thread::kStackOverflowReservedBytes); - LoadWordDisp(rs_r12, 0, rs_r12); + Load32Disp(rs_r12, 0, rs_r12); MarkPossibleStackOverflowException(); OpRegImm(kOpSub, rs_rARM_SP, frame_size_without_spills); } diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index b0bc11d458..646859c03b 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -126,8 +126,6 @@ class ArmMir2Lir FINAL : public Mir2Lir { RegLocation rl_src2); void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - LIR* GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, int offset, - ThrowKind kind); RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); @@ -162,7 +160,7 @@ class ArmMir2Lir FINAL : public Mir2Lir { LIR* OpMem(OpKind op, RegStorage r_base, int disp); LIR* OpPcRelLoad(RegStorage reg, LIR* target); LIR* OpReg(OpKind op, RegStorage r_dest_src); - LIR* OpRegCopy(RegStorage r_dest, RegStorage r_src); + void OpRegCopy(RegStorage r_dest, RegStorage r_src); LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src); LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value); LIR* OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset); diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index c876b3ac69..a2d6373622 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -314,11 +314,11 @@ LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_va /* * A common use of OpCmpImmBranch is for null checks, and using the Thumb 16-bit * compare-and-branch if zero is ideal if it will reach. However, because null checks - * branch forward to a launch pad, they will frequently not reach - and thus have to + * branch forward to a slow path, they will frequently not reach - and thus have to * be converted to a long form during assembly (which will trigger another assembly * pass). Here we estimate the branch distance for checks, and if large directly * generate the long form in an attempt to avoid an extra assembly pass. - * TODO: consider interspersing launchpads in code following unconditional branches. + * TODO: consider interspersing slowpaths in code following unconditional branches. */ bool skip = ((target != NULL) && (target->opcode == kPseudoThrowTarget)); skip &= ((cu_->code_item->insns_size_in_code_units_ - current_dalvik_offset_) > 64); @@ -361,37 +361,40 @@ LIR* ArmMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) { return res; } -LIR* ArmMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { - LIR* res = OpRegCopyNoInsert(r_dest, r_src); - AppendLIR(res); - return res; +void ArmMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { + if (r_dest != r_src) { + LIR* res = OpRegCopyNoInsert(r_dest, r_src); + AppendLIR(res); + } } void ArmMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { - bool dest_fp = ARM_FPREG(r_dest.GetLowReg()); - bool src_fp = ARM_FPREG(r_src.GetLowReg()); - if (dest_fp) { - if (src_fp) { - // FIXME: handle 64-bit solo's here. - OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())), - RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg()))); - } else { - NewLIR3(kThumb2Fmdrr, S2d(r_dest.GetLowReg(), r_dest.GetHighReg()), - r_src.GetLowReg(), r_src.GetHighReg()); - } - } else { - if (src_fp) { - NewLIR3(kThumb2Fmrrd, r_dest.GetLowReg(), r_dest.GetHighReg(), S2d(r_src.GetLowReg(), - r_src.GetHighReg())); + if (r_dest != r_src) { + bool dest_fp = ARM_FPREG(r_dest.GetLowReg()); + bool src_fp = ARM_FPREG(r_src.GetLowReg()); + if (dest_fp) { + if (src_fp) { + // FIXME: handle 64-bit solo's here. + OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())), + RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg()))); + } else { + NewLIR3(kThumb2Fmdrr, S2d(r_dest.GetLowReg(), r_dest.GetHighReg()), + r_src.GetLowReg(), r_src.GetHighReg()); + } } else { - // Handle overlap - if (r_src.GetHighReg() == r_dest.GetLowReg()) { - DCHECK_NE(r_src.GetLowReg(), r_dest.GetHighReg()); - OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); - OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + if (src_fp) { + NewLIR3(kThumb2Fmrrd, r_dest.GetLowReg(), r_dest.GetHighReg(), S2d(r_src.GetLowReg(), + r_src.GetHighReg())); } else { - OpRegCopy(r_dest.GetLow(), r_src.GetLow()); - OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + // Handle overlap + if (r_src.GetHighReg() == r_dest.GetLowReg()) { + DCHECK_NE(r_src.GetLowReg(), r_dest.GetHighReg()); + OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + } else { + OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + } } } } @@ -608,12 +611,6 @@ bool ArmMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) return true; } -LIR* ArmMir2Lir::GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, - int offset, ThrowKind kind) { - LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm"; - 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"; @@ -684,18 +681,18 @@ bool ArmMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { RegLocation rl_dest = InlineTarget(info); RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - if (size == kLong) { + if (size == k64) { // Fake unaligned LDRD by two unaligned LDR instructions on ARMv7 with SCTLR.A set to 0. if (rl_address.reg.GetReg() != rl_result.reg.GetLowReg()) { - LoadWordDisp(rl_address.reg, 0, rl_result.reg.GetLow()); - LoadWordDisp(rl_address.reg, 4, rl_result.reg.GetHigh()); + Load32Disp(rl_address.reg, 0, rl_result.reg.GetLow()); + Load32Disp(rl_address.reg, 4, rl_result.reg.GetHigh()); } else { - LoadWordDisp(rl_address.reg, 4, rl_result.reg.GetHigh()); - LoadWordDisp(rl_address.reg, 0, rl_result.reg.GetLow()); + Load32Disp(rl_address.reg, 4, rl_result.reg.GetHigh()); + Load32Disp(rl_address.reg, 0, rl_result.reg.GetLow()); } StoreValueWide(rl_dest, rl_result); } else { - DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + DCHECK(size == kSignedByte || size == kSignedHalf || size == k32); // Unaligned load with LDR and LDRSH is allowed on ARMv7 with SCTLR.A set to 0. LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, INVALID_SREG); StoreValue(rl_dest, rl_result); @@ -708,13 +705,13 @@ bool ArmMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1] RegLocation rl_src_value = info->args[2]; // [size] value RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); - if (size == kLong) { + if (size == k64) { // Fake unaligned STRD by two unaligned STR instructions on ARMv7 with SCTLR.A set to 0. RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg); - StoreBaseDisp(rl_address.reg, 0, rl_value.reg.GetLow(), kWord); - StoreBaseDisp(rl_address.reg, 4, rl_value.reg.GetHigh(), kWord); + StoreBaseDisp(rl_address.reg, 0, rl_value.reg.GetLow(), k32); + StoreBaseDisp(rl_address.reg, 4, rl_value.reg.GetHigh(), k32); } else { - DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + DCHECK(size == kSignedByte || size == kSignedHalf || size == k32); // Unaligned store with STR and STRSH is allowed on ARMv7 with SCTLR.A set to 0. RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size); @@ -1148,7 +1145,7 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, if (needs_range_check) { reg_len = AllocTemp(); /* Get len */ - LoadWordDisp(rl_array.reg, len_offset, reg_len); + Load32Disp(rl_array.reg, len_offset, reg_len); MarkPossibleNullPointerException(opt_flags); } else { ForceImplicitNullCheck(rl_array.reg, opt_flags); @@ -1167,9 +1164,9 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, if (needs_range_check) { if (constant_index) { - GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds); + GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len); } else { - GenRegRegCheck(kCondLs, reg_len, rl_index.reg, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); } FreeTemp(reg_len); } @@ -1196,7 +1193,7 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); @@ -1217,7 +1214,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, bool constant_index = rl_index.is_const; int data_offset; - if (size == kLong || size == kDouble) { + if (size == k64 || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); @@ -1254,7 +1251,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, reg_len = AllocTemp(); // NOTE: max live temps(4) here. /* Get len */ - LoadWordDisp(rl_array.reg, len_offset, reg_len); + Load32Disp(rl_array.reg, len_offset, reg_len); MarkPossibleNullPointerException(opt_flags); } else { ForceImplicitNullCheck(rl_array.reg, opt_flags); @@ -1271,9 +1268,9 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } if (needs_range_check) { if (constant_index) { - GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds); + GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len); } else { - GenRegRegCheck(kCondLs, reg_len, rl_index.reg, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); } FreeTemp(reg_len); } @@ -1289,7 +1286,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size); diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 1053a8fc41..305e89ba92 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -656,7 +656,7 @@ void ArmMir2Lir::FlushReg(RegStorage reg) { if (info->live && info->dirty) { info->dirty = false; int v_reg = mir_graph_->SRegToVReg(info->s_reg); - StoreBaseDisp(rs_rARM_SP, VRegOffset(v_reg), reg, kWord); + StoreBaseDisp(rs_rARM_SP, VRegOffset(v_reg), reg, k32); } } @@ -738,8 +738,8 @@ RegStorage ArmMir2Lir::LoadHelper(ThreadOffset<4> offset) { LIR* ArmMir2Lir::CheckSuspendUsingLoad() { RegStorage tmp = rs_r0; - LoadWordDisp(rs_rARM_SELF, Thread::ThreadSuspendTriggerOffset<4>().Int32Value(), tmp); - LIR* load2 = LoadWordDisp(tmp, 0, tmp); + Load32Disp(rs_rARM_SELF, Thread::ThreadSuspendTriggerOffset<4>().Int32Value(), tmp); + LIR* load2 = Load32Disp(tmp, 0, tmp); return load2; } diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index 70cbdd2e31..2e64f74235 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -699,23 +699,24 @@ LIR* ArmMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStora if (ARM_FPREG(r_dest.GetReg())) { if (ARM_SINGLEREG(r_dest.GetReg())) { - DCHECK((size == kWord) || (size == kSingle)); + DCHECK((size == k32) || (size == kSingle) || (size == kReference)); opcode = kThumb2Vldrs; size = kSingle; } else { DCHECK(ARM_DOUBLEREG(r_dest.GetReg())); - DCHECK((size == kLong) || (size == kDouble)); + DCHECK((size == k64) || (size == kDouble)); DCHECK_EQ((r_dest.GetReg() & 0x1), 0); opcode = kThumb2Vldrd; size = kDouble; } } else { if (size == kSingle) - size = kWord; + size = k32; } switch (size) { case kDouble: // fall-through + // Intentional fall-though. case kSingle: reg_ptr = AllocTemp(); if (scale) { @@ -727,7 +728,9 @@ LIR* ArmMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStora load = NewLIR3(opcode, r_dest.GetReg(), reg_ptr.GetReg(), 0); FreeTemp(reg_ptr); return load; - case kWord: + case k32: + // Intentional fall-though. + case kReference: opcode = (thumb_form) ? kThumbLdrRRR : kThumb2LdrRRR; break; case kUnsignedHalf: @@ -764,23 +767,24 @@ LIR* ArmMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor if (ARM_FPREG(r_src.GetReg())) { if (ARM_SINGLEREG(r_src.GetReg())) { - DCHECK((size == kWord) || (size == kSingle)); + DCHECK((size == k32) || (size == kSingle) || (size == kReference)); opcode = kThumb2Vstrs; size = kSingle; } else { DCHECK(ARM_DOUBLEREG(r_src.GetReg())); - DCHECK((size == kLong) || (size == kDouble)); + DCHECK((size == k64) || (size == kDouble)); DCHECK_EQ((r_src.GetReg() & 0x1), 0); opcode = kThumb2Vstrd; size = kDouble; } } else { if (size == kSingle) - size = kWord; + size = k32; } switch (size) { case kDouble: // fall-through + // Intentional fall-though. case kSingle: reg_ptr = AllocTemp(); if (scale) { @@ -792,14 +796,18 @@ LIR* ArmMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor store = NewLIR3(opcode, r_src.GetReg(), reg_ptr.GetReg(), 0); FreeTemp(reg_ptr); return store; - case kWord: + case k32: + // Intentional fall-though. + case kReference: opcode = (thumb_form) ? kThumbStrRRR : kThumb2StrRRR; break; case kUnsignedHalf: + // Intentional fall-though. case kSignedHalf: opcode = (thumb_form) ? kThumbStrhRRR : kThumb2StrhRRR; break; case kUnsignedByte: + // Intentional fall-though. case kSignedByte: opcode = (thumb_form) ? kThumbStrbRRR : kThumb2StrbRRR; break; @@ -832,7 +840,8 @@ LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorag bool null_pointer_safepoint = false; switch (size) { case kDouble: - case kLong: + // Intentional fall-though. + case k64: if (ARM_FPREG(dest_low_reg)) { // Note: following change to avoid using pairs for doubles, replace conversion w/ DCHECK. if (r_dest.IsPair()) { @@ -849,15 +858,18 @@ LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorag load = NewLIR4(kThumb2LdrdI8, r_dest.GetLowReg(), r_dest.GetHighReg(), r_base.GetReg(), displacement >> 2); } else { - load = LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), kWord, s_reg); + load = LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), k32, s_reg); null_pointer_safepoint = true; - LoadBaseDispBody(r_base, displacement + 4, r_dest.GetHigh(), kWord, INVALID_SREG); + LoadBaseDispBody(r_base, displacement + 4, r_dest.GetHigh(), k32, INVALID_SREG); } already_generated = true; } break; case kSingle: - case kWord: + // Intentional fall-though. + case k32: + // Intentional fall-though. + case kReference: if (ARM_FPREG(r_dest.GetReg())) { opcode = kThumb2Vldrs; if (displacement <= 1020) { @@ -953,13 +965,17 @@ LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorag LIR* ArmMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size, int s_reg) { - DCHECK(!((size == kLong) || (size == kDouble))); + DCHECK(!((size == k64) || (size == kDouble))); + // TODO: base this on target. + if (size == kWord) { + size = k32; + } return LoadBaseDispBody(r_base, displacement, r_dest, size, s_reg); } LIR* ArmMir2Lir::LoadBaseDispWide(RegStorage r_base, int displacement, RegStorage r_dest, int s_reg) { - return LoadBaseDispBody(r_base, displacement, r_dest, kLong, s_reg); + return LoadBaseDispBody(r_base, displacement, r_dest, k64, s_reg); } @@ -975,16 +991,16 @@ LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStora int src_low_reg = r_src.IsPair() ? r_src.GetLowReg() : r_src.GetReg(); bool null_pointer_safepoint = false; switch (size) { - case kLong: + case k64: case kDouble: if (!ARM_FPREG(src_low_reg)) { if (displacement <= 1020) { store = NewLIR4(kThumb2StrdI8, r_src.GetLowReg(), r_src.GetHighReg(), r_base.GetReg(), displacement >> 2); } else { - store = StoreBaseDispBody(r_base, displacement, r_src.GetLow(), kWord); + store = StoreBaseDispBody(r_base, displacement, r_src.GetLow(), k32); null_pointer_safepoint = true; - StoreBaseDispBody(r_base, displacement + 4, r_src.GetHigh(), kWord); + StoreBaseDispBody(r_base, displacement + 4, r_src.GetHigh(), k32); } already_generated = true; } else { @@ -1001,7 +1017,8 @@ LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStora } break; case kSingle: - case kWord: + case k32: + case kReference: if (ARM_FPREG(r_src.GetReg())) { DCHECK(ARM_SINGLEREG(r_src.GetReg())); opcode = kThumb2Vstrs; @@ -1082,12 +1099,16 @@ LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStora LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size) { - DCHECK(!((size == kLong) || (size == kDouble))); + // TODO: base this on target. + if (size == kWord) { + size = k32; + } + DCHECK(!((size == k64) || (size == kDouble))); return StoreBaseDispBody(r_base, displacement, r_src, size); } LIR* ArmMir2Lir::StoreBaseDispWide(RegStorage r_base, int displacement, RegStorage r_src) { - return StoreBaseDispBody(r_base, displacement, r_src, kLong); + return StoreBaseDispBody(r_base, displacement, r_src, k64); } LIR* ArmMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) { diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 6e6b8f0a30..501e4e204b 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -274,6 +274,19 @@ void Mir2Lir::DumpPromotionMap() { } } +void Mir2Lir::UpdateLIROffsets() { + // Only used for code listings. + size_t offset = 0; + for (LIR* lir = first_lir_insn_; lir != nullptr; lir = lir->next) { + lir->offset = offset; + if (!lir->flags.is_nop && !IsPseudoLirOp(lir->opcode)) { + offset += GetInsnSize(lir); + } else if (lir->opcode == kPseudoPseudoAlign4) { + offset += (offset & 0x2); + } + } +} + /* Dump instructions and constant pool contents */ void Mir2Lir::CodegenDump() { LOG(INFO) << "Dumping LIR insns for " @@ -293,6 +306,7 @@ void Mir2Lir::CodegenDump() { LOG(INFO) << "expansion factor: " << static_cast<float>(total_size_) / static_cast<float>(insns_size * 2); DumpPromotionMap(); + UpdateLIROffsets(); for (lir_insn = first_lir_insn_; lir_insn != NULL; lir_insn = lir_insn->next) { DumpLIRInsn(lir_insn, 0); } @@ -926,7 +940,6 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena mir_graph_(mir_graph), switch_tables_(arena, 4, kGrowableArraySwitchTables), fill_array_data_(arena, 4, kGrowableArrayFillArrayData), - throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads), suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads), tempreg_info_(arena, 20, kGrowableArrayMisc), reginfo_map_(arena, 64, kGrowableArrayMisc), @@ -1118,7 +1131,7 @@ bool Mir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest) { LIR *Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, int offset, int check_value, LIR* target) { // Handle this for architectures that can't compare to memory. - LoadWordDisp(base_reg, offset, temp_reg); + Load32Disp(base_reg, offset, temp_reg); LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target); return branch; } diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 8806e68b93..3ec31ba7d9 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -212,8 +212,8 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), - INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), - INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), + INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32), + INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64), INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), @@ -241,12 +241,12 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), - INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), - INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), + INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, k32), + INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, k64), INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), - INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), - INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), + INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, k32), + INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, k64), INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 055f60c1c8..313174d218 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -19,12 +19,17 @@ #include "dex/quick/mir_to_lir-inl.h" #include "entrypoints/quick/quick_entrypoints.h" #include "mirror/array.h" +#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "verifier/method_verifier.h" #include <functional> namespace art { +// Shortcuts to repeatedly used long types. +typedef mirror::ObjectArray<mirror::Object> ObjArray; +typedef mirror::ObjectArray<mirror::Class> ClassArray; + /* * This source files contains "gen" codegen routines that should * be applicable to most targets. Only mid-level support utilities @@ -42,22 +47,6 @@ void Mir2Lir::GenBarrier() { barrier->u.m.def_mask = ENCODE_ALL; } -LIR* Mir2Lir::GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, ThrowKind kind) { - LIR* tgt; - LIR* branch; - if (c_code == kCondAl) { - tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_, RegStorage::kInvalidRegVal, - imm_val); - branch = OpUnconditionalBranch(tgt); - } else { - tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_, reg.GetReg(), imm_val); - branch = OpCmpImmBranch(c_code, reg, imm_val, tgt); - } - // Remember branch target - will process later - throw_launchpads_.Insert(tgt); - return branch; -} - void Mir2Lir::GenDivZeroException() { LIR* branch = OpUnconditionalBranch(nullptr); AddDivZeroCheckSlowPath(branch); @@ -91,6 +80,59 @@ void Mir2Lir::AddDivZeroCheckSlowPath(LIR* branch) { AddSlowPath(new (arena_) DivZeroCheckSlowPath(this, branch)); } +void Mir2Lir::GenArrayBoundsCheck(RegStorage index, RegStorage length) { + class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, RegStorage index, RegStorage length) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch), + index_(index), length_(length) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), + index_, length_, true); + } + + private: + const RegStorage index_; + const RegStorage length_; + }; + + LIR* branch = OpCmpBranch(kCondUge, index, length, nullptr); + AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, length)); +} + +void Mir2Lir::GenArrayBoundsCheck(int index, RegStorage length) { + class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, int index, RegStorage length) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch), + index_(index), length_(length) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + + m2l_->OpRegCopy(m2l_->TargetReg(kArg1), length_); + m2l_->LoadConstant(m2l_->TargetReg(kArg0), index_); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), + m2l_->TargetReg(kArg0), m2l_->TargetReg(kArg1), true); + } + + private: + const int32_t index_; + const RegStorage length_; + }; + + LIR* branch = OpCmpImmBranch(kCondLs, length, index, nullptr); + AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, length)); +} + LIR* Mir2Lir::GenNullCheck(RegStorage reg) { class NullCheckSlowPath : public Mir2Lir::LIRSlowPath { public: @@ -151,23 +193,12 @@ void Mir2Lir::ForceImplicitNullCheck(RegStorage reg, int opt_flags) { // register with offset 0. This will cause a signal if the register contains 0 (null). RegStorage tmp = AllocTemp(); // TODO: for Mips, would be best to use rZERO as the bogus register target. - LIR* load = LoadWordDisp(reg, 0, tmp); + LIR* load = Load32Disp(reg, 0, tmp); FreeTemp(tmp); MarkSafepointPC(load); } } -/* Perform check on two registers */ -LIR* Mir2Lir::GenRegRegCheck(ConditionCode c_code, RegStorage reg1, RegStorage reg2, - ThrowKind kind) { - LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_, reg1.GetReg(), - reg2.GetReg()); - LIR* branch = OpCmpBranch(c_code, reg1, reg2, tgt); - // Remember branch target - will process later - throw_launchpads_.Insert(tgt); - return branch; -} - void Mir2Lir::GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1, RegLocation rl_src2, LIR* taken, LIR* fall_through) { @@ -373,7 +404,7 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { for (int i = 0; i < elems; i++) { RegLocation loc = UpdateLoc(info->args[i]); if (loc.location == kLocPhysReg) { - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kWord); + Store32Disp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg); } } /* @@ -410,8 +441,8 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { // Generate the copy loop. Going backwards for convenience LIR* target = NewLIR0(kPseudoTargetLabel); // Copy next element - LoadBaseIndexed(r_src, r_idx, r_val, 2, kWord); - StoreBaseIndexed(r_dst, r_idx, r_val, 2, kWord); + LoadBaseIndexed(r_src, r_idx, r_val, 2, k32); + StoreBaseIndexed(r_dst, r_idx, r_val, 2, k32); FreeTemp(r_val); OpDecAndBranch(kCondGe, r_idx, target); if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { @@ -423,9 +454,8 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { // TUNING: interleave for (int i = 0; i < elems; i++) { RegLocation rl_arg = LoadValue(info->args[i], kCoreReg); - StoreBaseDisp(TargetReg(kRet0), - mirror::Array::DataOffset(component_size).Int32Value() + i * 4, - rl_arg.reg, kWord); + Store32Disp(TargetReg(kRet0), + mirror::Array::DataOffset(component_size).Int32Value() + i * 4, rl_arg.reg); // If the LoadValue caused a temp to be allocated, free it if (IsTemp(rl_arg.reg)) { FreeTemp(rl_arg.reg); @@ -476,7 +506,7 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, // Fast path, static storage base is this method's class RegLocation rl_method = LoadCurrMethod(); r_base = AllocTemp(); - LoadWordDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base); + LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base); if (IsTemp(rl_method.reg)) { FreeTemp(rl_method.reg); } @@ -493,9 +523,9 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, LoadCurrMethodDirect(r_method); r_base = TargetReg(kArg0); LockTemp(r_base); - LoadWordDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base); - LoadWordDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - sizeof(int32_t*) * field_info.StorageIndex(), r_base); + LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base); + int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value(); + LoadRefDisp(r_base, offset_of_field, r_base); // r_base now points at static storage (Class*) or NULL if the type is not yet resolved. if (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) { @@ -535,8 +565,10 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, } if (is_long_or_double) { StoreBaseDispWide(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg); + } else if (rl_src.ref) { + StoreRefDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg); } else { - StoreWordDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg); + Store32Disp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg); } if (field_info.IsVolatile()) { // A load might follow the volatile store so insert a StoreLoad barrier. @@ -567,7 +599,7 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, // Fast path, static storage base is this method's class RegLocation rl_method = LoadCurrMethod(); r_base = AllocTemp(); - LoadWordDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base); + LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base); } else { // Medium path, static storage base in a different class which requires checks that the other // class is initialized @@ -580,9 +612,9 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, LoadCurrMethodDirect(r_method); r_base = TargetReg(kArg0); LockTemp(r_base); - LoadWordDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base); - LoadWordDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - sizeof(int32_t*) * field_info.StorageIndex(), r_base); + LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base); + int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value(); + LoadRefDisp(r_base, offset_of_field, r_base); // r_base now points at static storage (Class*) or NULL if the type is not yet resolved. if (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) { @@ -615,8 +647,10 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, if (is_long_or_double) { LoadBaseDispWide(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg, INVALID_SREG); + } else if (rl_result.ref) { + LoadRefDisp(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg); } else { - LoadWordDisp(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg); + Load32Disp(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg); } FreeTemp(r_base); @@ -675,82 +709,6 @@ void Mir2Lir::HandleSuspendLaunchPads() { } } -void Mir2Lir::HandleThrowLaunchPads() { - int num_elems = throw_launchpads_.Size(); - for (int i = 0; i < num_elems; i++) { - ResetRegPool(); - ResetDefTracking(); - LIR* lab = throw_launchpads_.Get(i); - current_dalvik_offset_ = lab->operands[1]; - AppendLIR(lab); - ThreadOffset<4> func_offset(-1); - int v1 = lab->operands[2]; - int v2 = lab->operands[3]; - const bool target_x86 = cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64; - switch (lab->operands[0]) { - case kThrowConstantArrayBounds: // v1 is length reg (for Arm/Mips), v2 constant index - // v1 holds the constant array index. Mips/Arm uses v2 for length, x86 reloads. - if (target_x86) { - OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v1), - mirror::Array::LengthOffset().Int32Value()); - } else { - OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v1)); - } - // Make sure the following LoadConstant doesn't mess with kArg1. - LockTemp(TargetReg(kArg1)); - LoadConstant(TargetReg(kArg0), v2); - func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds); - break; - case kThrowArrayBounds: - // Move v1 (array index) to kArg0 and v2 (array length) to kArg1 - if (v2 != TargetReg(kArg0).GetReg()) { - OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1)); - if (target_x86) { - // x86 leaves the array pointer in v2, so load the array length that the handler expects - OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2), - mirror::Array::LengthOffset().Int32Value()); - } else { - OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2)); - } - } else { - if (v1 == TargetReg(kArg1).GetReg()) { - // Swap v1 and v2, using kArg2 as a temp - OpRegCopy(TargetReg(kArg2), RegStorage::Solo32(v1)); - if (target_x86) { - // x86 leaves the array pointer in v2; load the array length that the handler expects - OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2), - mirror::Array::LengthOffset().Int32Value()); - } else { - OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2)); - } - OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); - } else { - if (target_x86) { - // x86 leaves the array pointer in v2; load the array length that the handler expects - OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2), - mirror::Array::LengthOffset().Int32Value()); - } else { - OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2)); - } - OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1)); - } - } - func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds); - break; - case kThrowNoSuchMethod: - OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1)); - func_offset = - QUICK_ENTRYPOINT_OFFSET(4, pThrowNoSuchMethod); - break; - default: - LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0]; - } - ClobberCallerSave(); - RegStorage r_tgt = CallHelperSetup(func_offset); - CallHelper(r_tgt, func_offset, true /* MarkSafepointPC */, true /* UseLink */); - } -} - void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object) { @@ -798,7 +756,7 @@ void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, } else { rl_result = EvalLoc(rl_dest, reg_class, true); GenNullCheck(rl_obj.reg, opt_flags); - LoadBaseDisp(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_result.reg, kWord, + LoadBaseDisp(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_result.reg, k32, rl_obj.s_reg_low); MarkPossibleNullPointerException(opt_flags); if (field_info.IsVolatile()) { @@ -862,7 +820,7 @@ void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size, // There might have been a store before this volatile one so insert StoreStore barrier. GenMemBarrier(kStoreStore); } - StoreBaseDisp(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_src.reg, kWord); + Store32Disp(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_src.reg); MarkPossibleNullPointerException(opt_flags); if (field_info.IsVolatile()) { // A load might follow the volatile store so insert a StoreLoad barrier. @@ -911,11 +869,9 @@ void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) { // We're don't need access checks, load type from dex cache int32_t dex_cache_offset = mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(); - LoadWordDisp(rl_method.reg, dex_cache_offset, res_reg); - int32_t offset_of_type = - mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*) - * type_idx); - LoadWordDisp(res_reg, offset_of_type, rl_result.reg); + Load32Disp(rl_method.reg, dex_cache_offset, res_reg); + int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); + Load32Disp(res_reg, offset_of_type, rl_result.reg); if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx) || SLOW_TYPE_PATH) { // Slow path, at runtime test if type is null and if so initialize @@ -961,8 +917,8 @@ void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) { void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) { /* NOTE: Most strings should be available at compile time */ - int32_t offset_of_string = mirror::Array::DataOffset(sizeof(mirror::String*)).Int32Value() + - (sizeof(mirror::String*) * string_idx); + int32_t offset_of_string = mirror::ObjectArray<mirror::String>::OffsetOfElement(string_idx). + Int32Value(); if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache( *cu_->dex_file, string_idx) || SLOW_STRING_PATH) { // slow path, resolve string if not in dex cache @@ -980,11 +936,11 @@ void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) { r_method = TargetReg(kArg2); LoadCurrMethodDirect(r_method); } - LoadWordDisp(r_method, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), - TargetReg(kArg0)); + LoadRefDisp(r_method, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), + TargetReg(kArg0)); // Might call out to helper, which will return resolved string in kRet0 - LoadWordDisp(TargetReg(kArg0), offset_of_string, TargetReg(kRet0)); + Load32Disp(TargetReg(kArg0), offset_of_string, TargetReg(kRet0)); if (cu_->instruction_set == kThumb2 || cu_->instruction_set == kMips) { // OpRegImm(kOpCmp, TargetReg(kRet0), 0); // Is resolved? @@ -1034,8 +990,8 @@ void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) { RegLocation rl_method = LoadCurrMethod(); RegStorage res_reg = AllocTemp(); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - LoadWordDisp(rl_method.reg, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), res_reg); - LoadWordDisp(res_reg, offset_of_string, rl_result.reg); + LoadRefDisp(rl_method.reg, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), res_reg); + Load32Disp(res_reg, offset_of_string, rl_result.reg); StoreValue(rl_dest, rl_result); } } @@ -1120,19 +1076,18 @@ void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, Re LoadCurrMethodDirect(check_class); if (use_declaring_class) { - LoadWordDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class); - LoadWordDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class); + LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class); + LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class); } else { - LoadWordDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - check_class); - LoadWordDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class); - int32_t offset_of_type = - mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + - (sizeof(mirror::Class*) * type_idx); - LoadWordDisp(check_class, offset_of_type, check_class); + LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), + check_class); + LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class); + int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); + LoadRefDisp(check_class, offset_of_type, check_class); } LIR* ne_branchover = NULL; + // FIXME: what should we be comparing here? compressed or decompressed references? if (cu_->instruction_set == kThumb2) { OpRegReg(kOpCmp, check_class, object_class); // Same? LIR* it = OpIT(kCondEq, ""); // if-convert the test @@ -1178,17 +1133,15 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref } else if (use_declaring_class) { LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref - LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), + LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), class_reg); } else { // Load dex cache entry into class_reg (kArg2) LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref - LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - class_reg); - int32_t offset_of_type = - mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*) - * type_idx); - LoadWordDisp(class_reg, offset_of_type, class_reg); + LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), + class_reg); + int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); + LoadRefDisp(class_reg, offset_of_type, class_reg); if (!can_assume_type_is_in_dex_cache) { // Need to test presence of type in dex cache at runtime LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL); @@ -1212,7 +1165,7 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know /* load object->klass_ */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); - LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); + LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */ LIR* branchover = NULL; if (type_known_final) { @@ -1315,16 +1268,14 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ type_idx, TargetReg(kArg1), true); OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path } else if (use_declaring_class) { - LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), - class_reg); + LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), + class_reg); } else { // Load dex cache entry into class_reg (kArg2) - LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), - class_reg); - int32_t offset_of_type = - mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + - (sizeof(mirror::Class*) * type_idx); - LoadWordDisp(class_reg, offset_of_type, class_reg); + LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), + class_reg); + int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); + LoadRefDisp(class_reg, offset_of_type, class_reg); if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) { // Need to test presence of type in dex cache at runtime LIR* hop_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL); @@ -1372,8 +1323,8 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ GenerateTargetLabel(); if (load_) { - m2l_->LoadWordDisp(m2l_->TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), - m2l_->TargetReg(kArg1)); + m2l_->LoadRefDisp(m2l_->TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), + m2l_->TargetReg(kArg1)); } m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pCheckCast), m2l_->TargetReg(kArg2), m2l_->TargetReg(kArg1), true); @@ -1399,7 +1350,7 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0), 0, NULL); /* load object->klass_ */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); - LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); + LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); LIR* branch2 = OpCmpBranch(kCondNe, TargetReg(kArg1), class_reg, NULL); LIR* cont = NewLIR0(kPseudoTargetLabel); @@ -1586,7 +1537,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, rl_src1 = LoadValue(rl_src1, kCoreReg); rl_src2 = LoadValue(rl_src2, kCoreReg); if (check_zero) { - GenDivZeroCheck(rl_src2.reg); + GenDivZeroCheck(rl_src2.reg); } rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv); done = true; @@ -1597,7 +1548,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, rl_src1 = LoadValue(rl_src1, kCoreReg); rl_src2 = LoadValue(rl_src2, kCoreReg); if (check_zero) { - GenDivZeroCheck(rl_src2.reg); + GenDivZeroCheck(rl_src2.reg); } rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv); done = true; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 4aae16d103..53b6ed420e 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -34,10 +34,10 @@ namespace art { * and "op" calls may be used here. */ -void Mir2Lir::AddIntrinsicLaunchpad(CallInfo* info, LIR* branch, LIR* resume) { - class IntrinsicLaunchpadPath : public Mir2Lir::LIRSlowPath { +void Mir2Lir::AddIntrinsicSlowPath(CallInfo* info, LIR* branch, LIR* resume) { + class IntrinsicSlowPathPath : public Mir2Lir::LIRSlowPath { public: - IntrinsicLaunchpadPath(Mir2Lir* m2l, CallInfo* info, LIR* branch, LIR* resume = nullptr) + IntrinsicSlowPathPath(Mir2Lir* m2l, CallInfo* info, LIR* branch, LIR* resume = nullptr) : LIRSlowPath(m2l, info->offset, branch, resume), info_(info) { } @@ -57,7 +57,7 @@ void Mir2Lir::AddIntrinsicLaunchpad(CallInfo* info, LIR* branch, LIR* resume) { CallInfo* const info_; }; - AddSlowPath(new (arena_) IntrinsicLaunchpadPath(this, info, branch, resume)); + AddSlowPath(new (arena_) IntrinsicSlowPathPath(this, info, branch, resume)); } /* @@ -255,12 +255,27 @@ void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset<4> helper_off CallHelper(r_tgt, helper_offset, safepoint_pc); } +void Mir2Lir::CopyToArgumentRegs(RegStorage arg0, RegStorage arg1) { + if (arg1.GetReg() == TargetReg(kArg0).GetReg()) { + if (arg0.GetReg() == TargetReg(kArg1).GetReg()) { + // Swap kArg0 and kArg1 with kArg2 as temp. + OpRegCopy(TargetReg(kArg2), arg1); + OpRegCopy(TargetReg(kArg0), arg0); + OpRegCopy(TargetReg(kArg1), TargetReg(kArg2)); + } else { + OpRegCopy(TargetReg(kArg1), arg1); + OpRegCopy(TargetReg(kArg0), arg0); + } + } else { + OpRegCopy(TargetReg(kArg0), arg0); + OpRegCopy(TargetReg(kArg1), arg1); + } +} + void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset<4> helper_offset, RegStorage arg0, RegStorage arg1, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - DCHECK_NE(TargetReg(kArg0).GetReg(), arg1.GetReg()); // check copy into arg0 won't clobber arg1 - OpRegCopy(TargetReg(kArg0), arg0); - OpRegCopy(TargetReg(kArg1), arg1); + CopyToArgumentRegs(arg0, arg1); ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -268,9 +283,7 @@ void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset<4> helper_offset, RegStorage void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset<4> helper_offset, RegStorage arg0, RegStorage arg1, int arg2, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - DCHECK_NE(TargetReg(kArg0).GetReg(), arg1.GetReg()); // check copy into arg0 won't clobber arg1 - OpRegCopy(TargetReg(kArg0), arg0); - OpRegCopy(TargetReg(kArg1), arg1); + CopyToArgumentRegs(arg0, arg1); LoadConstant(TargetReg(kArg2), arg2); ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); @@ -347,7 +360,11 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { rl_src.reg = TargetReg(kArg0); rl_src.home = false; MarkLive(rl_src.reg, rl_src.s_reg_low); - StoreValue(rl_method, rl_src); + if (rl_method.wide) { + StoreValueWide(rl_method, rl_src); + } else { + StoreValue(rl_method, rl_src); + } // If Method* has been promoted, explicitly flush if (rl_method.location == kLocPhysReg) { StoreWordDisp(TargetReg(kSp), 0, TargetReg(kArg0)); @@ -412,16 +429,15 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { } } if (need_flush) { - StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, kWord); + Store32Disp(TargetReg(kSp), SRegOffset(start_vreg + i), reg); } } else { // If arriving in frame & promoted if (v_map->core_location == kLocPhysReg) { - LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), - RegStorage::Solo32(v_map->core_reg)); + Load32Disp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->core_reg)); } if (v_map->fp_location == kLocPhysReg) { - LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->FpReg)); + Load32Disp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->FpReg)); } } } @@ -463,9 +479,9 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, cg->LoadCurrMethodDirect(cg->TargetReg(kArg0)); break; case 1: // Get method->dex_cache_resolved_methods_ - cg->LoadWordDisp(cg->TargetReg(kArg0), - mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), - cg->TargetReg(kArg0)); + cg->LoadRefDisp(cg->TargetReg(kArg0), + mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), + cg->TargetReg(kArg0)); // Set up direct code if known. if (direct_code != 0) { if (direct_code != static_cast<unsigned int>(-1)) { @@ -478,9 +494,9 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, break; case 2: // Grab target method* CHECK_EQ(cu->dex_file, target_method.dex_file); - cg->LoadWordDisp(cg->TargetReg(kArg0), - mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - (target_method.dex_method_index * 4), cg->TargetReg(kArg0)); + cg->LoadRefDisp(cg->TargetReg(kArg0), + mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + + (target_method.dex_method_index * 4), cg->TargetReg(kArg0)); break; case 3: // Grab the code from the method* if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { @@ -524,18 +540,18 @@ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, case 1: // Is "this" null? [use kArg1] cg->GenNullCheck(cg->TargetReg(kArg1), info->opt_flags); // get this->klass_ [use kArg1, set kInvokeTgt] - cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), - cg->TargetReg(kInvokeTgt)); + cg->LoadRefDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), + cg->TargetReg(kInvokeTgt)); cg->MarkPossibleNullPointerException(info->opt_flags); break; case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt] - cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(), - cg->TargetReg(kInvokeTgt)); + cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(), + cg->TargetReg(kInvokeTgt)); break; case 3: // Get target method [use kInvokeTgt, set kArg0] - cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), (method_idx * 4) + - mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), - cg->TargetReg(kArg0)); + cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), (method_idx * 4) + + mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), + cg->TargetReg(kArg0)); break; case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt] if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { @@ -579,15 +595,17 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, case 2: // Is "this" null? [use kArg1] cg->GenNullCheck(cg->TargetReg(kArg1), info->opt_flags); // Get this->klass_ [use kArg1, set kInvokeTgt] - cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), - cg->TargetReg(kInvokeTgt)); + cg->LoadRefDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), + cg->TargetReg(kInvokeTgt)); cg->MarkPossibleNullPointerException(info->opt_flags); break; case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt] - cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(), - cg->TargetReg(kInvokeTgt)); + // NOTE: native pointer. + cg->LoadRefDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(), + cg->TargetReg(kInvokeTgt)); break; case 4: // Get target method [use kInvokeTgt, set kArg0] + // NOTE: native pointer. cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) + mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), cg->TargetReg(kArg0)); @@ -740,11 +758,11 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, } else { // kArg2 & rArg3 can safely be used here reg = TargetReg(kArg3); - LoadWordDisp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg); + Load32Disp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg); call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); } - StoreBaseDisp(TargetReg(kSp), (next_use + 1) * 4, reg, kWord); + Store32Disp(TargetReg(kSp), (next_use + 1) * 4, reg); call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); next_use++; @@ -778,7 +796,7 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, StoreBaseDispWide(TargetReg(kSp), outs_offset, RegStorage::MakeRegPair(low_reg, high_reg)); next_use += 2; } else { - StoreWordDisp(TargetReg(kSp), outs_offset, low_reg); + Store32Disp(TargetReg(kSp), outs_offset, low_reg); next_use++; } call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, @@ -798,7 +816,7 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, // In lieu of generating a check for kArg1 being null, we need to // perform a load when doing implicit checks. RegStorage tmp = AllocTemp(); - LoadWordDisp(TargetReg(kArg1), 0, tmp); + Load32Disp(TargetReg(kArg1), 0, tmp); MarkPossibleNullPointerException(info->opt_flags); FreeTemp(tmp); } @@ -849,7 +867,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, } else { loc = UpdateLoc(loc); if ((next_arg >= 3) && (loc.location == kLocPhysReg)) { - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kWord); + Store32Disp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg); } next_arg++; } @@ -984,8 +1002,8 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, RegStorage temp = TargetReg(kArg3); // Now load the argument VR and store to the outs. - LoadWordDisp(TargetReg(kSp), current_src_offset, temp); - StoreWordDisp(TargetReg(kSp), current_dest_offset, temp); + Load32Disp(TargetReg(kSp), current_src_offset, temp); + Store32Disp(TargetReg(kSp), current_dest_offset, temp); } current_src_offset += bytes_to_move; @@ -1014,7 +1032,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, // In lieu of generating a check for kArg1 being null, we need to // perform a load when doing implicit checks. RegStorage tmp = AllocTemp(); - LoadWordDisp(TargetReg(kArg1), 0, tmp); + Load32Disp(TargetReg(kArg1), 0, tmp); MarkPossibleNullPointerException(info->opt_flags); FreeTemp(tmp); } @@ -1074,14 +1092,14 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { reg_ptr = AllocTemp(); if (range_check) { reg_max = AllocTemp(); - LoadWordDisp(rl_obj.reg, count_offset, reg_max); + Load32Disp(rl_obj.reg, count_offset, reg_max); MarkPossibleNullPointerException(info->opt_flags); } - LoadWordDisp(rl_obj.reg, offset_offset, reg_off); + Load32Disp(rl_obj.reg, offset_offset, reg_off); MarkPossibleNullPointerException(info->opt_flags); - LoadWordDisp(rl_obj.reg, value_offset, reg_ptr); + Load32Disp(rl_obj.reg, value_offset, reg_ptr); if (range_check) { - // Set up a launch pad to allow retry in case of bounds violation */ + // Set up a slow path to allow retry in case of bounds violation */ OpRegReg(kOpCmp, rl_idx.reg, reg_max); FreeTemp(reg_max); range_check_branch = OpCondBranch(kCondUge, nullptr); @@ -1102,8 +1120,8 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { } reg_off = AllocTemp(); reg_ptr = AllocTemp(); - LoadWordDisp(rl_obj.reg, offset_offset, reg_off); - LoadWordDisp(rl_obj.reg, value_offset, reg_ptr); + Load32Disp(rl_obj.reg, offset_offset, reg_off); + Load32Disp(rl_obj.reg, value_offset, reg_ptr); } if (rl_idx.is_const) { OpRegImm(kOpAdd, reg_off, mir_graph_->ConstantValue(rl_idx.orig_sreg)); @@ -1128,7 +1146,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { if (range_check) { DCHECK(range_check_branch != nullptr); info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've already null checked. - AddIntrinsicLaunchpad(info, range_check_branch); + AddIntrinsicSlowPath(info, range_check_branch); } return true; } @@ -1145,7 +1163,7 @@ bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { RegLocation rl_dest = InlineTarget(info); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); GenNullCheck(rl_obj.reg, info->opt_flags); - LoadWordDisp(rl_obj.reg, mirror::String::CountOffset().Int32Value(), rl_result.reg); + Load32Disp(rl_obj.reg, mirror::String::CountOffset().Int32Value(), rl_result.reg); MarkPossibleNullPointerException(info->opt_flags); if (is_empty) { // dst = (dst == 0); @@ -1169,9 +1187,9 @@ bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { return false; } RegLocation rl_src_i = info->args[0]; - RegLocation rl_dest = (size == kLong) ? InlineTargetWide(info) : InlineTarget(info); // result reg + RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info); // result reg RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - if (size == kLong) { + if (size == k64) { RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg); RegStorage r_i_low = rl_i.reg.GetLow(); if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) { @@ -1186,8 +1204,8 @@ bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { } StoreValueWide(rl_dest, rl_result); } else { - DCHECK(size == kWord || size == kSignedHalf); - OpKind op = (size == kWord) ? kOpRev : kOpRevsh; + DCHECK(size == k32 || size == kSignedHalf); + OpKind op = (size == k32) ? kOpRev : kOpRevsh; RegLocation rl_i = LoadValue(rl_src_i, kCoreReg); OpRegReg(op, rl_result.reg, rl_i.reg); StoreValue(rl_dest, rl_result); @@ -1339,7 +1357,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { DCHECK(high_code_point_branch != nullptr); LIR* resume_tgt = NewLIR0(kPseudoTargetLabel); info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked. - AddIntrinsicLaunchpad(info, high_code_point_branch, resume_tgt); + AddIntrinsicSlowPath(info, high_code_point_branch, resume_tgt); } else { DCHECK_EQ(mir_graph_->ConstantValue(rl_char) & ~0xFFFF, 0); DCHECK(high_code_point_branch == nullptr); @@ -1371,7 +1389,7 @@ bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) { info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked. // TUNING: check if rl_cmp.s_reg_low is already null checked LIR* cmp_null_check_branch = OpCmpImmBranch(kCondEq, reg_cmp, 0, nullptr); - AddIntrinsicLaunchpad(info, cmp_null_check_branch); + AddIntrinsicSlowPath(info, cmp_null_check_branch); // NOTE: not a safepoint if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) { OpReg(kOpBlx, r_tgt); @@ -1389,7 +1407,7 @@ bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) { RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); ThreadOffset<4> offset = Thread::PeerOffset<4>(); if (cu_->instruction_set == kThumb2 || cu_->instruction_set == kMips) { - LoadWordDisp(TargetReg(kSelf), offset.Int32Value(), rl_result.reg); + Load32Disp(TargetReg(kSelf), offset.Int32Value(), rl_result.reg); } else { CHECK(cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64); reinterpret_cast<X86Mir2Lir*>(this)->OpRegThreadMem(kOpMov, rl_result.reg.GetReg(), offset); @@ -1416,7 +1434,7 @@ bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, if (is_long) { if (cu_->instruction_set == kX86) { LoadBaseIndexedDisp(rl_object.reg, rl_offset.reg, 0, 0, rl_result.reg.GetLow(), - rl_result.reg.GetHigh(), kLong, INVALID_SREG); + rl_result.reg.GetHigh(), k64, INVALID_SREG); } else { RegStorage rl_temp_offset = AllocTemp(); OpRegRegReg(kOpAdd, rl_temp_offset, rl_object.reg, rl_offset.reg); @@ -1424,7 +1442,7 @@ bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, FreeTemp(rl_temp_offset.GetReg()); } } else { - LoadBaseIndexed(rl_object.reg, rl_offset.reg, rl_result.reg, 0, kWord); + LoadBaseIndexed(rl_object.reg, rl_offset.reg, rl_result.reg, 0, k32); } if (is_volatile) { @@ -1464,7 +1482,7 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, rl_value = LoadValueWide(rl_src_value, kCoreReg); if (cu_->instruction_set == kX86) { StoreBaseIndexedDisp(rl_object.reg, rl_offset.reg, 0, 0, rl_value.reg.GetLow(), - rl_value.reg.GetHigh(), kLong, INVALID_SREG); + rl_value.reg.GetHigh(), k64, INVALID_SREG); } else { RegStorage rl_temp_offset = AllocTemp(); OpRegRegReg(kOpAdd, rl_temp_offset, rl_object.reg, rl_offset.reg); @@ -1473,7 +1491,7 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, } } else { rl_value = LoadValue(rl_src_value, kCoreReg); - StoreBaseIndexed(rl_object.reg, rl_offset.reg, rl_value.reg, 0, kWord); + StoreBaseIndexed(rl_object.reg, rl_offset.reg, rl_value.reg, 0, k32); } // Free up the temp early, to ensure x86 doesn't run out of temporaries in MarkGCCard. diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc index 208eadde12..9808f7f36f 100644 --- a/compiler/dex/quick/gen_loadstore.cc +++ b/compiler/dex/quick/gen_loadstore.cc @@ -65,7 +65,7 @@ void Mir2Lir::Workaround7250540(RegLocation rl_dest, RegStorage zero_reg) { OpRegCopy(RegStorage::Solo32(promotion_map_[pmap_index].core_reg), temp_reg); } else { // Lives in the frame, need to store. - StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, kWord); + StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, k32); } if (!zero_reg.Valid()) { FreeTemp(temp_reg); @@ -74,15 +74,6 @@ void Mir2Lir::Workaround7250540(RegLocation rl_dest, RegStorage zero_reg) { } } -/* Load a word at base + displacement. Displacement must be word multiple */ -LIR* Mir2Lir::LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) { - return LoadBaseDisp(r_base, displacement, r_dest, kWord, INVALID_SREG); -} - -LIR* Mir2Lir::StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) { - return StoreBaseDisp(r_base, displacement, r_src, kWord); -} - /* * Load a Dalvik register into a physical register. Take care when * using this routine, as it doesn't perform any bookkeeping regarding @@ -93,11 +84,17 @@ void Mir2Lir::LoadValueDirect(RegLocation rl_src, RegStorage r_dest) { if (rl_src.location == kLocPhysReg) { OpRegCopy(r_dest, rl_src.reg); } else if (IsInexpensiveConstant(rl_src)) { + // On 64-bit targets, will sign extend. Make sure constant reference is always NULL. + DCHECK(!rl_src.ref || (mir_graph_->ConstantValue(rl_src) == 0)); LoadConstantNoClobber(r_dest, mir_graph_->ConstantValue(rl_src)); } else { DCHECK((rl_src.location == kLocDalvikFrame) || (rl_src.location == kLocCompilerTemp)); - LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest); + if (rl_src.ref) { + LoadRefDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest); + } else { + Load32Disp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest); + } } } @@ -194,7 +191,7 @@ void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) { ResetDefLoc(rl_dest); if (IsDirty(rl_dest.reg) && oat_live_out(rl_dest.s_reg_low)) { def_start = last_lir_insn_; - StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kWord); + Store32Disp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg); MarkClean(rl_dest); def_end = last_lir_insn_; if (!rl_dest.ref) { @@ -306,7 +303,7 @@ void Mir2Lir::StoreFinalValue(RegLocation rl_dest, RegLocation rl_src) { if (IsDirty(rl_dest.reg) && oat_live_out(rl_dest.s_reg_low)) { LIR *def_start = last_lir_insn_; - StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kWord); + Store32Disp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg); MarkClean(rl_dest); LIR *def_end = last_lir_insn_; if (!rl_dest.ref) { diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index a938478b3d..a237ac76b0 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -112,11 +112,11 @@ void MipsMir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset, RegStorage r_key = AllocTemp(); LIR* loop_label = NewLIR0(kPseudoTargetLabel); LIR* exit_branch = OpCmpBranch(kCondEq, r_base, r_end, NULL); - LoadWordDisp(r_base, 0, r_key); + Load32Disp(r_base, 0, r_key); OpRegImm(kOpAdd, r_base, 8); OpCmpBranch(kCondNe, rl_src.reg, r_key, loop_label); RegStorage r_disp = AllocTemp(); - LoadWordDisp(r_base, -4, r_disp); + Load32Disp(r_base, -4, r_disp); OpRegRegReg(kOpAdd, rs_rRA, rs_rRA, r_disp); OpReg(kOpBx, rs_rRA); @@ -200,7 +200,7 @@ void MipsMir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset, // Load the displacement from the switch table RegStorage r_disp = AllocTemp(); - LoadBaseIndexed(r_base, r_key, r_disp, 2, kWord); + LoadBaseIndexed(r_base, r_key, r_disp, 2, k32); // Add to rAP and go OpRegRegReg(kOpAdd, rs_rRA, rs_rRA, r_disp); @@ -263,9 +263,9 @@ void MipsMir2Lir::GenMoveException(RegLocation rl_dest) { int ex_offset = Thread::ExceptionOffset<4>().Int32Value(); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage reset_reg = AllocTemp(); - LoadWordDisp(rs_rMIPS_SELF, ex_offset, rl_result.reg); + Load32Disp(rs_rMIPS_SELF, ex_offset, rl_result.reg); LoadConstant(reset_reg, 0); - StoreWordDisp(rs_rMIPS_SELF, ex_offset, reset_reg); + Store32Disp(rs_rMIPS_SELF, ex_offset, reset_reg); FreeTemp(reset_reg); StoreValue(rl_dest, rl_result); } @@ -277,6 +277,7 @@ void MipsMir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) { RegStorage reg_card_base = AllocTemp(); RegStorage reg_card_no = AllocTemp(); LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); + // NOTE: native pointer. LoadWordDisp(rs_rMIPS_SELF, Thread::CardTableOffset<4>().Int32Value(), reg_card_base); OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); @@ -310,7 +311,7 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) RegStorage new_sp = AllocTemp(); if (!skip_overflow_check) { /* Load stack limit */ - LoadWordDisp(rs_rMIPS_SELF, Thread::StackEndOffset<4>().Int32Value(), check_reg); + Load32Disp(rs_rMIPS_SELF, Thread::StackEndOffset<4>().Int32Value(), check_reg); } /* Spill core callee saves */ SpillCoreRegs(); @@ -328,7 +329,7 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) m2l_->ResetDefTracking(); GenerateTargetLabel(); // LR is offset 0 since we push in reverse order. - m2l_->LoadWordDisp(rs_rMIPS_SP, 0, rs_rRA); + m2l_->Load32Disp(rs_rMIPS_SP, 0, rs_rRA); m2l_->OpRegImm(kOpAdd, rs_rMIPS_SP, sp_displace_); m2l_->ClobberCallerSave(); ThreadOffset<4> func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowStackOverflow); diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 40641d670d..81d6782288 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -126,8 +126,6 @@ class MipsMir2Lir FINAL : public Mir2Lir { RegLocation rl_src2); void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - LIR* GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, int offset, - ThrowKind kind); RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); @@ -161,7 +159,7 @@ class MipsMir2Lir FINAL : public Mir2Lir { LIR* OpMem(OpKind op, RegStorage r_base, int disp); LIR* OpPcRelLoad(RegStorage reg, LIR* target); LIR* OpReg(OpKind op, RegStorage r_dest_src); - LIR* OpRegCopy(RegStorage r_dest, RegStorage r_src); + void OpRegCopy(RegStorage r_dest, RegStorage r_src); LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src); LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value); LIR* OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset); diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index ac0847f22d..7c0becd41a 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -177,37 +177,40 @@ LIR* MipsMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) { return res; } -LIR* MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { - LIR *res = OpRegCopyNoInsert(r_dest, r_src); - AppendLIR(res); - return res; +void MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { + if (r_dest != r_src) { + LIR *res = OpRegCopyNoInsert(r_dest, r_src); + AppendLIR(res); + } } void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { - bool dest_fp = MIPS_FPREG(r_dest.GetLowReg()); - bool src_fp = MIPS_FPREG(r_src.GetLowReg()); - if (dest_fp) { - if (src_fp) { - // FIXME: handle this here - reserve OpRegCopy for 32-bit copies. - OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())), - RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg()))); - } else { - /* note the operands are swapped for the mtc1 instr */ - NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg()); - NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg()); - } - } else { - if (src_fp) { - NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg()); - NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg()); + if (r_dest != r_src) { + bool dest_fp = MIPS_FPREG(r_dest.GetLowReg()); + bool src_fp = MIPS_FPREG(r_src.GetLowReg()); + if (dest_fp) { + if (src_fp) { + // FIXME: handle this here - reserve OpRegCopy for 32-bit copies. + OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())), + RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg()))); + } else { + /* note the operands are swapped for the mtc1 instr */ + NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg()); + NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg()); + } } else { - // Handle overlap - if (r_src.GetHighReg() == r_dest.GetLowReg()) { - OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); - OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + if (src_fp) { + NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg()); + NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg()); } else { - OpRegCopy(r_dest.GetLow(), r_src.GetLow()); - OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + // Handle overlap + if (r_src.GetHighReg() == r_dest.GetLowReg()) { + OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + } else { + OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + } } } } @@ -221,12 +224,6 @@ void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch"; } -LIR* MipsMir2Lir::GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, - int offset, ThrowKind kind) { - LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm"; - return NULL; -} - RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2, bool is_div) { NewLIR2(kMipsDiv, reg1.GetReg(), reg2.GetReg()); @@ -346,7 +343,7 @@ void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) { DCHECK(reg.IsPair()); // TODO: support k64BitSolo. RegStorage t_reg = AllocTemp(); OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh()); - GenDivZeroCheck(kCondEq); + GenDivZeroCheck(t_reg); FreeTemp(t_reg); } @@ -480,7 +477,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_array = LoadValue(rl_array, kCoreReg); rl_index = LoadValue(rl_index, kCoreReg); - if (size == kLong || size == kDouble) { + if (size == k64 || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); @@ -495,12 +492,12 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, if (needs_range_check) { reg_len = AllocTemp(); /* Get len */ - LoadWordDisp(rl_array.reg, len_offset, reg_len); + Load32Disp(rl_array.reg, len_offset, reg_len); } /* reg_ptr -> array data */ OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); FreeTemp(rl_array.reg.GetReg()); - if ((size == kLong) || (size == kDouble)) { + if ((size == k64) || (size == kDouble)) { if (scale) { RegStorage r_new_index = AllocTemp(); OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale); @@ -513,7 +510,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } LoadBaseDispWide(reg_ptr, 0, rl_result.reg, INVALID_SREG); @@ -524,7 +521,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); @@ -544,7 +541,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; - if (size == kLong || size == kDouble) { + if (size == k64 || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); @@ -572,12 +569,12 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, reg_len = AllocTemp(); // NOTE: max live temps(4) here. /* Get len */ - LoadWordDisp(rl_array.reg, len_offset, reg_len); + Load32Disp(rl_array.reg, len_offset, reg_len); } /* reg_ptr -> array data */ OpRegImm(kOpAdd, reg_ptr, data_offset); /* at this point, reg_ptr points to array, 2 live temps */ - if ((size == kLong) || (size == kDouble)) { + if ((size == k64) || (size == kDouble)) { // TUNING: specific wide routine that can handle fp regs if (scale) { RegStorage r_new_index = AllocTemp(); @@ -590,7 +587,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, rl_src = LoadValueWide(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } @@ -598,7 +595,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } else { rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size); diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index 3e02faed55..7f4cd5e242 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -369,7 +369,7 @@ void MipsMir2Lir::FlushReg(RegStorage reg) { if (info->live && info->dirty) { info->dirty = false; int v_reg = mir_graph_->SRegToVReg(info->s_reg); - StoreBaseDisp(rs_rMIPS_SP, VRegOffset(v_reg), reg, kWord); + Store32Disp(rs_rMIPS_SP, VRegOffset(v_reg), reg); } } @@ -531,12 +531,14 @@ void MipsMir2Lir::FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free) { * there is a trap in the shadow. Allocate a temp register. */ RegStorage MipsMir2Lir::LoadHelper(ThreadOffset<4> offset) { + // NOTE: native pointer. LoadWordDisp(rs_rMIPS_SELF, offset.Int32Value(), rs_rT9); return rs_rT9; } LIR* MipsMir2Lir::CheckSuspendUsingLoad() { RegStorage tmp = AllocTemp(); + // NOTE: native pointer. LoadWordDisp(rs_rMIPS_SELF, Thread::ThreadSuspendTriggerOffset<4>().Int32Value(), tmp); LIR *inst = LoadWordDisp(tmp, 0, tmp); FreeTemp(tmp); @@ -553,7 +555,7 @@ void MipsMir2Lir::SpillCoreRegs() { for (int reg = 0; mask; mask >>= 1, reg++) { if (mask & 0x1) { offset -= 4; - StoreWordDisp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg)); + Store32Disp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg)); } } } @@ -567,7 +569,7 @@ void MipsMir2Lir::UnSpillCoreRegs() { for (int reg = 0; mask; mask >>= 1, reg++) { if (mask & 0x1) { offset -= 4; - LoadWordDisp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg)); + Load32Disp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg)); } } OpRegImm(kOpAdd, rs_rSP, frame_size_); diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index c959510025..a865430d6c 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -357,11 +357,11 @@ LIR* MipsMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor if (MIPS_FPREG(r_dest.GetReg())) { DCHECK(MIPS_SINGLEREG(r_dest.GetReg())); - DCHECK((size == kWord) || (size == kSingle)); + DCHECK((size == k32) || (size == kSingle) || (size == kReference)); size = kSingle; } else { if (size == kSingle) - size = kWord; + size = k32; } if (!scale) { @@ -375,7 +375,8 @@ LIR* MipsMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor case kSingle: opcode = kMipsFlwc1; break; - case kWord: + case k32: + case kReference: opcode = kMipsLw; break; case kUnsignedHalf: @@ -408,11 +409,11 @@ LIR* MipsMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto if (MIPS_FPREG(r_src.GetReg())) { DCHECK(MIPS_SINGLEREG(r_src.GetReg())); - DCHECK((size == kWord) || (size == kSingle)); + DCHECK((size == k32) || (size == kSingle) || (size == kReference)); size = kSingle; } else { if (size == kSingle) - size = kWord; + size = k32; } if (!scale) { @@ -426,7 +427,8 @@ LIR* MipsMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto case kSingle: opcode = kMipsFswc1; break; - case kWord: + case k32: + case kReference: opcode = kMipsSw; break; case kUnsignedHalf: @@ -463,7 +465,7 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora bool pair = false; switch (size) { - case kLong: + case k64: case kDouble: pair = true; opcode = kMipsLw; @@ -481,8 +483,9 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora short_form = IS_SIMM16_2WORD(displacement); DCHECK_EQ((displacement & 0x3), 0); break; - case kWord: + case k32: case kSingle: + case kReference: opcode = kMipsLw; if (MIPS_FPREG(r_dest.GetReg())) { opcode = kMipsFlwc1; @@ -544,13 +547,17 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora LIR* MipsMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size, int s_reg) { + // TODO: base this on target. + if (size == kWord) { + size = k32; + } return LoadBaseDispBody(r_base, displacement, r_dest, RegStorage::InvalidReg(), size, s_reg); } LIR* MipsMir2Lir::LoadBaseDispWide(RegStorage r_base, int displacement, RegStorage r_dest, int s_reg) { - return LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), r_dest.GetHigh(), kLong, s_reg); + return LoadBaseDispBody(r_base, displacement, r_dest.GetLow(), r_dest.GetHigh(), k64, s_reg); } LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, @@ -563,7 +570,7 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, bool pair = false; switch (size) { - case kLong: + case k64: case kDouble: pair = true; opcode = kMipsSw; @@ -580,8 +587,9 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, short_form = IS_SIMM16_2WORD(displacement); DCHECK_EQ((displacement & 0x3), 0); break; - case kWord: + case k32: case kSingle: + case kReference: opcode = kMipsSw; if (MIPS_FPREG(r_src.GetReg())) { opcode = kMipsFswc1; @@ -635,11 +643,15 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, LIR* MipsMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size) { + // TODO: base this on target. + if (size == kWord) { + size = k32; + } return StoreBaseDispBody(r_base, displacement, r_src, RegStorage::InvalidReg(), size); } LIR* MipsMir2Lir::StoreBaseDispWide(RegStorage r_base, int displacement, RegStorage r_src) { - return StoreBaseDispBody(r_base, displacement, r_src.GetLow(), r_src.GetHigh(), kLong); + return StoreBaseDispBody(r_base, displacement, r_src.GetLow(), r_src.GetHigh(), k64); } LIR* MipsMir2Lir::OpThreadMem(OpKind op, ThreadOffset<4> thread_offset) { diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 6fcdf70b12..b8ab609f31 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -63,14 +63,14 @@ RegStorage Mir2Lir::LoadArg(int in_position, bool wide) { } else { reg_arg_high = AllocTemp(); int offset_high = offset + sizeof(uint32_t); - LoadWordDisp(TargetReg(kSp), offset_high, reg_arg_high); + Load32Disp(TargetReg(kSp), offset_high, reg_arg_high); } } // If the low part is not in a register yet, we need to load it. if (!reg_arg_low.Valid()) { reg_arg_low = AllocTemp(); - LoadWordDisp(TargetReg(kSp), offset, reg_arg_low); + Load32Disp(TargetReg(kSp), offset, reg_arg_low); } if (wide) { @@ -96,7 +96,7 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) { if (reg.Valid()) { OpRegCopy(rl_dest.reg, reg); } else { - LoadWordDisp(TargetReg(kSp), offset, rl_dest.reg); + Load32Disp(TargetReg(kSp), offset, rl_dest.reg); } } else { RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position); @@ -107,10 +107,10 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) { } else if (reg_arg_low.Valid() && !reg_arg_high.Valid()) { OpRegCopy(rl_dest.reg, reg_arg_low); int offset_high = offset + sizeof(uint32_t); - LoadWordDisp(TargetReg(kSp), offset_high, rl_dest.reg.GetHigh()); + Load32Disp(TargetReg(kSp), offset_high, rl_dest.reg.GetHigh()); } else if (!reg_arg_low.Valid() && reg_arg_high.Valid()) { OpRegCopy(rl_dest.reg.GetHigh(), reg_arg_high); - LoadWordDisp(TargetReg(kSp), offset, rl_dest.reg.GetLow()); + Load32Disp(TargetReg(kSp), offset, rl_dest.reg.GetLow()); } else { LoadBaseDispWide(TargetReg(kSp), offset, rl_dest.reg, INVALID_SREG); } @@ -137,7 +137,7 @@ bool Mir2Lir::GenSpecialIGet(MIR* mir, const InlineMethod& special) { if (wide) { LoadBaseDispWide(reg_obj, data.field_offset, rl_dest.reg, INVALID_SREG); } else { - LoadWordDisp(reg_obj, data.field_offset, rl_dest.reg); + Load32Disp(reg_obj, data.field_offset, rl_dest.reg); } if (data.is_volatile) { // Without context sensitive analysis, we must issue the most conservative barriers. @@ -175,7 +175,7 @@ bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) { if (wide) { StoreBaseDispWide(reg_obj, data.field_offset, reg_src); } else { - StoreBaseDisp(reg_obj, data.field_offset, reg_src, kWord); + Store32Disp(reg_obj, data.field_offset, reg_src); } if (data.is_volatile) { // A load might follow the volatile store so insert a StoreLoad barrier. @@ -449,7 +449,7 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list rl_src[0] = LoadValue(rl_src[0], kCoreReg); GenNullCheck(rl_src[0].reg, opt_flags); rl_result = EvalLoc(rl_dest, kCoreReg, true); - LoadWordDisp(rl_src[0].reg, len_offset, rl_result.reg); + Load32Disp(rl_src[0].reg, len_offset, rl_result.reg); MarkPossibleNullPointerException(opt_flags); StoreValue(rl_dest, rl_result); break; @@ -562,11 +562,13 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list } case Instruction::AGET_WIDE: - GenArrayGet(opt_flags, kLong, rl_src[0], rl_src[1], rl_dest, 3); + GenArrayGet(opt_flags, k64, rl_src[0], rl_src[1], rl_dest, 3); break; - case Instruction::AGET: case Instruction::AGET_OBJECT: - GenArrayGet(opt_flags, kWord, rl_src[0], rl_src[1], rl_dest, 2); + GenArrayGet(opt_flags, kReference, rl_src[0], rl_src[1], rl_dest, 2); + break; + case Instruction::AGET: + GenArrayGet(opt_flags, k32, rl_src[0], rl_src[1], rl_dest, 2); break; case Instruction::AGET_BOOLEAN: GenArrayGet(opt_flags, kUnsignedByte, rl_src[0], rl_src[1], rl_dest, 0); @@ -581,10 +583,10 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list GenArrayGet(opt_flags, kSignedHalf, rl_src[0], rl_src[1], rl_dest, 1); break; case Instruction::APUT_WIDE: - GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3, false); + GenArrayPut(opt_flags, k64, rl_src[1], rl_src[2], rl_src[0], 3, false); break; case Instruction::APUT: - GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, false); + GenArrayPut(opt_flags, k32, rl_src[1], rl_src[2], rl_src[0], 2, false); break; case Instruction::APUT_OBJECT: { bool is_null = mir_graph_->IsConstantNullRef(rl_src[0]); @@ -597,7 +599,7 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list if (is_null || is_safe) { // Store of constant null doesn't require an assignability test and can be generated inline // without fixed register usage or a card mark. - GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, !is_null); + GenArrayPut(opt_flags, kReference, rl_src[1], rl_src[2], rl_src[0], 2, !is_null); } else { GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0]); } @@ -613,15 +615,15 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list break; case Instruction::IGET_OBJECT: - GenIGet(mir, opt_flags, kWord, rl_dest, rl_src[0], false, true); + GenIGet(mir, opt_flags, kReference, rl_dest, rl_src[0], false, true); break; case Instruction::IGET_WIDE: - GenIGet(mir, opt_flags, kLong, rl_dest, rl_src[0], true, false); + GenIGet(mir, opt_flags, k64, rl_dest, rl_src[0], true, false); break; case Instruction::IGET: - GenIGet(mir, opt_flags, kWord, rl_dest, rl_src[0], false, false); + GenIGet(mir, opt_flags, k32, rl_dest, rl_src[0], false, false); break; case Instruction::IGET_CHAR: @@ -638,15 +640,15 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list break; case Instruction::IPUT_WIDE: - GenIPut(mir, opt_flags, kLong, rl_src[0], rl_src[1], true, false); + GenIPut(mir, opt_flags, k64, rl_src[0], rl_src[1], true, false); break; case Instruction::IPUT_OBJECT: - GenIPut(mir, opt_flags, kWord, rl_src[0], rl_src[1], false, true); + GenIPut(mir, opt_flags, kReference, rl_src[0], rl_src[1], false, true); break; case Instruction::IPUT: - GenIPut(mir, opt_flags, kWord, rl_src[0], rl_src[1], false, false); + GenIPut(mir, opt_flags, k32, rl_src[0], rl_src[1], false, false); break; case Instruction::IPUT_BOOLEAN: @@ -988,6 +990,9 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { ResetRegPool(); if (cu_->disable_opt & (1 << kTrackLiveTemps)) { ClobberAllRegs(); + // Reset temp allocation to minimize differences when A/B testing. + reg_pool_->next_core_reg = 0; + reg_pool_->next_fp_reg = 0; } if (cu_->disable_opt & (1 << kSuppressLoads)) { @@ -1097,8 +1102,6 @@ void Mir2Lir::MethodMIR2LIR() { cu_->NewTimingSplit("Launchpads"); HandleSuspendLaunchPads(); - - HandleThrowLaunchPads(); } // diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 65910e9eb8..2b6d78b35a 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -109,6 +109,11 @@ typedef uint32_t CodeOffset; // Native code offset in bytes. #define REG_USE23 (REG_USE2 | REG_USE3) #define REG_USE123 (REG_USE1 | REG_USE2 | REG_USE3) +// TODO: #includes need a cleanup +#ifndef INVALID_SREG +#define INVALID_SREG (-1) +#endif + struct BasicBlock; struct CallInfo; struct CompilationUnit; @@ -554,12 +559,11 @@ class Mir2Lir : public Backend { RegisterInfo* GetRegInfo(int reg); // Shared by all targets - implemented in gen_common.cc. - void AddIntrinsicLaunchpad(CallInfo* info, LIR* branch, LIR* resume = nullptr); + void AddIntrinsicSlowPath(CallInfo* info, LIR* branch, LIR* resume = nullptr); bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, RegLocation rl_dest, int lit); bool HandleEasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit); void HandleSuspendLaunchPads(); - void HandleThrowLaunchPads(); void HandleSlowPaths(); void GenBarrier(); void GenDivZeroException(); @@ -567,6 +571,8 @@ class Mir2Lir : public Backend { void GenDivZeroCheck(ConditionCode c_code); // reg holds divisor. void GenDivZeroCheck(RegStorage reg); + void GenArrayBoundsCheck(RegStorage index, RegStorage length); + void GenArrayBoundsCheck(int32_t index, RegStorage length); LIR* GenNullCheck(RegStorage reg); void MarkPossibleNullPointerException(int opt_flags); void MarkPossibleStackOverflowException(); @@ -574,7 +580,6 @@ class Mir2Lir : public Backend { LIR* GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, ThrowKind kind); LIR* GenNullCheck(RegStorage m_reg, int opt_flags); LIR* GenExplicitNullCheck(RegStorage m_reg, int opt_flags); - LIR* GenRegRegCheck(ConditionCode c_code, RegStorage reg1, RegStorage reg2, ThrowKind kind); void GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1, RegLocation rl_src2, LIR* taken, LIR* fall_through); void GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, @@ -723,14 +728,42 @@ class Mir2Lir : public Backend { RegLocation LoadCurrMethod(); void LoadCurrMethodDirect(RegStorage r_tgt); LIR* LoadConstant(RegStorage r_dest, int value); - LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest); + // Natural word size. + LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) { + return LoadBaseDisp(r_base, displacement, r_dest, kWord, INVALID_SREG); + } + // Load 32 bits, regardless of target. + LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest) { + return LoadBaseDisp(r_base, displacement, r_dest, k32, INVALID_SREG); + } + // Load a reference at base + displacement and decompress into register. + LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest) { + return LoadBaseDisp(r_base, displacement, r_dest, kReference, INVALID_SREG); + } + // Load Dalvik value with 32-bit memory storage. If compressed object reference, decompress. RegLocation LoadValue(RegLocation rl_src, RegisterClass op_kind); + // Load Dalvik value with 64-bit memory storage. RegLocation LoadValueWide(RegLocation rl_src, RegisterClass op_kind); + // Load Dalvik value with 32-bit memory storage. If compressed object reference, decompress. void LoadValueDirect(RegLocation rl_src, RegStorage r_dest); + // Load Dalvik value with 32-bit memory storage. If compressed object reference, decompress. void LoadValueDirectFixed(RegLocation rl_src, RegStorage r_dest); + // Load Dalvik value with 64-bit memory storage. void LoadValueDirectWide(RegLocation rl_src, RegStorage r_dest); + // Load Dalvik value with 64-bit memory storage. void LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest); - LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src); + // Store an item of natural word size. + LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) { + return StoreBaseDisp(r_base, displacement, r_src, kWord); + } + // Store an uncompressed reference into a compressed 32-bit container. + LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src) { + return StoreBaseDisp(r_base, displacement, r_src, kReference); + } + // Store 32 bits, regardless of target. + LIR* Store32Disp(RegStorage r_base, int displacement, RegStorage r_src) { + return StoreBaseDisp(r_base, displacement, r_src, k32); + } /** * @brief Used to do the final store in the destination as per bytecode semantics. @@ -778,6 +811,8 @@ class Mir2Lir : public Backend { bool MethodBlockCodeGen(BasicBlock* bb); bool SpecialMIR2LIR(const InlineMethod& special); void MethodMIR2LIR(); + // Update LIR for verbose listings. + void UpdateLIROffsets(); /* * @brief Load the address of the dex method into the register. @@ -933,8 +968,6 @@ class Mir2Lir : public Backend { RegLocation rl_src2) = 0; virtual void GenXorLong(Instruction::Code, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; - virtual LIR* GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, - int offset, ThrowKind kind) = 0; virtual RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div) = 0; virtual RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, @@ -1019,7 +1052,7 @@ class Mir2Lir : public Backend { virtual LIR* OpMem(OpKind op, RegStorage r_base, int disp) = 0; virtual LIR* OpPcRelLoad(RegStorage reg, LIR* target) = 0; virtual LIR* OpReg(OpKind op, RegStorage r_dest_src) = 0; - virtual LIR* OpRegCopy(RegStorage r_dest, RegStorage r_src) = 0; + virtual void OpRegCopy(RegStorage r_dest, RegStorage r_src) = 0; virtual LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) = 0; virtual LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value) = 0; virtual LIR* OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset) = 0; @@ -1227,6 +1260,10 @@ class Mir2Lir : public Backend { void AddDivZeroCheckSlowPath(LIR* branch); + // Copy arg0 and arg1 to kArg0 and kArg1 safely, possibly using + // kArg2 as temp. + void CopyToArgumentRegs(RegStorage arg0, RegStorage arg1); + public: // TODO: add accessors for these. LIR* literal_list_; // Constants. @@ -1240,7 +1277,6 @@ class Mir2Lir : public Backend { MIRGraph* const mir_graph_; GrowableArray<SwitchTable*> switch_tables_; GrowableArray<FillArrayData*> fill_array_data_; - GrowableArray<LIR*> throw_launchpads_; GrowableArray<LIR*> suspend_launchpads_; GrowableArray<RegisterInfo*> tempreg_info_; GrowableArray<RegisterInfo*> reginfo_map_; diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 729b30d621..00831099fc 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -239,7 +239,7 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { // mov esp, ebp // in case a signal comes in that's not using an alternate signal stack and the large frame may // have moved us outside of the reserved area at the end of the stack. - // cmp rX86_SP, fs:[stack_end_]; jcc throw_launchpad + // cmp rX86_SP, fs:[stack_end_]; jcc throw_slowpath OpRegThreadMem(kOpCmp, rX86_SP, Thread::StackEndOffset<4>()); LIR* branch = OpCondBranch(kCondUlt, nullptr); AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, frame_size_ - 4)); @@ -251,7 +251,8 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { // We have been asked to save the address of the method start for later use. setup_method_address_[0] = NewLIR1(kX86StartOfMethod, rX86_ARG0); int displacement = SRegOffset(base_of_code_->s_reg_low); - setup_method_address_[1] = StoreBaseDisp(rs_rX86_SP, displacement, rs_rX86_ARG0, kWord); + // Native pointer - must be natural word size. + setup_method_address_[1] = StoreWordDisp(rs_rX86_SP, displacement, rs_rX86_ARG0); } FreeTemp(rX86_ARG0); diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 0b9823d667..760290cabe 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -127,15 +127,13 @@ class X86Mir2Lir FINAL : public Mir2Lir { RegLocation rl_src2); void GenXorLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - LIR* GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, int offset, - ThrowKind kind); - LIR* GenMemImmedCheck(ConditionCode c_code, RegStorage base, int offset, int check_value, - ThrowKind kind); // TODO: collapse reg_lo, reg_hi RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenDivZeroCheckWide(RegStorage reg); + void GenArrayBoundsCheck(RegStorage index, RegStorage array_base, int32_t len_offset); + void GenArrayBoundsCheck(int32_t index, RegStorage array_base, int32_t len_offset); void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); void GenExitSequence(); void GenSpecialExitSequence(); @@ -234,7 +232,7 @@ class X86Mir2Lir FINAL : public Mir2Lir { LIR* OpMem(OpKind op, RegStorage r_base, int disp); LIR* OpPcRelLoad(RegStorage reg, LIR* target); LIR* OpReg(OpKind op, RegStorage r_dest_src); - LIR* OpRegCopy(RegStorage r_dest, RegStorage r_src); + void OpRegCopy(RegStorage r_dest, RegStorage r_src); LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src); LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value); LIR* OpRegMem(OpKind op, RegStorage r_dest, RegStorage r_base, int offset); diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc index ee5387f050..f7b0c9d892 100644 --- a/compiler/dex/quick/x86/fp_x86.cc +++ b/compiler/dex/quick/x86/fp_x86.cc @@ -193,7 +193,7 @@ void X86Mir2Lir::GenLongToFP(RegLocation rl_dest, RegLocation rl_src, bool is_do } else { rl_result = EvalLoc(rl_dest, kFPReg, true); - LoadWordDisp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg); + Load32Disp(TargetReg(kSp), dest_v_reg_offset, rl_result.reg); StoreFinalValue(rl_dest, rl_result); } diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 4ffb9a4c38..3bff4976bd 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -24,34 +24,6 @@ namespace art { /* - * Perform register memory operation. - */ -LIR* X86Mir2Lir::GenRegMemCheck(ConditionCode c_code, RegStorage reg1, RegStorage base, - int offset, ThrowKind kind) { - LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, - current_dalvik_offset_, reg1.GetReg(), base.GetReg(), offset); - OpRegMem(kOpCmp, reg1, base, offset); - LIR* branch = OpCondBranch(c_code, tgt); - // Remember branch target - will process later - throw_launchpads_.Insert(tgt); - return branch; -} - -/* - * Perform a compare of memory to immediate value - */ -LIR* X86Mir2Lir::GenMemImmedCheck(ConditionCode c_code, RegStorage base, int offset, - int check_value, ThrowKind kind) { - LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, - current_dalvik_offset_, base.GetReg(), check_value, 0); - NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base.GetReg(), offset, check_value); - LIR* branch = OpCondBranch(c_code, tgt); - // Remember branch target - will process later - throw_launchpads_.Insert(tgt); - return branch; -} - -/* * Compare two 64-bit values * x = y return 0 * x < y return -1 @@ -144,52 +116,55 @@ LIR* X86Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) { return res; } -LIR* X86Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { - LIR *res = OpRegCopyNoInsert(r_dest, r_src); - AppendLIR(res); - return res; +void X86Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { + if (r_dest != r_src) { + LIR *res = OpRegCopyNoInsert(r_dest, r_src); + AppendLIR(res); + } } void X86Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { - // FIXME: handle k64BitSolo when we start using them. - DCHECK(r_dest.IsPair()); - DCHECK(r_src.IsPair()); - bool dest_fp = X86_FPREG(r_dest.GetLowReg()); - bool src_fp = X86_FPREG(r_src.GetLowReg()); - if (dest_fp) { - if (src_fp) { - // TODO: we ought to handle this case here - reserve OpRegCopy for 32-bit copies. - OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())), - RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg()))); - } else { - // TODO: Prevent this from happening in the code. The result is often - // unused or could have been loaded more easily from memory. - NewLIR2(kX86MovdxrRR, r_dest.GetLowReg(), r_src.GetLowReg()); - RegStorage r_tmp = AllocTempDouble(); - NewLIR2(kX86MovdxrRR, r_tmp.GetLowReg(), r_src.GetHighReg()); - NewLIR2(kX86PunpckldqRR, r_dest.GetLowReg(), r_tmp.GetLowReg()); - FreeTemp(r_tmp); - } - } else { - if (src_fp) { - NewLIR2(kX86MovdrxRR, r_dest.GetLowReg(), r_src.GetLowReg()); - NewLIR2(kX86PsrlqRI, r_src.GetLowReg(), 32); - NewLIR2(kX86MovdrxRR, r_dest.GetHighReg(), r_src.GetLowReg()); + if (r_dest != r_src) { + // FIXME: handle k64BitSolo when we start using them. + DCHECK(r_dest.IsPair()); + DCHECK(r_src.IsPair()); + bool dest_fp = X86_FPREG(r_dest.GetLowReg()); + bool src_fp = X86_FPREG(r_src.GetLowReg()); + if (dest_fp) { + if (src_fp) { + // TODO: we ought to handle this case here - reserve OpRegCopy for 32-bit copies. + OpRegCopy(RegStorage::Solo64(S2d(r_dest.GetLowReg(), r_dest.GetHighReg())), + RegStorage::Solo64(S2d(r_src.GetLowReg(), r_src.GetHighReg()))); + } else { + // TODO: Prevent this from happening in the code. The result is often + // unused or could have been loaded more easily from memory. + NewLIR2(kX86MovdxrRR, r_dest.GetLowReg(), r_src.GetLowReg()); + RegStorage r_tmp = AllocTempDouble(); + NewLIR2(kX86MovdxrRR, r_tmp.GetLowReg(), r_src.GetHighReg()); + NewLIR2(kX86PunpckldqRR, r_dest.GetLowReg(), r_tmp.GetLowReg()); + FreeTemp(r_tmp); + } } else { - // Handle overlap - if (r_src.GetHighReg() == r_dest.GetLowReg() && r_src.GetLowReg() == r_dest.GetHighReg()) { - // Deal with cycles. - RegStorage temp_reg = AllocTemp(); - OpRegCopy(temp_reg, r_dest.GetHigh()); - OpRegCopy(r_dest.GetHigh(), r_dest.GetLow()); - OpRegCopy(r_dest.GetLow(), temp_reg); - FreeTemp(temp_reg); - } else if (r_src.GetHighReg() == r_dest.GetLowReg()) { - OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); - OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + if (src_fp) { + NewLIR2(kX86MovdrxRR, r_dest.GetLowReg(), r_src.GetLowReg()); + NewLIR2(kX86PsrlqRI, r_src.GetLowReg(), 32); + NewLIR2(kX86MovdrxRR, r_dest.GetHighReg(), r_src.GetLowReg()); } else { - OpRegCopy(r_dest.GetLow(), r_src.GetLow()); - OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + // Handle overlap + if (r_src.GetHighReg() == r_dest.GetLowReg() && r_src.GetLowReg() == r_dest.GetHighReg()) { + // Deal with cycles. + RegStorage temp_reg = AllocTemp(); + OpRegCopy(temp_reg, r_dest.GetHigh()); + OpRegCopy(r_dest.GetHigh(), r_dest.GetLow()); + OpRegCopy(r_dest.GetLow(), temp_reg); + FreeTemp(temp_reg); + } else if (r_src.GetHighReg() == r_dest.GetLowReg()) { + OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + } else { + OpRegCopy(r_dest.GetLow(), r_src.GetLow()); + OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); + } } } } @@ -704,15 +679,15 @@ bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) { bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { RegLocation rl_src_address = info->args[0]; // long address rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1] - RegLocation rl_dest = size == kLong ? InlineTargetWide(info) : InlineTarget(info); + RegLocation rl_dest = size == k64 ? InlineTargetWide(info) : InlineTarget(info); RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - if (size == kLong) { + if (size == k64) { // Unaligned access is allowed on x86. LoadBaseDispWide(rl_address.reg, 0, rl_result.reg, INVALID_SREG); StoreValueWide(rl_dest, rl_result); } else { - DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + DCHECK(size == kSignedByte || size == kSignedHalf || size == k32); // Unaligned access is allowed on x86. LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, INVALID_SREG); StoreValue(rl_dest, rl_result); @@ -725,12 +700,12 @@ bool X86Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1] RegLocation rl_src_value = info->args[2]; // [size] value RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); - if (size == kLong) { + if (size == k64) { // Unaligned access is allowed on x86. RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg); StoreBaseDispWide(rl_address.reg, 0, rl_value.reg); } else { - DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + DCHECK(size == kSignedByte || size == kSignedHalf || size == k32); // Unaligned access is allowed on x86. RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size); @@ -780,6 +755,7 @@ bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { int srcObjSp = IsInReg(this, rl_src_obj, rs_rSI) ? 0 : (IsInReg(this, rl_src_obj, rs_rDI) ? 4 : (SRegOffset(rl_src_obj.s_reg_low) + push_offset)); + // FIXME: needs 64-bit update. LoadWordDisp(TargetReg(kSp), srcObjSp, rs_rDI); int srcOffsetSp = IsInReg(this, rl_src_offset, rs_rSI) ? 0 : (IsInReg(this, rl_src_offset, rs_rDI) ? 4 @@ -891,6 +867,86 @@ void X86Mir2Lir::GenDivZeroCheckWide(RegStorage reg) { FreeTemp(t_reg); } +void X86Mir2Lir::GenArrayBoundsCheck(RegStorage index, + RegStorage array_base, + int len_offset) { + class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, + RegStorage index, RegStorage array_base, int32_t len_offset) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch), + index_(index), array_base_(array_base), len_offset_(len_offset) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + + RegStorage new_index = index_; + // Move index out of kArg1, either directly to kArg0, or to kArg2. + if (index_.GetReg() == m2l_->TargetReg(kArg1).GetReg()) { + if (array_base_.GetReg() == m2l_->TargetReg(kArg0).GetReg()) { + m2l_->OpRegCopy(m2l_->TargetReg(kArg2), index_); + new_index = m2l_->TargetReg(kArg2); + } else { + m2l_->OpRegCopy(m2l_->TargetReg(kArg0), index_); + new_index = m2l_->TargetReg(kArg0); + } + } + // Load array length to kArg1. + m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1), array_base_, len_offset_); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), + new_index, m2l_->TargetReg(kArg1), true); + } + + private: + const RegStorage index_; + const RegStorage array_base_; + const int32_t len_offset_; + }; + + OpRegMem(kOpCmp, index, array_base, len_offset); + LIR* branch = OpCondBranch(kCondUge, nullptr); + AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, + index, array_base, len_offset)); +} + +void X86Mir2Lir::GenArrayBoundsCheck(int32_t index, + RegStorage array_base, + int32_t len_offset) { + class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, + int32_t index, RegStorage array_base, int32_t len_offset) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch), + index_(index), array_base_(array_base), len_offset_(len_offset) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + + // Load array length to kArg1. + m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1), array_base_, len_offset_); + m2l_->LoadConstant(m2l_->TargetReg(kArg0), index_); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), + m2l_->TargetReg(kArg0), m2l_->TargetReg(kArg1), true); + } + + private: + const int32_t index_; + const RegStorage array_base_; + const int32_t len_offset_; + }; + + NewLIR3(IS_SIMM8(index) ? kX86Cmp32MI8 : kX86Cmp32MI, array_base.GetReg(), len_offset, index); + LIR* branch = OpCondBranch(kCondLs, nullptr); + AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, + index, array_base, len_offset)); +} + // Test suspend flag, return target of taken suspend branch LIR* X86Mir2Lir::OpTestSuspend(LIR* target) { OpTlsCmp(Thread::ThreadFlagsOffset<4>(), 0); @@ -944,7 +1000,7 @@ void X86Mir2Lir::GenImulMemImm(RegStorage dest, int sreg, int displacement, int NewLIR2(kX86Xor32RR, dest.GetReg(), dest.GetReg()); break; case 1: - LoadBaseDisp(rs_rX86_SP, displacement, dest, kWord, sreg); + LoadBaseDisp(rs_rX86_SP, displacement, dest, k32, sreg); break; default: m = NewLIR4(IS_SIMM8(val) ? kX86Imul32RMI8 : kX86Imul32RMI, dest.GetReg(), rX86_SP, @@ -1050,7 +1106,7 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation NewLIR2(kX86Mov32RR, r1, rl_src1.reg.GetHighReg()); } else { LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src1.s_reg_low) + HIWORD_OFFSET, rs_r1, - kWord, GetSRegHi(rl_src1.s_reg_low)); + k32, GetSRegHi(rl_src1.s_reg_low)); } if (is_square) { @@ -1073,7 +1129,7 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation NewLIR2(kX86Mov32RR, r0, rl_src2.reg.GetHighReg()); } else { LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, rs_r0, - kWord, GetSRegHi(rl_src2.s_reg_low)); + k32, GetSRegHi(rl_src2.s_reg_low)); } // EAX <- EAX * 1L (2H * 1L) @@ -1105,7 +1161,7 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation NewLIR2(kX86Mov32RR, r0, rl_src2.reg.GetLowReg()); } else { LoadBaseDisp(rs_rX86_SP, SRegOffset(rl_src2.s_reg_low) + LOWORD_OFFSET, rs_r0, - kWord, rl_src2.s_reg_low); + k32, rl_src2.s_reg_low); } // EDX:EAX <- 2L * 1L (double precision) @@ -1325,7 +1381,7 @@ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_array = LoadValue(rl_array, kCoreReg); int data_offset; - if (size == kLong || size == kDouble) { + if (size == k64 || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); @@ -1348,14 +1404,13 @@ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) { if (constant_index) { - GenMemImmedCheck(kCondLs, rl_array.reg, len_offset, - constant_index_value, kThrowConstantArrayBounds); + GenArrayBoundsCheck(constant_index_value, rl_array.reg, len_offset); } else { - GenRegMemCheck(kCondUge, rl_index.reg, rl_array.reg, len_offset, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, rl_array.reg, len_offset); } } rl_result = EvalLoc(rl_dest, reg_class, true); - if ((size == kLong) || (size == kDouble)) { + if ((size == k64) || (size == kDouble)) { LoadBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, rl_result.reg.GetLow(), rl_result.reg.GetHigh(), size, INVALID_SREG); StoreValueWide(rl_dest, rl_result); @@ -1376,7 +1431,7 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; - if (size == kLong || size == kDouble) { + if (size == k64 || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); @@ -1400,13 +1455,12 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) { if (constant_index) { - GenMemImmedCheck(kCondLs, rl_array.reg, len_offset, - constant_index_value, kThrowConstantArrayBounds); + GenArrayBoundsCheck(constant_index_value, rl_array.reg, len_offset); } else { - GenRegMemCheck(kCondUge, rl_index.reg, rl_array.reg, len_offset, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, rl_array.reg, len_offset); } } - if ((size == kLong) || (size == kDouble)) { + if ((size == k64) || (size == kDouble)) { rl_src = LoadValueWide(rl_src, reg_class); } else { rl_src = LoadValue(rl_src, reg_class); @@ -1793,22 +1847,22 @@ void X86Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, if (rl_method.location == kLocPhysReg) { if (use_declaring_class) { - LoadWordDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), + LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class); } else { - LoadWordDisp(rl_method.reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), + LoadRefDisp(rl_method.reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), check_class); - LoadWordDisp(check_class, offset_of_type, check_class); + LoadRefDisp(check_class, offset_of_type, check_class); } } else { LoadCurrMethodDirect(check_class); if (use_declaring_class) { - LoadWordDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), + LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class); } else { - LoadWordDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), + LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), check_class); - LoadWordDisp(check_class, offset_of_type, check_class); + LoadRefDisp(check_class, offset_of_type, check_class); } } @@ -1849,17 +1903,17 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k LoadValueDirectFixed(rl_src, TargetReg(kArg0)); } else if (use_declaring_class) { LoadValueDirectFixed(rl_src, TargetReg(kArg0)); - LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), + LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DeclaringClassOffset().Int32Value(), class_reg); } else { // Load dex cache entry into class_reg (kArg2). LoadValueDirectFixed(rl_src, TargetReg(kArg0)); - LoadWordDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), + LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), class_reg); int32_t offset_of_type = mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*) * type_idx); - LoadWordDisp(class_reg, offset_of_type, class_reg); + LoadRefDisp(class_reg, offset_of_type, class_reg); if (!can_assume_type_is_in_dex_cache) { // Need to test presence of type in dex cache at runtime. LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL); @@ -1883,7 +1937,7 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k /* Load object->klass_. */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); - LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); + LoadRefDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class. */ LIR* branchover = nullptr; if (type_known_final) { @@ -2056,6 +2110,8 @@ void X86Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, // Can we do this directly into memory? rl_result = UpdateLoc(rl_dest); if (rl_result.location == kLocPhysReg) { + // Ensure res is in a core reg + rl_result = EvalLoc(rl_dest, kCoreReg, true); // Can we do this from memory directly? rl_rhs = UpdateLoc(rl_rhs); if (rl_rhs.location != kLocPhysReg) { diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 5a8ad7a2b4..3e3fa72150 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -369,12 +369,13 @@ void X86Mir2Lir::FlushRegWide(RegStorage reg) { } void X86Mir2Lir::FlushReg(RegStorage reg) { + // FIXME: need to handle 32 bits in 64-bit register as well as wide values held in single reg. DCHECK(!reg.IsPair()); RegisterInfo* info = GetRegInfo(reg.GetReg()); if (info->live && info->dirty) { info->dirty = false; int v_reg = mir_graph_->SRegToVReg(info->s_reg); - StoreBaseDisp(rs_rX86_SP, VRegOffset(v_reg), reg, kWord); + StoreBaseDisp(rs_rX86_SP, VRegOffset(v_reg), reg, k32); } } @@ -1033,14 +1034,14 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked. // Does the character fit in 16 bits? - LIR* launchpad_branch = nullptr; + LIR* slowpath_branch = nullptr; if (rl_char.is_const) { // We need the value in EAX. LoadConstantNoClobber(rs_rAX, char_value); } else { // Character is not a constant; compare at runtime. LoadValueDirectFixed(rl_char, rs_rAX); - launchpad_branch = OpCmpImmBranch(kCondGt, rs_rAX, 0xFFFF, nullptr); + slowpath_branch = OpCmpImmBranch(kCondGt, rs_rAX, 0xFFFF, nullptr); } // From here down, we know that we are looking for a char that fits in 16 bits. @@ -1061,7 +1062,7 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { NewLIR1(kX86Push32R, rDI); // Compute the number of words to search in to rCX. - LoadWordDisp(rs_rDX, count_offset, rs_rCX); + Load32Disp(rs_rDX, count_offset, rs_rCX); LIR *length_compare = nullptr; int start_value = 0; bool is_index_on_stack = false; @@ -1101,7 +1102,7 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { } else { // Load the start index from stack, remembering that we pushed EDI. int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t); - LoadWordDisp(rs_rX86_SP, displacement, rs_rBX); + Load32Disp(rs_rX86_SP, displacement, rs_rBX); OpRegReg(kOpXor, rs_rDI, rs_rDI); OpRegReg(kOpCmp, rs_rBX, rs_rDI); OpCondRegReg(kOpCmov, kCondLt, rs_rBX, rs_rDI); @@ -1120,8 +1121,8 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { // Load the address of the string into EBX. // The string starts at VALUE(String) + 2 * OFFSET(String) + DATA_OFFSET. - LoadWordDisp(rs_rDX, value_offset, rs_rDI); - LoadWordDisp(rs_rDX, offset_offset, rs_rBX); + Load32Disp(rs_rDX, value_offset, rs_rDI); + Load32Disp(rs_rDX, offset_offset, rs_rBX); OpLea(rs_rBX, rs_rDI, rs_rBX, 1, data_offset); // Now compute into EDI where the search will start. @@ -1167,9 +1168,9 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { NewLIR1(kX86Pop32R, rDI); // Out of line code returns here. - if (launchpad_branch != nullptr) { + if (slowpath_branch != nullptr) { LIR *return_point = NewLIR0(kPseudoTargetLabel); - AddIntrinsicLaunchpad(info, launchpad_branch, return_point); + AddIntrinsicSlowPath(info, slowpath_branch, return_point); } StoreValue(rl_dest, rl_return); diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index e9faa7ff53..4d45055927 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -426,7 +426,8 @@ LIR* X86Mir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, RegStorage t_reg = AllocTemp(); OpRegCopy(t_reg, r_src1); OpRegReg(op, t_reg, r_src2); - LIR* res = OpRegCopy(r_dest, t_reg); + LIR* res = OpRegCopyNoInsert(r_dest, t_reg); + AppendLIR(res); FreeTemp(t_reg); return res; } @@ -554,7 +555,7 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int bool is64bit = false; X86OpCode opcode = kX86Nop; switch (size) { - case kLong: + case k64: case kDouble: // TODO: use regstorage attributes here. is64bit = true; @@ -567,8 +568,9 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int // TODO: double store is to unaligned address DCHECK_EQ((displacement & 0x3), 0); break; - case kWord: + case k32: case kSingle: + case kReference: // TODO: update for reference decompression on 64-bit targets. opcode = is_array ? kX86Mov32RA : kX86Mov32RM; if (X86_FPREG(r_dest.GetReg())) { opcode = is_array ? kX86MovssRA : kX86MovssRM; @@ -669,6 +671,10 @@ LIR* X86Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStora LIR* X86Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size, int s_reg) { + // TODO: base this on target. + if (size == kWord) { + size = k32; + } return LoadBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_dest, RegStorage::InvalidReg(), size, s_reg); } @@ -676,7 +682,7 @@ LIR* X86Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, LIR* X86Mir2Lir::LoadBaseDispWide(RegStorage r_base, int displacement, RegStorage r_dest, int s_reg) { return LoadBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, - r_dest.GetLow(), r_dest.GetHigh(), kLong, s_reg); + r_dest.GetLow(), r_dest.GetHigh(), k64, s_reg); } LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, @@ -690,7 +696,7 @@ LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int bool is64bit = false; X86OpCode opcode = kX86Nop; switch (size) { - case kLong: + case k64: case kDouble: is64bit = true; if (X86_FPREG(r_src.GetReg())) { @@ -702,8 +708,9 @@ LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int // TODO: double store is to unaligned address DCHECK_EQ((displacement & 0x3), 0); break; - case kWord: + case k32: case kSingle: + case kReference: opcode = is_array ? kX86Mov32AR : kX86Mov32MR; if (X86_FPREG(r_src.GetReg())) { opcode = is_array ? kX86MovssAR : kX86MovssMR; @@ -763,13 +770,17 @@ LIR* X86Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size) { - return StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src, - RegStorage::InvalidReg(), size, INVALID_SREG); + // TODO: base this on target. + if (size == kWord) { + size = k32; + } + return StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src, + RegStorage::InvalidReg(), size, INVALID_SREG); } LIR* X86Mir2Lir::StoreBaseDispWide(RegStorage r_base, int displacement, RegStorage r_src) { return StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, - r_src.GetLow(), r_src.GetHigh(), kLong, INVALID_SREG); + r_src.GetLow(), r_src.GetHigh(), k64, INVALID_SREG); } /* diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index 8175c35077..864dadc963 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -50,7 +50,11 @@ TEST_F(ElfWriterTest, dlsym) { CHECK(host_dir != NULL); elf_filename = StringPrintf("%s/framework/core.oat", host_dir); } else { +#ifdef __LP64__ + elf_filename = "/data/art-test64/core.oat"; +#else elf_filename = "/data/art-test/core.oat"; +#endif } LOG(INFO) << "elf_filename=" << elf_filename; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 05d6693f70..7c5741bb23 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -164,7 +164,7 @@ TEST_F(ImageTest, WriteRead) { EXPECT_TRUE(reinterpret_cast<byte*>(klass) >= image_end || reinterpret_cast<byte*>(klass) < image_begin) << descriptor; } - EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord())); + EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false))); } } diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index f76587a26e..c35d4007b5 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -179,7 +179,7 @@ void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { image_bitmap_->Set(obj); // Before we stomp over the lock word, save the hash code for later. Monitor::Deflate(Thread::Current(), object);; - LockWord lw(object->GetLockWord()); + LockWord lw(object->GetLockWord(false)); switch (lw.GetState()) { case LockWord::kFatLocked: { LOG(FATAL) << "Fat locked object " << obj << " found during object copy"; @@ -199,7 +199,7 @@ void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { LOG(FATAL) << "Unreachable."; break; } - object->SetLockWord(LockWord::FromForwardingAddress(offset)); + object->SetLockWord(LockWord::FromForwardingAddress(offset), false); DCHECK(IsImageOffsetAssigned(object)); } @@ -212,13 +212,13 @@ void ImageWriter::AssignImageOffset(mirror::Object* object) { bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const { DCHECK(object != nullptr); - return object->GetLockWord().GetState() == LockWord::kForwardingAddress; + return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress; } size_t ImageWriter::GetImageOffset(mirror::Object* object) const { DCHECK(object != nullptr); DCHECK(IsImageOffsetAssigned(object)); - LockWord lock_word = object->GetLockWord(); + LockWord lock_word = object->GetLockWord(false); size_t offset = lock_word.ForwardingAddress(); DCHECK_LT(offset, image_end_); return offset; @@ -555,15 +555,15 @@ void ImageWriter::CopyAndFixupObjects() heap->VisitObjects(CopyAndFixupObjectsCallback, this); // Fix up the object previously had hash codes. for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) { - hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second)); + hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second), false); } saved_hashes_.clear(); self->EndAssertNoThreadSuspension(old_cause); } void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { - DCHECK(obj != NULL); - DCHECK(arg != NULL); + DCHECK(obj != nullptr); + DCHECK(arg != nullptr); ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); // see GetLocalAddress for similar computation size_t offset = image_writer->GetImageOffset(obj); @@ -575,7 +575,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { Object* copy = reinterpret_cast<Object*>(dst); // Write in a hash code of objects which have inflated monitors or a hash code in their monitor // word. - copy->SetLockWord(LockWord()); + copy->SetLockWord(LockWord(), false); image_writer->FixupObject(obj, copy); } @@ -680,14 +680,6 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_)); } else { // Normal (non-abstract non-native) methods have various tables to relocate. - uint32_t mapping_table_off = orig->GetOatMappingTableOffset(); - const byte* mapping_table = GetOatAddress(mapping_table_off); - copy->SetMappingTable<kVerifyNone>(mapping_table); - - uint32_t vmap_table_offset = orig->GetOatVmapTableOffset(); - const byte* vmap_table = GetOatAddress(vmap_table_offset); - copy->SetVmapTable<kVerifyNone>(vmap_table); - uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset(); const byte* native_gc_map = GetOatAddress(native_gc_map_offset); copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map)); diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 9cfef12b26..b5d39232ca 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -155,19 +155,19 @@ TEST_F(OatTest, WriteRead) { SirtRef<mirror::ClassLoader> loader(soa.Self(), nullptr); mirror::Class* klass = class_linker->FindClass(soa.Self(), descriptor, loader); - UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(i)); - CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor; + const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(i); + CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class.GetStatus()) << descriptor; CHECK_EQ(kCompile ? OatClassType::kOatClassAllCompiled : OatClassType::kOatClassNoneCompiled, - oat_class->GetType()) << descriptor; + oat_class.GetType()) << descriptor; size_t method_index = 0; for (size_t i = 0; i < klass->NumDirectMethods(); i++, method_index++) { CheckMethod(klass->GetDirectMethod(i), - oat_class->GetOatMethod(method_index), dex_file); + oat_class.GetOatMethod(method_index), dex_file); } for (size_t i = 0; i < num_virtual_methods; i++, method_index++) { CheckMethod(klass->GetVirtualMethod(i), - oat_class->GetOatMethod(method_index), dex_file); + oat_class.GetOatMethod(method_index), dex_file); } } } @@ -176,7 +176,8 @@ TEST_F(OatTest, OatHeaderSizeCheck) { // If this test is failing and you have to update these constants, // it is time to update OatHeader::kOatVersion EXPECT_EQ(80U, sizeof(OatHeader)); - EXPECT_EQ(28U, sizeof(OatMethodOffsets)); + EXPECT_EQ(20U, sizeof(OatMethodOffsets)); + EXPECT_EQ(12U, sizeof(OatMethodHeader)); } TEST_F(OatTest, OatHeaderIsValid) { diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index dc66e9c108..bbc9c3e325 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -38,6 +38,14 @@ namespace art { +#define DCHECK_OFFSET() \ + DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \ + << "file_offset=" << file_offset << " relative_offset=" << relative_offset + +#define DCHECK_OFFSET_() \ + DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \ + << "file_offset=" << file_offset << " offset_=" << offset_ + OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, uint32_t image_file_location_oat_checksum, uintptr_t image_file_location_oat_begin, @@ -66,7 +74,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_quick_resolution_trampoline_(0), size_quick_to_interpreter_bridge_(0), size_trampoline_alignment_(0), - size_code_size_(0), + size_method_header_(0), size_code_(0), size_code_alignment_(0), size_mapping_table_(0), @@ -99,6 +107,10 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, offset = InitOatClasses(offset); } { + TimingLogger::ScopedSplit split("InitOatMaps", timings); + offset = InitOatMaps(offset); + } + { TimingLogger::ScopedSplit split("InitOatCode", timings); offset = InitOatCode(offset); } @@ -118,6 +130,605 @@ OatWriter::~OatWriter() { STLDeleteElements(&oat_classes_); } +struct OatWriter::GcMapDataAccess { + static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + return &compiled_method->GetGcMap(); + } + + static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE { + return oat_class->method_offsets_[method_offsets_index].gc_map_offset_; + } + + static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset) + ALWAYS_INLINE { + oat_class->method_offsets_[method_offsets_index].gc_map_offset_ = offset; + } + + static const char* Name() ALWAYS_INLINE { + return "GC map"; + } +}; + +struct OatWriter::MappingTableDataAccess { + static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + return &compiled_method->GetMappingTable(); + } + + static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE { + uint32_t offset = oat_class->method_headers_[method_offsets_index].mapping_table_offset_; + return offset == 0u ? 0u : + (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset; + } + + static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset) + ALWAYS_INLINE { + oat_class->method_headers_[method_offsets_index].mapping_table_offset_ = + (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset; + } + + static const char* Name() ALWAYS_INLINE { + return "mapping table"; + } +}; + +struct OatWriter::VmapTableDataAccess { + static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + return &compiled_method->GetVmapTable(); + } + + static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE { + uint32_t offset = oat_class->method_headers_[method_offsets_index].vmap_table_offset_; + return offset == 0u ? 0u : + (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset; + } + + static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset) + ALWAYS_INLINE { + oat_class->method_headers_[method_offsets_index].vmap_table_offset_ = + (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset; + } + + static const char* Name() ALWAYS_INLINE { + return "vmap table"; + } +}; + +class OatWriter::DexMethodVisitor { + public: + DexMethodVisitor(OatWriter* writer, size_t offset) + : writer_(writer), + offset_(offset), + dex_file_(nullptr), + class_def_index_(DexFile::kDexNoIndex) { + } + + virtual bool StartClass(const DexFile* dex_file, size_t class_def_index) { + DCHECK(dex_file_ == nullptr); + DCHECK_EQ(class_def_index_, DexFile::kDexNoIndex); + dex_file_ = dex_file; + class_def_index_ = class_def_index; + return true; + } + + virtual bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) = 0; + + virtual bool EndClass() { + if (kIsDebugBuild) { + dex_file_ = nullptr; + class_def_index_ = DexFile::kDexNoIndex; + } + return true; + } + + size_t GetOffset() const { + return offset_; + } + + protected: + virtual ~DexMethodVisitor() { } + + OatWriter* const writer_; + + // The offset is usually advanced for each visited method by the derived class. + size_t offset_; + + // The dex file and class def index are set in StartClass(). + const DexFile* dex_file_; + size_t class_def_index_; +}; + +class OatWriter::OatDexMethodVisitor : public DexMethodVisitor { + public: + OatDexMethodVisitor(OatWriter* writer, size_t offset) + : DexMethodVisitor(writer, offset), + oat_class_index_(0u), + method_offsets_index_(0u) { + } + + bool StartClass(const DexFile* dex_file, size_t class_def_index) { + DexMethodVisitor::StartClass(dex_file, class_def_index); + DCHECK_LT(oat_class_index_, writer_->oat_classes_.size()); + method_offsets_index_ = 0u; + return true; + } + + bool EndClass() { + ++oat_class_index_; + return DexMethodVisitor::EndClass(); + } + + protected: + size_t oat_class_index_; + size_t method_offsets_index_; +}; + +class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { + public: + InitOatClassesMethodVisitor(OatWriter* writer, size_t offset) + : DexMethodVisitor(writer, offset), + compiled_methods_(), + num_non_null_compiled_methods_(0u) { + compiled_methods_.reserve(256u); + } + + bool StartClass(const DexFile* dex_file, size_t class_def_index) { + DexMethodVisitor::StartClass(dex_file, class_def_index); + compiled_methods_.clear(); + num_non_null_compiled_methods_ = 0u; + return true; + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) { + // Fill in the compiled_methods_ array for methods that have a + // CompiledMethod. We track the number of non-null entries in + // num_non_null_compiled_methods_ since we only want to allocate + // OatMethodOffsets for the compiled methods. + uint32_t method_idx = it.GetMemberIndex(); + CompiledMethod* compiled_method = + writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx)); + compiled_methods_.push_back(compiled_method); + if (compiled_method != nullptr) { + ++num_non_null_compiled_methods_; + } + return true; + } + + bool EndClass() { + ClassReference class_ref(dex_file_, class_def_index_); + CompiledClass* compiled_class = writer_->compiler_driver_->GetCompiledClass(class_ref); + mirror::Class::Status status; + if (compiled_class != NULL) { + status = compiled_class->GetStatus(); + } else if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { + status = mirror::Class::kStatusError; + } else { + status = mirror::Class::kStatusNotReady; + } + + OatClass* oat_class = new OatClass(offset_, compiled_methods_, + num_non_null_compiled_methods_, status); + writer_->oat_classes_.push_back(oat_class); + offset_ += oat_class->SizeOf(); + return DexMethodVisitor::EndClass(); + } + + private: + std::vector<CompiledMethod*> compiled_methods_; + size_t num_non_null_compiled_methods_; +}; + +class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { + public: + InitCodeMethodVisitor(OatWriter* writer, size_t offset) + : OatDexMethodVisitor(writer, offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + if (compiled_method != nullptr) { + // Derived from CompiledMethod. + uint32_t quick_code_offset = 0; + uint32_t frame_size_in_bytes = kStackAlignment; + uint32_t core_spill_mask = 0; + uint32_t fp_spill_mask = 0; + + const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode(); + const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode(); + if (portable_code != nullptr) { + CHECK(quick_code == nullptr); + size_t oat_method_offsets_offset = + oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index); + compiled_method->AddOatdataOffsetToCompliledCodeOffset( + oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_)); + } else { + CHECK(quick_code != nullptr); + offset_ = compiled_method->AlignCode(offset_); + DCHECK_ALIGNED_PARAM(offset_, + GetInstructionSetAlignment(compiled_method->GetInstructionSet())); + uint32_t code_size = quick_code->size() * sizeof(uint8_t); + CHECK_NE(code_size, 0U); + uint32_t thumb_offset = compiled_method->CodeDelta(); + quick_code_offset = offset_ + sizeof(OatMethodHeader) + thumb_offset; + + std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation(); + if (cfi_info != nullptr) { + // Copy in the FDE, if present + const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo(); + if (fde != nullptr) { + // Copy the information into cfi_info and then fix the address in the new copy. + int cur_offset = cfi_info->size(); + cfi_info->insert(cfi_info->end(), fde->begin(), fde->end()); + + // Set the 'initial_location' field to address the start of the method. + uint32_t new_value = quick_code_offset - writer_->oat_header_->GetExecutableOffset(); + uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t); + (*cfi_info)[offset_to_update+0] = new_value; + (*cfi_info)[offset_to_update+1] = new_value >> 8; + (*cfi_info)[offset_to_update+2] = new_value >> 16; + (*cfi_info)[offset_to_update+3] = new_value >> 24; + std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, false); + writer_->method_info_.push_back(DebugInfo(name, new_value, new_value + code_size)); + } + } + + DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size()); + OatMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_]; + method_header->code_size_ = code_size; + + // Deduplicate code arrays. + auto code_iter = dedupe_map_.find(compiled_method); + if (code_iter != dedupe_map_.end()) { + quick_code_offset = code_iter->second; + FixupMethodHeader(method_header, quick_code_offset - thumb_offset); + } else { + dedupe_map_.Put(compiled_method, quick_code_offset); + FixupMethodHeader(method_header, quick_code_offset - thumb_offset); + writer_->oat_header_->UpdateChecksum(method_header, sizeof(*method_header)); + offset_ += sizeof(*method_header); // Method header is prepended before code. + writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size); + offset_ += code_size; + } + } + frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); + core_spill_mask = compiled_method->GetCoreSpillMask(); + fp_spill_mask = compiled_method->GetFpSpillMask(); + + if (kIsDebugBuild) { + // We expect GC maps except when the class hasn't been verified or the method is native. + const CompilerDriver* compiler_driver = writer_->compiler_driver_; + ClassReference class_ref(dex_file_, class_def_index_); + CompiledClass* compiled_class = compiler_driver->GetCompiledClass(class_ref); + mirror::Class::Status status; + if (compiled_class != NULL) { + status = compiled_class->GetStatus(); + } else if (compiler_driver->GetVerificationResults()->IsClassRejected(class_ref)) { + status = mirror::Class::kStatusError; + } else { + status = mirror::Class::kStatusNotReady; + } + const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap(); + size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); + bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; + CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) + << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " + << (status < mirror::Class::kStatusVerified) << " " << status << " " + << PrettyMethod(it.GetMemberIndex(), *dex_file_); + } + + DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); + OatMethodOffsets* offsets = &oat_class->method_offsets_[method_offsets_index_]; + offsets->code_offset_ = quick_code_offset; + offsets->frame_size_in_bytes_ = frame_size_in_bytes; + offsets->core_spill_mask_ = core_spill_mask; + offsets->fp_spill_mask_ = fp_spill_mask; + ++method_offsets_index_; + } + + return true; + } + + private: + static void FixupMethodHeader(OatMethodHeader* method_header, uint32_t code_offset) { + // The code offset was 0 when the mapping/vmap table offset was set, so it's set + // to 0-offset and we need to adjust it by code_offset. + if (method_header->mapping_table_offset_ != 0u) { + method_header->mapping_table_offset_ += code_offset; + DCHECK_LT(method_header->mapping_table_offset_, code_offset); + } + if (method_header->vmap_table_offset_ != 0u) { + method_header->vmap_table_offset_ += code_offset; + DCHECK_LT(method_header->vmap_table_offset_, code_offset); + } + } + + // Deduplication is already done on a pointer basis by the compiler driver, + // so we can simply compare the pointers to find out if things are duplicated. + SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_; +}; + +template <typename DataAccess> +class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor { + public: + InitMapMethodVisitor(OatWriter* writer, size_t offset) + : OatDexMethodVisitor(writer, offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + if (compiled_method != nullptr) { + DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); + DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u); + + const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method); + uint32_t map_size = map->size() * sizeof((*map)[0]); + if (map_size != 0u) { + auto it = dedupe_map_.find(map); + if (it != dedupe_map_.end()) { + DataAccess::SetOffset(oat_class, method_offsets_index_, it->second); + } else { + DataAccess::SetOffset(oat_class, method_offsets_index_, offset_); + dedupe_map_.Put(map, offset_); + offset_ += map_size; + writer_->oat_header_->UpdateChecksum(&(*map)[0], map_size); + } + } + ++method_offsets_index_; + } + + return true; + } + + private: + // Deduplication is already done on a pointer basis by the compiler driver, + // so we can simply compare the pointers to find out if things are duplicated. + SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_; +}; + +class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { + public: + InitImageMethodVisitor(OatWriter* writer, size_t offset) + : OatDexMethodVisitor(writer, offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + OatMethodOffsets offsets(0u, kStackAlignment, 0u, 0u, 0u); + if (compiled_method != nullptr) { + DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); + offsets = oat_class->method_offsets_[method_offsets_index_]; + ++method_offsets_index_; + } + + // Derive frame size and spill masks for native methods without code: + // These are generic JNI methods... + uint32_t method_idx = it.GetMemberIndex(); + bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; + if (is_native && compiled_method == nullptr) { + // Compute Sirt size as putting _every_ reference into it, even null ones. + uint32_t s_len; + const char* shorty = dex_file_->GetMethodShorty(dex_file_->GetMethodId(method_idx), + &s_len); + DCHECK(shorty != nullptr); + uint32_t refs = 1; // Native method always has "this" or class. + for (uint32_t i = 1; i < s_len; ++i) { + if (shorty[i] == 'L') { + refs++; + } + } + size_t pointer_size = GetInstructionSetPointerSize( + writer_->compiler_driver_->GetInstructionSet()); + size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs); + + // Get the generic spill masks and base frame size. + mirror::ArtMethod* callee_save_method = + Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs); + + offsets.frame_size_in_bytes_ = callee_save_method->GetFrameSizeInBytes() + sirt_size; + offsets.core_spill_mask_ = callee_save_method->GetCoreSpillMask(); + offsets.fp_spill_mask_ = callee_save_method->GetFpSpillMask(); + DCHECK_EQ(offsets.gc_map_offset_, 0u); + } + + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + InvokeType invoke_type = it.GetMethodInvokeType(dex_file_->GetClassDef(class_def_index_)); + // Unchecked as we hold mutator_lock_ on entry. + ScopedObjectAccessUnchecked soa(Thread::Current()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(*dex_file_)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); + mirror::ArtMethod* method = linker->ResolveMethod(*dex_file_, method_idx, dex_cache, + class_loader, nullptr, invoke_type); + CHECK(method != NULL); + method->SetFrameSizeInBytes(offsets.frame_size_in_bytes_); + method->SetCoreSpillMask(offsets.core_spill_mask_); + method->SetFpSpillMask(offsets.fp_spill_mask_); + // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking. + method->SetQuickOatCodeOffset(offsets.code_offset_); + method->SetOatNativeGcMapOffset(offsets.gc_map_offset_); + + return true; + } +}; + +class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { + public: + WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset, + size_t relative_offset) + : OatDexMethodVisitor(writer, relative_offset), + out_(out), + file_offset_(file_offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + if (compiled_method != NULL) { // ie. not an abstract method + size_t file_offset = file_offset_; + OutputStream* out = out_; + + const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode(); + if (quick_code != nullptr) { + CHECK(compiled_method->GetPortableCode() == nullptr); + uint32_t aligned_offset = compiled_method->AlignCode(offset_); + uint32_t aligned_code_delta = aligned_offset - offset_; + if (aligned_code_delta != 0) { + static const uint8_t kPadding[] = { + 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u + }; + DCHECK_LE(aligned_code_delta, sizeof(kPadding)); + if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) { + ReportWriteFailure("code alignment padding", it); + return false; + } + writer_->size_code_alignment_ += aligned_code_delta; + offset_ += aligned_code_delta; + DCHECK_OFFSET_(); + } + DCHECK_ALIGNED_PARAM(offset_, + GetInstructionSetAlignment(compiled_method->GetInstructionSet())); + uint32_t code_size = quick_code->size() * sizeof(uint8_t); + CHECK_NE(code_size, 0U); + + // Deduplicate code arrays. + const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_]; + DCHECK(method_offsets.code_offset_ < offset_ || method_offsets.code_offset_ == + offset_ + sizeof(OatMethodHeader) + compiled_method->CodeDelta()) + << PrettyMethod(it.GetMemberIndex(), *dex_file_); + if (method_offsets.code_offset_ >= offset_) { + const OatMethodHeader& method_header = oat_class->method_headers_[method_offsets_index_]; + if (!out->WriteFully(&method_header, sizeof(method_header))) { + ReportWriteFailure("method header", it); + return false; + } + writer_->size_method_header_ += sizeof(method_header); + offset_ += sizeof(method_header); + DCHECK_OFFSET_(); + if (!out->WriteFully(&(*quick_code)[0], code_size)) { + ReportWriteFailure("method code", it); + return false; + } + writer_->size_code_ += code_size; + offset_ += code_size; + } + DCHECK_OFFSET_(); + } + ++method_offsets_index_; + } + + return true; + } + + private: + OutputStream* const out_; + size_t const file_offset_; + + void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) { + PLOG(ERROR) << "Failed to write " << what << " for " + << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation(); + } +}; + +template <typename DataAccess> +class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { + public: + WriteMapMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset, + size_t relative_offset) + : OatDexMethodVisitor(writer, relative_offset), + out_(out), + file_offset_(file_offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + if (compiled_method != NULL) { // ie. not an abstract method + size_t file_offset = file_offset_; + OutputStream* out = out_; + + uint32_t map_offset = DataAccess::GetOffset(oat_class, method_offsets_index_); + ++method_offsets_index_; + + // Write deduplicated map. + const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method); + size_t map_size = map->size() * sizeof((*map)[0]); + DCHECK((map_size == 0u && map_offset == 0u) || + (map_size != 0u && map_offset != 0u && map_offset <= offset_)) + << PrettyMethod(it.GetMemberIndex(), *dex_file_); + if (map_size != 0u && map_offset == offset_) { + if (UNLIKELY(!out->WriteFully(&(*map)[0], map_size))) { + ReportWriteFailure(it); + return false; + } + offset_ += map_size; + } + DCHECK_OFFSET_(); + } + + return true; + } + + private: + OutputStream* const out_; + size_t const file_offset_; + + void ReportWriteFailure(const ClassDataItemIterator& it) { + PLOG(ERROR) << "Failed to write " << DataAccess::Name() << " for " + << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation(); + } +}; + +// Visit all methods from all classes in all dex files with the specified visitor. +bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { + for (const DexFile* dex_file : *dex_files_) { + const size_t class_def_count = dex_file->NumClassDefs(); + for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) { + if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) { + return false; + } + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const byte* class_data = dex_file->GetClassData(class_def); + if (class_data != NULL) { // ie not an empty class, such as a marker interface + ClassDataItemIterator it(*dex_file, class_data); + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + size_t class_def_method_index = 0u; + while (it.HasNextDirectMethod()) { + if (!visitor->VisitMethod(class_def_method_index, it)) { + return false; + } + ++class_def_method_index; + it.Next(); + } + while (it.HasNextVirtualMethod()) { + if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) { + return false; + } + ++class_def_method_index; + it.Next(); + } + } + if (UNLIKELY(!visitor->EndClass())) { + return false; + } + } + } + return true; +} + size_t OatWriter::InitOatHeader() { // create the OatHeader oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(), @@ -161,78 +772,42 @@ size_t OatWriter::InitDexFiles(size_t offset) { } size_t OatWriter::InitOatClasses(size_t offset) { - // create the OatClasses // calculate the offsets within OatDexFiles to OatClasses - for (size_t i = 0; i != dex_files_->size(); ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - for (size_t class_def_index = 0; - class_def_index < dex_file->NumClassDefs(); - class_def_index++) { - oat_dex_files_[i]->methods_offsets_[class_def_index] = offset; - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const byte* class_data = dex_file->GetClassData(class_def); - uint32_t num_non_null_compiled_methods = 0; - UniquePtr<std::vector<CompiledMethod*> > compiled_methods(new std::vector<CompiledMethod*>()); - if (class_data != NULL) { // ie not an empty class, such as a marker interface - ClassDataItemIterator it(*dex_file, class_data); - size_t num_direct_methods = it.NumDirectMethods(); - size_t num_virtual_methods = it.NumVirtualMethods(); - size_t num_methods = num_direct_methods + num_virtual_methods; - - // Fill in the compiled_methods_ array for methods that have a - // CompiledMethod. We track the number of non-null entries in - // num_non_null_compiled_methods since we only want to allocate - // OatMethodOffsets for the compiled methods. - compiled_methods->reserve(num_methods); - while (it.HasNextStaticField()) { - it.Next(); - } - while (it.HasNextInstanceField()) { - it.Next(); - } - size_t class_def_method_index = 0; - while (it.HasNextDirectMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx)); - compiled_methods->push_back(compiled_method); - if (compiled_method != NULL) { - num_non_null_compiled_methods++; - } - class_def_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx)); - compiled_methods->push_back(compiled_method); - if (compiled_method != NULL) { - num_non_null_compiled_methods++; - } - class_def_method_index++; - it.Next(); - } - } - - ClassReference class_ref(dex_file, class_def_index); - CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); - mirror::Class::Status status; - if (compiled_class != NULL) { - status = compiled_class->GetStatus(); - } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { - status = mirror::Class::kStatusError; - } else { - status = mirror::Class::kStatusNotReady; - } - - OatClass* oat_class = new OatClass(offset, compiled_methods.release(), - num_non_null_compiled_methods, status); - oat_classes_.push_back(oat_class); - offset += oat_class->SizeOf(); + InitOatClassesMethodVisitor visitor(this, offset); + bool success = VisitDexMethods(&visitor); + CHECK(success); + offset = visitor.GetOffset(); + + // Update oat_dex_files_. + auto oat_class_it = oat_classes_.begin(); + for (OatDexFile* oat_dex_file : oat_dex_files_) { + for (uint32_t& offset : oat_dex_file->methods_offsets_) { + DCHECK(oat_class_it != oat_classes_.end()); + offset = (*oat_class_it)->offset_; + ++oat_class_it; } - oat_dex_files_[i]->UpdateChecksum(oat_header_); + oat_dex_file->UpdateChecksum(oat_header_); } + CHECK(oat_class_it == oat_classes_.end()); + + return offset; +} + +size_t OatWriter::InitOatMaps(size_t offset) { + #define VISIT(VisitorType) \ + do { \ + VisitorType visitor(this, offset); \ + bool success = VisitDexMethods(&visitor); \ + DCHECK(success); \ + offset = visitor.GetOffset(); \ + } while (false) + + VISIT(InitMapMethodVisitor<GcMapDataAccess>); + VISIT(InitMapMethodVisitor<MappingTableDataAccess>); + VISIT(InitMapMethodVisitor<VmapTableDataAccess>); + + #undef VISIT + return offset; } @@ -280,280 +855,24 @@ size_t OatWriter::InitOatCode(size_t offset) { } size_t OatWriter::InitOatCodeDexFiles(size_t offset) { - size_t oat_class_index = 0; - for (size_t i = 0; i != dex_files_->size(); ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - CHECK(dex_file != NULL); - offset = InitOatCodeDexFile(offset, &oat_class_index, *dex_file); - } - return offset; -} - -size_t OatWriter::InitOatCodeDexFile(size_t offset, - size_t* oat_class_index, - const DexFile& dex_file) { - for (size_t class_def_index = 0; - class_def_index < dex_file.NumClassDefs(); - class_def_index++, (*oat_class_index)++) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - offset = InitOatCodeClassDef(offset, *oat_class_index, class_def_index, dex_file, class_def); - oat_classes_[*oat_class_index]->UpdateChecksum(oat_header_); - } - return offset; -} - -size_t OatWriter::InitOatCodeClassDef(size_t offset, - size_t oat_class_index, size_t class_def_index, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) { - const byte* class_data = dex_file.GetClassData(class_def); - if (class_data == NULL) { - // empty class, such as a marker interface - return offset; - } - ClassDataItemIterator it(dex_file, class_data); - CHECK_LE(oat_classes_[oat_class_index]->method_offsets_.size(), - it.NumDirectMethods() + it.NumVirtualMethods()); - // Skip fields - while (it.HasNextStaticField()) { - it.Next(); - } - while (it.HasNextInstanceField()) { - it.Next(); - } - // Process methods - size_t class_def_method_index = 0; - size_t method_offsets_index = 0; - while (it.HasNextDirectMethod()) { - bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; - offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index, - &method_offsets_index, is_native, - it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file); - class_def_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; - offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index, - &method_offsets_index, is_native, - it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file); - class_def_method_index++; - it.Next(); - } - DCHECK(!it.HasNext()); - CHECK_LE(method_offsets_index, class_def_method_index); - return offset; -} - -size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, - size_t __attribute__((unused)) class_def_index, - size_t class_def_method_index, - size_t* method_offsets_index, - bool __attribute__((unused)) is_native, - InvokeType invoke_type, - uint32_t method_idx, const DexFile& dex_file) { - // Derived from CompiledMethod if available. - uint32_t quick_code_offset = 0; - uint32_t frame_size_in_bytes = kStackAlignment; - uint32_t core_spill_mask = 0; - uint32_t fp_spill_mask = 0; - uint32_t mapping_table_offset = 0; - uint32_t vmap_table_offset = 0; - uint32_t gc_map_offset = 0; - - OatClass* oat_class = oat_classes_[oat_class_index]; - CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (compiled_method != nullptr) { - const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode(); - const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode(); - if (portable_code != nullptr) { - CHECK(quick_code == nullptr); - size_t oat_method_offsets_offset = - oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index); - compiled_method->AddOatdataOffsetToCompliledCodeOffset( - oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_)); - } else { - CHECK(quick_code != nullptr); - offset = compiled_method->AlignCode(offset); - DCHECK_ALIGNED_PARAM(offset, - GetInstructionSetAlignment(compiled_method->GetInstructionSet())); - - uint32_t code_size = quick_code->size() * sizeof(uint8_t); - CHECK_NE(code_size, 0U); - uint32_t thumb_offset = compiled_method->CodeDelta(); - quick_code_offset = offset + sizeof(code_size) + thumb_offset; - - std::vector<uint8_t>* cfi_info = compiler_driver_->GetCallFrameInformation(); - if (cfi_info != nullptr) { - // Copy in the FDE, if present - const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo(); - if (fde != nullptr) { - // Copy the information into cfi_info and then fix the address in the new copy. - int cur_offset = cfi_info->size(); - cfi_info->insert(cfi_info->end(), fde->begin(), fde->end()); - - // Set the 'initial_location' field to address the start of the method. - uint32_t new_value = quick_code_offset - oat_header_->GetExecutableOffset(); - uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t); - (*cfi_info)[offset_to_update+0] = new_value; - (*cfi_info)[offset_to_update+1] = new_value >> 8; - (*cfi_info)[offset_to_update+2] = new_value >> 16; - (*cfi_info)[offset_to_update+3] = new_value >> 24; - method_info_.push_back(DebugInfo(PrettyMethod(method_idx, dex_file, false), - new_value, new_value + code_size)); - } - } - - // Deduplicate code arrays - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = - code_offsets_.find(quick_code); - if (code_iter != code_offsets_.end()) { - quick_code_offset = code_iter->second; - } else { - code_offsets_.Put(quick_code, quick_code_offset); - offset += sizeof(code_size); // code size is prepended before code - offset += code_size; - oat_header_->UpdateChecksum(&(*quick_code)[0], code_size); - } - } - frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); - core_spill_mask = compiled_method->GetCoreSpillMask(); - fp_spill_mask = compiled_method->GetFpSpillMask(); - - const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable(); - size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]); - mapping_table_offset = (mapping_table_size == 0) ? 0 : offset; - - // Deduplicate mapping tables - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator mapping_iter = - mapping_table_offsets_.find(&mapping_table); - if (mapping_iter != mapping_table_offsets_.end()) { - mapping_table_offset = mapping_iter->second; - } else { - mapping_table_offsets_.Put(&mapping_table, mapping_table_offset); - offset += mapping_table_size; - oat_header_->UpdateChecksum(&mapping_table[0], mapping_table_size); - } - - const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable(); - size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]); - vmap_table_offset = (vmap_table_size == 0) ? 0 : offset; - - // Deduplicate vmap tables - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator vmap_iter = - vmap_table_offsets_.find(&vmap_table); - if (vmap_iter != vmap_table_offsets_.end()) { - vmap_table_offset = vmap_iter->second; - } else { - vmap_table_offsets_.Put(&vmap_table, vmap_table_offset); - offset += vmap_table_size; - oat_header_->UpdateChecksum(&vmap_table[0], vmap_table_size); - } - - const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap(); - size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); - gc_map_offset = (gc_map_size == 0) ? 0 : offset; - - if (kIsDebugBuild) { - // We expect GC maps except when the class hasn't been verified or the method is native - ClassReference class_ref(&dex_file, class_def_index); - CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); - mirror::Class::Status status; - if (compiled_class != NULL) { - status = compiled_class->GetStatus(); - } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { - status = mirror::Class::kStatusError; - } else { - status = mirror::Class::kStatusNotReady; - } - CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) - << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " - << (status < mirror::Class::kStatusVerified) << " " << status << " " - << PrettyMethod(method_idx, dex_file); - } - - // Deduplicate GC maps - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter = - gc_map_offsets_.find(&gc_map); - if (gc_map_iter != gc_map_offsets_.end()) { - gc_map_offset = gc_map_iter->second; - } else { - gc_map_offsets_.Put(&gc_map, gc_map_offset); - offset += gc_map_size; - oat_header_->UpdateChecksum(&gc_map[0], gc_map_size); - } - - oat_class->method_offsets_[*method_offsets_index] = - OatMethodOffsets(quick_code_offset, - frame_size_in_bytes, - core_spill_mask, - fp_spill_mask, - mapping_table_offset, - vmap_table_offset, - gc_map_offset); - (*method_offsets_index)++; - } - - + #define VISIT(VisitorType) \ + do { \ + VisitorType visitor(this, offset); \ + bool success = VisitDexMethods(&visitor); \ + DCHECK(success); \ + offset = visitor.GetOffset(); \ + } while (false) + + VISIT(InitCodeMethodVisitor); if (compiler_driver_->IsImage()) { - // Derive frame size and spill masks for native methods without code: - // These are generic JNI methods... - if (is_native && compiled_method == nullptr) { - // Compute Sirt size as putting _every_ reference into it, even null ones. - uint32_t s_len; - const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx), &s_len); - DCHECK(shorty != nullptr); - uint32_t refs = 1; // Native method always has "this" or class. - for (uint32_t i = 1; i < s_len; ++i) { - if (shorty[i] == 'L') { - refs++; - } - } - size_t pointer_size = GetInstructionSetPointerSize(compiler_driver_->GetInstructionSet()); - size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs); - - // Get the generic spill masks and base frame size. - mirror::ArtMethod* callee_save_method = - Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs); - - frame_size_in_bytes = callee_save_method->GetFrameSizeInBytes() + sirt_size; - core_spill_mask = callee_save_method->GetCoreSpillMask(); - fp_spill_mask = callee_save_method->GetFpSpillMask(); - mapping_table_offset = 0; - vmap_table_offset = 0; - gc_map_offset = 0; - } - - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - // Unchecked as we hold mutator_lock_ on entry. - ScopedObjectAccessUnchecked soa(Thread::Current()); - SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(dex_file)); - SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); - mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache, - class_loader, nullptr, invoke_type); - CHECK(method != NULL); - method->SetFrameSizeInBytes(frame_size_in_bytes); - method->SetCoreSpillMask(core_spill_mask); - method->SetFpSpillMask(fp_spill_mask); - method->SetOatMappingTableOffset(mapping_table_offset); - // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking. - method->SetQuickOatCodeOffset(quick_code_offset); - method->SetOatVmapTableOffset(vmap_table_offset); - method->SetOatNativeGcMapOffset(gc_map_offset); + VISIT(InitImageMethodVisitor); } + #undef VISIT + return offset; } -#define DCHECK_OFFSET() \ - DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \ - << "file_offset=" << file_offset << " relative_offset=" << relative_offset - -#define DCHECK_OFFSET_() \ - DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \ - << "file_offset=" << file_offset << " offset_=" << offset_ - bool OatWriter::Write(OutputStream* out) { const size_t file_offset = out->Seek(0, kSeekCurrent); @@ -574,7 +893,14 @@ bool OatWriter::Write(OutputStream* out) { return false; } - size_t relative_offset = WriteCode(out, file_offset); + size_t relative_offset = out->Seek(0, kSeekCurrent) - file_offset; + relative_offset = WriteMaps(out, file_offset, relative_offset); + if (relative_offset == 0) { + LOG(ERROR) << "Failed to write oat code to " << out->GetLocation(); + return false; + } + + relative_offset = WriteCode(out, file_offset, relative_offset); if (relative_offset == 0) { LOG(ERROR) << "Failed to write oat code to " << out->GetLocation(); return false; @@ -608,7 +934,7 @@ bool OatWriter::Write(OutputStream* out) { DO_STAT(size_quick_resolution_trampoline_); DO_STAT(size_quick_to_interpreter_bridge_); DO_STAT(size_trampoline_alignment_); - DO_STAT(size_code_size_); + DO_STAT(size_method_header_); DO_STAT(size_code_); DO_STAT(size_code_alignment_); DO_STAT(size_mapping_table_); @@ -669,9 +995,37 @@ bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) { return true; } -size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset) { - size_t relative_offset = oat_header_->GetExecutableOffset(); +size_t OatWriter::WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset) { + #define VISIT(VisitorType) \ + do { \ + VisitorType visitor(this, out, file_offset, relative_offset); \ + if (UNLIKELY(!VisitDexMethods(&visitor))) { \ + return 0; \ + } \ + relative_offset = visitor.GetOffset(); \ + } while (false) + + size_t gc_maps_offset = relative_offset; + VISIT(WriteMapMethodVisitor<GcMapDataAccess>); + size_gc_map_ = relative_offset - gc_maps_offset; + + size_t mapping_tables_offset = relative_offset; + VISIT(WriteMapMethodVisitor<MappingTableDataAccess>); + size_mapping_table_ = relative_offset - mapping_tables_offset; + + size_t vmap_tables_offset = relative_offset; + VISIT(WriteMapMethodVisitor<VmapTableDataAccess>); + size_vmap_table_ = relative_offset - vmap_tables_offset; + + #undef VISIT + + return relative_offset; +} + +size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset) { off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent); + relative_offset += size_executable_offset_alignment_; + DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset()); size_t expected_file_offset = file_offset + relative_offset; if (static_cast<uint32_t>(new_offset) != expected_file_offset) { PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset @@ -715,218 +1069,18 @@ size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset) { size_t OatWriter::WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset) { - size_t oat_class_index = 0; - for (size_t i = 0; i != oat_dex_files_.size(); ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - CHECK(dex_file != NULL); - relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, &oat_class_index, - *dex_file); - if (relative_offset == 0) { - return 0; - } - } - return relative_offset; -} - -size_t OatWriter::WriteCodeDexFile(OutputStream* out, const size_t file_offset, - size_t relative_offset, size_t* oat_class_index, - const DexFile& dex_file) { - for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); - class_def_index++, (*oat_class_index)++) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, *oat_class_index, - dex_file, class_def); - if (relative_offset == 0) { - return 0; - } - } - return relative_offset; -} - -void OatWriter::ReportWriteFailure(const char* what, uint32_t method_idx, - const DexFile& dex_file, const OutputStream& out) const { - PLOG(ERROR) << "Failed to write " << what << " for " << PrettyMethod(method_idx, dex_file) - << " to " << out.GetLocation(); -} + #define VISIT(VisitorType) \ + do { \ + VisitorType visitor(this, out, file_offset, relative_offset); \ + if (UNLIKELY(!VisitDexMethods(&visitor))) { \ + return 0; \ + } \ + relative_offset = visitor.GetOffset(); \ + } while (false) -size_t OatWriter::WriteCodeClassDef(OutputStream* out, - const size_t file_offset, - size_t relative_offset, - size_t oat_class_index, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) { - const byte* class_data = dex_file.GetClassData(class_def); - if (class_data == NULL) { - // ie. an empty class such as a marker interface - return relative_offset; - } - ClassDataItemIterator it(dex_file, class_data); - // Skip fields - while (it.HasNextStaticField()) { - it.Next(); - } - while (it.HasNextInstanceField()) { - it.Next(); - } - // Process methods - size_t class_def_method_index = 0; - size_t method_offsets_index = 0; - while (it.HasNextDirectMethod()) { - bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0; - relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index, - class_def_method_index, &method_offsets_index, is_static, - it.GetMemberIndex(), dex_file); - if (relative_offset == 0) { - return 0; - } - class_def_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index, - class_def_method_index, &method_offsets_index, false, - it.GetMemberIndex(), dex_file); - if (relative_offset == 0) { - return 0; - } - class_def_method_index++; - it.Next(); - } - DCHECK(!it.HasNext()); - CHECK_LE(method_offsets_index, class_def_method_index); - return relative_offset; -} + VISIT(WriteCodeMethodVisitor); -size_t OatWriter::WriteCodeMethod(OutputStream* out, const size_t file_offset, - size_t relative_offset, size_t oat_class_index, - size_t class_def_method_index, size_t* method_offsets_index, - bool is_static, uint32_t method_idx, const DexFile& dex_file) { - OatClass* oat_class = oat_classes_[oat_class_index]; - const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (compiled_method != NULL) { // ie. not an abstract method - const OatMethodOffsets method_offsets = oat_class->method_offsets_[*method_offsets_index]; - (*method_offsets_index)++; - const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode(); - if (quick_code != nullptr) { - CHECK(compiled_method->GetPortableCode() == nullptr); - uint32_t aligned_offset = compiled_method->AlignCode(relative_offset); - uint32_t aligned_code_delta = aligned_offset - relative_offset; - if (aligned_code_delta != 0) { - off_t new_offset = out->Seek(aligned_code_delta, kSeekCurrent); - size_code_alignment_ += aligned_code_delta; - uint32_t expected_offset = file_offset + aligned_offset; - if (static_cast<uint32_t>(new_offset) != expected_offset) { - PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset - << " Expected: " << expected_offset << " File: " << out->GetLocation(); - return 0; - } - relative_offset += aligned_code_delta; - DCHECK_OFFSET(); - } - DCHECK_ALIGNED_PARAM(relative_offset, - GetInstructionSetAlignment(compiled_method->GetInstructionSet())); - - uint32_t code_size = quick_code->size() * sizeof(uint8_t); - CHECK_NE(code_size, 0U); - - // Deduplicate code arrays - size_t code_offset = relative_offset + sizeof(code_size) + compiled_method->CodeDelta(); - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = - code_offsets_.find(quick_code); - if (code_iter != code_offsets_.end() && code_offset != method_offsets.code_offset_) { - DCHECK(code_iter->second == method_offsets.code_offset_) - << PrettyMethod(method_idx, dex_file); - } else { - DCHECK(code_offset == method_offsets.code_offset_) << PrettyMethod(method_idx, dex_file); - if (!out->WriteFully(&code_size, sizeof(code_size))) { - ReportWriteFailure("method code size", method_idx, dex_file, *out); - return 0; - } - size_code_size_ += sizeof(code_size); - relative_offset += sizeof(code_size); - DCHECK_OFFSET(); - if (!out->WriteFully(&(*quick_code)[0], code_size)) { - ReportWriteFailure("method code", method_idx, dex_file, *out); - return 0; - } - size_code_ += code_size; - relative_offset += code_size; - } - DCHECK_OFFSET(); - } - const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable(); - size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]); - - // Deduplicate mapping tables - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator mapping_iter = - mapping_table_offsets_.find(&mapping_table); - if (mapping_iter != mapping_table_offsets_.end() && - relative_offset != method_offsets.mapping_table_offset_) { - DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0) - || mapping_iter->second == method_offsets.mapping_table_offset_) - << PrettyMethod(method_idx, dex_file); - } else { - DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0) - || relative_offset == method_offsets.mapping_table_offset_) - << PrettyMethod(method_idx, dex_file); - if (!out->WriteFully(&mapping_table[0], mapping_table_size)) { - ReportWriteFailure("mapping table", method_idx, dex_file, *out); - return 0; - } - size_mapping_table_ += mapping_table_size; - relative_offset += mapping_table_size; - } - DCHECK_OFFSET(); - - const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable(); - size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]); - - // Deduplicate vmap tables - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator vmap_iter = - vmap_table_offsets_.find(&vmap_table); - if (vmap_iter != vmap_table_offsets_.end() && - relative_offset != method_offsets.vmap_table_offset_) { - DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0) - || vmap_iter->second == method_offsets.vmap_table_offset_) - << PrettyMethod(method_idx, dex_file); - } else { - DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0) - || relative_offset == method_offsets.vmap_table_offset_) - << PrettyMethod(method_idx, dex_file); - if (!out->WriteFully(&vmap_table[0], vmap_table_size)) { - ReportWriteFailure("vmap table", method_idx, dex_file, *out); - return 0; - } - size_vmap_table_ += vmap_table_size; - relative_offset += vmap_table_size; - } - DCHECK_OFFSET(); - - const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap(); - size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); - - // Deduplicate GC maps - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter = - gc_map_offsets_.find(&gc_map); - if (gc_map_iter != gc_map_offsets_.end() && - relative_offset != method_offsets.gc_map_offset_) { - DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0) - || gc_map_iter->second == method_offsets.gc_map_offset_) - << PrettyMethod(method_idx, dex_file); - } else { - DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0) - || relative_offset == method_offsets.gc_map_offset_) - << PrettyMethod(method_idx, dex_file); - if (!out->WriteFully(&gc_map[0], gc_map_size)) { - ReportWriteFailure("GC map", method_idx, dex_file, *out); - return 0; - } - size_gc_map_ += gc_map_size; - relative_offset += gc_map_size; - } - DCHECK_OFFSET(); - } + #undef VISIT return relative_offset; } @@ -993,15 +1147,14 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, } OatWriter::OatClass::OatClass(size_t offset, - std::vector<CompiledMethod*>* compiled_methods, + const std::vector<CompiledMethod*>& compiled_methods, uint32_t num_non_null_compiled_methods, - mirror::Class::Status status) { - CHECK(compiled_methods != NULL); - uint32_t num_methods = compiled_methods->size(); + mirror::Class::Status status) + : compiled_methods_(compiled_methods) { + uint32_t num_methods = compiled_methods.size(); CHECK_LE(num_non_null_compiled_methods, num_methods); offset_ = offset; - compiled_methods_ = compiled_methods; oat_method_offsets_offsets_from_oat_class_.resize(num_methods); // Since both kOatClassNoneCompiled and kOatClassAllCompiled could @@ -1020,6 +1173,7 @@ OatWriter::OatClass::OatClass(size_t offset, status_ = status; method_offsets_.resize(num_non_null_compiled_methods); + method_headers_.resize(num_non_null_compiled_methods); uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_); if (type_ == kOatClassSomeCompiled) { @@ -1033,7 +1187,7 @@ OatWriter::OatClass::OatClass(size_t offset, } for (size_t i = 0; i < num_methods; i++) { - CompiledMethod* compiled_method = (*compiled_methods_)[i]; + CompiledMethod* compiled_method = compiled_methods_[i]; if (compiled_method == NULL) { oat_method_offsets_offsets_from_oat_class_[i] = 0; } else { @@ -1048,7 +1202,6 @@ OatWriter::OatClass::OatClass(size_t offset, OatWriter::OatClass::~OatClass() { delete method_bitmap_; - delete compiled_methods_; } size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader( diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index bab1a26d44..7cdd5329bd 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -50,16 +50,30 @@ class OutputStream; // ... // OatClass[C] // +// GcMap one variable sized blob with GC map. +// GcMap GC maps are deduplicated. +// ... +// GcMap +// +// VmapTable one variable sized VmapTable blob (quick compiler only). +// VmapTable VmapTables are deduplicated. +// ... +// VmapTable +// +// MappingTable one variable sized blob with MappingTable (quick compiler only). +// MappingTable MappingTables are deduplicated. +// ... +// MappingTable +// // padding if necessary so that the following code will be page aligned // -// CompiledMethod one variable sized blob with the contents of each CompiledMethod -// CompiledMethod -// CompiledMethod -// CompiledMethod -// CompiledMethod -// CompiledMethod +// OatMethodHeader fixed size header for a CompiledMethod including the size of the MethodCode. +// MethodCode one variable sized blob with the code of a CompiledMethod. +// OatMethodHeader (OatMethodHeader, MethodCode) pairs are deduplicated. +// MethodCode // ... -// CompiledMethod +// OatMethodHeader +// MethodCode // class OatWriter { public: @@ -96,43 +110,47 @@ class OatWriter { } private: + // The DataAccess classes are helper classes that provide access to members related to + // a given map, i.e. GC map, mapping table or vmap table. By abstracting these away + // we can share a lot of code for processing the maps with template classes below. + struct GcMapDataAccess; + struct MappingTableDataAccess; + struct VmapTableDataAccess; + + // The function VisitDexMethods() below iterates through all the methods in all + // the compiled dex files in order of their definitions. The method visitor + // classes provide individual bits of processing for each of the passes we need to + // first collect the data we want to write to the oat file and then, in later passes, + // to actually write it. + class DexMethodVisitor; + class OatDexMethodVisitor; + class InitOatClassesMethodVisitor; + class InitCodeMethodVisitor; + template <typename DataAccess> + class InitMapMethodVisitor; + class InitImageMethodVisitor; + class WriteCodeMethodVisitor; + template <typename DataAccess> + class WriteMapMethodVisitor; + + // Visit all the methods in all the compiled dex files in their definition order + // with a given DexMethodVisitor. + bool VisitDexMethods(DexMethodVisitor* visitor); + size_t InitOatHeader(); size_t InitOatDexFiles(size_t offset); size_t InitDexFiles(size_t offset); size_t InitOatClasses(size_t offset); + size_t InitOatMaps(size_t offset); size_t InitOatCode(size_t offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeDexFiles(size_t offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - size_t InitOatCodeDexFile(size_t offset, - size_t* oat_class_index, - const DexFile& dex_file) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - size_t InitOatCodeClassDef(size_t offset, - size_t oat_class_index, size_t class_def_index, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - size_t InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t class_def_index, - size_t class_def_method_index, size_t* method_offsets_index, - bool is_native, InvokeType type, uint32_t method_idx, const DexFile&) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool WriteTables(OutputStream* out, const size_t file_offset); - size_t WriteCode(OutputStream* out, const size_t file_offset); + size_t WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset); + size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset); size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset); - size_t WriteCodeDexFile(OutputStream* out, const size_t file_offset, size_t relative_offset, - size_t* oat_class_index, const DexFile& dex_file); - size_t WriteCodeClassDef(OutputStream* out, const size_t file_offset, size_t relative_offset, - size_t oat_class_index, const DexFile& dex_file, - const DexFile::ClassDef& class_def); - size_t WriteCodeMethod(OutputStream* out, const size_t file_offset, size_t relative_offset, - size_t oat_class_index, size_t class_def_method_index, - size_t* method_offsets_index, bool is_static, uint32_t method_idx, - const DexFile& dex_file); - - void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file, - const OutputStream& out) const; class OatDexFile { public: @@ -159,7 +177,7 @@ class OatWriter { class OatClass { public: explicit OatClass(size_t offset, - std::vector<CompiledMethod*>* compiled_methods, + const std::vector<CompiledMethod*>& compiled_methods, uint32_t num_non_null_compiled_methods, mirror::Class::Status status); ~OatClass(); @@ -170,8 +188,8 @@ class OatWriter { bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const; CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const { - DCHECK(compiled_methods_ != NULL); - return (*compiled_methods_)[class_def_method_index]; + DCHECK_LT(class_def_method_index, compiled_methods_.size()); + return compiled_methods_[class_def_method_index]; } // Offset of start of OatClass from beginning of OatHeader. It is @@ -182,7 +200,7 @@ class OatWriter { size_t offset_; // CompiledMethods for each class_def_method_index, or NULL if no method is available. - std::vector<CompiledMethod*>* compiled_methods_; + std::vector<CompiledMethod*> compiled_methods_; // Offset from OatClass::offset_ to the OatMethodOffsets for the // class_def_method_index. If 0, it means the corresponding @@ -207,12 +225,13 @@ class OatWriter { // not is kOatClassBitmap, the bitmap will be NULL. BitVector* method_bitmap_; - // OatMethodOffsets for each CompiledMethod present in the - // OatClass. Note that some may be missing if + // OatMethodOffsets and OatMethodHeaders for each CompiledMethod + // present in the OatClass. Note that some may be missing if // OatClass::compiled_methods_ contains NULL values (and // oat_method_offsets_offsets_from_oat_class_ should contain 0 // values in this case). std::vector<OatMethodOffsets> method_offsets_; + std::vector<OatMethodHeader> method_headers_; private: DISALLOW_COPY_AND_ASSIGN(OatClass); @@ -265,7 +284,7 @@ class OatWriter { uint32_t size_quick_resolution_trampoline_; uint32_t size_quick_to_interpreter_bridge_; uint32_t size_trampoline_alignment_; - uint32_t size_code_size_; + uint32_t size_method_header_; uint32_t size_code_; uint32_t size_code_alignment_; uint32_t size_mapping_table_; @@ -281,12 +300,21 @@ class OatWriter { uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; - // Code mappings for deduplication. Deduplication is already done on a pointer basis by the - // compiler driver, so we can simply compare the pointers to find out if things are duplicated. - SafeMap<const std::vector<uint8_t>*, uint32_t> code_offsets_; - SafeMap<const std::vector<uint8_t>*, uint32_t> vmap_table_offsets_; - SafeMap<const std::vector<uint8_t>*, uint32_t> mapping_table_offsets_; - SafeMap<const std::vector<uint8_t>*, uint32_t> gc_map_offsets_; + struct CodeOffsetsKeyComparator { + bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const { + if (lhs->GetQuickCode() != rhs->GetQuickCode()) { + return lhs->GetQuickCode() < rhs->GetQuickCode(); + } + // If the code is the same, all other fields are likely to be the same as well. + if (UNLIKELY(&lhs->GetMappingTable() != &rhs->GetMappingTable())) { + return &lhs->GetMappingTable() < &rhs->GetMappingTable(); + } + if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) { + return &lhs->GetVmapTable() < &rhs->GetVmapTable(); + } + return false; + } + }; DISALLOW_COPY_AND_ASSIGN(OatWriter); }; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 7e63c69f5c..ff316e5b04 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -47,7 +47,7 @@ void CodeGenerator::CompileBlock(HBasicBlock* block) { Bind(GetLabelOf(block)); HGraphVisitor* location_builder = GetLocationBuilder(); HGraphVisitor* instruction_visitor = GetInstructionVisitor(); - for (HInstructionIterator it(block); !it.Done(); it.Advance()) { + for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); current->Accept(location_builder); InitLocations(current); @@ -55,9 +55,105 @@ void CodeGenerator::CompileBlock(HBasicBlock* block) { } } +size_t CodeGenerator::AllocateFreeRegisterInternal( + bool* blocked_registers, size_t number_of_registers) const { + for (size_t regno = 0; regno < number_of_registers; regno++) { + if (!blocked_registers[regno]) { + blocked_registers[regno] = true; + return regno; + } + } + LOG(FATAL) << "Unreachable"; + return -1; +} + + +void CodeGenerator::AllocateRegistersLocally(HInstruction* instruction) const { + LocationSummary* locations = instruction->GetLocations(); + if (locations == nullptr) return; + + for (size_t i = 0, e = GetNumberOfRegisters(); i < e; ++i) { + blocked_registers_[i] = false; + } + + // Mark all fixed input, temp and output registers as used. + for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) { + Location loc = locations->InAt(i); + if (loc.IsRegister()) { + // Check that a register is not specified twice in the summary. + DCHECK(!blocked_registers_[loc.GetEncoding()]); + blocked_registers_[loc.GetEncoding()] = true; + } + } + + for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) { + Location loc = locations->GetTemp(i); + if (loc.IsRegister()) { + // Check that a register is not specified twice in the summary. + DCHECK(!blocked_registers_[loc.GetEncoding()]); + blocked_registers_[loc.GetEncoding()] = true; + } + } + + SetupBlockedRegisters(blocked_registers_); + + // Allocate all unallocated input locations. + for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) { + Location loc = locations->InAt(i); + HInstruction* input = instruction->InputAt(i); + if (loc.IsUnallocated()) { + if (loc.GetPolicy() == Location::kRequiresRegister) { + loc = Location::RegisterLocation( + AllocateFreeRegister(input->GetType(), blocked_registers_)); + } else { + DCHECK_EQ(loc.GetPolicy(), Location::kAny); + HLoadLocal* load = input->AsLoadLocal(); + if (load != nullptr) { + loc = GetStackLocation(load); + } else { + loc = Location::RegisterLocation( + AllocateFreeRegister(input->GetType(), blocked_registers_)); + } + } + locations->SetInAt(i, loc); + } + } + + // Allocate all unallocated temp locations. + for (size_t i = 0, e = locations->GetTempCount(); i < e; ++i) { + Location loc = locations->GetTemp(i); + if (loc.IsUnallocated()) { + DCHECK_EQ(loc.GetPolicy(), Location::kRequiresRegister); + // TODO: Adjust handling of temps. We currently consider temps to use + // core registers. They may also use floating point registers at some point. + loc = Location::RegisterLocation(static_cast<ManagedRegister>( + AllocateFreeRegister(Primitive::kPrimInt, blocked_registers_))); + locations->SetTempAt(i, loc); + } + } + + Location result_location = locations->Out(); + if (result_location.IsUnallocated()) { + switch (result_location.GetPolicy()) { + case Location::kAny: + case Location::kRequiresRegister: + result_location = Location::RegisterLocation( + AllocateFreeRegister(instruction->GetType(), blocked_registers_)); + break; + case Location::kSameAsFirstInput: + result_location = locations->InAt(0); + break; + } + locations->SetOut(result_location); + } +} + void CodeGenerator::InitLocations(HInstruction* instruction) { - if (instruction->GetLocations() == nullptr) return; - for (int i = 0; i < instruction->InputCount(); i++) { + if (instruction->GetLocations() == nullptr) { + return; + } + AllocateRegistersLocally(instruction); + for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { Location location = instruction->GetLocations()->InAt(i); if (location.IsValid()) { // Move the input to the desired location. diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 5c7cac1e5c..74cbccc4b8 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -62,6 +62,12 @@ class Location : public ValueObject { // bits are in a stack slot. The kQuickParameter kind is for // handling this special case. kQuickParameter = 4, + + // Unallocated location represents a location that is not fixed and can be + // allocated by a register allocator. Each unallocated location has + // a policy that specifies what kind of location is suitable. Payload + // contains register allocation policy. + kUnallocated = 5, }; Location() : value_(kInvalid) { @@ -166,10 +172,50 @@ class Location : public ValueObject { case kStackSlot: return "S"; case kDoubleStackSlot: return "DS"; case kQuickParameter: return "Q"; + case kUnallocated: return "U"; } return "?"; } + // Unallocated locations. + enum Policy { + kAny, + kRequiresRegister, + kSameAsFirstInput, + }; + + bool IsUnallocated() const { + return GetKind() == kUnallocated; + } + + static Location UnallocatedLocation(Policy policy) { + return Location(kUnallocated, PolicyField::Encode(policy)); + } + + // Any free register is suitable to replace this unallocated location. + static Location Any() { + return UnallocatedLocation(kAny); + } + + static Location RequiresRegister() { + return UnallocatedLocation(kRequiresRegister); + } + + // The location of the first input to the instruction will be + // used to replace this unallocated location. + static Location SameAsFirstInput() { + return UnallocatedLocation(kSameAsFirstInput); + } + + Policy GetPolicy() const { + DCHECK(IsUnallocated()); + return PolicyField::Decode(GetPayload()); + } + + uword GetEncoding() const { + return GetPayload(); + } + private: // Number of bits required to encode Kind value. static constexpr uint32_t kBitsForKind = 4; @@ -187,6 +233,9 @@ class Location : public ValueObject { typedef BitField<Kind, 0, kBitsForKind> KindField; typedef BitField<uword, kBitsForKind, kBitsForPayload> PayloadField; + // Layout for kUnallocated locations payload. + typedef BitField<Policy, 0, 3> PolicyField; + // Layout for stack slots. static const intptr_t kStackIndexBias = static_cast<intptr_t>(1) << (kBitsForPayload - 1); @@ -208,40 +257,52 @@ class Location : public ValueObject { class LocationSummary : public ArenaObject { public: explicit LocationSummary(HInstruction* instruction) - : inputs(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()), - temps(instruction->GetBlock()->GetGraph()->GetArena(), 0) { - inputs.SetSize(instruction->InputCount()); - for (int i = 0; i < instruction->InputCount(); i++) { - inputs.Put(i, Location()); + : inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()), + temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0) { + inputs_.SetSize(instruction->InputCount()); + for (size_t i = 0; i < instruction->InputCount(); i++) { + inputs_.Put(i, Location()); } } void SetInAt(uint32_t at, Location location) { - inputs.Put(at, location); + inputs_.Put(at, location); } Location InAt(uint32_t at) const { - return inputs.Get(at); + return inputs_.Get(at); + } + + size_t GetInputCount() const { + return inputs_.Size(); } void SetOut(Location location) { - output = Location(location); + output_ = Location(location); } void AddTemp(Location location) { - temps.Add(location); + temps_.Add(location); } Location GetTemp(uint32_t at) const { - return temps.Get(at); + return temps_.Get(at); + } + + void SetTempAt(uint32_t at, Location location) { + temps_.Put(at, location); } - Location Out() const { return output; } + size_t GetTempCount() const { + return temps_.Size(); + } + + Location Out() const { return output_; } private: - GrowableArray<Location> inputs; - GrowableArray<Location> temps; - Location output; + GrowableArray<Location> inputs_; + GrowableArray<Location> temps_; + Location output_; DISALLOW_COPY_AND_ASSIGN(LocationSummary); }; @@ -286,15 +347,33 @@ class CodeGenerator : public ArenaObject { std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const; protected: - explicit CodeGenerator(HGraph* graph) + CodeGenerator(HGraph* graph, size_t number_of_registers) : frame_size_(0), graph_(graph), block_labels_(graph->GetArena(), 0), - pc_infos_(graph->GetArena(), 32) { + pc_infos_(graph->GetArena(), 32), + blocked_registers_(static_cast<bool*>( + graph->GetArena()->Alloc(number_of_registers * sizeof(bool), kArenaAllocData))) { block_labels_.SetSize(graph->GetBlocks()->Size()); } ~CodeGenerator() { } + // Register allocation logic. + void AllocateRegistersLocally(HInstruction* instruction) const; + + // Backend specific implementation for allocating a register. + virtual ManagedRegister AllocateFreeRegister(Primitive::Type type, + bool* blocked_registers) const = 0; + + // Raw implementation of allocating a register: loops over blocked_registers to find + // the first available register. + size_t AllocateFreeRegisterInternal(bool* blocked_registers, size_t number_of_registers) const; + + virtual void SetupBlockedRegisters(bool* blocked_registers) const = 0; + virtual size_t GetNumberOfRegisters() const = 0; + + virtual Location GetStackLocation(HLoadLocal* load) const = 0; + // Frame size required for this method. uint32_t frame_size_; uint32_t core_spill_mask_; @@ -309,6 +388,9 @@ class CodeGenerator : public ArenaObject { GrowableArray<Label> block_labels_; GrowableArray<PcInfo> pc_infos_; + // Temporary data structure used when doing register allocation. + bool* const blocked_registers_; + DISALLOW_COPY_AND_ASSIGN(CodeGenerator); }; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 27691ac080..a446701b39 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -35,6 +35,81 @@ namespace arm { static constexpr int kNumberOfPushedRegistersAtEntry = 1; static constexpr int kCurrentMethodStackOffset = 0; +CodeGeneratorARM::CodeGeneratorARM(HGraph* graph) + : CodeGenerator(graph, kNumberOfRegIds), + location_builder_(graph, this), + instruction_visitor_(graph, this) {} + +static bool* GetBlockedRegisterPairs(bool* blocked_registers) { + return blocked_registers + kNumberOfAllocIds; +} + +ManagedRegister CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type, + bool* blocked_registers) const { + switch (type) { + case Primitive::kPrimLong: { + size_t reg = AllocateFreeRegisterInternal( + GetBlockedRegisterPairs(blocked_registers), kNumberOfRegisterPairs); + ArmManagedRegister pair = + ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); + blocked_registers[pair.AsRegisterPairLow()] = true; + blocked_registers[pair.AsRegisterPairHigh()] = true; + return pair; + } + + case Primitive::kPrimByte: + case Primitive::kPrimBoolean: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + size_t reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCoreRegisters); + return ArmManagedRegister::FromCoreRegister(static_cast<Register>(reg)); + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << type; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << type; + } + + return ManagedRegister::NoRegister(); +} + +void CodeGeneratorARM::SetupBlockedRegisters(bool* blocked_registers) const { + bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers); + + // Don't allocate the dalvik style register pair passing. + blocked_register_pairs[R1_R2] = true; + + // Stack register, LR and PC are always reserved. + blocked_registers[SP] = true; + blocked_registers[LR] = true; + blocked_registers[PC] = true; + + // Reserve R4 for suspend check. + blocked_registers[R4] = true; + blocked_register_pairs[R4_R5] = true; + + // Reserve thread register. + blocked_registers[TR] = true; + + // TODO: We currently don't use Quick's callee saved registers. + blocked_registers[R5] = true; + blocked_registers[R6] = true; + blocked_registers[R7] = true; + blocked_registers[R8] = true; + blocked_registers[R10] = true; + blocked_registers[R11] = true; + blocked_register_pairs[R6_R7] = true; +} + +size_t CodeGeneratorARM::GetNumberOfRegisters() const { + return kNumberOfRegIds; +} + static Location ArmCoreLocation(Register reg) { return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg)); } @@ -85,6 +160,32 @@ int32_t CodeGeneratorARM::GetStackSlot(HLocal* local) const { } } +Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { + switch (load->GetType()) { + case Primitive::kPrimLong: + return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); + break; + + case Primitive::kPrimInt: + case Primitive::kPrimNot: + return Location::StackSlot(GetStackSlot(load->GetLocal())); + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented type " << load->GetType(); + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimVoid: + LOG(FATAL) << "Unexpected type " << load->GetType(); + } + + LOG(FATAL) << "Unreachable"; + return Location(); +} + Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { switch (type) { case Primitive::kPrimBoolean: @@ -302,7 +403,7 @@ void InstructionCodeGeneratorARM::VisitExit(HExit* exit) { void LocationsBuilderARM::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, ArmCoreLocation(R0)); + locations->SetInAt(0, Location::RequiresRegister()); if_instr->SetLocations(locations); } @@ -317,9 +418,9 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { void LocationsBuilderARM::VisitEqual(HEqual* equal) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal); - locations->SetInAt(0, ArmCoreLocation(R0)); - locations->SetInAt(1, ArmCoreLocation(R1)); - locations->SetOut(ArmCoreLocation(R0)); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); equal->SetLocations(locations); } @@ -409,7 +510,8 @@ void LocationsBuilderARM::VisitReturn(HReturn* ret) { break; case Primitive::kPrimLong: - locations->SetInAt(0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); + locations->SetInAt( + 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); break; default: @@ -444,10 +546,10 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); - locations->AddTemp(ArmCoreLocation(R0)); + locations->AddTemp(Location::RequiresRegister()); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (int i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->InputCount(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -512,19 +614,11 @@ void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { void LocationsBuilderARM::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add); switch (add->GetResultType()) { - case Primitive::kPrimInt: { - locations->SetInAt(0, ArmCoreLocation(R0)); - locations->SetInAt(1, ArmCoreLocation(R1)); - locations->SetOut(ArmCoreLocation(R0)); - break; - } - + case Primitive::kPrimInt: case Primitive::kPrimLong: { - locations->SetInAt( - 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); - locations->SetInAt( - 1, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R2_R3))); - locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); break; } @@ -574,19 +668,11 @@ void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) { void LocationsBuilderARM::VisitSub(HSub* sub) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub); switch (sub->GetResultType()) { - case Primitive::kPrimInt: { - locations->SetInAt(0, ArmCoreLocation(R0)); - locations->SetInAt(1, ArmCoreLocation(R1)); - locations->SetOut(ArmCoreLocation(R0)); - break; - } - + case Primitive::kPrimInt: case Primitive::kPrimLong: { - locations->SetInAt( - 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); - locations->SetInAt( - 1, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R2_R3))); - locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); break; } @@ -649,6 +735,9 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register> { void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1))); locations->SetOut(ArmCoreLocation(R0)); instruction->SetLocations(locations); } @@ -683,8 +772,8 @@ void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instructi void LocationsBuilderARM::VisitNot(HNot* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); - locations->SetInAt(0, ArmCoreLocation(R0)); - locations->SetOut(ArmCoreLocation(R0)); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); instruction->SetLocations(locations); } @@ -694,5 +783,13 @@ void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) { locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(1)); } +void LocationsBuilderARM::VisitPhi(HPhi* instruction) { + LOG(FATAL) << "Unimplemented"; +} + +void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) { + LOG(FATAL) << "Unimplemented"; +} + } // namespace arm } // namespace art diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index ed35f94e2b..2405d4b5a6 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -101,10 +101,7 @@ class InstructionCodeGeneratorARM : public HGraphVisitor { class CodeGeneratorARM : public CodeGenerator { public: - explicit CodeGeneratorARM(HGraph* graph) - : CodeGenerator(graph), - location_builder_(graph, this), - instruction_visitor_(graph, this) { } + explicit CodeGeneratorARM(HGraph* graph); virtual ~CodeGeneratorARM() { } virtual void GenerateFrameEntry() OVERRIDE; @@ -128,7 +125,13 @@ class CodeGeneratorARM : public CodeGenerator { return &assembler_; } + virtual void SetupBlockedRegisters(bool* blocked_registers) const OVERRIDE; + virtual ManagedRegister AllocateFreeRegister( + Primitive::Type type, bool* blocked_registers) const OVERRIDE; + virtual size_t GetNumberOfRegisters() const OVERRIDE; + int32_t GetStackSlot(HLocal* local) const; + virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; private: // Helper method to move a 32bits value between two locations. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 114263161d..fbb054ae88 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -35,6 +35,72 @@ namespace x86 { static constexpr int kNumberOfPushedRegistersAtEntry = 1; static constexpr int kCurrentMethodStackOffset = 0; +CodeGeneratorX86::CodeGeneratorX86(HGraph* graph) + : CodeGenerator(graph, kNumberOfRegIds), + location_builder_(graph, this), + instruction_visitor_(graph, this) {} + +static bool* GetBlockedRegisterPairs(bool* blocked_registers) { + return blocked_registers + kNumberOfAllocIds; +} + +ManagedRegister CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type, + bool* blocked_registers) const { + switch (type) { + case Primitive::kPrimLong: { + size_t reg = AllocateFreeRegisterInternal( + GetBlockedRegisterPairs(blocked_registers), kNumberOfRegisterPairs); + X86ManagedRegister pair = + X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); + blocked_registers[pair.AsRegisterPairLow()] = true; + blocked_registers[pair.AsRegisterPairHigh()] = true; + return pair; + } + + case Primitive::kPrimByte: + case Primitive::kPrimBoolean: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + size_t reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCpuRegisters); + return X86ManagedRegister::FromCpuRegister(static_cast<Register>(reg)); + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << type; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << type; + } + + return ManagedRegister::NoRegister(); +} + +void CodeGeneratorX86::SetupBlockedRegisters(bool* blocked_registers) const { + bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers); + + // Don't allocate the dalvik style register pair passing. + blocked_register_pairs[ECX_EDX] = true; + + // Stack register is always reserved. + blocked_registers[ESP] = true; + + // TODO: We currently don't use Quick's callee saved registers. + blocked_registers[EBP] = true; + blocked_registers[ESI] = true; + blocked_registers[EDI] = true; + blocked_register_pairs[EAX_EDI] = true; + blocked_register_pairs[EDX_EDI] = true; + blocked_register_pairs[ECX_EDI] = true; + blocked_register_pairs[EBX_EDI] = true; +} + +size_t CodeGeneratorX86::GetNumberOfRegisters() const { + return kNumberOfRegIds; +} + static Location X86CpuLocation(Register reg) { return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg)); } @@ -90,6 +156,33 @@ int32_t CodeGeneratorX86::GetStackSlot(HLocal* local) const { } } + +Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const { + switch (load->GetType()) { + case Primitive::kPrimLong: + return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); + break; + + case Primitive::kPrimInt: + case Primitive::kPrimNot: + return Location::StackSlot(GetStackSlot(load->GetLocal())); + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented type " << load->GetType(); + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimVoid: + LOG(FATAL) << "Unexpected type " << load->GetType(); + } + + LOG(FATAL) << "Unreachable"; + return Location(); +} + static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX }; static constexpr size_t kRuntimeParameterCoreRegistersLength = arraysize(kRuntimeParameterCoreRegisters); @@ -311,13 +404,18 @@ void InstructionCodeGeneratorX86::VisitExit(HExit* exit) { void LocationsBuilderX86::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, X86CpuLocation(EAX)); + locations->SetInAt(0, Location::Any()); if_instr->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) { // TODO: Generate the input as a condition, instead of materializing in a register. - __ cmpl(if_instr->GetLocations()->InAt(0).AsX86().AsCpuRegister(), Immediate(0)); + Location location = if_instr->GetLocations()->InAt(0); + if (location.IsRegister()) { + __ cmpl(location.AsX86().AsCpuRegister(), Immediate(0)); + } else { + __ cmpl(Address(ESP, location.GetStackIndex()), Immediate(0)); + } __ j(kEqual, codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfTrueSuccessor())) { __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); @@ -367,16 +465,22 @@ void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) { void LocationsBuilderX86::VisitEqual(HEqual* equal) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal); - locations->SetInAt(0, X86CpuLocation(EAX)); - locations->SetInAt(1, X86CpuLocation(ECX)); - locations->SetOut(X86CpuLocation(EAX)); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); equal->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitEqual(HEqual* equal) { - __ cmpl(equal->GetLocations()->InAt(0).AsX86().AsCpuRegister(), - equal->GetLocations()->InAt(1).AsX86().AsCpuRegister()); - __ setb(kEqual, equal->GetLocations()->Out().AsX86().AsCpuRegister()); + LocationSummary* locations = equal->GetLocations(); + if (locations->InAt(1).IsRegister()) { + __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), + locations->InAt(1).AsX86().AsCpuRegister()); + } else { + __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), + Address(ESP, locations->InAt(1).GetStackIndex())); + } + __ setb(kEqual, locations->Out().AsX86().AsCpuRegister()); } void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) { @@ -453,10 +557,10 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); - locations->AddTemp(X86CpuLocation(EAX)); + locations->AddTemp(Location::RequiresRegister()); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (int i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->InputCount(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -514,18 +618,11 @@ void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { void LocationsBuilderX86::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add); switch (add->GetResultType()) { - case Primitive::kPrimInt: { - locations->SetInAt(0, X86CpuLocation(EAX)); - locations->SetInAt(1, X86CpuLocation(ECX)); - locations->SetOut(X86CpuLocation(EAX)); - break; - } + case Primitive::kPrimInt: case Primitive::kPrimLong: { - locations->SetInAt( - 0, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); - locations->SetInAt( - 1, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(ECX_EBX))); - locations->SetOut(Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); break; } @@ -548,18 +645,30 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { case Primitive::kPrimInt: { DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), locations->Out().AsX86().AsCpuRegister()); - __ addl(locations->InAt(0).AsX86().AsCpuRegister(), - locations->InAt(1).AsX86().AsCpuRegister()); + if (locations->InAt(1).IsRegister()) { + __ addl(locations->InAt(0).AsX86().AsCpuRegister(), + locations->InAt(1).AsX86().AsCpuRegister()); + } else { + __ addl(locations->InAt(0).AsX86().AsCpuRegister(), + Address(ESP, locations->InAt(1).GetStackIndex())); + } break; } case Primitive::kPrimLong: { DCHECK_EQ(locations->InAt(0).AsX86().AsRegisterPair(), locations->Out().AsX86().AsRegisterPair()); - __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(), - locations->InAt(1).AsX86().AsRegisterPairLow()); - __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(), - locations->InAt(1).AsX86().AsRegisterPairHigh()); + if (locations->InAt(1).IsRegister()) { + __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(), + locations->InAt(1).AsX86().AsRegisterPairLow()); + __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(), + locations->InAt(1).AsX86().AsRegisterPairHigh()); + } else { + __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(), + Address(ESP, locations->InAt(1).GetStackIndex())); + __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(), + Address(ESP, locations->InAt(1).GetHighStackIndex(kX86WordSize))); + } break; } @@ -578,19 +687,11 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { void LocationsBuilderX86::VisitSub(HSub* sub) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub); switch (sub->GetResultType()) { - case Primitive::kPrimInt: { - locations->SetInAt(0, X86CpuLocation(EAX)); - locations->SetInAt(1, X86CpuLocation(ECX)); - locations->SetOut(X86CpuLocation(EAX)); - break; - } - + case Primitive::kPrimInt: case Primitive::kPrimLong: { - locations->SetInAt( - 0, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); - locations->SetInAt( - 1, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(ECX_EBX))); - locations->SetOut(Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); break; } @@ -613,18 +714,30 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { case Primitive::kPrimInt: { DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), locations->Out().AsX86().AsCpuRegister()); - __ subl(locations->InAt(0).AsX86().AsCpuRegister(), - locations->InAt(1).AsX86().AsCpuRegister()); + if (locations->InAt(1).IsRegister()) { + __ subl(locations->InAt(0).AsX86().AsCpuRegister(), + locations->InAt(1).AsX86().AsCpuRegister()); + } else { + __ subl(locations->InAt(0).AsX86().AsCpuRegister(), + Address(ESP, locations->InAt(1).GetStackIndex())); + } break; } case Primitive::kPrimLong: { DCHECK_EQ(locations->InAt(0).AsX86().AsRegisterPair(), locations->Out().AsX86().AsRegisterPair()); - __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(), - locations->InAt(1).AsX86().AsRegisterPairLow()); - __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(), - locations->InAt(1).AsX86().AsRegisterPairHigh()); + if (locations->InAt(1).IsRegister()) { + __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(), + locations->InAt(1).AsX86().AsRegisterPairLow()); + __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(), + locations->InAt(1).AsX86().AsRegisterPairHigh()); + } else { + __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(), + Address(ESP, locations->InAt(1).GetStackIndex())); + __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(), + Address(ESP, locations->InAt(1).GetHighStackIndex(kX86WordSize))); + } break; } @@ -643,14 +756,16 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); locations->SetOut(X86CpuLocation(EAX)); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(1))); instruction->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { InvokeRuntimeCallingConvention calling_convention; LoadCurrentMethod(calling_convention.GetRegisterAt(1)); - __ movl(calling_convention.GetRegisterAt(0), - Immediate(instruction->GetTypeIndex())); + __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex())); __ fs()->call( Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck))); @@ -676,15 +791,24 @@ void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instructi void LocationsBuilderX86::VisitNot(HNot* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); - locations->SetInAt(0, X86CpuLocation(EAX)); - locations->SetOut(X86CpuLocation(EAX)); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); instruction->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) { LocationSummary* locations = instruction->GetLocations(); - DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), locations->Out().AsX86().AsCpuRegister()); - __ xorl(locations->Out().AsX86().AsCpuRegister(), Immediate(1)); + Location out = locations->Out(); + DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), out.AsX86().AsCpuRegister()); + __ xorl(out.AsX86().AsCpuRegister(), Immediate(1)); +} + +void LocationsBuilderX86::VisitPhi(HPhi* instruction) { + LOG(FATAL) << "Unimplemented"; +} + +void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) { + LOG(FATAL) << "Unimplemented"; } } // namespace x86 diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index f22890e708..1ee11bf0e8 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -102,10 +102,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { class CodeGeneratorX86 : public CodeGenerator { public: - explicit CodeGeneratorX86(HGraph* graph) - : CodeGenerator(graph), - location_builder_(graph, this), - instruction_visitor_(graph, this) { } + explicit CodeGeneratorX86(HGraph* graph); virtual ~CodeGeneratorX86() { } virtual void GenerateFrameEntry() OVERRIDE; @@ -129,7 +126,13 @@ class CodeGeneratorX86 : public CodeGenerator { return &assembler_; } + virtual size_t GetNumberOfRegisters() const OVERRIDE; + virtual void SetupBlockedRegisters(bool* blocked_registers) const OVERRIDE; + virtual ManagedRegister AllocateFreeRegister( + Primitive::Type type, bool* blocked_registers) const OVERRIDE; + int32_t GetStackSlot(HLocal* local) const; + virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; private: // Helper method to move a 32bits value between two locations. diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 498deba2b4..3d6aeb7300 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -15,6 +15,7 @@ */ #include "nodes.h" +#include "ssa_builder.h" #include "utils/growable_array.h" namespace art { @@ -34,7 +35,13 @@ void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) const { if (!visited.IsBitSet(i)) { HBasicBlock* block = blocks_.Get(i); for (size_t j = 0; j < block->GetSuccessors()->Size(); j++) { - block->GetSuccessors()->Get(j)->RemovePredecessor(block); + block->GetSuccessors()->Get(j)->RemovePredecessor(block, false); + } + for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + block->RemovePhi(it.Current()->AsPhi()); + } + for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + block->RemoveInstruction(it.Current()); } } } @@ -120,11 +127,112 @@ void HGraph::VisitBlockForDominatorTree(HBasicBlock* block, } } -void HBasicBlock::AddInstruction(HInstruction* instruction) { +void HGraph::TransformToSSA() { + DCHECK(!dominator_order_.IsEmpty()); + SimplifyCFG(); + SsaBuilder ssa_builder(this); + ssa_builder.BuildSsa(); +} + +void HGraph::SimplifyCFG() { + for (size_t i = 0; i < dominator_order_.Size(); i++) { + HBasicBlock* current = dominator_order_.Get(i); + if (current->IsLoopHeader()) { + // Make sure the loop has only one pre header. This simplifies SSA building by having + // to just look at the pre header to know which locals are initialized at entry of the + // loop. + HLoopInformation* info = current->GetLoopInformation(); + size_t number_of_incomings = current->GetPredecessors()->Size() - info->NumberOfBackEdges(); + if (number_of_incomings != 1) { + HBasicBlock* pre_header = new (arena_) HBasicBlock(this); + AddBlock(pre_header); + pre_header->AddInstruction(new (arena_) HGoto()); + pre_header->SetDominator(current->GetDominator()); + current->SetDominator(pre_header); + dominator_order_.InsertAt(i, pre_header); + i++; + + ArenaBitVector back_edges(arena_, GetBlocks()->Size(), false); + for (size_t pred = 0; pred < info->GetBackEdges()->Size(); pred++) { + back_edges.SetBit(info->GetBackEdges()->Get(pred)->GetBlockId()); + } + for (size_t pred = 0; pred < current->GetPredecessors()->Size(); pred++) { + HBasicBlock* predecessor = current->GetPredecessors()->Get(pred); + if (!back_edges.IsBitSet(predecessor->GetBlockId())) { + current->RemovePredecessor(predecessor); + pred--; + predecessor->AddSuccessor(pre_header); + } + } + pre_header->AddSuccessor(current); + } + info->SetPreHeader(current->GetDominator()); + } + } +} + +void HLoopInformation::SetPreHeader(HBasicBlock* block) { + DCHECK_EQ(header_->GetDominator(), block); + pre_header_ = block; +} + +static void Add(HInstructionList* instruction_list, + HBasicBlock* block, + HInstruction* instruction) { DCHECK(instruction->GetBlock() == nullptr); DCHECK_EQ(instruction->GetId(), -1); - instruction->SetBlock(this); - instruction->SetId(GetGraph()->GetNextInstructionId()); + instruction->SetBlock(block); + instruction->SetId(block->GetGraph()->GetNextInstructionId()); + instruction_list->AddInstruction(instruction); +} + +void HBasicBlock::AddInstruction(HInstruction* instruction) { + Add(&instructions_, this, instruction); +} + +void HBasicBlock::AddPhi(HPhi* phi) { + Add(&phis_, this, phi); +} + +static void Remove(HInstructionList* instruction_list, + HBasicBlock* block, + HInstruction* instruction) { + DCHECK_EQ(block, instruction->GetBlock()); + DCHECK(instruction->GetUses() == nullptr); + DCHECK(instruction->GetEnvUses() == nullptr); + instruction->SetBlock(nullptr); + instruction_list->RemoveInstruction(instruction); + + for (size_t i = 0; i < instruction->InputCount(); i++) { + instruction->InputAt(i)->RemoveUser(instruction, i); + } +} + +void HBasicBlock::RemoveInstruction(HInstruction* instruction) { + Remove(&instructions_, this, instruction); +} + +void HBasicBlock::RemovePhi(HPhi* phi) { + Remove(&phis_, this, phi); +} + +void HInstruction::RemoveUser(HInstruction* user, size_t input_index) { + HUseListNode<HInstruction>* previous = nullptr; + HUseListNode<HInstruction>* current = uses_; + while (current != nullptr) { + if (current->GetUser() == user && current->GetIndex() == input_index) { + if (previous == NULL) { + uses_ = current->GetTail(); + } else { + previous->SetTail(current->GetTail()); + } + } + previous = current; + current = current->GetTail(); + } +} + +void HInstructionList::AddInstruction(HInstruction* instruction) { if (first_instruction_ == nullptr) { DCHECK(last_instruction_ == nullptr); first_instruction_ = last_instruction_ = instruction; @@ -133,9 +241,51 @@ void HBasicBlock::AddInstruction(HInstruction* instruction) { instruction->previous_ = last_instruction_; last_instruction_ = instruction; } - for (int i = 0; i < instruction->InputCount(); i++) { - instruction->InputAt(i)->AddUse(instruction); + for (size_t i = 0; i < instruction->InputCount(); i++) { + instruction->InputAt(i)->AddUseAt(instruction, i); + } +} + +void HInstructionList::RemoveInstruction(HInstruction* instruction) { + if (instruction->previous_ != nullptr) { + instruction->previous_->next_ = instruction->next_; + } + if (instruction->next_ != nullptr) { + instruction->next_->previous_ = instruction->previous_; } + if (instruction == first_instruction_) { + first_instruction_ = instruction->next_; + } + if (instruction == last_instruction_) { + last_instruction_ = instruction->previous_; + } +} + +void HInstruction::ReplaceWith(HInstruction* other) { + for (HUseIterator<HInstruction> it(GetUses()); !it.Done(); it.Advance()) { + HUseListNode<HInstruction>* current = it.Current(); + HInstruction* user = current->GetUser(); + size_t input_index = current->GetIndex(); + user->SetRawInputAt(input_index, other); + other->AddUseAt(user, input_index); + } + + for (HUseIterator<HEnvironment> it(GetEnvUses()); !it.Done(); it.Advance()) { + HUseListNode<HEnvironment>* current = it.Current(); + HEnvironment* user = current->GetUser(); + size_t input_index = current->GetIndex(); + user->SetRawEnvAt(input_index, other); + other->AddEnvUseAt(user, input_index); + } + + uses_ = nullptr; + env_uses_ = nullptr; +} + +void HPhi::AddInput(HInstruction* input) { + DCHECK(input->GetBlock() != nullptr); + inputs_.Add(input); + input->AddUseAt(this, inputs_.Size() - 1); } #define DEFINE_ACCEPT(name) \ @@ -155,7 +305,10 @@ void HGraphVisitor::VisitInsertionOrder() { } void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { - for (HInstructionIterator it(block); !it.Done(); it.Advance()) { + for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + it.Current()->Accept(this); + } + for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { it.Current()->Accept(this); } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 3da9ed9461..581c1d56f2 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -24,9 +24,11 @@ namespace art { class HBasicBlock; +class HEnvironment; class HInstruction; class HIntConstant; class HGraphVisitor; +class HPhi; class LocationSummary; static const int kDefaultNumberOfBlocks = 8; @@ -34,6 +36,23 @@ static const int kDefaultNumberOfSuccessors = 2; static const int kDefaultNumberOfPredecessors = 2; static const int kDefaultNumberOfBackEdges = 1; +class HInstructionList { + public: + HInstructionList() : first_instruction_(nullptr), last_instruction_(nullptr) {} + + void AddInstruction(HInstruction* instruction); + void RemoveInstruction(HInstruction* instruction); + + private: + HInstruction* first_instruction_; + HInstruction* last_instruction_; + + friend class HBasicBlock; + friend class HInstructionIterator; + + DISALLOW_COPY_AND_ASSIGN(HInstructionList); +}; + // Control-flow graph of a method. Contains a list of basic blocks. class HGraph : public ArenaObject { public: @@ -56,7 +75,10 @@ class HGraph : public ArenaObject { void SetExitBlock(HBasicBlock* block) { exit_block_ = block; } void AddBlock(HBasicBlock* block); + void BuildDominatorTree(); + void TransformToSSA(); + void SimplifyCFG(); int GetNextInstructionId() { return current_instruction_id_++; @@ -86,6 +108,9 @@ class HGraph : public ArenaObject { return number_of_in_vregs_; } + GrowableArray<HBasicBlock*>* GetDominatorOrder() { + return &dominator_order_; + } private: HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const; @@ -138,7 +163,18 @@ class HLoopInformation : public ArenaObject { return back_edges_.Size(); } + void SetPreHeader(HBasicBlock* block); + + HBasicBlock* GetPreHeader() const { + return pre_header_; + } + + const GrowableArray<HBasicBlock*>* GetBackEdges() const { + return &back_edges_; + } + private: + HBasicBlock* pre_header_; HBasicBlock* header_; GrowableArray<HBasicBlock*> back_edges_; @@ -154,8 +190,6 @@ class HBasicBlock : public ArenaObject { : graph_(graph), predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors), successors_(graph->GetArena(), kDefaultNumberOfSuccessors), - first_instruction_(nullptr), - last_instruction_(nullptr), loop_information_(nullptr), dominator_(nullptr), block_id_(-1) { } @@ -189,26 +223,42 @@ class HBasicBlock : public ArenaObject { : loop_information_->NumberOfBackEdges(); } - HInstruction* GetFirstInstruction() const { return first_instruction_; } - HInstruction* GetLastInstruction() const { return last_instruction_; } + HInstruction* GetFirstInstruction() const { return instructions_.first_instruction_; } + HInstruction* GetLastInstruction() const { return instructions_.last_instruction_; } + HInstructionList const* GetInstructions() const { return &instructions_; } + HInstructionList const* GetPhis() const { return &phis_; } void AddSuccessor(HBasicBlock* block) { successors_.Add(block); block->predecessors_.Add(this); } - void RemovePredecessor(HBasicBlock* block) { + void RemovePredecessor(HBasicBlock* block, bool remove_in_successor = true) { predecessors_.Delete(block); + if (remove_in_successor) { + block->successors_.Delete(this); + } } void AddInstruction(HInstruction* instruction); + void RemoveInstruction(HInstruction* instruction); + void AddPhi(HPhi* phi); + void RemovePhi(HPhi* phi); + + bool IsLoopHeader() const { + return loop_information_ != nullptr; + } + + HLoopInformation* GetLoopInformation() const { + return loop_information_; + } private: HGraph* const graph_; GrowableArray<HBasicBlock*> predecessors_; GrowableArray<HBasicBlock*> successors_; - HInstruction* first_instruction_; - HInstruction* last_instruction_; + HInstructionList instructions_; + HInstructionList phis_; HLoopInformation* loop_information_; HBasicBlock* dominator_; int block_id_; @@ -230,6 +280,7 @@ class HBasicBlock : public ArenaObject { M(NewInstance) \ M(Not) \ M(ParameterValue) \ + M(Phi) \ M(Return) \ M(ReturnVoid) \ M(StoreLocal) \ @@ -244,17 +295,22 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) virtual const char* DebugName() const { return #type; } \ virtual H##type* As##type() { return this; } \ +template <typename T> class HUseListNode : public ArenaObject { public: - HUseListNode(HInstruction* instruction, HUseListNode* tail) - : instruction_(instruction), tail_(tail) { } + HUseListNode(T* user, size_t index, HUseListNode* tail) + : user_(user), index_(index), tail_(tail) { } HUseListNode* GetTail() const { return tail_; } - HInstruction* GetInstruction() const { return instruction_; } + T* GetUser() const { return user_; } + size_t GetIndex() const { return index_; } + + void SetTail(HUseListNode<T>* node) { tail_ = node; } private: - HInstruction* const instruction_; - HUseListNode* const tail_; + T* const user_; + const size_t index_; + HUseListNode<T>* tail_; DISALLOW_COPY_AND_ASSIGN(HUseListNode); }; @@ -267,6 +323,8 @@ class HInstruction : public ArenaObject { block_(nullptr), id_(-1), uses_(nullptr), + env_uses_(nullptr), + environment_(nullptr), locations_(nullptr) { } virtual ~HInstruction() { } @@ -277,28 +335,43 @@ class HInstruction : public ArenaObject { HBasicBlock* GetBlock() const { return block_; } void SetBlock(HBasicBlock* block) { block_ = block; } - virtual intptr_t InputCount() const = 0; - virtual HInstruction* InputAt(intptr_t i) const = 0; + virtual size_t InputCount() const = 0; + virtual HInstruction* InputAt(size_t i) const = 0; virtual void Accept(HGraphVisitor* visitor) = 0; virtual const char* DebugName() const = 0; virtual Primitive::Type GetType() const { return Primitive::kPrimVoid; } + virtual void SetRawInputAt(size_t index, HInstruction* input) = 0; + + virtual bool NeedsEnvironment() const { return false; } - void AddUse(HInstruction* user) { - uses_ = new (block_->GetGraph()->GetArena()) HUseListNode(user, uses_); + void AddUseAt(HInstruction* user, size_t index) { + uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HInstruction>(user, index, uses_); } - HUseListNode* GetUses() const { return uses_; } + void AddEnvUseAt(HEnvironment* user, size_t index) { + env_uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HEnvironment>( + user, index, env_uses_); + } + + void RemoveUser(HInstruction* user, size_t index); + + HUseListNode<HInstruction>* GetUses() const { return uses_; } + HUseListNode<HEnvironment>* GetEnvUses() const { return env_uses_; } bool HasUses() const { return uses_ != nullptr; } int GetId() const { return id_; } void SetId(int id) { id_ = id; } + void SetEnvironment(HEnvironment* environment) { environment_ = environment; } + LocationSummary* GetLocations() const { return locations_; } void SetLocations(LocationSummary* locations) { locations_ = locations; } + void ReplaceWith(HInstruction* instruction); + #define INSTRUCTION_TYPE_CHECK(type) \ virtual H##type* As##type() { return nullptr; } @@ -315,19 +388,27 @@ class HInstruction : public ArenaObject { // has not beed added to the graph. int id_; - HUseListNode* uses_; + // List of instructions that have this instruction as input. + HUseListNode<HInstruction>* uses_; + + // List of environments that contain this instruction. + HUseListNode<HEnvironment>* env_uses_; + + HEnvironment* environment_; // Set by the code generator. LocationSummary* locations_; friend class HBasicBlock; + friend class HInstructionList; DISALLOW_COPY_AND_ASSIGN(HInstruction); }; +template<typename T> class HUseIterator : public ValueObject { public: - explicit HUseIterator(HInstruction* instruction) : current_(instruction->GetUses()) { } + explicit HUseIterator(HUseListNode<T>* uses) : current_(uses) {} bool Done() const { return current_ == nullptr; } @@ -336,17 +417,51 @@ class HUseIterator : public ValueObject { current_ = current_->GetTail(); } - HInstruction* Current() const { + HUseListNode<T>* Current() const { DCHECK(!Done()); - return current_->GetInstruction(); + return current_; } private: - HUseListNode* current_; + HUseListNode<T>* current_; friend class HValue; }; +// A HEnvironment object contains the values of virtual registers at a given location. +class HEnvironment : public ArenaObject { + public: + HEnvironment(ArenaAllocator* arena, size_t number_of_vregs) : vregs_(arena, number_of_vregs) { + vregs_.SetSize(number_of_vregs); + for (size_t i = 0; i < number_of_vregs; i++) { + vregs_.Put(i, nullptr); + } + } + + void Populate(const GrowableArray<HInstruction*>& env) { + for (size_t i = 0; i < env.Size(); i++) { + HInstruction* instruction = env.Get(i); + vregs_.Put(i, instruction); + if (instruction != nullptr) { + instruction->AddEnvUseAt(this, i); + } + } + } + + void SetRawEnvAt(size_t index, HInstruction* instruction) { + vregs_.Put(index, instruction); + } + + GrowableArray<HInstruction*>* GetVRegs() { + return &vregs_; + } + + private: + GrowableArray<HInstruction*> vregs_; + + DISALLOW_COPY_AND_ASSIGN(HEnvironment); +}; + class HInputIterator : public ValueObject { public: explicit HInputIterator(HInstruction* instruction) : instruction_(instruction), index_(0) { } @@ -357,15 +472,15 @@ class HInputIterator : public ValueObject { private: HInstruction* instruction_; - int index_; + size_t index_; DISALLOW_COPY_AND_ASSIGN(HInputIterator); }; class HInstructionIterator : public ValueObject { public: - explicit HInstructionIterator(HBasicBlock* block) - : instruction_(block->GetFirstInstruction()) { + explicit HInstructionIterator(const HInstructionList& instructions) + : instruction_(instructions.first_instruction_) { next_ = Done() ? nullptr : instruction_->GetNext(); } @@ -434,16 +549,18 @@ class HTemplateInstruction: public HInstruction { HTemplateInstruction<N>() : inputs_() { } virtual ~HTemplateInstruction() { } - virtual intptr_t InputCount() const { return N; } - virtual HInstruction* InputAt(intptr_t i) const { return inputs_[i]; } + virtual size_t InputCount() const { return N; } + virtual HInstruction* InputAt(size_t i) const { return inputs_[i]; } protected: - void SetRawInputAt(intptr_t i, HInstruction* instruction) { + virtual void SetRawInputAt(size_t i, HInstruction* instruction) { inputs_[i] = instruction; } private: EmbeddedArray<HInstruction*, N> inputs_; + + friend class SsaBuilder; }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow @@ -658,11 +775,19 @@ class HInvoke : public HInstruction { inputs_.SetSize(number_of_arguments); } - virtual intptr_t InputCount() const { return inputs_.Size(); } - virtual HInstruction* InputAt(intptr_t i) const { return inputs_.Get(i); } + virtual size_t InputCount() const { return inputs_.Size(); } + virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); } + + // Runtime needs to walk the stack, so Dex -> Dex calls need to + // know their environment. + virtual bool NeedsEnvironment() const { return true; } void SetArgumentAt(size_t index, HInstruction* argument) { - inputs_.Put(index, argument); + SetRawInputAt(index, argument); + } + + virtual void SetRawInputAt(size_t index, HInstruction* input) { + inputs_.Put(index, input); } virtual Primitive::Type GetType() const { return return_type_; } @@ -707,6 +832,9 @@ class HNewInstance : public HTemplateInstruction<0> { virtual Primitive::Type GetType() const { return Primitive::kPrimNot; } + // Calls runtime so needs an environment. + virtual bool NeedsEnvironment() const { return true; } + DECLARE_INSTRUCTION(NewInstance) private: @@ -779,6 +907,39 @@ class HNot : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HNot); }; +class HPhi : public HInstruction { + public: + HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type) + : inputs_(arena, number_of_inputs), + reg_number_(reg_number), + type_(type) { + inputs_.SetSize(number_of_inputs); + } + + virtual size_t InputCount() const { return inputs_.Size(); } + virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); } + + virtual void SetRawInputAt(size_t index, HInstruction* input) { + inputs_.Put(index, input); + } + + void AddInput(HInstruction* input); + + virtual Primitive::Type GetType() const { return type_; } + + uint32_t GetRegNumber() const { return reg_number_; } + + DECLARE_INSTRUCTION(Phi) + + protected: + GrowableArray<HInstruction*> inputs_; + const uint32_t reg_number_; + const Primitive::Type type_; + + private: + DISALLOW_COPY_AND_ASSIGN(HPhi); +}; + class HGraphVisitor : public ValueObject { public: explicit HGraphVisitor(HGraph* graph) : graph_(graph) { } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index d19c40c291..9438890941 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -100,6 +100,10 @@ CompiledMethod* OptimizingCompiler::TryCompile(CompilerDriver& driver, std::vector<uint8_t> gc_map; codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit); + // Run these phases to get some test coverage. + graph->BuildDominatorTree(); + graph->TransformToSSA(); + return new CompiledMethod(driver, instruction_set, allocator.GetMemory(), diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h index 606c91519e..c82d0cc6a4 100644 --- a/compiler/optimizing/pretty_printer.h +++ b/compiler/optimizing/pretty_printer.h @@ -25,11 +25,19 @@ class HPrettyPrinter : public HGraphVisitor { public: explicit HPrettyPrinter(HGraph* graph) : HGraphVisitor(graph) { } - virtual void VisitInstruction(HInstruction* instruction) { + void PrintPreInstruction(HInstruction* instruction) { PrintString(" "); PrintInt(instruction->GetId()); PrintString(": "); + } + + virtual void VisitInstruction(HInstruction* instruction) { + PrintPreInstruction(instruction); PrintString(instruction->DebugName()); + PrintPostInstruction(instruction); + } + + void PrintPostInstruction(HInstruction* instruction) { if (instruction->InputCount() != 0) { PrintString("("); bool first = true; @@ -46,13 +54,13 @@ class HPrettyPrinter : public HGraphVisitor { if (instruction->HasUses()) { PrintString(" ["); bool first = true; - for (HUseIterator it(instruction); !it.Done(); it.Advance()) { + for (HUseIterator<HInstruction> it(instruction->GetUses()); !it.Done(); it.Advance()) { if (first) { first = false; } else { PrintString(", "); } - PrintInt(it.Current()->GetId()); + PrintInt(it.Current()->GetUser()->GetId()); } PrintString("]"); } diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc new file mode 100644 index 0000000000..bfb4f38f50 --- /dev/null +++ b/compiler/optimizing/ssa_builder.cc @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ssa_builder.h" +#include "nodes.h" + +namespace art { + +void SsaBuilder::BuildSsa() { + // 1) Visit in dominator order. We need to have all predecessors of a block visited + // (with the exception of loops) in order to create the right environment for that + // block. For loops, we create phis whose inputs will be set in 2). + for (size_t i = 0; i < GetGraph()->GetDominatorOrder()->Size(); i++) { + VisitBasicBlock(GetGraph()->GetDominatorOrder()->Get(i)); + } + + // 2) Set inputs of loop phis. + for (size_t i = 0; i < loop_headers_.Size(); i++) { + HBasicBlock* block = loop_headers_.Get(i); + for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + HPhi* phi = it.Current()->AsPhi(); + for (size_t pred = 0; pred < block->GetPredecessors()->Size(); pred++) { + phi->AddInput(ValueOfLocal(block->GetPredecessors()->Get(pred), phi->GetRegNumber())); + } + } + } + + // 3) Clear locals. + // TODO: Move this to a dead code eliminator phase. + for (HInstructionIterator it(*GetGraph()->GetEntryBlock()->GetInstructions()); + !it.Done(); + it.Advance()) { + HInstruction* current = it.Current(); + if (current->AsLocal() != nullptr) { + current->GetBlock()->RemoveInstruction(current); + } + } +} + +HInstruction* SsaBuilder::ValueOfLocal(HBasicBlock* block, size_t local) { + return GetLocalsFor(block)->Get(local); +} + +void SsaBuilder::VisitBasicBlock(HBasicBlock* block) { + current_locals_ = GetLocalsFor(block); + + if (block->IsLoopHeader()) { + // If the block is a loop header, we know we only have visited the pre header + // because we are visiting in dominator order. We create phis for all initialized + // locals from the pre header. Their inputs will be populated at the end of + // the analysis. + for (size_t local = 0; local < current_locals_->Size(); local++) { + HInstruction* incoming = ValueOfLocal(block->GetLoopInformation()->GetPreHeader(), local); + if (incoming != nullptr) { + // TODO: Compute union type. + HPhi* phi = new (GetGraph()->GetArena()) HPhi( + GetGraph()->GetArena(), local, 0, Primitive::kPrimVoid); + block->AddPhi(phi); + current_locals_->Put(local, phi); + } + } + // Save the loop header so that the last phase of the analysis knows which + // blocks need to be updated. + loop_headers_.Add(block); + } else if (block->GetPredecessors()->Size() > 0) { + // All predecessors have already been visited because we are visiting in dominator order. + // We merge the values of all locals, creating phis if those values differ. + for (size_t local = 0; local < current_locals_->Size(); local++) { + bool is_different = false; + HInstruction* value = ValueOfLocal(block->GetPredecessors()->Get(0), local); + for (size_t i = 1; i < block->GetPredecessors()->Size(); i++) { + if (ValueOfLocal(block->GetPredecessors()->Get(i), local) != value) { + is_different = true; + break; + } + } + if (is_different) { + // TODO: Compute union type. + HPhi* phi = new (GetGraph()->GetArena()) HPhi( + GetGraph()->GetArena(), local, block->GetPredecessors()->Size(), Primitive::kPrimVoid); + for (size_t i = 0; i < block->GetPredecessors()->Size(); i++) { + phi->SetRawInputAt(i, ValueOfLocal(block->GetPredecessors()->Get(i), local)); + } + block->AddPhi(phi); + value = phi; + } + current_locals_->Put(local, value); + } + } + + // Visit all instructions. The instructions of interest are: + // - HLoadLocal: replace them with the current value of the local. + // - HStoreLocal: update current value of the local and remove the instruction. + // - Instructions that require an environment: populate their environment + // with the current values of the locals. + for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + it.Current()->Accept(this); + } +} + +void SsaBuilder::VisitLoadLocal(HLoadLocal* load) { + load->ReplaceWith(current_locals_->Get(load->GetLocal()->GetRegNumber())); + load->GetBlock()->RemoveInstruction(load); +} + +void SsaBuilder::VisitStoreLocal(HStoreLocal* store) { + current_locals_->Put(store->GetLocal()->GetRegNumber(), store->InputAt(1)); + store->GetBlock()->RemoveInstruction(store); +} + +void SsaBuilder::VisitInstruction(HInstruction* instruction) { + if (!instruction->NeedsEnvironment()) { + return; + } + HEnvironment* environment = new (GetGraph()->GetArena()) HEnvironment( + GetGraph()->GetArena(), current_locals_->Size()); + environment->Populate(*current_locals_); + instruction->SetEnvironment(environment); +} + +} // namespace art diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h new file mode 100644 index 0000000000..b6c6c0b658 --- /dev/null +++ b/compiler/optimizing/ssa_builder.h @@ -0,0 +1,71 @@ +/* + * 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_OPTIMIZING_SSA_BUILDER_H_ +#define ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_ + +#include "nodes.h" + +namespace art { + +static constexpr int kDefaultNumberOfLoops = 2; + +class SsaBuilder : public HGraphVisitor { + public: + explicit SsaBuilder(HGraph* graph) + : HGraphVisitor(graph), + current_locals_(nullptr), + loop_headers_(graph->GetArena(), kDefaultNumberOfLoops), + locals_for_(graph->GetArena(), graph->GetBlocks()->Size()) { + locals_for_.SetSize(graph->GetBlocks()->Size()); + } + + void BuildSsa(); + + GrowableArray<HInstruction*>* GetLocalsFor(HBasicBlock* block) { + HEnvironment* env = locals_for_.Get(block->GetBlockId()); + if (env == nullptr) { + env = new (GetGraph()->GetArena()) HEnvironment( + GetGraph()->GetArena(), GetGraph()->GetNumberOfVRegs()); + locals_for_.Put(block->GetBlockId(), env); + } + return env->GetVRegs(); + } + + HInstruction* ValueOfLocal(HBasicBlock* block, size_t local); + + void VisitBasicBlock(HBasicBlock* block); + void VisitLoadLocal(HLoadLocal* load); + void VisitStoreLocal(HStoreLocal* store); + void VisitInstruction(HInstruction* instruction); + + private: + // Locals for the current block being visited. + GrowableArray<HInstruction*>* current_locals_; + + // Keep track of loop headers found. The last phase of the analysis iterates + // over these blocks to set the inputs of their phis. + GrowableArray<HBasicBlock*> loop_headers_; + + // HEnvironment for each block. + GrowableArray<HEnvironment*> locals_for_; + + DISALLOW_COPY_AND_ASSIGN(SsaBuilder); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_ diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc new file mode 100644 index 0000000000..7c3633b5e9 --- /dev/null +++ b/compiler/optimizing/ssa_test.cc @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/stringprintf.h" +#include "builder.h" +#include "dex_file.h" +#include "dex_instruction.h" +#include "nodes.h" +#include "optimizing_unit_test.h" +#include "pretty_printer.h" +#include "ssa_builder.h" +#include "utils/arena_allocator.h" + +#include "gtest/gtest.h" + +namespace art { + +class StringPrettyPrinter : public HPrettyPrinter { + public: + explicit StringPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {} + + virtual void PrintInt(int value) { + str_ += StringPrintf("%d", value); + } + + virtual void PrintString(const char* value) { + str_ += value; + } + + virtual void PrintNewLine() { + str_ += '\n'; + } + + void Clear() { str_.clear(); } + + std::string str() const { return str_; } + + virtual void VisitIntConstant(HIntConstant* constant) { + PrintPreInstruction(constant); + str_ += constant->DebugName(); + str_ += " "; + PrintInt(constant->GetValue()); + PrintPostInstruction(constant); + } + + private: + std::string str_; + + DISALLOW_COPY_AND_ASSIGN(StringPrettyPrinter); +}; + +static void ReNumberInstructions(HGraph* graph) { + int id = 0; + for (size_t i = 0; i < graph->GetBlocks()->Size(); i++) { + HBasicBlock* block = graph->GetBlocks()->Get(i); + for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) { + it.Current()->SetId(id++); + } + for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) { + it.Current()->SetId(id++); + } + } +} + +static void TestCode(const uint16_t* data, const char* expected) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraphBuilder builder(&allocator); + const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); + HGraph* graph = builder.BuildGraph(*item); + ASSERT_NE(graph, nullptr); + graph->BuildDominatorTree(); + graph->TransformToSSA(); + ReNumberInstructions(graph); + + StringPrettyPrinter printer(graph); + printer.VisitInsertionOrder(); + + ASSERT_STREQ(expected, printer.str().c_str()); +} + +TEST(SsaTest, CFG1) { + // Test that we get rid of loads and stores. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [2, 2]\n" + " 1: Goto\n" + "BasicBlock 1, pred: 0, succ: 3, 2\n" + " 2: Equal(0, 0) [3]\n" + " 3: If(2)\n" + "BasicBlock 2, pred: 1, succ: 3\n" + " 4: Goto\n" + "BasicBlock 3, pred: 1, 2, succ: 4\n" + " 5: ReturnVoid\n" + "BasicBlock 4, pred: 3\n" + " 6: Exit\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 3, + Instruction::GOTO | 0x100, + Instruction::RETURN_VOID); + + TestCode(data, expected); +} + +TEST(SsaTest, CFG2) { + // Test that we create a phi for the join block of an if control flow instruction + // when there is only code in the else branch. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [6, 3, 3]\n" + " 1: IntConstant 4 [6]\n" + " 2: Goto\n" + "BasicBlock 1, pred: 0, succ: 3, 2\n" + " 3: Equal(0, 0) [4]\n" + " 4: If(3)\n" + "BasicBlock 2, pred: 1, succ: 3\n" + " 5: Goto\n" + "BasicBlock 3, pred: 1, 2, succ: 4\n" + " 6: Phi(0, 1) [7]\n" + " 7: Return(6)\n" + "BasicBlock 4, pred: 3\n" + " 8: Exit\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 3, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::RETURN | 0 << 8); + + TestCode(data, expected); +} + +TEST(SsaTest, CFG3) { + // Test that we create a phi for the join block of an if control flow instruction + // when there both branches update a local. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [4, 4]\n" + " 1: IntConstant 4 [8]\n" + " 2: IntConstant 5 [8]\n" + " 3: Goto\n" + "BasicBlock 1, pred: 0, succ: 3, 2\n" + " 4: Equal(0, 0) [5]\n" + " 5: If(4)\n" + "BasicBlock 2, pred: 1, succ: 4\n" + " 6: Goto\n" + "BasicBlock 3, pred: 1, succ: 4\n" + " 7: Goto\n" + "BasicBlock 4, pred: 2, 3, succ: 5\n" + " 8: Phi(1, 2) [9]\n" + " 9: Return(8)\n" + "BasicBlock 5, pred: 4\n" + " 10: Exit\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 4, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::GOTO | 0x200, + Instruction::CONST_4 | 5 << 12 | 0, + Instruction::RETURN | 0 << 8); + + TestCode(data, expected); +} + +TEST(SsaTest, Loop1) { + // Test that we create a phi for an initialized local at entry of a loop. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [6, 4, 2, 2]\n" + " 1: Goto\n" + "BasicBlock 1, pred: 0, succ: 3, 2\n" + " 2: Equal(0, 0) [3]\n" + " 3: If(2)\n" + "BasicBlock 2, pred: 1, 3, succ: 3\n" + " 4: Phi(0, 6) [6]\n" + " 5: Goto\n" + "BasicBlock 3, pred: 1, 2, succ: 2\n" + " 6: Phi(0, 4) [4]\n" + " 7: Goto\n" + "BasicBlock 4\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 3, + Instruction::GOTO | 0x100, + Instruction::GOTO | 0xFF00); + + TestCode(data, expected); +} + +TEST(SsaTest, Loop2) { + // Simple loop with one preheader and one back edge. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [4]\n" + " 1: IntConstant 4 [4]\n" + " 2: Goto\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 3: Goto\n" + "BasicBlock 2, pred: 1, 3, succ: 4, 3\n" + " 4: Phi(0, 1) [5, 5]\n" + " 5: Equal(4, 4) [6]\n" + " 6: If(5)\n" + "BasicBlock 3, pred: 2, succ: 2\n" + " 7: Goto\n" + "BasicBlock 4, pred: 2, succ: 5\n" + " 8: ReturnVoid\n" + "BasicBlock 5, pred: 4\n" + " 9: Exit\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 4, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::GOTO | 0xFD00, + Instruction::RETURN_VOID); + + TestCode(data, expected); +} + +TEST(SsaTest, Loop3) { + // Test that a local not yet defined at the entry of a loop is handled properly. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [5]\n" + " 1: IntConstant 4 [5]\n" + " 2: IntConstant 5 [9]\n" + " 3: Goto\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 4: Goto\n" + "BasicBlock 2, pred: 1, 3, succ: 4, 3\n" + " 5: Phi(0, 1) [6, 6]\n" + " 6: Equal(5, 5) [7]\n" + " 7: If(6)\n" + "BasicBlock 3, pred: 2, succ: 2\n" + " 8: Goto\n" + "BasicBlock 4, pred: 2, succ: 5\n" + " 9: Return(2)\n" + "BasicBlock 5, pred: 4\n" + " 10: Exit\n"; + + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 4, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::GOTO | 0xFD00, + Instruction::CONST_4 | 5 << 12 | 1 << 8, + Instruction::RETURN | 1 << 8); + + TestCode(data, expected); +} + +TEST(SsaTest, Loop4) { + // Make sure we support a preheader of a loop not being the first predecessor + // in the predecessor list of the header. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [4]\n" + " 1: IntConstant 4 [4]\n" + " 2: Goto\n" + "BasicBlock 1, pred: 0, succ: 4\n" + " 3: Goto\n" + "BasicBlock 2, pred: 3, 4, succ: 5, 3\n" + " 4: Phi(1, 0) [9, 5, 5]\n" + " 5: Equal(4, 4) [6]\n" + " 6: If(5)\n" + "BasicBlock 3, pred: 2, succ: 2\n" + " 7: Goto\n" + "BasicBlock 4, pred: 1, succ: 2\n" + " 8: Goto\n" + "BasicBlock 5, pred: 2, succ: 6\n" + " 9: Return(4)\n" + "BasicBlock 6, pred: 5\n" + " 10: Exit\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::GOTO | 0x500, + Instruction::IF_EQ, 5, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::GOTO | 0xFD00, + Instruction::GOTO | 0xFC00, + Instruction::RETURN | 0 << 8); + + TestCode(data, expected); +} + +TEST(SsaTest, Loop5) { + // Make sure we create a preheader of a loop when a header originally has two + // incoming blocks and one back edge. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [4, 4]\n" + " 1: IntConstant 4 [14]\n" + " 2: IntConstant 5 [14]\n" + " 3: Goto\n" + "BasicBlock 1, pred: 0, succ: 3, 2\n" + " 4: Equal(0, 0) [5]\n" + " 5: If(4)\n" + "BasicBlock 2, pred: 1, succ: 8\n" + " 6: Goto\n" + "BasicBlock 3, pred: 1, succ: 8\n" + " 7: Goto\n" + "BasicBlock 4, pred: 5, 8, succ: 6, 5\n" + " 8: Phi(8, 14) [8, 12, 9, 9]\n" + " 9: Equal(8, 8) [10]\n" + " 10: If(9)\n" + "BasicBlock 5, pred: 4, succ: 4\n" + " 11: Goto\n" + "BasicBlock 6, pred: 4, succ: 7\n" + " 12: Return(8)\n" + "BasicBlock 7, pred: 6\n" + " 13: Exit\n" + "BasicBlock 8, pred: 2, 3, succ: 4\n" + " 14: Phi(1, 2) [8]\n" + " 15: Goto\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 4, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::GOTO | 0x200, + Instruction::CONST_4 | 5 << 12 | 0, + Instruction::IF_EQ, 3, + Instruction::GOTO | 0xFE00, + Instruction::RETURN | 0 << 8); + + TestCode(data, expected); +} + +TEST(SsaTest, Loop6) { + // Test a loop with one preheader and two back edges (e.g. continue). + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [5]\n" + " 1: IntConstant 4 [5, 8, 8]\n" + " 2: IntConstant 5 [5]\n" + " 3: Goto\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 4: Goto\n" + "BasicBlock 2, pred: 1, 4, 5, succ: 6, 3\n" + " 5: Phi(0, 2, 1) [12, 6, 6]\n" + " 6: Equal(5, 5) [7]\n" + " 7: If(6)\n" + "BasicBlock 3, pred: 2, succ: 5, 4\n" + " 8: Equal(1, 1) [9]\n" + " 9: If(8)\n" + "BasicBlock 4, pred: 3, succ: 2\n" + " 10: Goto\n" + "BasicBlock 5, pred: 3, succ: 2\n" + " 11: Goto\n" + "BasicBlock 6, pred: 2, succ: 7\n" + " 12: Return(5)\n" + "BasicBlock 7, pred: 6\n" + " 13: Exit\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 8, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::IF_EQ, 4, + Instruction::CONST_4 | 5 << 12 | 0, + Instruction::GOTO | 0xFA00, + Instruction::GOTO | 0xF900, + Instruction::RETURN | 0 << 8); + + TestCode(data, expected); +} + +TEST(SsaTest, Loop7) { + // Test a loop with one preheader, one back edge, and two exit edges (e.g. break). + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0 [5]\n" + " 1: IntConstant 4 [5, 8, 8]\n" + " 2: IntConstant 5 [12]\n" + " 3: Goto\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 4: Goto\n" + "BasicBlock 2, pred: 1, 5, succ: 6, 3\n" + " 5: Phi(0, 1) [12, 6, 6]\n" + " 6: Equal(5, 5) [7]\n" + " 7: If(6)\n" + "BasicBlock 3, pred: 2, succ: 5, 4\n" + " 8: Equal(1, 1) [9]\n" + " 9: If(8)\n" + "BasicBlock 4, pred: 3, succ: 6\n" + " 10: Goto\n" + "BasicBlock 5, pred: 3, succ: 2\n" + " 11: Goto\n" + "BasicBlock 6, pred: 2, 4, succ: 7\n" + " 12: Phi(5, 2) [13]\n" + " 13: Return(12)\n" + "BasicBlock 7, pred: 6\n" + " 14: Exit\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 8, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::IF_EQ, 4, + Instruction::CONST_4 | 5 << 12 | 0, + Instruction::GOTO | 0x0200, + Instruction::GOTO | 0xF900, + Instruction::RETURN | 0 << 8); + + TestCode(data, expected); +} + +TEST(SsaTest, DeadLocal) { + // Test that we correctly handle a local not being used. + const char* expected = + "BasicBlock 0, succ: 1\n" + " 0: IntConstant 0\n" + " 1: Goto\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 2: ReturnVoid\n" + "BasicBlock 2, pred: 1\n" + " 3: Exit\n"; + + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::RETURN_VOID); + + TestCode(data, expected); +} + +} // namespace art diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h index e6c53dab24..993492da6d 100644 --- a/compiler/utils/growable_array.h +++ b/compiler/utils/growable_array.h @@ -31,7 +31,6 @@ enum OatListKind { kGrowableArrayDfsOrder, kGrowableArrayDfsPostOrder, kGrowableArrayDomPostOrderTraversal, - kGrowableArrayThrowLaunchPads, kGrowableArraySuspendLaunchPads, kGrowableArraySwitchTables, kGrowableArrayFillArrayData, diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 7d02c7c8a8..9507e1207a 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -1493,7 +1493,7 @@ void X86_64Assembler::EmitOptionalRex(bool force, bool w, bool r, bool x, bool b } void X86_64Assembler::EmitOptionalRex32(CpuRegister reg) { - EmitOptionalRex(false, false, reg.NeedsRex(), false, false); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); } void X86_64Assembler::EmitOptionalRex32(CpuRegister dst, CpuRegister src) { @@ -1540,8 +1540,9 @@ void X86_64Assembler::EmitOptionalRex32(XmmRegister dst, const Operand& operand) } void X86_64Assembler::EmitRex64(CpuRegister reg) { - EmitOptionalRex(false, true, reg.NeedsRex(), false, false); + EmitOptionalRex(false, true, false, false, reg.NeedsRex()); } + void X86_64Assembler::EmitRex64(CpuRegister dst, CpuRegister src) { EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex()); } |