diff options
Diffstat (limited to 'compiler')
103 files changed, 7589 insertions, 1638 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 96e13ac9a3..20c80235ba 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -66,6 +66,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/builder.cc \ optimizing/code_generator.cc \ optimizing/code_generator_utils.cc \ + optimizing/constant_area_fixups_x86.cc \ optimizing/constant_folding.cc \ optimizing/dead_code_elimination.cc \ optimizing/graph_checker.cc \ @@ -210,7 +211,8 @@ LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm64 := \ dex/quick/arm64/arm64_lir.h LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_mips := \ - dex/quick/mips/mips_lir.h + dex/quick/mips/mips_lir.h \ + utils/mips/assembler_mips.h LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_mips64 := \ $(LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_mips) diff --git a/compiler/compiler.h b/compiler/compiler.h index 9b4dbe02e2..8788dc1950 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -77,9 +77,8 @@ class Compiler { * information. * @note This is used for backtrace information in generated code. */ - virtual std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver) - const { - UNUSED(driver); + virtual std::vector<uint8_t>* GetCallFrameInformationInitialization( + const CompilerDriver& driver ATTRIBUTE_UNUSED) const { return nullptr; } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 4efe4af896..b0972d98d4 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -511,9 +511,8 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffs /* Process instructions with the kSwitch flag */ BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, - int width, int flags, + int width, int flags ATTRIBUTE_UNUSED, ScopedArenaVector<uint16_t>* dex_pc_to_block_map) { - UNUSED(flags); const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(GetCurrentInsns() + cur_offset + static_cast<int32_t>(insn->dalvikInsn.vB)); @@ -592,11 +591,15 @@ BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffs } /* Process instructions with the kThrow flag */ -BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, - int width, int flags, ArenaBitVector* try_block_addr, - const uint16_t* code_ptr, const uint16_t* code_end, +BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, + MIR* insn, + DexOffset cur_offset, + int width, + int flags ATTRIBUTE_UNUSED, + ArenaBitVector* try_block_addr, + const uint16_t* code_ptr, + const uint16_t* code_end, ScopedArenaVector<uint16_t>* dex_pc_to_block_map) { - UNUSED(flags); bool in_try_block = try_block_addr->IsBitSet(cur_offset); bool is_throw = (insn->dalvikInsn.opcode == Instruction::THROW); diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h index 0def056f4f..16414efada 100644 --- a/compiler/dex/pass.h +++ b/compiler/dex/pass.h @@ -53,10 +53,7 @@ class Pass { * @param data the PassDataHolder. * @return whether or not to execute the pass. */ - virtual bool Gate(const PassDataHolder* data) const { - // Unused parameter. - UNUSED(data); - + virtual bool Gate(const PassDataHolder* data ATTRIBUTE_UNUSED) const { // Base class says yes. return true; } @@ -64,17 +61,13 @@ class Pass { /** * @brief Start of the pass: called before the Worker function. */ - virtual void Start(PassDataHolder* data) const { - // Unused parameter. - UNUSED(data); + virtual void Start(PassDataHolder* data ATTRIBUTE_UNUSED) const { } /** * @brief End of the pass: called after the WalkBasicBlocks function. */ - virtual void End(PassDataHolder* data) const { - // Unused parameter. - UNUSED(data); + virtual void End(PassDataHolder* data ATTRIBUTE_UNUSED) const { } /** diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h index 8762b53af4..34a6f630f1 100644 --- a/compiler/dex/pass_driver.h +++ b/compiler/dex/pass_driver.h @@ -125,8 +125,7 @@ class PassDriver { * @brief Dispatch a patch. * Gives the ability to add logic when running the patch. */ - virtual void DispatchPass(const Pass* pass) { - UNUSED(pass); + virtual void DispatchPass(const Pass* pass ATTRIBUTE_UNUSED) { } /** @brief List of passes: provides the order to execute the passes. diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index db76cc6f53..b2bd6faca2 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -216,8 +216,7 @@ void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, void ArmMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code, int32_t true_val, int32_t false_val, RegStorage rs_dest, - RegisterClass dest_reg_class) { - UNUSED(dest_reg_class); + RegisterClass dest_reg_class ATTRIBUTE_UNUSED) { // TODO: Generalize the IT below to accept more than one-instruction loads. DCHECK(InexpensiveConstantInt(true_val)); DCHECK(InexpensiveConstantInt(false_val)); @@ -239,8 +238,7 @@ void ArmMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, Condi OpEndIT(it); } -void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { - UNUSED(bb); +void ArmMir2Lir::GenSelect(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir) { RegLocation rl_result; RegLocation rl_src = mir_graph_->GetSrc(mir, 0); RegLocation rl_dest = mir_graph_->GetDest(mir); @@ -516,9 +514,8 @@ static const MagicTable magic_table[] = { }; // Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4) -bool ArmMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, +bool ArmMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED, bool is_div, RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(dalvik_opcode); if ((lit < 0) || (lit >= static_cast<int>(sizeof(magic_table)/sizeof(magic_table[0])))) { return false; } @@ -728,16 +725,19 @@ bool ArmMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) return true; } -RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, bool is_div, int flags) { - UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags); +RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + RegLocation rl_src2 ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of GenDivRem for Arm"; UNREACHABLE(); } -RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, - bool is_div) { - UNUSED(rl_dest, rl_src1, lit, is_div); +RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm"; UNREACHABLE(); } @@ -1160,9 +1160,8 @@ void ArmMir2Lir::GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLoc } void ArmMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, - RegLocation rl_result, int lit, + RegLocation rl_result, int lit ATTRIBUTE_UNUSED, int first_bit, int second_bit) { - UNUSED(lit); OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg, EncodeShift(kArmLsl, second_bit - first_bit)); if (first_bit != 0) { @@ -1257,9 +1256,8 @@ void ArmMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { StoreValueWide(rl_dest, rl_result); } -void ArmMir2Lir::GenMulLong(Instruction::Code opcode, RegLocation rl_dest, +void ArmMir2Lir::GenMulLong(Instruction::Code opcode ATTRIBUTE_UNUSED, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { - UNUSED(opcode); /* * tmp1 = src1.hi * src2.lo; // src1.hi is no longer needed * dest = src1.lo * src2.lo; @@ -1564,8 +1562,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, void ArmMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift, - int flags) { - UNUSED(flags); + int flags ATTRIBUTE_UNUSED) { rl_src = LoadValueWide(rl_src, kCoreReg); // Per spec, we only care about low 6 bits of shift amount. int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f; diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 5f27338e6b..355485e03b 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -987,8 +987,7 @@ int ArmMir2Lir::GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) { return count; } -void ArmMir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) { - UNUSED(bb); +void ArmMir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir) { DCHECK(MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)); RegLocation rl_src[3]; RegLocation rl_dest = mir_graph_->GetBadLoc(); diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index 062f7aff66..c31f46b8fe 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -419,20 +419,26 @@ LIR* ArmMir2Lir::OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2) return OpRegRegShift(op, r_dest_src1, r_src2, 0); } -LIR* ArmMir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, MoveType move_type) { - UNUSED(r_dest, r_base, offset, move_type); +LIR* ArmMir2Lir::OpMovRegMem(RegStorage r_dest ATTRIBUTE_UNUSED, + RegStorage r_base ATTRIBUTE_UNUSED, + int offset ATTRIBUTE_UNUSED, + MoveType move_type ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL); UNREACHABLE(); } -LIR* ArmMir2Lir::OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type) { - UNUSED(r_base, offset, r_src, move_type); +LIR* ArmMir2Lir::OpMovMemReg(RegStorage r_base ATTRIBUTE_UNUSED, + int offset ATTRIBUTE_UNUSED, + RegStorage r_src ATTRIBUTE_UNUSED, + MoveType move_type ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL); UNREACHABLE(); } -LIR* ArmMir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src) { - UNUSED(op, cc, r_dest, r_src); +LIR* ArmMir2Lir::OpCondRegReg(OpKind op ATTRIBUTE_UNUSED, + ConditionCode cc ATTRIBUTE_UNUSED, + RegStorage r_dest ATTRIBUTE_UNUSED, + RegStorage r_src ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpCondRegReg for Arm"; UNREACHABLE(); } @@ -1243,14 +1249,17 @@ LIR* ArmMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) { return res; } -LIR* ArmMir2Lir::OpMem(OpKind op, RegStorage r_base, int disp) { - UNUSED(op, r_base, disp); +LIR* ArmMir2Lir::OpMem(OpKind op ATTRIBUTE_UNUSED, + RegStorage r_base ATTRIBUTE_UNUSED, + int disp ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpMem for Arm"; UNREACHABLE(); } -LIR* ArmMir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) { - UNUSED(trampoline); // The address of the trampoline is already loaded into r_tgt. +LIR* ArmMir2Lir::InvokeTrampoline(OpKind op, + RegStorage r_tgt, + // The address of the trampoline is already loaded into r_tgt. + QuickEntrypointEnum trampoline ATTRIBUTE_UNUSED) { return OpReg(op, r_tgt); } diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index 31cf6675af..d92dea21c2 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -37,14 +37,12 @@ LIR* Arm64Mir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage s return OpCondBranch(cond, target); } -LIR* Arm64Mir2Lir::OpIT(ConditionCode ccode, const char* guide) { - UNUSED(ccode, guide); +LIR* Arm64Mir2Lir::OpIT(ConditionCode ccode ATTRIBUTE_UNUSED, const char* guide ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpIT for Arm64"; UNREACHABLE(); } -void Arm64Mir2Lir::OpEndIT(LIR* it) { - UNUSED(it); +void Arm64Mir2Lir::OpEndIT(LIR* it ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpEndIT for Arm64"; } @@ -188,8 +186,7 @@ void Arm64Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, Con GenSelect(true_val, false_val, code, rs_dest, dest_reg_class); } -void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { - UNUSED(bb); +void Arm64Mir2Lir::GenSelect(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir) { RegLocation rl_src = mir_graph_->GetSrc(mir, 0); rl_src = LoadValue(rl_src, rl_src.ref ? kRefReg : kCoreReg); // rl_src may be aliased with rl_result/rl_dest, so do compare early. @@ -413,9 +410,11 @@ static const MagicTable magic_table[] = { }; // Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4) -bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, - RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(dalvik_opcode); +bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED, + bool is_div, + RegLocation rl_src, + RegLocation rl_dest, + int lit) { if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) { return false; } @@ -457,9 +456,11 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d return true; } -bool Arm64Mir2Lir::SmallLiteralDivRem64(Instruction::Code dalvik_opcode, bool is_div, - RegLocation rl_src, RegLocation rl_dest, int64_t lit) { - UNUSED(dalvik_opcode); +bool Arm64Mir2Lir::SmallLiteralDivRem64(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED, + bool is_div, + RegLocation rl_src, + RegLocation rl_dest, + int64_t lit) { if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) { return false; } @@ -599,15 +600,17 @@ bool Arm64Mir2Lir::HandleEasyDivRem64(Instruction::Code dalvik_opcode, bool is_d return true; } -bool Arm64Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(rl_src, rl_dest, lit); +bool Arm64Mir2Lir::EasyMultiply(RegLocation rl_src ATTRIBUTE_UNUSED, + RegLocation rl_dest ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of EasyMultiply for Arm64"; UNREACHABLE(); } -RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, - bool is_div) { - UNUSED(rl_dest, rl_src1, lit, is_div); +RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm64"; UNREACHABLE(); } @@ -626,9 +629,11 @@ RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int return rl_result; } -RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, bool is_div, int flags) { - UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags); +RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + RegLocation rl_src2 ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of GenDivRem for Arm64"; UNREACHABLE(); } @@ -963,14 +968,12 @@ void Arm64Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, dex_cache_access_insns_.push_back(ldr); } -LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base, int count) { - UNUSED(r_base, count); +LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base ATTRIBUTE_UNUSED, int count ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpVldm for Arm64"; UNREACHABLE(); } -LIR* Arm64Mir2Lir::OpVstm(RegStorage r_base, int count) { - UNUSED(r_base, count); +LIR* Arm64Mir2Lir::OpVstm(RegStorage r_base ATTRIBUTE_UNUSED, int count ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpVstm for Arm64"; UNREACHABLE(); } diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc index 6efa11e1fd..691bfd9edd 100644 --- a/compiler/dex/quick/arm64/target_arm64.cc +++ b/compiler/dex/quick/arm64/target_arm64.cc @@ -881,8 +881,7 @@ int Arm64Mir2Lir::GenDalvikArgsBulkCopy(CallInfo* /*info*/, int /*first*/, int c return count; } -void Arm64Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) { - UNUSED(bb); +void Arm64Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir) { DCHECK(MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)); RegLocation rl_src[3]; RegLocation rl_dest = mir_graph_->GetBadLoc(); diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc index 483231f931..58769ea9cc 100644 --- a/compiler/dex/quick/arm64/utility_arm64.cc +++ b/compiler/dex/quick/arm64/utility_arm64.cc @@ -672,22 +672,26 @@ LIR* Arm64Mir2Lir::OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2 } } -LIR* Arm64Mir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, - MoveType move_type) { - UNUSED(r_dest, r_base, offset, move_type); +LIR* Arm64Mir2Lir::OpMovRegMem(RegStorage r_dest ATTRIBUTE_UNUSED, + RegStorage r_base ATTRIBUTE_UNUSED, + int offset ATTRIBUTE_UNUSED, + MoveType move_type ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL); UNREACHABLE(); } -LIR* Arm64Mir2Lir::OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, - MoveType move_type) { - UNUSED(r_base, offset, r_src, move_type); +LIR* Arm64Mir2Lir::OpMovMemReg(RegStorage r_base ATTRIBUTE_UNUSED, + int offset ATTRIBUTE_UNUSED, + RegStorage r_src ATTRIBUTE_UNUSED, + MoveType move_type ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL); return nullptr; } -LIR* Arm64Mir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src) { - UNUSED(op, cc, r_dest, r_src); +LIR* Arm64Mir2Lir::OpCondRegReg(OpKind op ATTRIBUTE_UNUSED, + ConditionCode cc ATTRIBUTE_UNUSED, + RegStorage r_dest ATTRIBUTE_UNUSED, + RegStorage r_src ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpCondRegReg for Arm64"; UNREACHABLE(); } @@ -1381,14 +1385,15 @@ LIR* Arm64Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage return store; } -LIR* Arm64Mir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) { - UNUSED(r_dest, r_src); +LIR* Arm64Mir2Lir::OpFpRegCopy(RegStorage r_dest ATTRIBUTE_UNUSED, + RegStorage r_src ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpFpRegCopy for Arm64"; UNREACHABLE(); } -LIR* Arm64Mir2Lir::OpMem(OpKind op, RegStorage r_base, int disp) { - UNUSED(op, r_base, disp); +LIR* Arm64Mir2Lir::OpMem(OpKind op ATTRIBUTE_UNUSED, + RegStorage r_base ATTRIBUTE_UNUSED, + int disp ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpMem for Arm64"; UNREACHABLE(); } diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index dbcc868657..cde99b3fae 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -992,8 +992,7 @@ void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table) { } /* Set up special LIR to mark a Dalvik byte-code instruction start for pretty printing */ -void Mir2Lir::MarkBoundary(DexOffset offset, const char* inst_str) { - UNUSED(offset); +void Mir2Lir::MarkBoundary(DexOffset offset ATTRIBUTE_UNUSED, const char* inst_str) { // NOTE: only used for debug listings. NewLIR1(kPseudoDalvikByteCodeBoundary, WrapPointer(ArenaStrdup(inst_str))); } @@ -1358,8 +1357,8 @@ RegLocation Mir2Lir::NarrowRegLoc(RegLocation loc) { return loc; } -void Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) { - UNUSED(bb, mir); +void Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb ATTRIBUTE_UNUSED, + MIR* mir ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unknown MIR opcode not supported on this architecture"; UNREACHABLE(); } diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index af93aabc91..e1a2838f3e 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -73,6 +73,7 @@ static constexpr bool kIntrinsicIsStatic[] = { false, // kIntrinsicUnsafeGet false, // kIntrinsicUnsafePut true, // kIntrinsicSystemArrayCopyCharArray + true, // kIntrinsicSystemArrayCopy }; static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop, "arraysize of kIntrinsicIsStatic unexpected"); @@ -121,6 +122,8 @@ static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet_must_not_be_s static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static"); static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray], "SystemArrayCopyCharArray must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopy], + "SystemArrayCopy must be static"); MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke) { MIR* insn = mir_graph->NewMIR(); @@ -326,6 +329,9 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { // kProtoCacheCharArrayICharArrayII_V { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} }, + // kProtoCacheObjectIObjectII_V + { kClassCacheVoid, 5, {kClassCacheJavaLangObject, kClassCacheInt, + kClassCacheJavaLangObject, kClassCacheInt, kClassCacheInt} }, // kProtoCacheIICharArrayI_V { kClassCacheVoid, 4, { kClassCacheInt, kClassCacheInt, kClassCacheJavaLangCharArray, kClassCacheInt } }, @@ -481,6 +487,8 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray, 0), + INTRINSIC(JavaLangSystem, ArrayCopy, ObjectIObjectII_V , kIntrinsicSystemArrayCopy, + 0), INTRINSIC(JavaLangInteger, RotateRight, II_I, kIntrinsicRotateRight, k32), INTRINSIC(JavaLangLong, RotateRight, JI_J, kIntrinsicRotateRight, k64), @@ -653,6 +661,7 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { case kIntrinsicNumberOfTrailingZeros: case kIntrinsicRotateRight: case kIntrinsicRotateLeft: + case kIntrinsicSystemArrayCopy: return false; // not implemented in quick. default: LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode; diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 8458806e5e..5ce110c120 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -259,6 +259,7 @@ class DexFileMethodInliner { kProtoCacheObjectJ_Object, kProtoCacheObjectJObject_V, kProtoCacheCharArrayICharArrayII_V, + kProtoCacheObjectIObjectII_V, kProtoCacheIICharArrayI_V, kProtoCacheByteArrayIII_String, kProtoCacheIICharArray_String, diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 2a1d64425b..2b60a51e22 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -2102,15 +2102,15 @@ void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target) { } /* Call out to helper assembly routine that will null check obj and then lock it. */ -void Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { - UNUSED(opt_flags); // TODO: avoid null check with specialized non-null helper. +void Mir2Lir::GenMonitorEnter(int opt_flags ATTRIBUTE_UNUSED, RegLocation rl_src) { + // TODO: avoid null check with specialized non-null helper. FlushAllRegs(); CallRuntimeHelperRegLocation(kQuickLockObject, rl_src, true); } /* Call out to helper assembly routine that will null check obj and then unlock it. */ -void Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { - UNUSED(opt_flags); // TODO: avoid null check with specialized non-null helper. +void Mir2Lir::GenMonitorExit(int opt_flags ATTRIBUTE_UNUSED, RegLocation rl_src) { + // TODO: avoid null check with specialized non-null helper. FlushAllRegs(); CallRuntimeHelperRegLocation(kQuickUnlockObject, rl_src, true); } diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 3c5c2fe010..422d82ffa2 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -521,10 +521,9 @@ static bool CommonCallCodeLoadCodePointerIntoInvokeTgt(const RegStorage* alt_fro * kArg1 here rather than the standard GenDalvikArgs. */ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, - int state, const MethodReference& target_method, + int state, const MethodReference& target_method ATTRIBUTE_UNUSED, uint32_t method_idx, uintptr_t, uintptr_t, InvokeType) { - UNUSED(target_method); Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); /* * This is the fast path in which the target virtual method is @@ -607,10 +606,12 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, return state + 1; } -static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, - QuickEntrypointEnum trampoline, int state, - const MethodReference& target_method, uint32_t method_idx) { - UNUSED(info, method_idx); +static int NextInvokeInsnSP(CompilationUnit* cu, + CallInfo* info ATTRIBUTE_UNUSED, + QuickEntrypointEnum trampoline, + int state, + const MethodReference& target_method, + uint32_t method_idx ATTRIBUTE_UNUSED) { Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); /* @@ -1266,35 +1267,31 @@ bool Mir2Lir::GenInlinedAbsLong(CallInfo* info) { return true; } -bool Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) { +bool Mir2Lir::GenInlinedReverseBits(CallInfo* info ATTRIBUTE_UNUSED, OpSize size ATTRIBUTE_UNUSED) { // Currently implemented only for ARM64. - UNUSED(info, size); return false; } -bool Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) { +bool Mir2Lir::GenInlinedMinMaxFP(CallInfo* info ATTRIBUTE_UNUSED, + bool is_min ATTRIBUTE_UNUSED, + bool is_double ATTRIBUTE_UNUSED) { // Currently implemented only for ARM64. - UNUSED(info, is_min, is_double); return false; } -bool Mir2Lir::GenInlinedCeil(CallInfo* info) { - UNUSED(info); +bool Mir2Lir::GenInlinedCeil(CallInfo* info ATTRIBUTE_UNUSED) { return false; } -bool Mir2Lir::GenInlinedFloor(CallInfo* info) { - UNUSED(info); +bool Mir2Lir::GenInlinedFloor(CallInfo* info ATTRIBUTE_UNUSED) { return false; } -bool Mir2Lir::GenInlinedRint(CallInfo* info) { - UNUSED(info); +bool Mir2Lir::GenInlinedRint(CallInfo* info ATTRIBUTE_UNUSED) { return false; } -bool Mir2Lir::GenInlinedRound(CallInfo* info, bool is_double) { - UNUSED(info, is_double); +bool Mir2Lir::GenInlinedRound(CallInfo* info ATTRIBUTE_UNUSED, bool is_double ATTRIBUTE_UNUSED) { return false; } @@ -1328,8 +1325,7 @@ bool Mir2Lir::GenInlinedDoubleCvt(CallInfo* info) { return true; } -bool Mir2Lir::GenInlinedArrayCopyCharArray(CallInfo* info) { - UNUSED(info); +bool Mir2Lir::GenInlinedArrayCopyCharArray(CallInfo* info ATTRIBUTE_UNUSED) { return false; } diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 8863c058a1..4a736f3d93 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -32,9 +32,10 @@ namespace art { -bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special) { +bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb ATTRIBUTE_UNUSED, + MIR* mir ATTRIBUTE_UNUSED, + const InlineMethod& special ATTRIBUTE_UNUSED) { // TODO - UNUSED(bb, mir, special); return false; } diff --git a/compiler/dex/quick/mips/fp_mips.cc b/compiler/dex/quick/mips/fp_mips.cc index 45fd1a9433..52706df7a5 100644 --- a/compiler/dex/quick/mips/fp_mips.cc +++ b/compiler/dex/quick/mips/fp_mips.cc @@ -115,17 +115,17 @@ void MipsMir2Lir::GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest StoreValueWide(rl_dest, rl_result); } -void MipsMir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1, - int32_t constant) { +void MipsMir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + int32_t constant ATTRIBUTE_UNUSED) { // TODO: need mips implementation. - UNUSED(rl_dest, rl_src1, constant); LOG(FATAL) << "Unimplemented GenMultiplyByConstantFloat in mips"; } -void MipsMir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1, - int64_t constant) { +void MipsMir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + int64_t constant ATTRIBUTE_UNUSED) { // TODO: need mips implementation. - UNUSED(rl_dest, rl_src1, constant); LOG(FATAL) << "Unimplemented GenMultiplyByConstantDouble in mips"; } @@ -254,8 +254,10 @@ void MipsMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLoc StoreValue(rl_dest, rl_result); } -void MipsMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) { - UNUSED(bb, mir, gt_bias, is_double); +void MipsMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb ATTRIBUTE_UNUSED, + MIR* mir ATTRIBUTE_UNUSED, + bool gt_bias ATTRIBUTE_UNUSED, + bool is_double ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "Need codegen for fused fp cmp branch"; } @@ -288,9 +290,10 @@ void MipsMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) { StoreValueWide(rl_dest, rl_result); } -bool MipsMir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) { +bool MipsMir2Lir::GenInlinedMinMax(CallInfo* info ATTRIBUTE_UNUSED, + bool is_min ATTRIBUTE_UNUSED, + bool is_long ATTRIBUTE_UNUSED) { // TODO: need Mips implementation. - UNUSED(info, is_min, is_long); return false; } diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 1099303f7d..8ca53ea228 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -279,8 +279,7 @@ void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code, int32_t true_val, int32_t false_val, RegStorage rs_dest, - RegisterClass dest_reg_class) { - UNUSED(dest_reg_class); + RegisterClass dest_reg_class ATTRIBUTE_UNUSED) { // Implement as a branch-over. // TODO: Conditional move? LoadConstant(rs_dest, true_val); @@ -290,13 +289,12 @@ void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, Cond ne_branchover->target = target_label; } -void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { - UNUSED(bb, mir); +void MipsMir2Lir::GenSelect(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "Need codegen for select"; } -void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { - UNUSED(bb, mir); +void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb ATTRIBUTE_UNUSED, + MIR* mir ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch"; } @@ -327,39 +325,40 @@ RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int return rl_result; } -RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, - bool is_div, int flags) { - UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags); +RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + RegLocation rl_src2 ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of GenDivRem for Mips"; UNREACHABLE(); } -RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, - bool is_div) { - UNUSED(rl_dest, rl_src1, lit, is_div); +RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips"; UNREACHABLE(); } -bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { - UNUSED(info, is_long, is_object); +bool MipsMir2Lir::GenInlinedCas(CallInfo* info ATTRIBUTE_UNUSED, + bool is_long ATTRIBUTE_UNUSED, + bool is_object ATTRIBUTE_UNUSED) { return false; } -bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) { - UNUSED(info); +bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info ATTRIBUTE_UNUSED) { // TODO: add Mips implementation. return false; } -bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) { - UNUSED(info); +bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info ATTRIBUTE_UNUSED) { // TODO: add Mips implementation. return false; } -bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) { - UNUSED(info); +bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info ATTRIBUTE_UNUSED) { return false; } @@ -408,27 +407,26 @@ bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { return true; } -void MipsMir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) { - UNUSED(reg, target); +void MipsMir2Lir::OpPcRelLoad(RegStorage reg ATTRIBUTE_UNUSED, LIR* target ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips"; UNREACHABLE(); } -LIR* MipsMir2Lir::OpVldm(RegStorage r_base, int count) { - UNUSED(r_base, count); +LIR* MipsMir2Lir::OpVldm(RegStorage r_base ATTRIBUTE_UNUSED, int count ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpVldm for Mips"; UNREACHABLE(); } -LIR* MipsMir2Lir::OpVstm(RegStorage r_base, int count) { - UNUSED(r_base, count); +LIR* MipsMir2Lir::OpVstm(RegStorage r_base ATTRIBUTE_UNUSED, int count ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpVstm for Mips"; UNREACHABLE(); } -void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, - int first_bit, int second_bit) { - UNUSED(lit); +void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, + RegLocation rl_result, + int lit ATTRIBUTE_UNUSED, + int first_bit, + int second_bit) { RegStorage t_reg = AllocTemp(); OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit); OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg); @@ -462,27 +460,28 @@ LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* targ return OpCmpImmBranch(c_code, reg, 0, target); } -bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, - RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit); - LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips"; +bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED, + RegLocation rl_src ATTRIBUTE_UNUSED, + RegLocation rl_dest ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED) { + LOG(FATAL) << "Unexpected use of smallLiteralDivRem in Mips"; UNREACHABLE(); } -bool MipsMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(rl_src, rl_dest, lit); +bool MipsMir2Lir::EasyMultiply(RegLocation rl_src ATTRIBUTE_UNUSED, + RegLocation rl_dest ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of easyMultiply in Mips"; UNREACHABLE(); } -LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) { - UNUSED(cond, guide); +LIR* MipsMir2Lir::OpIT(ConditionCode cond ATTRIBUTE_UNUSED, const char* guide ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpIT in Mips"; UNREACHABLE(); } -void MipsMir2Lir::OpEndIT(LIR* it) { - UNUSED(it); +void MipsMir2Lir::OpEndIT(LIR* it ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpEndIT in Mips"; } @@ -621,9 +620,12 @@ void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocati StoreValueWide(rl_dest, rl_result); } -void MipsMir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, bool is_div, int flags) { - UNUSED(opcode); +void MipsMir2Lir::GenDivRemLong(Instruction::Code opcode ATTRIBUTE_UNUSED, + RegLocation rl_dest, + RegLocation rl_src1, + RegLocation rl_src2, + bool is_div, + int flags) { // TODO: Implement easy div/rem? rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); @@ -855,9 +857,11 @@ void MipsMir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, StoreValueWide(rl_dest, rl_result); } -void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_shift, int flags) { - UNUSED(flags); +void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, + RegLocation rl_dest, + RegLocation rl_src1, + RegLocation rl_shift, + int flags ATTRIBUTE_UNUSED) { if (!cu_->target64) { // Default implementation is just to ignore the constant case. GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index ec2475a7f7..372fe2b599 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -103,18 +103,15 @@ bool MipsMir2Lir::InexpensiveConstantInt(int32_t value) { return ((value == 0) || IsUint<16>(value) || IsInt<16>(value)); } -bool MipsMir2Lir::InexpensiveConstantFloat(int32_t value) { - UNUSED(value); +bool MipsMir2Lir::InexpensiveConstantFloat(int32_t value ATTRIBUTE_UNUSED) { return false; // TUNING } -bool MipsMir2Lir::InexpensiveConstantLong(int64_t value) { - UNUSED(value); +bool MipsMir2Lir::InexpensiveConstantLong(int64_t value ATTRIBUTE_UNUSED) { return false; // TUNING } -bool MipsMir2Lir::InexpensiveConstantDouble(int64_t value) { - UNUSED(value); +bool MipsMir2Lir::InexpensiveConstantDouble(int64_t value ATTRIBUTE_UNUSED) { return false; // TUNING } @@ -520,21 +517,26 @@ LIR* MipsMir2Lir::OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2) return NewLIR2(opcode, r_dest_src1.GetReg(), r_src2.GetReg()); } -LIR* MipsMir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, - MoveType move_type) { - UNUSED(r_dest, r_base, offset, move_type); +LIR* MipsMir2Lir::OpMovRegMem(RegStorage r_dest ATTRIBUTE_UNUSED, + RegStorage r_base ATTRIBUTE_UNUSED, + int offset ATTRIBUTE_UNUSED, + MoveType move_type ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL); UNREACHABLE(); } -LIR* MipsMir2Lir::OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type) { - UNUSED(r_base, offset, r_src, move_type); +LIR* MipsMir2Lir::OpMovMemReg(RegStorage r_base ATTRIBUTE_UNUSED, + int offset ATTRIBUTE_UNUSED, + RegStorage r_src ATTRIBUTE_UNUSED, + MoveType move_type ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL); UNREACHABLE(); } -LIR* MipsMir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src) { - UNUSED(op, cc, r_dest, r_src); +LIR* MipsMir2Lir::OpCondRegReg(OpKind op ATTRIBUTE_UNUSED, + ConditionCode cc ATTRIBUTE_UNUSED, + RegStorage r_dest ATTRIBUTE_UNUSED, + RegStorage r_src ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpCondRegReg for MIPS"; UNREACHABLE(); } @@ -1031,14 +1033,14 @@ LIR* MipsMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage return store; } -LIR* MipsMir2Lir::OpMem(OpKind op, RegStorage r_base, int disp) { - UNUSED(op, r_base, disp); +LIR* MipsMir2Lir::OpMem(OpKind op ATTRIBUTE_UNUSED, + RegStorage r_base ATTRIBUTE_UNUSED, + int disp ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpMem for MIPS"; UNREACHABLE(); } -LIR* MipsMir2Lir::OpCondBranch(ConditionCode cc, LIR* target) { - UNUSED(cc, target); +LIR* MipsMir2Lir::OpCondBranch(ConditionCode cc ATTRIBUTE_UNUSED, LIR* target ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpCondBranch for MIPS"; UNREACHABLE(); } diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index c50246d182..8da386368b 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -1411,8 +1411,7 @@ void Mir2Lir::CheckRegLocationImpl(RegLocation rl, bool fail, bool report) const rl.ref ? RefCheck::kCheckRef : RefCheck::kCheckNotRef, FPCheck::kIgnoreFP, fail, report); } -size_t Mir2Lir::GetInstructionOffset(LIR* lir) { - UNUSED(lir); +size_t Mir2Lir::GetInstructionOffset(LIR* lir ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "Unsupported GetInstructionOffset()"; UNREACHABLE(); } diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 4e3aab2f0b..a0db1e87ba 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -1463,8 +1463,7 @@ class Mir2Lir { virtual bool InexpensiveConstantFloat(int32_t value) = 0; virtual bool InexpensiveConstantLong(int64_t value) = 0; virtual bool InexpensiveConstantDouble(int64_t value) = 0; - virtual bool InexpensiveConstantInt(int32_t value, Instruction::Code opcode) { - UNUSED(opcode); + virtual bool InexpensiveConstantInt(int32_t value, Instruction::Code opcode ATTRIBUTE_UNUSED) { return InexpensiveConstantInt(value); } diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index 1cd742abac..6673ea8ac5 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -37,6 +37,7 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "elf_writer_quick.h" +#include "experimental_flags.h" #include "jni/quick/jni_compiler.h" #include "mir_to_lir.h" #include "mirror/object.h" @@ -523,7 +524,8 @@ static bool SkipScanningUnsupportedOpcodes(InstructionSet instruction_set) { // All opcodes are supported no matter what. Usually not the case // since experimental opcodes are not implemented in the quick compiler. return true; - } else if (LIKELY(!Runtime::Current()->AreExperimentalLambdasEnabled())) { + } else if (LIKELY(!Runtime::Current()-> + AreExperimentalFlagsEnabled(ExperimentalFlags::kLambdas))) { // Experimental opcodes are disabled. // // If all unsupported opcodes are experimental we don't need to do scanning. @@ -849,8 +851,8 @@ uintptr_t QuickCompiler::GetEntryPointOf(ArtMethod* method) const { InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet()))); } -Mir2Lir* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) { - UNUSED(compilation_unit); +Mir2Lir* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, + void* compilation_unit ATTRIBUTE_UNUSED) { Mir2Lir* mir_to_lir = nullptr; switch (cu->instruction_set) { #ifdef ART_ENABLE_CODEGEN_arm diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index 8ec86fa56c..d9d0434e8a 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -320,15 +320,13 @@ RegStorage Mir2Lir::AllocPreservedFpReg(int s_reg) { } // TODO: this is Thumb2 only. Remove when DoPromotion refactored. -RegStorage Mir2Lir::AllocPreservedDouble(int s_reg) { - UNUSED(s_reg); +RegStorage Mir2Lir::AllocPreservedDouble(int s_reg ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "Unexpected use of AllocPreservedDouble"; UNREACHABLE(); } // TODO: this is Thumb2 only. Remove when DoPromotion refactored. -RegStorage Mir2Lir::AllocPreservedSingle(int s_reg) { - UNUSED(s_reg); +RegStorage Mir2Lir::AllocPreservedSingle(int s_reg ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "Unexpected use of AllocPreservedSingle"; UNREACHABLE(); } @@ -1553,8 +1551,7 @@ int Mir2Lir::GetSRegHi(int lowSreg) { return (lowSreg == INVALID_SREG) ? INVALID_SREG : lowSreg + 1; } -bool Mir2Lir::LiveOut(int s_reg) { - UNUSED(s_reg); +bool Mir2Lir::LiveOut(int s_reg ATTRIBUTE_UNUSED) { // For now. return true; } diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 12523aca76..64becb9b85 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -1629,8 +1629,8 @@ void X86Mir2Lir::EmitUnimplemented(const X86EncodingMap* entry, LIR* lir) { * instruction. In those cases we will try to substitute a new code * sequence or request that the trace be shortened and retried. */ -AssemblerStatus X86Mir2Lir::AssembleInstructions(LIR* first_lir_insn, CodeOffset start_addr) { - UNUSED(start_addr); +AssemblerStatus X86Mir2Lir::AssembleInstructions(LIR* first_lir_insn, + CodeOffset start_addr ATTRIBUTE_UNUSED) { LIR *lir; AssemblerStatus res = kSuccess; // Assume success diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc index 8e81746db5..b11d41caf0 100755 --- a/compiler/dex/quick/x86/fp_x86.cc +++ b/compiler/dex/quick/x86/fp_x86.cc @@ -124,17 +124,17 @@ void X86Mir2Lir::GenArithOpDouble(Instruction::Code opcode, StoreValueWide(rl_dest, rl_result); } -void X86Mir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1, - int32_t constant) { +void X86Mir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + int32_t constant ATTRIBUTE_UNUSED) { // TODO: need x86 implementation. - UNUSED(rl_dest, rl_src1, constant); LOG(FATAL) << "Unimplemented GenMultiplyByConstantFloat in x86"; } -void X86Mir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1, - int64_t constant) { +void X86Mir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1 ATTRIBUTE_UNUSED, + int64_t constant ATTRIBUTE_UNUSED) { // TODO: need x86 implementation. - UNUSED(rl_dest, rl_src1, constant); LOG(FATAL) << "Unimplemented GenMultiplyByConstantDouble in x86"; } diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index ecd23e9ef0..a8706c3b09 100755 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -270,8 +270,7 @@ void X86Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, Condi } } -void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { - UNUSED(bb); +void X86Mir2Lir::GenSelect(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir) { RegLocation rl_result; RegLocation rl_src = mir_graph_->GetSrc(mir, 0); RegLocation rl_dest = mir_graph_->GetDest(mir); @@ -597,8 +596,10 @@ void X86Mir2Lir::CalculateMagicAndShift(int64_t divisor, int64_t& magic, int& sh shift = (is_long) ? p - 64 : p - 32; } -RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div) { - UNUSED(rl_dest, reg_lo, lit, is_div); +RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegStorage reg_lo ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of GenDivRemLit for x86"; UNREACHABLE(); } @@ -766,16 +767,19 @@ RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src, return rl_result; } -RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, - bool is_div) { - UNUSED(rl_dest, reg_lo, reg_hi, is_div); +RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegStorage reg_lo ATTRIBUTE_UNUSED, + RegStorage reg_hi ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of GenDivRem for x86"; UNREACHABLE(); } -RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, bool is_div, int flags) { - UNUSED(rl_dest); +RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest ATTRIBUTE_UNUSED, + RegLocation rl_src1, + RegLocation rl_src2, + bool is_div, + int flags) { // We have to use fixed registers, so flush all the temps. // Prepare for explicit register usage. @@ -1449,22 +1453,21 @@ void X86Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, R } } -LIR* X86Mir2Lir::OpVldm(RegStorage r_base, int count) { - UNUSED(r_base, count); +LIR* X86Mir2Lir::OpVldm(RegStorage r_base ATTRIBUTE_UNUSED, int count ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpVldm for x86"; UNREACHABLE(); } -LIR* X86Mir2Lir::OpVstm(RegStorage r_base, int count) { - UNUSED(r_base, count); +LIR* X86Mir2Lir::OpVstm(RegStorage r_base ATTRIBUTE_UNUSED, int count ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpVstm for x86"; UNREACHABLE(); } void X86Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, - RegLocation rl_result, int lit, - int first_bit, int second_bit) { - UNUSED(lit); + RegLocation rl_result, + int lit ATTRIBUTE_UNUSED, + int first_bit, + int second_bit) { RegStorage t_reg = AllocTemp(); OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit); OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg); @@ -1595,27 +1598,28 @@ LIR* X86Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* targe return OpCondBranch(c_code, target); } -bool X86Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, - RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit); - LOG(FATAL) << "Unexpected use of smallLiteralDive in x86"; +bool X86Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED, + bool is_div ATTRIBUTE_UNUSED, + RegLocation rl_src ATTRIBUTE_UNUSED, + RegLocation rl_dest ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED) { + LOG(FATAL) << "Unexpected use of smallLiteralDivRem in x86"; UNREACHABLE(); } -bool X86Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(rl_src, rl_dest, lit); +bool X86Mir2Lir::EasyMultiply(RegLocation rl_src ATTRIBUTE_UNUSED, + RegLocation rl_dest ATTRIBUTE_UNUSED, + int lit ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of easyMultiply in x86"; UNREACHABLE(); } -LIR* X86Mir2Lir::OpIT(ConditionCode cond, const char* guide) { - UNUSED(cond, guide); +LIR* X86Mir2Lir::OpIT(ConditionCode cond ATTRIBUTE_UNUSED, const char* guide ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpIT in x86"; UNREACHABLE(); } -void X86Mir2Lir::OpEndIT(LIR* it) { - UNUSED(it); +void X86Mir2Lir::OpEndIT(LIR* it ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of OpEndIT in x86"; UNREACHABLE(); } @@ -1634,8 +1638,10 @@ void X86Mir2Lir::GenImulRegImm(RegStorage dest, RegStorage src, int val) { } } -void X86Mir2Lir::GenImulMemImm(RegStorage dest, int sreg, int displacement, int val) { - UNUSED(sreg); +void X86Mir2Lir::GenImulMemImm(RegStorage dest, + int sreg ATTRIBUTE_UNUSED, + int displacement, + int val) { // All memory accesses below reference dalvik regs. ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); @@ -2548,9 +2554,11 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } } -RegLocation X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src, int shift_amount, int flags) { - UNUSED(flags); +RegLocation X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, + RegLocation rl_dest, + RegLocation rl_src, + int shift_amount, + int flags ATTRIBUTE_UNUSED) { RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); if (cu_->target64) { OpKind op = static_cast<OpKind>(0); /* Make gcc happy */ diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index c62cd47315..25fb8869b6 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -254,8 +254,7 @@ RegStorage X86Mir2Lir::TargetReg32(SpecialTargetRegister reg) const { : RegStorage32FromSpecialTargetRegister_Target32[reg]; } -RegStorage X86Mir2Lir::TargetReg(SpecialTargetRegister reg) { - UNUSED(reg); +RegStorage X86Mir2Lir::TargetReg(SpecialTargetRegister reg ATTRIBUTE_UNUSED) { LOG(FATAL) << "Do not use this function!!!"; UNREACHABLE(); } @@ -861,8 +860,7 @@ Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, } // Not used in x86(-64) -RegStorage X86Mir2Lir::LoadHelper(QuickEntrypointEnum trampoline) { - UNUSED(trampoline); +RegStorage X86Mir2Lir::LoadHelper(QuickEntrypointEnum trampoline ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unexpected use of LoadHelper in x86"; UNREACHABLE(); } @@ -2323,13 +2321,11 @@ void X86Mir2Lir::GenSetVector(MIR* mir) { } } -void X86Mir2Lir::GenPackedArrayGet(BasicBlock* bb, MIR* mir) { - UNUSED(bb, mir); +void X86Mir2Lir::GenPackedArrayGet(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "Extended opcode kMirOpPackedArrayGet not supported."; } -void X86Mir2Lir::GenPackedArrayPut(BasicBlock* bb, MIR* mir) { - UNUSED(bb, mir); +void X86Mir2Lir::GenPackedArrayPut(BasicBlock* bb ATTRIBUTE_UNUSED, MIR* mir ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "Extended opcode kMirOpPackedArrayPut not supported."; } diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index b16ae982f2..61354dfc53 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -57,8 +57,7 @@ LIR* X86Mir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) { return res; } -bool X86Mir2Lir::InexpensiveConstantInt(int32_t value) { - UNUSED(value); +bool X86Mir2Lir::InexpensiveConstantInt(int32_t value ATTRIBUTE_UNUSED) { return true; } @@ -66,8 +65,7 @@ bool X86Mir2Lir::InexpensiveConstantFloat(int32_t value) { return value == 0; } -bool X86Mir2Lir::InexpensiveConstantLong(int64_t value) { - UNUSED(value); +bool X86Mir2Lir::InexpensiveConstantLong(int64_t value ATTRIBUTE_UNUSED) { return true; } @@ -942,9 +940,14 @@ LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r return store; } -LIR* X86Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target, LIR** compare) { - UNUSED(temp_reg); // Comparison performed directly with memory. +LIR* X86Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, + // Comparison performed directly with memory. + RegStorage temp_reg ATTRIBUTE_UNUSED, + RegStorage base_reg, + int offset, + int check_value, + LIR* target, + LIR** compare) { LIR* inst = NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base_reg.GetReg(), offset, check_value); if (compare != nullptr) { @@ -1114,8 +1117,11 @@ RegLocation X86Mir2Lir::UpdateLocWideTyped(RegLocation loc) { return loc; } -LIR* X86Mir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) { - UNUSED(r_tgt); // Call to absolute memory location doesn't need a temporary target register. +LIR* X86Mir2Lir::InvokeTrampoline(OpKind op, + // Call to absolute memory location doesn't + // need a temporary target register. + RegStorage r_tgt ATTRIBUTE_UNUSED, + QuickEntrypointEnum trampoline) { if (cu_->target64) { return OpThreadMem(op, GetThreadOffset<8>(trampoline)); } else { diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 6f2b2341e0..65b0ad6400 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -34,7 +34,6 @@ VerificationResults::VerificationResults(const CompilerOptions* compiler_options verified_methods_(), rejected_classes_lock_("compiler rejected classes lock"), rejected_classes_() { - UNUSED(compiler_options); } VerificationResults::~VerificationResults() { diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index e535afd272..1a7dbe3a9f 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -370,7 +370,9 @@ inline int CompilerDriver::IsFastInvoke( nullptr, kVirtual); } else { StackHandleScope<1> hs(soa.Self()); - auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile(*devirt_target->dex_file))); + auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile( + *devirt_target->dex_file, + class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get())))); called_method = class_linker->ResolveMethod( *devirt_target->dex_file, devirt_target->dex_method_index, target_dex_cache, class_loader, nullptr, kVirtual); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 74f19a1029..8324bf30d6 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -953,7 +953,9 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { uint16_t exception_type_idx = exception_type.first; const DexFile* dex_file = exception_type.second; StackHandleScope<2> hs2(self); - Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file))); + Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile( + *dex_file, + Runtime::Current()->GetLinearAlloc()))); Handle<mirror::Class> klass(hs2.NewHandle( class_linker->ResolveType(*dex_file, exception_type_idx, dex_cache, NullHandle<mirror::ClassLoader>()))); @@ -2010,9 +2012,11 @@ class ResolveTypeVisitor : public CompilationVisitor { ClassLinker* class_linker = manager_->GetClassLinker(); const DexFile& dex_file = *manager_->GetDexFile(); StackHandleScope<2> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile(dex_file))); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader*>(manager_->GetClassLoader()))); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile( + dex_file, + class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get())))); mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader); if (klass == nullptr) { diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index b6a40a203b..a45df95d1d 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -19,6 +19,7 @@ #include "art_method-inl.h" #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" +#include "base/stringpiece.h" #include "base/time_utils.h" #include "base/timing_logger.h" #include "compiler_callbacks.h" @@ -86,7 +87,37 @@ JitCompiler::JitCompiler() : total_time_(0) { /* init_failure_output */ nullptr, /* abort_on_hard_verifier_failure */ false)); const InstructionSet instruction_set = kRuntimeISA; - instruction_set_features_.reset(InstructionSetFeatures::FromCppDefines()); + for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) { + VLOG(compiler) << "JIT compiler option " << option; + std::string error_msg; + if (option.starts_with("--instruction-set-variant=")) { + StringPiece str = option.substr(strlen("--instruction-set-variant=")).data(); + VLOG(compiler) << "JIT instruction set variant " << str; + instruction_set_features_.reset(InstructionSetFeatures::FromVariant( + instruction_set, str.as_string(), &error_msg)); + if (instruction_set_features_ == nullptr) { + LOG(WARNING) << "Error parsing " << option << " message=" << error_msg; + } + } else if (option.starts_with("--instruction-set-features=")) { + StringPiece str = option.substr(strlen("--instruction-set-features=")).data(); + VLOG(compiler) << "JIT instruction set features " << str; + if (instruction_set_features_.get() == nullptr) { + instruction_set_features_.reset(InstructionSetFeatures::FromVariant( + instruction_set, "default", &error_msg)); + if (instruction_set_features_ == nullptr) { + LOG(WARNING) << "Error parsing " << option << " message=" << error_msg; + } + } + instruction_set_features_.reset( + instruction_set_features_->AddFeaturesFromString(str.as_string(), &error_msg)); + if (instruction_set_features_ == nullptr) { + LOG(WARNING) << "Error parsing " << option << " message=" << error_msg; + } + } + } + if (instruction_set_features_ == nullptr) { + instruction_set_features_.reset(InstructionSetFeatures::FromCppDefines()); + } cumulative_logger_.reset(new CumulativeLogger("jit times")); verification_results_.reset(new VerificationResults(compiler_options_.get())); method_inliner_map_.reset(new DexFileToMethodInlinerMap); diff --git a/compiler/jni/jni_cfi_test_expected.inc b/compiler/jni/jni_cfi_test_expected.inc index 8b5fdc3fcb..16b4386938 100644 --- a/compiler/jni/jni_cfi_test_expected.inc +++ b/compiler/jni/jni_cfi_test_expected.inc @@ -325,156 +325,146 @@ static constexpr uint8_t expected_cfi_kX86_64[] = { // 0x0000007f: .cfi_def_cfa_offset: 128 static constexpr uint8_t expected_asm_kMips[] = { - 0xC0, 0xFF, 0xBD, 0x27, 0x3C, 0x00, 0xBF, 0xAF, 0x38, 0x00, 0xB8, 0xAF, - 0x34, 0x00, 0xAF, 0xAF, 0x30, 0x00, 0xAE, 0xAF, 0x2C, 0x00, 0xAD, 0xAF, - 0x28, 0x00, 0xAC, 0xAF, 0x24, 0x00, 0xAB, 0xAF, 0x20, 0x00, 0xAA, 0xAF, - 0x1C, 0x00, 0xA9, 0xAF, 0x18, 0x00, 0xA8, 0xAF, 0x00, 0x00, 0xA4, 0xAF, - 0x44, 0x00, 0xA5, 0xAF, 0x48, 0x00, 0xAC, 0xE7, 0x4C, 0x00, 0xA6, 0xAF, - 0x50, 0x00, 0xA7, 0xAF, 0xE0, 0xFF, 0xBD, 0x27, 0x20, 0x00, 0xBD, 0x27, - 0x18, 0x00, 0xA8, 0x8F, 0x1C, 0x00, 0xA9, 0x8F, 0x20, 0x00, 0xAA, 0x8F, - 0x24, 0x00, 0xAB, 0x8F, 0x28, 0x00, 0xAC, 0x8F, 0x2C, 0x00, 0xAD, 0x8F, - 0x30, 0x00, 0xAE, 0x8F, 0x34, 0x00, 0xAF, 0x8F, 0x38, 0x00, 0xB8, 0x8F, - 0x3C, 0x00, 0xBF, 0x8F, 0x40, 0x00, 0xBD, 0x27, 0x09, 0x00, 0xE0, 0x03, - 0x00, 0x00, 0x00, 0x00, + 0xC0, 0xFF, 0xBD, 0x27, 0x3C, 0x00, 0xBF, 0xAF, 0x38, 0x00, 0xBE, 0xAF, + 0x34, 0x00, 0xB7, 0xAF, 0x30, 0x00, 0xB6, 0xAF, 0x2C, 0x00, 0xB5, 0xAF, + 0x28, 0x00, 0xB4, 0xAF, 0x24, 0x00, 0xB3, 0xAF, 0x20, 0x00, 0xB2, 0xAF, + 0x00, 0x00, 0xA4, 0xAF, 0x44, 0x00, 0xA5, 0xAF, 0x48, 0x00, 0xAC, 0xE7, + 0x4C, 0x00, 0xA6, 0xAF, 0x50, 0x00, 0xA7, 0xAF, 0xE0, 0xFF, 0xBD, 0x27, + 0x20, 0x00, 0xBD, 0x27, 0x20, 0x00, 0xB2, 0x8F, 0x24, 0x00, 0xB3, 0x8F, + 0x28, 0x00, 0xB4, 0x8F, 0x2C, 0x00, 0xB5, 0x8F, 0x30, 0x00, 0xB6, 0x8F, + 0x34, 0x00, 0xB7, 0x8F, 0x38, 0x00, 0xBE, 0x8F, 0x3C, 0x00, 0xBF, 0x8F, + 0x40, 0x00, 0xBD, 0x27, 0x09, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, }; static constexpr uint8_t expected_cfi_kMips[] = { - 0x44, 0x0E, 0x40, 0x44, 0x9F, 0x01, 0x44, 0x98, 0x02, 0x44, 0x8F, 0x03, - 0x44, 0x8E, 0x04, 0x44, 0x8D, 0x05, 0x44, 0x8C, 0x06, 0x44, 0x8B, 0x07, - 0x44, 0x8A, 0x08, 0x44, 0x89, 0x09, 0x44, 0x88, 0x0A, 0x58, 0x0E, 0x60, - 0x44, 0x0E, 0x40, 0x0A, 0x44, 0xC8, 0x44, 0xC9, 0x44, 0xCA, 0x44, 0xCB, - 0x44, 0xCC, 0x44, 0xCD, 0x44, 0xCE, 0x44, 0xCF, 0x44, 0xD8, 0x44, 0xDF, - 0x44, 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40, + 0x44, 0x0E, 0x40, 0x44, 0x9F, 0x01, 0x44, 0x9E, 0x02, 0x44, 0x97, 0x03, + 0x44, 0x96, 0x04, 0x44, 0x95, 0x05, 0x44, 0x94, 0x06, 0x44, 0x93, 0x07, + 0x44, 0x92, 0x08, 0x58, 0x0E, 0x60, 0x44, 0x0E, 0x40, 0x0A, 0x44, 0xD2, + 0x44, 0xD3, 0x44, 0xD4, 0x44, 0xD5, 0x44, 0xD6, 0x44, 0xD7, 0x44, 0xDE, + 0x44, 0xDF, 0x44, 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40, }; // 0x00000000: addiu r29, r29, -64 // 0x00000004: .cfi_def_cfa_offset: 64 // 0x00000004: sw r31, +60(r29) // 0x00000008: .cfi_offset: r31 at cfa-4 -// 0x00000008: sw r24, +56(r29) -// 0x0000000c: .cfi_offset: r24 at cfa-8 -// 0x0000000c: sw r15, +52(r29) -// 0x00000010: .cfi_offset: r15 at cfa-12 -// 0x00000010: sw r14, +48(r29) -// 0x00000014: .cfi_offset: r14 at cfa-16 -// 0x00000014: sw r13, +44(r29) -// 0x00000018: .cfi_offset: r13 at cfa-20 -// 0x00000018: sw r12, +40(r29) -// 0x0000001c: .cfi_offset: r12 at cfa-24 -// 0x0000001c: sw r11, +36(r29) -// 0x00000020: .cfi_offset: r11 at cfa-28 -// 0x00000020: sw r10, +32(r29) -// 0x00000024: .cfi_offset: r10 at cfa-32 -// 0x00000024: sw r9, +28(r29) -// 0x00000028: .cfi_offset: r9 at cfa-36 -// 0x00000028: sw r8, +24(r29) -// 0x0000002c: .cfi_offset: r8 at cfa-40 -// 0x0000002c: sw r4, +0(r29) -// 0x00000030: sw r5, +68(r29) -// 0x00000034: swc1 f12, +72(r29) -// 0x00000038: sw r6, +76(r29) -// 0x0000003c: sw r7, +80(r29) -// 0x00000040: addiu r29, r29, -32 -// 0x00000044: .cfi_def_cfa_offset: 96 -// 0x00000044: addiu r29, r29, 32 -// 0x00000048: .cfi_def_cfa_offset: 64 -// 0x00000048: .cfi_remember_state -// 0x00000048: lw r8, +24(r29) -// 0x0000004c: .cfi_restore: r8 -// 0x0000004c: lw r9, +28(r29) -// 0x00000050: .cfi_restore: r9 -// 0x00000050: lw r10, +32(r29) -// 0x00000054: .cfi_restore: r10 -// 0x00000054: lw r11, +36(r29) -// 0x00000058: .cfi_restore: r11 -// 0x00000058: lw r12, +40(r29) -// 0x0000005c: .cfi_restore: r12 -// 0x0000005c: lw r13, +44(r29) -// 0x00000060: .cfi_restore: r13 -// 0x00000060: lw r14, +48(r29) -// 0x00000064: .cfi_restore: r14 -// 0x00000064: lw r15, +52(r29) -// 0x00000068: .cfi_restore: r15 -// 0x00000068: lw r24, +56(r29) -// 0x0000006c: .cfi_restore: r24 -// 0x0000006c: lw r31, +60(r29) -// 0x00000070: .cfi_restore: r31 -// 0x00000070: addiu r29, r29, 64 -// 0x00000074: .cfi_def_cfa_offset: 0 -// 0x00000074: jr r31 -// 0x00000078: nop -// 0x0000007c: .cfi_restore_state -// 0x0000007c: .cfi_def_cfa_offset: 64 +// 0x00000008: sw r30, +56(r29) +// 0x0000000c: .cfi_offset: r30 at cfa-8 +// 0x0000000c: sw r23, +52(r29) +// 0x00000010: .cfi_offset: r23 at cfa-12 +// 0x00000010: sw r22, +48(r29) +// 0x00000014: .cfi_offset: r22 at cfa-16 +// 0x00000014: sw r21, +44(r29) +// 0x00000018: .cfi_offset: r21 at cfa-20 +// 0x00000018: sw r20, +40(r29) +// 0x0000001c: .cfi_offset: r20 at cfa-24 +// 0x0000001c: sw r19, +36(r29) +// 0x00000020: .cfi_offset: r19 at cfa-28 +// 0x00000020: sw r18, +32(r29) +// 0x00000024: .cfi_offset: r18 at cfa-32 +// 0x00000024: sw r4, +0(r29) +// 0x00000028: sw r5, +68(r29) +// 0x0000002c: swc1 f12, +72(r29) +// 0x00000030: sw r6, +76(r29) +// 0x00000034: sw r7, +80(r29) +// 0x00000038: addiu r29, r29, -32 +// 0x0000003c: .cfi_def_cfa_offset: 96 +// 0x0000003c: addiu r29, r29, 32 +// 0x00000040: .cfi_def_cfa_offset: 64 +// 0x00000040: .cfi_remember_state +// 0x00000040: lw r18, +32(r29) +// 0x00000044: .cfi_restore: r18 +// 0x00000044: lw r19, +36(r29) +// 0x00000048: .cfi_restore: r19 +// 0x00000048: lw r20, +40(r29) +// 0x0000004c: .cfi_restore: r20 +// 0x0000004c: lw r21, +44(r29) +// 0x00000050: .cfi_restore: r21 +// 0x00000050: lw r22, +48(r29) +// 0x00000054: .cfi_restore: r22 +// 0x00000054: lw r23, +52(r29) +// 0x00000058: .cfi_restore: r23 +// 0x00000058: lw r30, +56(r29) +// 0x0000005c: .cfi_restore: r30 +// 0x0000005c: lw r31, +60(r29) +// 0x00000060: .cfi_restore: r31 +// 0x00000060: addiu r29, r29, 64 +// 0x00000064: .cfi_def_cfa_offset: 0 +// 0x00000064: jr r31 +// 0x00000068: nop +// 0x0000006c: .cfi_restore_state +// 0x0000006c: .cfi_def_cfa_offset: 64 static constexpr uint8_t expected_asm_kMips64[] = { - 0xA0, 0xFF, 0xBD, 0x67, 0x58, 0x00, 0xBF, 0xFF, 0x50, 0x00, 0xBE, 0xFF, - 0x48, 0x00, 0xBC, 0xFF, 0x40, 0x00, 0xB7, 0xFF, 0x38, 0x00, 0xB6, 0xFF, - 0x30, 0x00, 0xB5, 0xFF, 0x28, 0x00, 0xB4, 0xFF, 0x20, 0x00, 0xB3, 0xFF, - 0x18, 0x00, 0xB2, 0xFF, 0x00, 0x00, 0xA4, 0xFF, 0x68, 0x00, 0xA5, 0xAF, - 0x6C, 0x00, 0xAE, 0xE7, 0x70, 0x00, 0xA7, 0xAF, 0x74, 0x00, 0xA8, 0xAF, - 0xE0, 0xFF, 0xBD, 0x67, 0x20, 0x00, 0xBD, 0x67, 0x18, 0x00, 0xB2, 0xDF, - 0x20, 0x00, 0xB3, 0xDF, 0x28, 0x00, 0xB4, 0xDF, 0x30, 0x00, 0xB5, 0xDF, - 0x38, 0x00, 0xB6, 0xDF, 0x40, 0x00, 0xB7, 0xDF, 0x48, 0x00, 0xBC, 0xDF, - 0x50, 0x00, 0xBE, 0xDF, 0x58, 0x00, 0xBF, 0xDF, 0x60, 0x00, 0xBD, 0x67, + 0x90, 0xFF, 0xBD, 0x67, 0x68, 0x00, 0xBF, 0xFF, 0x60, 0x00, 0xBE, 0xFF, + 0x58, 0x00, 0xBC, 0xFF, 0x50, 0x00, 0xB7, 0xFF, 0x48, 0x00, 0xB6, 0xFF, + 0x40, 0x00, 0xB5, 0xFF, 0x38, 0x00, 0xB4, 0xFF, 0x30, 0x00, 0xB3, 0xFF, + 0x28, 0x00, 0xB2, 0xFF, 0x00, 0x00, 0xA4, 0xFF, 0x78, 0x00, 0xA5, 0xAF, + 0x7C, 0x00, 0xAE, 0xE7, 0x80, 0x00, 0xA7, 0xAF, 0x84, 0x00, 0xA8, 0xAF, + 0xE0, 0xFF, 0xBD, 0x67, 0x20, 0x00, 0xBD, 0x67, 0x28, 0x00, 0xB2, 0xDF, + 0x30, 0x00, 0xB3, 0xDF, 0x38, 0x00, 0xB4, 0xDF, 0x40, 0x00, 0xB5, 0xDF, + 0x48, 0x00, 0xB6, 0xDF, 0x50, 0x00, 0xB7, 0xDF, 0x58, 0x00, 0xBC, 0xDF, + 0x60, 0x00, 0xBE, 0xDF, 0x68, 0x00, 0xBF, 0xDF, 0x70, 0x00, 0xBD, 0x67, 0x09, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x00, }; static constexpr uint8_t expected_cfi_kMips64[] = { - 0x44, 0x0E, 0x60, 0x44, 0x9F, 0x02, 0x44, 0x9E, 0x04, 0x44, 0x9C, 0x06, + 0x44, 0x0E, 0x70, 0x44, 0x9F, 0x02, 0x44, 0x9E, 0x04, 0x44, 0x9C, 0x06, 0x44, 0x97, 0x08, 0x44, 0x96, 0x0A, 0x44, 0x95, 0x0C, 0x44, 0x94, 0x0E, - 0x44, 0x93, 0x10, 0x44, 0x92, 0x12, 0x58, 0x0E, 0x80, 0x01, 0x44, 0x0E, - 0x60, 0x0A, 0x44, 0xD2, 0x44, 0xD3, 0x44, 0xD4, 0x44, 0xD5, 0x44, 0xD6, + 0x44, 0x93, 0x10, 0x44, 0x92, 0x12, 0x58, 0x0E, 0x90, 0x01, 0x44, 0x0E, + 0x70, 0x0A, 0x44, 0xD2, 0x44, 0xD3, 0x44, 0xD4, 0x44, 0xD5, 0x44, 0xD6, 0x44, 0xD7, 0x44, 0xDC, 0x44, 0xDE, 0x44, 0xDF, 0x44, 0x0E, 0x00, 0x48, - 0x0B, 0x0E, 0x60, + 0x0B, 0x0E, 0x70, }; -// 0x00000000: daddiu r29, r29, -96 -// 0x00000004: .cfi_def_cfa_offset: 96 -// 0x00000004: sd r31, +88(r29) +// 0x00000000: daddiu r29, r29, -112 +// 0x00000004: .cfi_def_cfa_offset: 112 +// 0x00000004: sd r31, +104(r29) // 0x00000008: .cfi_offset: r31 at cfa-8 -// 0x00000008: sd r30, +80(r29) +// 0x00000008: sd r30, +96(r29) // 0x0000000c: .cfi_offset: r30 at cfa-16 -// 0x0000000c: sd r28, +72(r29) +// 0x0000000c: sd r28, +88(r29) // 0x00000010: .cfi_offset: r28 at cfa-24 -// 0x00000010: sd r23, +64(r29) +// 0x00000010: sd r23, +80(r29) // 0x00000014: .cfi_offset: r23 at cfa-32 -// 0x00000014: sd r22, +56(r29) +// 0x00000014: sd r22, +72(r29) // 0x00000018: .cfi_offset: r22 at cfa-40 -// 0x00000018: sd r21, +48(r29) +// 0x00000018: sd r21, +64(r29) // 0x0000001c: .cfi_offset: r21 at cfa-48 -// 0x0000001c: sd r20, +40(r29) +// 0x0000001c: sd r20, +56(r29) // 0x00000020: .cfi_offset: r20 at cfa-56 -// 0x00000020: sd r19, +32(r29) +// 0x00000020: sd r19, +48(r29) // 0x00000024: .cfi_offset: r19 at cfa-64 -// 0x00000024: sd r18, +24(r29) +// 0x00000024: sd r18, +40(r29) // 0x00000028: .cfi_offset: r18 at cfa-72 // 0x00000028: sd r4, +0(r29) -// 0x0000002c: sw r5, +104(r29) -// 0x00000030: swc1 f14, +108(r29) -// 0x00000034: sw r7, +112(r29) -// 0x00000038: sw r8, +116(r29) +// 0x0000002c: sw r5, +120(r29) +// 0x00000030: swc1 f14, +124(r29) +// 0x00000034: sw r7, +128(r29) +// 0x00000038: sw r8, +132(r29) // 0x0000003c: daddiu r29, r29, -32 -// 0x00000040: .cfi_def_cfa_offset: 128 +// 0x00000040: .cfi_def_cfa_offset: 144 // 0x00000040: daddiu r29, r29, 32 -// 0x00000044: .cfi_def_cfa_offset: 96 +// 0x00000044: .cfi_def_cfa_offset: 112 // 0x00000044: .cfi_remember_state -// 0x00000044: ld r18, +24(r29) +// 0x00000044: ld r18, +40(r29) // 0x00000048: .cfi_restore: r18 -// 0x00000048: ld r19, +32(r29) +// 0x00000048: ld r19, +48(r29) // 0x0000004c: .cfi_restore: r19 -// 0x0000004c: ld r20, +40(r29) +// 0x0000004c: ld r20, +56(r29) // 0x00000050: .cfi_restore: r20 -// 0x00000050: ld r21, +48(r29) +// 0x00000050: ld r21, +64(r29) // 0x00000054: .cfi_restore: r21 -// 0x00000054: ld r22, +56(r29) +// 0x00000054: ld r22, +72(r29) // 0x00000058: .cfi_restore: r22 -// 0x00000058: ld r23, +64(r29) +// 0x00000058: ld r23, +80(r29) // 0x0000005c: .cfi_restore: r23 -// 0x0000005c: ld r28, +72(r29) +// 0x0000005c: ld r28, +88(r29) // 0x00000060: .cfi_restore: r28 -// 0x00000060: ld r30, +80(r29) +// 0x00000060: ld r30, +96(r29) // 0x00000064: .cfi_restore: r30 -// 0x00000064: ld r31, +88(r29) +// 0x00000064: ld r31, +104(r29) // 0x00000068: .cfi_restore: r31 -// 0x00000068: daddiu r29, r29, 96 +// 0x00000068: daddiu r29, r29, 112 // 0x0000006c: .cfi_def_cfa_offset: 0 // 0x0000006c: jr r31 // 0x00000070: nop // 0x00000074: .cfi_restore_state -// 0x00000074: .cfi_def_cfa_offset: 96 +// 0x00000074: .cfi_def_cfa_offset: 112 + diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 953dfcb2c3..34f0802444 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -67,6 +67,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, const bool is_synchronized = (access_flags & kAccSynchronized) != 0; const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx)); InstructionSet instruction_set = driver->GetInstructionSet(); + const InstructionSetFeatures* instruction_set_features = driver->GetInstructionSetFeatures(); const bool is_64_bit_target = Is64BitInstructionSet(instruction_set); // Calling conventions used to iterate over parameters to method std::unique_ptr<JniCallingConvention> main_jni_conv( @@ -93,7 +94,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, JniCallingConvention::Create(is_static, is_synchronized, jni_end_shorty, instruction_set)); // Assembler that holds generated instructions - std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set)); + std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set, instruction_set_features)); jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetGenerateDebugInfo()); // Offsets into data structures diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc index be2397f518..2d31a9881e 100644 --- a/compiler/jni/quick/mips/calling_convention_mips.cc +++ b/compiler/jni/quick/mips/calling_convention_mips.cc @@ -162,22 +162,19 @@ MipsJniCallingConvention::MipsJniCallingConvention(bool is_static, bool is_synch } padding_ = padding; - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T0)); - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T1)); - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T2)); - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T3)); - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T4)); - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T5)); - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T6)); - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T7)); - callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T8)); + callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S2)); + callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S3)); + callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S4)); + callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S5)); + callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S6)); + callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S7)); + callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(FP)); } uint32_t MipsJniCallingConvention::CoreSpillMask() const { // Compute spill mask to agree with callee saves initialized in the constructor uint32_t result = 0; - result = 1 << T0 | 1 << T1 | 1 << T2 | 1 << T3 | 1 << T4 | 1 << T5 | 1 << T6 | - 1 << T7 | 1 << T8 | 1 << RA; + result = 1 << S2 | 1 << S3 | 1 << S4 | 1 << S5 | 1 << S6 | 1 << S7 | 1 << FP | 1 << RA; return result; } @@ -186,7 +183,7 @@ ManagedRegister MipsJniCallingConvention::ReturnScratchRegister() const { } size_t MipsJniCallingConvention::FrameSize() { - // Method*, LR and callee save area size, local reference segment state + // ArtMethod*, RA and callee save area size, local reference segment state size_t frame_data_size = kMipsPointerSize + (2 + CalleeSaveRegisters().size()) * kFramePointerSize; // References plus 2 words for HandleScope header diff --git a/compiler/jni/quick/mips64/calling_convention_mips64.cc b/compiler/jni/quick/mips64/calling_convention_mips64.cc index 3a11bcfe9c..807d740b4d 100644 --- a/compiler/jni/quick/mips64/calling_convention_mips64.cc +++ b/compiler/jni/quick/mips64/calling_convention_mips64.cc @@ -140,6 +140,7 @@ uint32_t Mips64JniCallingConvention::CoreSpillMask() const { // Compute spill mask to agree with callee saves initialized in the constructor uint32_t result = 0; result = 1 << S2 | 1 << S3 | 1 << S4 | 1 << S5 | 1 << S6 | 1 << S7 | 1 << GP | 1 << S8 | 1 << RA; + DCHECK_EQ(static_cast<size_t>(POPCOUNT(result)), callee_save_regs_.size() + 1); return result; } @@ -148,9 +149,9 @@ ManagedRegister Mips64JniCallingConvention::ReturnScratchRegister() const { } size_t Mips64JniCallingConvention::FrameSize() { - // Mehtod* and callee save area size, local reference segment state + // ArtMethod*, RA and callee save area size, local reference segment state size_t frame_data_size = kFramePointerSize + - CalleeSaveRegisters().size() * kFramePointerSize + sizeof(uint32_t); + (CalleeSaveRegisters().size() + 1) * kFramePointerSize + sizeof(uint32_t); // References plus 2 words for HandleScope header size_t handle_scope_size = HandleScope::SizeOf(kFramePointerSize, ReferenceCount()); // Plus return value spill area size diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc index 9c7eab1cc7..b6b11ca51f 100644 --- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc +++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc @@ -38,8 +38,7 @@ ManagedRegister X86_64JniCallingConvention::ReturnScratchRegister() const { return ManagedRegister::NoRegister(); // No free regs, so assembler uses push/pop } -static ManagedRegister ReturnRegisterForShorty(const char* shorty, bool jni) { - UNUSED(jni); +static ManagedRegister ReturnRegisterForShorty(const char* shorty, bool jni ATTRIBUTE_UNUSED) { if (shorty[0] == 'F' || shorty[0] == 'D') { return X86_64ManagedRegister::FromXmmRegister(XMM0); } else if (shorty[0] == 'J') { diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 7b410bfe37..a78a5b3644 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -31,6 +31,7 @@ #include "dex/verification_results.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" +#include "gc/space/image_space.h" #include "gc/space/space.h" #include "image_writer.h" #include "linker/relative_patcher.h" @@ -43,6 +44,7 @@ #include "safe_map.h" #include "scoped_thread_state_change.h" #include "handle_scope-inl.h" +#include "utils/dex_cache_arrays_layout-inl.h" #include "verifier/method_verifier.h" namespace art { @@ -143,6 +145,18 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, } size_ = offset; + if (!HasImage()) { + // Allocate space for app dex cache arrays in the .bss section. + size_t bss_start = RoundUp(size_, kPageSize); + size_t pointer_size = GetInstructionSetPointerSize(instruction_set); + bss_size_ = 0u; + for (const DexFile* dex_file : dex_files) { + dex_cache_arrays_offsets_.Put(dex_file, bss_start + bss_size_); + DexCacheArraysLayout layout(pointer_size, dex_file); + bss_size_ += layout.Size(); + } + } + CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); CHECK_EQ(compiler->IsImage(), image_writer_ != nullptr); CHECK_EQ(compiler->IsImage(), @@ -655,10 +669,10 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { no_thread_suspension_(soa_.Self(), "OatWriter patching"), class_linker_(Runtime::Current()->GetClassLinker()), dex_cache_(nullptr) { - if (writer_->image_writer_ != nullptr) { + patched_code_.reserve(16 * KB); + if (writer_->HasImage()) { // If we're creating the image, the address space must be ready so that we can apply patches. CHECK(writer_->image_writer_->IsImageAddressSpaceReady()); - patched_code_.reserve(16 * KB); } } @@ -841,24 +855,28 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) { - if (writer_->image_writer_ != nullptr) { + if (writer_->HasImage()) { auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<const uint8_t*>( patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset()); const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_; return element - oat_data; } else { - LOG(FATAL) << "Unimplemented."; - UNREACHABLE(); + size_t start = writer_->dex_cache_arrays_offsets_.Get(patch.TargetDexCacheDexFile()); + return start + patch.TargetDexCacheElementOffset(); } } void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object) SHARED_REQUIRES(Locks::mutator_lock_) { - // NOTE: Direct method pointers across oat files don't use linker patches. However, direct - // type pointers across oat files do. (TODO: Investigate why.) - if (writer_->image_writer_ != nullptr) { + if (writer_->HasImage()) { object = writer_->image_writer_->GetImageAddress(object); + } else { + // NOTE: We're using linker patches for app->boot references when the image can + // be relocated and therefore we need to emit .oat_patches. We're not using this + // for app->app references, so check that the object is in the image space. + DCHECK(Runtime::Current()->GetHeap()->FindSpaceFromObject(object, false)->IsImageSpace()); } + // Note: We only patch targeting Objects in image which is in the low 4gb. uint32_t address = PointerToLowMemUInt32(object); DCHECK_LE(offset + 4, code->size()); uint8_t* data = &(*code)[offset]; @@ -870,12 +888,17 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { void PatchMethodAddress(std::vector<uint8_t>* code, uint32_t offset, ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) { - // NOTE: Direct method pointers across oat files don't use linker patches. However, direct - // type pointers across oat files do. (TODO: Investigate why.) - if (writer_->image_writer_ != nullptr) { + if (writer_->HasImage()) { method = writer_->image_writer_->GetImageMethodAddress(method); + } else if (kIsDebugBuild) { + // NOTE: We're using linker patches for app->boot references when the image can + // be relocated and therefore we need to emit .oat_patches. We're not using this + // for app->app references, so check that the method is an image method. + gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); + size_t method_offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin(); + CHECK(image_space->GetImageHeader().GetMethodsSection().Contains(method_offset)); } - // Note: We only patch ArtMethods to low 4gb since thats where the image is. + // Note: We only patch targeting ArtMethods in image which is in the low 4gb. uint32_t address = PointerToLowMemUInt32(method); DCHECK_LE(offset + 4, code->size()); uint8_t* data = &(*code)[offset]; @@ -887,9 +910,11 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset) SHARED_REQUIRES(Locks::mutator_lock_) { - uint32_t address = writer_->image_writer_ == nullptr ? target_offset : - PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() + - writer_->oat_data_offset_ + target_offset); + uint32_t address = target_offset; + if (writer_->HasImage()) { + address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() + + writer_->oat_data_offset_ + target_offset); + } DCHECK_LE(offset + 4, code->size()); uint8_t* data = &(*code)[offset]; data[0] = address & 0xffu; diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 48fbc0b2ac..d6cb65bd64 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -90,6 +90,13 @@ class OatWriter { TimingLogger* timings, SafeMap<std::string, std::string>* key_value_store); + // Returns whether the oat file has an associated image. + bool HasImage() const { + // Since the image is being created at the same time as the oat file, + // check if there's an image writer. + return image_writer_ != nullptr; + } + const OatHeader& GetOatHeader() const { return *oat_header_; } @@ -272,6 +279,10 @@ class OatWriter { // The size of the required .bss section holding the DexCache data. size_t bss_size_; + // Offsets of the dex cache arrays for each app dex file. For the + // boot image, this information is provided by the ImageWriter. + SafeMap<const DexFile*, size_t> dex_cache_arrays_offsets_; // DexFiles not owned. + // Offset of the oat data from the start of the mmapped region of the elf file. size_t oat_data_offset_; diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc index 5b346872b0..f985745e7a 100644 --- a/compiler/optimizing/boolean_simplifier.cc +++ b/compiler/optimizing/boolean_simplifier.cc @@ -69,19 +69,17 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { if (cond->IsCondition()) { HInstruction* lhs = cond->InputAt(0); HInstruction* rhs = cond->InputAt(1); - if (cond->IsEqual()) { - return new (allocator) HNotEqual(lhs, rhs); - } else if (cond->IsNotEqual()) { - return new (allocator) HEqual(lhs, rhs); - } else if (cond->IsLessThan()) { - return new (allocator) HGreaterThanOrEqual(lhs, rhs); - } else if (cond->IsLessThanOrEqual()) { - return new (allocator) HGreaterThan(lhs, rhs); - } else if (cond->IsGreaterThan()) { - return new (allocator) HLessThanOrEqual(lhs, rhs); - } else { - DCHECK(cond->IsGreaterThanOrEqual()); - return new (allocator) HLessThan(lhs, rhs); + switch (cond->AsCondition()->GetOppositeCondition()) { // get *opposite* + case kCondEQ: return new (allocator) HEqual(lhs, rhs); + case kCondNE: return new (allocator) HNotEqual(lhs, rhs); + case kCondLT: return new (allocator) HLessThan(lhs, rhs); + case kCondLE: return new (allocator) HLessThanOrEqual(lhs, rhs); + case kCondGT: return new (allocator) HGreaterThan(lhs, rhs); + case kCondGE: return new (allocator) HGreaterThanOrEqual(lhs, rhs); + case kCondB: return new (allocator) HBelow(lhs, rhs); + case kCondBE: return new (allocator) HBelowOrEqual(lhs, rhs); + case kCondA: return new (allocator) HAbove(lhs, rhs); + case kCondAE: return new (allocator) HAboveOrEqual(lhs, rhs); } } else if (cond->IsIntConstant()) { HIntConstant* int_const = cond->AsIntConstant(); @@ -91,11 +89,10 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { DCHECK(int_const->IsOne()); return graph->GetIntConstant(0); } - } else { - // General case when 'cond' is another instruction of type boolean, - // as verified by SSAChecker. - return new (allocator) HBooleanNot(cond); } + // General case when 'cond' is another instruction of type boolean, + // as verified by SSAChecker. + return new (allocator) HBooleanNot(cond); } void HBooleanSimplifier::TryRemovingBooleanSelection(HBasicBlock* block) { diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index eb55ebc117..5dd5be3259 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -1184,10 +1184,9 @@ void HGraphBuilder::PotentiallySimplifyFakeString(uint16_t original_dex_register verified_method->GetStringInitPcRegMap(); auto map_it = string_init_map.find(dex_pc); if (map_it != string_init_map.end()) { - std::set<uint32_t> reg_set = map_it->second; - for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { + for (uint32_t reg : map_it->second) { HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot, dex_pc); - UpdateLocal(*set_it, load_local, dex_pc); + UpdateLocal(reg, load_local, dex_pc); } } } else { @@ -1311,7 +1310,13 @@ bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const { soa, dex_cache, class_loader, type_index, dex_compilation_unit_))); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); - return outer_class.Get() == cls.Get(); + // GetOutermostCompilingClass returns null when the class is unresolved + // (e.g. if it derives from an unresolved class). This is bogus knowing that + // we are compiling it. + // When this happens we cannot establish a direct relation between the current + // class and the outer class, so we return false. + // (Note that this is only used for optimizing invokes and field accesses) + return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get()); } void HGraphBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction, diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 28970062cc..6a743ebbc9 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -41,6 +41,7 @@ #include "driver/dex_compilation_unit.h" #include "gc_map_builder.h" #include "graph_visualizer.h" +#include "intrinsics.h" #include "leb128.h" #include "mapping_table.h" #include "mirror/array-inl.h" @@ -1381,4 +1382,57 @@ void SlowPathCode::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* } } +void CodeGenerator::CreateSystemArrayCopyLocationSummary(HInvoke* invoke) { + // Check to see if we have known failures that will cause us to have to bail out + // to the runtime, and just generate the runtime call directly. + HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant(); + HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant(); + + // The positions must be non-negative. + if ((src_pos != nullptr && src_pos->GetValue() < 0) || + (dest_pos != nullptr && dest_pos->GetValue() < 0)) { + // We will have to fail anyways. + return; + } + + // The length must be >= 0. + HIntConstant* length = invoke->InputAt(4)->AsIntConstant(); + if (length != nullptr) { + int32_t len = length->GetValue(); + if (len < 0) { + // Just call as normal. + return; + } + } + + SystemArrayCopyOptimizations optimizations(invoke); + + if (optimizations.GetDestinationIsSource()) { + if (src_pos != nullptr && dest_pos != nullptr && src_pos->GetValue() < dest_pos->GetValue()) { + // We only support backward copying if source and destination are the same. + return; + } + } + + if (optimizations.GetDestinationIsPrimitiveArray() || optimizations.GetSourceIsPrimitiveArray()) { + // We currently don't intrinsify primitive copying. + return; + } + + ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena(); + LocationSummary* locations = new (allocator) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length). + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3))); + locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4))); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + } // namespace art diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index acce5b3359..b04dfc00b2 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -421,6 +421,8 @@ class CodeGenerator { Location runtime_type_index_location, Location runtime_return_location); + static void CreateSystemArrayCopyLocationSummary(HInvoke* invoke); + void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; } DisassemblyInformation* GetDisassemblyInformation() const { return disasm_info_; } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index d172fba888..92a5878476 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -409,7 +409,7 @@ class ArraySetSlowPathARM : public SlowPathCode { #undef __ #define __ down_cast<ArmAssembler*>(GetAssembler())-> -inline Condition ARMSignedOrFPCondition(IfCondition cond) { +inline Condition ARMCondition(IfCondition cond) { switch (cond) { case kCondEQ: return EQ; case kCondNE: return NE; @@ -417,19 +417,30 @@ inline Condition ARMSignedOrFPCondition(IfCondition cond) { case kCondLE: return LE; case kCondGT: return GT; case kCondGE: return GE; + case kCondB: return LO; + case kCondBE: return LS; + case kCondA: return HI; + case kCondAE: return HS; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); } +// Maps signed condition to unsigned condition. inline Condition ARMUnsignedCondition(IfCondition cond) { switch (cond) { case kCondEQ: return EQ; case kCondNE: return NE; + // Signed to unsigned. case kCondLT: return LO; case kCondLE: return LS; case kCondGT: return HI; case kCondGE: return HS; + // Unsigned remain unchanged. + case kCondB: return LO; + case kCondBE: return LS; + case kCondA: return HI; + case kCondAE: return HS; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); @@ -1130,8 +1141,7 @@ void LocationsBuilderARM::VisitExit(HExit* exit) { exit->SetLocations(nullptr); } -void InstructionCodeGeneratorARM::VisitExit(HExit* exit) { - UNUSED(exit); +void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { } void InstructionCodeGeneratorARM::GenerateCompareWithImmediate(Register left, int32_t right) { @@ -1149,12 +1159,13 @@ void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label) { __ vmstat(); // transfer FP status register to ARM APSR. + // TODO: merge into a single branch (except "equal or unordered" and "not equal") if (cond->IsFPConditionTrueIfNaN()) { __ b(true_label, VS); // VS for unordered. } else if (cond->IsFPConditionFalseIfNaN()) { __ b(false_label, VS); // VS for unordered. } - __ b(true_label, ARMSignedOrFPCondition(cond->GetCondition())); + __ b(true_label, ARMCondition(cond->GetCondition())); } void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, @@ -1169,10 +1180,11 @@ void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, Register left_low = left.AsRegisterPairLow<Register>(); IfCondition true_high_cond = if_cond; IfCondition false_high_cond = cond->GetOppositeCondition(); - Condition final_condition = ARMUnsignedCondition(if_cond); + Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part // Set the conditions for the test, remembering that == needs to be // decided using the low words. + // TODO: consider avoiding jumps with temporary and CMP low+SBC high switch (if_cond) { case kCondEQ: case kCondNE: @@ -1190,6 +1202,18 @@ void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, case kCondGE: true_high_cond = kCondGT; break; + case kCondB: + false_high_cond = kCondA; + break; + case kCondBE: + true_high_cond = kCondB; + break; + case kCondA: + false_high_cond = kCondB; + break; + case kCondAE: + true_high_cond = kCondA; + break; } if (right.IsConstant()) { int64_t value = right.GetConstant()->AsLongConstant()->GetValue(); @@ -1198,12 +1222,12 @@ void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, GenerateCompareWithImmediate(left_high, val_high); if (if_cond == kCondNE) { - __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); + __ b(true_label, ARMCondition(true_high_cond)); } else if (if_cond == kCondEQ) { - __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); + __ b(false_label, ARMCondition(false_high_cond)); } else { - __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); - __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); + __ b(true_label, ARMCondition(true_high_cond)); + __ b(false_label, ARMCondition(false_high_cond)); } // Must be equal high, so compare the lows. GenerateCompareWithImmediate(left_low, val_low); @@ -1213,17 +1237,18 @@ void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, __ cmp(left_high, ShifterOperand(right_high)); if (if_cond == kCondNE) { - __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); + __ b(true_label, ARMCondition(true_high_cond)); } else if (if_cond == kCondEQ) { - __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); + __ b(false_label, ARMCondition(false_high_cond)); } else { - __ b(true_label, ARMSignedOrFPCondition(true_high_cond)); - __ b(false_label, ARMSignedOrFPCondition(false_high_cond)); + __ b(true_label, ARMCondition(true_high_cond)); + __ b(false_label, ARMCondition(false_high_cond)); } // Must be equal high, so compare the lows. __ cmp(left_low, ShifterOperand(right_low)); } // The last comparison might be unsigned. + // TODO: optimize cases where this is always true/false __ b(true_label, final_condition); } @@ -1315,7 +1340,7 @@ void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instructio DCHECK(right.IsConstant()); GenerateCompareWithImmediate(left, CodeGenerator::GetInt32ValueOf(right.GetConstant())); } - __ b(true_target, ARMSignedOrFPCondition(cond->AsCondition()->GetCondition())); + __ b(true_target, ARMCondition(cond->AsCondition()->GetCondition())); } } if (false_target != nullptr) { @@ -1351,8 +1376,7 @@ void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); HInstruction* cond = deoptimize->InputAt(0); - DCHECK(cond->IsCondition()); - if (cond->AsCondition()->NeedsMaterialization()) { + if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { locations->SetInAt(0, Location::RequiresRegister()); } } @@ -1417,11 +1441,11 @@ void InstructionCodeGeneratorARM::VisitCondition(HCondition* cond) { GenerateCompareWithImmediate(left.AsRegister<Register>(), CodeGenerator::GetInt32ValueOf(right.GetConstant())); } - __ it(ARMSignedOrFPCondition(cond->GetCondition()), kItElse); + __ it(ARMCondition(cond->GetCondition()), kItElse); __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1), - ARMSignedOrFPCondition(cond->GetCondition())); + ARMCondition(cond->GetCondition())); __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0), - ARMSignedOrFPCondition(cond->GetOppositeCondition())); + ARMCondition(cond->GetOppositeCondition())); return; } case Primitive::kPrimLong: @@ -1500,6 +1524,38 @@ void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* c VisitCondition(comp); } +void LocationsBuilderARM::VisitBelow(HBelow* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitBelow(HBelow* comp) { + VisitCondition(comp); +} + +void LocationsBuilderARM::VisitBelowOrEqual(HBelowOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitBelowOrEqual(HBelowOrEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderARM::VisitAbove(HAbove* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitAbove(HAbove* comp) { + VisitCondition(comp); +} + +void LocationsBuilderARM::VisitAboveOrEqual(HAboveOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorARM::VisitAboveOrEqual(HAboveOrEqual* comp) { + VisitCondition(comp); +} + void LocationsBuilderARM::VisitLocal(HLocal* local) { local->SetLocations(nullptr); } @@ -1512,9 +1568,8 @@ void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) { load->SetLocations(nullptr); } -void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load) { +void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) { // Nothing to do, this is driven by the code generator. - UNUSED(load); } void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) { @@ -1541,8 +1596,7 @@ void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) { } } -void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) { - UNUSED(store); +void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) { } void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { @@ -1551,9 +1605,8 @@ void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) { +void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderARM::VisitNullConstant(HNullConstant* constant) { @@ -1562,9 +1615,8 @@ void LocationsBuilderARM::VisitNullConstant(HNullConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM::VisitNullConstant(HNullConstant* constant) { +void InstructionCodeGeneratorARM::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { @@ -1573,9 +1625,8 @@ void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) { +void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) { @@ -1584,9 +1635,8 @@ void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant) { +void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) { @@ -1595,9 +1645,8 @@ void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant) { +void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { @@ -1612,8 +1661,7 @@ void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } -void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret) { - UNUSED(ret); +void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) { codegen_->GenerateFrameExit(); } @@ -1623,8 +1671,7 @@ void LocationsBuilderARM::VisitReturn(HReturn* ret) { locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType())); } -void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { - UNUSED(ret); +void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) { codegen_->GenerateFrameExit(); } @@ -1645,6 +1692,7 @@ void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invok DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), + codegen_->GetAssembler(), codegen_->GetInstructionSetFeatures()); if (intrinsic.TryDispatch(invoke)) { return; @@ -1684,6 +1732,7 @@ void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), + codegen_->GetAssembler(), codegen_->GetInstructionSetFeatures()); if (intrinsic.TryDispatch(invoke)) { return; @@ -3268,8 +3317,7 @@ void LocationsBuilderARM::VisitPhi(HPhi* instruction) { locations->SetOut(Location::Any()); } -void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) { - UNUSED(instruction); +void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unreachable"; } @@ -3512,6 +3560,47 @@ void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldI } } +Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant, + Opcode opcode) { + DCHECK(!Primitive::IsFloatingPointType(constant->GetType())); + if (constant->IsConstant() && + CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) { + return Location::ConstantLocation(constant->AsConstant()); + } + return Location::RequiresRegister(); +} + +bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst, + Opcode opcode) { + uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst)); + if (Primitive::Is64BitType(input_cst->GetType())) { + return CanEncodeConstantAsImmediate(Low32Bits(value), opcode) && + CanEncodeConstantAsImmediate(High32Bits(value), opcode); + } else { + return CanEncodeConstantAsImmediate(Low32Bits(value), opcode); + } +} + +bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode) { + ShifterOperand so; + ArmAssembler* assembler = codegen_->GetAssembler(); + if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, &so)) { + return true; + } + Opcode neg_opcode = kNoOperand; + switch (opcode) { + case AND: + neg_opcode = BIC; + break; + case ORR: + neg_opcode = ORN; + break; + default: + return false; + } + return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, ~value, &so); +} + void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); @@ -4189,13 +4278,11 @@ void LocationsBuilderARM::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } -void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp) { +void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) { // Nothing to do, this is driven by the code generator. - UNUSED(temp); } -void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction) { - UNUSED(instruction); +void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unreachable"; } @@ -4912,17 +4999,18 @@ void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instr nullptr); } -void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction); } -void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction); } -void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); } +void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } +void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); } +void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); } -void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction) { +void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); DCHECK(instruction->GetResultType() == Primitive::kPrimInt || instruction->GetResultType() == Primitive::kPrimLong); + // Note: GVN reorders commutative operations to have the constant on the right hand side. locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode)); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } @@ -4938,48 +5026,131 @@ void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); } +void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { + // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). + if (value == 0xffffffffu) { + if (out != first) { + __ mov(out, ShifterOperand(first)); + } + return; + } + if (value == 0u) { + __ mov(out, ShifterOperand(0)); + return; + } + ShifterOperand so; + if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) { + __ and_(out, first, so); + } else { + DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)); + __ bic(out, first, ShifterOperand(~value)); + } +} + +void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) { + // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier). + if (value == 0u) { + if (out != first) { + __ mov(out, ShifterOperand(first)); + } + return; + } + if (value == 0xffffffffu) { + __ mvn(out, ShifterOperand(0)); + return; + } + ShifterOperand so; + if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) { + __ orr(out, first, so); + } else { + DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so)); + __ orn(out, first, ShifterOperand(~value)); + } +} + +void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) { + // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier). + if (value == 0u) { + if (out != first) { + __ mov(out, ShifterOperand(first)); + } + return; + } + __ eor(out, first, ShifterOperand(value)); +} + void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) { LocationSummary* locations = instruction->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + Location out = locations->Out(); + + if (second.IsConstant()) { + uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); + uint32_t value_low = Low32Bits(value); + if (instruction->GetResultType() == Primitive::kPrimInt) { + Register first_reg = first.AsRegister<Register>(); + Register out_reg = out.AsRegister<Register>(); + if (instruction->IsAnd()) { + GenerateAndConst(out_reg, first_reg, value_low); + } else if (instruction->IsOr()) { + GenerateOrrConst(out_reg, first_reg, value_low); + } else { + DCHECK(instruction->IsXor()); + GenerateEorConst(out_reg, first_reg, value_low); + } + } else { + DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); + uint32_t value_high = High32Bits(value); + Register first_low = first.AsRegisterPairLow<Register>(); + Register first_high = first.AsRegisterPairHigh<Register>(); + Register out_low = out.AsRegisterPairLow<Register>(); + Register out_high = out.AsRegisterPairHigh<Register>(); + if (instruction->IsAnd()) { + GenerateAndConst(out_low, first_low, value_low); + GenerateAndConst(out_high, first_high, value_high); + } else if (instruction->IsOr()) { + GenerateOrrConst(out_low, first_low, value_low); + GenerateOrrConst(out_high, first_high, value_high); + } else { + DCHECK(instruction->IsXor()); + GenerateEorConst(out_low, first_low, value_low); + GenerateEorConst(out_high, first_high, value_high); + } + } + return; + } if (instruction->GetResultType() == Primitive::kPrimInt) { - Register first = locations->InAt(0).AsRegister<Register>(); - Register second = locations->InAt(1).AsRegister<Register>(); - Register out = locations->Out().AsRegister<Register>(); + Register first_reg = first.AsRegister<Register>(); + ShifterOperand second_reg(second.AsRegister<Register>()); + Register out_reg = out.AsRegister<Register>(); if (instruction->IsAnd()) { - __ and_(out, first, ShifterOperand(second)); + __ and_(out_reg, first_reg, second_reg); } else if (instruction->IsOr()) { - __ orr(out, first, ShifterOperand(second)); + __ orr(out_reg, first_reg, second_reg); } else { DCHECK(instruction->IsXor()); - __ eor(out, first, ShifterOperand(second)); + __ eor(out_reg, first_reg, second_reg); } } else { DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); - Location first = locations->InAt(0); - Location second = locations->InAt(1); - Location out = locations->Out(); + Register first_low = first.AsRegisterPairLow<Register>(); + Register first_high = first.AsRegisterPairHigh<Register>(); + ShifterOperand second_low(second.AsRegisterPairLow<Register>()); + ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); + Register out_low = out.AsRegisterPairLow<Register>(); + Register out_high = out.AsRegisterPairHigh<Register>(); if (instruction->IsAnd()) { - __ and_(out.AsRegisterPairLow<Register>(), - first.AsRegisterPairLow<Register>(), - ShifterOperand(second.AsRegisterPairLow<Register>())); - __ and_(out.AsRegisterPairHigh<Register>(), - first.AsRegisterPairHigh<Register>(), - ShifterOperand(second.AsRegisterPairHigh<Register>())); + __ and_(out_low, first_low, second_low); + __ and_(out_high, first_high, second_high); } else if (instruction->IsOr()) { - __ orr(out.AsRegisterPairLow<Register>(), - first.AsRegisterPairLow<Register>(), - ShifterOperand(second.AsRegisterPairLow<Register>())); - __ orr(out.AsRegisterPairHigh<Register>(), - first.AsRegisterPairHigh<Register>(), - ShifterOperand(second.AsRegisterPairHigh<Register>())); + __ orr(out_low, first_low, second_low); + __ orr(out_high, first_high, second_high); } else { DCHECK(instruction->IsXor()); - __ eor(out.AsRegisterPairLow<Register>(), - first.AsRegisterPairLow<Register>(), - ShifterOperand(second.AsRegisterPairLow<Register>())); - __ eor(out.AsRegisterPairHigh<Register>(), - first.AsRegisterPairHigh<Register>(), - ShifterOperand(second.AsRegisterPairHigh<Register>())); + __ eor(out_low, first_low, second_low); + __ eor(out_high, first_high, second_high); } } } @@ -5159,15 +5330,13 @@ Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_m return DeduplicateMethodLiteral(target_method, &call_patches_); } -void LocationsBuilderARM::VisitBoundType(HBoundType* instruction) { +void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. - UNUSED(instruction); LOG(FATAL) << "Unreachable"; } -void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction) { +void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. - UNUSED(instruction); LOG(FATAL) << "Unreachable"; } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 16d1d383b4..6900933e87 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -169,11 +169,15 @@ class LocationsBuilderARM : public HGraphVisitor { private: void HandleInvoke(HInvoke* invoke); - void HandleBitwiseOperation(HBinaryOperation* operation); + void HandleBitwiseOperation(HBinaryOperation* operation, Opcode opcode); void HandleShift(HBinaryOperation* operation); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + Location ArmEncodableConstantOrRegister(HInstruction* constant, Opcode opcode); + bool CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode); + bool CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode); + CodeGeneratorARM* const codegen_; InvokeDexCallingConventionVisitorARM parameter_visitor_; @@ -205,6 +209,9 @@ class InstructionCodeGeneratorARM : public HGraphVisitor { // the suspend call. void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg); + void GenerateAndConst(Register out, Register first, uint32_t value); + void GenerateOrrConst(Register out, Register first, uint32_t value); + void GenerateEorConst(Register out, Register first, uint32_t value); void HandleBitwiseOperation(HBinaryOperation* operation); void HandleShift(HBinaryOperation* operation); void GenerateMemoryBarrier(MemBarrierKind kind); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index c94da86d2c..f68b11b504 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -77,6 +77,10 @@ inline Condition ARM64Condition(IfCondition cond) { case kCondLE: return le; case kCondGT: return gt; case kCondGE: return ge; + case kCondB: return lo; + case kCondBE: return ls; + case kCondA: return hi; + case kCondAE: return hs; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); @@ -1326,8 +1330,7 @@ enum UnimplementedInstructionBreakCode { }; #define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS(name) \ - void InstructionCodeGeneratorARM64::Visit##name(H##name* instr) { \ - UNUSED(instr); \ + void InstructionCodeGeneratorARM64::Visit##name(H##name* instr ATTRIBUTE_UNUSED) { \ __ Brk(UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name)); \ } \ void LocationsBuilderARM64::Visit##name(H##name* instr) { \ @@ -1937,7 +1940,11 @@ void InstructionCodeGeneratorARM64::VisitCondition(HCondition* instruction) { M(LessThan) \ M(LessThanOrEqual) \ M(GreaterThan) \ - M(GreaterThanOrEqual) + M(GreaterThanOrEqual) \ + M(Below) \ + M(BelowOrEqual) \ + M(Above) \ + M(AboveOrEqual) #define DEFINE_CONDITION_VISITORS(Name) \ void LocationsBuilderARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } \ void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } @@ -2175,8 +2182,8 @@ void LocationsBuilderARM64::VisitDoubleConstant(HDoubleConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM64::VisitDoubleConstant(HDoubleConstant* constant) { - UNUSED(constant); +void InstructionCodeGeneratorARM64::VisitDoubleConstant( + HDoubleConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. } @@ -2184,8 +2191,7 @@ void LocationsBuilderARM64::VisitExit(HExit* exit) { exit->SetLocations(nullptr); } -void InstructionCodeGeneratorARM64::VisitExit(HExit* exit) { - UNUSED(exit); +void InstructionCodeGeneratorARM64::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { } void LocationsBuilderARM64::VisitFloatConstant(HFloatConstant* constant) { @@ -2194,8 +2200,7 @@ void LocationsBuilderARM64::VisitFloatConstant(HFloatConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM64::VisitFloatConstant(HFloatConstant* constant) { - UNUSED(constant); +void InstructionCodeGeneratorARM64::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. } @@ -2348,8 +2353,7 @@ void LocationsBuilderARM64::VisitDeoptimize(HDeoptimize* deoptimize) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); HInstruction* cond = deoptimize->InputAt(0); - DCHECK(cond->IsCondition()); - if (cond->AsCondition()->NeedsMaterialization()) { + if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { locations->SetInAt(0, Location::RequiresRegister()); } } @@ -2682,9 +2686,8 @@ void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant) { +void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderARM64::VisitNullConstant(HNullConstant* constant) { @@ -2692,9 +2695,8 @@ void LocationsBuilderARM64::VisitNullConstant(HNullConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM64::VisitNullConstant(HNullConstant* constant) { +void InstructionCodeGeneratorARM64::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { @@ -3085,9 +3087,8 @@ void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) { load->SetLocations(nullptr); } -void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load) { +void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) { // Nothing to do, this is driven by the code generator. - UNUSED(load); } void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { @@ -3124,9 +3125,8 @@ void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant) { +void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderARM64::VisitMonitorOperation(HMonitorOperation* instruction) { @@ -3393,8 +3393,7 @@ void LocationsBuilderARM64::VisitPhi(HPhi* instruction) { locations->SetOut(Location::Any()); } -void InstructionCodeGeneratorARM64::VisitPhi(HPhi* instruction) { - UNUSED(instruction); +void InstructionCodeGeneratorARM64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unreachable"; } @@ -3464,8 +3463,7 @@ void LocationsBuilderARM64::VisitReturn(HReturn* instruction) { locations->SetInAt(0, ARM64ReturnLocation(return_type)); } -void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction) { - UNUSED(instruction); +void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction ATTRIBUTE_UNUSED) { codegen_->GenerateFrameExit(); } @@ -3473,8 +3471,7 @@ void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) { instruction->SetLocations(nullptr); } -void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction) { - UNUSED(instruction); +void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction ATTRIBUTE_UNUSED) { codegen_->GenerateFrameExit(); } @@ -3518,8 +3515,7 @@ void LocationsBuilderARM64::VisitStoreLocal(HStoreLocal* store) { } } -void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store) { - UNUSED(store); +void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) { } void LocationsBuilderARM64::VisitSub(HSub* instruction) { @@ -3636,9 +3632,8 @@ void LocationsBuilderARM64::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } -void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp) { +void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) { // Nothing to do, this is driven by the code generator. - UNUSED(temp); } void LocationsBuilderARM64::VisitThrow(HThrow* instruction) { @@ -3737,15 +3732,13 @@ void InstructionCodeGeneratorARM64::VisitXor(HXor* instruction) { HandleBinaryOp(instruction); } -void LocationsBuilderARM64::VisitBoundType(HBoundType* instruction) { +void LocationsBuilderARM64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. - UNUSED(instruction); LOG(FATAL) << "Unreachable"; } -void InstructionCodeGeneratorARM64::VisitBoundType(HBoundType* instruction) { +void InstructionCodeGeneratorARM64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. - UNUSED(instruction); LOG(FATAL) << "Unreachable"; } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 1a08503cf9..f561c97eb0 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1778,6 +1778,9 @@ void InstructionCodeGeneratorMIPS64::VisitCondition(HCondition* instruction) { return; } + // TODO: generalize to long + DCHECK_NE(instruction->InputAt(0)->GetType(), Primitive::kPrimLong); + LocationSummary* locations = instruction->GetLocations(); GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); @@ -1855,6 +1858,48 @@ void InstructionCodeGeneratorMIPS64::VisitCondition(HCondition* instruction) { } } break; + + case kCondB: + case kCondAE: + if (use_imm && 0 <= rhs_imm && rhs_imm <= 0x7fff) { + __ Sltiu(dst, lhs, rhs_imm); + } else { + if (use_imm) { + rhs_reg = TMP; + __ LoadConst32(rhs_reg, rhs_imm); + } + __ Sltu(dst, lhs, rhs_reg); + } + if (if_cond == kCondAE) { + // Simulate lhs >= rhs via !(lhs < rhs) since there's + // only the sltu instruction but no sgeu. + __ Xori(dst, dst, 1); + } + break; + + case kCondBE: + case kCondA: + if (use_imm && 0 <= rhs_imm && rhs_imm <= 0x7ffe) { + // Simulate lhs <= rhs via lhs < rhs + 1. + __ Sltiu(dst, lhs, rhs_imm + 1); + if (if_cond == kCondA) { + // Simulate lhs > rhs via !(lhs <= rhs) since there's + // only the sltiu instruction but no sgtiu. + __ Xori(dst, dst, 1); + } + } else { + if (use_imm) { + rhs_reg = TMP; + __ LoadConst32(rhs_reg, rhs_imm); + } + __ Sltu(dst, rhs_reg, lhs); + if (if_cond == kCondBE) { + // Simulate lhs <= rhs via !(rhs < lhs) since there's + // only the sltu instruction but no sleu. + __ Xori(dst, dst, 1); + } + } + break; } } @@ -2072,6 +2117,17 @@ void InstructionCodeGeneratorMIPS64::GenerateTestAndBranch(HInstruction* instruc case kCondGT: __ Bgtzc(lhs, true_target); break; + case kCondB: + break; // always false + case kCondBE: + __ Beqzc(lhs, true_target); // <= 0 if zero + break; + case kCondA: + __ Bnezc(lhs, true_target); // > 0 if non-zero + break; + case kCondAE: + __ B(true_target); // always true + break; } } else { if (use_imm) { @@ -2086,12 +2142,16 @@ void InstructionCodeGeneratorMIPS64::GenerateTestAndBranch(HInstruction* instruc case kCondEQ: case kCondGE: case kCondLE: + case kCondBE: + case kCondAE: // if lhs == rhs for a positive condition, then it is a branch __ B(true_target); break; case kCondNE: case kCondLT: case kCondGT: + case kCondB: + case kCondA: // if lhs == rhs for a negative condition, then it is a NOP break; } @@ -2115,6 +2175,18 @@ void InstructionCodeGeneratorMIPS64::GenerateTestAndBranch(HInstruction* instruc case kCondGT: __ Bltc(rhs_reg, lhs, true_target); break; + case kCondB: + __ Bltuc(lhs, rhs_reg, true_target); + break; + case kCondAE: + __ Bgeuc(lhs, rhs_reg, true_target); + break; + case kCondBE: + __ Bgeuc(rhs_reg, lhs, true_target); + break; + case kCondA: + __ Bltuc(rhs_reg, lhs, true_target); + break; } } } @@ -2151,8 +2223,7 @@ void LocationsBuilderMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); HInstruction* cond = deoptimize->InputAt(0); - DCHECK(cond->IsCondition()); - if (cond->AsCondition()->NeedsMaterialization()) { + if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { locations->SetInAt(0, Location::RequiresRegister()); } } @@ -3462,6 +3533,38 @@ void InstructionCodeGeneratorMIPS64::VisitGreaterThanOrEqual(HGreaterThanOrEqual VisitCondition(comp); } +void LocationsBuilderMIPS64::VisitBelow(HBelow* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitBelow(HBelow* comp) { + VisitCondition(comp); +} + +void LocationsBuilderMIPS64::VisitBelowOrEqual(HBelowOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitBelowOrEqual(HBelowOrEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderMIPS64::VisitAbove(HAbove* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitAbove(HAbove* comp) { + VisitCondition(comp); +} + +void LocationsBuilderMIPS64::VisitAboveOrEqual(HAboveOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitAboveOrEqual(HAboveOrEqual* comp) { + VisitCondition(comp); +} + void LocationsBuilderMIPS64::VisitFakeString(HFakeString* instruction) { DCHECK(codegen_->IsBaseline()); LocationSummary* locations = diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 5e8f9e7f30..7799437235 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -117,7 +117,7 @@ class FieldAccessCallingConventionMIPS64 : public FieldAccessCallingConvention { return Location::RegisterLocation(A0); } Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { - return Location::RegisterLocation(A0); + return Location::RegisterLocation(V0); } Location GetSetValueLocation( Primitive::Type type ATTRIBUTE_UNUSED, bool is_instance) const OVERRIDE { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index f8be21a06e..963eec2529 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -428,7 +428,7 @@ class ArraySetSlowPathX86 : public SlowPathCode { #undef __ #define __ down_cast<X86Assembler*>(GetAssembler())-> -inline Condition X86SignedCondition(IfCondition cond) { +inline Condition X86Condition(IfCondition cond) { switch (cond) { case kCondEQ: return kEqual; case kCondNE: return kNotEqual; @@ -436,19 +436,30 @@ inline Condition X86SignedCondition(IfCondition cond) { case kCondLE: return kLessEqual; case kCondGT: return kGreater; case kCondGE: return kGreaterEqual; + case kCondB: return kBelow; + case kCondBE: return kBelowEqual; + case kCondA: return kAbove; + case kCondAE: return kAboveEqual; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); } +// Maps signed condition to unsigned condition and FP condition to x86 name. inline Condition X86UnsignedOrFPCondition(IfCondition cond) { switch (cond) { case kCondEQ: return kEqual; case kCondNE: return kNotEqual; + // Signed to unsigned, and FP to x86 name. case kCondLT: return kBelow; case kCondLE: return kBelowEqual; case kCondGT: return kAbove; case kCondGE: return kAboveEqual; + // Unsigned remain unchanged. + case kCondB: return kBelow; + case kCondBE: return kBelowEqual; + case kCondA: return kAbove; + case kCondAE: return kAboveEqual; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); @@ -521,7 +532,8 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, move_resolver_(graph->GetArena(), this), isa_features_(isa_features), method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Use a fake return address register to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister)); } @@ -1040,8 +1052,7 @@ void LocationsBuilderX86::VisitExit(HExit* exit) { exit->SetLocations(nullptr); } -void InstructionCodeGeneratorX86::VisitExit(HExit* exit) { - UNUSED(exit); +void InstructionCodeGeneratorX86::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { } void InstructionCodeGeneratorX86::GenerateFPJumps(HCondition* cond, @@ -1067,7 +1078,7 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, Register left_low = left.AsRegisterPairLow<Register>(); IfCondition true_high_cond = if_cond; IfCondition false_high_cond = cond->GetOppositeCondition(); - Condition final_condition = X86UnsignedOrFPCondition(if_cond); + Condition final_condition = X86UnsignedOrFPCondition(if_cond); // unsigned on lower part // Set the conditions for the test, remembering that == needs to be // decided using the low words. @@ -1088,6 +1099,18 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, case kCondGE: true_high_cond = kCondGT; break; + case kCondB: + false_high_cond = kCondA; + break; + case kCondBE: + true_high_cond = kCondB; + break; + case kCondA: + false_high_cond = kCondB; + break; + case kCondAE: + true_high_cond = kCondA; + break; } if (right.IsConstant()) { @@ -1101,12 +1124,12 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, __ cmpl(left_high, Immediate(val_high)); } if (if_cond == kCondNE) { - __ j(X86SignedCondition(true_high_cond), true_label); + __ j(X86Condition(true_high_cond), true_label); } else if (if_cond == kCondEQ) { - __ j(X86SignedCondition(false_high_cond), false_label); + __ j(X86Condition(false_high_cond), false_label); } else { - __ j(X86SignedCondition(true_high_cond), true_label); - __ j(X86SignedCondition(false_high_cond), false_label); + __ j(X86Condition(true_high_cond), true_label); + __ j(X86Condition(false_high_cond), false_label); } // Must be equal high, so compare the lows. if (val_low == 0) { @@ -1120,12 +1143,12 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond, __ cmpl(left_high, right_high); if (if_cond == kCondNE) { - __ j(X86SignedCondition(true_high_cond), true_label); + __ j(X86Condition(true_high_cond), true_label); } else if (if_cond == kCondEQ) { - __ j(X86SignedCondition(false_high_cond), false_label); + __ j(X86Condition(false_high_cond), false_label); } else { - __ j(X86SignedCondition(true_high_cond), true_label); - __ j(X86SignedCondition(false_high_cond), false_label); + __ j(X86Condition(true_high_cond), true_label); + __ j(X86Condition(false_high_cond), false_label); } // Must be equal high, so compare the lows. __ cmpl(left_low, right_low); @@ -1214,7 +1237,7 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio } __ j(kNotEqual, true_target); } else { - __ j(X86SignedCondition(cond->AsCondition()->GetCondition()), true_target); + __ j(X86Condition(cond->AsCondition()->GetCondition()), true_target); } } else { // Condition has not been materialized, use its inputs as the @@ -1247,7 +1270,7 @@ void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instructio } else { __ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex())); } - __ j(X86SignedCondition(cond->AsCondition()->GetCondition()), true_target); + __ j(X86Condition(cond->AsCondition()->GetCondition()), true_target); } } if (false_target != nullptr) { @@ -1283,8 +1306,7 @@ void LocationsBuilderX86::VisitDeoptimize(HDeoptimize* deoptimize) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); HInstruction* cond = deoptimize->InputAt(0); - DCHECK(cond->IsCondition()); - if (cond->AsCondition()->NeedsMaterialization()) { + if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { locations->SetInAt(0, Location::Any()); } } @@ -1309,9 +1331,8 @@ void LocationsBuilderX86::VisitLoadLocal(HLoadLocal* local) { local->SetLocations(nullptr); } -void InstructionCodeGeneratorX86::VisitLoadLocal(HLoadLocal* load) { +void InstructionCodeGeneratorX86::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) { // Nothing to do, this is driven by the code generator. - UNUSED(load); } void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) { @@ -1338,8 +1359,7 @@ void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) { } } -void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) { - UNUSED(store); +void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) { } void LocationsBuilderX86::VisitCondition(HCondition* cond) { @@ -1405,7 +1425,7 @@ void InstructionCodeGeneratorX86::VisitCondition(HCondition* cond) { } else { __ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex())); } - __ setb(X86SignedCondition(cond->GetCondition()), reg); + __ setb(X86Condition(cond->GetCondition()), reg); return; } case Primitive::kPrimLong: @@ -1483,15 +1503,46 @@ void InstructionCodeGeneratorX86::VisitGreaterThanOrEqual(HGreaterThanOrEqual* c VisitCondition(comp); } +void LocationsBuilderX86::VisitBelow(HBelow* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitBelow(HBelow* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86::VisitBelowOrEqual(HBelowOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitBelowOrEqual(HBelowOrEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86::VisitAbove(HAbove* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitAbove(HAbove* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86::VisitAboveOrEqual(HAboveOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86::VisitAboveOrEqual(HAboveOrEqual* comp) { + VisitCondition(comp); +} + void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) { +void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86::VisitNullConstant(HNullConstant* constant) { @@ -1500,9 +1551,8 @@ void LocationsBuilderX86::VisitNullConstant(HNullConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86::VisitNullConstant(HNullConstant* constant) { +void InstructionCodeGeneratorX86::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) { @@ -1511,9 +1561,8 @@ void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant) { +void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86::VisitFloatConstant(HFloatConstant* constant) { @@ -1522,9 +1571,8 @@ void LocationsBuilderX86::VisitFloatConstant(HFloatConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86::VisitFloatConstant(HFloatConstant* constant) { +void InstructionCodeGeneratorX86::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86::VisitDoubleConstant(HDoubleConstant* constant) { @@ -1533,9 +1581,8 @@ void LocationsBuilderX86::VisitDoubleConstant(HDoubleConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86::VisitDoubleConstant(HDoubleConstant* constant) { +void InstructionCodeGeneratorX86::VisitDoubleConstant(HDoubleConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { @@ -1550,8 +1597,7 @@ void LocationsBuilderX86::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } -void InstructionCodeGeneratorX86::VisitReturnVoid(HReturnVoid* ret) { - UNUSED(ret); +void InstructionCodeGeneratorX86::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) { codegen_->GenerateFrameExit(); } @@ -3685,8 +3731,7 @@ void LocationsBuilderX86::VisitPhi(HPhi* instruction) { locations->SetOut(Location::Any()); } -void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) { - UNUSED(instruction); +void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unreachable"; } @@ -4684,13 +4729,11 @@ void LocationsBuilderX86::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } -void InstructionCodeGeneratorX86::VisitTemporary(HTemporary* temp) { +void InstructionCodeGeneratorX86::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) { // Nothing to do, this is driven by the code generator. - UNUSED(temp); } -void LocationsBuilderX86::VisitParallelMove(HParallelMove* instruction) { - UNUSED(instruction); +void LocationsBuilderX86::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unreachable"; } @@ -5613,15 +5656,13 @@ void InstructionCodeGeneratorX86::HandleBitwiseOperation(HBinaryOperation* instr } } -void LocationsBuilderX86::VisitBoundType(HBoundType* instruction) { +void LocationsBuilderX86::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. - UNUSED(instruction); LOG(FATAL) << "Unreachable"; } -void InstructionCodeGeneratorX86::VisitBoundType(HBoundType* instruction) { +void InstructionCodeGeneratorX86::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. - UNUSED(instruction); LOG(FATAL) << "Unreachable"; } @@ -5669,6 +5710,51 @@ void InstructionCodeGeneratorX86::VisitPackedSwitch(HPackedSwitch* switch_instr) } } +void LocationsBuilderX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + + // Constant area pointer. + locations->SetInAt(1, Location::RequiresRegister()); + + // And the temporary we need. + locations->AddTemp(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorX86::VisitX86PackedSwitch(HX86PackedSwitch* switch_instr) { + int32_t lower_bound = switch_instr->GetStartValue(); + int32_t num_entries = switch_instr->GetNumEntries(); + LocationSummary* locations = switch_instr->GetLocations(); + Register value_reg = locations->InAt(0).AsRegister<Register>(); + HBasicBlock* default_block = switch_instr->GetDefaultBlock(); + + // Optimizing has a jump area. + Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); + Register constant_area = locations->InAt(1).AsRegister<Register>(); + + // Remove the bias, if needed. + if (lower_bound != 0) { + __ leal(temp_reg, Address(value_reg, -lower_bound)); + value_reg = temp_reg; + } + + // Is the value in range? + DCHECK_GE(num_entries, 1); + __ cmpl(value_reg, Immediate(num_entries - 1)); + __ j(kAbove, codegen_->GetLabelOf(default_block)); + + // We are in the range of the table. + // Load (target-constant_area) from the jump table, indexing by the value. + __ movl(temp_reg, codegen_->LiteralCaseTable(switch_instr, constant_area, value_reg)); + + // Compute the actual target address by adding in constant_area. + __ addl(temp_reg, constant_area); + + // And jump. + __ jmp(temp_reg); +} + void LocationsBuilderX86::VisitX86ComputeBaseMethodAddress( HX86ComputeBaseMethodAddress* insn) { LocationSummary* locations = @@ -5752,28 +5838,18 @@ void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromCons } } -void CodeGeneratorX86::Finalize(CodeAllocator* allocator) { - // Generate the constant area if needed. - X86Assembler* assembler = GetAssembler(); - if (!assembler->IsConstantAreaEmpty()) { - // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8 - // byte values. - assembler->Align(4, 0); - constant_area_start_ = assembler->CodeSize(); - assembler->AddConstantArea(); - } - - // And finish up. - CodeGenerator::Finalize(allocator); -} - /** * Class to handle late fixup of offsets into constant area. */ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenerator> { public: - RIPFixup(const CodeGeneratorX86& codegen, int offset) - : codegen_(codegen), offset_into_constant_area_(offset) {} + RIPFixup(CodeGeneratorX86& codegen, size_t offset) + : codegen_(&codegen), offset_into_constant_area_(offset) {} + + protected: + void SetOffset(size_t offset) { offset_into_constant_area_ = offset; } + + CodeGeneratorX86* codegen_; private: void Process(const MemoryRegion& region, int pos) OVERRIDE { @@ -5781,19 +5857,77 @@ class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenera // last 4 bytes of the instruction. // The value to patch is the distance from the offset in the constant area // from the address computed by the HX86ComputeBaseMethodAddress instruction. - int32_t constant_offset = codegen_.ConstantAreaStart() + offset_into_constant_area_; - int32_t relative_position = constant_offset - codegen_.GetMethodAddressOffset();; + int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_; + int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset();; // Patch in the right value. region.StoreUnaligned<int32_t>(pos - 4, relative_position); } - const CodeGeneratorX86& codegen_; - // Location in constant area that the fixup refers to. - int offset_into_constant_area_; + int32_t offset_into_constant_area_; }; +/** + * Class to handle late fixup of offsets to a jump table that will be created in the + * constant area. + */ +class JumpTableRIPFixup : public RIPFixup { + public: + JumpTableRIPFixup(CodeGeneratorX86& codegen, HX86PackedSwitch* switch_instr) + : RIPFixup(codegen, static_cast<size_t>(-1)), switch_instr_(switch_instr) {} + + void CreateJumpTable() { + X86Assembler* assembler = codegen_->GetAssembler(); + + // Ensure that the reference to the jump table has the correct offset. + const int32_t offset_in_constant_table = assembler->ConstantAreaSize(); + SetOffset(offset_in_constant_table); + + // The label values in the jump table are computed relative to the + // instruction addressing the constant area. + const int32_t relative_offset = codegen_->GetMethodAddressOffset(); + + // Populate the jump table with the correct values for the jump table. + int32_t num_entries = switch_instr_->GetNumEntries(); + HBasicBlock* block = switch_instr_->GetBlock(); + const ArenaVector<HBasicBlock*>& successors = block->GetSuccessors(); + // The value that we want is the target offset - the position of the table. + for (int32_t i = 0; i < num_entries; i++) { + HBasicBlock* b = successors[i]; + Label* l = codegen_->GetLabelOf(b); + DCHECK(l->IsBound()); + int32_t offset_to_block = l->Position() - relative_offset; + assembler->AppendInt32(offset_to_block); + } + } + + private: + const HX86PackedSwitch* switch_instr_; +}; + +void CodeGeneratorX86::Finalize(CodeAllocator* allocator) { + // Generate the constant area if needed. + X86Assembler* assembler = GetAssembler(); + if (!assembler->IsConstantAreaEmpty() || !fixups_to_jump_tables_.empty()) { + // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8 + // byte values. + assembler->Align(4, 0); + constant_area_start_ = assembler->CodeSize(); + + // Populate any jump tables. + for (auto jump_table : fixups_to_jump_tables_) { + jump_table->CreateJumpTable(); + } + + // And now add the constant area to the generated code. + assembler->AddConstantArea(); + } + + // And finish up. + CodeGenerator::Finalize(allocator); +} + Address CodeGeneratorX86::LiteralDoubleAddress(double v, Register reg) { AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddDouble(v)); return Address(reg, kDummy32BitOffset, fixup); @@ -5814,98 +5948,18 @@ Address CodeGeneratorX86::LiteralInt64Address(int64_t v, Register reg) { return Address(reg, kDummy32BitOffset, fixup); } -/** - * Finds instructions that need the constant area base as an input. - */ -class ConstantHandlerVisitor : public HGraphVisitor { - public: - explicit ConstantHandlerVisitor(HGraph* graph) : HGraphVisitor(graph), base_(nullptr) {} - - private: - void VisitAdd(HAdd* add) OVERRIDE { - BinaryFP(add); - } - - void VisitSub(HSub* sub) OVERRIDE { - BinaryFP(sub); - } - - void VisitMul(HMul* mul) OVERRIDE { - BinaryFP(mul); - } - - void VisitDiv(HDiv* div) OVERRIDE { - BinaryFP(div); - } +Address CodeGeneratorX86::LiteralCaseTable(HX86PackedSwitch* switch_instr, + Register reg, + Register value) { + // Create a fixup to be used to create and address the jump table. + JumpTableRIPFixup* table_fixup = + new (GetGraph()->GetArena()) JumpTableRIPFixup(*this, switch_instr); - void VisitReturn(HReturn* ret) OVERRIDE { - HConstant* value = ret->InputAt(0)->AsConstant(); - if ((value != nullptr && Primitive::IsFloatingPointType(value->GetType()))) { - ReplaceInput(ret, value, 0, true); - } - } - - void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE { - HandleInvoke(invoke); - } - - void VisitInvokeVirtual(HInvokeVirtual* invoke) OVERRIDE { - HandleInvoke(invoke); - } - - void VisitInvokeInterface(HInvokeInterface* invoke) OVERRIDE { - HandleInvoke(invoke); - } - - void BinaryFP(HBinaryOperation* bin) { - HConstant* rhs = bin->InputAt(1)->AsConstant(); - if (rhs != nullptr && Primitive::IsFloatingPointType(bin->GetResultType())) { - ReplaceInput(bin, rhs, 1, false); - } - } - - void InitializeConstantAreaPointer(HInstruction* user) { - // Ensure we only initialize the pointer once. - if (base_ != nullptr) { - return; - } - - HGraph* graph = GetGraph(); - HBasicBlock* entry = graph->GetEntryBlock(); - base_ = new (graph->GetArena()) HX86ComputeBaseMethodAddress(); - HInstruction* insert_pos = (user->GetBlock() == entry) ? user : entry->GetLastInstruction(); - entry->InsertInstructionBefore(base_, insert_pos); - DCHECK(base_ != nullptr); - } - - void ReplaceInput(HInstruction* insn, HConstant* value, int input_index, bool materialize) { - InitializeConstantAreaPointer(insn); - HGraph* graph = GetGraph(); - HBasicBlock* block = insn->GetBlock(); - HX86LoadFromConstantTable* load_constant = - new (graph->GetArena()) HX86LoadFromConstantTable(base_, value, materialize); - block->InsertInstructionBefore(load_constant, insn); - insn->ReplaceInput(load_constant, input_index); - } - - void HandleInvoke(HInvoke* invoke) { - // Ensure that we can load FP arguments from the constant area. - for (size_t i = 0, e = invoke->InputCount(); i < e; i++) { - HConstant* input = invoke->InputAt(i)->AsConstant(); - if (input != nullptr && Primitive::IsFloatingPointType(input->GetType())) { - ReplaceInput(invoke, input, i, true); - } - } - } - - // The generated HX86ComputeBaseMethodAddress in the entry block needed as an - // input to the HX86LoadFromConstantTable instructions. - HX86ComputeBaseMethodAddress* base_; -}; + // We have to populate the jump tables. + fixups_to_jump_tables_.push_back(table_fixup); -void ConstantAreaFixups::Run() { - ConstantHandlerVisitor visitor(graph_); - visitor.VisitInsertionOrder(); + // We want a scaled address, as we are extracting the correct offset from the table. + return Address(reg, value, TIMES_4, kDummy32BitOffset, table_fixup); } // TODO: target as memory. diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index ae2d84f945..fdfc5ab69b 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -245,6 +245,8 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorX86); }; +class JumpTableRIPFixup; + class CodeGeneratorX86 : public CodeGenerator { public: CodeGeneratorX86(HGraph* graph, @@ -385,6 +387,8 @@ class CodeGeneratorX86 : public CodeGenerator { Address LiteralInt32Address(int32_t v, Register reg); Address LiteralInt64Address(int64_t v, Register reg); + Address LiteralCaseTable(HX86PackedSwitch* switch_instr, Register reg, Register value); + void Finalize(CodeAllocator* allocator) OVERRIDE; private: @@ -405,6 +409,9 @@ class CodeGeneratorX86 : public CodeGenerator { // Used for fixups to the constant area. int32_t constant_area_start_; + // Fixups for jump tables that need to be patched after the constant table is generated. + ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_; + // If there is a HX86ComputeBaseMethodAddress instruction in the graph // (which shall be the sole instruction of this kind), subtracting this offset // from the value contained in the out register of this HX86ComputeBaseMethodAddress diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 21c3fb89c0..ed2e4ca87c 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -36,9 +36,6 @@ namespace art { namespace x86_64 { -// Some x86_64 instructions require a register to be available as temp. -static constexpr Register TMP = R11; - static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kMethodRegisterArgument = RDI; @@ -452,11 +449,16 @@ inline Condition X86_64IntegerCondition(IfCondition cond) { case kCondLE: return kLessEqual; case kCondGT: return kGreater; case kCondGE: return kGreaterEqual; + case kCondB: return kBelow; + case kCondBE: return kBelowEqual; + case kCondA: return kAbove; + case kCondAE: return kAboveEqual; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); } +// Maps FP condition to x86_64 name. inline Condition X86_64FPCondition(IfCondition cond) { switch (cond) { case kCondEQ: return kEqual; @@ -465,6 +467,7 @@ inline Condition X86_64FPCondition(IfCondition cond) { case kCondLE: return kBelowEqual; case kCondGT: return kAbove; case kCondGE: return kAboveEqual; + default: break; // should not happen }; LOG(FATAL) << "Unreachable"; UNREACHABLE(); @@ -673,7 +676,8 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, constant_area_start_(0), method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - pc_rel_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + pc_rel_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister)); } @@ -1046,8 +1050,7 @@ void LocationsBuilderX86_64::VisitExit(HExit* exit) { exit->SetLocations(nullptr); } -void InstructionCodeGeneratorX86_64::VisitExit(HExit* exit) { - UNUSED(exit); +void InstructionCodeGeneratorX86_64::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { } void InstructionCodeGeneratorX86_64::GenerateFPJumps(HCondition* cond, @@ -1249,8 +1252,7 @@ void LocationsBuilderX86_64::VisitDeoptimize(HDeoptimize* deoptimize) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); HInstruction* cond = deoptimize->InputAt(0); - DCHECK(cond->IsCondition()); - if (cond->AsCondition()->NeedsMaterialization()) { + if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { locations->SetInAt(0, Location::Any()); } } @@ -1275,9 +1277,8 @@ void LocationsBuilderX86_64::VisitLoadLocal(HLoadLocal* local) { local->SetLocations(nullptr); } -void InstructionCodeGeneratorX86_64::VisitLoadLocal(HLoadLocal* load) { +void InstructionCodeGeneratorX86_64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) { // Nothing to do, this is driven by the code generator. - UNUSED(load); } void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) { @@ -1304,8 +1305,7 @@ void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) { } } -void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) { - UNUSED(store); +void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) { } void LocationsBuilderX86_64::VisitCondition(HCondition* cond) { @@ -1477,6 +1477,38 @@ void InstructionCodeGeneratorX86_64::VisitGreaterThanOrEqual(HGreaterThanOrEqual VisitCondition(comp); } +void LocationsBuilderX86_64::VisitBelow(HBelow* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitBelow(HBelow* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86_64::VisitBelowOrEqual(HBelowOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitBelowOrEqual(HBelowOrEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86_64::VisitAbove(HAbove* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitAbove(HAbove* comp) { + VisitCondition(comp); +} + +void LocationsBuilderX86_64::VisitAboveOrEqual(HAboveOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorX86_64::VisitAboveOrEqual(HAboveOrEqual* comp) { + VisitCondition(comp); +} + void LocationsBuilderX86_64::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); @@ -1578,9 +1610,8 @@ void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) { +void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86_64::VisitNullConstant(HNullConstant* constant) { @@ -1589,9 +1620,8 @@ void LocationsBuilderX86_64::VisitNullConstant(HNullConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86_64::VisitNullConstant(HNullConstant* constant) { +void InstructionCodeGeneratorX86_64::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) { @@ -1600,9 +1630,8 @@ void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) { +void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86_64::VisitFloatConstant(HFloatConstant* constant) { @@ -1611,9 +1640,8 @@ void LocationsBuilderX86_64::VisitFloatConstant(HFloatConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86_64::VisitFloatConstant(HFloatConstant* constant) { +void InstructionCodeGeneratorX86_64::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86_64::VisitDoubleConstant(HDoubleConstant* constant) { @@ -1622,9 +1650,9 @@ void LocationsBuilderX86_64::VisitDoubleConstant(HDoubleConstant* constant) { locations->SetOut(Location::ConstantLocation(constant)); } -void InstructionCodeGeneratorX86_64::VisitDoubleConstant(HDoubleConstant* constant) { +void InstructionCodeGeneratorX86_64::VisitDoubleConstant( + HDoubleConstant* constant ATTRIBUTE_UNUSED) { // Will be generated at use site. - UNUSED(constant); } void LocationsBuilderX86_64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { @@ -1639,8 +1667,7 @@ void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } -void InstructionCodeGeneratorX86_64::VisitReturnVoid(HReturnVoid* ret) { - UNUSED(ret); +void InstructionCodeGeneratorX86_64::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) { codegen_->GenerateFrameExit(); } @@ -3594,8 +3621,7 @@ void LocationsBuilderX86_64::VisitPhi(HPhi* instruction) { locations->SetOut(Location::Any()); } -void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction) { - UNUSED(instruction); +void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unimplemented"; } @@ -4415,13 +4441,11 @@ void LocationsBuilderX86_64::VisitTemporary(HTemporary* temp) { temp->SetLocations(nullptr); } -void InstructionCodeGeneratorX86_64::VisitTemporary(HTemporary* temp) { +void InstructionCodeGeneratorX86_64::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) { // Nothing to do, this is driven by the code generator. - UNUSED(temp); } -void LocationsBuilderX86_64::VisitParallelMove(HParallelMove* instruction) { - UNUSED(instruction); +void LocationsBuilderX86_64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { LOG(FATAL) << "Unimplemented"; } @@ -5296,15 +5320,13 @@ void InstructionCodeGeneratorX86_64::HandleBitwiseOperation(HBinaryOperation* in } } -void LocationsBuilderX86_64::VisitBoundType(HBoundType* instruction) { +void LocationsBuilderX86_64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. - UNUSED(instruction); LOG(FATAL) << "Unreachable"; } -void InstructionCodeGeneratorX86_64::VisitBoundType(HBoundType* instruction) { +void InstructionCodeGeneratorX86_64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. - UNUSED(instruction); LOG(FATAL) << "Unreachable"; } @@ -5325,31 +5347,43 @@ void LocationsBuilderX86_64::VisitPackedSwitch(HPackedSwitch* switch_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); } void InstructionCodeGeneratorX86_64::VisitPackedSwitch(HPackedSwitch* switch_instr) { int32_t lower_bound = switch_instr->GetStartValue(); int32_t num_entries = switch_instr->GetNumEntries(); LocationSummary* locations = switch_instr->GetLocations(); - CpuRegister value_reg = locations->InAt(0).AsRegister<CpuRegister>(); - HBasicBlock* default_block = switch_instr->GetDefaultBlock(); + CpuRegister value_reg_in = locations->InAt(0).AsRegister<CpuRegister>(); + CpuRegister temp_reg = locations->GetTemp(0).AsRegister<CpuRegister>(); + CpuRegister base_reg = locations->GetTemp(1).AsRegister<CpuRegister>(); - // Create a series of compare/jumps. - const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); - for (int i = 0; i < num_entries; i++) { - int32_t case_value = lower_bound + i; - if (case_value == 0) { - __ testl(value_reg, value_reg); - } else { - __ cmpl(value_reg, Immediate(case_value)); - } - __ j(kEqual, codegen_->GetLabelOf(successors[i])); + // Remove the bias, if needed. + Register value_reg_out = value_reg_in.AsRegister(); + if (lower_bound != 0) { + __ leal(temp_reg, Address(value_reg_in, -lower_bound)); + value_reg_out = temp_reg.AsRegister(); } + CpuRegister value_reg(value_reg_out); - // And the default for any other value. - if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { - __ jmp(codegen_->GetLabelOf(default_block)); - } + // Is the value in range? + HBasicBlock* default_block = switch_instr->GetDefaultBlock(); + __ cmpl(value_reg, Immediate(num_entries - 1)); + __ j(kAbove, codegen_->GetLabelOf(default_block)); + + // We are in the range of the table. + // Load the address of the jump table in the constant area. + __ leaq(base_reg, codegen_->LiteralCaseTable(switch_instr)); + + // Load the (signed) offset from the jump table. + __ movsxd(temp_reg, Address(base_reg, value_reg, TIMES_4, 0)); + + // Add the offset to the address of the table base. + __ addq(temp_reg, base_reg); + + // And jump. + __ jmp(temp_reg); } void CodeGeneratorX86_64::Load64BitValue(CpuRegister dest, int64_t value) { @@ -5375,15 +5409,85 @@ void CodeGeneratorX86_64::Store64BitValueToStack(Location dest, int64_t value) { } } +/** + * Class to handle late fixup of offsets into constant area. + */ +class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenerator> { + public: + RIPFixup(CodeGeneratorX86_64& codegen, size_t offset) + : codegen_(&codegen), offset_into_constant_area_(offset) {} + + protected: + void SetOffset(size_t offset) { offset_into_constant_area_ = offset; } + + CodeGeneratorX86_64* codegen_; + + private: + void Process(const MemoryRegion& region, int pos) OVERRIDE { + // Patch the correct offset for the instruction. We use the address of the + // 'next' instruction, which is 'pos' (patch the 4 bytes before). + int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_; + int32_t relative_position = constant_offset - pos; + + // Patch in the right value. + region.StoreUnaligned<int32_t>(pos - 4, relative_position); + } + + // Location in constant area that the fixup refers to. + size_t offset_into_constant_area_; +}; + +/** + t * Class to handle late fixup of offsets to a jump table that will be created in the + * constant area. + */ +class JumpTableRIPFixup : public RIPFixup { + public: + JumpTableRIPFixup(CodeGeneratorX86_64& codegen, HPackedSwitch* switch_instr) + : RIPFixup(codegen, -1), switch_instr_(switch_instr) {} + + void CreateJumpTable() { + X86_64Assembler* assembler = codegen_->GetAssembler(); + + // Ensure that the reference to the jump table has the correct offset. + const int32_t offset_in_constant_table = assembler->ConstantAreaSize(); + SetOffset(offset_in_constant_table); + + // Compute the offset from the start of the function to this jump table. + const int32_t current_table_offset = assembler->CodeSize() + offset_in_constant_table; + + // Populate the jump table with the correct values for the jump table. + int32_t num_entries = switch_instr_->GetNumEntries(); + HBasicBlock* block = switch_instr_->GetBlock(); + const ArenaVector<HBasicBlock*>& successors = block->GetSuccessors(); + // The value that we want is the target offset - the position of the table. + for (int32_t i = 0; i < num_entries; i++) { + HBasicBlock* b = successors[i]; + Label* l = codegen_->GetLabelOf(b); + DCHECK(l->IsBound()); + int32_t offset_to_block = l->Position() - current_table_offset; + assembler->AppendInt32(offset_to_block); + } + } + + private: + const HPackedSwitch* switch_instr_; +}; + void CodeGeneratorX86_64::Finalize(CodeAllocator* allocator) { // Generate the constant area if needed. X86_64Assembler* assembler = GetAssembler(); - if (!assembler->IsConstantAreaEmpty()) { - // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8 - // byte values. If used for vectors at a later time, this will need to be - // updated to 16 bytes with the appropriate offset. + if (!assembler->IsConstantAreaEmpty() || !fixups_to_jump_tables_.empty()) { + // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8 byte values. assembler->Align(4, 0); constant_area_start_ = assembler->CodeSize(); + + // Populate any jump tables. + for (auto jump_table : fixups_to_jump_tables_) { + jump_table->CreateJumpTable(); + } + + // And now add the constant area to the generated code. assembler->AddConstantArea(); } @@ -5391,31 +5495,6 @@ void CodeGeneratorX86_64::Finalize(CodeAllocator* allocator) { CodeGenerator::Finalize(allocator); } -/** - * Class to handle late fixup of offsets into constant area. - */ -class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocCodeGenerator> { - public: - RIPFixup(const CodeGeneratorX86_64& codegen, int offset) - : codegen_(codegen), offset_into_constant_area_(offset) {} - - private: - void Process(const MemoryRegion& region, int pos) OVERRIDE { - // Patch the correct offset for the instruction. We use the address of the - // 'next' instruction, which is 'pos' (patch the 4 bytes before). - int constant_offset = codegen_.ConstantAreaStart() + offset_into_constant_area_; - int relative_position = constant_offset - pos; - - // Patch in the right value. - region.StoreUnaligned<int32_t>(pos - 4, relative_position); - } - - const CodeGeneratorX86_64& codegen_; - - // Location in constant area that the fixup refers to. - int offset_into_constant_area_; -}; - Address CodeGeneratorX86_64::LiteralDoubleAddress(double v) { AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddDouble(v)); return Address::RIP(fixup); @@ -5456,6 +5535,16 @@ void CodeGeneratorX86_64::MoveFromReturnRegister(Location trg, Primitive::Type t GetMoveResolver()->EmitNativeCode(¶llel_move); } +Address CodeGeneratorX86_64::LiteralCaseTable(HPackedSwitch* switch_instr) { + // Create a fixup to be used to create and address the jump table. + JumpTableRIPFixup* table_fixup = + new (GetGraph()->GetArena()) JumpTableRIPFixup(*this, switch_instr); + + // We have to populate the jump tables. + fixups_to_jump_tables_.push_back(table_fixup); + return Address::RIP(table_fixup); +} + #undef __ } // namespace x86_64 diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index ecc8630e6b..dc86a48ce7 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -30,6 +30,9 @@ namespace x86_64 { // Use a local definition to prevent copying mistakes. static constexpr size_t kX86_64WordSize = kX86_64PointerSize; +// Some x86_64 instructions require a register to be available as temp. +static constexpr Register TMP = R11; + static constexpr Register kParameterCoreRegisters[] = { RSI, RDX, RCX, R8, R9 }; static constexpr FloatRegister kParameterFloatRegisters[] = { XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7 }; @@ -231,6 +234,9 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor { DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorX86_64); }; +// Class for fixups to jump tables. +class JumpTableRIPFixup; + class CodeGeneratorX86_64 : public CodeGenerator { public: CodeGeneratorX86_64(HGraph* graph, @@ -351,6 +357,7 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Load a 64 bit value into a register in the most efficient manner. void Load64BitValue(CpuRegister dest, int64_t value); + Address LiteralCaseTable(HPackedSwitch* switch_instr); // Store a 64 bit value into a DoubleStackSlot in the most efficient manner. void Store64BitValueToStack(Location dest, int64_t value); @@ -388,6 +395,9 @@ class CodeGeneratorX86_64 : public CodeGenerator { // We will fix this up in the linker later to have the right value. static constexpr int32_t kDummy32BitOffset = 256; + // Fixups for jump tables need to be handled specially. + ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86_64); }; diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 22f227c56a..fe5af2fc5e 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -761,4 +761,130 @@ TEST(CodegenTest, ReturnDivInt2Addr) { TestCode(data, true, 2); } +// Helper method. +static void TestComparison(IfCondition condition, int64_t i, int64_t j, Primitive::Type type) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = CreateGraph(&allocator); + + HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(entry_block); + graph->SetEntryBlock(entry_block); + entry_block->AddInstruction(new (&allocator) HGoto()); + + HBasicBlock* block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(block); + + HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(exit_block); + graph->SetExitBlock(exit_block); + exit_block->AddInstruction(new (&allocator) HExit()); + + entry_block->AddSuccessor(block); + block->AddSuccessor(exit_block); + + HInstruction* op1; + HInstruction* op2; + if (type == Primitive::kPrimInt) { + op1 = graph->GetIntConstant(i); + op2 = graph->GetIntConstant(j); + } else { + DCHECK_EQ(type, Primitive::kPrimLong); + op1 = graph->GetLongConstant(i); + op2 = graph->GetLongConstant(j); + } + + HInstruction* comparison = nullptr; + bool expected_result = false; + const uint64_t x = i; + const uint64_t y = j; + switch (condition) { + case kCondEQ: + comparison = new (&allocator) HEqual(op1, op2); + expected_result = (i == j); + break; + case kCondNE: + comparison = new (&allocator) HNotEqual(op1, op2); + expected_result = (i != j); + break; + case kCondLT: + comparison = new (&allocator) HLessThan(op1, op2); + expected_result = (i < j); + break; + case kCondLE: + comparison = new (&allocator) HLessThanOrEqual(op1, op2); + expected_result = (i <= j); + break; + case kCondGT: + comparison = new (&allocator) HGreaterThan(op1, op2); + expected_result = (i > j); + break; + case kCondGE: + comparison = new (&allocator) HGreaterThanOrEqual(op1, op2); + expected_result = (i >= j); + break; + case kCondB: + comparison = new (&allocator) HBelow(op1, op2); + expected_result = (x < y); + break; + case kCondBE: + comparison = new (&allocator) HBelowOrEqual(op1, op2); + expected_result = (x <= y); + break; + case kCondA: + comparison = new (&allocator) HAbove(op1, op2); + expected_result = (x > y); + break; + case kCondAE: + comparison = new (&allocator) HAboveOrEqual(op1, op2); + expected_result = (x >= y); + break; + } + block->AddInstruction(comparison); + block->AddInstruction(new (&allocator) HReturn(comparison)); + + auto hook_before_codegen = [](HGraph*) { + }; + RunCodeOptimized(graph, hook_before_codegen, true, expected_result); +} + +TEST(CodegenTest, ComparisonsInt) { + for (int64_t i = -1; i <= 1; i++) { + for (int64_t j = -1; j <= 1; j++) { + TestComparison(kCondEQ, i, j, Primitive::kPrimInt); + TestComparison(kCondNE, i, j, Primitive::kPrimInt); + TestComparison(kCondLT, i, j, Primitive::kPrimInt); + TestComparison(kCondLE, i, j, Primitive::kPrimInt); + TestComparison(kCondGT, i, j, Primitive::kPrimInt); + TestComparison(kCondGE, i, j, Primitive::kPrimInt); + TestComparison(kCondB, i, j, Primitive::kPrimInt); + TestComparison(kCondBE, i, j, Primitive::kPrimInt); + TestComparison(kCondA, i, j, Primitive::kPrimInt); + TestComparison(kCondAE, i, j, Primitive::kPrimInt); + } + } +} + +TEST(CodegenTest, ComparisonsLong) { + // TODO: make MIPS work for long + if (kRuntimeISA == kMips || kRuntimeISA == kMips64) { + return; + } + + for (int64_t i = -1; i <= 1; i++) { + for (int64_t j = -1; j <= 1; j++) { + TestComparison(kCondEQ, i, j, Primitive::kPrimLong); + TestComparison(kCondNE, i, j, Primitive::kPrimLong); + TestComparison(kCondLT, i, j, Primitive::kPrimLong); + TestComparison(kCondLE, i, j, Primitive::kPrimLong); + TestComparison(kCondGT, i, j, Primitive::kPrimLong); + TestComparison(kCondGE, i, j, Primitive::kPrimLong); + TestComparison(kCondB, i, j, Primitive::kPrimLong); + TestComparison(kCondBE, i, j, Primitive::kPrimLong); + TestComparison(kCondA, i, j, Primitive::kPrimLong); + TestComparison(kCondAE, i, j, Primitive::kPrimLong); + } + } +} + } // namespace art diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index f54547534f..4abe5e953c 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -206,7 +206,9 @@ static bool CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* inst if (instr->IsAdd() || instr->IsSub() || instr->IsCondition() || instr->IsCompare() || instr->IsBoundsCheck()) { // Uses aliases of ADD/SUB instructions. - return vixl::Assembler::IsImmAddSub(value); + // If `value` does not fit but `-value` does, VIXL will automatically use + // the 'opposite' instruction. + return vixl::Assembler::IsImmAddSub(value) || vixl::Assembler::IsImmAddSub(-value); } else if (instr->IsAnd() || instr->IsOr() || instr->IsXor()) { // Uses logical operations. return vixl::Assembler::IsImmLogical(value, vixl::kXRegSize); diff --git a/compiler/optimizing/constant_area_fixups_x86.cc b/compiler/optimizing/constant_area_fixups_x86.cc new file mode 100644 index 0000000000..c3470002c5 --- /dev/null +++ b/compiler/optimizing/constant_area_fixups_x86.cc @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "constant_area_fixups_x86.h" + +namespace art { +namespace x86 { + +/** + * Finds instructions that need the constant area base as an input. + */ +class ConstantHandlerVisitor : public HGraphVisitor { + public: + explicit ConstantHandlerVisitor(HGraph* graph) : HGraphVisitor(graph), base_(nullptr) {} + + private: + void VisitAdd(HAdd* add) OVERRIDE { + BinaryFP(add); + } + + void VisitSub(HSub* sub) OVERRIDE { + BinaryFP(sub); + } + + void VisitMul(HMul* mul) OVERRIDE { + BinaryFP(mul); + } + + void VisitDiv(HDiv* div) OVERRIDE { + BinaryFP(div); + } + + void VisitReturn(HReturn* ret) OVERRIDE { + HConstant* value = ret->InputAt(0)->AsConstant(); + if ((value != nullptr && Primitive::IsFloatingPointType(value->GetType()))) { + ReplaceInput(ret, value, 0, true); + } + } + + void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE { + HandleInvoke(invoke); + } + + void VisitInvokeVirtual(HInvokeVirtual* invoke) OVERRIDE { + HandleInvoke(invoke); + } + + void VisitInvokeInterface(HInvokeInterface* invoke) OVERRIDE { + HandleInvoke(invoke); + } + + void BinaryFP(HBinaryOperation* bin) { + HConstant* rhs = bin->InputAt(1)->AsConstant(); + if (rhs != nullptr && Primitive::IsFloatingPointType(bin->GetResultType())) { + ReplaceInput(bin, rhs, 1, false); + } + } + + void VisitPackedSwitch(HPackedSwitch* switch_insn) OVERRIDE { + // We need to replace the HPackedSwitch with a HX86PackedSwitch in order to + // address the constant area. + InitializeConstantAreaPointer(switch_insn); + HGraph* graph = GetGraph(); + HBasicBlock* block = switch_insn->GetBlock(); + HX86PackedSwitch* x86_switch = new (graph->GetArena()) HX86PackedSwitch( + switch_insn->GetStartValue(), + switch_insn->GetNumEntries(), + switch_insn->InputAt(0), + base_, + switch_insn->GetDexPc()); + block->ReplaceAndRemoveInstructionWith(switch_insn, x86_switch); + } + + void InitializeConstantAreaPointer(HInstruction* user) { + // Ensure we only initialize the pointer once. + if (base_ != nullptr) { + return; + } + + HGraph* graph = GetGraph(); + HBasicBlock* entry = graph->GetEntryBlock(); + base_ = new (graph->GetArena()) HX86ComputeBaseMethodAddress(); + HInstruction* insert_pos = (user->GetBlock() == entry) ? user : entry->GetLastInstruction(); + entry->InsertInstructionBefore(base_, insert_pos); + DCHECK(base_ != nullptr); + } + + void ReplaceInput(HInstruction* insn, HConstant* value, int input_index, bool materialize) { + InitializeConstantAreaPointer(insn); + HGraph* graph = GetGraph(); + HBasicBlock* block = insn->GetBlock(); + HX86LoadFromConstantTable* load_constant = + new (graph->GetArena()) HX86LoadFromConstantTable(base_, value, materialize); + block->InsertInstructionBefore(load_constant, insn); + insn->ReplaceInput(load_constant, input_index); + } + + void HandleInvoke(HInvoke* invoke) { + // Ensure that we can load FP arguments from the constant area. + for (size_t i = 0, e = invoke->InputCount(); i < e; i++) { + HConstant* input = invoke->InputAt(i)->AsConstant(); + if (input != nullptr && Primitive::IsFloatingPointType(input->GetType())) { + ReplaceInput(invoke, input, i, true); + } + } + } + + // The generated HX86ComputeBaseMethodAddress in the entry block needed as an + // input to the HX86LoadFromConstantTable instructions. + HX86ComputeBaseMethodAddress* base_; +}; + +void ConstantAreaFixups::Run() { + ConstantHandlerVisitor visitor(graph_); + visitor.VisitInsertionOrder(); +} + +} // namespace x86 +} // namespace art diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 694f7687ba..b2e222f1a9 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -51,7 +51,7 @@ static void TestCode(const uint16_t* data, X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), CompilerOptions()); HConstantFolding(graph).Run(); - SSAChecker ssa_checker_cf(&allocator, graph); + SSAChecker ssa_checker_cf(graph); ssa_checker_cf.Run(); ASSERT_TRUE(ssa_checker_cf.IsValid()); @@ -63,7 +63,7 @@ static void TestCode(const uint16_t* data, check_after_cf(graph); HDeadCodeElimination(graph).Run(); - SSAChecker ssa_checker_dce(&allocator, graph); + SSAChecker ssa_checker_dce(graph); ssa_checker_dce.Run(); ASSERT_TRUE(ssa_checker_dce.IsValid()); diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index ee3a61aa0c..cf0a4acd4a 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -45,7 +45,7 @@ static void TestCode(const uint16_t* data, X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), CompilerOptions()); HDeadCodeElimination(graph).Run(); - SSAChecker ssa_checker(&allocator, graph); + SSAChecker ssa_checker(graph); ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 89da1b1fbe..3de96b5d84 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -16,10 +16,12 @@ #include "graph_checker.h" +#include <algorithm> #include <map> #include <string> #include <sstream> +#include "base/arena_containers.h" #include "base/bit_vector-inl.h" #include "base/stringprintf.h" @@ -29,19 +31,21 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { current_block_ = block; // Check consistency with respect to predecessors of `block`. - std::map<HBasicBlock*, size_t> predecessors_count; + ArenaSafeMap<HBasicBlock*, size_t> predecessors_count( + std::less<HBasicBlock*>(), GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker)); for (HBasicBlock* p : block->GetPredecessors()) { - ++predecessors_count[p]; + auto it = predecessors_count.find(p); + if (it != predecessors_count.end()) { + ++it->second; + } else { + predecessors_count.Put(p, 1u); + } } for (auto& pc : predecessors_count) { HBasicBlock* p = pc.first; size_t p_count_in_block_predecessors = pc.second; - size_t block_count_in_p_successors = 0; - for (HBasicBlock* p_successor : p->GetSuccessors()) { - if (p_successor == block) { - ++block_count_in_p_successors; - } - } + size_t block_count_in_p_successors = + std::count(p->GetSuccessors().begin(), p->GetSuccessors().end(), block); if (p_count_in_block_predecessors != block_count_in_p_successors) { AddError(StringPrintf( "Block %d lists %zu occurrences of block %d in its predecessors, whereas " @@ -52,19 +56,21 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { } // Check consistency with respect to successors of `block`. - std::map<HBasicBlock*, size_t> successors_count; + ArenaSafeMap<HBasicBlock*, size_t> successors_count( + std::less<HBasicBlock*>(), GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker)); for (HBasicBlock* s : block->GetSuccessors()) { - ++successors_count[s]; + auto it = successors_count.find(s); + if (it != successors_count.end()) { + ++it->second; + } else { + successors_count.Put(s, 1u); + } } for (auto& sc : successors_count) { HBasicBlock* s = sc.first; size_t s_count_in_block_successors = sc.second; - size_t block_count_in_s_predecessors = 0; - for (HBasicBlock* s_predecessor : s->GetPredecessors()) { - if (s_predecessor == block) { - ++block_count_in_s_predecessors; - } - } + size_t block_count_in_s_predecessors = + std::count(s->GetPredecessors().begin(), s->GetPredecessors().end(), block); if (s_count_in_block_successors != block_count_in_s_predecessors) { AddError(StringPrintf( "Block %d lists %zu occurrences of block %d in its successors, whereas " diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 7ddffc136a..abf3659d91 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -26,12 +26,11 @@ namespace art { // A control-flow graph visitor performing various checks. class GraphChecker : public HGraphDelegateVisitor { public: - GraphChecker(ArenaAllocator* allocator, HGraph* graph, - const char* dump_prefix = "art::GraphChecker: ") + explicit GraphChecker(HGraph* graph, const char* dump_prefix = "art::GraphChecker: ") : HGraphDelegateVisitor(graph), - allocator_(allocator), + errors_(graph->GetArena()->Adapter(kArenaAllocGraphChecker)), dump_prefix_(dump_prefix), - seen_ids_(allocator, graph->GetCurrentInstructionId(), false) {} + seen_ids_(graph->GetArena(), graph->GetCurrentInstructionId(), false) {} // Check the whole graph (in insertion order). virtual void Run() { VisitInsertionOrder(); } @@ -65,7 +64,7 @@ class GraphChecker : public HGraphDelegateVisitor { } // Get the list of detected errors. - const std::vector<std::string>& GetErrors() const { + const ArenaVector<std::string>& GetErrors() const { return errors_; } @@ -82,11 +81,10 @@ class GraphChecker : public HGraphDelegateVisitor { errors_.push_back(error); } - ArenaAllocator* const allocator_; // The block currently visited. HBasicBlock* current_block_ = nullptr; // Errors encountered while checking the graph. - std::vector<std::string> errors_; + ArenaVector<std::string> errors_; private: // String displayed before dumped errors. @@ -102,9 +100,8 @@ class SSAChecker : public GraphChecker { public: typedef GraphChecker super_type; - // TODO: There's no need to pass a separate allocator as we could get it from the graph. - SSAChecker(ArenaAllocator* allocator, HGraph* graph) - : GraphChecker(allocator, graph, "art::SSAChecker: ") {} + explicit SSAChecker(HGraph* graph) + : GraphChecker(graph, "art::SSAChecker: ") {} // Check the whole graph (in reverse post-order). void Run() OVERRIDE { diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc index 0f6677519e..fee56c7f9e 100644 --- a/compiler/optimizing/graph_checker_test.cc +++ b/compiler/optimizing/graph_checker_test.cc @@ -50,7 +50,7 @@ static void TestCode(const uint16_t* data) { HGraph* graph = CreateCFG(&allocator, data); ASSERT_NE(graph, nullptr); - GraphChecker graph_checker(&allocator, graph); + GraphChecker graph_checker(graph); graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); } @@ -64,7 +64,7 @@ static void TestCodeSSA(const uint16_t* data) { graph->BuildDominatorTree(); graph->TransformToSsa(); - SSAChecker ssa_checker(&allocator, graph); + SSAChecker ssa_checker(graph); ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); } @@ -112,7 +112,7 @@ TEST(GraphChecker, InconsistentPredecessorsAndSuccessors) { ArenaAllocator allocator(&pool); HGraph* graph = CreateSimpleCFG(&allocator); - GraphChecker graph_checker(&allocator, graph); + GraphChecker graph_checker(graph); graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); @@ -130,7 +130,7 @@ TEST(GraphChecker, BlockEndingWithNonBranchInstruction) { ArenaAllocator allocator(&pool); HGraph* graph = CreateSimpleCFG(&allocator); - GraphChecker graph_checker(&allocator, graph); + GraphChecker graph_checker(graph); graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index cf0f3493fd..8968a44da8 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -650,8 +650,7 @@ bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr, case kCondLE: return lower_value <= upper_value; case kCondGT: return lower_value > upper_value; case kCondGE: return lower_value >= upper_value; - case kCondEQ: - case kCondNE: LOG(FATAL) << "CONDITION UNREACHABLE"; + default: LOG(FATAL) << "CONDITION UNREACHABLE"; } } return false; // not certain, may be untaken @@ -680,8 +679,8 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr, (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value - 1)); case kCondGE: return (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value)); - case kCondEQ: - case kCondNE: LOG(FATAL) << "CONDITION UNREACHABLE"; + default: + LOG(FATAL) << "CONDITION UNREACHABLE"; } return false; // not certain, may be infinite } diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 19af2fb5db..f16da2a3f7 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -20,7 +20,6 @@ #include "builder.h" #include "gtest/gtest.h" #include "induction_var_analysis.h" -#include "induction_var_range.h" #include "nodes.h" #include "optimizing_unit_test.h" @@ -523,36 +522,6 @@ TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) { EXPECT_STREQ("periodic(( - (1)), (0))", GetInductionInfo(neg, 0).c_str()); } -TEST_F(InductionVarAnalysisTest, FindRange) { - // Setup: - // for (int i = 0; i < 100; i++) { - // k = i << 1; - // k = k + 1; - // a[k] = 0; - // } - BuildLoopNest(1); - HInstruction *shl = InsertInstruction( - new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0); - InsertLocalStore(induc_, shl, 0); - HInstruction *add = InsertInstruction( - new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0); - InsertLocalStore(induc_, add, 0); - HInstruction* store = InsertArrayStore(induc_, 0); - PerformInductionVarAnalysis(); - - EXPECT_STREQ("((2) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str()); - - InductionVarRange range(iva_); - InductionVarRange::Value v_min = range.GetMinInduction(store, store->InputAt(1)); - InductionVarRange::Value v_max = range.GetMaxInduction(store, store->InputAt(1)); - ASSERT_TRUE(v_min.is_known); - EXPECT_EQ(0, v_min.a_constant); - EXPECT_EQ(1, v_min.b_constant); - ASSERT_TRUE(v_max.is_known); - EXPECT_EQ(0, v_max.a_constant); - EXPECT_EQ(199, v_max.b_constant); -} - TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) { // Setup: // k = 0; diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index db12819060..f4842f9696 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -75,6 +75,13 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { return v; } +static HInstruction* Insert(HBasicBlock* preheader, HInstruction* instruction) { + DCHECK(preheader != nullptr); + DCHECK(instruction != nullptr); + preheader->InsertInstructionBefore(instruction, preheader->GetLastInstruction()); + return instruction; +} + // // Public class methods. // @@ -94,6 +101,21 @@ InductionVarRange::Value InductionVarRange::GetMaxInduction(HInstruction* contex return SimplifyMax(GetInduction(context, instruction, /* is_min */ false)); } +bool InductionVarRange::CanGenerateCode(HInstruction* context, + HInstruction* instruction, + /*out*/bool* top_test) { + return GenerateCode(context, instruction, nullptr, nullptr, nullptr, nullptr, top_test); +} + +bool InductionVarRange::GenerateCode(HInstruction* context, + HInstruction* instruction, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** lower, + /*out*/HInstruction** upper) { + return GenerateCode(context, instruction, graph, block, lower, upper, nullptr); +} + // // Private class methods. // @@ -162,15 +184,15 @@ InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::Induct case HInductionVarAnalysis::kFetch: return GetFetch(info->fetch, trip, in_body, is_min); case HInductionVarAnalysis::kTripCountInLoop: - if (!in_body) { - return is_min ? Value(0) - : GetVal(info->op_b, trip, in_body, is_min); // one extra! + if (!in_body && !is_min) { // one extra! + return GetVal(info->op_b, trip, in_body, is_min); } FALLTHROUGH_INTENDED; case HInductionVarAnalysis::kTripCountInBody: - if (in_body) { - return is_min ? Value(0) - : SubValue(GetVal(info->op_b, trip, in_body, is_min), Value(1)); + if (is_min) { + return Value(0); + } else if (in_body) { + return SubValue(GetVal(info->op_b, trip, in_body, is_min), Value(1)); } break; default: @@ -256,9 +278,11 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct bool InductionVarRange::GetConstant(HInductionVarAnalysis::InductionInfo* info, int32_t *value) { Value v_min = GetVal(info, nullptr, false, /* is_min */ true); Value v_max = GetVal(info, nullptr, false, /* is_min */ false); - if (v_min.a_constant == 0 && v_max.a_constant == 0 && v_min.b_constant == v_max.b_constant) { - *value = v_min.b_constant; - return true; + if (v_min.is_known && v_max.is_known) { + if (v_min.a_constant == 0 && v_max.a_constant == 0 && v_min.b_constant == v_max.b_constant) { + *value = v_min.b_constant; + return true; + } } return false; } @@ -326,4 +350,129 @@ InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is return Value(); } +bool InductionVarRange::GenerateCode(HInstruction* context, + HInstruction* instruction, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** lower, + /*out*/HInstruction** upper, + /*out*/bool* top_test) { + HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop + if (loop != nullptr) { + HBasicBlock* header = loop->GetHeader(); + bool in_body = context->GetBlock() != header; + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction); + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + if (info != nullptr && trip != nullptr) { + if (top_test != nullptr) { + *top_test = trip->operation != HInductionVarAnalysis::kTripCountInLoop; + } + return + // Success on lower if invariant (not set), or code can be generated. + ((info->induction_class == HInductionVarAnalysis::kInvariant) || + GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) && + // And success on upper. + GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false); + } + } + return false; +} + +bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, + HInductionVarAnalysis::InductionInfo* trip, + HGraph* graph, // when set, code is generated + HBasicBlock* block, + /*out*/HInstruction** result, + bool in_body, + bool is_min) { + if (info != nullptr) { + Primitive::Type type = Primitive::kPrimInt; + HInstruction* opa = nullptr; + HInstruction* opb = nullptr; + int32_t value = 0; + switch (info->induction_class) { + case HInductionVarAnalysis::kInvariant: + // Invariants. + switch (info->operation) { + case HInductionVarAnalysis::kAdd: + if (GenerateCode(info->op_a, trip, graph, block, &opa, in_body, is_min) && + GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) { + if (graph != nullptr) { + *result = Insert(block, new (graph->GetArena()) HAdd(type, opa, opb)); + } + return true; + } + break; + case HInductionVarAnalysis::kSub: // second reversed! + if (GenerateCode(info->op_a, trip, graph, block, &opa, in_body, is_min) && + GenerateCode(info->op_b, trip, graph, block, &opb, in_body, !is_min)) { + if (graph != nullptr) { + *result = Insert(block, new (graph->GetArena()) HSub(type, opa, opb)); + } + return true; + } + break; + case HInductionVarAnalysis::kNeg: // reversed! + if (GenerateCode(info->op_b, trip, graph, block, &opb, in_body, !is_min)) { + if (graph != nullptr) { + *result = Insert(block, new (graph->GetArena()) HNeg(type, opb)); + } + return true; + } + break; + case HInductionVarAnalysis::kFetch: + if (graph != nullptr) { + *result = info->fetch; // already in HIR + } + return true; + case HInductionVarAnalysis::kTripCountInLoop: + if (!in_body && !is_min) { // one extra! + return GenerateCode(info->op_b, trip, graph, block, result, in_body, is_min); + } + FALLTHROUGH_INTENDED; + case HInductionVarAnalysis::kTripCountInBody: + if (is_min) { + if (graph != nullptr) { + *result = graph->GetIntConstant(0); + } + return true; + } else if (in_body) { + if (GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) { + if (graph != nullptr) { + *result = Insert(block, + new (graph->GetArena()) + HSub(type, opb, graph->GetIntConstant(1))); + } + return true; + } + } + break; + default: + break; + } + break; + case HInductionVarAnalysis::kLinear: + // Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only + // to avoid arithmetic wrap-around situations that are hard to guard against. + if (GetConstant(info->op_a, &value)) { + if (value == 1 || value == -1) { + const bool is_min_a = value == 1 ? is_min : !is_min; + if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) && + GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) { + if (graph != nullptr) { + *result = Insert(block, new (graph->GetArena()) HAdd(type, opa, opb)); + } + return true; + } + } + } + break; + default: // TODO(ajcbik): add more cases + break; + } + } + return false; +} + } // namespace art diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index dbdd2eedac..7fa5a26dce 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -68,6 +68,33 @@ class InductionVarRange { */ Value GetMaxInduction(HInstruction* context, HInstruction* instruction); + /** + * Returns true if range analysis is able to generate code for the lower and upper bound + * expressions on the instruction in the given context. Output parameter top_test denotes + * whether a top test is needed to protect the trip-count expression evaluation. + */ + bool CanGenerateCode(HInstruction* context, HInstruction* instruction, /*out*/bool* top_test); + + /** + * Generates the actual code in the HIR for the lower and upper bound expressions on the + * instruction in the given context. Code for the lower and upper bound expression are + * generated in given block and graph and are returned in lower and upper, respectively. + * For a loop invariant, lower is not set. + * + * For example, given expression x+i with range [0, 5] for i, calling this method + * will generate the following sequence: + * + * block: + * lower: add x, 0 + * upper: add x, 5 + */ + bool GenerateCode(HInstruction* context, + HInstruction* instruction, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** lower, + /*out*/HInstruction** upper); + private: // // Private helper methods. @@ -102,6 +129,27 @@ class InductionVarRange { static Value DivValue(Value v1, Value v2); static Value MergeVal(Value v1, Value v2, bool is_min); + /** + * Generates code for lower/upper expression in the HIR. Returns true on success. + * With graph == nullptr, the method can be used to determine if code generation + * would be successful without generating actual code yet. + */ + bool GenerateCode(HInstruction* context, + HInstruction* instruction, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** lower, + /*out*/HInstruction** upper, + bool* top_test); + + static bool GenerateCode(HInductionVarAnalysis::InductionInfo* info, + HInductionVarAnalysis::InductionInfo* trip, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** result, + bool in_body, + bool is_min); + /** Results of prior induction variable analysis. */ HInductionVarAnalysis *induction_analysis_; diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index 4497a884d9..56f661ed46 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -49,12 +49,52 @@ class InductionVarRangeTest : public testing::Test { /** Constructs bare minimum graph. */ void BuildGraph() { graph_->SetNumberOfVRegs(1); - HBasicBlock* entry_block = new (&allocator_) HBasicBlock(graph_); - HBasicBlock* exit_block = new (&allocator_) HBasicBlock(graph_); - graph_->AddBlock(entry_block); - graph_->AddBlock(exit_block); - graph_->SetEntryBlock(entry_block); - graph_->SetExitBlock(exit_block); + entry_block_ = new (&allocator_) HBasicBlock(graph_); + exit_block_ = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(entry_block_); + graph_->AddBlock(exit_block_); + graph_->SetEntryBlock(entry_block_); + graph_->SetExitBlock(exit_block_); + } + + /** Constructs loop with given upper bound. */ + void BuildLoop(HInstruction* upper) { + // Control flow. + loop_preheader_ = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(loop_preheader_); + HBasicBlock* loop_header = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(loop_header); + HBasicBlock* loop_body = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(loop_body); + entry_block_->AddSuccessor(loop_preheader_); + loop_preheader_->AddSuccessor(loop_header); + loop_header->AddSuccessor(loop_body); + loop_header->AddSuccessor(exit_block_); + loop_body->AddSuccessor(loop_header); + // Instructions. + HLocal* induc = new (&allocator_) HLocal(0); + entry_block_->AddInstruction(induc); + loop_preheader_->AddInstruction( + new (&allocator_) HStoreLocal(induc, graph_->GetIntConstant(0))); // i = 0 + loop_preheader_->AddInstruction(new (&allocator_) HGoto()); + HInstruction* load = new (&allocator_) HLoadLocal(induc, Primitive::kPrimInt); + loop_header->AddInstruction(load); + condition_ = new (&allocator_) HLessThan(load, upper); + loop_header->AddInstruction(condition_); + loop_header->AddInstruction(new (&allocator_) HIf(condition_)); // i < u + load = new (&allocator_) HLoadLocal(induc, Primitive::kPrimInt); + loop_body->AddInstruction(load); + increment_ = new (&allocator_) HAdd(Primitive::kPrimInt, load, graph_->GetIntConstant(1)); + loop_body->AddInstruction(increment_); + loop_body->AddInstruction(new (&allocator_) HStoreLocal(induc, increment_)); // i++ + loop_body->AddInstruction(new (&allocator_) HGoto()); + exit_block_->AddInstruction(new (&allocator_) HReturnVoid()); + } + + /** Performs induction variable analysis. */ + void PerformInductionVarAnalysis() { + ASSERT_TRUE(graph_->TryBuildingSsa()); + iva_->Run(); } /** Constructs an invariant. */ @@ -146,15 +186,20 @@ class InductionVarRangeTest : public testing::Test { ArenaPool pool_; ArenaAllocator allocator_; HGraph* graph_; + HBasicBlock* entry_block_; + HBasicBlock* exit_block_; + HBasicBlock* loop_preheader_; HInductionVarAnalysis* iva_; - // Two dummy instructions. + // Instructions. + HInstruction* condition_; + HInstruction* increment_; HReturnVoid x_; HReturnVoid y_; }; // -// The actual InductionVarRange tests. +// Tests on static methods. // TEST_F(InductionVarRangeTest, GetMinMaxNull) { @@ -349,4 +394,81 @@ TEST_F(InductionVarRangeTest, MaxValue) { ExpectEqual(Value(), MaxValue(Value(55), Value(&y_, 1, -50))); } +// +// Tests on instance methods. +// + +TEST_F(InductionVarRangeTest, FindRangeConstantTripCount) { + BuildLoop(graph_->GetIntConstant(1000)); + PerformInductionVarAnalysis(); + InductionVarRange range(iva_); + + // In context of header: known. + ExpectEqual(Value(0), range.GetMinInduction(condition_, condition_->InputAt(0))); + ExpectEqual(Value(1000), range.GetMaxInduction(condition_, condition_->InputAt(0))); + + // In context of loop-body: known. + ExpectEqual(Value(0), range.GetMinInduction(increment_, condition_->InputAt(0))); + ExpectEqual(Value(999), range.GetMaxInduction(increment_, condition_->InputAt(0))); + ExpectEqual(Value(1), range.GetMinInduction(increment_, increment_)); + ExpectEqual(Value(1000), range.GetMaxInduction(increment_, increment_)); +} + +TEST_F(InductionVarRangeTest, FindRangeSymbolicTripCount) { + HInstruction* parameter = new (&allocator_) HParameterValue(0, Primitive::kPrimInt); + entry_block_->AddInstruction(parameter); + BuildLoop(parameter); + PerformInductionVarAnalysis(); + InductionVarRange range(iva_); + + // In context of header: full range unknown. + ExpectEqual(Value(0), range.GetMinInduction(condition_, condition_->InputAt(0))); + ExpectEqual(Value(), range.GetMaxInduction(condition_, condition_->InputAt(0))); + + // In context of loop-body: known. + ExpectEqual(Value(0), range.GetMinInduction(increment_, condition_->InputAt(0))); + ExpectEqual(Value(parameter, 1, -1), range.GetMaxInduction(increment_, condition_->InputAt(0))); + ExpectEqual(Value(1), range.GetMinInduction(increment_, increment_)); + ExpectEqual(Value(parameter, 1, 0), range.GetMaxInduction(increment_, increment_)); +} + +TEST_F(InductionVarRangeTest, CodeGeneration) { + HInstruction* parameter = new (&allocator_) HParameterValue(0, Primitive::kPrimInt); + entry_block_->AddInstruction(parameter); + BuildLoop(parameter); + PerformInductionVarAnalysis(); + InductionVarRange range(iva_); + + HInstruction* lower = nullptr; + HInstruction* upper = nullptr; + bool top_test = false; + + // Can generate code in context of loop-body only. + EXPECT_FALSE(range.CanGenerateCode(condition_, condition_->InputAt(0), &top_test)); + ASSERT_TRUE(range.CanGenerateCode(increment_, condition_->InputAt(0), &top_test)); + EXPECT_TRUE(top_test); + + // Generates code. + EXPECT_TRUE(range.GenerateCode( + increment_, condition_->InputAt(0), graph_, loop_preheader_, &lower, &upper)); + + // Verify lower is 0+0. + ASSERT_TRUE(lower != nullptr); + ASSERT_TRUE(lower->IsAdd()); + ASSERT_TRUE(lower->InputAt(0)->IsIntConstant()); + EXPECT_EQ(0, lower->InputAt(0)->AsIntConstant()->GetValue()); + ASSERT_TRUE(lower->InputAt(1)->IsIntConstant()); + EXPECT_EQ(0, lower->InputAt(1)->AsIntConstant()->GetValue()); + + // Verify upper is (V-1)+0 + ASSERT_TRUE(upper != nullptr); + ASSERT_TRUE(upper->IsAdd()); + ASSERT_TRUE(upper->InputAt(0)->IsSub()); + EXPECT_TRUE(upper->InputAt(0)->InputAt(0)->IsParameterValue()); + ASSERT_TRUE(upper->InputAt(0)->InputAt(1)->IsIntConstant()); + EXPECT_EQ(1, upper->InputAt(0)->InputAt(1)->AsIntConstant()->GetValue()); + ASSERT_TRUE(upper->InputAt(1)->IsIntConstant()); + EXPECT_EQ(0, upper->InputAt(1)->AsIntConstant()->GetValue()); +} + } // namespace art diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 078d8e5194..7814eb9c11 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -73,9 +73,13 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE; void VisitFakeString(HFakeString* fake_string) OVERRIDE; void VisitInvoke(HInvoke* invoke) OVERRIDE; + void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE; bool CanEnsureNotNullAt(HInstruction* instr, HInstruction* at) const; + void SimplifySystemArrayCopy(HInvoke* invoke); + void SimplifyStringEquals(HInvoke* invoke); + OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; int simplifications_at_current_position_ = 0; @@ -615,6 +619,8 @@ void InstructionSimplifierVisitor::VisitLessThanOrEqual(HLessThanOrEqual* condit VisitCondition(condition); } +// TODO: unsigned comparisons too? + void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) { // Try to fold an HCompare into this HCondition. @@ -1049,27 +1055,111 @@ void InstructionSimplifierVisitor::VisitFakeString(HFakeString* instruction) { instruction->GetBlock()->RemoveInstruction(instruction); } +void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { + HInstruction* argument = instruction->InputAt(1); + HInstruction* receiver = instruction->InputAt(0); + if (receiver == argument) { + // Because String.equals is an instance call, the receiver is + // a null check if we don't know it's null. The argument however, will + // be the actual object. So we cannot end up in a situation where both + // are equal but could be null. + DCHECK(CanEnsureNotNullAt(argument, instruction)); + instruction->ReplaceWith(GetGraph()->GetIntConstant(1)); + instruction->GetBlock()->RemoveInstruction(instruction); + } else { + StringEqualsOptimizations optimizations(instruction); + if (CanEnsureNotNullAt(argument, instruction)) { + optimizations.SetArgumentNotNull(); + } + ScopedObjectAccess soa(Thread::Current()); + ReferenceTypeInfo argument_rti = argument->GetReferenceTypeInfo(); + if (argument_rti.IsValid() && argument_rti.IsStringClass()) { + optimizations.SetArgumentIsString(); + } + } +} + +static bool IsArrayLengthOf(HInstruction* potential_length, HInstruction* potential_array) { + if (potential_length->IsArrayLength()) { + return potential_length->InputAt(0) == potential_array; + } + + if (potential_array->IsNewArray()) { + return potential_array->InputAt(0) == potential_length; + } + + return false; +} + +void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) { + HInstruction* source = instruction->InputAt(0); + HInstruction* destination = instruction->InputAt(2); + HInstruction* count = instruction->InputAt(4); + SystemArrayCopyOptimizations optimizations(instruction); + if (CanEnsureNotNullAt(source, instruction)) { + optimizations.SetSourceIsNotNull(); + } + if (CanEnsureNotNullAt(destination, instruction)) { + optimizations.SetDestinationIsNotNull(); + } + if (destination == source) { + optimizations.SetDestinationIsSource(); + } + + if (IsArrayLengthOf(count, source)) { + optimizations.SetCountIsSourceLength(); + } + + if (IsArrayLengthOf(count, destination)) { + optimizations.SetCountIsDestinationLength(); + } + + { + ScopedObjectAccess soa(Thread::Current()); + ReferenceTypeInfo destination_rti = destination->GetReferenceTypeInfo(); + if (destination_rti.IsValid()) { + if (destination_rti.IsObjectArray()) { + if (destination_rti.IsExact()) { + optimizations.SetDoesNotNeedTypeCheck(); + } + optimizations.SetDestinationIsTypedObjectArray(); + } + if (destination_rti.IsPrimitiveArrayClass()) { + optimizations.SetDestinationIsPrimitiveArray(); + } else if (destination_rti.IsNonPrimitiveArrayClass()) { + optimizations.SetDestinationIsNonPrimitiveArray(); + } + } + ReferenceTypeInfo source_rti = source->GetReferenceTypeInfo(); + if (source_rti.IsValid()) { + if (destination_rti.IsValid() && destination_rti.CanArrayHoldValuesOf(source_rti)) { + optimizations.SetDoesNotNeedTypeCheck(); + } + if (source_rti.IsPrimitiveArrayClass()) { + optimizations.SetSourceIsPrimitiveArray(); + } else if (source_rti.IsNonPrimitiveArrayClass()) { + optimizations.SetSourceIsNonPrimitiveArray(); + } + } + } +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) { - HInstruction* argument = instruction->InputAt(1); - HInstruction* receiver = instruction->InputAt(0); - if (receiver == argument) { - // Because String.equals is an instance call, the receiver is - // a null check if we don't know it's null. The argument however, will - // be the actual object. So we cannot end up in a situation where both - // are equal but could be null. - DCHECK(CanEnsureNotNullAt(argument, instruction)); - instruction->ReplaceWith(GetGraph()->GetIntConstant(1)); - instruction->GetBlock()->RemoveInstruction(instruction); + SimplifyStringEquals(instruction); + } else if (instruction->GetIntrinsic() == Intrinsics::kSystemArrayCopy) { + SimplifySystemArrayCopy(instruction); + } +} + +void InstructionSimplifierVisitor::VisitDeoptimize(HDeoptimize* deoptimize) { + HInstruction* cond = deoptimize->InputAt(0); + if (cond->IsConstant()) { + if (cond->AsIntConstant()->IsZero()) { + // Never deopt: instruction can be removed. + deoptimize->GetBlock()->RemoveInstruction(deoptimize); } else { - StringEqualsOptimizations optimizations(instruction); - if (CanEnsureNotNullAt(argument, instruction)) { - optimizations.SetArgumentNotNull(); - } - ScopedObjectAccess soa(Thread::Current()); - if (argument->GetReferenceTypeInfo().IsStringClass()) { - optimizations.SetArgumentIsString(); - } + // Always deopt. } } } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 95646222ef..dbe75249be 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -210,6 +210,9 @@ static Intrinsics GetIntrinsic(InlineMethod method, InstructionSet instruction_s case kIntrinsicSystemArrayCopyCharArray: return Intrinsics::kSystemArrayCopyChar; + case kIntrinsicSystemArrayCopy: + return Intrinsics::kSystemArrayCopy; + // Thread.currentThread. case kIntrinsicCurrentThread: return Intrinsics::kThreadCurrentThread; diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index d50fe79f93..e459516e59 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -168,6 +168,26 @@ class StringEqualsOptimizations : public IntrinsicOptimizations { DISALLOW_COPY_AND_ASSIGN(StringEqualsOptimizations); }; +class SystemArrayCopyOptimizations : public IntrinsicOptimizations { + public: + explicit SystemArrayCopyOptimizations(HInvoke* invoke) : IntrinsicOptimizations(invoke) {} + + INTRINSIC_OPTIMIZATION(SourceIsNotNull, 0); + INTRINSIC_OPTIMIZATION(DestinationIsNotNull, 1); + INTRINSIC_OPTIMIZATION(DestinationIsSource, 2); + INTRINSIC_OPTIMIZATION(CountIsSourceLength, 3); + INTRINSIC_OPTIMIZATION(CountIsDestinationLength, 4); + INTRINSIC_OPTIMIZATION(DoesNotNeedTypeCheck, 5); + INTRINSIC_OPTIMIZATION(DestinationIsTypedObjectArray, 6); + INTRINSIC_OPTIMIZATION(DestinationIsNonPrimitiveArray, 7); + INTRINSIC_OPTIMIZATION(DestinationIsPrimitiveArray, 8); + INTRINSIC_OPTIMIZATION(SourceIsNonPrimitiveArray, 9); + INTRINSIC_OPTIMIZATION(SourceIsPrimitiveArray, 10); + + private: + DISALLOW_COPY_AND_ASSIGN(SystemArrayCopyOptimizations); +}; + #undef INTRISIC_OPTIMIZATION } // namespace art diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 938c78e9c1..58e479afc7 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1307,6 +1307,308 @@ void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) { + CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke); + LocationSummary* locations = invoke->GetLocations(); + if (locations == nullptr) { + return; + } + + HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant(); + HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant(); + HIntConstant* length = invoke->InputAt(4)->AsIntConstant(); + + if (src_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(src_pos->GetValue())) { + locations->SetInAt(1, Location::RequiresRegister()); + } + if (dest_pos != nullptr && !assembler_->ShifterOperandCanAlwaysHold(dest_pos->GetValue())) { + locations->SetInAt(3, Location::RequiresRegister()); + } + if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) { + locations->SetInAt(4, Location::RequiresRegister()); + } +} + +static void CheckPosition(ArmAssembler* assembler, + Location pos, + Register input, + Location length, + SlowPathCode* slow_path, + Register input_len, + Register temp, + bool length_is_input_length = false) { + // Where is the length in the Array? + const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value(); + + if (pos.IsConstant()) { + int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue(); + if (pos_const == 0) { + if (!length_is_input_length) { + // Check that length(input) >= length. + __ LoadFromOffset(kLoadWord, temp, input, length_offset); + if (length.IsConstant()) { + __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmp(temp, ShifterOperand(length.AsRegister<Register>())); + } + __ b(slow_path->GetEntryLabel(), LT); + } + } else { + // Check that length(input) >= pos. + __ LoadFromOffset(kLoadWord, input_len, input, length_offset); + __ subs(temp, input_len, ShifterOperand(pos_const)); + __ b(slow_path->GetEntryLabel(), LT); + + // Check that (length(input) - pos) >= length. + if (length.IsConstant()) { + __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmp(temp, ShifterOperand(length.AsRegister<Register>())); + } + __ b(slow_path->GetEntryLabel(), LT); + } + } else if (length_is_input_length) { + // The only way the copy can succeed is if pos is zero. + Register pos_reg = pos.AsRegister<Register>(); + __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel()); + } else { + // Check that pos >= 0. + Register pos_reg = pos.AsRegister<Register>(); + __ cmp(pos_reg, ShifterOperand(0)); + __ b(slow_path->GetEntryLabel(), LT); + + // Check that pos <= length(input). + __ LoadFromOffset(kLoadWord, temp, input, length_offset); + __ subs(temp, temp, ShifterOperand(pos_reg)); + __ b(slow_path->GetEntryLabel(), LT); + + // Check that (length(input) - pos) >= length. + if (length.IsConstant()) { + __ cmp(temp, ShifterOperand(length.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmp(temp, ShifterOperand(length.AsRegister<Register>())); + } + __ b(slow_path->GetEntryLabel(), LT); + } +} + +void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); + uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); + uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); + + Register src = locations->InAt(0).AsRegister<Register>(); + Location src_pos = locations->InAt(1); + Register dest = locations->InAt(2).AsRegister<Register>(); + Location dest_pos = locations->InAt(3); + Location length = locations->InAt(4); + Register temp1 = locations->GetTemp(0).AsRegister<Register>(); + Register temp2 = locations->GetTemp(1).AsRegister<Register>(); + Register temp3 = locations->GetTemp(2).AsRegister<Register>(); + + SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); + codegen_->AddSlowPath(slow_path); + + Label ok; + SystemArrayCopyOptimizations optimizations(invoke); + + if (!optimizations.GetDestinationIsSource()) { + if (!src_pos.IsConstant() || !dest_pos.IsConstant()) { + __ cmp(src, ShifterOperand(dest)); + } + } + + // If source and destination are the same, we go to slow path if we need to do + // forward copying. + if (src_pos.IsConstant()) { + int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); + if (dest_pos.IsConstant()) { + // Checked when building locations. + DCHECK(!optimizations.GetDestinationIsSource() + || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue())); + } else { + if (!optimizations.GetDestinationIsSource()) { + __ b(&ok, NE); + } + __ cmp(dest_pos.AsRegister<Register>(), ShifterOperand(src_pos_constant)); + __ b(slow_path->GetEntryLabel(), GT); + } + } else { + if (!optimizations.GetDestinationIsSource()) { + __ b(&ok, NE); + } + if (dest_pos.IsConstant()) { + int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); + __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos_constant)); + } else { + __ cmp(src_pos.AsRegister<Register>(), ShifterOperand(dest_pos.AsRegister<Register>())); + } + __ b(slow_path->GetEntryLabel(), LT); + } + + __ Bind(&ok); + + if (!optimizations.GetSourceIsNotNull()) { + // Bail out if the source is null. + __ CompareAndBranchIfZero(src, slow_path->GetEntryLabel()); + } + + if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) { + // Bail out if the destination is null. + __ CompareAndBranchIfZero(dest, slow_path->GetEntryLabel()); + } + + // If the length is negative, bail out. + // We have already checked in the LocationsBuilder for the constant case. + if (!length.IsConstant() && + !optimizations.GetCountIsSourceLength() && + !optimizations.GetCountIsDestinationLength()) { + __ cmp(length.AsRegister<Register>(), ShifterOperand(0)); + __ b(slow_path->GetEntryLabel(), LT); + } + + // Validity checks: source. + CheckPosition(assembler, + src_pos, + src, + length, + slow_path, + temp1, + temp2, + optimizations.GetCountIsSourceLength()); + + // Validity checks: dest. + CheckPosition(assembler, + dest_pos, + dest, + length, + slow_path, + temp1, + temp2, + optimizations.GetCountIsDestinationLength()); + + if (!optimizations.GetDoesNotNeedTypeCheck()) { + // Check whether all elements of the source array are assignable to the component + // type of the destination array. We do two checks: the classes are the same, + // or the destination is Object[]. If none of these checks succeed, we go to the + // slow path. + __ LoadFromOffset(kLoadWord, temp1, dest, class_offset); + __ LoadFromOffset(kLoadWord, temp2, src, class_offset); + bool did_unpoison = false; + if (!optimizations.GetDestinationIsNonPrimitiveArray() || + !optimizations.GetSourceIsNonPrimitiveArray()) { + // One or two of the references need to be unpoisoned. Unpoisoned them + // both to make the identity check valid. + __ MaybeUnpoisonHeapReference(temp1); + __ MaybeUnpoisonHeapReference(temp2); + did_unpoison = true; + } + + if (!optimizations.GetDestinationIsNonPrimitiveArray()) { + // Bail out if the destination is not a non primitive array. + __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset); + __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel()); + __ MaybeUnpoisonHeapReference(temp3); + __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset); + static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); + __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel()); + } + + if (!optimizations.GetSourceIsNonPrimitiveArray()) { + // Bail out if the source is not a non primitive array. + // Bail out if the destination is not a non primitive array. + __ LoadFromOffset(kLoadWord, temp3, temp2, component_offset); + __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel()); + __ MaybeUnpoisonHeapReference(temp3); + __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset); + static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); + __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel()); + } + + __ cmp(temp1, ShifterOperand(temp2)); + + if (optimizations.GetDestinationIsTypedObjectArray()) { + Label do_copy; + __ b(&do_copy, EQ); + if (!did_unpoison) { + __ MaybeUnpoisonHeapReference(temp1); + } + __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); + __ MaybeUnpoisonHeapReference(temp1); + __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset); + // No need to unpoison the result, we're comparing against null. + __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); + __ Bind(&do_copy); + } else { + __ b(slow_path->GetEntryLabel(), NE); + } + } else if (!optimizations.GetSourceIsNonPrimitiveArray()) { + DCHECK(optimizations.GetDestinationIsNonPrimitiveArray()); + // Bail out if the source is not a non primitive array. + __ LoadFromOffset(kLoadWord, temp1, src, class_offset); + __ MaybeUnpoisonHeapReference(temp1); + __ LoadFromOffset(kLoadWord, temp3, temp1, component_offset); + __ CompareAndBranchIfZero(temp3, slow_path->GetEntryLabel()); + __ MaybeUnpoisonHeapReference(temp3); + __ LoadFromOffset(kLoadUnsignedHalfword, temp3, temp3, primitive_offset); + static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); + __ CompareAndBranchIfNonZero(temp3, slow_path->GetEntryLabel()); + } + + // Compute base source address, base destination address, and end source address. + + uint32_t element_size = sizeof(int32_t); + uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value(); + if (src_pos.IsConstant()) { + int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); + __ AddConstant(temp1, src, element_size * constant + offset); + } else { + __ add(temp1, src, ShifterOperand(src_pos.AsRegister<Register>(), LSL, 2)); + __ AddConstant(temp1, offset); + } + + if (dest_pos.IsConstant()) { + int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); + __ AddConstant(temp2, dest, element_size * constant + offset); + } else { + __ add(temp2, dest, ShifterOperand(dest_pos.AsRegister<Register>(), LSL, 2)); + __ AddConstant(temp2, offset); + } + + if (length.IsConstant()) { + int32_t constant = length.GetConstant()->AsIntConstant()->GetValue(); + __ AddConstant(temp3, temp1, element_size * constant); + } else { + __ add(temp3, temp1, ShifterOperand(length.AsRegister<Register>(), LSL, 2)); + } + + // Iterate over the arrays and do a raw copy of the objects. We don't need to + // poison/unpoison, nor do any read barrier as the next uses of the destination + // array will do it. + Label loop, done; + __ cmp(temp1, ShifterOperand(temp3)); + __ b(&done, EQ); + __ Bind(&loop); + __ ldr(IP, Address(temp1, element_size, Address::PostIndex)); + __ str(IP, Address(temp2, element_size, Address::PostIndex)); + __ cmp(temp1, ShifterOperand(temp3)); + __ b(&loop, NE); + __ Bind(&done); + + // We only need one card marking on the destination array. + codegen_->MarkGCCard(temp1, + temp2, + dest, + Register(kNoRegister), + false); + + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ diff --git a/compiler/optimizing/intrinsics_arm.h b/compiler/optimizing/intrinsics_arm.h index 2abb605e6e..127e9a4aa0 100644 --- a/compiler/optimizing/intrinsics_arm.h +++ b/compiler/optimizing/intrinsics_arm.h @@ -33,8 +33,10 @@ class CodeGeneratorARM; class IntrinsicLocationsBuilderARM FINAL : public IntrinsicVisitor { public: - IntrinsicLocationsBuilderARM(ArenaAllocator* arena, const ArmInstructionSetFeatures& features) - : arena_(arena), features_(features) {} + IntrinsicLocationsBuilderARM(ArenaAllocator* arena, + ArmAssembler* assembler, + const ArmInstructionSetFeatures& features) + : arena_(arena), assembler_(assembler), features_(features) {} // Define visitor methods. @@ -52,6 +54,7 @@ INTRINSICS_LIST(OPTIMIZING_INTRINSICS) private: ArenaAllocator* arena_; + ArmAssembler* assembler_; const ArmInstructionSetFeatures& features_; diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index b0cfd0d1bc..4da94ee9b3 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1447,6 +1447,7 @@ void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED } UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index bfe5e55c56..8f1d5e1c4d 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -58,6 +58,7 @@ V(MathRoundDouble, kStatic, kNeedsEnvironmentOrCache) \ V(MathRoundFloat, kStatic, kNeedsEnvironmentOrCache) \ V(SystemArrayCopyChar, kStatic, kNeedsEnvironmentOrCache) \ + V(SystemArrayCopy, kStatic, kNeedsEnvironmentOrCache) \ V(ThreadCurrentThread, kStatic, kNeedsEnvironmentOrCache) \ V(MemoryPeekByte, kStatic, kNeedsEnvironmentOrCache) \ V(MemoryPeekIntNative, kStatic, kNeedsEnvironmentOrCache) \ diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index b60905d682..56c4177b29 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -43,6 +43,93 @@ ArenaAllocator* IntrinsicCodeGeneratorMIPS64::GetAllocator() { return codegen_->GetGraph()->GetArena(); } +#define __ codegen->GetAssembler()-> + +static void MoveFromReturnRegister(Location trg, + Primitive::Type type, + CodeGeneratorMIPS64* codegen) { + if (!trg.IsValid()) { + DCHECK_EQ(type, Primitive::kPrimVoid); + return; + } + + DCHECK_NE(type, Primitive::kPrimVoid); + + if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) { + GpuRegister trg_reg = trg.AsRegister<GpuRegister>(); + if (trg_reg != V0) { + __ Move(V0, trg_reg); + } + } else { + FpuRegister trg_reg = trg.AsFpuRegister<FpuRegister>(); + if (trg_reg != F0) { + if (type == Primitive::kPrimFloat) { + __ MovS(F0, trg_reg); + } else { + __ MovD(F0, trg_reg); + } + } + } +} + +static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS64* codegen) { + InvokeDexCallingConventionVisitorMIPS64 calling_convention_visitor; + IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor); +} + +// Slow-path for fallback (calling the managed code to handle the +// intrinsic) in an intrinsified call. This will copy the arguments +// into the positions for a regular call. +// +// Note: The actual parameters are required to be in the locations +// given by the invoke's location summary. If an intrinsic +// modifies those locations before a slowpath call, they must be +// restored! +class IntrinsicSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + explicit IntrinsicSlowPathMIPS64(HInvoke* invoke) : invoke_(invoke) { } + + void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { + CodeGeneratorMIPS64* codegen = down_cast<CodeGeneratorMIPS64*>(codegen_in); + + __ Bind(GetEntryLabel()); + + SaveLiveRegisters(codegen, invoke_->GetLocations()); + + MoveArguments(invoke_, codegen); + + if (invoke_->IsInvokeStaticOrDirect()) { + codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), + Location::RegisterLocation(A0)); + codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); + } else { + UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented"; + UNREACHABLE(); + } + + // Copy the result back to the expected output. + Location out = invoke_->GetLocations()->Out(); + if (out.IsValid()) { + DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory. + DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg())); + MoveFromReturnRegister(out, invoke_->GetType(), codegen); + } + + RestoreLiveRegisters(codegen, invoke_->GetLocations()); + __ B(GetExitLabel()); + } + + const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathMIPS64"; } + + private: + // The instruction where this slow path is happening. + HInvoke* const invoke_; + + DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathMIPS64); +}; + +#undef __ + bool IntrinsicLocationsBuilderMIPS64::TryDispatch(HInvoke* invoke) { Dispatch(invoke); LocationSummary* res = invoke->GetLocations(); @@ -185,7 +272,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitShortReverseBytes(HInvoke* invoke) { GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler()); } -static void GenCountZeroes(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) { +static void GenNumberOfLeadingZeroes(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) { GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); @@ -202,7 +289,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitIntegerNumberOfLeadingZeros(HInvoke* } void IntrinsicCodeGeneratorMIPS64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { - GenCountZeroes(invoke->GetLocations(), false, GetAssembler()); + GenNumberOfLeadingZeroes(invoke->GetLocations(), false, GetAssembler()); } // int java.lang.Long.numberOfLeadingZeros(long i) @@ -211,7 +298,166 @@ void IntrinsicLocationsBuilderMIPS64::VisitLongNumberOfLeadingZeros(HInvoke* inv } void IntrinsicCodeGeneratorMIPS64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { - GenCountZeroes(invoke->GetLocations(), true, GetAssembler()); + GenNumberOfLeadingZeroes(invoke->GetLocations(), true, GetAssembler()); +} + +static void GenNumberOfTrailingZeroes(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) { + Location in = locations->InAt(0); + Location out = locations->Out(); + + if (is64bit) { + __ Dsbh(out.AsRegister<GpuRegister>(), in.AsRegister<GpuRegister>()); + __ Dshd(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>()); + __ Dbitswap(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>()); + __ Dclz(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>()); + } else { + __ Rotr(out.AsRegister<GpuRegister>(), in.AsRegister<GpuRegister>(), 16); + __ Wsbh(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>()); + __ Bitswap(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>()); + __ Clz(out.AsRegister<GpuRegister>(), out.AsRegister<GpuRegister>()); + } +} + +// int java.lang.Integer.numberOfTrailingZeros(int i) +void IntrinsicLocationsBuilderMIPS64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { + GenNumberOfTrailingZeroes(invoke->GetLocations(), false, GetAssembler()); +} + +// int java.lang.Long.numberOfTrailingZeros(long i) +void IntrinsicLocationsBuilderMIPS64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { + GenNumberOfTrailingZeroes(invoke->GetLocations(), true, GetAssembler()); +} + +static void GenRotateRight(HInvoke* invoke, + Primitive::Type type, + Mips64Assembler* assembler) { + DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); + + LocationSummary* locations = invoke->GetLocations(); + GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + + if (invoke->InputAt(1)->IsIntConstant()) { + uint32_t shift = static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()); + if (type == Primitive::kPrimInt) { + shift &= 0x1f; + __ Rotr(out, in, shift); + } else { + shift &= 0x3f; + if (shift < 32) { + __ Drotr(out, in, shift); + } else { + shift &= 0x1f; + __ Drotr32(out, in, shift); + } + } + } else { + GpuRegister shamt = locations->InAt(1).AsRegister<GpuRegister>(); + if (type == Primitive::kPrimInt) { + __ Rotrv(out, in, shamt); + } else { + __ Drotrv(out, in, shamt); + } + } +} + +// int java.lang.Integer.rotateRight(int i, int distance) +void IntrinsicLocationsBuilderMIPS64::VisitIntegerRotateRight(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void IntrinsicCodeGeneratorMIPS64::VisitIntegerRotateRight(HInvoke* invoke) { + GenRotateRight(invoke, Primitive::kPrimInt, GetAssembler()); +} + +// int java.lang.Long.rotateRight(long i, int distance) +void IntrinsicLocationsBuilderMIPS64::VisitLongRotateRight(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void IntrinsicCodeGeneratorMIPS64::VisitLongRotateRight(HInvoke* invoke) { + GenRotateRight(invoke, Primitive::kPrimLong, GetAssembler()); +} + +static void GenRotateLeft(HInvoke* invoke, + Primitive::Type type, + Mips64Assembler* assembler) { + DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); + + LocationSummary* locations = invoke->GetLocations(); + GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + + if (invoke->InputAt(1)->IsIntConstant()) { + int32_t shift = -static_cast<int32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()); + if (type == Primitive::kPrimInt) { + shift &= 0x1f; + __ Rotr(out, in, shift); + } else { + shift &= 0x3f; + if (shift < 32) { + __ Drotr(out, in, shift); + } else { + shift &= 0x1f; + __ Drotr32(out, in, shift); + } + } + } else { + GpuRegister shamt = locations->InAt(1).AsRegister<GpuRegister>(); + if (type == Primitive::kPrimInt) { + __ Subu(TMP, ZERO, shamt); + __ Rotrv(out, in, TMP); + } else { + __ Dsubu(TMP, ZERO, shamt); + __ Drotrv(out, in, TMP); + } + } +} + +// int java.lang.Integer.rotateLeft(int i, int distance) +void IntrinsicLocationsBuilderMIPS64::VisitIntegerRotateLeft(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void IntrinsicCodeGeneratorMIPS64::VisitIntegerRotateLeft(HInvoke* invoke) { + GenRotateLeft(invoke, Primitive::kPrimInt, GetAssembler()); +} + +// int java.lang.Long.rotateLeft(long i, int distance) +void IntrinsicLocationsBuilderMIPS64::VisitLongRotateLeft(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void IntrinsicCodeGeneratorMIPS64::VisitLongRotateLeft(HInvoke* invoke) { + GenRotateLeft(invoke, Primitive::kPrimLong, GetAssembler()); } static void GenReverse(LocationSummary* locations, @@ -765,6 +1011,505 @@ void IntrinsicCodeGeneratorMIPS64::VisitThreadCurrentThread(HInvoke* invoke) { Thread::PeerOffset<kMips64PointerSize>().Int32Value()); } +static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +static void GenUnsafeGet(HInvoke* invoke, + Primitive::Type type, + bool is_volatile, + CodeGeneratorMIPS64* codegen) { + LocationSummary* locations = invoke->GetLocations(); + DCHECK((type == Primitive::kPrimInt) || + (type == Primitive::kPrimLong) || + (type == Primitive::kPrimNot)); + Mips64Assembler* assembler = codegen->GetAssembler(); + // Object pointer. + GpuRegister base = locations->InAt(1).AsRegister<GpuRegister>(); + // Long offset. + GpuRegister offset = locations->InAt(2).AsRegister<GpuRegister>(); + GpuRegister trg = locations->Out().AsRegister<GpuRegister>(); + + __ Daddu(TMP, base, offset); + if (is_volatile) { + __ Sync(0); + } + switch (type) { + case Primitive::kPrimInt: + __ Lw(trg, TMP, 0); + break; + + case Primitive::kPrimNot: + __ Lwu(trg, TMP, 0); + break; + + case Primitive::kPrimLong: + __ Ld(trg, TMP, 0); + break; + + default: + LOG(FATAL) << "Unsupported op size " << type; + UNREACHABLE(); + } +} + +// int sun.misc.Unsafe.getInt(Object o, long offset) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGet(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGet(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_); +} + +// int sun.misc.Unsafe.getIntVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_); +} + +// long sun.misc.Unsafe.getLong(Object o, long offset) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetLong(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetLong(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_); +} + +// long sun.misc.Unsafe.getLongVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetLongVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetLongVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_); +} + +// Object sun.misc.Unsafe.getObject(Object o, long offset) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetObject(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetObject(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_); +} + +// Object sun.misc.Unsafe.getObjectVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_); +} + +static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); +} + +static void GenUnsafePut(LocationSummary* locations, + Primitive::Type type, + bool is_volatile, + bool is_ordered, + CodeGeneratorMIPS64* codegen) { + DCHECK((type == Primitive::kPrimInt) || + (type == Primitive::kPrimLong) || + (type == Primitive::kPrimNot)); + Mips64Assembler* assembler = codegen->GetAssembler(); + // Object pointer. + GpuRegister base = locations->InAt(1).AsRegister<GpuRegister>(); + // Long offset. + GpuRegister offset = locations->InAt(2).AsRegister<GpuRegister>(); + GpuRegister value = locations->InAt(3).AsRegister<GpuRegister>(); + + __ Daddu(TMP, base, offset); + if (is_volatile || is_ordered) { + __ Sync(0); + } + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimNot: + __ Sw(value, TMP, 0); + break; + + case Primitive::kPrimLong: + __ Sd(value, TMP, 0); + break; + + default: + LOG(FATAL) << "Unsupported op size " << type; + UNREACHABLE(); + } + if (is_volatile) { + __ Sync(0); + } + + if (type == Primitive::kPrimNot) { + codegen->MarkGCCard(base, value); + } +} + +// void sun.misc.Unsafe.putInt(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePut(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePut(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_); +} + +// void sun.misc.Unsafe.putOrderedInt(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_); +} + +// void sun.misc.Unsafe.putIntVolatile(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_); +} + +// void sun.misc.Unsafe.putObject(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutObject(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutObject(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_); +} + +// void sun.misc.Unsafe.putOrderedObject(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutObjectOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutObjectOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_); +} + +// void sun.misc.Unsafe.putObjectVolatile(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutObjectVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutObjectVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_); +} + +// void sun.misc.Unsafe.putLong(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutLong(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutLong(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_); +} + +// void sun.misc.Unsafe.putOrderedLong(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutLongOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutLongOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_); +} + +// void sun.misc.Unsafe.putLongVolatile(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafePutLongVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoid(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutLongVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_); +} + +// char java.lang.String.charAt(int index) +void IntrinsicLocationsBuilderMIPS64::VisitStringCharAt(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringCharAt(HInvoke* invoke) { + LocationSummary* locations = invoke->GetLocations(); + Mips64Assembler* assembler = GetAssembler(); + + // Location of reference to data array + const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); + // Location of count + const int32_t count_offset = mirror::String::CountOffset().Int32Value(); + + GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister idx = locations->InAt(1).AsRegister<GpuRegister>(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + + // TODO: Maybe we can support range check elimination. Overall, + // though, I think it's not worth the cost. + // TODO: For simplicity, the index parameter is requested in a + // register, so different from Quick we will not optimize the + // code for constants (which would save a register). + + SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke); + codegen_->AddSlowPath(slow_path); + + // Load the string size + __ Lw(TMP, obj, count_offset); + codegen_->MaybeRecordImplicitNullCheck(invoke); + // Revert to slow path if idx is too large, or negative + __ Bgeuc(idx, TMP, slow_path->GetEntryLabel()); + + // out = obj[2*idx]. + __ Sll(TMP, idx, 1); // idx * 2 + __ Daddu(TMP, TMP, obj); // Address of char at location idx + __ Lhu(out, TMP, value_offset); // Load char at location idx + + __ Bind(slow_path->GetExitLabel()); +} + +// int java.lang.String.compareTo(String anotherString) +void IntrinsicLocationsBuilderMIPS64::VisitStringCompareTo(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>())); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringCompareTo(HInvoke* invoke) { + Mips64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + GpuRegister argument = locations->InAt(1).AsRegister<GpuRegister>(); + SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqzc(argument, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadDoubleword, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, + pStringCompareTo).Int32Value()); + __ Jalr(TMP); + __ Nop(); + __ Bind(slow_path->GetExitLabel()); +} + +static void GenerateStringIndexOf(HInvoke* invoke, + Mips64Assembler* assembler, + CodeGeneratorMIPS64* codegen, + ArenaAllocator* allocator, + bool start_at_zero) { + LocationSummary* locations = invoke->GetLocations(); + GpuRegister tmp_reg = start_at_zero ? locations->GetTemp(0).AsRegister<GpuRegister>() : TMP; + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + // Check for code points > 0xFFFF. Either a slow-path check when we + // don't know statically, or directly dispatch if we have a constant. + SlowPathCodeMIPS64* slow_path = nullptr; + if (invoke->InputAt(1)->IsIntConstant()) { + if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) { + // Always needs the slow-path. We could directly dispatch to it, + // but this case should be rare, so for simplicity just put the + // full slow-path down and branch unconditionally. + slow_path = new (allocator) IntrinsicSlowPathMIPS64(invoke); + codegen->AddSlowPath(slow_path); + __ B(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + return; + } + } else { + GpuRegister char_reg = locations->InAt(1).AsRegister<GpuRegister>(); + __ LoadConst32(tmp_reg, std::numeric_limits<uint16_t>::max()); + slow_path = new (allocator) IntrinsicSlowPathMIPS64(invoke); + codegen->AddSlowPath(slow_path); + __ Bltuc(tmp_reg, char_reg, slow_path->GetEntryLabel()); // UTF-16 required + } + + if (start_at_zero) { + DCHECK_EQ(tmp_reg, A2); + // Start-index = 0. + __ Clear(tmp_reg); + } else { + __ Slt(TMP, A2, ZERO); // if fromIndex < 0 + __ Seleqz(A2, A2, TMP); // fromIndex = 0 + } + + __ LoadFromOffset(kLoadDoubleword, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, pIndexOf).Int32Value()); + __ Jalr(TMP); + __ Nop(); + + if (slow_path != nullptr) { + __ Bind(slow_path->GetExitLabel()); + } +} + +// int java.lang.String.indexOf(int ch) +void IntrinsicLocationsBuilderMIPS64::VisitStringIndexOf(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime + // calling convention. So it's best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>())); + + // Need a temp for slow-path codepoint compare, and need to send start-index=0. + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringIndexOf(HInvoke* invoke) { + GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), true); +} + +// int java.lang.String.indexOf(int ch, int fromIndex) +void IntrinsicLocationsBuilderMIPS64::VisitStringIndexOfAfter(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime + // calling convention. So it's best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>())); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringIndexOfAfter(HInvoke* invoke) { + GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), false); +} + +// java.lang.String.String(byte[] bytes) +void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>())); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke) { + Mips64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + GpuRegister byte_array = locations->InAt(0).AsRegister<GpuRegister>(); + SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqzc(byte_array, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadDoubleword, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, pAllocStringFromBytes).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Jalr(TMP); + __ Nop(); + __ Bind(slow_path->GetExitLabel()); +} + +// java.lang.String.String(char[] value) +void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>())); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromChars(HInvoke* invoke) { + Mips64Assembler* assembler = GetAssembler(); + + __ LoadFromOffset(kLoadDoubleword, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, pAllocStringFromChars).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Jalr(TMP); + __ Nop(); +} + +// java.lang.String.String(String original) +void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>())); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromString(HInvoke* invoke) { + Mips64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + GpuRegister string_to_copy = locations->InAt(0).AsRegister<GpuRegister>(); + SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqzc(string_to_copy, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadDoubleword, + TMP, + TR, + QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, pAllocStringFromString).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Jalr(TMP); + __ Nop(); + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -776,42 +1521,15 @@ void IntrinsicCodeGeneratorMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(UnsafeGet) -UNIMPLEMENTED_INTRINSIC(UnsafeGetVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafeGetLong) -UNIMPLEMENTED_INTRINSIC(UnsafeGetLongVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafeGetObject) -UNIMPLEMENTED_INTRINSIC(UnsafeGetObjectVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePut) -UNIMPLEMENTED_INTRINSIC(UnsafePutOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePutObject) -UNIMPLEMENTED_INTRINSIC(UnsafePutObjectOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutObjectVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePutLong) -UNIMPLEMENTED_INTRINSIC(UnsafePutLongOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutLongVolatile) UNIMPLEMENTED_INTRINSIC(UnsafeCASInt) UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) UNIMPLEMENTED_INTRINSIC(UnsafeCASObject) -UNIMPLEMENTED_INTRINSIC(StringCharAt) -UNIMPLEMENTED_INTRINSIC(StringCompareTo) UNIMPLEMENTED_INTRINSIC(StringEquals) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromBytes) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromChars) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromString) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) #undef UNIMPLEMENTED_INTRINSIC diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 263c37596f..e83aebb5be 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2255,6 +2255,7 @@ UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(LongRotateRight) UNIMPLEMENTED_INTRINSIC(LongRotateLeft) +UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) #undef UNIMPLEMENTED_INTRINSIC diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 098db4ca28..e0d88a91d3 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -752,7 +752,7 @@ void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); - // arraycopy(Object src, int srcPos, Object dest, int destPos, int length). + // arraycopy(Object src, int src_pos, Object dest, int dest_pos, int length). locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); locations->SetInAt(2, Location::RequiresRegister()); @@ -768,19 +768,27 @@ void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) static void CheckPosition(X86_64Assembler* assembler, Location pos, CpuRegister input, - CpuRegister length, + Location length, SlowPathCode* slow_path, CpuRegister input_len, - CpuRegister temp) { - // Where is the length in the String? + CpuRegister temp, + bool length_is_input_length = false) { + // Where is the length in the Array? const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value(); if (pos.IsConstant()) { int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue(); if (pos_const == 0) { - // Check that length(input) >= length. - __ cmpl(Address(input, length_offset), length); - __ j(kLess, slow_path->GetEntryLabel()); + if (!length_is_input_length) { + // Check that length(input) >= length. + if (length.IsConstant()) { + __ cmpl(Address(input, length_offset), + Immediate(length.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmpl(Address(input, length_offset), length.AsRegister<CpuRegister>()); + } + __ j(kLess, slow_path->GetEntryLabel()); + } } else { // Check that length(input) >= pos. __ movl(input_len, Address(input, length_offset)); @@ -789,9 +797,18 @@ static void CheckPosition(X86_64Assembler* assembler, // Check that (length(input) - pos) >= length. __ leal(temp, Address(input_len, -pos_const)); - __ cmpl(temp, length); + if (length.IsConstant()) { + __ cmpl(temp, Immediate(length.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmpl(temp, length.AsRegister<CpuRegister>()); + } __ j(kLess, slow_path->GetEntryLabel()); } + } else if (length_is_input_length) { + // The only way the copy can succeed is if pos is zero. + CpuRegister pos_reg = pos.AsRegister<CpuRegister>(); + __ testl(pos_reg, pos_reg); + __ j(kNotEqual, slow_path->GetEntryLabel()); } else { // Check that pos >= 0. CpuRegister pos_reg = pos.AsRegister<CpuRegister>(); @@ -805,7 +822,11 @@ static void CheckPosition(X86_64Assembler* assembler, // Check that (length(input) - pos) >= length. __ movl(temp, Address(input, length_offset)); __ subl(temp, pos_reg); - __ cmpl(temp, length); + if (length.IsConstant()) { + __ cmpl(temp, Immediate(length.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmpl(temp, length.AsRegister<CpuRegister>()); + } __ j(kLess, slow_path->GetEntryLabel()); } } @@ -815,9 +836,9 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { LocationSummary* locations = invoke->GetLocations(); CpuRegister src = locations->InAt(0).AsRegister<CpuRegister>(); - Location srcPos = locations->InAt(1); + Location src_pos = locations->InAt(1); CpuRegister dest = locations->InAt(2).AsRegister<CpuRegister>(); - Location destPos = locations->InAt(3); + Location dest_pos = locations->InAt(3); Location length = locations->InAt(4); // Temporaries that we need for MOVSW. @@ -850,6 +871,12 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { __ j(kLess, slow_path->GetEntryLabel()); } + // Validity checks: source. + CheckPosition(assembler, src_pos, src, length, slow_path, src_base, dest_base); + + // Validity checks: dest. + CheckPosition(assembler, dest_pos, dest, length, slow_path, src_base, dest_base); + // We need the count in RCX. if (length.IsConstant()) { __ movl(count, Immediate(length.GetConstant()->AsIntConstant()->GetValue())); @@ -857,12 +884,6 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { __ movl(count, length.AsRegister<CpuRegister>()); } - // Validity checks: source. - CheckPosition(assembler, srcPos, src, count, slow_path, src_base, dest_base); - - // Validity checks: dest. - CheckPosition(assembler, destPos, dest, count, slow_path, src_base, dest_base); - // Okay, everything checks out. Finally time to do the copy. // Check assumption that sizeof(Char) is 2 (used in scaling below). const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); @@ -870,18 +891,18 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); - if (srcPos.IsConstant()) { - int32_t srcPos_const = srcPos.GetConstant()->AsIntConstant()->GetValue(); - __ leal(src_base, Address(src, char_size * srcPos_const + data_offset)); + if (src_pos.IsConstant()) { + int32_t src_pos_const = src_pos.GetConstant()->AsIntConstant()->GetValue(); + __ leal(src_base, Address(src, char_size * src_pos_const + data_offset)); } else { - __ leal(src_base, Address(src, srcPos.AsRegister<CpuRegister>(), + __ leal(src_base, Address(src, src_pos.AsRegister<CpuRegister>(), ScaleFactor::TIMES_2, data_offset)); } - if (destPos.IsConstant()) { - int32_t destPos_const = destPos.GetConstant()->AsIntConstant()->GetValue(); - __ leal(dest_base, Address(dest, char_size * destPos_const + data_offset)); + if (dest_pos.IsConstant()) { + int32_t dest_pos_const = dest_pos.GetConstant()->AsIntConstant()->GetValue(); + __ leal(dest_base, Address(dest, char_size * dest_pos_const + data_offset)); } else { - __ leal(dest_base, Address(dest, destPos.AsRegister<CpuRegister>(), + __ leal(dest_base, Address(dest, dest_pos.AsRegister<CpuRegister>(), ScaleFactor::TIMES_2, data_offset)); } @@ -891,6 +912,231 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } + +void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopy(HInvoke* invoke) { + CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke); +} + +void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) { + X86_64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); + uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); + uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); + + CpuRegister src = locations->InAt(0).AsRegister<CpuRegister>(); + Location src_pos = locations->InAt(1); + CpuRegister dest = locations->InAt(2).AsRegister<CpuRegister>(); + Location dest_pos = locations->InAt(3); + Location length = locations->InAt(4); + CpuRegister temp1 = locations->GetTemp(0).AsRegister<CpuRegister>(); + CpuRegister temp2 = locations->GetTemp(1).AsRegister<CpuRegister>(); + CpuRegister temp3 = locations->GetTemp(2).AsRegister<CpuRegister>(); + + SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke); + codegen_->AddSlowPath(slow_path); + + NearLabel ok; + SystemArrayCopyOptimizations optimizations(invoke); + + if (!optimizations.GetDestinationIsSource()) { + if (!src_pos.IsConstant() || !dest_pos.IsConstant()) { + __ cmpl(src, dest); + } + } + + // If source and destination are the same, we go to slow path if we need to do + // forward copying. + if (src_pos.IsConstant()) { + int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); + if (dest_pos.IsConstant()) { + // Checked when building locations. + DCHECK(!optimizations.GetDestinationIsSource() + || (src_pos_constant >= dest_pos.GetConstant()->AsIntConstant()->GetValue())); + } else { + if (!optimizations.GetDestinationIsSource()) { + __ j(kNotEqual, &ok); + } + __ cmpl(dest_pos.AsRegister<CpuRegister>(), Immediate(src_pos_constant)); + __ j(kGreater, slow_path->GetEntryLabel()); + } + } else { + if (!optimizations.GetDestinationIsSource()) { + __ j(kNotEqual, &ok); + } + if (dest_pos.IsConstant()) { + int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); + __ cmpl(src_pos.AsRegister<CpuRegister>(), Immediate(dest_pos_constant)); + __ j(kLess, slow_path->GetEntryLabel()); + } else { + __ cmpl(src_pos.AsRegister<CpuRegister>(), dest_pos.AsRegister<CpuRegister>()); + __ j(kLess, slow_path->GetEntryLabel()); + } + } + + __ Bind(&ok); + + if (!optimizations.GetSourceIsNotNull()) { + // Bail out if the source is null. + __ testl(src, src); + __ j(kEqual, slow_path->GetEntryLabel()); + } + + if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) { + // Bail out if the destination is null. + __ testl(dest, dest); + __ j(kEqual, slow_path->GetEntryLabel()); + } + + // If the length is negative, bail out. + // We have already checked in the LocationsBuilder for the constant case. + if (!length.IsConstant() && + !optimizations.GetCountIsSourceLength() && + !optimizations.GetCountIsDestinationLength()) { + __ testl(length.AsRegister<CpuRegister>(), length.AsRegister<CpuRegister>()); + __ j(kLess, slow_path->GetEntryLabel()); + } + + // Validity checks: source. + CheckPosition(assembler, + src_pos, + src, + length, + slow_path, + temp1, + temp2, + optimizations.GetCountIsSourceLength()); + + // Validity checks: dest. + CheckPosition(assembler, + dest_pos, + dest, + length, + slow_path, + temp1, + temp2, + optimizations.GetCountIsDestinationLength()); + + if (!optimizations.GetDoesNotNeedTypeCheck()) { + // Check whether all elements of the source array are assignable to the component + // type of the destination array. We do two checks: the classes are the same, + // or the destination is Object[]. If none of these checks succeed, we go to the + // slow path. + __ movl(temp1, Address(dest, class_offset)); + __ movl(temp2, Address(src, class_offset)); + bool did_unpoison = false; + if (!optimizations.GetDestinationIsNonPrimitiveArray() || + !optimizations.GetSourceIsNonPrimitiveArray()) { + // One or two of the references need to be unpoisoned. Unpoisoned them + // both to make the identity check valid. + __ MaybeUnpoisonHeapReference(temp1); + __ MaybeUnpoisonHeapReference(temp2); + did_unpoison = true; + } + + if (!optimizations.GetDestinationIsNonPrimitiveArray()) { + // Bail out if the destination is not a non primitive array. + __ movl(CpuRegister(TMP), Address(temp1, component_offset)); + __ testl(CpuRegister(TMP), CpuRegister(TMP)); + __ j(kEqual, slow_path->GetEntryLabel()); + __ MaybeUnpoisonHeapReference(CpuRegister(TMP)); + __ cmpw(Address(CpuRegister(TMP), primitive_offset), Immediate(Primitive::kPrimNot)); + __ j(kNotEqual, slow_path->GetEntryLabel()); + } + + if (!optimizations.GetSourceIsNonPrimitiveArray()) { + // Bail out if the source is not a non primitive array. + __ movl(CpuRegister(TMP), Address(temp2, component_offset)); + __ testl(CpuRegister(TMP), CpuRegister(TMP)); + __ j(kEqual, slow_path->GetEntryLabel()); + __ MaybeUnpoisonHeapReference(CpuRegister(TMP)); + __ cmpw(Address(CpuRegister(TMP), primitive_offset), Immediate(Primitive::kPrimNot)); + __ j(kNotEqual, slow_path->GetEntryLabel()); + } + + __ cmpl(temp1, temp2); + + if (optimizations.GetDestinationIsTypedObjectArray()) { + NearLabel do_copy; + __ j(kEqual, &do_copy); + if (!did_unpoison) { + __ MaybeUnpoisonHeapReference(temp1); + } + __ movl(temp1, Address(temp1, component_offset)); + __ MaybeUnpoisonHeapReference(temp1); + __ movl(temp1, Address(temp1, super_offset)); + // No need to unpoison the result, we're comparing against null. + __ testl(temp1, temp1); + __ j(kNotEqual, slow_path->GetEntryLabel()); + __ Bind(&do_copy); + } else { + __ j(kNotEqual, slow_path->GetEntryLabel()); + } + } else if (!optimizations.GetSourceIsNonPrimitiveArray()) { + DCHECK(optimizations.GetDestinationIsNonPrimitiveArray()); + // Bail out if the source is not a non primitive array. + __ movl(temp1, Address(src, class_offset)); + __ MaybeUnpoisonHeapReference(temp1); + __ movl(CpuRegister(TMP), Address(temp1, component_offset)); + __ testl(CpuRegister(TMP), CpuRegister(TMP)); + __ j(kEqual, slow_path->GetEntryLabel()); + __ MaybeUnpoisonHeapReference(CpuRegister(TMP)); + __ cmpw(Address(CpuRegister(TMP), primitive_offset), Immediate(Primitive::kPrimNot)); + __ j(kNotEqual, slow_path->GetEntryLabel()); + } + + // Compute base source address, base destination address, and end source address. + + uint32_t element_size = sizeof(int32_t); + uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value(); + if (src_pos.IsConstant()) { + int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); + __ leal(temp1, Address(src, element_size * constant + offset)); + } else { + __ leal(temp1, Address(src, src_pos.AsRegister<CpuRegister>(), ScaleFactor::TIMES_4, offset)); + } + + if (dest_pos.IsConstant()) { + int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); + __ leal(temp2, Address(dest, element_size * constant + offset)); + } else { + __ leal(temp2, Address(dest, dest_pos.AsRegister<CpuRegister>(), ScaleFactor::TIMES_4, offset)); + } + + if (length.IsConstant()) { + int32_t constant = length.GetConstant()->AsIntConstant()->GetValue(); + __ leal(temp3, Address(temp1, element_size * constant)); + } else { + __ leal(temp3, Address(temp1, length.AsRegister<CpuRegister>(), ScaleFactor::TIMES_4, 0)); + } + + // Iterate over the arrays and do a raw copy of the objects. We don't need to + // poison/unpoison, nor do any read barrier as the next uses of the destination + // array will do it. + NearLabel loop, done; + __ cmpl(temp1, temp3); + __ j(kEqual, &done); + __ Bind(&loop); + __ movl(CpuRegister(TMP), Address(temp1, 0)); + __ movl(Address(temp2, 0), CpuRegister(TMP)); + __ addl(temp1, Immediate(element_size)); + __ addl(temp2, Immediate(element_size)); + __ cmpl(temp1, temp3); + __ j(kNotEqual, &loop); + __ Bind(&done); + + // We only need one card marking on the destination array. + codegen_->MarkGCCard(temp1, + temp2, + dest, + CpuRegister(kNoRegister), + false); + + __ Bind(slow_path->GetExitLabel()); +} + void IntrinsicLocationsBuilderX86_64::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 24a89bca4e..ed401b67c5 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -606,8 +606,23 @@ static void UpdateInputsUsers(HInstruction* instruction) { void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial, HInstruction* replacement) { DCHECK(initial->GetBlock() == this); - InsertInstructionBefore(replacement, initial); - initial->ReplaceWith(replacement); + if (initial->IsControlFlow()) { + // We can only replace a control flow instruction with another control flow instruction. + DCHECK(replacement->IsControlFlow()); + DCHECK_EQ(replacement->GetId(), -1); + DCHECK_EQ(replacement->GetType(), Primitive::kPrimVoid); + DCHECK_EQ(initial->GetBlock(), this); + DCHECK_EQ(initial->GetType(), Primitive::kPrimVoid); + DCHECK(initial->GetUses().IsEmpty()); + DCHECK(initial->GetEnvUses().IsEmpty()); + replacement->SetBlock(this); + replacement->SetId(GetGraph()->GetNextInstructionId()); + instructions_.InsertInstructionBefore(replacement, initial); + UpdateInputsUsers(replacement); + } else { + InsertInstructionBefore(replacement, initial); + initial->ReplaceWith(replacement); + } RemoveInstruction(initial); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 22327c6a47..0db1ba2419 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -81,12 +81,19 @@ static constexpr InvokeType kInvalidInvokeType = static_cast<InvokeType>(-1); static constexpr uint32_t kNoDexPc = -1; enum IfCondition { - kCondEQ, - kCondNE, - kCondLT, - kCondLE, - kCondGT, - kCondGE, + // All types. + kCondEQ, // == + kCondNE, // != + // Signed integers and floating-point numbers. + kCondLT, // < + kCondLE, // <= + kCondGT, // > + kCondGE, // >= + // Unsigned integers. + kCondB, // < + kCondBE, // <= + kCondA, // > + kCondAE, // >= }; class HInstructionList : public ValueObject { @@ -988,11 +995,15 @@ class HLoopInformationOutwardIterator : public ValueObject { }; #define FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \ + M(Above, Condition) \ + M(AboveOrEqual, Condition) \ M(Add, BinaryOperation) \ M(And, BinaryOperation) \ M(ArrayGet, Instruction) \ M(ArrayLength, Instruction) \ M(ArraySet, Instruction) \ + M(Below, Condition) \ + M(BelowOrEqual, Condition) \ M(BooleanNot, UnaryOperation) \ M(BoundsCheck, Instruction) \ M(BoundType, Instruction) \ @@ -1074,7 +1085,8 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_X86(M) \ M(X86ComputeBaseMethodAddress, Instruction) \ - M(X86LoadFromConstantTable, Instruction) + M(X86LoadFromConstantTable, Instruction) \ + M(X86PackedSwitch, Instruction) #define FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M) @@ -1637,7 +1649,8 @@ class ReferenceTypeInfo : ValueObject { } bool IsStringClass() const SHARED_REQUIRES(Locks::mutator_lock_) { - return IsValid() && GetTypeHandle()->IsStringClass(); + DCHECK(IsValid()); + return GetTypeHandle()->IsStringClass(); } bool IsObjectArray() const SHARED_REQUIRES(Locks::mutator_lock_) { @@ -1651,15 +1664,36 @@ class ReferenceTypeInfo : ValueObject { } bool IsArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); return GetTypeHandle()->IsArrayClass(); } + bool IsPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsPrimitiveArray(); + } + + bool IsNonPrimitiveArrayClass() const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + return GetTypeHandle()->IsArrayClass() && !GetTypeHandle()->IsPrimitiveArray(); + } + bool CanArrayHold(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); if (!IsExact()) return false; if (!IsArrayClass()) return false; return GetTypeHandle()->GetComponentType()->IsAssignableFrom(rti.GetTypeHandle().Get()); } + bool CanArrayHoldValuesOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(IsValid()); + if (!IsExact()) return false; + if (!IsArrayClass()) return false; + if (!rti.IsArrayClass()) return false; + return GetTypeHandle()->GetComponentType()->IsAssignableFrom( + rti.GetTypeHandle()->GetComponentType()); + } + Handle<mirror::Class> GetTypeHandle() const { return type_handle_; } bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) { @@ -1765,8 +1799,7 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { return true; } - virtual bool CanDoImplicitNullCheckOn(HInstruction* obj) const { - UNUSED(obj); + virtual bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const { return false; } @@ -1883,16 +1916,14 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { virtual bool CanBeMoved() const { return false; } // Returns whether the two instructions are of the same kind. - virtual bool InstructionTypeEquals(HInstruction* other) const { - UNUSED(other); + virtual bool InstructionTypeEquals(HInstruction* other ATTRIBUTE_UNUSED) const { return false; } // Returns whether any data encoded in the two instructions is equal. // This method does not look at the inputs. Both instructions must be // of the same type, otherwise the method has undefined behavior. - virtual bool InstructionDataEquals(HInstruction* other) const { - UNUSED(other); + virtual bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const { return false; } @@ -2455,8 +2486,7 @@ class HUnaryOperation : public HExpression<1> { Primitive::Type GetResultType() const { return GetType(); } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -2526,8 +2556,7 @@ class HBinaryOperation : public HExpression<2> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -2631,8 +2660,6 @@ class HEqual : public HCondition { bool IsCommutative() const OVERRIDE { return true; } - template <typename T> bool Compute(T x, T y) const { return x == y; } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -2653,6 +2680,8 @@ class HEqual : public HCondition { } private: + template <typename T> bool Compute(T x, T y) const { return x == y; } + DISALLOW_COPY_AND_ASSIGN(HEqual); }; @@ -2663,8 +2692,6 @@ class HNotEqual : public HCondition { bool IsCommutative() const OVERRIDE { return true; } - template <typename T> bool Compute(T x, T y) const { return x != y; } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -2685,6 +2712,8 @@ class HNotEqual : public HCondition { } private: + template <typename T> bool Compute(T x, T y) const { return x != y; } + DISALLOW_COPY_AND_ASSIGN(HNotEqual); }; @@ -2693,8 +2722,6 @@ class HLessThan : public HCondition { HLessThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} - template <typename T> bool Compute(T x, T y) const { return x < y; } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -2715,6 +2742,8 @@ class HLessThan : public HCondition { } private: + template <typename T> bool Compute(T x, T y) const { return x < y; } + DISALLOW_COPY_AND_ASSIGN(HLessThan); }; @@ -2723,8 +2752,6 @@ class HLessThanOrEqual : public HCondition { HLessThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} - template <typename T> bool Compute(T x, T y) const { return x <= y; } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -2745,6 +2772,8 @@ class HLessThanOrEqual : public HCondition { } private: + template <typename T> bool Compute(T x, T y) const { return x <= y; } + DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual); }; @@ -2753,8 +2782,6 @@ class HGreaterThan : public HCondition { HGreaterThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} - template <typename T> bool Compute(T x, T y) const { return x > y; } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -2775,6 +2802,8 @@ class HGreaterThan : public HCondition { } private: + template <typename T> bool Compute(T x, T y) const { return x > y; } + DISALLOW_COPY_AND_ASSIGN(HGreaterThan); }; @@ -2783,8 +2812,6 @@ class HGreaterThanOrEqual : public HCondition { HGreaterThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) : HCondition(first, second, dex_pc) {} - template <typename T> bool Compute(T x, T y) const { return x >= y; } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -2805,9 +2832,138 @@ class HGreaterThanOrEqual : public HCondition { } private: + template <typename T> bool Compute(T x, T y) const { return x >= y; } + DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual); }; +class HBelow : public HCondition { + public: + HBelow(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) + : HCondition(first, second, dex_pc) {} + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(static_cast<uint32_t>(x->GetValue()), + static_cast<uint32_t>(y->GetValue())), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(static_cast<uint64_t>(x->GetValue()), + static_cast<uint64_t>(y->GetValue())), GetDexPc()); + } + + DECLARE_INSTRUCTION(Below); + + IfCondition GetCondition() const OVERRIDE { + return kCondB; + } + + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondAE; + } + + private: + template <typename T> bool Compute(T x, T y) const { return x < y; } + + DISALLOW_COPY_AND_ASSIGN(HBelow); +}; + +class HBelowOrEqual : public HCondition { + public: + HBelowOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) + : HCondition(first, second, dex_pc) {} + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(static_cast<uint32_t>(x->GetValue()), + static_cast<uint32_t>(y->GetValue())), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(static_cast<uint64_t>(x->GetValue()), + static_cast<uint64_t>(y->GetValue())), GetDexPc()); + } + + DECLARE_INSTRUCTION(BelowOrEqual); + + IfCondition GetCondition() const OVERRIDE { + return kCondBE; + } + + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondA; + } + + private: + template <typename T> bool Compute(T x, T y) const { return x <= y; } + + DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual); +}; + +class HAbove : public HCondition { + public: + HAbove(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) + : HCondition(first, second, dex_pc) {} + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(static_cast<uint32_t>(x->GetValue()), + static_cast<uint32_t>(y->GetValue())), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(static_cast<uint64_t>(x->GetValue()), + static_cast<uint64_t>(y->GetValue())), GetDexPc()); + } + + DECLARE_INSTRUCTION(Above); + + IfCondition GetCondition() const OVERRIDE { + return kCondA; + } + + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondBE; + } + + private: + template <typename T> bool Compute(T x, T y) const { return x > y; } + + DISALLOW_COPY_AND_ASSIGN(HAbove); +}; + +class HAboveOrEqual : public HCondition { + public: + HAboveOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) + : HCondition(first, second, dex_pc) {} + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(static_cast<uint32_t>(x->GetValue()), + static_cast<uint32_t>(y->GetValue())), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(static_cast<uint64_t>(x->GetValue()), + static_cast<uint64_t>(y->GetValue())), GetDexPc()); + } + + DECLARE_INSTRUCTION(AboveOrEqual); + + IfCondition GetCondition() const OVERRIDE { + return kCondAE; + } + + IfCondition GetOppositeCondition() const OVERRIDE { + return kCondB; + } + + private: + template <typename T> bool Compute(T x, T y) const { return x >= y; } + + DISALLOW_COPY_AND_ASSIGN(HAboveOrEqual); +}; // Instruction to check how two inputs compare to each other. // Result is 0 if input0 == input1, 1 if input0 > input1, or -1 if input0 < input1. @@ -3231,8 +3387,7 @@ class HInvokeStaticOrDirect : public HInvoke { target_method_(target_method), dispatch_info_(dispatch_info) {} - bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { - UNUSED(obj); + bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { // We access the method via the dex cache so we can't do an implicit null check. // TODO: for intrinsics we can generate implicit null checks. return false; @@ -3670,8 +3825,7 @@ class HDivZeroCheck : public HExpression<1> { bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -3948,8 +4102,7 @@ class HNot : public HUnaryOperation { : HUnaryOperation(result_type, input, dex_pc) {} bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -3974,8 +4127,7 @@ class HBooleanNot : public HUnaryOperation { : HUnaryOperation(Primitive::Type::kPrimBoolean, input, dex_pc) {} bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -4141,8 +4293,7 @@ class HNullCheck : public HExpression<1> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -4287,12 +4438,10 @@ class HArrayGet : public HExpression<2> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } - bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { - UNUSED(obj); + bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { // TODO: We can be smarter here. // Currently, the array access is always preceded by an ArrayLength or a NullCheck // which generates the implicit null check. There are cases when these can be removed @@ -4340,8 +4489,7 @@ class HArraySet : public HTemplateInstruction<3> { // Can throw ArrayStoreException. bool CanThrow() const OVERRIDE { return needs_type_check_; } - bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { - UNUSED(obj); + bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { // TODO: Same as for ArrayGet. return false; } @@ -4396,7 +4544,7 @@ class HArraySet : public HTemplateInstruction<3> { class HArrayLength : public HExpression<1> { public: - explicit HArrayLength(HInstruction* array, uint32_t dex_pc) + HArrayLength(HInstruction* array, uint32_t dex_pc) : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) { // Note that arrays do not change length, so the instruction does not // depend on any write. @@ -4404,8 +4552,7 @@ class HArrayLength : public HExpression<1> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { @@ -4428,8 +4575,7 @@ class HBoundsCheck : public HExpression<2> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -4511,6 +4657,9 @@ class HLoadClass : public HExpression<1> { generate_clinit_check_(false), needs_access_check_(needs_access_check), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { + // Referrers class should not need access check. We never inline unverified + // methods so we can't possibly end up in this situation. + DCHECK(!is_referrers_class_ || !needs_access_check_); SetRawInputAt(0, current_method); } @@ -4533,7 +4682,7 @@ class HLoadClass : public HExpression<1> { bool NeedsEnvironment() const OVERRIDE { // Will call runtime and load the class if the class is not loaded yet. // TODO: finer grain decision. - return !is_referrers_class_ || needs_access_check_; + return !is_referrers_class_; } bool MustGenerateClinitCheck() const { @@ -4640,8 +4789,7 @@ class HClinitCheck : public HExpression<1> { } bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - UNUSED(other); + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -5259,7 +5407,7 @@ class HGraphVisitor : public ValueObject { explicit HGraphVisitor(HGraph* graph) : graph_(graph) {} virtual ~HGraphVisitor() {} - virtual void VisitInstruction(HInstruction* instruction) { UNUSED(instruction); } + virtual void VisitInstruction(HInstruction* instruction ATTRIBUTE_UNUSED) {} virtual void VisitBasicBlock(HBasicBlock* block); // Visit the graph following basic block insertion order. diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h index f7cc872419..556217bf74 100644 --- a/compiler/optimizing/nodes_x86.h +++ b/compiler/optimizing/nodes_x86.h @@ -62,6 +62,45 @@ class HX86LoadFromConstantTable : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable); }; +// X86 version of HPackedSwitch that holds a pointer to the base method address. +class HX86PackedSwitch : public HTemplateInstruction<2> { + public: + HX86PackedSwitch(int32_t start_value, + int32_t num_entries, + HInstruction* input, + HX86ComputeBaseMethodAddress* method_base, + uint32_t dex_pc) + : HTemplateInstruction(SideEffects::None(), dex_pc), + start_value_(start_value), + num_entries_(num_entries) { + SetRawInputAt(0, input); + SetRawInputAt(1, method_base); + } + + bool IsControlFlow() const OVERRIDE { return true; } + + int32_t GetStartValue() const { return start_value_; } + + int32_t GetNumEntries() const { return num_entries_; } + + HX86ComputeBaseMethodAddress* GetBaseMethodAddress() const { + return InputAt(1)->AsX86ComputeBaseMethodAddress(); + } + + HBasicBlock* GetDefaultBlock() const { + // Last entry is the default block. + return GetBlock()->GetSuccessors()[num_entries_]; + } + + DECLARE_INSTRUCTION(X86PackedSwitch); + + private: + const int32_t start_value_; + const int32_t num_entries_; + + DISALLOW_COPY_AND_ASSIGN(HX86PackedSwitch); +}; + } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_X86_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b501980dbe..17a4743290 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -169,13 +169,13 @@ class PassObserver : public ValueObject { if (kIsDebugBuild) { if (!graph_in_bad_state_) { if (graph_->IsInSsaForm()) { - SSAChecker checker(graph_->GetArena(), graph_); + SSAChecker checker(graph_); checker.Run(); if (!checker.IsValid()) { LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<SSAChecker>(checker); } } else { - GraphChecker checker(graph_->GetArena(), graph_); + GraphChecker checker(graph_); checker.Run(); if (!checker.IsValid()) { LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<GraphChecker>(checker); @@ -666,7 +666,6 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite jobject class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const { - UNUSED(invoke_type); std::string method_name = PrettyMethod(method_idx, dex_file); MaybeRecordStat(MethodCompilationStat::kAttemptCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index abfbcacef7..26a05da4cb 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -763,7 +763,9 @@ void ReferenceTypePropagation::ProcessWorklist() { while (!worklist_.empty()) { HInstruction* instruction = worklist_.back(); worklist_.pop_back(); - if (UpdateNullability(instruction) || UpdateReferenceTypeInfo(instruction)) { + bool updated_nullability = UpdateNullability(instruction); + bool updated_reference_type = UpdateReferenceTypeInfo(instruction); + if (updated_nullability || updated_reference_type) { AddDependentInstructionsToWorklist(instruction); } } diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index 967b191d32..d59bc6be40 100644 --- a/compiler/utils/arm/assembler_arm.h +++ b/compiler/utils/arm/assembler_arm.h @@ -470,6 +470,13 @@ class ArmAssembler : public Assembler { orr(rd, rn, so, cond, kCcSet); } + virtual void orn(Register rd, Register rn, const ShifterOperand& so, + Condition cond = AL, SetCc set_cc = kCcDontCare) = 0; + + virtual void orns(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL) { + orn(rd, rn, so, cond, kCcSet); + } + virtual void mov(Register rd, const ShifterOperand& so, Condition cond = AL, SetCc set_cc = kCcDontCare) = 0; @@ -832,6 +839,8 @@ class ArmAssembler : public Assembler { uint32_t immediate, ShifterOperand* shifter_op) = 0; + virtual bool ShifterOperandCanAlwaysHold(uint32_t immediate) = 0; + static bool IsInstructionForExceptionHandling(uintptr_t pc); virtual void CompareAndBranchIfZero(Register r, Label* label) = 0; diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index f7772aea3d..6e7c828b4a 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -48,6 +48,11 @@ bool Arm32Assembler::ShifterOperandCanHoldArm32(uint32_t immediate, ShifterOpera return false; } +bool Arm32Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) { + ShifterOperand shifter_op; + return ShifterOperandCanHoldArm32(immediate, &shifter_op); +} + bool Arm32Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED, Register rn ATTRIBUTE_UNUSED, Opcode opcode ATTRIBUTE_UNUSED, @@ -130,6 +135,15 @@ void Arm32Assembler::orr(Register rd, Register rn, const ShifterOperand& so, } +void Arm32Assembler::orn(Register rd ATTRIBUTE_UNUSED, + Register rn ATTRIBUTE_UNUSED, + const ShifterOperand& so ATTRIBUTE_UNUSED, + Condition cond ATTRIBUTE_UNUSED, + SetCc set_cc ATTRIBUTE_UNUSED) { + LOG(FATAL) << "orn is not supported on ARM32"; +} + + void Arm32Assembler::mov(Register rd, const ShifterOperand& so, Condition cond, SetCc set_cc) { EmitType01(cond, so.type(), MOV, set_cc, R0, rd, so); diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h index 3407369654..4646538716 100644 --- a/compiler/utils/arm/assembler_arm32.h +++ b/compiler/utils/arm/assembler_arm32.h @@ -74,6 +74,9 @@ class Arm32Assembler FINAL : public ArmAssembler { virtual void orr(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE; + virtual void orn(Register rd, Register rn, const ShifterOperand& so, + Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE; + virtual void mov(Register rd, const ShifterOperand& so, Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE; @@ -294,6 +297,7 @@ class Arm32Assembler FINAL : public ArmAssembler { uint32_t immediate, ShifterOperand* shifter_op) OVERRIDE; + bool ShifterOperandCanAlwaysHold(uint32_t immediate) OVERRIDE; static bool IsInstructionForExceptionHandling(uintptr_t pc); diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 0f6c4f5a34..cc87856e82 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -390,6 +390,10 @@ void Thumb2Assembler::FinalizeCode() { EmitLiterals(); } +bool Thumb2Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) { + return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate; +} + bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED, Register rn ATTRIBUTE_UNUSED, Opcode opcode, @@ -410,6 +414,7 @@ bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED, case MOV: // TODO: Support less than or equal to 12bits. return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate; + case MVN: default: return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate; @@ -492,6 +497,12 @@ void Thumb2Assembler::orr(Register rd, Register rn, const ShifterOperand& so, } +void Thumb2Assembler::orn(Register rd, Register rn, const ShifterOperand& so, + Condition cond, SetCc set_cc) { + EmitDataProcessing(cond, ORN, set_cc, rn, rd, so); +} + + void Thumb2Assembler::mov(Register rd, const ShifterOperand& so, Condition cond, SetCc set_cc) { EmitDataProcessing(cond, MOV, set_cc, R0, rd, so); @@ -1105,6 +1116,7 @@ bool Thumb2Assembler::Is32BitDataProcessing(Condition cond, rn_is_valid = false; // There is no Rn for these instructions. break; case TEQ: + case ORN: return true; case ADD: case SUB: @@ -1222,6 +1234,7 @@ void Thumb2Assembler::Emit32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED, case MOV: thumb_opcode = 2U /* 0b0010 */; rn = PC; break; case BIC: thumb_opcode = 1U /* 0b0001 */; break; case MVN: thumb_opcode = 3U /* 0b0011 */; rn = PC; break; + case ORN: thumb_opcode = 3U /* 0b0011 */; break; default: break; } diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index a1a8927f44..055b1379ad 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -98,6 +98,9 @@ class Thumb2Assembler FINAL : public ArmAssembler { virtual void orr(Register rd, Register rn, const ShifterOperand& so, Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE; + virtual void orn(Register rd, Register rn, const ShifterOperand& so, + Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE; + virtual void mov(Register rd, const ShifterOperand& so, Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE; @@ -337,6 +340,8 @@ class Thumb2Assembler FINAL : public ArmAssembler { uint32_t immediate, ShifterOperand* shifter_op) OVERRIDE; + bool ShifterOperandCanAlwaysHold(uint32_t immediate) OVERRIDE; + static bool IsInstructionForExceptionHandling(uintptr_t pc); diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h index 6b4daed909..2060064423 100644 --- a/compiler/utils/arm/constants_arm.h +++ b/compiler/utils/arm/constants_arm.h @@ -148,7 +148,8 @@ enum Opcode { MOV = 13, // Move BIC = 14, // Bit Clear MVN = 15, // Move Not - kMaxOperand = 16 + ORN = 16, // Logical OR NOT. + kMaxOperand = 17 }; std::ostream& operator<<(std::ostream& os, const Opcode& rhs); diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc index 496ca95ff9..b01b0fe4e0 100644 --- a/compiler/utils/assembler.cc +++ b/compiler/utils/assembler.cc @@ -122,7 +122,8 @@ void DebugFrameOpCodeWriterForAssembler::ImplicitlyAdvancePC() { this->AdvancePC(assembler_->CodeSize()); } -Assembler* Assembler::Create(InstructionSet instruction_set) { +Assembler* Assembler::Create(InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features) { switch (instruction_set) { #ifdef ART_ENABLE_CODEGEN_arm case kArm: @@ -136,7 +137,9 @@ Assembler* Assembler::Create(InstructionSet instruction_set) { #endif #ifdef ART_ENABLE_CODEGEN_mips case kMips: - return new mips::MipsAssembler(); + return new mips::MipsAssembler(instruction_set_features != nullptr + ? instruction_set_features->AsMipsInstructionSetFeatures() + : nullptr); #endif #ifdef ART_ENABLE_CODEGEN_mips64 case kMips64: diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index 1088cb1bbd..d97a2a40b2 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -20,6 +20,7 @@ #include <vector> #include "arch/instruction_set.h" +#include "arch/instruction_set_features.h" #include "base/logging.h" #include "base/macros.h" #include "arm/constants_arm.h" @@ -284,7 +285,8 @@ class DebugFrameOpCodeWriterForAssembler FINAL class Assembler { public: - static Assembler* Create(InstructionSet instruction_set); + static Assembler* Create(InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features = nullptr); // Finalize the code; emit slow paths, fixup branches, add literal pool, etc. virtual void FinalizeCode() { buffer_.EmitSlowPaths(this); } @@ -299,7 +301,7 @@ class Assembler { } // TODO: Implement with disassembler. - virtual void Comment(const char* format, ...) { UNUSED(format); } + virtual void Comment(const char* format ATTRIBUTE_UNUSED, ...) {} // Emit code that will create an activation on the stack virtual void BuildFrame(size_t frame_size, ManagedRegister method_reg, diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index bd994f46fc..f1233ca457 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -83,6 +83,15 @@ class AssemblerTest : public testing::Test { fmt); } + std::string RepeatRRNoDupes(void (Ass::*f)(Reg, Reg), std::string fmt) { + return RepeatTemplatedRegistersNoDupes<Reg, Reg>(f, + GetRegisters(), + GetRegisters(), + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + fmt); + } + std::string Repeatrr(void (Ass::*f)(Reg, Reg), std::string fmt) { return RepeatTemplatedRegisters<Reg, Reg>(f, GetRegisters(), @@ -129,13 +138,14 @@ class AssemblerTest : public testing::Test { return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt); } - template <typename Reg1Type, typename Reg2Type, typename ImmType, - RegisterView Reg1View, RegisterView Reg2View> - std::string RepeatRegRegImmBits(void (Ass::*f)(Reg1Type, Reg2Type, ImmType), - int imm_bits, - std::string fmt) { - const std::vector<Reg1Type*> reg1_registers = GetRegisters(); - const std::vector<Reg2Type*> reg2_registers = GetRegisters(); + template <typename Reg1, typename Reg2, typename ImmType> + std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, ImmType), + int imm_bits, + const std::vector<Reg1*> reg1_registers, + const std::vector<Reg2*> reg2_registers, + std::string (AssemblerTest::*GetName1)(const Reg1&), + std::string (AssemblerTest::*GetName2)(const Reg2&), + std::string fmt) { std::string str; std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0); @@ -146,13 +156,13 @@ class AssemblerTest : public testing::Test { (assembler_.get()->*f)(*reg1, *reg2, new_imm); std::string base = fmt; - std::string reg1_string = GetRegName<Reg1View>(*reg1); + std::string reg1_string = (this->*GetName1)(*reg1); size_t reg1_index; while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) { base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string); } - std::string reg2_string = GetRegName<Reg2View>(*reg2); + std::string reg2_string = (this->*GetName2)(*reg2); size_t reg2_index; while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) { base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string); @@ -178,15 +188,75 @@ class AssemblerTest : public testing::Test { return str; } - template <typename Reg1Type, typename Reg2Type, typename ImmType> - std::string RepeatRRIb(void (Ass::*f)(Reg1Type, Reg2Type, ImmType), - int imm_bits, - std::string fmt) { - return RepeatRegRegImmBits<Reg1Type, - Reg2Type, - ImmType, - RegisterView::kUsePrimaryName, - RegisterView::kUsePrimaryName>(f, imm_bits, fmt); + template <typename RegType, typename ImmType> + std::string RepeatTemplatedRegisterImmBits(void (Ass::*f)(RegType, ImmType), + int imm_bits, + const std::vector<Reg*> registers, + std::string (AssemblerTest::*GetName)(const RegType&), + std::string fmt) { + std::string str; + std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0); + + for (auto reg : registers) { + for (int64_t imm : imms) { + ImmType new_imm = CreateImmediate(imm); + (assembler_.get()->*f)(*reg, new_imm); + std::string base = fmt; + + std::string reg_string = (this->*GetName)(*reg); + size_t reg_index; + while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) { + base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string); + } + + size_t imm_index = base.find(IMM_TOKEN); + if (imm_index != std::string::npos) { + std::ostringstream sreg; + sreg << imm; + std::string imm_string = sreg.str(); + base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); + } + + if (str.size() > 0) { + str += "\n"; + } + str += base; + } + } + // Add a newline at the end. + str += "\n"; + return str; + } + + template <typename ImmType> + std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), int imm_bits, std::string fmt) { + return RepeatTemplatedRegistersImmBits<Reg, Reg, ImmType>(f, + imm_bits, + GetRegisters(), + GetRegisters(), + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + fmt); + } + + template <typename ImmType> + std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, std::string fmt) { + return RepeatTemplatedRegisterImmBits<Reg, ImmType>(f, + imm_bits, + GetRegisters(), + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + fmt); + } + + template <typename ImmType> + std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType), int imm_bits, std::string fmt) { + return RepeatTemplatedRegistersImmBits<FPReg, Reg, ImmType>(f, + imm_bits, + GetFPRegisters(), + GetRegisters(), + &AssemblerTest::GetFPRegName, + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + fmt); } std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) { @@ -547,6 +617,45 @@ class AssemblerTest : public testing::Test { return str; } + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRegistersNoDupes(void (Ass::*f)(Reg1, Reg2), + const std::vector<Reg1*> reg1_registers, + const std::vector<Reg2*> reg2_registers, + std::string (AssemblerTest::*GetName1)(const Reg1&), + std::string (AssemblerTest::*GetName2)(const Reg2&), + std::string fmt) { + WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); + + std::string str; + for (auto reg1 : reg1_registers) { + for (auto reg2 : reg2_registers) { + if (reg1 == reg2) continue; + (assembler_.get()->*f)(*reg1, *reg2); + std::string base = fmt; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) { + base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string); + } + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) { + base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string); + } + + if (str.size() > 0) { + str += "\n"; + } + str += base; + } + } + // Add a newline at the end. + str += "\n"; + return str; + } + template <typename Reg1, typename Reg2, typename Reg3> std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2, Reg3), const std::vector<Reg1*> reg1_registers, diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index b2a354b63c..2ae88413e7 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -238,6 +238,7 @@ TEST(Thumb2AssemblerTest, DataProcessingRegister) { __ sub(R0, R1, ShifterOperand(R2), AL, kCcKeep); __ and_(R0, R1, ShifterOperand(R2), AL, kCcKeep); __ orr(R0, R1, ShifterOperand(R2), AL, kCcKeep); + __ orn(R0, R1, ShifterOperand(R2), AL, kCcKeep); __ eor(R0, R1, ShifterOperand(R2), AL, kCcKeep); __ bic(R0, R1, ShifterOperand(R2), AL, kCcKeep); __ adc(R0, R1, ShifterOperand(R2), AL, kCcKeep); @@ -371,6 +372,7 @@ TEST(Thumb2AssemblerTest, DataProcessingImmediate) { __ sub(R0, R1, ShifterOperand(0x55)); __ and_(R0, R1, ShifterOperand(0x55)); __ orr(R0, R1, ShifterOperand(0x55)); + __ orn(R0, R1, ShifterOperand(0x55)); __ eor(R0, R1, ShifterOperand(0x55)); __ bic(R0, R1, ShifterOperand(0x55)); __ adc(R0, R1, ShifterOperand(0x55)); @@ -403,6 +405,7 @@ TEST(Thumb2AssemblerTest, DataProcessingModifiedImmediate) { __ sub(R0, R1, ShifterOperand(0x550055)); __ and_(R0, R1, ShifterOperand(0x550055)); __ orr(R0, R1, ShifterOperand(0x550055)); + __ orn(R0, R1, ShifterOperand(0x550055)); __ eor(R0, R1, ShifterOperand(0x550055)); __ bic(R0, R1, ShifterOperand(0x550055)); __ adc(R0, R1, ShifterOperand(0x550055)); diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 82ad6429bf..b79c2e46f0 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -23,109 +23,110 @@ const char* DataProcessingRegisterResults[] = { " 8: eba1 0002 sub.w r0, r1, r2\n", " c: ea01 0002 and.w r0, r1, r2\n", " 10: ea41 0002 orr.w r0, r1, r2\n", - " 14: ea81 0002 eor.w r0, r1, r2\n", - " 18: ea21 0002 bic.w r0, r1, r2\n", - " 1c: eb41 0002 adc.w r0, r1, r2\n", - " 20: eb61 0002 sbc.w r0, r1, r2\n", - " 24: ebc1 0002 rsb r0, r1, r2\n", - " 28: ea90 0f01 teq r0, r1\n", - " 2c: 0008 movs r0, r1\n", - " 2e: 4608 mov r0, r1\n", - " 30: 43c8 mvns r0, r1\n", - " 32: 4408 add r0, r1\n", - " 34: 1888 adds r0, r1, r2\n", - " 36: 1a88 subs r0, r1, r2\n", - " 38: 4148 adcs r0, r1\n", - " 3a: 4188 sbcs r0, r1\n", - " 3c: 4008 ands r0, r1\n", - " 3e: 4308 orrs r0, r1\n", - " 40: 4048 eors r0, r1\n", - " 42: 4388 bics r0, r1\n", - " 44: 4208 tst r0, r1\n", - " 46: 4288 cmp r0, r1\n", - " 48: 42c8 cmn r0, r1\n", - " 4a: 4641 mov r1, r8\n", - " 4c: 4681 mov r9, r0\n", - " 4e: 46c8 mov r8, r9\n", - " 50: 4441 add r1, r8\n", - " 52: 4481 add r9, r0\n", - " 54: 44c8 add r8, r9\n", - " 56: 4548 cmp r0, r9\n", - " 58: 4588 cmp r8, r1\n", - " 5a: 45c1 cmp r9, r8\n", - " 5c: 4248 negs r0, r1\n", - " 5e: 4240 negs r0, r0\n", - " 60: ea5f 0008 movs.w r0, r8\n", - " 64: ea7f 0008 mvns.w r0, r8\n", - " 68: eb01 0008 add.w r0, r1, r8\n", - " 6c: eb11 0008 adds.w r0, r1, r8\n", - " 70: ebb1 0008 subs.w r0, r1, r8\n", - " 74: eb50 0008 adcs.w r0, r0, r8\n", - " 78: eb70 0008 sbcs.w r0, r0, r8\n", - " 7c: ea10 0008 ands.w r0, r0, r8\n", - " 80: ea50 0008 orrs.w r0, r0, r8\n", - " 84: ea90 0008 eors.w r0, r0, r8\n", - " 88: ea30 0008 bics.w r0, r0, r8\n", - " 8c: ea10 0f08 tst.w r0, r8\n", - " 90: eb10 0f08 cmn.w r0, r8\n", - " 94: f1d8 0000 rsbs r0, r8, #0\n", - " 98: f1d8 0800 rsbs r8, r8, #0\n", - " 9c: bf08 it eq\n", - " 9e: ea7f 0001 mvnseq.w r0, r1\n", - " a2: bf08 it eq\n", - " a4: eb11 0002 addseq.w r0, r1, r2\n", - " a8: bf08 it eq\n", - " aa: ebb1 0002 subseq.w r0, r1, r2\n", - " ae: bf08 it eq\n", - " b0: eb50 0001 adcseq.w r0, r0, r1\n", - " b4: bf08 it eq\n", - " b6: eb70 0001 sbcseq.w r0, r0, r1\n", - " ba: bf08 it eq\n", - " bc: ea10 0001 andseq.w r0, r0, r1\n", - " c0: bf08 it eq\n", - " c2: ea50 0001 orrseq.w r0, r0, r1\n", - " c6: bf08 it eq\n", - " c8: ea90 0001 eorseq.w r0, r0, r1\n", - " cc: bf08 it eq\n", - " ce: ea30 0001 bicseq.w r0, r0, r1\n", - " d2: bf08 it eq\n", - " d4: 43c8 mvneq r0, r1\n", + " 14: ea61 0002 orn r0, r1, r2\n", + " 18: ea81 0002 eor.w r0, r1, r2\n", + " 1c: ea21 0002 bic.w r0, r1, r2\n", + " 20: eb41 0002 adc.w r0, r1, r2\n", + " 24: eb61 0002 sbc.w r0, r1, r2\n", + " 28: ebc1 0002 rsb r0, r1, r2\n", + " 2c: ea90 0f01 teq r0, r1\n", + " 30: 0008 movs r0, r1\n", + " 32: 4608 mov r0, r1\n", + " 34: 43c8 mvns r0, r1\n", + " 36: 4408 add r0, r1\n", + " 38: 1888 adds r0, r1, r2\n", + " 3a: 1a88 subs r0, r1, r2\n", + " 3c: 4148 adcs r0, r1\n", + " 3e: 4188 sbcs r0, r1\n", + " 40: 4008 ands r0, r1\n", + " 42: 4308 orrs r0, r1\n", + " 44: 4048 eors r0, r1\n", + " 46: 4388 bics r0, r1\n", + " 48: 4208 tst r0, r1\n", + " 4a: 4288 cmp r0, r1\n", + " 4c: 42c8 cmn r0, r1\n", + " 4e: 4641 mov r1, r8\n", + " 50: 4681 mov r9, r0\n", + " 52: 46c8 mov r8, r9\n", + " 54: 4441 add r1, r8\n", + " 56: 4481 add r9, r0\n", + " 58: 44c8 add r8, r9\n", + " 5a: 4548 cmp r0, r9\n", + " 5c: 4588 cmp r8, r1\n", + " 5e: 45c1 cmp r9, r8\n", + " 60: 4248 negs r0, r1\n", + " 62: 4240 negs r0, r0\n", + " 64: ea5f 0008 movs.w r0, r8\n", + " 68: ea7f 0008 mvns.w r0, r8\n", + " 6c: eb01 0008 add.w r0, r1, r8\n", + " 70: eb11 0008 adds.w r0, r1, r8\n", + " 74: ebb1 0008 subs.w r0, r1, r8\n", + " 78: eb50 0008 adcs.w r0, r0, r8\n", + " 7c: eb70 0008 sbcs.w r0, r0, r8\n", + " 80: ea10 0008 ands.w r0, r0, r8\n", + " 84: ea50 0008 orrs.w r0, r0, r8\n", + " 88: ea90 0008 eors.w r0, r0, r8\n", + " 8c: ea30 0008 bics.w r0, r0, r8\n", + " 90: ea10 0f08 tst.w r0, r8\n", + " 94: eb10 0f08 cmn.w r0, r8\n", + " 98: f1d8 0000 rsbs r0, r8, #0\n", + " 9c: f1d8 0800 rsbs r8, r8, #0\n", + " a0: bf08 it eq\n", + " a2: ea7f 0001 mvnseq.w r0, r1\n", + " a6: bf08 it eq\n", + " a8: eb11 0002 addseq.w r0, r1, r2\n", + " ac: bf08 it eq\n", + " ae: ebb1 0002 subseq.w r0, r1, r2\n", + " b2: bf08 it eq\n", + " b4: eb50 0001 adcseq.w r0, r0, r1\n", + " b8: bf08 it eq\n", + " ba: eb70 0001 sbcseq.w r0, r0, r1\n", + " be: bf08 it eq\n", + " c0: ea10 0001 andseq.w r0, r0, r1\n", + " c4: bf08 it eq\n", + " c6: ea50 0001 orrseq.w r0, r0, r1\n", + " ca: bf08 it eq\n", + " cc: ea90 0001 eorseq.w r0, r0, r1\n", + " d0: bf08 it eq\n", + " d2: ea30 0001 bicseq.w r0, r0, r1\n", " d6: bf08 it eq\n", - " d8: 1888 addeq r0, r1, r2\n", + " d8: 43c8 mvneq r0, r1\n", " da: bf08 it eq\n", - " dc: 1a88 subeq r0, r1, r2\n", + " dc: 1888 addeq r0, r1, r2\n", " de: bf08 it eq\n", - " e0: 4148 adceq r0, r1\n", + " e0: 1a88 subeq r0, r1, r2\n", " e2: bf08 it eq\n", - " e4: 4188 sbceq r0, r1\n", + " e4: 4148 adceq r0, r1\n", " e6: bf08 it eq\n", - " e8: 4008 andeq r0, r1\n", + " e8: 4188 sbceq r0, r1\n", " ea: bf08 it eq\n", - " ec: 4308 orreq r0, r1\n", + " ec: 4008 andeq r0, r1\n", " ee: bf08 it eq\n", - " f0: 4048 eoreq r0, r1\n", + " f0: 4308 orreq r0, r1\n", " f2: bf08 it eq\n", - " f4: 4388 biceq r0, r1\n", - " f6: 4608 mov r0, r1\n", - " f8: 43c8 mvns r0, r1\n", - " fa: 4408 add r0, r1\n", - " fc: 1888 adds r0, r1, r2\n", - " fe: 1a88 subs r0, r1, r2\n", - " 100: 4148 adcs r0, r1\n", - " 102: 4188 sbcs r0, r1\n", - " 104: 4008 ands r0, r1\n", - " 106: 4308 orrs r0, r1\n", - " 108: 4048 eors r0, r1\n", - " 10a: 4388 bics r0, r1\n", - " 10c: 4641 mov r1, r8\n", - " 10e: 4681 mov r9, r0\n", - " 110: 46c8 mov r8, r9\n", - " 112: 4441 add r1, r8\n", - " 114: 4481 add r9, r0\n", - " 116: 44c8 add r8, r9\n", - " 118: 4248 negs r0, r1\n", - " 11a: 4240 negs r0, r0\n", - " 11c: eb01 0c00 add.w ip, r1, r0\n", + " f4: 4048 eoreq r0, r1\n", + " f6: bf08 it eq\n", + " f8: 4388 biceq r0, r1\n", + " fa: 4608 mov r0, r1\n", + " fc: 43c8 mvns r0, r1\n", + " fe: 4408 add r0, r1\n", + " 100: 1888 adds r0, r1, r2\n", + " 102: 1a88 subs r0, r1, r2\n", + " 104: 4148 adcs r0, r1\n", + " 106: 4188 sbcs r0, r1\n", + " 108: 4008 ands r0, r1\n", + " 10a: 4308 orrs r0, r1\n", + " 10c: 4048 eors r0, r1\n", + " 10e: 4388 bics r0, r1\n", + " 110: 4641 mov r1, r8\n", + " 112: 4681 mov r9, r0\n", + " 114: 46c8 mov r8, r9\n", + " 116: 4441 add r1, r8\n", + " 118: 4481 add r9, r0\n", + " 11a: 44c8 add r8, r9\n", + " 11c: 4248 negs r0, r1\n", + " 11e: 4240 negs r0, r0\n", + " 120: eb01 0c00 add.w ip, r1, r0\n", nullptr }; const char* DataProcessingImmediateResults[] = { @@ -135,21 +136,22 @@ const char* DataProcessingImmediateResults[] = { " a: f2a1 0055 subw r0, r1, #85 ; 0x55\n", " e: f001 0055 and.w r0, r1, #85 ; 0x55\n", " 12: f041 0055 orr.w r0, r1, #85 ; 0x55\n", - " 16: f081 0055 eor.w r0, r1, #85 ; 0x55\n", - " 1a: f021 0055 bic.w r0, r1, #85 ; 0x55\n", - " 1e: f141 0055 adc.w r0, r1, #85 ; 0x55\n", - " 22: f161 0055 sbc.w r0, r1, #85 ; 0x55\n", - " 26: f1c1 0055 rsb r0, r1, #85 ; 0x55\n", - " 2a: f010 0f55 tst.w r0, #85 ; 0x55\n", - " 2e: f090 0f55 teq r0, #85 ; 0x55\n", - " 32: 2855 cmp r0, #85 ; 0x55\n", - " 34: f110 0f55 cmn.w r0, #85 ; 0x55\n", - " 38: 1d48 adds r0, r1, #5\n", - " 3a: 1f48 subs r0, r1, #5\n", - " 3c: 2055 movs r0, #85 ; 0x55\n", - " 3e: f07f 0055 mvns.w r0, #85 ; 0x55\n", - " 42: 1d48 adds r0, r1, #5\n", - " 44: 1f48 subs r0, r1, #5\n", + " 16: f061 0055 orn r0, r1, #85 ; 0x55\n", + " 1a: f081 0055 eor.w r0, r1, #85 ; 0x55\n", + " 1e: f021 0055 bic.w r0, r1, #85 ; 0x55\n", + " 22: f141 0055 adc.w r0, r1, #85 ; 0x55\n", + " 26: f161 0055 sbc.w r0, r1, #85 ; 0x55\n", + " 2a: f1c1 0055 rsb r0, r1, #85 ; 0x55\n", + " 2e: f010 0f55 tst.w r0, #85 ; 0x55\n", + " 32: f090 0f55 teq r0, #85 ; 0x55\n", + " 36: 2855 cmp r0, #85 ; 0x55\n", + " 38: f110 0f55 cmn.w r0, #85 ; 0x55\n", + " 3c: 1d48 adds r0, r1, #5\n", + " 3e: 1f48 subs r0, r1, #5\n", + " 40: 2055 movs r0, #85 ; 0x55\n", + " 42: f07f 0055 mvns.w r0, #85 ; 0x55\n", + " 46: 1d48 adds r0, r1, #5\n", + " 48: 1f48 subs r0, r1, #5\n", nullptr }; const char* DataProcessingModifiedImmediateResults[] = { @@ -159,15 +161,16 @@ const char* DataProcessingModifiedImmediateResults[] = { " c: f1a1 1055 sub.w r0, r1, #5570645 ; 0x550055\n", " 10: f001 1055 and.w r0, r1, #5570645 ; 0x550055\n", " 14: f041 1055 orr.w r0, r1, #5570645 ; 0x550055\n", - " 18: f081 1055 eor.w r0, r1, #5570645 ; 0x550055\n", - " 1c: f021 1055 bic.w r0, r1, #5570645 ; 0x550055\n", - " 20: f141 1055 adc.w r0, r1, #5570645 ; 0x550055\n", - " 24: f161 1055 sbc.w r0, r1, #5570645 ; 0x550055\n", - " 28: f1c1 1055 rsb r0, r1, #5570645 ; 0x550055\n", - " 2c: f010 1f55 tst.w r0, #5570645 ; 0x550055\n", - " 30: f090 1f55 teq r0, #5570645 ; 0x550055\n", - " 34: f1b0 1f55 cmp.w r0, #5570645 ; 0x550055\n", - " 38: f110 1f55 cmn.w r0, #5570645 ; 0x550055\n", + " 18: f061 1055 orn r0, r1, #5570645 ; 0x550055\n", + " 1c: f081 1055 eor.w r0, r1, #5570645 ; 0x550055\n", + " 20: f021 1055 bic.w r0, r1, #5570645 ; 0x550055\n", + " 24: f141 1055 adc.w r0, r1, #5570645 ; 0x550055\n", + " 28: f161 1055 sbc.w r0, r1, #5570645 ; 0x550055\n", + " 2c: f1c1 1055 rsb r0, r1, #5570645 ; 0x550055\n", + " 30: f010 1f55 tst.w r0, #5570645 ; 0x550055\n", + " 34: f090 1f55 teq r0, #5570645 ; 0x550055\n", + " 38: f1b0 1f55 cmp.w r0, #5570645 ; 0x550055\n", + " 3c: f110 1f55 cmn.w r0, #5570645 ; 0x550055\n", nullptr }; const char* DataProcessingModifiedImmediatesResults[] = { diff --git a/compiler/utils/label.h b/compiler/utils/label.h index ff4a1a4333..1038f44ffe 100644 --- a/compiler/utils/label.h +++ b/compiler/utils/label.h @@ -70,6 +70,13 @@ class Label { public: Label() : position_(0) {} + Label(Label&& src) + : position_(src.position_) { + // We must unlink/unbind the src label when moving; if not, calling the destructor on + // the src label would fail. + src.position_ = 0; + } + ~Label() { // Assert if label is being destroyed with unresolved branches pending. CHECK(!IsLinked()); diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index c5fae92f3c..6f35e9ef59 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -19,6 +19,7 @@ #include "base/bit_utils.h" #include "base/casts.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "entrypoints/quick/quick_entrypoints_enum.h" #include "memory_region.h" #include "thread.h" @@ -34,170 +35,191 @@ std::ostream& operator<<(std::ostream& os, const DRegister& rhs) { return os; } -void MipsAssembler::Emit(int32_t value) { - AssemblerBuffer::EnsureCapacity ensured(&buffer_); - buffer_.Emit<int32_t>(value); +void MipsAssembler::FinalizeCode() { + for (auto& exception_block : exception_blocks_) { + EmitExceptionPoll(&exception_block); + } + PromoteBranches(); +} + +void MipsAssembler::FinalizeInstructions(const MemoryRegion& region) { + EmitBranches(); + Assembler::FinalizeInstructions(region); +} + +void MipsAssembler::EmitBranches() { + CHECK(!overwriting_); + // Switch from appending instructions at the end of the buffer to overwriting + // existing instructions (branch placeholders) in the buffer. + overwriting_ = true; + for (auto& branch : branches_) { + EmitBranch(&branch); + } + overwriting_ = false; +} + +void MipsAssembler::Emit(uint32_t value) { + if (overwriting_) { + // Branches to labels are emitted into their placeholders here. + buffer_.Store<uint32_t>(overwrite_location_, value); + overwrite_location_ += sizeof(uint32_t); + } else { + // Other instructions are simply appended at the end here. + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + buffer_.Emit<uint32_t>(value); + } } void MipsAssembler::EmitR(int opcode, Register rs, Register rt, Register rd, int shamt, int funct) { CHECK_NE(rs, kNoRegister); CHECK_NE(rt, kNoRegister); CHECK_NE(rd, kNoRegister); - int32_t encoding = opcode << kOpcodeShift | - static_cast<int32_t>(rs) << kRsShift | - static_cast<int32_t>(rt) << kRtShift | - static_cast<int32_t>(rd) << kRdShift | - shamt << kShamtShift | - funct; + uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | + static_cast<uint32_t>(rs) << kRsShift | + static_cast<uint32_t>(rt) << kRtShift | + static_cast<uint32_t>(rd) << kRdShift | + shamt << kShamtShift | + funct; Emit(encoding); } void MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) { CHECK_NE(rs, kNoRegister); CHECK_NE(rt, kNoRegister); - int32_t encoding = opcode << kOpcodeShift | - static_cast<int32_t>(rs) << kRsShift | - static_cast<int32_t>(rt) << kRtShift | - imm; + uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | + static_cast<uint32_t>(rs) << kRsShift | + static_cast<uint32_t>(rt) << kRtShift | + imm; + Emit(encoding); +} + +void MipsAssembler::EmitI21(int opcode, Register rs, uint32_t imm21) { + CHECK_NE(rs, kNoRegister); + CHECK(IsUint<21>(imm21)) << imm21; + uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | + static_cast<uint32_t>(rs) << kRsShift | + imm21; Emit(encoding); } -void MipsAssembler::EmitJ(int opcode, int address) { - int32_t encoding = opcode << kOpcodeShift | - address; +void MipsAssembler::EmitI26(int opcode, uint32_t imm26) { + CHECK(IsUint<26>(imm26)) << imm26; + uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | imm26; Emit(encoding); } -void MipsAssembler::EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd, int funct) { +void MipsAssembler::EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd, + int funct) { CHECK_NE(ft, kNoFRegister); CHECK_NE(fs, kNoFRegister); CHECK_NE(fd, kNoFRegister); - int32_t encoding = opcode << kOpcodeShift | - fmt << kFmtShift | - static_cast<int32_t>(ft) << kFtShift | - static_cast<int32_t>(fs) << kFsShift | - static_cast<int32_t>(fd) << kFdShift | - funct; + uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | + fmt << kFmtShift | + static_cast<uint32_t>(ft) << kFtShift | + static_cast<uint32_t>(fs) << kFsShift | + static_cast<uint32_t>(fd) << kFdShift | + funct; Emit(encoding); } -void MipsAssembler::EmitFI(int opcode, int fmt, FRegister rt, uint16_t imm) { - CHECK_NE(rt, kNoFRegister); - int32_t encoding = opcode << kOpcodeShift | - fmt << kFmtShift | - static_cast<int32_t>(rt) << kRtShift | - imm; +void MipsAssembler::EmitFI(int opcode, int fmt, FRegister ft, uint16_t imm) { + CHECK_NE(ft, kNoFRegister); + uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | + fmt << kFmtShift | + static_cast<uint32_t>(ft) << kFtShift | + imm; Emit(encoding); } -void MipsAssembler::EmitBranch(Register rt, Register rs, Label* label, bool equal) { - int offset; - if (label->IsBound()) { - offset = label->Position() - buffer_.Size(); - } else { - // Use the offset field of the branch instruction for linking the sites. - offset = label->position_; - label->LinkTo(buffer_.Size()); - } - if (equal) { - Beq(rt, rs, (offset >> 2) & kBranchOffsetMask); - } else { - Bne(rt, rs, (offset >> 2) & kBranchOffsetMask); - } +void MipsAssembler::Addu(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x21); } -void MipsAssembler::EmitJump(Label* label, bool link) { - int offset; - if (label->IsBound()) { - offset = label->Position() - buffer_.Size(); - } else { - // Use the offset field of the jump instruction for linking the sites. - offset = label->position_; - label->LinkTo(buffer_.Size()); - } - if (link) { - Jal((offset >> 2) & kJumpOffsetMask); - } else { - J((offset >> 2) & kJumpOffsetMask); - } +void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) { + EmitI(0x9, rs, rt, imm16); } -int32_t MipsAssembler::EncodeBranchOffset(int offset, int32_t inst, bool is_jump) { - CHECK_ALIGNED(offset, 4); - CHECK(IsInt(POPCOUNT(kBranchOffsetMask), offset)) << offset; +void MipsAssembler::Subu(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x23); +} - // Properly preserve only the bits supported in the instruction. - offset >>= 2; - if (is_jump) { - offset &= kJumpOffsetMask; - return (inst & ~kJumpOffsetMask) | offset; - } else { - offset &= kBranchOffsetMask; - return (inst & ~kBranchOffsetMask) | offset; - } +void MipsAssembler::MultR2(Register rs, Register rt) { + CHECK(!IsR6()); + EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18); } -int MipsAssembler::DecodeBranchOffset(int32_t inst, bool is_jump) { - // Sign-extend, then left-shift by 2. - if (is_jump) { - return (((inst & kJumpOffsetMask) << 6) >> 4); - } else { - return (((inst & kBranchOffsetMask) << 16) >> 14); - } +void MipsAssembler::MultuR2(Register rs, Register rt) { + CHECK(!IsR6()); + EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19); } -void MipsAssembler::Bind(Label* label, bool is_jump) { - CHECK(!label->IsBound()); - int bound_pc = buffer_.Size(); - while (label->IsLinked()) { - int32_t position = label->Position(); - int32_t next = buffer_.Load<int32_t>(position); - int32_t offset = is_jump ? bound_pc - position : bound_pc - position - 4; - int32_t encoded = MipsAssembler::EncodeBranchOffset(offset, next, is_jump); - buffer_.Store<int32_t>(position, encoded); - label->position_ = MipsAssembler::DecodeBranchOffset(next, is_jump); - } - label->BindTo(bound_pc); +void MipsAssembler::DivR2(Register rs, Register rt) { + CHECK(!IsR6()); + EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a); } -void MipsAssembler::Add(Register rd, Register rs, Register rt) { - EmitR(0, rs, rt, rd, 0, 0x20); +void MipsAssembler::DivuR2(Register rs, Register rt) { + CHECK(!IsR6()); + EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b); } -void MipsAssembler::Addu(Register rd, Register rs, Register rt) { - EmitR(0, rs, rt, rd, 0, 0x21); +void MipsAssembler::MulR2(Register rd, Register rs, Register rt) { + CHECK(!IsR6()); + EmitR(0x1c, rs, rt, rd, 0, 2); } -void MipsAssembler::Addi(Register rt, Register rs, uint16_t imm16) { - EmitI(0x8, rs, rt, imm16); +void MipsAssembler::DivR2(Register rd, Register rs, Register rt) { + CHECK(!IsR6()); + DivR2(rs, rt); + Mflo(rd); } -void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) { - EmitI(0x9, rs, rt, imm16); +void MipsAssembler::ModR2(Register rd, Register rs, Register rt) { + CHECK(!IsR6()); + DivR2(rs, rt); + Mfhi(rd); } -void MipsAssembler::Sub(Register rd, Register rs, Register rt) { - EmitR(0, rs, rt, rd, 0, 0x22); +void MipsAssembler::DivuR2(Register rd, Register rs, Register rt) { + CHECK(!IsR6()); + DivuR2(rs, rt); + Mflo(rd); } -void MipsAssembler::Subu(Register rd, Register rs, Register rt) { - EmitR(0, rs, rt, rd, 0, 0x23); +void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) { + CHECK(!IsR6()); + DivuR2(rs, rt); + Mfhi(rd); } -void MipsAssembler::Mult(Register rs, Register rt) { - EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18); +void MipsAssembler::MulR6(Register rd, Register rs, Register rt) { + CHECK(IsR6()); + EmitR(0, rs, rt, rd, 2, 0x18); } -void MipsAssembler::Multu(Register rs, Register rt) { - EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19); +void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) { + CHECK(IsR6()); + EmitR(0, rs, rt, rd, 3, 0x19); } -void MipsAssembler::Div(Register rs, Register rt) { - EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a); +void MipsAssembler::DivR6(Register rd, Register rs, Register rt) { + CHECK(IsR6()); + EmitR(0, rs, rt, rd, 2, 0x1a); } -void MipsAssembler::Divu(Register rs, Register rt) { - EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b); +void MipsAssembler::ModR6(Register rd, Register rs, Register rt) { + CHECK(IsR6()); + EmitR(0, rs, rt, rd, 3, 0x1a); +} + +void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) { + CHECK(IsR6()); + EmitR(0, rs, rt, rd, 2, 0x1b); +} + +void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) { + CHECK(IsR6()); + EmitR(0, rs, rt, rd, 3, 0x1b); } void MipsAssembler::And(Register rd, Register rs, Register rt) { @@ -228,27 +250,35 @@ void MipsAssembler::Nor(Register rd, Register rs, Register rt) { EmitR(0, rs, rt, rd, 0, 0x27); } -void MipsAssembler::Sll(Register rd, Register rs, int shamt) { - EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x00); +void MipsAssembler::Seb(Register rd, Register rt) { + EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x10, 0x20); } -void MipsAssembler::Srl(Register rd, Register rs, int shamt) { - EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x02); +void MipsAssembler::Seh(Register rd, Register rt) { + EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20); } -void MipsAssembler::Sra(Register rd, Register rs, int shamt) { - EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x03); +void MipsAssembler::Sll(Register rd, Register rt, int shamt) { + EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x00); } -void MipsAssembler::Sllv(Register rd, Register rs, Register rt) { +void MipsAssembler::Srl(Register rd, Register rt, int shamt) { + EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x02); +} + +void MipsAssembler::Sra(Register rd, Register rt, int shamt) { + EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x03); +} + +void MipsAssembler::Sllv(Register rd, Register rt, Register rs) { EmitR(0, rs, rt, rd, 0, 0x04); } -void MipsAssembler::Srlv(Register rd, Register rs, Register rt) { +void MipsAssembler::Srlv(Register rd, Register rt, Register rs) { EmitR(0, rs, rt, rd, 0, 0x06); } -void MipsAssembler::Srav(Register rd, Register rs, Register rt) { +void MipsAssembler::Srav(Register rd, Register rt, Register rs) { EmitR(0, rs, rt, rd, 0, 0x07); } @@ -276,11 +306,18 @@ void MipsAssembler::Lui(Register rt, uint16_t imm16) { EmitI(0xf, static_cast<Register>(0), rt, imm16); } +void MipsAssembler::Sync(uint32_t stype) { + EmitR(0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0), + stype & 0x1f, 0xf); +} + void MipsAssembler::Mfhi(Register rd) { + CHECK(!IsR6()); EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x10); } void MipsAssembler::Mflo(Register rd) { + CHECK(!IsR6()); EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x12); } @@ -312,34 +349,276 @@ void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) { EmitI(0xb, rs, rt, imm16); } -void MipsAssembler::Beq(Register rt, Register rs, uint16_t imm16) { +void MipsAssembler::B(uint16_t imm16) { + EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16); +} + +void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) { EmitI(0x4, rs, rt, imm16); - Nop(); } -void MipsAssembler::Bne(Register rt, Register rs, uint16_t imm16) { +void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) { EmitI(0x5, rs, rt, imm16); - Nop(); } -void MipsAssembler::J(uint32_t address) { - EmitJ(0x2, address); - Nop(); +void MipsAssembler::Beqz(Register rt, uint16_t imm16) { + Beq(ZERO, rt, imm16); } -void MipsAssembler::Jal(uint32_t address) { - EmitJ(0x2, address); - Nop(); +void MipsAssembler::Bnez(Register rt, uint16_t imm16) { + Bne(ZERO, rt, imm16); } -void MipsAssembler::Jr(Register rs) { - EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x09); // Jalr zero, rs - Nop(); +void MipsAssembler::Bltz(Register rt, uint16_t imm16) { + EmitI(0x1, rt, static_cast<Register>(0), imm16); +} + +void MipsAssembler::Bgez(Register rt, uint16_t imm16) { + EmitI(0x1, rt, static_cast<Register>(0x1), imm16); +} + +void MipsAssembler::Blez(Register rt, uint16_t imm16) { + EmitI(0x6, rt, static_cast<Register>(0), imm16); +} + +void MipsAssembler::Bgtz(Register rt, uint16_t imm16) { + EmitI(0x7, rt, static_cast<Register>(0), imm16); +} + +void MipsAssembler::J(uint32_t addr26) { + EmitI26(0x2, addr26); +} + +void MipsAssembler::Jal(uint32_t addr26) { + EmitI26(0x3, addr26); +} + +void MipsAssembler::Jalr(Register rd, Register rs) { + EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09); } void MipsAssembler::Jalr(Register rs) { - EmitR(0, rs, static_cast<Register>(0), RA, 0, 0x09); - Nop(); + Jalr(RA, rs); +} + +void MipsAssembler::Jr(Register rs) { + Jalr(ZERO, rs); +} + +void MipsAssembler::Nal() { + EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0); +} + +void MipsAssembler::Auipc(Register rs, uint16_t imm16) { + CHECK(IsR6()); + EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16); +} + +void MipsAssembler::Addiupc(Register rs, uint32_t imm19) { + CHECK(IsR6()); + CHECK(IsUint<19>(imm19)) << imm19; + EmitI21(0x3B, rs, imm19); +} + +void MipsAssembler::Bc(uint32_t imm26) { + CHECK(IsR6()); + EmitI26(0x32, imm26); +} + +void MipsAssembler::Jic(Register rt, uint16_t imm16) { + CHECK(IsR6()); + EmitI(0x36, static_cast<Register>(0), rt, imm16); +} + +void MipsAssembler::Jialc(Register rt, uint16_t imm16) { + CHECK(IsR6()); + EmitI(0x3E, static_cast<Register>(0), rt, imm16); +} + +void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rs, ZERO); + CHECK_NE(rt, ZERO); + CHECK_NE(rs, rt); + EmitI(0x17, rs, rt, imm16); +} + +void MipsAssembler::Bltzc(Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rt, ZERO); + EmitI(0x17, rt, rt, imm16); +} + +void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rt, ZERO); + EmitI(0x17, static_cast<Register>(0), rt, imm16); +} + +void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rs, ZERO); + CHECK_NE(rt, ZERO); + CHECK_NE(rs, rt); + EmitI(0x16, rs, rt, imm16); +} + +void MipsAssembler::Bgezc(Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rt, ZERO); + EmitI(0x16, rt, rt, imm16); +} + +void MipsAssembler::Blezc(Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rt, ZERO); + EmitI(0x16, static_cast<Register>(0), rt, imm16); +} + +void MipsAssembler::Bltuc(Register rs, Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rs, ZERO); + CHECK_NE(rt, ZERO); + CHECK_NE(rs, rt); + EmitI(0x7, rs, rt, imm16); +} + +void MipsAssembler::Bgeuc(Register rs, Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rs, ZERO); + CHECK_NE(rt, ZERO); + CHECK_NE(rs, rt); + EmitI(0x6, rs, rt, imm16); +} + +void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rs, ZERO); + CHECK_NE(rt, ZERO); + CHECK_NE(rs, rt); + EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16); +} + +void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) { + CHECK(IsR6()); + CHECK_NE(rs, ZERO); + CHECK_NE(rt, ZERO); + CHECK_NE(rs, rt); + EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16); +} + +void MipsAssembler::Beqzc(Register rs, uint32_t imm21) { + CHECK(IsR6()); + CHECK_NE(rs, ZERO); + EmitI21(0x36, rs, imm21); +} + +void MipsAssembler::Bnezc(Register rs, uint32_t imm21) { + CHECK(IsR6()); + CHECK_NE(rs, ZERO); + EmitI21(0x3E, rs, imm21); +} + +void MipsAssembler::EmitBcond(BranchCondition cond, Register rs, Register rt, uint16_t imm16) { + switch (cond) { + case kCondLTZ: + CHECK_EQ(rt, ZERO); + Bltz(rs, imm16); + break; + case kCondGEZ: + CHECK_EQ(rt, ZERO); + Bgez(rs, imm16); + break; + case kCondLEZ: + CHECK_EQ(rt, ZERO); + Blez(rs, imm16); + break; + case kCondGTZ: + CHECK_EQ(rt, ZERO); + Bgtz(rs, imm16); + break; + case kCondEQ: + Beq(rs, rt, imm16); + break; + case kCondNE: + Bne(rs, rt, imm16); + break; + case kCondEQZ: + CHECK_EQ(rt, ZERO); + Beqz(rs, imm16); + break; + case kCondNEZ: + CHECK_EQ(rt, ZERO); + Bnez(rs, imm16); + break; + case kCondLT: + case kCondGE: + case kCondLE: + case kCondGT: + case kCondLTU: + case kCondGEU: + case kUncond: + // We don't support synthetic R2 branches (preceded with slt[u]) at this level + // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >). + LOG(FATAL) << "Unexpected branch condition " << cond; + UNREACHABLE(); + } +} + +void MipsAssembler::EmitBcondc(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) { + switch (cond) { + case kCondLT: + Bltc(rs, rt, imm16_21); + break; + case kCondGE: + Bgec(rs, rt, imm16_21); + break; + case kCondLE: + Bgec(rt, rs, imm16_21); + break; + case kCondGT: + Bltc(rt, rs, imm16_21); + break; + case kCondLTZ: + CHECK_EQ(rt, ZERO); + Bltzc(rs, imm16_21); + break; + case kCondGEZ: + CHECK_EQ(rt, ZERO); + Bgezc(rs, imm16_21); + break; + case kCondLEZ: + CHECK_EQ(rt, ZERO); + Blezc(rs, imm16_21); + break; + case kCondGTZ: + CHECK_EQ(rt, ZERO); + Bgtzc(rs, imm16_21); + break; + case kCondEQ: + Beqc(rs, rt, imm16_21); + break; + case kCondNE: + Bnec(rs, rt, imm16_21); + break; + case kCondEQZ: + CHECK_EQ(rt, ZERO); + Beqzc(rs, imm16_21); + break; + case kCondNEZ: + CHECK_EQ(rt, ZERO); + Bnezc(rs, imm16_21); + break; + case kCondLTU: + Bltuc(rs, rt, imm16_21); + break; + case kCondGEU: + Bgeuc(rs, rt, imm16_21); + break; + case kUncond: + LOG(FATAL) << "Unexpected branch condition " << cond; + UNREACHABLE(); + } } void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) { @@ -358,52 +637,84 @@ void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) { EmitFR(0x11, 0x10, ft, fs, fd, 0x3); } -void MipsAssembler::AddD(DRegister fd, DRegister fs, DRegister ft) { - EmitFR(0x11, 0x11, ConvertDRegToFReg(ft), ConvertDRegToFReg(fs), ConvertDRegToFReg(fd), 0x0); +void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) { + EmitFR(0x11, 0x11, ft, fs, fd, 0x0); } -void MipsAssembler::SubD(DRegister fd, DRegister fs, DRegister ft) { - EmitFR(0x11, 0x11, ConvertDRegToFReg(ft), ConvertDRegToFReg(fs), ConvertDRegToFReg(fd), 0x1); +void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) { + EmitFR(0x11, 0x11, ft, fs, fd, 0x1); } -void MipsAssembler::MulD(DRegister fd, DRegister fs, DRegister ft) { - EmitFR(0x11, 0x11, ConvertDRegToFReg(ft), ConvertDRegToFReg(fs), ConvertDRegToFReg(fd), 0x2); +void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) { + EmitFR(0x11, 0x11, ft, fs, fd, 0x2); } -void MipsAssembler::DivD(DRegister fd, DRegister fs, DRegister ft) { - EmitFR(0x11, 0x11, ConvertDRegToFReg(ft), ConvertDRegToFReg(fs), ConvertDRegToFReg(fd), 0x3); +void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) { + EmitFR(0x11, 0x11, ft, fs, fd, 0x3); } void MipsAssembler::MovS(FRegister fd, FRegister fs) { EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6); } -void MipsAssembler::MovD(DRegister fd, DRegister fs) { - EmitFR(0x11, 0x11, static_cast<FRegister>(0), ConvertDRegToFReg(fs), ConvertDRegToFReg(fd), 0x6); +void MipsAssembler::MovD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6); +} + +void MipsAssembler::NegS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7); +} + +void MipsAssembler::NegD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7); +} + +void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20); +} + +void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21); +} + +void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20); +} + +void MipsAssembler::Cvtds(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21); } void MipsAssembler::Mfc1(Register rt, FRegister fs) { - EmitFR(0x11, 0x00, ConvertRegToFReg(rt), fs, static_cast<FRegister>(0), 0x0); + EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0); +} + +void MipsAssembler::Mtc1(Register rt, FRegister fs) { + EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0); } -void MipsAssembler::Mtc1(FRegister ft, Register rs) { - EmitFR(0x11, 0x04, ft, ConvertRegToFReg(rs), static_cast<FRegister>(0), 0x0); +void MipsAssembler::Mfhc1(Register rt, FRegister fs) { + EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0); +} + +void MipsAssembler::Mthc1(Register rt, FRegister fs) { + EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0); } void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) { - EmitI(0x31, rs, ConvertFRegToReg(ft), imm16); + EmitI(0x31, rs, static_cast<Register>(ft), imm16); } -void MipsAssembler::Ldc1(DRegister ft, Register rs, uint16_t imm16) { - EmitI(0x35, rs, ConvertDRegToReg(ft), imm16); +void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) { + EmitI(0x35, rs, static_cast<Register>(ft), imm16); } void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) { - EmitI(0x39, rs, ConvertFRegToReg(ft), imm16); + EmitI(0x39, rs, static_cast<Register>(ft), imm16); } -void MipsAssembler::Sdc1(DRegister ft, Register rs, uint16_t imm16) { - EmitI(0x3d, rs, ConvertDRegToReg(ft), imm16); +void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) { + EmitI(0x3d, rs, static_cast<Register>(ft), imm16); } void MipsAssembler::Break() { @@ -415,63 +726,881 @@ void MipsAssembler::Nop() { EmitR(0x0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0), 0, 0x0); } -void MipsAssembler::Move(Register rt, Register rs) { - EmitI(0x9, rs, rt, 0); // Addiu +void MipsAssembler::Move(Register rd, Register rs) { + Or(rd, rs, ZERO); } -void MipsAssembler::Clear(Register rt) { - EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rt, 0, 0x20); +void MipsAssembler::Clear(Register rd) { + Move(rd, ZERO); } -void MipsAssembler::Not(Register rt, Register rs) { - EmitR(0, static_cast<Register>(0), rs, rt, 0, 0x27); +void MipsAssembler::Not(Register rd, Register rs) { + Nor(rd, rs, ZERO); } -void MipsAssembler::Mul(Register rd, Register rs, Register rt) { - Mult(rs, rt); - Mflo(rd); +void MipsAssembler::Push(Register rs) { + IncreaseFrameSize(kMipsWordSize); + Sw(rs, SP, 0); } -void MipsAssembler::Div(Register rd, Register rs, Register rt) { - Div(rs, rt); - Mflo(rd); +void MipsAssembler::Pop(Register rd) { + Lw(rd, SP, 0); + DecreaseFrameSize(kMipsWordSize); } -void MipsAssembler::Rem(Register rd, Register rs, Register rt) { - Div(rs, rt); - Mfhi(rd); +void MipsAssembler::PopAndReturn(Register rd, Register rt) { + Lw(rd, SP, 0); + Jr(rt); + DecreaseFrameSize(kMipsWordSize); } -void MipsAssembler::AddConstant(Register rt, Register rs, int32_t value) { - Addiu(rt, rs, value); +void MipsAssembler::LoadConst32(Register rd, int32_t value) { + if (IsUint<16>(value)) { + // Use OR with (unsigned) immediate to encode 16b unsigned int. + Ori(rd, ZERO, value); + } else if (IsInt<16>(value)) { + // Use ADD with (signed) immediate to encode 16b signed int. + Addiu(rd, ZERO, value); + } else { + Lui(rd, High16Bits(value)); + if (value & 0xFFFF) + Ori(rd, rd, Low16Bits(value)); + } } -void MipsAssembler::LoadImmediate(Register rt, int32_t value) { - Addiu(rt, ZERO, value); +void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) { + LoadConst32(reg_lo, Low32Bits(value)); + LoadConst32(reg_hi, High32Bits(value)); } -void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, - size_t size) { - MipsManagedRegister dst = m_dst.AsMips(); - if (dst.IsNoRegister()) { - CHECK_EQ(0u, size) << dst; - } else if (dst.IsCoreRegister()) { - CHECK_EQ(4u, size) << dst; - LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset); - } else if (dst.IsRegisterPair()) { - CHECK_EQ(8u, size) << dst; - LoadFromOffset(kLoadWord, dst.AsRegisterPairLow(), src_register, src_offset); - LoadFromOffset(kLoadWord, dst.AsRegisterPairHigh(), src_register, src_offset + 4); - } else if (dst.IsFRegister()) { - LoadSFromOffset(dst.AsFRegister(), src_register, src_offset); +void MipsAssembler::StoreConst32ToOffset(int32_t value, + Register base, + int32_t offset, + Register temp) { + if (!IsInt<16>(offset)) { + CHECK_NE(temp, AT); // Must not use AT as temp, as not to overwrite the loaded value. + LoadConst32(AT, offset); + Addu(AT, AT, base); + base = AT; + offset = 0; + } + LoadConst32(temp, value); + Sw(temp, base, offset); +} + +void MipsAssembler::StoreConst64ToOffset(int64_t value, + Register base, + int32_t offset, + Register temp) { + // IsInt<16> must be passed a signed value. + if (!IsInt<16>(offset) || !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize))) { + CHECK_NE(temp, AT); // Must not use AT as temp, as not to overwrite the loaded value. + LoadConst32(AT, offset); + Addu(AT, AT, base); + base = AT; + offset = 0; + } + LoadConst32(temp, Low32Bits(value)); + Sw(temp, base, offset); + LoadConst32(temp, High32Bits(value)); + Sw(temp, base, offset + kMipsWordSize); +} + +void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) { + LoadConst32(temp, value); + Mtc1(temp, r); +} + +void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) { + LoadConst32(temp, Low32Bits(value)); + Mtc1(temp, rd); + LoadConst32(temp, High32Bits(value)); + Mthc1(temp, rd); +} + +void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) { + if (IsInt<16>(value)) { + Addiu(rt, rs, value); + } else { + LoadConst32(temp, value); + Addu(rt, rs, temp); + } +} + +void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size, + MipsAssembler::Branch::Type short_type, + MipsAssembler::Branch::Type long_type) { + type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type; +} + +void MipsAssembler::Branch::InitializeType(bool is_call, bool is_r6) { + OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_); + if (is_r6) { + // R6 + if (is_call) { + InitShortOrLong(offset_size, kR6Call, kR6LongCall); + } else if (condition_ == kUncond) { + InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch); + } else { + if (condition_ == kCondEQZ || condition_ == kCondNEZ) { + // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions. + type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch; + } else { + InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch); + } + } + } else { + // R2 + if (is_call) { + InitShortOrLong(offset_size, kCall, kLongCall); + } else if (condition_ == kUncond) { + InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch); + } else { + InitShortOrLong(offset_size, kCondBranch, kLongCondBranch); + } + } + old_type_ = type_; +} + +bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) { + switch (condition) { + case kCondLT: + case kCondGT: + case kCondNE: + case kCondLTU: + return lhs == rhs; + default: + return false; + } +} + +bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) { + switch (condition) { + case kUncond: + return true; + case kCondGE: + case kCondLE: + case kCondEQ: + case kCondGEU: + return lhs == rhs; + default: + return false; + } +} + +MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target) + : old_location_(location), + location_(location), + target_(target), + lhs_reg_(0), + rhs_reg_(0), + condition_(kUncond) { + InitializeType(false, is_r6); +} + +MipsAssembler::Branch::Branch(bool is_r6, + uint32_t location, + uint32_t target, + MipsAssembler::BranchCondition condition, + Register lhs_reg, + Register rhs_reg) + : old_location_(location), + location_(location), + target_(target), + lhs_reg_(lhs_reg), + rhs_reg_(rhs_reg), + condition_(condition) { + CHECK_NE(condition, kUncond); + switch (condition) { + case kCondLT: + case kCondGE: + case kCondLE: + case kCondGT: + case kCondLTU: + case kCondGEU: + // We don't support synthetic R2 branches (preceded with slt[u]) at this level + // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >). + // We leave this up to the caller. + CHECK(is_r6); + FALLTHROUGH_INTENDED; + case kCondEQ: + case kCondNE: + // Require registers other than 0 not only for R6, but also for R2 to catch errors. + // To compare with 0, use dedicated kCond*Z conditions. + CHECK_NE(lhs_reg, ZERO); + CHECK_NE(rhs_reg, ZERO); + break; + case kCondLTZ: + case kCondGEZ: + case kCondLEZ: + case kCondGTZ: + case kCondEQZ: + case kCondNEZ: + // Require registers other than 0 not only for R6, but also for R2 to catch errors. + CHECK_NE(lhs_reg, ZERO); + CHECK_EQ(rhs_reg, ZERO); + break; + case kUncond: + UNREACHABLE(); + } + CHECK(!IsNop(condition, lhs_reg, rhs_reg)); + if (IsUncond(condition, lhs_reg, rhs_reg)) { + // Branch condition is always true, make the branch unconditional. + condition_ = kUncond; + } + InitializeType(false, is_r6); +} + +MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, Register indirect_reg) + : old_location_(location), + location_(location), + target_(target), + lhs_reg_(indirect_reg), + rhs_reg_(0), + condition_(kUncond) { + CHECK_NE(indirect_reg, ZERO); + CHECK_NE(indirect_reg, AT); + InitializeType(true, is_r6); +} + +MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition( + MipsAssembler::BranchCondition cond) { + switch (cond) { + case kCondLT: + return kCondGE; + case kCondGE: + return kCondLT; + case kCondLE: + return kCondGT; + case kCondGT: + return kCondLE; + case kCondLTZ: + return kCondGEZ; + case kCondGEZ: + return kCondLTZ; + case kCondLEZ: + return kCondGTZ; + case kCondGTZ: + return kCondLEZ; + case kCondEQ: + return kCondNE; + case kCondNE: + return kCondEQ; + case kCondEQZ: + return kCondNEZ; + case kCondNEZ: + return kCondEQZ; + case kCondLTU: + return kCondGEU; + case kCondGEU: + return kCondLTU; + case kUncond: + LOG(FATAL) << "Unexpected branch condition " << cond; + } + UNREACHABLE(); +} + +MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const { + return type_; +} + +MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const { + return condition_; +} + +Register MipsAssembler::Branch::GetLeftRegister() const { + return static_cast<Register>(lhs_reg_); +} + +Register MipsAssembler::Branch::GetRightRegister() const { + return static_cast<Register>(rhs_reg_); +} + +uint32_t MipsAssembler::Branch::GetTarget() const { + return target_; +} + +uint32_t MipsAssembler::Branch::GetLocation() const { + return location_; +} + +uint32_t MipsAssembler::Branch::GetOldLocation() const { + return old_location_; +} + +uint32_t MipsAssembler::Branch::GetLength() const { + return branch_info_[type_].length; +} + +uint32_t MipsAssembler::Branch::GetOldLength() const { + return branch_info_[old_type_].length; +} + +uint32_t MipsAssembler::Branch::GetSize() const { + return GetLength() * sizeof(uint32_t); +} + +uint32_t MipsAssembler::Branch::GetOldSize() const { + return GetOldLength() * sizeof(uint32_t); +} + +uint32_t MipsAssembler::Branch::GetEndLocation() const { + return GetLocation() + GetSize(); +} + +uint32_t MipsAssembler::Branch::GetOldEndLocation() const { + return GetOldLocation() + GetOldSize(); +} + +bool MipsAssembler::Branch::IsLong() const { + switch (type_) { + // R2 short branches. + case kUncondBranch: + case kCondBranch: + case kCall: + // R6 short branches. + case kR6UncondBranch: + case kR6CondBranch: + case kR6Call: + return false; + // R2 long branches. + case kLongUncondBranch: + case kLongCondBranch: + case kLongCall: + // R6 long branches. + case kR6LongUncondBranch: + case kR6LongCondBranch: + case kR6LongCall: + return true; + } + UNREACHABLE(); +} + +bool MipsAssembler::Branch::IsResolved() const { + return target_ != kUnresolved; +} + +MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const { + OffsetBits offset_size = + (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ)) + ? kOffset23 + : branch_info_[type_].offset_size; + return offset_size; +} + +MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location, + uint32_t target) { + // For unresolved targets assume the shortest encoding + // (later it will be made longer if needed). + if (target == kUnresolved) + return kOffset16; + int64_t distance = static_cast<int64_t>(target) - location; + // To simplify calculations in composite branches consisting of multiple instructions + // bump up the distance by a value larger than the max byte size of a composite branch. + distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize; + if (IsInt<kOffset16>(distance)) + return kOffset16; + else if (IsInt<kOffset18>(distance)) + return kOffset18; + else if (IsInt<kOffset21>(distance)) + return kOffset21; + else if (IsInt<kOffset23>(distance)) + return kOffset23; + else if (IsInt<kOffset28>(distance)) + return kOffset28; + return kOffset32; +} + +void MipsAssembler::Branch::Resolve(uint32_t target) { + target_ = target; +} + +void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) { + if (location_ > expand_location) { + location_ += delta; + } + if (!IsResolved()) { + return; // Don't know the target yet. + } + if (target_ > expand_location) { + target_ += delta; + } +} + +void MipsAssembler::Branch::PromoteToLong() { + switch (type_) { + // R2 short branches. + case kUncondBranch: + type_ = kLongUncondBranch; + break; + case kCondBranch: + type_ = kLongCondBranch; + break; + case kCall: + type_ = kLongCall; + break; + // R6 short branches. + case kR6UncondBranch: + type_ = kR6LongUncondBranch; + break; + case kR6CondBranch: + type_ = kR6LongCondBranch; + break; + case kR6Call: + type_ = kR6LongCall; + break; + default: + // Note: 'type_' is already long. + break; + } + CHECK(IsLong()); +} + +uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t max_short_distance) { + // If the branch is still unresolved or already long, nothing to do. + if (IsLong() || !IsResolved()) { + return 0; + } + // Promote the short branch to long if the offset size is too small + // to hold the distance between location_ and target_. + if (GetOffsetSizeNeeded(location_, target_) > GetOffsetSize()) { + PromoteToLong(); + uint32_t old_size = GetOldSize(); + uint32_t new_size = GetSize(); + CHECK_GT(new_size, old_size); + return new_size - old_size; + } + // The following logic is for debugging/testing purposes. + // Promote some short branches to long when it's not really required. + if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) { + int64_t distance = static_cast<int64_t>(target_) - location_; + distance = (distance >= 0) ? distance : -distance; + if (distance >= max_short_distance) { + PromoteToLong(); + uint32_t old_size = GetOldSize(); + uint32_t new_size = GetSize(); + CHECK_GT(new_size, old_size); + return new_size - old_size; + } + } + return 0; +} + +uint32_t MipsAssembler::Branch::GetOffsetLocation() const { + return location_ + branch_info_[type_].instr_offset * sizeof(uint32_t); +} + +uint32_t MipsAssembler::Branch::GetOffset() const { + CHECK(IsResolved()); + uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize()); + // Calculate the byte distance between instructions and also account for + // different PC-relative origins. + uint32_t offset = target_ - GetOffsetLocation() - branch_info_[type_].pc_org * sizeof(uint32_t); + // Prepare the offset for encoding into the instruction(s). + offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift; + return offset; +} + +MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) { + CHECK_LT(branch_id, branches_.size()); + return &branches_[branch_id]; +} + +const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const { + CHECK_LT(branch_id, branches_.size()); + return &branches_[branch_id]; +} + +void MipsAssembler::Bind(MipsLabel* label) { + CHECK(!label->IsBound()); + uint32_t bound_pc = buffer_.Size(); + + // Walk the list of branches referring to and preceding this label. + // Store the previously unknown target addresses in them. + while (label->IsLinked()) { + uint32_t branch_id = label->Position(); + Branch* branch = GetBranch(branch_id); + branch->Resolve(bound_pc); + + uint32_t branch_location = branch->GetLocation(); + // Extract the location of the previous branch in the list (walking the list backwards; + // the previous branch ID was stored in the space reserved for this branch). + uint32_t prev = buffer_.Load<uint32_t>(branch_location); + + // On to the previous branch in the list... + label->position_ = prev; + } + + // Now make the label object contain its own location (relative to the end of the preceding + // branch, if any; it will be used by the branches referring to and following this label). + label->prev_branch_id_plus_one_ = branches_.size(); + if (label->prev_branch_id_plus_one_) { + uint32_t branch_id = label->prev_branch_id_plus_one_ - 1; + const Branch* branch = GetBranch(branch_id); + bound_pc -= branch->GetEndLocation(); + } + label->BindTo(bound_pc); +} + +uint32_t MipsAssembler::GetLabelLocation(MipsLabel* label) const { + CHECK(label->IsBound()); + uint32_t target = label->Position(); + if (label->prev_branch_id_plus_one_) { + // Get label location based on the branch preceding it. + uint32_t branch_id = label->prev_branch_id_plus_one_ - 1; + const Branch* branch = GetBranch(branch_id); + target += branch->GetEndLocation(); + } + return target; +} + +uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) { + // We can reconstruct the adjustment by going through all the branches from the beginning + // up to the old_position. Since we expect AdjustedPosition() to be called in a loop + // with increasing old_position, we can use the data from last AdjustedPosition() to + // continue where we left off and the whole loop should be O(m+n) where m is the number + // of positions to adjust and n is the number of branches. + if (old_position < last_old_position_) { + last_position_adjustment_ = 0; + last_old_position_ = 0; + last_branch_id_ = 0; + } + while (last_branch_id_ != branches_.size()) { + const Branch* branch = GetBranch(last_branch_id_); + if (branch->GetLocation() >= old_position + last_position_adjustment_) { + break; + } + last_position_adjustment_ += branch->GetSize() - branch->GetOldSize(); + ++last_branch_id_; + } + last_old_position_ = old_position; + return old_position + last_position_adjustment_; +} + +void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) { + uint32_t length = branches_.back().GetLength(); + if (!label->IsBound()) { + // Branch forward (to a following label), distance is unknown. + // The first branch forward will contain 0, serving as the terminator of + // the list of forward-reaching branches. + Emit(label->position_); + length--; + // Now make the label object point to this branch + // (this forms a linked list of branches preceding this label). + uint32_t branch_id = branches_.size() - 1; + label->LinkTo(branch_id); + } + // Reserve space for the branch. + while (length--) { + Nop(); + } +} + +void MipsAssembler::Buncond(MipsLabel* label) { + uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; + branches_.emplace_back(IsR6(), buffer_.Size(), target); + FinalizeLabeledBranch(label); +} + +void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) { + // If lhs = rhs, this can be a NOP. + if (Branch::IsNop(condition, lhs, rhs)) { + return; + } + uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; + branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs); + FinalizeLabeledBranch(label); +} + +void MipsAssembler::Call(MipsLabel* label, Register indirect_reg) { + uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; + branches_.emplace_back(IsR6(), buffer_.Size(), target, indirect_reg); + FinalizeLabeledBranch(label); +} + +void MipsAssembler::PromoteBranches() { + // Promote short branches to long as necessary. + bool changed; + do { + changed = false; + for (auto& branch : branches_) { + CHECK(branch.IsResolved()); + uint32_t delta = branch.PromoteIfNeeded(); + // If this branch has been promoted and needs to expand in size, + // relocate all branches by the expansion size. + if (delta) { + changed = true; + uint32_t expand_location = branch.GetLocation(); + for (auto& branch2 : branches_) { + branch2.Relocate(expand_location, delta); + } + } + } + } while (changed); + + // Account for branch expansion by resizing the code buffer + // and moving the code in it to its final location. + size_t branch_count = branches_.size(); + if (branch_count > 0) { + // Resize. + Branch& last_branch = branches_[branch_count - 1]; + uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation(); + uint32_t old_size = buffer_.Size(); + buffer_.Resize(old_size + size_delta); + // Move the code residing between branch placeholders. + uint32_t end = old_size; + for (size_t i = branch_count; i > 0; ) { + Branch& branch = branches_[--i]; + uint32_t size = end - branch.GetOldEndLocation(); + buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size); + end = branch.GetOldLocation(); + } + } +} + +// Note: make sure branch_info_[] and EmitBranch() are kept synchronized. +const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = { + // R2 short branches. + { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch + { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch + { 5, 2, 0, MipsAssembler::Branch::kOffset16, 0 }, // kCall + // R2 long branches. + { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch + { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch + { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall + // R6 short branches. + { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch + { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch + // Exception: kOffset23 for beqzc/bnezc. + { 2, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Call + // R6 long branches. + { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch + { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch + { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall +}; + +// Note: make sure branch_info_[] and mitBranch() are kept synchronized. +void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) { + CHECK_EQ(overwriting_, true); + overwrite_location_ = branch->GetLocation(); + uint32_t offset = branch->GetOffset(); + BranchCondition condition = branch->GetCondition(); + Register lhs = branch->GetLeftRegister(); + Register rhs = branch->GetRightRegister(); + switch (branch->GetType()) { + // R2 short branches. + case Branch::kUncondBranch: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + B(offset); + Nop(); // TODO: improve by filling the delay slot. + break; + case Branch::kCondBranch: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + EmitBcond(condition, lhs, rhs, offset); + Nop(); // TODO: improve by filling the delay slot. + break; + case Branch::kCall: + Nal(); + Nop(); // TODO: is this NOP really needed here? + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Addiu(lhs, RA, offset); + Jalr(lhs); + Nop(); + break; + + // R2 long branches. + case Branch::kLongUncondBranch: + // To get the value of the PC register we need to use the NAL instruction. + // NAL clobbers the RA register. However, RA must be preserved if the + // method is compiled without the entry/exit sequences that would take care + // of preserving RA (typically, leaf methods don't preserve RA explicitly). + // So, we need to preserve RA in some temporary storage ourselves. The AT + // register can't be used for this because we need it to load a constant + // which will be added to the value that NAL stores in RA. And we can't + // use T9 for this in the context of the JNI compiler, which uses it + // as a scratch register (see InterproceduralScratchRegister()). + // If we were to add a 32-bit constant to RA using two ADDIU instructions, + // we'd also need to use the ROTR instruction, which requires no less than + // MIPSR2. + // Perhaps, we could use T8 or one of R2's multiplier/divider registers + // (LO or HI) or even a floating-point register, but that doesn't seem + // like a nice solution. We may want this to work on both R6 and pre-R6. + // For now simply use the stack for RA. This should be OK since for the + // vast majority of code a short PC-relative branch is sufficient. + // TODO: can this be improved? + Push(RA); + Nal(); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Lui(AT, High16Bits(offset)); + Ori(AT, AT, Low16Bits(offset)); + Addu(AT, AT, RA); + Lw(RA, SP, 0); + Jr(AT); + DecreaseFrameSize(kMipsWordSize); + break; + case Branch::kLongCondBranch: + // The comment on case 'Branch::kLongUncondBranch' applies here as well. + // Note: the opposite condition branch encodes 8 as the distance, which is equal to the + // number of instructions skipped: + // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR). + EmitBcond(Branch::OppositeCondition(condition), lhs, rhs, 8); + Push(RA); + Nal(); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Lui(AT, High16Bits(offset)); + Ori(AT, AT, Low16Bits(offset)); + Addu(AT, AT, RA); + Lw(RA, SP, 0); + Jr(AT); + DecreaseFrameSize(kMipsWordSize); + break; + case Branch::kLongCall: + Nal(); + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Lui(AT, High16Bits(offset)); + Ori(AT, AT, Low16Bits(offset)); + Addu(lhs, AT, RA); + Jalr(lhs); + Nop(); + break; + + // R6 short branches. + case Branch::kR6UncondBranch: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Bc(offset); + break; + case Branch::kR6CondBranch: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + EmitBcondc(condition, lhs, rhs, offset); + Nop(); // TODO: improve by filling the forbidden slot. + break; + case Branch::kR6Call: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Addiupc(lhs, offset); + Jialc(lhs, 0); + break; + + // R6 long branches. + case Branch::kR6LongUncondBranch: + offset += (offset & 0x8000) << 1; // Account for sign extension in jic. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Jic(AT, Low16Bits(offset)); + break; + case Branch::kR6LongCondBranch: + EmitBcondc(Branch::OppositeCondition(condition), lhs, rhs, 2); + offset += (offset & 0x8000) << 1; // Account for sign extension in jic. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Jic(AT, Low16Bits(offset)); + break; + case Branch::kR6LongCall: + offset += (offset & 0x8000) << 1; // Account for sign extension in addiu. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(lhs, High16Bits(offset)); + Addiu(lhs, lhs, Low16Bits(offset)); + Jialc(lhs, 0); + break; + } + CHECK_EQ(overwrite_location_, branch->GetEndLocation()); + CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize)); +} + +void MipsAssembler::B(MipsLabel* label) { + Buncond(label); +} + +void MipsAssembler::Jalr(MipsLabel* label, Register indirect_reg) { + Call(label, indirect_reg); +} + +void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) { + Bcond(label, kCondEQ, rs, rt); +} + +void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) { + Bcond(label, kCondNE, rs, rt); +} + +void MipsAssembler::Beqz(Register rt, MipsLabel* label) { + Bcond(label, kCondEQZ, rt); +} + +void MipsAssembler::Bnez(Register rt, MipsLabel* label) { + Bcond(label, kCondNEZ, rt); +} + +void MipsAssembler::Bltz(Register rt, MipsLabel* label) { + Bcond(label, kCondLTZ, rt); +} + +void MipsAssembler::Bgez(Register rt, MipsLabel* label) { + Bcond(label, kCondGEZ, rt); +} + +void MipsAssembler::Blez(Register rt, MipsLabel* label) { + Bcond(label, kCondLEZ, rt); +} + +void MipsAssembler::Bgtz(Register rt, MipsLabel* label) { + Bcond(label, kCondGTZ, rt); +} + +void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) { + if (IsR6()) { + Bcond(label, kCondLT, rs, rt); + } else if (!Branch::IsNop(kCondLT, rs, rt)) { + // Synthesize the instruction (not available on R2). + Slt(AT, rs, rt); + Bnez(AT, label); + } +} + +void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) { + if (IsR6()) { + Bcond(label, kCondGE, rs, rt); + } else if (Branch::IsUncond(kCondGE, rs, rt)) { + B(label); + } else { + // Synthesize the instruction (not available on R2). + Slt(AT, rs, rt); + Beqz(AT, label); + } +} + +void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) { + if (IsR6()) { + Bcond(label, kCondLTU, rs, rt); + } else if (!Branch::IsNop(kCondLTU, rs, rt)) { + // Synthesize the instruction (not available on R2). + Sltu(AT, rs, rt); + Bnez(AT, label); + } +} + +void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) { + if (IsR6()) { + Bcond(label, kCondGEU, rs, rt); + } else if (Branch::IsUncond(kCondGEU, rs, rt)) { + B(label); } else { - CHECK(dst.IsDRegister()) << dst; - LoadDFromOffset(dst.AsDRegister(), src_register, src_offset); + // Synthesize the instruction (not available on R2). + Sltu(AT, rs, rt); + Beqz(AT, label); } } void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset) { + // IsInt<16> must be passed a signed value. + if (!IsInt<16>(offset) || + (type == kLoadDoubleword && !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) { + LoadConst32(AT, offset); + Addu(AT, AT, base); + base = AT; + offset = 0; + } + switch (type) { case kLoadSignedByte: Lb(reg, base, offset); @@ -488,8 +1617,16 @@ void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register case kLoadWord: Lw(reg, base, offset); break; - case kLoadWordPair: - LOG(FATAL) << "UNREACHABLE"; + case kLoadDoubleword: + if (reg == base) { + // This will clobber the base when loading the lower register. Since we have to load the + // higher register as well, this will fail. Solution: reverse the order. + Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize); + Lw(reg, base, offset); + } else { + Lw(reg, base, offset); + Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize); + } break; default: LOG(FATAL) << "UNREACHABLE"; @@ -497,15 +1634,74 @@ void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register } void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) { + if (!IsInt<16>(offset)) { + LoadConst32(AT, offset); + Addu(AT, AT, base); + base = AT; + offset = 0; + } + Lwc1(reg, base, offset); } -void MipsAssembler::LoadDFromOffset(DRegister reg, Register base, int32_t offset) { - Ldc1(reg, base, offset); +void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) { + // IsInt<16> must be passed a signed value. + if (!IsInt<16>(offset) || + (!IsAligned<kMipsDoublewordSize>(offset) && + !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) { + LoadConst32(AT, offset); + Addu(AT, AT, base); + base = AT; + offset = 0; + } + + if (offset & 0x7) { + if (Is32BitFPU()) { + Lwc1(reg, base, offset); + Lwc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize); + } else { + // 64-bit FPU. + Lwc1(reg, base, offset); + Lw(T8, base, offset + kMipsWordSize); + Mthc1(T8, reg); + } + } else { + Ldc1(reg, base, offset); + } +} + +void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, + size_t size) { + MipsManagedRegister dst = m_dst.AsMips(); + if (dst.IsNoRegister()) { + CHECK_EQ(0u, size) << dst; + } else if (dst.IsCoreRegister()) { + CHECK_EQ(kMipsWordSize, size) << dst; + LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset); + } else if (dst.IsRegisterPair()) { + CHECK_EQ(kMipsDoublewordSize, size) << dst; + LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset); + } else if (dst.IsFRegister()) { + if (size == kMipsWordSize) { + LoadSFromOffset(dst.AsFRegister(), src_register, src_offset); + } else { + CHECK_EQ(kMipsDoublewordSize, size) << dst; + LoadDFromOffset(dst.AsFRegister(), src_register, src_offset); + } + } } void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register base, int32_t offset) { + // IsInt<16> must be passed a signed value. + if (!IsInt<16>(offset) || + (type == kStoreDoubleword && !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) { + LoadConst32(AT, offset); + Addu(AT, AT, base); + base = AT; + offset = 0; + } + switch (type) { case kStoreByte: Sb(reg, base, offset); @@ -516,8 +1712,11 @@ void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register case kStoreWord: Sw(reg, base, offset); break; - case kStoreWordPair: - LOG(FATAL) << "UNREACHABLE"; + case kStoreDoubleword: + CHECK_NE(reg, base); + CHECK_NE(static_cast<Register>(reg + 1), base); + Sw(reg, base, offset); + Sw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize); break; default: LOG(FATAL) << "UNREACHABLE"; @@ -525,11 +1724,40 @@ void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register } void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) { + if (!IsInt<16>(offset)) { + LoadConst32(AT, offset); + Addu(AT, AT, base); + base = AT; + offset = 0; + } + Swc1(reg, base, offset); } -void MipsAssembler::StoreDToOffset(DRegister reg, Register base, int32_t offset) { - Sdc1(reg, base, offset); +void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) { + // IsInt<16> must be passed a signed value. + if (!IsInt<16>(offset) || + (!IsAligned<kMipsDoublewordSize>(offset) && + !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) { + LoadConst32(AT, offset); + Addu(AT, AT, base); + base = AT; + offset = 0; + } + + if (offset & 0x7) { + if (Is32BitFPU()) { + Swc1(reg, base, offset); + Swc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize); + } else { + // 64-bit FPU. + Mfhc1(T8, reg); + Swc1(reg, base, offset); + Sw(T8, base, offset + kMipsWordSize); + } + } else { + Sdc1(reg, base, offset); + } } static dwarf::Reg DWARFReg(Register reg) { @@ -546,7 +1774,7 @@ void MipsAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, // Increase frame to required size. IncreaseFrameSize(frame_size); - // Push callee saves and return address + // Push callee saves and return address. int stack_offset = frame_size - kFramePointerSize; StoreToOffset(kStoreWord, RA, SP, stack_offset); cfi_.RelOffset(DWARFReg(RA), stack_offset); @@ -569,13 +1797,13 @@ void MipsAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, offset += spill.getSize(); } else if (reg.IsCoreRegister()) { StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset); - offset += 4; + offset += kMipsWordSize; } else if (reg.IsFRegister()) { StoreSToOffset(reg.AsFRegister(), SP, offset); - offset += 4; + offset += kMipsWordSize; } else if (reg.IsDRegister()) { - StoreDToOffset(reg.AsDRegister(), SP, offset); - offset += 8; + StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset); + offset += kMipsDoublewordSize; } } } @@ -585,7 +1813,7 @@ void MipsAssembler::RemoveFrame(size_t frame_size, CHECK_ALIGNED(frame_size, kStackAlignment); cfi_.RememberState(); - // Pop callee saves and return address + // Pop callee saves and return address. int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize; for (size_t i = 0; i < callee_save_regs.size(); ++i) { Register reg = callee_save_regs.at(i).AsMips().AsCoreRegister(); @@ -601,6 +1829,7 @@ void MipsAssembler::RemoveFrame(size_t frame_size, // Then jump to the return address. Jr(RA); + Nop(); // The CFI should be restored for any code that follows the exit block. cfi_.RestoreState(); @@ -608,14 +1837,14 @@ void MipsAssembler::RemoveFrame(size_t frame_size, } void MipsAssembler::IncreaseFrameSize(size_t adjust) { - CHECK_ALIGNED(adjust, kStackAlignment); - AddConstant(SP, SP, -adjust); + CHECK_ALIGNED(adjust, kFramePointerSize); + Addiu32(SP, SP, -adjust); cfi_.AdjustCFAOffset(adjust); } void MipsAssembler::DecreaseFrameSize(size_t adjust) { - CHECK_ALIGNED(adjust, kStackAlignment); - AddConstant(SP, SP, adjust); + CHECK_ALIGNED(adjust, kFramePointerSize); + Addiu32(SP, SP, adjust); cfi_.AdjustCFAOffset(-adjust); } @@ -624,18 +1853,20 @@ void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) { if (src.IsNoRegister()) { CHECK_EQ(0u, size); } else if (src.IsCoreRegister()) { - CHECK_EQ(4u, size); + CHECK_EQ(kMipsWordSize, size); StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); } else if (src.IsRegisterPair()) { - CHECK_EQ(8u, size); + CHECK_EQ(kMipsDoublewordSize, size); StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value()); StoreToOffset(kStoreWord, src.AsRegisterPairHigh(), - SP, dest.Int32Value() + 4); + SP, dest.Int32Value() + kMipsWordSize); } else if (src.IsFRegister()) { - StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value()); - } else { - CHECK(src.IsDRegister()); - StoreDToOffset(src.AsDRegister(), SP, dest.Int32Value()); + if (size == kMipsWordSize) { + StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value()); + } else { + CHECK_EQ(kMipsDoublewordSize, size); + StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value()); + } } } @@ -655,29 +1886,30 @@ void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm, ManagedRegister mscratch) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; - LoadImmediate(scratch.AsCoreRegister(), imm); + LoadConst32(scratch.AsCoreRegister(), imm); StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); } -void MipsAssembler::StoreImmediateToThread32(ThreadOffset<4> dest, uint32_t imm, - ManagedRegister mscratch) { +void MipsAssembler::StoreImmediateToThread32(ThreadOffset<kMipsWordSize> dest, uint32_t imm, + ManagedRegister mscratch) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; - LoadImmediate(scratch.AsCoreRegister(), imm); + // Is this function even referenced anywhere else in the code? + LoadConst32(scratch.AsCoreRegister(), imm); StoreToOffset(kStoreWord, scratch.AsCoreRegister(), S1, dest.Int32Value()); } -void MipsAssembler::StoreStackOffsetToThread32(ThreadOffset<4> thr_offs, - FrameOffset fr_offs, - ManagedRegister mscratch) { +void MipsAssembler::StoreStackOffsetToThread32(ThreadOffset<kMipsWordSize> thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; - AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); + Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); StoreToOffset(kStoreWord, scratch.AsCoreRegister(), S1, thr_offs.Int32Value()); } -void MipsAssembler::StoreStackPointerToThread32(ThreadOffset<4> thr_offs) { +void MipsAssembler::StoreStackPointerToThread32(ThreadOffset<kMipsWordSize> thr_offs) { StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value()); } @@ -687,14 +1919,15 @@ void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc, MipsManagedRegister scratch = mscratch.AsMips(); StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value()); - StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize); } void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) { return EmitLoad(mdest, SP, src.Int32Value(), size); } -void MipsAssembler::LoadFromThread32(ManagedRegister mdest, ThreadOffset<4> src, size_t size) { +void MipsAssembler::LoadFromThread32(ManagedRegister mdest, + ThreadOffset<kMipsWordSize> src, size_t size) { return EmitLoad(mdest, S1, src.Int32Value(), size); } @@ -707,7 +1940,7 @@ void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) { void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs, bool unpoison_reference) { MipsManagedRegister dest = mdest.AsMips(); - CHECK(dest.IsCoreRegister() && dest.IsCoreRegister()); + CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister()); LoadFromOffset(kLoadWord, dest.AsCoreRegister(), base.AsMips().AsCoreRegister(), offs.Int32Value()); if (kPoisonHeapReferences && unpoison_reference) { @@ -715,16 +1948,15 @@ void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberO } } -void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, - Offset offs) { +void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) { MipsManagedRegister dest = mdest.AsMips(); - CHECK(dest.IsCoreRegister() && dest.IsCoreRegister()) << dest; + CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister()); LoadFromOffset(kLoadWord, dest.AsCoreRegister(), base.AsMips().AsCoreRegister(), offs.Int32Value()); } void MipsAssembler::LoadRawPtrFromThread32(ManagedRegister mdest, - ThreadOffset<4> offs) { + ThreadOffset<kMipsWordSize> offs) { MipsManagedRegister dest = mdest.AsMips(); CHECK(dest.IsCoreRegister()); LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value()); @@ -738,7 +1970,7 @@ void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) { UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips"; } -void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t /*size*/) { +void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) { MipsManagedRegister dest = mdest.AsMips(); MipsManagedRegister src = msrc.AsMips(); if (!dest.Equals(src)) { @@ -747,14 +1979,19 @@ void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t /*s Move(dest.AsCoreRegister(), src.AsCoreRegister()); } else if (dest.IsFRegister()) { CHECK(src.IsFRegister()) << src; - MovS(dest.AsFRegister(), src.AsFRegister()); + if (size == kMipsWordSize) { + MovS(dest.AsFRegister(), src.AsFRegister()); + } else { + CHECK_EQ(kMipsDoublewordSize, size); + MovD(dest.AsFRegister(), src.AsFRegister()); + } } else if (dest.IsDRegister()) { CHECK(src.IsDRegister()) << src; - MovD(dest.AsDRegister(), src.AsDRegister()); + MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow()); } else { CHECK(dest.IsRegisterPair()) << dest; CHECK(src.IsRegisterPair()) << src; - // Ensure that the first move doesn't clobber the input of the second + // Ensure that the first move doesn't clobber the input of the second. if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) { Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow()); Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh()); @@ -766,8 +2003,7 @@ void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t /*s } } -void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, - ManagedRegister mscratch) { +void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); @@ -775,8 +2011,8 @@ void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, } void MipsAssembler::CopyRawPtrFromThread32(FrameOffset fr_offs, - ThreadOffset<4> thr_offs, - ManagedRegister mscratch) { + ThreadOffset<kMipsWordSize> thr_offs, + ManagedRegister mscratch) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), @@ -785,9 +2021,9 @@ void MipsAssembler::CopyRawPtrFromThread32(FrameOffset fr_offs, SP, fr_offs.Int32Value()); } -void MipsAssembler::CopyRawPtrToThread32(ThreadOffset<4> thr_offs, - FrameOffset fr_offs, - ManagedRegister mscratch) { +void MipsAssembler::CopyRawPtrToThread32(ThreadOffset<kMipsWordSize> thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), @@ -796,26 +2032,25 @@ void MipsAssembler::CopyRawPtrToThread32(ThreadOffset<4> thr_offs, S1, thr_offs.Int32Value()); } -void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, - ManagedRegister mscratch, size_t size) { +void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; - CHECK(size == 4 || size == 8) << size; - if (size == 4) { + CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size; + if (size == kMipsWordSize) { LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); - } else if (size == 8) { + } else if (size == kMipsDoublewordSize) { LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); - LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + 4); - StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize); } } void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, ManagedRegister mscratch, size_t size) { Register scratch = mscratch.AsMips().AsCoreRegister(); - CHECK_EQ(size, 4u); + CHECK_EQ(size, kMipsWordSize); LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value()); StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value()); } @@ -823,107 +2058,117 @@ void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_ void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, ManagedRegister mscratch, size_t size) { Register scratch = mscratch.AsMips().AsCoreRegister(); - CHECK_EQ(size, 4u); + CHECK_EQ(size, kMipsWordSize); LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value()); StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value()); } -void MipsAssembler::Copy(FrameOffset /*dest*/, FrameOffset /*src_base*/, Offset /*src_offset*/, - ManagedRegister /*mscratch*/, size_t /*size*/) { - UNIMPLEMENTED(FATAL) << "no mips implementation"; +void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED, + FrameOffset src_base ATTRIBUTE_UNUSED, + Offset src_offset ATTRIBUTE_UNUSED, + ManagedRegister mscratch ATTRIBUTE_UNUSED, + size_t size ATTRIBUTE_UNUSED) { + UNIMPLEMENTED(FATAL) << "no MIPS implementation"; } void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset, ManagedRegister src, Offset src_offset, ManagedRegister mscratch, size_t size) { - CHECK_EQ(size, 4u); + CHECK_EQ(size, kMipsWordSize); Register scratch = mscratch.AsMips().AsCoreRegister(); LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value()); StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value()); } -void MipsAssembler::Copy(FrameOffset /*dest*/, Offset /*dest_offset*/, FrameOffset /*src*/, Offset /*src_offset*/, - ManagedRegister /*mscratch*/, size_t /*size*/) { - UNIMPLEMENTED(FATAL) << "no mips implementation"; +void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED, + Offset dest_offset ATTRIBUTE_UNUSED, + FrameOffset src ATTRIBUTE_UNUSED, + Offset src_offset ATTRIBUTE_UNUSED, + ManagedRegister mscratch ATTRIBUTE_UNUSED, + size_t size ATTRIBUTE_UNUSED) { + UNIMPLEMENTED(FATAL) << "no MIPS implementation"; } void MipsAssembler::MemoryBarrier(ManagedRegister) { - UNIMPLEMENTED(FATAL) << "no mips implementation"; + // TODO: sync? + UNIMPLEMENTED(FATAL) << "no MIPS implementation"; } void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg, - FrameOffset handle_scope_offset, - ManagedRegister min_reg, bool null_allowed) { + FrameOffset handle_scope_offset, + ManagedRegister min_reg, + bool null_allowed) { MipsManagedRegister out_reg = mout_reg.AsMips(); MipsManagedRegister in_reg = min_reg.AsMips(); CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; CHECK(out_reg.IsCoreRegister()) << out_reg; if (null_allowed) { - Label null_arg; + MipsLabel null_arg; // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. - // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset) + // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset). if (in_reg.IsNoRegister()) { LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); in_reg = out_reg; } if (!out_reg.Equals(in_reg)) { - LoadImmediate(out_reg.AsCoreRegister(), 0); + LoadConst32(out_reg.AsCoreRegister(), 0); } - EmitBranch(in_reg.AsCoreRegister(), ZERO, &null_arg, true); - AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); - Bind(&null_arg, false); + Beqz(in_reg.AsCoreRegister(), &null_arg); + Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); + Bind(&null_arg); } else { - AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); + Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); } } void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off, - FrameOffset handle_scope_offset, - ManagedRegister mscratch, - bool null_allowed) { + FrameOffset handle_scope_offset, + ManagedRegister mscratch, + bool null_allowed) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; if (null_allowed) { - Label null_arg; - LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, - handle_scope_offset.Int32Value()); + MipsLabel null_arg; + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. - // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset) - EmitBranch(scratch.AsCoreRegister(), ZERO, &null_arg, true); - AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); - Bind(&null_arg, false); + // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset). + Beqz(scratch.AsCoreRegister(), &null_arg); + Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); + Bind(&null_arg); } else { - AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); + Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value()); } StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value()); } // Given a handle scope entry, load the associated reference. void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg, - ManagedRegister min_reg) { + ManagedRegister min_reg) { MipsManagedRegister out_reg = mout_reg.AsMips(); MipsManagedRegister in_reg = min_reg.AsMips(); CHECK(out_reg.IsCoreRegister()) << out_reg; CHECK(in_reg.IsCoreRegister()) << in_reg; - Label null_arg; + MipsLabel null_arg; if (!out_reg.Equals(in_reg)) { - LoadImmediate(out_reg.AsCoreRegister(), 0); + LoadConst32(out_reg.AsCoreRegister(), 0); } - EmitBranch(in_reg.AsCoreRegister(), ZERO, &null_arg, true); + Beqz(in_reg.AsCoreRegister(), &null_arg); LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0); - Bind(&null_arg, false); + Bind(&null_arg); } -void MipsAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) { - // TODO: not validating references +void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED, + bool could_be_null ATTRIBUTE_UNUSED) { + // TODO: not validating references. } -void MipsAssembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) { - // TODO: not validating references +void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED, + bool could_be_null ATTRIBUTE_UNUSED) { + // TODO: not validating references. } void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) { @@ -934,22 +2179,24 @@ void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister m LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), base.AsCoreRegister(), offset.Int32Value()); Jalr(scratch.AsCoreRegister()); - // TODO: place reference map on call + Nop(); + // TODO: place reference map on call. } void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) { MipsManagedRegister scratch = mscratch.AsMips(); CHECK(scratch.IsCoreRegister()) << scratch; // Call *(*(SP + base) + offset) - LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), - SP, base.Int32Value()); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value()); LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), scratch.AsCoreRegister(), offset.Int32Value()); Jalr(scratch.AsCoreRegister()); - // TODO: place reference map on call + Nop(); + // TODO: place reference map on call. } -void MipsAssembler::CallFromThread32(ThreadOffset<4> /*offset*/, ManagedRegister /*mscratch*/) { +void MipsAssembler::CallFromThread32(ThreadOffset<kMipsWordSize> offset ATTRIBUTE_UNUSED, + ManagedRegister mscratch ATTRIBUTE_UNUSED) { UNIMPLEMENTED(FATAL) << "no mips implementation"; } @@ -958,35 +2205,38 @@ void MipsAssembler::GetCurrentThread(ManagedRegister tr) { } void MipsAssembler::GetCurrentThread(FrameOffset offset, - ManagedRegister /*mscratch*/) { + ManagedRegister mscratch ATTRIBUTE_UNUSED) { StoreToOffset(kStoreWord, S1, SP, offset.Int32Value()); } void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) { MipsManagedRegister scratch = mscratch.AsMips(); - MipsExceptionSlowPath* slow = new MipsExceptionSlowPath(scratch, stack_adjust); - buffer_.EnqueueSlowPath(slow); + exception_blocks_.emplace_back(scratch, stack_adjust); LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), - S1, Thread::ExceptionOffset<4>().Int32Value()); - EmitBranch(scratch.AsCoreRegister(), ZERO, slow->Entry(), false); -} - -void MipsExceptionSlowPath::Emit(Assembler* sasm) { - MipsAssembler* sp_asm = down_cast<MipsAssembler*>(sasm); -#define __ sp_asm-> - __ Bind(&entry_, false); - if (stack_adjust_ != 0) { // Fix up the frame. - __ DecreaseFrameSize(stack_adjust_); - } - // Pass exception object as argument - // Don't care about preserving A0 as this call won't return - __ Move(A0, scratch_.AsCoreRegister()); - // Set up call to Thread::Current()->pDeliverException - __ LoadFromOffset(kLoadWord, T9, S1, QUICK_ENTRYPOINT_OFFSET(4, pDeliverException).Int32Value()); - __ Jr(T9); - // Call never returns - __ Break(); -#undef __ + S1, Thread::ExceptionOffset<kMipsWordSize>().Int32Value()); + // TODO: on MIPS32R6 prefer Bnezc(scratch.AsCoreRegister(), slow.Entry()); + // as the NAL instruction (occurring in long R2 branches) may become deprecated. + // For now use common for R2 and R6 instructions as this code must execute on both. + Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry()); +} + +void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) { + Bind(exception->Entry()); + if (exception->stack_adjust_ != 0) { // Fix up the frame. + DecreaseFrameSize(exception->stack_adjust_); + } + // Pass exception object as argument. + // Don't care about preserving A0 as this call won't return. + CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); + Move(A0, exception->scratch_.AsCoreRegister()); + // Set up call to Thread::Current()->pDeliverException. + LoadFromOffset(kLoadWord, T9, S1, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pDeliverException).Int32Value()); + Jr(T9); + Nop(); + + // Call never returns. + Break(); } } // namespace mips diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 378a59cb3e..aa187b812b 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -17,54 +17,111 @@ #ifndef ART_COMPILER_UTILS_MIPS_ASSEMBLER_MIPS_H_ #define ART_COMPILER_UTILS_MIPS_ASSEMBLER_MIPS_H_ +#include <utility> #include <vector> +#include "arch/mips/instruction_set_features_mips.h" #include "base/macros.h" #include "constants_mips.h" #include "globals.h" #include "managed_register_mips.h" -#include "utils/assembler.h" #include "offsets.h" +#include "utils/assembler.h" +#include "utils/label.h" namespace art { namespace mips { +static constexpr size_t kMipsWordSize = 4; +static constexpr size_t kMipsDoublewordSize = 8; + enum LoadOperandType { kLoadSignedByte, kLoadUnsignedByte, kLoadSignedHalfword, kLoadUnsignedHalfword, kLoadWord, - kLoadWordPair, - kLoadSWord, - kLoadDWord + kLoadDoubleword }; enum StoreOperandType { kStoreByte, kStoreHalfword, kStoreWord, - kStoreWordPair, - kStoreSWord, - kStoreDWord + kStoreDoubleword +}; + +class MipsLabel : public Label { + public: + MipsLabel() : prev_branch_id_plus_one_(0) {} + + MipsLabel(MipsLabel&& src) + : Label(std::move(src)), prev_branch_id_plus_one_(src.prev_branch_id_plus_one_) {} + + private: + uint32_t prev_branch_id_plus_one_; // To get distance from preceding branch, if any. + + friend class MipsAssembler; + DISALLOW_COPY_AND_ASSIGN(MipsLabel); +}; + +// Slowpath entered when Thread::Current()->_exception is non-null. +class MipsExceptionSlowPath { + public: + explicit MipsExceptionSlowPath(MipsManagedRegister scratch, size_t stack_adjust) + : scratch_(scratch), stack_adjust_(stack_adjust) {} + + MipsExceptionSlowPath(MipsExceptionSlowPath&& src) + : scratch_(std::move(src.scratch_)), + stack_adjust_(std::move(src.stack_adjust_)), + exception_entry_(std::move(src.exception_entry_)) {} + + private: + MipsLabel* Entry() { return &exception_entry_; } + const MipsManagedRegister scratch_; + const size_t stack_adjust_; + MipsLabel exception_entry_; + + friend class MipsAssembler; + DISALLOW_COPY_AND_ASSIGN(MipsExceptionSlowPath); }; class MipsAssembler FINAL : public Assembler { public: - MipsAssembler() {} - virtual ~MipsAssembler() {} + explicit MipsAssembler(const MipsInstructionSetFeatures* instruction_set_features = nullptr) + : overwriting_(false), + overwrite_location_(0), + last_position_adjustment_(0), + last_old_position_(0), + last_branch_id_(0), + isa_features_(instruction_set_features) {} + + virtual ~MipsAssembler() { + for (auto& branch : branches_) { + CHECK(branch.IsResolved()); + } + } // Emit Machine Instructions. - void Add(Register rd, Register rs, Register rt); void Addu(Register rd, Register rs, Register rt); - void Addi(Register rt, Register rs, uint16_t imm16); void Addiu(Register rt, Register rs, uint16_t imm16); - void Sub(Register rd, Register rs, Register rt); void Subu(Register rd, Register rs, Register rt); - void Mult(Register rs, Register rt); - void Multu(Register rs, Register rt); - void Div(Register rs, Register rt); - void Divu(Register rs, Register rt); + + void MultR2(Register rs, Register rt); // R2 + void MultuR2(Register rs, Register rt); // R2 + void DivR2(Register rs, Register rt); // R2 + void DivuR2(Register rs, Register rt); // R2 + void MulR2(Register rd, Register rs, Register rt); // R2 + void DivR2(Register rd, Register rs, Register rt); // R2 + void ModR2(Register rd, Register rs, Register rt); // R2 + void DivuR2(Register rd, Register rs, Register rt); // R2 + void ModuR2(Register rd, Register rs, Register rt); // R2 + void MulR6(Register rd, Register rs, Register rt); // R6 + void MuhuR6(Register rd, Register rs, Register rt); // R6 + void DivR6(Register rd, Register rs, Register rt); // R6 + void ModR6(Register rd, Register rs, Register rt); // R6 + void DivuR6(Register rd, Register rs, Register rt); // R6 + void ModuR6(Register rd, Register rs, Register rt); // R6 void And(Register rd, Register rs, Register rt); void Andi(Register rt, Register rs, uint16_t imm16); @@ -74,12 +131,15 @@ class MipsAssembler FINAL : public Assembler { void Xori(Register rt, Register rs, uint16_t imm16); void Nor(Register rd, Register rs, Register rt); - void Sll(Register rd, Register rs, int shamt); - void Srl(Register rd, Register rs, int shamt); - void Sra(Register rd, Register rs, int shamt); - void Sllv(Register rd, Register rs, Register rt); - void Srlv(Register rd, Register rs, Register rt); - void Srav(Register rd, Register rs, Register rt); + void Seb(Register rd, Register rt); // R2+ + void Seh(Register rd, Register rt); // R2+ + + void Sll(Register rd, Register rt, int shamt); + void Srl(Register rd, Register rt, int shamt); + void Sra(Register rd, Register rt, int shamt); + void Sllv(Register rd, Register rt, Register rs); + void Srlv(Register rd, Register rt, Register rs); + void Srav(Register rd, Register rt, Register rs); void Lb(Register rt, Register rs, uint16_t imm16); void Lh(Register rt, Register rs, uint16_t imm16); @@ -87,8 +147,9 @@ class MipsAssembler FINAL : public Assembler { void Lbu(Register rt, Register rs, uint16_t imm16); void Lhu(Register rt, Register rs, uint16_t imm16); void Lui(Register rt, uint16_t imm16); - void Mfhi(Register rd); - void Mflo(Register rd); + void Sync(uint32_t stype); + void Mfhi(Register rd); // R2 + void Mflo(Register rd); // R2 void Sb(Register rt, Register rs, uint16_t imm16); void Sh(Register rt, Register rs, uint16_t imm16); @@ -99,81 +160,138 @@ class MipsAssembler FINAL : public Assembler { void Slti(Register rt, Register rs, uint16_t imm16); void Sltiu(Register rt, Register rs, uint16_t imm16); - void Beq(Register rt, Register rs, uint16_t imm16); - void Bne(Register rt, Register rs, uint16_t imm16); - void J(uint32_t address); - void Jal(uint32_t address); - void Jr(Register rs); + void B(uint16_t imm16); + void Beq(Register rs, Register rt, uint16_t imm16); + void Bne(Register rs, Register rt, uint16_t imm16); + void Beqz(Register rt, uint16_t imm16); + void Bnez(Register rt, uint16_t imm16); + void Bltz(Register rt, uint16_t imm16); + void Bgez(Register rt, uint16_t imm16); + void Blez(Register rt, uint16_t imm16); + void Bgtz(Register rt, uint16_t imm16); + void J(uint32_t addr26); + void Jal(uint32_t addr26); + void Jalr(Register rd, Register rs); void Jalr(Register rs); + void Jr(Register rs); + void Nal(); + void Auipc(Register rs, uint16_t imm16); // R6 + void Addiupc(Register rs, uint32_t imm19); // R6 + void Bc(uint32_t imm26); // R6 + void Jic(Register rt, uint16_t imm16); // R6 + void Jialc(Register rt, uint16_t imm16); // R6 + void Bltc(Register rs, Register rt, uint16_t imm16); // R6 + void Bltzc(Register rt, uint16_t imm16); // R6 + void Bgtzc(Register rt, uint16_t imm16); // R6 + void Bgec(Register rs, Register rt, uint16_t imm16); // R6 + void Bgezc(Register rt, uint16_t imm16); // R6 + void Blezc(Register rt, uint16_t imm16); // R6 + void Bltuc(Register rs, Register rt, uint16_t imm16); // R6 + void Bgeuc(Register rs, Register rt, uint16_t imm16); // R6 + void Beqc(Register rs, Register rt, uint16_t imm16); // R6 + void Bnec(Register rs, Register rt, uint16_t imm16); // R6 + void Beqzc(Register rs, uint32_t imm21); // R6 + void Bnezc(Register rs, uint32_t imm21); // R6 void AddS(FRegister fd, FRegister fs, FRegister ft); void SubS(FRegister fd, FRegister fs, FRegister ft); void MulS(FRegister fd, FRegister fs, FRegister ft); void DivS(FRegister fd, FRegister fs, FRegister ft); - void AddD(DRegister fd, DRegister fs, DRegister ft); - void SubD(DRegister fd, DRegister fs, DRegister ft); - void MulD(DRegister fd, DRegister fs, DRegister ft); - void DivD(DRegister fd, DRegister fs, DRegister ft); + void AddD(FRegister fd, FRegister fs, FRegister ft); + void SubD(FRegister fd, FRegister fs, FRegister ft); + void MulD(FRegister fd, FRegister fs, FRegister ft); + void DivD(FRegister fd, FRegister fs, FRegister ft); void MovS(FRegister fd, FRegister fs); - void MovD(DRegister fd, DRegister fs); + void MovD(FRegister fd, FRegister fs); + void NegS(FRegister fd, FRegister fs); + void NegD(FRegister fd, FRegister fs); + + void Cvtsw(FRegister fd, FRegister fs); + void Cvtdw(FRegister fd, FRegister fs); + void Cvtsd(FRegister fd, FRegister fs); + void Cvtds(FRegister fd, FRegister fs); void Mfc1(Register rt, FRegister fs); - void Mtc1(FRegister ft, Register rs); + void Mtc1(Register rt, FRegister fs); + void Mfhc1(Register rt, FRegister fs); + void Mthc1(Register rt, FRegister fs); void Lwc1(FRegister ft, Register rs, uint16_t imm16); - void Ldc1(DRegister ft, Register rs, uint16_t imm16); + void Ldc1(FRegister ft, Register rs, uint16_t imm16); void Swc1(FRegister ft, Register rs, uint16_t imm16); - void Sdc1(DRegister ft, Register rs, uint16_t imm16); + void Sdc1(FRegister ft, Register rs, uint16_t imm16); void Break(); void Nop(); - void Move(Register rt, Register rs); - void Clear(Register rt); - void Not(Register rt, Register rs); - void Mul(Register rd, Register rs, Register rt); - void Div(Register rd, Register rs, Register rt); - void Rem(Register rd, Register rs, Register rt); - - void AddConstant(Register rt, Register rs, int32_t value); - void LoadImmediate(Register rt, int32_t value); + void Move(Register rd, Register rs); + void Clear(Register rd); + void Not(Register rd, Register rs); + + // Higher level composite instructions. + void LoadConst32(Register rd, int32_t value); + void LoadConst64(Register reg_hi, Register reg_lo, int64_t value); + void LoadDConst64(FRegister rd, int64_t value, Register temp); + void LoadSConst32(FRegister r, int32_t value, Register temp); + void StoreConst32ToOffset(int32_t value, Register base, int32_t offset, Register temp); + void StoreConst64ToOffset(int64_t value, Register base, int32_t offset, Register temp); + void Addiu32(Register rt, Register rs, int32_t value, Register rtmp = AT); + + // These will generate R2 branches or R6 branches as appropriate. + void Bind(MipsLabel* label); + void B(MipsLabel* label); + void Jalr(MipsLabel* label, Register indirect_reg); + void Beq(Register rs, Register rt, MipsLabel* label); + void Bne(Register rs, Register rt, MipsLabel* label); + void Beqz(Register rt, MipsLabel* label); + void Bnez(Register rt, MipsLabel* label); + void Bltz(Register rt, MipsLabel* label); + void Bgez(Register rt, MipsLabel* label); + void Blez(Register rt, MipsLabel* label); + void Bgtz(Register rt, MipsLabel* label); + void Blt(Register rs, Register rt, MipsLabel* label); + void Bge(Register rs, Register rt, MipsLabel* label); + void Bltu(Register rs, Register rt, MipsLabel* label); + void Bgeu(Register rs, Register rt, MipsLabel* label); void EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, size_t size); void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset); void LoadSFromOffset(FRegister reg, Register base, int32_t offset); - void LoadDFromOffset(DRegister reg, Register base, int32_t offset); + void LoadDFromOffset(FRegister reg, Register base, int32_t offset); void StoreToOffset(StoreOperandType type, Register reg, Register base, int32_t offset); void StoreSToOffset(FRegister reg, Register base, int32_t offset); - void StoreDToOffset(DRegister reg, Register base, int32_t offset); + void StoreDToOffset(FRegister reg, Register base, int32_t offset); // Emit data (e.g. encoded instruction or immediate) to the instruction stream. - void Emit(int32_t value); - void EmitBranch(Register rt, Register rs, Label* label, bool equal); - void EmitJump(Label* label, bool link); - void Bind(Label* label, bool is_jump); + void Emit(uint32_t value); + + // Push/pop composite routines. + void Push(Register rs); + void Pop(Register rd); + void PopAndReturn(Register rd, Register rt); void Bind(Label* label) OVERRIDE { - Bind(label, false); + Bind(down_cast<MipsLabel*>(label)); } - void Jump(Label* label) OVERRIDE { - EmitJump(label, false); + void Jump(Label* label ATTRIBUTE_UNUSED) OVERRIDE { + UNIMPLEMENTED(FATAL) << "Do not use Jump for MIPS"; } // - // Overridden common assembler high-level functionality + // Overridden common assembler high-level functionality. // - // Emit code that will create an activation on the stack + // Emit code that will create an activation on the stack. void BuildFrame(size_t frame_size, ManagedRegister method_reg, const std::vector<ManagedRegister>& callee_save_regs, const ManagedRegisterEntrySpills& entry_spills) OVERRIDE; - // Emit code that will remove an activation from the stack + // Emit code that will remove an activation from the stack. void RemoveFrame(size_t frame_size, const std::vector<ManagedRegister>& callee_save_regs) OVERRIDE; void IncreaseFrameSize(size_t adjust) OVERRIDE; void DecreaseFrameSize(size_t adjust) OVERRIDE; - // Store routines + // Store routines. void Store(FrameOffset offs, ManagedRegister msrc, size_t size) OVERRIDE; void StoreRef(FrameOffset dest, ManagedRegister msrc) OVERRIDE; void StoreRawPtr(FrameOffset dest, ManagedRegister msrc) OVERRIDE; @@ -191,7 +309,7 @@ class MipsAssembler FINAL : public Assembler { void StoreSpanning(FrameOffset dest, ManagedRegister msrc, FrameOffset in_off, ManagedRegister mscratch) OVERRIDE; - // Load routines + // Load routines. void Load(ManagedRegister mdest, FrameOffset src, size_t size) OVERRIDE; void LoadFromThread32(ManagedRegister mdest, ThreadOffset<4> src, size_t size) OVERRIDE; @@ -205,7 +323,7 @@ class MipsAssembler FINAL : public Assembler { void LoadRawPtrFromThread32(ManagedRegister mdest, ThreadOffset<4> offs) OVERRIDE; - // Copying routines + // Copying routines. void Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) OVERRIDE; void CopyRawPtrFromThread32(FrameOffset fr_offs, ThreadOffset<4> thr_offs, @@ -235,13 +353,13 @@ class MipsAssembler FINAL : public Assembler { void MemoryBarrier(ManagedRegister) OVERRIDE; - // Sign extension + // Sign extension. void SignExtend(ManagedRegister mreg, size_t size) OVERRIDE; - // Zero extension + // Zero extension. void ZeroExtend(ManagedRegister mreg, size_t size) OVERRIDE; - // Exploit fast access in managed code to Thread::Current() + // Exploit fast access in managed code to Thread::Current(). void GetCurrentThread(ManagedRegister tr) OVERRIDE; void GetCurrentThread(FrameOffset dest_offset, ManagedRegister mscratch) OVERRIDE; @@ -257,7 +375,7 @@ class MipsAssembler FINAL : public Assembler { void CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handlescope_offset, ManagedRegister mscratch, bool null_allowed) OVERRIDE; - // src holds a handle scope entry (Object**) load this into dst + // src holds a handle scope entry (Object**) load this into dst. void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) OVERRIDE; // Heap::VerifyObject on src. In some cases (such as a reference to this) we @@ -265,7 +383,7 @@ class MipsAssembler FINAL : public Assembler { void VerifyObject(ManagedRegister src, bool could_be_null) OVERRIDE; void VerifyObject(FrameOffset src, bool could_be_null) OVERRIDE; - // Call to address held at [base+offset] + // Call to address held at [base+offset]. void Call(ManagedRegister base, Offset offset, ManagedRegister mscratch) OVERRIDE; void Call(FrameOffset base, Offset offset, ManagedRegister mscratch) OVERRIDE; void CallFromThread32(ThreadOffset<4> offset, ManagedRegister mscratch) OVERRIDE; @@ -274,41 +392,251 @@ class MipsAssembler FINAL : public Assembler { // and branch to a ExceptionSlowPath if it is. void ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) OVERRIDE; + // Emit slow paths queued during assembly and promote short branches to long if needed. + void FinalizeCode() OVERRIDE; + + // Emit branches and finalize all instructions. + void FinalizeInstructions(const MemoryRegion& region); + + // Returns the (always-)current location of a label (can be used in class CodeGeneratorMIPS, + // must be used instead of MipsLabel::GetPosition()). + uint32_t GetLabelLocation(MipsLabel* label) const; + + // Get the final position of a label after local fixup based on the old position + // recorded before FinalizeCode(). + uint32_t GetAdjustedPosition(uint32_t old_position); + + enum BranchCondition { + kCondLT, + kCondGE, + kCondLE, + kCondGT, + kCondLTZ, + kCondGEZ, + kCondLEZ, + kCondGTZ, + kCondEQ, + kCondNE, + kCondEQZ, + kCondNEZ, + kCondLTU, + kCondGEU, + kUncond, + }; + friend std::ostream& operator<<(std::ostream& os, const BranchCondition& rhs); + private: + class Branch { + public: + enum Type { + // R2 short branches. + kUncondBranch, + kCondBranch, + kCall, + // R2 long branches. + kLongUncondBranch, + kLongCondBranch, + kLongCall, + // R6 short branches. + kR6UncondBranch, + kR6CondBranch, + kR6Call, + // R6 long branches. + kR6LongUncondBranch, + kR6LongCondBranch, + kR6LongCall, + }; + // Bit sizes of offsets defined as enums to minimize chance of typos. + enum OffsetBits { + kOffset16 = 16, + kOffset18 = 18, + kOffset21 = 21, + kOffset23 = 23, + kOffset28 = 28, + kOffset32 = 32, + }; + + static constexpr uint32_t kUnresolved = 0xffffffff; // Unresolved target_ + static constexpr int32_t kMaxBranchLength = 32; + static constexpr int32_t kMaxBranchSize = kMaxBranchLength * sizeof(uint32_t); + + struct BranchInfo { + // Branch length as a number of 4-byte-long instructions. + uint32_t length; + // Ordinal number (0-based) of the first (or the only) instruction that contains the branch's + // PC-relative offset (or its most significant 16-bit half, which goes first). + uint32_t instr_offset; + // Different MIPS instructions with PC-relative offsets apply said offsets to slightly + // different origins, e.g. to PC or PC+4. Encode the origin distance (as a number of 4-byte + // instructions) from the instruction containing the offset. + uint32_t pc_org; + // How large (in bits) a PC-relative offset can be for a given type of branch (kR6CondBranch + // is an exception: use kOffset23 for beqzc/bnezc). + OffsetBits offset_size; + // Some MIPS instructions with PC-relative offsets shift the offset by 2. Encode the shift + // count. + int offset_shift; + }; + static const BranchInfo branch_info_[/* Type */]; + + // Unconditional branch. + Branch(bool is_r6, uint32_t location, uint32_t target); + // Conditional branch. + Branch(bool is_r6, + uint32_t location, + uint32_t target, + BranchCondition condition, + Register lhs_reg, + Register rhs_reg = ZERO); + // Call (branch and link) that stores the target address in a given register (i.e. T9). + Branch(bool is_r6, uint32_t location, uint32_t target, Register indirect_reg); + + // Some conditional branches with lhs = rhs are effectively NOPs, while some + // others are effectively unconditional. MIPSR6 conditional branches require lhs != rhs. + // So, we need a way to identify such branches in order to emit no instructions for them + // or change them to unconditional. + static bool IsNop(BranchCondition condition, Register lhs, Register rhs); + static bool IsUncond(BranchCondition condition, Register lhs, Register rhs); + + static BranchCondition OppositeCondition(BranchCondition cond); + + Type GetType() const; + BranchCondition GetCondition() const; + Register GetLeftRegister() const; + Register GetRightRegister() const; + uint32_t GetTarget() const; + uint32_t GetLocation() const; + uint32_t GetOldLocation() const; + uint32_t GetLength() const; + uint32_t GetOldLength() const; + uint32_t GetSize() const; + uint32_t GetOldSize() const; + uint32_t GetEndLocation() const; + uint32_t GetOldEndLocation() const; + bool IsLong() const; + bool IsResolved() const; + + // Returns the bit size of the signed offset that the branch instruction can handle. + OffsetBits GetOffsetSize() const; + + // Calculates the distance between two byte locations in the assembler buffer and + // returns the number of bits needed to represent the distance as a signed integer. + // + // Branch instructions have signed offsets of 16, 19 (addiupc), 21 (beqzc/bnezc), + // and 26 (bc) bits, which are additionally shifted left 2 positions at run time. + // + // Composite branches (made of several instructions) with longer reach have 32-bit + // offsets encoded as 2 16-bit "halves" in two instructions (high half goes first). + // The composite branches cover the range of PC + +/-2GB. + // + // The returned values are therefore: 18, 21, 23, 28 and 32. There's also a special + // case with the addiu instruction and a 16 bit offset. + static OffsetBits GetOffsetSizeNeeded(uint32_t location, uint32_t target); + + // Resolve a branch when the target is known. + void Resolve(uint32_t target); + + // Relocate a branch by a given delta if needed due to expansion of this or another + // branch at a given location by this delta (just changes location_ and target_). + void Relocate(uint32_t expand_location, uint32_t delta); + + // If the branch is short, changes its type to long. + void PromoteToLong(); + + // If necessary, updates the type by promoting a short branch to a long branch + // based on the branch location and target. Returns the amount (in bytes) by + // which the branch size has increased. + // max_short_distance caps the maximum distance between location_ and target_ + // that is allowed for short branches. This is for debugging/testing purposes. + // max_short_distance = 0 forces all short branches to become long. + // Use the implicit default argument when not debugging/testing. + uint32_t PromoteIfNeeded(uint32_t max_short_distance = std::numeric_limits<uint32_t>::max()); + + // Returns the location of the instruction(s) containing the offset. + uint32_t GetOffsetLocation() const; + + // Calculates and returns the offset ready for encoding in the branch instruction(s). + uint32_t GetOffset() const; + + private: + // Completes branch construction by determining and recording its type. + void InitializeType(bool is_call, bool is_r6); + // Helper for the above. + void InitShortOrLong(OffsetBits ofs_size, Type short_type, Type long_type); + + uint32_t old_location_; // Offset into assembler buffer in bytes. + uint32_t location_; // Offset into assembler buffer in bytes. + uint32_t target_; // Offset into assembler buffer in bytes. + + uint32_t lhs_reg_ : 5; // Left-hand side register in conditional branches or + // indirect call register. + uint32_t rhs_reg_ : 5; // Right-hand side register in conditional branches. + BranchCondition condition_ : 5; // Condition for conditional branches. + + Type type_ : 5; // Current type of the branch. + Type old_type_ : 5; // Initial type of the branch. + }; + friend std::ostream& operator<<(std::ostream& os, const Branch::Type& rhs); + friend std::ostream& operator<<(std::ostream& os, const Branch::OffsetBits& rhs); + void EmitR(int opcode, Register rs, Register rt, Register rd, int shamt, int funct); void EmitI(int opcode, Register rs, Register rt, uint16_t imm); - void EmitJ(int opcode, int address); + void EmitI21(int opcode, Register rs, uint32_t imm21); + void EmitI26(int opcode, uint32_t imm26); void EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd, int funct); void EmitFI(int opcode, int fmt, FRegister rt, uint16_t imm); - - int32_t EncodeBranchOffset(int offset, int32_t inst, bool is_jump); - int DecodeBranchOffset(int32_t inst, bool is_jump); - - FRegister ConvertDRegToFReg(DRegister reg) { - return static_cast<FRegister>(reg * 2); - } - Register ConvertDRegToReg(DRegister reg) { - return static_cast<Register>(reg * 2); + void EmitBcond(BranchCondition cond, Register rs, Register rt, uint16_t imm16); + void EmitBcondc(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21); // R6 + + void Buncond(MipsLabel* label); + void Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs = ZERO); + void Call(MipsLabel* label, Register indirect_reg); + void FinalizeLabeledBranch(MipsLabel* label); + + Branch* GetBranch(uint32_t branch_id); + const Branch* GetBranch(uint32_t branch_id) const; + + void PromoteBranches(); + void EmitBranch(Branch* branch); + void EmitBranches(); + + // Emits exception block. + void EmitExceptionPoll(MipsExceptionSlowPath* exception); + + bool IsR6() const { + if (isa_features_ != nullptr) { + return isa_features_->IsR6(); + } else { + return false; + } } - Register ConvertFRegToReg(FRegister reg) { - return static_cast<Register>(reg); - } - FRegister ConvertRegToFReg(Register reg) { - return static_cast<FRegister>(reg); + + bool Is32BitFPU() const { + if (isa_features_ != nullptr) { + return isa_features_->Is32BitFloatingPoint(); + } else { + return true; + } } - DISALLOW_COPY_AND_ASSIGN(MipsAssembler); -}; + // List of exception blocks to generate at the end of the code cache. + std::vector<MipsExceptionSlowPath> exception_blocks_; -// Slowpath entered when Thread::Current()->_exception is non-null -class MipsExceptionSlowPath FINAL : public SlowPath { - public: - MipsExceptionSlowPath(MipsManagedRegister scratch, size_t stack_adjust) - : scratch_(scratch), stack_adjust_(stack_adjust) {} - virtual void Emit(Assembler *sp_asm) OVERRIDE; - private: - const MipsManagedRegister scratch_; - const size_t stack_adjust_; + std::vector<Branch> branches_; + + // Whether appending instructions at the end of the buffer or overwriting the existing ones. + bool overwriting_; + // The current overwrite location. + uint32_t overwrite_location_; + + // Data for AdjustedPosition(), see the description there. + uint32_t last_position_adjustment_; + uint32_t last_old_position_; + uint32_t last_branch_id_; + + const MipsInstructionSetFeatures* isa_features_; + + DISALLOW_COPY_AND_ASSIGN(MipsAssembler); }; } // namespace mips diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc new file mode 100644 index 0000000000..063d8bd825 --- /dev/null +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -0,0 +1,1324 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "assembler_mips.h" + +#include <map> + +#include "base/stl_util.h" +#include "utils/assembler_test.h" + +namespace art { + +struct MIPSCpuRegisterCompare { + bool operator()(const mips::Register& a, const mips::Register& b) const { + return a < b; + } +}; + +class AssemblerMIPSTest : public AssemblerTest<mips::MipsAssembler, + mips::Register, + mips::FRegister, + uint32_t> { + public: + typedef AssemblerTest<mips::MipsAssembler, mips::Register, mips::FRegister, uint32_t> Base; + + protected: + // Get the typically used name for this architecture, e.g., aarch64, x86-64, ... + std::string GetArchitectureString() OVERRIDE { + return "mips"; + } + + std::string GetAssemblerParameters() OVERRIDE { + return " --no-warn -32 -march=mips32r2"; + } + + std::string GetDisassembleParameters() OVERRIDE { + return " -D -bbinary -mmips:isa32r2"; + } + + void SetUpHelpers() OVERRIDE { + if (registers_.size() == 0) { + registers_.push_back(new mips::Register(mips::ZERO)); + registers_.push_back(new mips::Register(mips::AT)); + registers_.push_back(new mips::Register(mips::V0)); + registers_.push_back(new mips::Register(mips::V1)); + registers_.push_back(new mips::Register(mips::A0)); + registers_.push_back(new mips::Register(mips::A1)); + registers_.push_back(new mips::Register(mips::A2)); + registers_.push_back(new mips::Register(mips::A3)); + registers_.push_back(new mips::Register(mips::T0)); + registers_.push_back(new mips::Register(mips::T1)); + registers_.push_back(new mips::Register(mips::T2)); + registers_.push_back(new mips::Register(mips::T3)); + registers_.push_back(new mips::Register(mips::T4)); + registers_.push_back(new mips::Register(mips::T5)); + registers_.push_back(new mips::Register(mips::T6)); + registers_.push_back(new mips::Register(mips::T7)); + registers_.push_back(new mips::Register(mips::S0)); + registers_.push_back(new mips::Register(mips::S1)); + registers_.push_back(new mips::Register(mips::S2)); + registers_.push_back(new mips::Register(mips::S3)); + registers_.push_back(new mips::Register(mips::S4)); + registers_.push_back(new mips::Register(mips::S5)); + registers_.push_back(new mips::Register(mips::S6)); + registers_.push_back(new mips::Register(mips::S7)); + registers_.push_back(new mips::Register(mips::T8)); + registers_.push_back(new mips::Register(mips::T9)); + registers_.push_back(new mips::Register(mips::K0)); + registers_.push_back(new mips::Register(mips::K1)); + registers_.push_back(new mips::Register(mips::GP)); + registers_.push_back(new mips::Register(mips::SP)); + registers_.push_back(new mips::Register(mips::FP)); + registers_.push_back(new mips::Register(mips::RA)); + + secondary_register_names_.emplace(mips::Register(mips::ZERO), "zero"); + secondary_register_names_.emplace(mips::Register(mips::AT), "at"); + secondary_register_names_.emplace(mips::Register(mips::V0), "v0"); + secondary_register_names_.emplace(mips::Register(mips::V1), "v1"); + secondary_register_names_.emplace(mips::Register(mips::A0), "a0"); + secondary_register_names_.emplace(mips::Register(mips::A1), "a1"); + secondary_register_names_.emplace(mips::Register(mips::A2), "a2"); + secondary_register_names_.emplace(mips::Register(mips::A3), "a3"); + secondary_register_names_.emplace(mips::Register(mips::T0), "t0"); + secondary_register_names_.emplace(mips::Register(mips::T1), "t1"); + secondary_register_names_.emplace(mips::Register(mips::T2), "t2"); + secondary_register_names_.emplace(mips::Register(mips::T3), "t3"); + secondary_register_names_.emplace(mips::Register(mips::T4), "t4"); + secondary_register_names_.emplace(mips::Register(mips::T5), "t5"); + secondary_register_names_.emplace(mips::Register(mips::T6), "t6"); + secondary_register_names_.emplace(mips::Register(mips::T7), "t7"); + secondary_register_names_.emplace(mips::Register(mips::S0), "s0"); + secondary_register_names_.emplace(mips::Register(mips::S1), "s1"); + secondary_register_names_.emplace(mips::Register(mips::S2), "s2"); + secondary_register_names_.emplace(mips::Register(mips::S3), "s3"); + secondary_register_names_.emplace(mips::Register(mips::S4), "s4"); + secondary_register_names_.emplace(mips::Register(mips::S5), "s5"); + secondary_register_names_.emplace(mips::Register(mips::S6), "s6"); + secondary_register_names_.emplace(mips::Register(mips::S7), "s7"); + secondary_register_names_.emplace(mips::Register(mips::T8), "t8"); + secondary_register_names_.emplace(mips::Register(mips::T9), "t9"); + secondary_register_names_.emplace(mips::Register(mips::K0), "k0"); + secondary_register_names_.emplace(mips::Register(mips::K1), "k1"); + secondary_register_names_.emplace(mips::Register(mips::GP), "gp"); + secondary_register_names_.emplace(mips::Register(mips::SP), "sp"); + secondary_register_names_.emplace(mips::Register(mips::FP), "fp"); + secondary_register_names_.emplace(mips::Register(mips::RA), "ra"); + + fp_registers_.push_back(new mips::FRegister(mips::F0)); + fp_registers_.push_back(new mips::FRegister(mips::F1)); + fp_registers_.push_back(new mips::FRegister(mips::F2)); + fp_registers_.push_back(new mips::FRegister(mips::F3)); + fp_registers_.push_back(new mips::FRegister(mips::F4)); + fp_registers_.push_back(new mips::FRegister(mips::F5)); + fp_registers_.push_back(new mips::FRegister(mips::F6)); + fp_registers_.push_back(new mips::FRegister(mips::F7)); + fp_registers_.push_back(new mips::FRegister(mips::F8)); + fp_registers_.push_back(new mips::FRegister(mips::F9)); + fp_registers_.push_back(new mips::FRegister(mips::F10)); + fp_registers_.push_back(new mips::FRegister(mips::F11)); + fp_registers_.push_back(new mips::FRegister(mips::F12)); + fp_registers_.push_back(new mips::FRegister(mips::F13)); + fp_registers_.push_back(new mips::FRegister(mips::F14)); + fp_registers_.push_back(new mips::FRegister(mips::F15)); + fp_registers_.push_back(new mips::FRegister(mips::F16)); + fp_registers_.push_back(new mips::FRegister(mips::F17)); + fp_registers_.push_back(new mips::FRegister(mips::F18)); + fp_registers_.push_back(new mips::FRegister(mips::F19)); + fp_registers_.push_back(new mips::FRegister(mips::F20)); + fp_registers_.push_back(new mips::FRegister(mips::F21)); + fp_registers_.push_back(new mips::FRegister(mips::F22)); + fp_registers_.push_back(new mips::FRegister(mips::F23)); + fp_registers_.push_back(new mips::FRegister(mips::F24)); + fp_registers_.push_back(new mips::FRegister(mips::F25)); + fp_registers_.push_back(new mips::FRegister(mips::F26)); + fp_registers_.push_back(new mips::FRegister(mips::F27)); + fp_registers_.push_back(new mips::FRegister(mips::F28)); + fp_registers_.push_back(new mips::FRegister(mips::F29)); + fp_registers_.push_back(new mips::FRegister(mips::F30)); + fp_registers_.push_back(new mips::FRegister(mips::F31)); + } + } + + void TearDown() OVERRIDE { + AssemblerTest::TearDown(); + STLDeleteElements(®isters_); + STLDeleteElements(&fp_registers_); + } + + std::vector<mips::Register*> GetRegisters() OVERRIDE { + return registers_; + } + + std::vector<mips::FRegister*> GetFPRegisters() OVERRIDE { + return fp_registers_; + } + + uint32_t CreateImmediate(int64_t imm_value) OVERRIDE { + return imm_value; + } + + std::string GetSecondaryRegisterName(const mips::Register& reg) OVERRIDE { + CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end()); + return secondary_register_names_[reg]; + } + + std::string RepeatInsn(size_t count, const std::string& insn) { + std::string result; + for (; count != 0u; --count) { + result += insn; + } + return result; + } + + private: + std::vector<mips::Register*> registers_; + std::map<mips::Register, std::string, MIPSCpuRegisterCompare> secondary_register_names_; + + std::vector<mips::FRegister*> fp_registers_; +}; + + +TEST_F(AssemblerMIPSTest, Toolchain) { + EXPECT_TRUE(CheckTools()); +} + +#define __ GetAssembler()-> + +TEST_F(AssemblerMIPSTest, Addu) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Addu, "addu ${reg1}, ${reg2}, ${reg3}"), "Addu"); +} + +TEST_F(AssemblerMIPSTest, Addiu) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Addiu, -16, "addiu ${reg1}, ${reg2}, {imm}"), "Addiu"); +} + +TEST_F(AssemblerMIPSTest, Subu) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Subu, "subu ${reg1}, ${reg2}, ${reg3}"), "Subu"); +} + +TEST_F(AssemblerMIPSTest, MultR2) { + DriverStr(RepeatRR(&mips::MipsAssembler::MultR2, "mult ${reg1}, ${reg2}"), "MultR2"); +} + +TEST_F(AssemblerMIPSTest, MultuR2) { + DriverStr(RepeatRR(&mips::MipsAssembler::MultuR2, "multu ${reg1}, ${reg2}"), "MultuR2"); +} + +TEST_F(AssemblerMIPSTest, DivR2Basic) { + DriverStr(RepeatRR(&mips::MipsAssembler::DivR2, "div $zero, ${reg1}, ${reg2}"), "DivR2Basic"); +} + +TEST_F(AssemblerMIPSTest, DivuR2Basic) { + DriverStr(RepeatRR(&mips::MipsAssembler::DivuR2, "divu $zero, ${reg1}, ${reg2}"), "DivuR2Basic"); +} + +TEST_F(AssemblerMIPSTest, MulR2) { + DriverStr(RepeatRRR(&mips::MipsAssembler::MulR2, "mul ${reg1}, ${reg2}, ${reg3}"), "MulR2"); +} + +TEST_F(AssemblerMIPSTest, DivR2) { + DriverStr(RepeatRRR(&mips::MipsAssembler::DivR2, "div $zero, ${reg2}, ${reg3}\nmflo ${reg1}"), + "DivR2"); +} + +TEST_F(AssemblerMIPSTest, ModR2) { + DriverStr(RepeatRRR(&mips::MipsAssembler::ModR2, "div $zero, ${reg2}, ${reg3}\nmfhi ${reg1}"), + "ModR2"); +} + +TEST_F(AssemblerMIPSTest, DivuR2) { + DriverStr(RepeatRRR(&mips::MipsAssembler::DivuR2, "divu $zero, ${reg2}, ${reg3}\nmflo ${reg1}"), + "DivuR2"); +} + +TEST_F(AssemblerMIPSTest, ModuR2) { + DriverStr(RepeatRRR(&mips::MipsAssembler::ModuR2, "divu $zero, ${reg2}, ${reg3}\nmfhi ${reg1}"), + "ModuR2"); +} + +TEST_F(AssemblerMIPSTest, And) { + DriverStr(RepeatRRR(&mips::MipsAssembler::And, "and ${reg1}, ${reg2}, ${reg3}"), "And"); +} + +TEST_F(AssemblerMIPSTest, Andi) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Andi, 16, "andi ${reg1}, ${reg2}, {imm}"), "Andi"); +} + +TEST_F(AssemblerMIPSTest, Or) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Or, "or ${reg1}, ${reg2}, ${reg3}"), "Or"); +} + +TEST_F(AssemblerMIPSTest, Ori) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Ori, 16, "ori ${reg1}, ${reg2}, {imm}"), "Ori"); +} + +TEST_F(AssemblerMIPSTest, Xor) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Xor, "xor ${reg1}, ${reg2}, ${reg3}"), "Xor"); +} + +TEST_F(AssemblerMIPSTest, Xori) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Xori, 16, "xori ${reg1}, ${reg2}, {imm}"), "Xori"); +} + +TEST_F(AssemblerMIPSTest, Nor) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Nor, "nor ${reg1}, ${reg2}, ${reg3}"), "Nor"); +} + +TEST_F(AssemblerMIPSTest, Seb) { + DriverStr(RepeatRR(&mips::MipsAssembler::Seb, "seb ${reg1}, ${reg2}"), "Seb"); +} + +TEST_F(AssemblerMIPSTest, Seh) { + DriverStr(RepeatRR(&mips::MipsAssembler::Seh, "seh ${reg1}, ${reg2}"), "Seh"); +} + +TEST_F(AssemblerMIPSTest, Sll) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Sll, 5, "sll ${reg1}, ${reg2}, {imm}"), "Sll"); +} + +TEST_F(AssemblerMIPSTest, Srl) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Srl, 5, "srl ${reg1}, ${reg2}, {imm}"), "Srl"); +} + +TEST_F(AssemblerMIPSTest, Sra) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Sra, 5, "sra ${reg1}, ${reg2}, {imm}"), "Sra"); +} + +TEST_F(AssemblerMIPSTest, Sllv) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Sllv, "sllv ${reg1}, ${reg2}, ${reg3}"), "Sllv"); +} + +TEST_F(AssemblerMIPSTest, Srlv) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Srlv, "srlv ${reg1}, ${reg2}, ${reg3}"), "Srlv"); +} + +TEST_F(AssemblerMIPSTest, Srav) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Srav, "srav ${reg1}, ${reg2}, ${reg3}"), "Srav"); +} + +TEST_F(AssemblerMIPSTest, Lb) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Lb, -16, "lb ${reg1}, {imm}(${reg2})"), "Lb"); +} + +TEST_F(AssemblerMIPSTest, Lh) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Lh, -16, "lh ${reg1}, {imm}(${reg2})"), "Lh"); +} + +TEST_F(AssemblerMIPSTest, Lw) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Lw, -16, "lw ${reg1}, {imm}(${reg2})"), "Lw"); +} + +TEST_F(AssemblerMIPSTest, Lbu) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Lbu, -16, "lbu ${reg1}, {imm}(${reg2})"), "Lbu"); +} + +TEST_F(AssemblerMIPSTest, Lhu) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Lhu, -16, "lhu ${reg1}, {imm}(${reg2})"), "Lhu"); +} + +TEST_F(AssemblerMIPSTest, Lui) { + DriverStr(RepeatRIb(&mips::MipsAssembler::Lui, 16, "lui ${reg}, {imm}"), "Lui"); +} + +TEST_F(AssemblerMIPSTest, Mfhi) { + DriverStr(RepeatR(&mips::MipsAssembler::Mfhi, "mfhi ${reg}"), "Mfhi"); +} + +TEST_F(AssemblerMIPSTest, Mflo) { + DriverStr(RepeatR(&mips::MipsAssembler::Mflo, "mflo ${reg}"), "Mflo"); +} + +TEST_F(AssemblerMIPSTest, Sb) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Sb, -16, "sb ${reg1}, {imm}(${reg2})"), "Sb"); +} + +TEST_F(AssemblerMIPSTest, Sh) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Sh, -16, "sh ${reg1}, {imm}(${reg2})"), "Sh"); +} + +TEST_F(AssemblerMIPSTest, Sw) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Sw, -16, "sw ${reg1}, {imm}(${reg2})"), "Sw"); +} + +TEST_F(AssemblerMIPSTest, Slt) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Slt, "slt ${reg1}, ${reg2}, ${reg3}"), "Slt"); +} + +TEST_F(AssemblerMIPSTest, Sltu) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Sltu, "sltu ${reg1}, ${reg2}, ${reg3}"), "Sltu"); +} + +TEST_F(AssemblerMIPSTest, Slti) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Slti, -16, "slti ${reg1}, ${reg2}, {imm}"), "Slti"); +} + +TEST_F(AssemblerMIPSTest, Sltiu) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Sltiu, -16, "sltiu ${reg1}, ${reg2}, {imm}"), "Sltiu"); +} + +TEST_F(AssemblerMIPSTest, AddS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::AddS, "add.s ${reg1}, ${reg2}, ${reg3}"), "AddS"); +} + +TEST_F(AssemblerMIPSTest, AddD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::AddD, "add.d ${reg1}, ${reg2}, ${reg3}"), "AddD"); +} + +TEST_F(AssemblerMIPSTest, SubS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::SubS, "sub.s ${reg1}, ${reg2}, ${reg3}"), "SubS"); +} + +TEST_F(AssemblerMIPSTest, SubD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::SubD, "sub.d ${reg1}, ${reg2}, ${reg3}"), "SubD"); +} + +TEST_F(AssemblerMIPSTest, MulS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::MulS, "mul.s ${reg1}, ${reg2}, ${reg3}"), "MulS"); +} + +TEST_F(AssemblerMIPSTest, MulD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::MulD, "mul.d ${reg1}, ${reg2}, ${reg3}"), "MulD"); +} + +TEST_F(AssemblerMIPSTest, DivS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::DivS, "div.s ${reg1}, ${reg2}, ${reg3}"), "DivS"); +} + +TEST_F(AssemblerMIPSTest, DivD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::DivD, "div.d ${reg1}, ${reg2}, ${reg3}"), "DivD"); +} + +TEST_F(AssemblerMIPSTest, MovS) { + DriverStr(RepeatFF(&mips::MipsAssembler::MovS, "mov.s ${reg1}, ${reg2}"), "MovS"); +} + +TEST_F(AssemblerMIPSTest, MovD) { + DriverStr(RepeatFF(&mips::MipsAssembler::MovD, "mov.d ${reg1}, ${reg2}"), "MovD"); +} + +TEST_F(AssemblerMIPSTest, NegS) { + DriverStr(RepeatFF(&mips::MipsAssembler::NegS, "neg.s ${reg1}, ${reg2}"), "NegS"); +} + +TEST_F(AssemblerMIPSTest, NegD) { + DriverStr(RepeatFF(&mips::MipsAssembler::NegD, "neg.d ${reg1}, ${reg2}"), "NegD"); +} + +TEST_F(AssemblerMIPSTest, CvtSW) { + DriverStr(RepeatFF(&mips::MipsAssembler::Cvtsw, "cvt.s.w ${reg1}, ${reg2}"), "CvtSW"); +} + +TEST_F(AssemblerMIPSTest, CvtDW) { + DriverStr(RepeatFF(&mips::MipsAssembler::Cvtdw, "cvt.d.w ${reg1}, ${reg2}"), "CvtDW"); +} + +TEST_F(AssemblerMIPSTest, CvtSD) { + DriverStr(RepeatFF(&mips::MipsAssembler::Cvtsd, "cvt.s.d ${reg1}, ${reg2}"), "CvtSD"); +} + +TEST_F(AssemblerMIPSTest, CvtDS) { + DriverStr(RepeatFF(&mips::MipsAssembler::Cvtds, "cvt.d.s ${reg1}, ${reg2}"), "CvtDS"); +} + +TEST_F(AssemblerMIPSTest, Mfc1) { + DriverStr(RepeatRF(&mips::MipsAssembler::Mfc1, "mfc1 ${reg1}, ${reg2}"), "Mfc1"); +} + +TEST_F(AssemblerMIPSTest, Mtc1) { + DriverStr(RepeatRF(&mips::MipsAssembler::Mtc1, "mtc1 ${reg1}, ${reg2}"), "Mtc1"); +} + +TEST_F(AssemblerMIPSTest, Mfhc1) { + DriverStr(RepeatRF(&mips::MipsAssembler::Mfhc1, "mfhc1 ${reg1}, ${reg2}"), "Mfhc1"); +} + +TEST_F(AssemblerMIPSTest, Mthc1) { + DriverStr(RepeatRF(&mips::MipsAssembler::Mthc1, "mthc1 ${reg1}, ${reg2}"), "Mthc1"); +} + +TEST_F(AssemblerMIPSTest, Lwc1) { + DriverStr(RepeatFRIb(&mips::MipsAssembler::Lwc1, -16, "lwc1 ${reg1}, {imm}(${reg2})"), "Lwc1"); +} + +TEST_F(AssemblerMIPSTest, Ldc1) { + DriverStr(RepeatFRIb(&mips::MipsAssembler::Ldc1, -16, "ldc1 ${reg1}, {imm}(${reg2})"), "Ldc1"); +} + +TEST_F(AssemblerMIPSTest, Swc1) { + DriverStr(RepeatFRIb(&mips::MipsAssembler::Swc1, -16, "swc1 ${reg1}, {imm}(${reg2})"), "Swc1"); +} + +TEST_F(AssemblerMIPSTest, Sdc1) { + DriverStr(RepeatFRIb(&mips::MipsAssembler::Sdc1, -16, "sdc1 ${reg1}, {imm}(${reg2})"), "Sdc1"); +} + +TEST_F(AssemblerMIPSTest, Move) { + DriverStr(RepeatRR(&mips::MipsAssembler::Move, "or ${reg1}, ${reg2}, $zero"), "Move"); +} + +TEST_F(AssemblerMIPSTest, Clear) { + DriverStr(RepeatR(&mips::MipsAssembler::Clear, "or ${reg}, $zero, $zero"), "Clear"); +} + +TEST_F(AssemblerMIPSTest, Not) { + DriverStr(RepeatRR(&mips::MipsAssembler::Not, "nor ${reg1}, ${reg2}, $zero"), "Not"); +} + +TEST_F(AssemblerMIPSTest, LoadFromOffset) { + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A0, 0); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 256); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 1000); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0x8000); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0x10000); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0x12345678); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, -256); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0xFFFF8000); + __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0xABCDEF00); + + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A0, 0); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 256); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 1000); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0x8000); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0x10000); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0x12345678); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, -256); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0xFFFF8000); + __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0xABCDEF00); + + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A0, 0); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 256); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 1000); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0x8000); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0x10000); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0x12345678); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, -256); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0xFFFF8000); + __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0xABCDEF00); + + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A0, 0); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 256); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 1000); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0x8000); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0x10000); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0x12345678); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, -256); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0xFFFF8000); + __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0xABCDEF00); + + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A0, 0); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 256); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 1000); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0x8000); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0x10000); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0x12345678); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, -256); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0xFFFF8000); + __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0xABCDEF00); + + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A0, 0); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A1, 0); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A1, mips::A0, 0); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 256); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 1000); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0x8000); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0x10000); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0x12345678); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -256); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0xFFFF8000); + __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0xABCDEF00); + + const char* expected = + "lb $a0, 0($a0)\n" + "lb $a0, 0($a1)\n" + "lb $a0, 256($a1)\n" + "lb $a0, 1000($a1)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a1\n" + "lb $a0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a1\n" + "lb $a0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a1\n" + "lb $a0, 0($at)\n" + "lb $a0, -256($a1)\n" + "lb $a0, 0xFFFF8000($a1)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a1\n" + "lb $a0, 0($at)\n" + + "lbu $a0, 0($a0)\n" + "lbu $a0, 0($a1)\n" + "lbu $a0, 256($a1)\n" + "lbu $a0, 1000($a1)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a1\n" + "lbu $a0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a1\n" + "lbu $a0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a1\n" + "lbu $a0, 0($at)\n" + "lbu $a0, -256($a1)\n" + "lbu $a0, 0xFFFF8000($a1)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a1\n" + "lbu $a0, 0($at)\n" + + "lh $a0, 0($a0)\n" + "lh $a0, 0($a1)\n" + "lh $a0, 256($a1)\n" + "lh $a0, 1000($a1)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a1\n" + "lh $a0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a1\n" + "lh $a0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a1\n" + "lh $a0, 0($at)\n" + "lh $a0, -256($a1)\n" + "lh $a0, 0xFFFF8000($a1)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a1\n" + "lh $a0, 0($at)\n" + + "lhu $a0, 0($a0)\n" + "lhu $a0, 0($a1)\n" + "lhu $a0, 256($a1)\n" + "lhu $a0, 1000($a1)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a1\n" + "lhu $a0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a1\n" + "lhu $a0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a1\n" + "lhu $a0, 0($at)\n" + "lhu $a0, -256($a1)\n" + "lhu $a0, 0xFFFF8000($a1)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a1\n" + "lhu $a0, 0($at)\n" + + "lw $a0, 0($a0)\n" + "lw $a0, 0($a1)\n" + "lw $a0, 256($a1)\n" + "lw $a0, 1000($a1)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a1\n" + "lw $a0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a1\n" + "lw $a0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a1\n" + "lw $a0, 0($at)\n" + "lw $a0, -256($a1)\n" + "lw $a0, 0xFFFF8000($a1)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a1\n" + "lw $a0, 0($at)\n" + + "lw $a1, 4($a0)\n" + "lw $a0, 0($a0)\n" + "lw $a0, 0($a1)\n" + "lw $a1, 4($a1)\n" + "lw $a1, 0($a0)\n" + "lw $a2, 4($a0)\n" + "lw $a0, 0($a2)\n" + "lw $a1, 4($a2)\n" + "lw $a0, 256($a2)\n" + "lw $a1, 260($a2)\n" + "lw $a0, 1000($a2)\n" + "lw $a1, 1004($a2)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a2\n" + "lw $a0, 0($at)\n" + "lw $a1, 4($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a2\n" + "lw $a0, 0($at)\n" + "lw $a1, 4($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a2\n" + "lw $a0, 0($at)\n" + "lw $a1, 4($at)\n" + "lw $a0, -256($a2)\n" + "lw $a1, -252($a2)\n" + "lw $a0, 0xFFFF8000($a2)\n" + "lw $a1, 0xFFFF8004($a2)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a2\n" + "lw $a0, 0($at)\n" + "lw $a1, 4($at)\n"; + DriverStr(expected, "LoadFromOffset"); +} + +TEST_F(AssemblerMIPSTest, LoadSFromOffset) { + __ LoadSFromOffset(mips::F0, mips::A0, 0); + __ LoadSFromOffset(mips::F0, mips::A0, 4); + __ LoadSFromOffset(mips::F0, mips::A0, 256); + __ LoadSFromOffset(mips::F0, mips::A0, 0x8000); + __ LoadSFromOffset(mips::F0, mips::A0, 0x10000); + __ LoadSFromOffset(mips::F0, mips::A0, 0x12345678); + __ LoadSFromOffset(mips::F0, mips::A0, -256); + __ LoadSFromOffset(mips::F0, mips::A0, 0xFFFF8000); + __ LoadSFromOffset(mips::F0, mips::A0, 0xABCDEF00); + + const char* expected = + "lwc1 $f0, 0($a0)\n" + "lwc1 $f0, 4($a0)\n" + "lwc1 $f0, 256($a0)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a0\n" + "lwc1 $f0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a0\n" + "lwc1 $f0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a0\n" + "lwc1 $f0, 0($at)\n" + "lwc1 $f0, -256($a0)\n" + "lwc1 $f0, 0xFFFF8000($a0)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a0\n" + "lwc1 $f0, 0($at)\n"; + DriverStr(expected, "LoadSFromOffset"); +} + + +TEST_F(AssemblerMIPSTest, LoadDFromOffset) { + __ LoadDFromOffset(mips::F0, mips::A0, 0); + __ LoadDFromOffset(mips::F0, mips::A0, 4); + __ LoadDFromOffset(mips::F0, mips::A0, 256); + __ LoadDFromOffset(mips::F0, mips::A0, 0x8000); + __ LoadDFromOffset(mips::F0, mips::A0, 0x10000); + __ LoadDFromOffset(mips::F0, mips::A0, 0x12345678); + __ LoadDFromOffset(mips::F0, mips::A0, -256); + __ LoadDFromOffset(mips::F0, mips::A0, 0xFFFF8000); + __ LoadDFromOffset(mips::F0, mips::A0, 0xABCDEF00); + + const char* expected = + "ldc1 $f0, 0($a0)\n" + "lwc1 $f0, 4($a0)\n" + "lwc1 $f1, 8($a0)\n" + "ldc1 $f0, 256($a0)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a0\n" + "ldc1 $f0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a0\n" + "ldc1 $f0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a0\n" + "ldc1 $f0, 0($at)\n" + "ldc1 $f0, -256($a0)\n" + "ldc1 $f0, 0xFFFF8000($a0)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a0\n" + "ldc1 $f0, 0($at)\n"; + DriverStr(expected, "LoadDFromOffset"); +} + +TEST_F(AssemblerMIPSTest, StoreToOffset) { + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A0, 0); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 256); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 1000); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0x8000); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0x10000); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0x12345678); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, -256); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0xFFFF8000); + __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0xABCDEF00); + + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A0, 0); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 256); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 1000); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0x8000); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0x10000); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0x12345678); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, -256); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0xFFFF8000); + __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0xABCDEF00); + + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A0, 0); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 256); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 1000); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0x8000); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0x10000); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0x12345678); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, -256); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0xFFFF8000); + __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0xABCDEF00); + + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0); + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 256); + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 1000); + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0x8000); + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0x10000); + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0x12345678); + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -256); + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0xFFFF8000); + __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0xABCDEF00); + + const char* expected = + "sb $a0, 0($a0)\n" + "sb $a0, 0($a1)\n" + "sb $a0, 256($a1)\n" + "sb $a0, 1000($a1)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a1\n" + "sb $a0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a1\n" + "sb $a0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a1\n" + "sb $a0, 0($at)\n" + "sb $a0, -256($a1)\n" + "sb $a0, 0xFFFF8000($a1)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a1\n" + "sb $a0, 0($at)\n" + + "sh $a0, 0($a0)\n" + "sh $a0, 0($a1)\n" + "sh $a0, 256($a1)\n" + "sh $a0, 1000($a1)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a1\n" + "sh $a0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a1\n" + "sh $a0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a1\n" + "sh $a0, 0($at)\n" + "sh $a0, -256($a1)\n" + "sh $a0, 0xFFFF8000($a1)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a1\n" + "sh $a0, 0($at)\n" + + "sw $a0, 0($a0)\n" + "sw $a0, 0($a1)\n" + "sw $a0, 256($a1)\n" + "sw $a0, 1000($a1)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a1\n" + "sw $a0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a1\n" + "sw $a0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a1\n" + "sw $a0, 0($at)\n" + "sw $a0, -256($a1)\n" + "sw $a0, 0xFFFF8000($a1)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a1\n" + "sw $a0, 0($at)\n" + + "sw $a0, 0($a2)\n" + "sw $a1, 4($a2)\n" + "sw $a0, 256($a2)\n" + "sw $a1, 260($a2)\n" + "sw $a0, 1000($a2)\n" + "sw $a1, 1004($a2)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a2\n" + "sw $a0, 0($at)\n" + "sw $a1, 4($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a2\n" + "sw $a0, 0($at)\n" + "sw $a1, 4($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a2\n" + "sw $a0, 0($at)\n" + "sw $a1, 4($at)\n" + "sw $a0, -256($a2)\n" + "sw $a1, -252($a2)\n" + "sw $a0, 0xFFFF8000($a2)\n" + "sw $a1, 0xFFFF8004($a2)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a2\n" + "sw $a0, 0($at)\n" + "sw $a1, 4($at)\n"; + DriverStr(expected, "StoreToOffset"); +} + +TEST_F(AssemblerMIPSTest, StoreSToOffset) { + __ StoreSToOffset(mips::F0, mips::A0, 0); + __ StoreSToOffset(mips::F0, mips::A0, 4); + __ StoreSToOffset(mips::F0, mips::A0, 256); + __ StoreSToOffset(mips::F0, mips::A0, 0x8000); + __ StoreSToOffset(mips::F0, mips::A0, 0x10000); + __ StoreSToOffset(mips::F0, mips::A0, 0x12345678); + __ StoreSToOffset(mips::F0, mips::A0, -256); + __ StoreSToOffset(mips::F0, mips::A0, 0xFFFF8000); + __ StoreSToOffset(mips::F0, mips::A0, 0xABCDEF00); + + const char* expected = + "swc1 $f0, 0($a0)\n" + "swc1 $f0, 4($a0)\n" + "swc1 $f0, 256($a0)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a0\n" + "swc1 $f0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a0\n" + "swc1 $f0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a0\n" + "swc1 $f0, 0($at)\n" + "swc1 $f0, -256($a0)\n" + "swc1 $f0, 0xFFFF8000($a0)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a0\n" + "swc1 $f0, 0($at)\n"; + DriverStr(expected, "StoreSToOffset"); +} + +TEST_F(AssemblerMIPSTest, StoreDToOffset) { + __ StoreDToOffset(mips::F0, mips::A0, 0); + __ StoreDToOffset(mips::F0, mips::A0, 4); + __ StoreDToOffset(mips::F0, mips::A0, 256); + __ StoreDToOffset(mips::F0, mips::A0, 0x8000); + __ StoreDToOffset(mips::F0, mips::A0, 0x10000); + __ StoreDToOffset(mips::F0, mips::A0, 0x12345678); + __ StoreDToOffset(mips::F0, mips::A0, -256); + __ StoreDToOffset(mips::F0, mips::A0, 0xFFFF8000); + __ StoreDToOffset(mips::F0, mips::A0, 0xABCDEF00); + + const char* expected = + "sdc1 $f0, 0($a0)\n" + "swc1 $f0, 4($a0)\n" + "swc1 $f1, 8($a0)\n" + "sdc1 $f0, 256($a0)\n" + "ori $at, $zero, 0x8000\n" + "addu $at, $at, $a0\n" + "sdc1 $f0, 0($at)\n" + "lui $at, 1\n" + "addu $at, $at, $a0\n" + "sdc1 $f0, 0($at)\n" + "lui $at, 0x1234\n" + "ori $at, 0x5678\n" + "addu $at, $at, $a0\n" + "sdc1 $f0, 0($at)\n" + "sdc1 $f0, -256($a0)\n" + "sdc1 $f0, 0xFFFF8000($a0)\n" + "lui $at, 0xABCD\n" + "ori $at, 0xEF00\n" + "addu $at, $at, $a0\n" + "sdc1 $f0, 0($at)\n"; + DriverStr(expected, "StoreDToOffset"); +} + +TEST_F(AssemblerMIPSTest, B) { + mips::MipsLabel label1, label2; + __ B(&label1); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label1); + __ B(&label2); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label2); + __ B(&label1); + + std::string expected = + ".set noreorder\n" + "b 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + "b 2f\n" + "nop\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "2:\n" + "b 1b\n" + "nop\n"; + DriverStr(expected, "B"); +} + +TEST_F(AssemblerMIPSTest, Beq) { + mips::MipsLabel label; + __ Beq(mips::A0, mips::A1, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Beq(mips::A2, mips::A3, &label); + + std::string expected = + ".set noreorder\n" + "beq $a0, $a1, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "beq $a2, $a3, 1b\n" + "nop\n"; + DriverStr(expected, "Beq"); +} + +TEST_F(AssemblerMIPSTest, Bne) { + mips::MipsLabel label; + __ Bne(mips::A0, mips::A1, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bne(mips::A2, mips::A3, &label); + + std::string expected = + ".set noreorder\n" + "bne $a0, $a1, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "bne $a2, $a3, 1b\n" + "nop\n"; + DriverStr(expected, "Bne"); +} + +TEST_F(AssemblerMIPSTest, Beqz) { + mips::MipsLabel label; + __ Beqz(mips::A0, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Beqz(mips::A1, &label); + + std::string expected = + ".set noreorder\n" + "beq $zero, $a0, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "beq $zero, $a1, 1b\n" + "nop\n"; + DriverStr(expected, "Beqz"); +} + +TEST_F(AssemblerMIPSTest, Bnez) { + mips::MipsLabel label; + __ Bnez(mips::A0, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bnez(mips::A1, &label); + + std::string expected = + ".set noreorder\n" + "bne $zero, $a0, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "bne $zero, $a1, 1b\n" + "nop\n"; + DriverStr(expected, "Bnez"); +} + +TEST_F(AssemblerMIPSTest, Bltz) { + mips::MipsLabel label; + __ Bltz(mips::A0, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bltz(mips::A1, &label); + + std::string expected = + ".set noreorder\n" + "bltz $a0, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "bltz $a1, 1b\n" + "nop\n"; + DriverStr(expected, "Bltz"); +} + +TEST_F(AssemblerMIPSTest, Bgez) { + mips::MipsLabel label; + __ Bgez(mips::A0, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bgez(mips::A1, &label); + + std::string expected = + ".set noreorder\n" + "bgez $a0, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "bgez $a1, 1b\n" + "nop\n"; + DriverStr(expected, "Bgez"); +} + +TEST_F(AssemblerMIPSTest, Blez) { + mips::MipsLabel label; + __ Blez(mips::A0, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Blez(mips::A1, &label); + + std::string expected = + ".set noreorder\n" + "blez $a0, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "blez $a1, 1b\n" + "nop\n"; + DriverStr(expected, "Blez"); +} + +TEST_F(AssemblerMIPSTest, Bgtz) { + mips::MipsLabel label; + __ Bgtz(mips::A0, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bgtz(mips::A1, &label); + + std::string expected = + ".set noreorder\n" + "bgtz $a0, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "bgtz $a1, 1b\n" + "nop\n"; + DriverStr(expected, "Bgtz"); +} + +TEST_F(AssemblerMIPSTest, Blt) { + mips::MipsLabel label; + __ Blt(mips::A0, mips::A1, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Blt(mips::A2, mips::A3, &label); + + std::string expected = + ".set noreorder\n" + "slt $at, $a0, $a1\n" + "bne $zero, $at, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "slt $at, $a2, $a3\n" + "bne $zero, $at, 1b\n" + "nop\n"; + DriverStr(expected, "Blt"); +} + +TEST_F(AssemblerMIPSTest, Bge) { + mips::MipsLabel label; + __ Bge(mips::A0, mips::A1, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bge(mips::A2, mips::A3, &label); + + std::string expected = + ".set noreorder\n" + "slt $at, $a0, $a1\n" + "beq $zero, $at, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "slt $at, $a2, $a3\n" + "beq $zero, $at, 1b\n" + "nop\n"; + DriverStr(expected, "Bge"); +} + +TEST_F(AssemblerMIPSTest, Bltu) { + mips::MipsLabel label; + __ Bltu(mips::A0, mips::A1, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bltu(mips::A2, mips::A3, &label); + + std::string expected = + ".set noreorder\n" + "sltu $at, $a0, $a1\n" + "bne $zero, $at, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "sltu $at, $a2, $a3\n" + "bne $zero, $at, 1b\n" + "nop\n"; + DriverStr(expected, "Bltu"); +} + +TEST_F(AssemblerMIPSTest, Bgeu) { + mips::MipsLabel label; + __ Bgeu(mips::A0, mips::A1, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bgeu(mips::A2, mips::A3, &label); + + std::string expected = + ".set noreorder\n" + "sltu $at, $a0, $a1\n" + "beq $zero, $at, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + "sltu $at, $a2, $a3\n" + "beq $zero, $at, 1b\n" + "nop\n"; + DriverStr(expected, "Bgeu"); +} + +#undef __ + +} // namespace art diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index b078f3e4cf..00e8995bff 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -335,6 +335,10 @@ void Mips64Assembler::Sllv(GpuRegister rd, GpuRegister rt, GpuRegister rs) { EmitR(0, rs, rt, rd, 0, 0x04); } +void Mips64Assembler::Rotrv(GpuRegister rd, GpuRegister rt, GpuRegister rs) { + EmitR(0, rs, rt, rd, 1, 0x06); +} + void Mips64Assembler::Srlv(GpuRegister rd, GpuRegister rt, GpuRegister rs) { EmitR(0, rs, rt, rd, 0, 0x06); } @@ -351,6 +355,10 @@ void Mips64Assembler::Dsrl(GpuRegister rd, GpuRegister rt, int shamt) { EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3a); } +void Mips64Assembler::Drotr(GpuRegister rd, GpuRegister rt, int shamt) { + EmitR(0, static_cast<GpuRegister>(1), rt, rd, shamt, 0x3a); +} + void Mips64Assembler::Dsra(GpuRegister rd, GpuRegister rt, int shamt) { EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3b); } @@ -363,6 +371,10 @@ void Mips64Assembler::Dsrl32(GpuRegister rd, GpuRegister rt, int shamt) { EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3e); } +void Mips64Assembler::Drotr32(GpuRegister rd, GpuRegister rt, int shamt) { + EmitR(0, static_cast<GpuRegister>(1), rt, rd, shamt, 0x3e); +} + void Mips64Assembler::Dsra32(GpuRegister rd, GpuRegister rt, int shamt) { EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3f); } @@ -375,6 +387,10 @@ void Mips64Assembler::Dsrlv(GpuRegister rd, GpuRegister rt, GpuRegister rs) { EmitR(0, rs, rt, rd, 0, 0x16); } +void Mips64Assembler::Drotrv(GpuRegister rd, GpuRegister rt, GpuRegister rs) { + EmitR(0, rs, rt, rd, 1, 0x16); +} + void Mips64Assembler::Dsrav(GpuRegister rd, GpuRegister rt, GpuRegister rs) { EmitR(0, rs, rt, rd, 0, 0x17); } @@ -773,6 +789,10 @@ void Mips64Assembler::Cvtds(FpuRegister fd, FpuRegister fs) { EmitFR(0x11, 0x10, static_cast<FpuRegister>(0), fs, fd, 0x21); } +void Mips64Assembler::Cvtsl(FpuRegister fd, FpuRegister fs) { + EmitFR(0x11, 0x15, static_cast<FpuRegister>(0), fs, fd, 0x20); +} + void Mips64Assembler::Cvtdl(FpuRegister fd, FpuRegister fs) { EmitFR(0x11, 0x15, static_cast<FpuRegister>(0), fs, fd, 0x21); } diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index d083eb4306..33f22d2c2d 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -123,15 +123,19 @@ class Mips64Assembler FINAL : public Assembler { void Sra(GpuRegister rd, GpuRegister rt, int shamt); void Sllv(GpuRegister rd, GpuRegister rt, GpuRegister rs); void Srlv(GpuRegister rd, GpuRegister rt, GpuRegister rs); + void Rotrv(GpuRegister rd, GpuRegister rt, GpuRegister rs); void Srav(GpuRegister rd, GpuRegister rt, GpuRegister rs); void Dsll(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsrl(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 + void Drotr(GpuRegister rd, GpuRegister rt, int shamt); void Dsra(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsll32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsrl32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 + void Drotr32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsra32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsllv(GpuRegister rd, GpuRegister rt, GpuRegister rs); // MIPS64 void Dsrlv(GpuRegister rd, GpuRegister rt, GpuRegister rs); // MIPS64 + void Drotrv(GpuRegister rd, GpuRegister rt, GpuRegister rs); // MIPS64 void Dsrav(GpuRegister rd, GpuRegister rt, GpuRegister rs); // MIPS64 void Lb(GpuRegister rt, GpuRegister rs, uint16_t imm16); @@ -230,6 +234,7 @@ class Mips64Assembler FINAL : public Assembler { void Cvtdw(FpuRegister fd, FpuRegister fs); void Cvtsd(FpuRegister fd, FpuRegister fs); void Cvtds(FpuRegister fd, FpuRegister fs); + void Cvtsl(FpuRegister fd, FpuRegister fs); void Cvtdl(FpuRegister fd, FpuRegister fs); void Mfc1(GpuRegister rt, FpuRegister fs); diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index 2071aca546..16f29b00bc 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -215,6 +215,22 @@ TEST_F(AssemblerMIPS64Test, AbsD) { DriverStr(RepeatFF(&mips64::Mips64Assembler::AbsD, "abs.d ${reg1}, ${reg2}"), "abs.d"); } +TEST_F(AssemblerMIPS64Test, MovS) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::MovS, "mov.s ${reg1}, ${reg2}"), "mov.s"); +} + +TEST_F(AssemblerMIPS64Test, MovD) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::MovD, "mov.d ${reg1}, ${reg2}"), "mov.d"); +} + +TEST_F(AssemblerMIPS64Test, NegS) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::NegS, "neg.s ${reg1}, ${reg2}"), "neg.s"); +} + +TEST_F(AssemblerMIPS64Test, NegD) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::NegD, "neg.d ${reg1}, ${reg2}"), "neg.d"); +} + TEST_F(AssemblerMIPS64Test, RoundLS) { DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundLS, "round.l.s ${reg1}, ${reg2}"), "round.l.s"); } @@ -307,6 +323,34 @@ TEST_F(AssemblerMIPS64Test, CvtDL) { DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtdl, "cvt.d.l ${reg1}, ${reg2}"), "cvt.d.l"); } +TEST_F(AssemblerMIPS64Test, CvtDS) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtds, "cvt.d.s ${reg1}, ${reg2}"), "cvt.d.s"); +} + +TEST_F(AssemblerMIPS64Test, CvtDW) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtdw, "cvt.d.w ${reg1}, ${reg2}"), "cvt.d.w"); +} + +TEST_F(AssemblerMIPS64Test, CvtSL) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtsl, "cvt.s.l ${reg1}, ${reg2}"), "cvt.s.l"); +} + +TEST_F(AssemblerMIPS64Test, CvtSD) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtsd, "cvt.s.d ${reg1}, ${reg2}"), "cvt.s.d"); +} + +TEST_F(AssemblerMIPS64Test, CvtSW) { + DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtsw, "cvt.s.w ${reg1}, ${reg2}"), "cvt.s.w"); +} + +//////////////// +// CALL / JMP // +//////////////// + +TEST_F(AssemblerMIPS64Test, Jalr) { + DriverStr(RepeatRRNoDupes(&mips64::Mips64Assembler::Jalr, "jalr ${reg1}, ${reg2}"), "jalr"); +} + ////////// // MISC // ////////// @@ -319,6 +363,14 @@ TEST_F(AssemblerMIPS64Test, Dbitswap) { DriverStr(RepeatRR(&mips64::Mips64Assembler::Dbitswap, "dbitswap ${reg1}, ${reg2}"), "dbitswap"); } +TEST_F(AssemblerMIPS64Test, Seb) { + DriverStr(RepeatRR(&mips64::Mips64Assembler::Seb, "seb ${reg1}, ${reg2}"), "seb"); +} + +TEST_F(AssemblerMIPS64Test, Seh) { + DriverStr(RepeatRR(&mips64::Mips64Assembler::Seh, "seh ${reg1}, ${reg2}"), "seh"); +} + TEST_F(AssemblerMIPS64Test, Dsbh) { DriverStr(RepeatRR(&mips64::Mips64Assembler::Dsbh, "dsbh ${reg1}, ${reg2}"), "dsbh"); } @@ -331,6 +383,42 @@ TEST_F(AssemblerMIPS64Test, Wsbh) { DriverStr(RepeatRR(&mips64::Mips64Assembler::Wsbh, "wsbh ${reg1}, ${reg2}"), "wsbh"); } +TEST_F(AssemblerMIPS64Test, Sll) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sll, 5, "sll ${reg1}, ${reg2}, {imm}"), "sll"); +} + +TEST_F(AssemblerMIPS64Test, Srl) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Srl, 5, "srl ${reg1}, ${reg2}, {imm}"), "srl"); +} + +TEST_F(AssemblerMIPS64Test, Sra) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sra, 5, "sra ${reg1}, ${reg2}, {imm}"), "sra"); +} + +TEST_F(AssemblerMIPS64Test, Dsll) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Dsll, 5, "dsll ${reg1}, ${reg2}, {imm}"), "dsll"); +} + +TEST_F(AssemblerMIPS64Test, Dsrl) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Dsrl, 5, "dsrl ${reg1}, ${reg2}, {imm}"), "dsrl"); +} + +TEST_F(AssemblerMIPS64Test, Dsra) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Dsra, 5, "dsra ${reg1}, ${reg2}, {imm}"), "dsra"); +} + +TEST_F(AssemblerMIPS64Test, Dsll32) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Dsll32, 5, "dsll32 ${reg1}, ${reg2}, {imm}"), "dsll32"); +} + +TEST_F(AssemblerMIPS64Test, Dsrl32) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Dsrl32, 5, "dsrl32 ${reg1}, ${reg2}, {imm}"), "dsrl32"); +} + +TEST_F(AssemblerMIPS64Test, Dsra32) { + DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Dsra32, 5, "dsra32 ${reg1}, ${reg2}, {imm}"), "dsra32"); +} + TEST_F(AssemblerMIPS64Test, Sc) { DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sc, -9, "sc ${reg1}, {imm}(${reg2})"), "sc"); } diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 04e815aa1d..5347bf0302 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -2369,44 +2369,48 @@ void X86Assembler::AddConstantArea() { } } -int ConstantArea::AddInt32(int32_t v) { +size_t ConstantArea::AppendInt32(int32_t v) { + size_t result = buffer_.size() * elem_size_; + buffer_.push_back(v); + return result; +} + +size_t ConstantArea::AddInt32(int32_t v) { for (size_t i = 0, e = buffer_.size(); i < e; i++) { if (v == buffer_[i]) { - return i * kEntrySize; + return i * elem_size_; } } // Didn't match anything. - int result = buffer_.size() * kEntrySize; - buffer_.push_back(v); - return result; + return AppendInt32(v); } -int ConstantArea::AddInt64(int64_t v) { +size_t ConstantArea::AddInt64(int64_t v) { int32_t v_low = Low32Bits(v); int32_t v_high = High32Bits(v); if (buffer_.size() > 1) { // Ensure we don't pass the end of the buffer. for (size_t i = 0, e = buffer_.size() - 1; i < e; i++) { if (v_low == buffer_[i] && v_high == buffer_[i + 1]) { - return i * kEntrySize; + return i * elem_size_; } } } // Didn't match anything. - int result = buffer_.size() * kEntrySize; + size_t result = buffer_.size() * elem_size_; buffer_.push_back(v_low); buffer_.push_back(v_high); return result; } -int ConstantArea::AddDouble(double v) { +size_t ConstantArea::AddDouble(double v) { // Treat the value as a 64-bit integer value. return AddInt64(bit_cast<int64_t, double>(v)); } -int ConstantArea::AddFloat(float v) { +size_t ConstantArea::AddFloat(float v) { // Treat the value as a 32-bit integer value. return AddInt32(bit_cast<int32_t, float>(v)); } diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 93ecdf52fe..b50fda907a 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -166,6 +166,39 @@ class Address : public Operand { Init(base_in, disp.Int32Value()); } + Address(Register index_in, ScaleFactor scale_in, int32_t disp) { + CHECK_NE(index_in, ESP); // Illegal addressing mode. + SetModRM(0, ESP); + SetSIB(scale_in, index_in, EBP); + SetDisp32(disp); + } + + Address(Register base_in, Register index_in, ScaleFactor scale_in, int32_t disp) { + Init(base_in, index_in, scale_in, disp); + } + + Address(Register base_in, + Register index_in, + ScaleFactor scale_in, + int32_t disp, AssemblerFixup *fixup) { + Init(base_in, index_in, scale_in, disp); + SetFixup(fixup); + } + + static Address Absolute(uintptr_t addr) { + Address result; + result.SetModRM(0, EBP); + result.SetDisp32(addr); + return result; + } + + static Address Absolute(ThreadOffset<4> addr) { + return Absolute(addr.Int32Value()); + } + + private: + Address() {} + void Init(Register base_in, int32_t disp) { if (disp == 0 && base_in != EBP) { SetModRM(0, base_in); @@ -181,14 +214,7 @@ class Address : public Operand { } } - Address(Register index_in, ScaleFactor scale_in, int32_t disp) { - CHECK_NE(index_in, ESP); // Illegal addressing mode. - SetModRM(0, ESP); - SetSIB(scale_in, index_in, EBP); - SetDisp32(disp); - } - - Address(Register base_in, Register index_in, ScaleFactor scale_in, int32_t disp) { + void Init(Register base_in, Register index_in, ScaleFactor scale_in, int32_t disp) { CHECK_NE(index_in, ESP); // Illegal addressing mode. if (disp == 0 && base_in != EBP) { SetModRM(0, ESP); @@ -203,20 +229,6 @@ class Address : public Operand { SetDisp32(disp); } } - - static Address Absolute(uintptr_t addr) { - Address result; - result.SetModRM(0, EBP); - result.SetDisp32(addr); - return result; - } - - static Address Absolute(ThreadOffset<4> addr) { - return Absolute(addr.Int32Value()); - } - - private: - Address() {} }; @@ -252,40 +264,39 @@ class ConstantArea { // Add a double to the constant area, returning the offset into // the constant area where the literal resides. - int AddDouble(double v); + size_t AddDouble(double v); // Add a float to the constant area, returning the offset into // the constant area where the literal resides. - int AddFloat(float v); + size_t AddFloat(float v); // Add an int32_t to the constant area, returning the offset into // the constant area where the literal resides. - int AddInt32(int32_t v); + size_t AddInt32(int32_t v); + + // Add an int32_t to the end of the constant area, returning the offset into + // the constant area where the literal resides. + size_t AppendInt32(int32_t v); // Add an int64_t to the constant area, returning the offset into // the constant area where the literal resides. - int AddInt64(int64_t v); + size_t AddInt64(int64_t v); bool IsEmpty() const { return buffer_.size() == 0; } - const std::vector<int32_t>& GetBuffer() const { - return buffer_; - } - - void AddFixup(AssemblerFixup* fixup) { - fixups_.push_back(fixup); + size_t GetSize() const { + return buffer_.size() * elem_size_; } - const std::vector<AssemblerFixup*>& GetFixups() const { - return fixups_; + const std::vector<int32_t>& GetBuffer() const { + return buffer_; } private: - static constexpr size_t kEntrySize = sizeof(int32_t); + static constexpr size_t elem_size_ = sizeof(int32_t); std::vector<int32_t> buffer_; - std::vector<AssemblerFixup*> fixups_; }; class X86Assembler FINAL : public Assembler { @@ -740,26 +751,36 @@ class X86Assembler FINAL : public Assembler { // Add a double to the constant area, returning the offset into // the constant area where the literal resides. - int AddDouble(double v) { return constant_area_.AddDouble(v); } + size_t AddDouble(double v) { return constant_area_.AddDouble(v); } // Add a float to the constant area, returning the offset into // the constant area where the literal resides. - int AddFloat(float v) { return constant_area_.AddFloat(v); } + size_t AddFloat(float v) { return constant_area_.AddFloat(v); } // Add an int32_t to the constant area, returning the offset into // the constant area where the literal resides. - int AddInt32(int32_t v) { return constant_area_.AddInt32(v); } + size_t AddInt32(int32_t v) { + return constant_area_.AddInt32(v); + } + + // Add an int32_t to the end of the constant area, returning the offset into + // the constant area where the literal resides. + size_t AppendInt32(int32_t v) { + return constant_area_.AppendInt32(v); + } // Add an int64_t to the constant area, returning the offset into // the constant area where the literal resides. - int AddInt64(int64_t v) { return constant_area_.AddInt64(v); } + size_t AddInt64(int64_t v) { return constant_area_.AddInt64(v); } // Add the contents of the constant area to the assembler buffer. void AddConstantArea(); // Is the constant area empty? Return true if there are no literals in the constant area. bool IsConstantAreaEmpty() const { return constant_area_.IsEmpty(); } - void AddConstantAreaFixup(AssemblerFixup* fixup) { constant_area_.AddFixup(fixup); } + + // Return the current size of the constant area. + size_t ConstantAreaSize() const { return constant_area_.GetSize(); } private: inline void EmitUint8(uint8_t value); diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 6e7d74d528..9eb5e67041 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -3122,7 +3122,14 @@ void X86_64Assembler::AddConstantArea() { } } -int ConstantArea::AddInt32(int32_t v) { +size_t ConstantArea::AppendInt32(int32_t v) { + size_t result = buffer_.size() * elem_size_; + buffer_.push_back(v); + return result; +} + +size_t ConstantArea::AddInt32(int32_t v) { + // Look for an existing match. for (size_t i = 0, e = buffer_.size(); i < e; i++) { if (v == buffer_[i]) { return i * elem_size_; @@ -3130,12 +3137,10 @@ int ConstantArea::AddInt32(int32_t v) { } // Didn't match anything. - int result = buffer_.size() * elem_size_; - buffer_.push_back(v); - return result; + return AppendInt32(v); } -int ConstantArea::AddInt64(int64_t v) { +size_t ConstantArea::AddInt64(int64_t v) { int32_t v_low = v; int32_t v_high = v >> 32; if (buffer_.size() > 1) { @@ -3148,18 +3153,18 @@ int ConstantArea::AddInt64(int64_t v) { } // Didn't match anything. - int result = buffer_.size() * elem_size_; + size_t result = buffer_.size() * elem_size_; buffer_.push_back(v_low); buffer_.push_back(v_high); return result; } -int ConstantArea::AddDouble(double v) { +size_t ConstantArea::AddDouble(double v) { // Treat the value as a 64-bit integer value. return AddInt64(bit_cast<int64_t, double>(v)); } -int ConstantArea::AddFloat(float v) { +size_t ConstantArea::AddFloat(float v) { // Treat the value as a 32-bit integer value. return AddInt32(bit_cast<int32_t, float>(v)); } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 255f551675..01d28e305d 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -269,36 +269,40 @@ class Address : public Operand { * Class to handle constant area values. */ class ConstantArea { - public: - ConstantArea() {} + public: + ConstantArea() {} - // Add a double to the constant area, returning the offset into - // the constant area where the literal resides. - int AddDouble(double v); + // Add a double to the constant area, returning the offset into + // the constant area where the literal resides. + size_t AddDouble(double v); - // Add a float to the constant area, returning the offset into - // the constant area where the literal resides. - int AddFloat(float v); + // Add a float to the constant area, returning the offset into + // the constant area where the literal resides. + size_t AddFloat(float v); - // Add an int32_t to the constant area, returning the offset into - // the constant area where the literal resides. - int AddInt32(int32_t v); + // Add an int32_t to the constant area, returning the offset into + // the constant area where the literal resides. + size_t AddInt32(int32_t v); - // Add an int64_t to the constant area, returning the offset into - // the constant area where the literal resides. - int AddInt64(int64_t v); + // Add an int32_t to the end of the constant area, returning the offset into + // the constant area where the literal resides. + size_t AppendInt32(int32_t v); - int GetSize() const { - return buffer_.size() * elem_size_; - } + // Add an int64_t to the constant area, returning the offset into + // the constant area where the literal resides. + size_t AddInt64(int64_t v); - const std::vector<int32_t>& GetBuffer() const { - return buffer_; - } + size_t GetSize() const { + return buffer_.size() * elem_size_; + } - private: - static constexpr size_t elem_size_ = sizeof(int32_t); - std::vector<int32_t> buffer_; + const std::vector<int32_t>& GetBuffer() const { + return buffer_; + } + + private: + static constexpr size_t elem_size_ = sizeof(int32_t); + std::vector<int32_t> buffer_; }; @@ -806,19 +810,27 @@ class X86_64Assembler FINAL : public Assembler { // Add a double to the constant area, returning the offset into // the constant area where the literal resides. - int AddDouble(double v) { return constant_area_.AddDouble(v); } + size_t AddDouble(double v) { return constant_area_.AddDouble(v); } // Add a float to the constant area, returning the offset into // the constant area where the literal resides. - int AddFloat(float v) { return constant_area_.AddFloat(v); } + size_t AddFloat(float v) { return constant_area_.AddFloat(v); } // Add an int32_t to the constant area, returning the offset into // the constant area where the literal resides. - int AddInt32(int32_t v) { return constant_area_.AddInt32(v); } + size_t AddInt32(int32_t v) { + return constant_area_.AddInt32(v); + } + + // Add an int32_t to the end of the constant area, returning the offset into + // the constant area where the literal resides. + size_t AppendInt32(int32_t v) { + return constant_area_.AppendInt32(v); + } // Add an int64_t to the constant area, returning the offset into // the constant area where the literal resides. - int AddInt64(int64_t v) { return constant_area_.AddInt64(v); } + size_t AddInt64(int64_t v) { return constant_area_.AddInt64(v); } // Add the contents of the constant area to the assembler buffer. void AddConstantArea(); @@ -826,6 +838,9 @@ class X86_64Assembler FINAL : public Assembler { // Is the constant area empty? Return true if there are no literals in the constant area. bool IsConstantAreaEmpty() const { return constant_area_.GetSize() == 0; } + // Return the current size of the constant area. + size_t ConstantAreaSize() const { return constant_area_.GetSize(); } + // // Heap poisoning. // |