diff options
Diffstat (limited to 'compiler')
62 files changed, 3034 insertions, 929 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index b17cd52fad..6d656e63f1 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -158,11 +158,10 @@ define build-libart-compiler art_ndebug_or_debug := $(2) include $(CLEAR_VARS) - ifeq ($$(art_target_or_host),target) - include external/stlport/libstlport.mk - else + ifeq ($$(art_target_or_host),host) LOCAL_IS_HOST_MODULE := true endif + include art/build/Android.libcxx.mk LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) ifeq ($$(art_ndebug_or_debug),ndebug) LOCAL_MODULE := libart-compiler diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 9a21da070a..fdf09a50a6 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -299,7 +299,7 @@ class CommonCompilerTest : public CommonRuntimeTest { // for ARM, do a runtime check to make sure that the features we are passed from // the build match the features we actually determine at runtime. - ASSERT_EQ(instruction_set_features, runtime_features); + ASSERT_LE(instruction_set_features, runtime_features); #elif defined(__aarch64__) instruction_set = kArm64; // TODO: arm64 compilation support. diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index 8e013c1ece..59ed8277d0 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -82,21 +82,7 @@ uint32_t CompiledCode::AlignCode(uint32_t offset) const { } uint32_t CompiledCode::AlignCode(uint32_t offset, InstructionSet instruction_set) { - switch (instruction_set) { - case kArm: - case kThumb2: - return RoundUp(offset, kArmAlignment); - case kArm64: - return RoundUp(offset, kArm64Alignment); - case kMips: - return RoundUp(offset, kMipsAlignment); - case kX86: // Fall-through. - case kX86_64: - return RoundUp(offset, kX86Alignment); - default: - LOG(FATAL) << "Unknown InstructionSet: " << instruction_set; - return 0; - } + return RoundUp(offset, GetInstructionSetAlignment(instruction_set)); } size_t CompiledCode::CodeDelta() const { diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 6c8c85d16d..6f4fa3ab50 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -323,10 +323,6 @@ enum X86ConditionCode { std::ostream& operator<<(std::ostream& os, const X86ConditionCode& kind); enum ThrowKind { - kThrowNullPointer, - kThrowDivZero, - kThrowArrayBounds, - kThrowConstantArrayBounds, kThrowNoSuchMethod, }; diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index e48e5bf122..ed2ecace36 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -155,14 +155,16 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, cu.compiler_driver = &driver; cu.class_linker = class_linker; cu.instruction_set = driver.GetInstructionSet(); - cu.target64 = (cu.instruction_set == kX86_64) || (cu.instruction_set == kArm64); + if (cu.instruction_set == kArm) { + cu.instruction_set = kThumb2; + } + cu.target64 = Is64BitInstructionSet(cu.instruction_set); cu.compiler = compiler; // TODO: x86_64 & arm64 are not yet implemented. - DCHECK((cu.instruction_set == kThumb2) || - (cu.instruction_set == kX86) || - (cu.instruction_set == kX86_64) || - (cu.instruction_set == kMips)); - + CHECK((cu.instruction_set == kThumb2) || + (cu.instruction_set == kX86) || + (cu.instruction_set == kX86_64) || + (cu.instruction_set == kMips)); /* Adjust this value accordingly once inlining is performed */ cu.num_dalvik_registers = code_item->registers_size_; @@ -179,6 +181,17 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, (cu.enable_debug & (1 << kDebugVerbose)); } + if (gVerboseMethods.size() != 0) { + cu.verbose = false; + for (size_t i = 0; i < gVerboseMethods.size(); ++i) { + if (PrettyMethod(method_idx, dex_file).find(gVerboseMethods[i]) + != std::string::npos) { + cu.verbose = true; + break; + } + } + } + /* * TODO: rework handling of optimization and debug flags. Should we split out * MIR and backend flags? Need command-line setting as well. diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index 8dbc2bb9c3..c0068b2331 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -215,17 +215,13 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::CONST_STRING_JUMBO: case Instruction::CONST_CLASS: case Instruction::NEW_ARRAY: - if ((mir->optimization_flags & MIR_INLINED) == 0) { - // 1 result, treat as unique each time, use result s_reg - will be unique. - res = MarkNonAliasingNonNull(mir); - } + // 1 result, treat as unique each time, use result s_reg - will be unique. + res = MarkNonAliasingNonNull(mir); break; case Instruction::MOVE_RESULT_WIDE: - if ((mir->optimization_flags & MIR_INLINED) == 0) { - // 1 wide result, treat as unique each time, use result s_reg - will be unique. - res = GetOperandValueWide(mir->ssa_rep->defs[0]); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } + // 1 wide result, treat as unique each time, use result s_reg - will be unique. + res = GetOperandValueWide(mir->ssa_rep->defs[0]); + SetOperandValueWide(mir->ssa_rep->defs[0], res); break; case kMirOpPhi: diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index 1784af3653..c9acd66bba 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -454,8 +454,6 @@ enum ArmOpcode { kThumb2Vcmps, // vcmp [111011101] D [11010] rd[15-12] [1011] E [1] M [0] rm[3-0]. kThumb2LdrPcRel12, // ldr rd,[pc,#imm12] [1111100011011111] rt[15-12] imm12[11-0]. kThumb2BCond, // b<c> [1110] S cond[25-22] imm6[21-16] [10] J1 [0] J2 imm11[10..0]. - kThumb2Vmovd_RR, // vmov [111011101] D [110000] vd[15-12 [101101] M [0] vm[3-0]. - kThumb2Vmovs_RR, // vmov [111011101] D [110000] vd[15-12 [101001] M [0] vm[3-0]. kThumb2Fmrs, // vmov [111011100000] vn[19-16] rt[15-12] [1010] N [0010000]. kThumb2Fmsr, // vmov [111011100001] vn[19-16] rt[15-12] [1010] N [0010000]. kThumb2Fmrrd, // vmov [111011000100] rt2[19-16] rt[15-12] [101100] M [1] vm[3-0]. diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 1c35018be3..f77b0a6302 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -848,14 +848,6 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES | NEEDS_FIXUP, "b!1c", "!0t", 4, kFixupCondBranch), - ENCODING_MAP(kThumb2Vmovd_RR, 0xeeb00b40, - kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vmov.f64", "!0S, !1S", 4, kFixupNone), - ENCODING_MAP(kThumb2Vmovs_RR, 0xeeb00a40, - kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vmov.f32", "!0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2Fmrs, 0xee100a10, kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 13fa6353b0..b0bc11d458 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -131,7 +131,7 @@ class ArmMir2Lir FINAL : public Mir2Lir { RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenDivZeroCheck(RegStorage reg); + void GenDivZeroCheckWide(RegStorage reg); void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); void GenExitSequence(); void GenSpecialExitSequence(); diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 1c563bb126..d5b34a5c39 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -90,8 +90,7 @@ void ArmMir2Lir::OpEndIT(LIR* it) { * neg rX * done: */ -void ArmMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { +void ArmMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { LIR* target1; LIR* target2; rl_src1 = LoadValueWide(rl_src1, kCoreReg); @@ -101,7 +100,7 @@ void ArmMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, OpRegReg(kOpCmp, rl_src1.reg.GetHigh(), rl_src2.reg.GetHigh()); LIR* branch1 = OpCondBranch(kCondLt, NULL); LIR* branch2 = OpCondBranch(kCondGt, NULL); - OpRegRegReg(kOpSub, t_reg, rl_src1.reg, rl_src2.reg); + OpRegRegReg(kOpSub, t_reg, rl_src1.reg.GetLow(), rl_src2.reg.GetLow()); LIR* branch3 = OpCondBranch(kCondEq, NULL); LIR* it = OpIT(kCondHi, "E"); @@ -434,10 +433,6 @@ bool ArmMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div if (pattern == DivideNone) { return false; } - // Tuning: add rem patterns - if (!is_div) { - return false; - } RegStorage r_magic = AllocTemp(); LoadConstant(r_magic, magic_table[lit].magic); @@ -445,25 +440,45 @@ bool ArmMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage r_hi = AllocTemp(); RegStorage r_lo = AllocTemp(); + + // rl_dest and rl_src might overlap. + // Reuse r_hi to save the div result for reminder case. + RegStorage r_div_result = is_div ? rl_result.reg : r_hi; + NewLIR4(kThumb2Smull, r_lo.GetReg(), r_hi.GetReg(), r_magic.GetReg(), rl_src.reg.GetReg()); switch (pattern) { case Divide3: - OpRegRegRegShift(kOpSub, rl_result.reg, r_hi, rl_src.reg, EncodeShift(kArmAsr, 31)); + OpRegRegRegShift(kOpSub, r_div_result, r_hi, rl_src.reg, EncodeShift(kArmAsr, 31)); break; case Divide5: OpRegRegImm(kOpAsr, r_lo, rl_src.reg, 31); - OpRegRegRegShift(kOpRsub, rl_result.reg, r_lo, r_hi, + OpRegRegRegShift(kOpRsub, r_div_result, r_lo, r_hi, EncodeShift(kArmAsr, magic_table[lit].shift)); break; case Divide7: OpRegReg(kOpAdd, r_hi, rl_src.reg); OpRegRegImm(kOpAsr, r_lo, rl_src.reg, 31); - OpRegRegRegShift(kOpRsub, rl_result.reg, r_lo, r_hi, + OpRegRegRegShift(kOpRsub, r_div_result, r_lo, r_hi, EncodeShift(kArmAsr, magic_table[lit].shift)); break; default: LOG(FATAL) << "Unexpected pattern: " << pattern; } + + if (!is_div) { + // div_result = src / lit + // tmp1 = div_result * lit + // dest = src - tmp1 + RegStorage tmp1 = r_lo; + EasyMultiplyOp ops[2]; + + bool canEasyMultiply = GetEasyMultiplyTwoOps(lit, ops); + DCHECK_NE(canEasyMultiply, false); + + GenEasyMultiplyTwoOps(tmp1, r_div_result, ops); + OpRegRegReg(kOpSub, rl_result.reg, rl_src.reg, tmp1); + } + StoreValue(rl_dest, rl_result); return true; } @@ -489,6 +504,7 @@ bool ArmMir2Lir::GetEasyMultiplyOp(int lit, ArmMir2Lir::EasyMultiplyOp* op) { } op->op = kOpInvalid; + op->shift = 0; return false; } @@ -497,6 +513,7 @@ bool ArmMir2Lir::GetEasyMultiplyTwoOps(int lit, EasyMultiplyOp* ops) { GetEasyMultiplyOp(lit, &ops[0]); if (GetEasyMultiplyOp(lit, &ops[0])) { ops[1].op = kOpInvalid; + ops[1].shift = 0; return true; } @@ -527,31 +544,52 @@ bool ArmMir2Lir::GetEasyMultiplyTwoOps(int lit, EasyMultiplyOp* ops) { return false; } +// Generate instructions to do multiply. +// Additional temporary register is required, +// if it need to generate 2 instructions and src/dest overlap. void ArmMir2Lir::GenEasyMultiplyTwoOps(RegStorage r_dest, RegStorage r_src, EasyMultiplyOp* ops) { - // dest = ( src << shift1) + [ src | -src | 0 ] - // dest = (dest << shift2) + [ src | -src | 0 ] - for (int i = 0; i < 2; i++) { - RegStorage r_src2; - if (i == 0) { - r_src2 = r_src; - } else { - r_src2 = r_dest; - } - switch (ops[i].op) { + // tmp1 = ( src << shift1) + [ src | -src | 0 ] + // dest = (tmp1 << shift2) + [ src | -src | 0 ] + + RegStorage r_tmp1; + if (ops[1].op == kOpInvalid) { + r_tmp1 = r_dest; + } else if (r_dest.GetReg() != r_src.GetReg()) { + r_tmp1 = r_dest; + } else { + r_tmp1 = AllocTemp(); + } + + switch (ops[0].op) { case kOpLsl: - OpRegRegImm(kOpLsl, r_dest, r_src2, ops[i].shift); + OpRegRegImm(kOpLsl, r_tmp1, r_src, ops[0].shift); break; case kOpAdd: - OpRegRegRegShift(kOpAdd, r_dest, r_src, r_src2, EncodeShift(kArmLsl, ops[i].shift)); + OpRegRegRegShift(kOpAdd, r_tmp1, r_src, r_src, EncodeShift(kArmLsl, ops[0].shift)); break; case kOpRsub: - OpRegRegRegShift(kOpRsub, r_dest, r_src, r_src2, EncodeShift(kArmLsl, ops[i].shift)); + OpRegRegRegShift(kOpRsub, r_tmp1, r_src, r_src, EncodeShift(kArmLsl, ops[0].shift)); break; default: - DCHECK_NE(i, 0); - DCHECK_EQ(ops[i].op, kOpInvalid); + DCHECK_EQ(ops[0].op, kOpInvalid); + break; + } + + switch (ops[1].op) { + case kOpInvalid: + return; + case kOpLsl: + OpRegRegImm(kOpLsl, r_dest, r_tmp1, ops[1].shift); + break; + case kOpAdd: + OpRegRegRegShift(kOpAdd, r_dest, r_src, r_tmp1, EncodeShift(kArmLsl, ops[1].shift)); + break; + case kOpRsub: + OpRegRegRegShift(kOpRsub, r_dest, r_src, r_tmp1, EncodeShift(kArmLsl, ops[1].shift)); + break; + default: + LOG(FATAL) << "Unexpected opcode passed to GenEasyMultiplyTwoOps"; break; - } } } @@ -873,12 +911,12 @@ void ArmMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, } } -void ArmMir2Lir::GenDivZeroCheck(RegStorage reg) { +void ArmMir2Lir::GenDivZeroCheckWide(RegStorage reg) { DCHECK(reg.IsPair()); // TODO: support k64BitSolo. RegStorage t_reg = AllocTemp(); NewLIR4(kThumb2OrrRRRs, t_reg.GetReg(), reg.GetLowReg(), reg.GetHighReg(), 0); FreeTemp(t_reg); - GenCheck(kCondEq, kThrowDivZero); + GenDivZeroCheck(kCondEq); } // Test suspend flag, return target of taken suspend branch @@ -1129,9 +1167,9 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, if (needs_range_check) { if (constant_index) { - GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds); + GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len); } else { - GenRegRegCheck(kCondLs, reg_len, rl_index.reg, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); } FreeTemp(reg_len); } @@ -1158,7 +1196,7 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); @@ -1233,9 +1271,9 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } if (needs_range_check) { if (constant_index) { - GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds); + GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len); } else { - GenRegRegCheck(kCondLs, reg_len, rl_index.reg, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); } FreeTemp(reg_len); } @@ -1251,7 +1289,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size); diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 5e9a8b0b5c..1053a8fc41 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -562,7 +562,8 @@ void ArmMir2Lir::CompilerInitializeRegAlloc() { // Keep special registers from being allocated // Don't reserve the r4 if we are doing implicit suspend checks. - bool no_suspend = NO_SUSPEND || !Runtime::Current()->ExplicitSuspendChecks(); + // TODO: re-enable this when we can safely save r4 over the suspension code path. + bool no_suspend = NO_SUSPEND; // || !Runtime::Current()->ExplicitSuspendChecks(); for (int i = 0; i < num_reserved; i++) { if (no_suspend && (ReservedRegs[i] == rARM_SUSPEND)) { // Don't reserve the suspend register. diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index fa6de963a0..8806e68b93 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -47,6 +47,22 @@ MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke, MIR* move_return) { return insn; } +uint32_t GetInvokeReg(MIR* invoke, uint32_t arg) { + DCHECK_LT(arg, invoke->dalvikInsn.vA); + if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) { + return invoke->dalvikInsn.vC + arg; // Non-range invoke. + } else { + DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k35c); + return invoke->dalvikInsn.arg[arg]; // Range invoke. + } +} + +bool WideArgIsInConsecutiveDalvikRegs(MIR* invoke, uint32_t arg) { + DCHECK_LT(arg + 1, invoke->dalvikInsn.vA); + return Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc || + invoke->dalvikInsn.arg[arg + 1u] == invoke->dalvikInsn.arg[arg] + 1u; +} + } // anonymous namespace const uint32_t DexFileMethodInliner::kIndexUnresolved; @@ -396,7 +412,8 @@ bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* i result = GenInlineIGet(mir_graph, bb, invoke, move_result, method, method_idx); break; case kInlineOpIPut: - result = GenInlineIPut(mir_graph, bb, invoke, method, method_idx); + move_result = mir_graph->FindMoveResult(bb, invoke); + result = GenInlineIPut(mir_graph, bb, invoke, move_result, method, method_idx); break; default: LOG(FATAL) << "Unexpected inline op: " << method.opcode; @@ -578,25 +595,24 @@ bool DexFileMethodInliner::GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* b // Select opcode and argument. const InlineReturnArgData& data = method.d.return_data; Instruction::Code opcode = Instruction::MOVE_FROM16; + uint32_t arg = GetInvokeReg(invoke, data.arg); if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { DCHECK_EQ(data.is_object, 1u); + DCHECK_EQ(data.is_wide, 0u); opcode = Instruction::MOVE_OBJECT_FROM16; } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE) { DCHECK_EQ(data.is_wide, 1u); + DCHECK_EQ(data.is_object, 0u); opcode = Instruction::MOVE_WIDE_FROM16; + if (!WideArgIsInConsecutiveDalvikRegs(invoke, data.arg)) { + // The two halfs of the source value are not in consecutive dalvik registers in INVOKE. + return false; + } } else { DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT); DCHECK_EQ(data.is_wide, 0u); DCHECK_EQ(data.is_object, 0u); } - DCHECK_LT(data.is_wide ? data.arg + 1u : data.arg, invoke->dalvikInsn.vA); - int arg; - if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k35c) { - arg = invoke->dalvikInsn.arg[data.arg]; // Non-range invoke. - } else { - DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k3rc); - arg = invoke->dalvikInsn.vC + data.arg; // Range invoke. - } // Insert the move instruction MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); @@ -616,33 +632,39 @@ bool DexFileMethodInliner::GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MI } const InlineIGetIPutData& data = method.d.ifield_data; - if (invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || - invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE || - data.object_arg != 0) { - // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE). - return false; - } + Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IGET + data.op_variant); + DCHECK_EQ(InlineMethodAnalyser::IGetVariant(opcode), data.op_variant); + uint32_t object_reg = GetInvokeReg(invoke, data.object_arg); if (move_result == nullptr) { // Result is unused. If volatile, we still need to emit the IGET but we have no destination. return !data.is_volatile; } - Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IGET + data.op_variant); - DCHECK_EQ(InlineMethodAnalyser::IGetVariant(opcode), data.op_variant); + DCHECK_EQ(data.method_is_static != 0u, + invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || + invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE); + bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u); + if (!object_is_this) { + // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE). + // Allow synthetic accessors. We don't care about losing their stack frame in NPE. + if (!InlineMethodAnalyser::IsSyntheticAccessor( + mir_graph->GetMethodLoweringInfo(invoke).GetTargetMethod())) { + return false; + } + } + + if (object_is_this) { + // Mark invoke as NOP, null-check is done on IGET. No aborts after this. + invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); + } MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); insn->width += insn->offset - invoke->offset; insn->offset = invoke->offset; insn->dalvikInsn.opcode = opcode; insn->dalvikInsn.vA = move_result->dalvikInsn.vA; - DCHECK_LT(data.object_arg, invoke->dalvikInsn.vA); - if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) { - insn->dalvikInsn.vB = invoke->dalvikInsn.vC + data.object_arg; - } else { - DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k35c); - insn->dalvikInsn.vB = invoke->dalvikInsn.arg[data.object_arg]; - } + insn->dalvikInsn.vB = object_reg; mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn); DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved()); @@ -655,32 +677,55 @@ bool DexFileMethodInliner::GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MI } bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, - const InlineMethod& method, uint32_t method_idx) { + MIR* move_result, const InlineMethod& method, + uint32_t method_idx) { CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit(); if (cu->enable_debug & (1 << kDebugSlowFieldPath)) { return false; } const InlineIGetIPutData& data = method.d.ifield_data; - if (invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || - invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE || - data.object_arg != 0) { - // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE). + Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IPUT + data.op_variant); + DCHECK_EQ(InlineMethodAnalyser::IPutVariant(opcode), data.op_variant); + uint32_t object_reg = GetInvokeReg(invoke, data.object_arg); + uint32_t src_reg = GetInvokeReg(invoke, data.src_arg); + uint32_t return_reg = + data.return_arg_plus1 != 0u ? GetInvokeReg(invoke, data.return_arg_plus1 - 1u) : 0u; + + if (opcode == Instruction::IPUT_WIDE && !WideArgIsInConsecutiveDalvikRegs(invoke, data.src_arg)) { + // The two halfs of the source value are not in consecutive dalvik registers in INVOKE. return false; } - Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IPUT + data.op_variant); - DCHECK_EQ(InlineMethodAnalyser::IPutVariant(opcode), data.op_variant); + DCHECK(move_result == nullptr || data.return_arg_plus1 != 0u); + if (move_result != nullptr && move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE && + !WideArgIsInConsecutiveDalvikRegs(invoke, data.return_arg_plus1 - 1u)) { + // The two halfs of the return value are not in consecutive dalvik registers in INVOKE. + return false; + } - MIR* insn = AllocReplacementMIR(mir_graph, invoke, nullptr); - insn->dalvikInsn.opcode = opcode; - if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) { - insn->dalvikInsn.vA = invoke->dalvikInsn.vC + data.src_arg; - insn->dalvikInsn.vB = invoke->dalvikInsn.vC + data.object_arg; - } else { - insn->dalvikInsn.vA = invoke->dalvikInsn.arg[data.src_arg]; - insn->dalvikInsn.vB = invoke->dalvikInsn.arg[data.object_arg]; + DCHECK_EQ(data.method_is_static != 0u, + invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || + invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE); + bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u); + if (!object_is_this) { + // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE). + // Allow synthetic accessors. We don't care about losing their stack frame in NPE. + if (!InlineMethodAnalyser::IsSyntheticAccessor( + mir_graph->GetMethodLoweringInfo(invoke).GetTargetMethod())) { + return false; + } + } + + if (object_is_this) { + // Mark invoke as NOP, null-check is done on IPUT. No aborts after this. + invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); } + + MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); + insn->dalvikInsn.opcode = opcode; + insn->dalvikInsn.vA = src_reg; + insn->dalvikInsn.vB = object_reg; mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn); DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved()); @@ -689,6 +734,24 @@ bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MI DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u); bb->InsertMIRAfter(invoke, insn); + + if (move_result != nullptr) { + MIR* move = AllocReplacementMIR(mir_graph, invoke, move_result); + insn->width = invoke->width; + move->offset = move_result->offset; + move->width = move_result->width; + if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT) { + move->dalvikInsn.opcode = Instruction::MOVE_FROM16; + } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { + move->dalvikInsn.opcode = Instruction::MOVE_OBJECT_FROM16; + } else { + DCHECK_EQ(move_result->dalvikInsn.opcode, Instruction::MOVE_RESULT_WIDE); + move->dalvikInsn.opcode = Instruction::MOVE_WIDE_FROM16; + } + move->dalvikInsn.vA = move_result->dalvikInsn.vA; + move->dalvikInsn.vB = return_reg; + bb->InsertMIRAfter(insn, move); + } return true; } diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index b4e190a89e..c03f89c8fa 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -302,7 +302,7 @@ class DexFileMethodInliner { static bool GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, MIR* move_result, const InlineMethod& method, uint32_t method_idx); static bool GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, - const InlineMethod& method, uint32_t method_idx); + MIR* move_result, const InlineMethod& method, uint32_t method_idx); ReaderWriterMutex lock_; /* diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index bfa22dab93..6781a9bed4 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -42,17 +42,6 @@ void Mir2Lir::GenBarrier() { barrier->u.m.def_mask = ENCODE_ALL; } -// TODO: need to do some work to split out targets with -// condition codes and those without -LIR* Mir2Lir::GenCheck(ConditionCode c_code, ThrowKind kind) { - DCHECK_NE(cu_->instruction_set, kMips); - LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_); - LIR* branch = OpCondBranch(c_code, tgt); - // Remember branch target - will process later - throw_launchpads_.Insert(tgt); - return branch; -} - LIR* Mir2Lir::GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, ThrowKind kind) { LIR* tgt; LIR* branch; @@ -69,6 +58,111 @@ LIR* Mir2Lir::GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, T return branch; } +void Mir2Lir::GenDivZeroException() { + LIR* branch = OpUnconditionalBranch(nullptr); + AddDivZeroCheckSlowPath(branch); +} + +void Mir2Lir::GenDivZeroCheck(ConditionCode c_code) { + LIR* branch = OpCondBranch(c_code, nullptr); + AddDivZeroCheckSlowPath(branch); +} + +void Mir2Lir::GenDivZeroCheck(RegStorage reg) { + LIR* branch = OpCmpImmBranch(kCondEq, reg, 0, nullptr); + AddDivZeroCheckSlowPath(branch); +} + +void Mir2Lir::AddDivZeroCheckSlowPath(LIR* branch) { + class DivZeroCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + DivZeroCheckSlowPath(Mir2Lir* m2l, LIR* branch) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + m2l_->CallRuntimeHelper(QUICK_ENTRYPOINT_OFFSET(4, pThrowDivZero), true); + } + }; + + AddSlowPath(new (arena_) DivZeroCheckSlowPath(this, branch)); +} + +void Mir2Lir::GenArrayBoundsCheck(RegStorage index, RegStorage length) { + class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, RegStorage index, RegStorage length) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch), + index_(index), length_(length) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), + index_, length_, true); + } + + private: + const RegStorage index_; + const RegStorage length_; + }; + + LIR* branch = OpCmpBranch(kCondUge, index, length, nullptr); + AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, length)); +} + +void Mir2Lir::GenArrayBoundsCheck(int index, RegStorage length) { + class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, int index, RegStorage length) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch), + index_(index), length_(length) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + + m2l_->OpRegCopy(m2l_->TargetReg(kArg1), length_); + m2l_->LoadConstant(m2l_->TargetReg(kArg0), index_); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), + m2l_->TargetReg(kArg0), m2l_->TargetReg(kArg1), true); + } + + private: + const int32_t index_; + const RegStorage length_; + }; + + LIR* branch = OpCmpImmBranch(kCondLs, length, index, nullptr); + AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, length)); +} + +LIR* Mir2Lir::GenNullCheck(RegStorage reg) { + class NullCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + NullCheckSlowPath(Mir2Lir* m2l, LIR* branch) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + m2l_->CallRuntimeHelper(QUICK_ENTRYPOINT_OFFSET(4, pThrowNullPointer), true); + } + }; + + LIR* branch = OpCmpImmBranch(kCondEq, reg, 0, nullptr); + AddSlowPath(new (arena_) NullCheckSlowPath(this, branch)); + return branch; +} /* Perform null-check on a register. */ LIR* Mir2Lir::GenNullCheck(RegStorage m_reg, int opt_flags) { @@ -83,7 +177,7 @@ LIR* Mir2Lir::GenExplicitNullCheck(RegStorage m_reg, int opt_flags) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return NULL; } - return GenImmedCheck(kCondEq, m_reg, 0, kThrowNullPointer); + return GenNullCheck(m_reg); } void Mir2Lir::MarkPossibleNullPointerException(int opt_flags) { @@ -479,7 +573,12 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, } // rBase now holds static storage base if (is_long_or_double) { - rl_src = LoadValueWide(rl_src, kAnyReg); + RegisterClass register_kind = kAnyReg; + if (field_info.IsVolatile() && cu_->instruction_set == kX86) { + // Force long/double volatile stores into SSE registers to avoid tearing. + register_kind = kFPReg; + } + rl_src = LoadValueWide(rl_src, register_kind); } else { rl_src = LoadValue(rl_src, kAnyReg); } @@ -560,7 +659,12 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, FreeTemp(r_method); } // r_base now holds static storage base - RegLocation rl_result = EvalLoc(rl_dest, kAnyReg, true); + RegisterClass result_reg_kind = kAnyReg; + if (field_info.IsVolatile() && cu_->instruction_set == kX86) { + // Force long/double volatile loads into SSE registers to avoid tearing. + result_reg_kind = kFPReg; + } + RegLocation rl_result = EvalLoc(rl_dest, result_reg_kind, true); if (is_long_or_double) { LoadBaseDispWide(r_base, field_info.FieldOffset().Int32Value(), rl_result.reg, INVALID_SREG); @@ -634,64 +738,7 @@ void Mir2Lir::HandleThrowLaunchPads() { AppendLIR(lab); ThreadOffset<4> func_offset(-1); int v1 = lab->operands[2]; - int v2 = lab->operands[3]; - const bool target_x86 = cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64; switch (lab->operands[0]) { - case kThrowNullPointer: - func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowNullPointer); - break; - case kThrowConstantArrayBounds: // v1 is length reg (for Arm/Mips), v2 constant index - // v1 holds the constant array index. Mips/Arm uses v2 for length, x86 reloads. - if (target_x86) { - OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v1), - mirror::Array::LengthOffset().Int32Value()); - } else { - OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v1)); - } - // Make sure the following LoadConstant doesn't mess with kArg1. - LockTemp(TargetReg(kArg1)); - LoadConstant(TargetReg(kArg0), v2); - func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds); - break; - case kThrowArrayBounds: - // Move v1 (array index) to kArg0 and v2 (array length) to kArg1 - if (v2 != TargetReg(kArg0).GetReg()) { - OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1)); - if (target_x86) { - // x86 leaves the array pointer in v2, so load the array length that the handler expects - OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2), - mirror::Array::LengthOffset().Int32Value()); - } else { - OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2)); - } - } else { - if (v1 == TargetReg(kArg1).GetReg()) { - // Swap v1 and v2, using kArg2 as a temp - OpRegCopy(TargetReg(kArg2), RegStorage::Solo32(v1)); - if (target_x86) { - // x86 leaves the array pointer in v2; load the array length that the handler expects - OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2), - mirror::Array::LengthOffset().Int32Value()); - } else { - OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2)); - } - OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); - } else { - if (target_x86) { - // x86 leaves the array pointer in v2; load the array length that the handler expects - OpRegMem(kOpMov, TargetReg(kArg1), RegStorage::Solo32(v2), - mirror::Array::LengthOffset().Int32Value()); - } else { - OpRegCopy(TargetReg(kArg1), RegStorage::Solo32(v2)); - } - OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1)); - } - } - func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds); - break; - case kThrowDivZero: - func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowDivZero); - break; case kThrowNoSuchMethod: OpRegCopy(TargetReg(kArg0), RegStorage::Solo32(v1)); func_offset = @@ -720,9 +767,12 @@ void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, DCHECK(rl_dest.wide); GenNullCheck(rl_obj.reg, opt_flags); if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { - rl_result = EvalLoc(rl_dest, reg_class, true); - // FIXME? duplicate null check? - GenNullCheck(rl_obj.reg, opt_flags); + RegisterClass result_reg_kind = kAnyReg; + if (field_info.IsVolatile() && cu_->instruction_set == kX86) { + // Force long/double volatile loads into SSE registers to avoid tearing. + result_reg_kind = kFPReg; + } + rl_result = EvalLoc(rl_dest, result_reg_kind, true); LoadBaseDispWide(rl_obj.reg, field_info.FieldOffset().Int32Value(), rl_result.reg, rl_obj.s_reg_low); MarkPossibleNullPointerException(opt_flags); @@ -787,7 +837,12 @@ void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size, DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); rl_obj = LoadValue(rl_obj, kCoreReg); if (is_long_or_double) { - rl_src = LoadValueWide(rl_src, kAnyReg); + RegisterClass src_reg_kind = kAnyReg; + if (field_info.IsVolatile() && cu_->instruction_set == kX86) { + // Force long/double volatile stores into SSE registers to avoid tearing. + src_reg_kind = kFPReg; + } + rl_src = LoadValueWide(rl_src, src_reg_kind); GenNullCheck(rl_obj.reg, opt_flags); RegStorage reg_ptr = AllocTemp(); OpRegRegImm(kOpAdd, reg_ptr, rl_obj.reg, field_info.FieldOffset().Int32Value()); @@ -1533,7 +1588,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, rl_src1 = LoadValue(rl_src1, kCoreReg); rl_src2 = LoadValue(rl_src2, kCoreReg); if (check_zero) { - GenImmedCheck(kCondEq, rl_src2.reg, 0, kThrowDivZero); + GenDivZeroCheck(rl_src2.reg); } rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv); done = true; @@ -1544,7 +1599,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, rl_src1 = LoadValue(rl_src1, kCoreReg); rl_src2 = LoadValue(rl_src2, kCoreReg); if (check_zero) { - GenImmedCheck(kCondEq, rl_src2.reg, 0, kThrowDivZero); + GenDivZeroCheck(rl_src2.reg); } rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv); done = true; @@ -1559,7 +1614,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, RegStorage r_tgt = CallHelperSetup(func_offset); LoadValueDirectFixed(rl_src1, TargetReg(kArg0)); if (check_zero) { - GenImmedCheck(kCondEq, TargetReg(kArg1), 0, kThrowDivZero); + GenDivZeroCheck(TargetReg(kArg1)); } // NOTE: callout here is not a safepoint. CallHelper(r_tgt, func_offset, false /* not a safepoint */); @@ -1654,9 +1709,8 @@ bool Mir2Lir::HandleEasyMultiply(RegLocation rl_src, RegLocation rl_dest, int li StoreValue(rl_dest, rl_result); return true; } - // There is RegRegRegShift on Arm, so check for more special cases. - // TODO: disabled, need to handle case of "dest == src" properly. - if (false && cu_->instruction_set == kThumb2) { + // There is RegRegRegShift on Arm, so check for more special cases + if (cu_->instruction_set == kThumb2) { return EasyMultiply(rl_src, rl_dest, lit); } // Can we simplify this multiplication? @@ -1785,7 +1839,7 @@ void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, Re case Instruction::REM_INT_LIT8: case Instruction::REM_INT_LIT16: { if (lit == 0) { - GenImmedCheck(kCondAl, RegStorage::InvalidReg(), 0, kThrowDivZero); + GenDivZeroException(); return; } if ((opcode == Instruction::DIV_INT) || @@ -1959,7 +2013,7 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegStorage r_tmp2 = RegStorage::MakeRegPair(TargetReg(kArg2), TargetReg(kArg3)); LoadValueDirectWideFixed(rl_src2, r_tmp2); RegStorage r_tgt = CallHelperSetup(func_offset); - GenDivZeroCheck(RegStorage::MakeRegPair(TargetReg(kArg2), TargetReg(kArg3))); + GenDivZeroCheckWide(RegStorage::MakeRegPair(TargetReg(kArg2), TargetReg(kArg3))); LoadValueDirectWideFixed(rl_src1, r_tmp1); // NOTE: callout here is not a safepoint CallHelper(r_tgt, func_offset, false /* not safepoint */); diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 396a709994..758096b954 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -87,6 +87,12 @@ LIR* Mir2Lir::CallHelper(RegStorage r_tgt, ThreadOffset<4> helper_offset, bool s return call_inst; } +void Mir2Lir::CallRuntimeHelper(ThreadOffset<4> helper_offset, bool safepoint_pc) { + RegStorage r_tgt = CallHelperSetup(helper_offset); + ClobberCallerSave(); + CallHelper(r_tgt, helper_offset, safepoint_pc); +} + void Mir2Lir::CallRuntimeHelperImm(ThreadOffset<4> helper_offset, int arg0, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); LoadConstant(TargetReg(kArg0), arg0); @@ -249,12 +255,27 @@ void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset<4> helper_off CallHelper(r_tgt, helper_offset, safepoint_pc); } +void Mir2Lir::CopyToArgumentRegs(RegStorage arg0, RegStorage arg1) { + if (arg1.GetReg() == TargetReg(kArg0).GetReg()) { + if (arg0.GetReg() == TargetReg(kArg1).GetReg()) { + // Swap kArg0 and kArg1 with kArg2 as temp. + OpRegCopy(TargetReg(kArg2), arg1); + OpRegCopy(TargetReg(kArg0), arg0); + OpRegCopy(TargetReg(kArg1), TargetReg(kArg2)); + } else { + OpRegCopy(TargetReg(kArg1), arg1); + OpRegCopy(TargetReg(kArg0), arg0); + } + } else { + OpRegCopy(TargetReg(kArg0), arg0); + OpRegCopy(TargetReg(kArg1), arg1); + } +} + void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset<4> helper_offset, RegStorage arg0, RegStorage arg1, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - DCHECK_NE(TargetReg(kArg0).GetReg(), arg1.GetReg()); // check copy into arg0 won't clobber arg1 - OpRegCopy(TargetReg(kArg0), arg0); - OpRegCopy(TargetReg(kArg1), arg1); + CopyToArgumentRegs(arg0, arg1); ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -262,9 +283,7 @@ void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset<4> helper_offset, RegStorage void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset<4> helper_offset, RegStorage arg0, RegStorage arg1, int arg2, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - DCHECK_NE(TargetReg(kArg0).GetReg(), arg1.GetReg()); // check copy into arg0 won't clobber arg1 - OpRegCopy(TargetReg(kArg0), arg0); - OpRegCopy(TargetReg(kArg1), arg1); + CopyToArgumentRegs(arg0, arg1); LoadConstant(TargetReg(kArg2), arg2); ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); @@ -1490,7 +1509,7 @@ void Mir2Lir::GenInvoke(CallInfo* info) { ((cu_->disable_opt & (1 << kNullCheckElimination)) != 0 || (info->opt_flags & MIR_IGNORE_NULL_CHECK) == 0)) { RegLocation rl_obj = LoadValue(info->args[0], kCoreReg); - GenImmedCheck(kCondEq, rl_obj.reg, 0, kThrowNullPointer); + GenNullCheck(rl_obj.reg); } return; } diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc index 897d86d09a..208eadde12 100644 --- a/compiler/dex/quick/gen_loadstore.cc +++ b/compiler/dex/quick/gen_loadstore.cc @@ -211,7 +211,12 @@ RegLocation Mir2Lir::LoadValueWide(RegLocation rl_src, RegisterClass op_kind) { LoadValueDirectWide(rl_src, rl_src.reg); rl_src.location = kLocPhysReg; MarkLive(rl_src.reg.GetLow(), rl_src.s_reg_low); - MarkLive(rl_src.reg.GetHigh(), GetSRegHi(rl_src.s_reg_low)); + if (rl_src.reg.GetLowReg() != rl_src.reg.GetHighReg()) { + MarkLive(rl_src.reg.GetHigh(), GetSRegHi(rl_src.s_reg_low)); + } else { + // This must be an x86 vector register value. + DCHECK(IsFpReg(rl_src.reg) && (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64)); + } } return rl_src; } diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 5089111cc3..40641d670d 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -131,7 +131,7 @@ class MipsMir2Lir FINAL : public Mir2Lir { RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenDivZeroCheck(RegStorage reg); + void GenDivZeroCheckWide(RegStorage reg); void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); void GenExitSequence(); void GenSpecialExitSequence(); diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 5fe96d28a7..237572034d 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -342,11 +342,11 @@ void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, } } -void MipsMir2Lir::GenDivZeroCheck(RegStorage reg) { +void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) { DCHECK(reg.IsPair()); // TODO: support k64BitSolo. RegStorage t_reg = AllocTemp(); OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh()); - GenImmedCheck(kCondEq, t_reg, 0, kThrowDivZero); + GenDivZeroCheck(t_reg); FreeTemp(t_reg); } @@ -513,7 +513,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } LoadBaseDispWide(reg_ptr, 0, rl_result.reg, INVALID_SREG); @@ -524,7 +524,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); @@ -590,7 +590,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, rl_src = LoadValueWide(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } @@ -598,7 +598,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } else { rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondUge, rl_index.reg, reg_len, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size); diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 73fdc82854..6fcdf70b12 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -120,7 +120,7 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) { bool Mir2Lir::GenSpecialIGet(MIR* mir, const InlineMethod& special) { // FastInstance() already checked by DexFileMethodInliner. const InlineIGetIPutData& data = special.d.ifield_data; - if (data.method_is_static || data.object_arg != 0) { + if (data.method_is_static != 0u || data.object_arg != 0u) { // The object is not "this" and has to be null-checked. return false; } @@ -151,10 +151,14 @@ bool Mir2Lir::GenSpecialIGet(MIR* mir, const InlineMethod& special) { bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) { // FastInstance() already checked by DexFileMethodInliner. const InlineIGetIPutData& data = special.d.ifield_data; - if (data.method_is_static || data.object_arg != 0) { + if (data.method_is_static != 0u || data.object_arg != 0u) { // The object is not "this" and has to be null-checked. return false; } + if (data.return_arg_plus1 != 0u) { + // The setter returns a method argument which we don't support here. + return false; + } bool wide = (data.op_variant == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE)); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 35f948e083..1f69eb5aa2 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -562,7 +562,14 @@ class Mir2Lir : public Backend { void HandleThrowLaunchPads(); void HandleSlowPaths(); void GenBarrier(); - LIR* GenCheck(ConditionCode c_code, ThrowKind kind); + void GenDivZeroException(); + // c_code holds condition code that's generated from testing divisor against 0. + void GenDivZeroCheck(ConditionCode c_code); + // reg holds divisor. + void GenDivZeroCheck(RegStorage reg); + void GenArrayBoundsCheck(RegStorage index, RegStorage length); + void GenArrayBoundsCheck(int32_t index, RegStorage length); + LIR* GenNullCheck(RegStorage reg); void MarkPossibleNullPointerException(int opt_flags); void MarkPossibleStackOverflowException(); void ForceImplicitNullCheck(RegStorage reg, int opt_flags); @@ -619,6 +626,7 @@ class Mir2Lir : public Backend { LIR* CallHelper(RegStorage r_tgt, ThreadOffset<4> helper_offset, bool safepoint_pc, bool use_link = true); RegStorage CallHelperSetup(ThreadOffset<4> helper_offset); + void CallRuntimeHelper(ThreadOffset<4> helper_offset, bool safepoint_pc); void CallRuntimeHelperImm(ThreadOffset<4> helper_offset, int arg0, bool safepoint_pc); void CallRuntimeHelperReg(ThreadOffset<4> helper_offset, RegStorage arg0, bool safepoint_pc); void CallRuntimeHelperRegLocation(ThreadOffset<4> helper_offset, RegLocation arg0, @@ -958,10 +966,9 @@ class Mir2Lir : public Backend { * @brief Used for generating code that throws ArithmeticException if both registers are zero. * @details This is used for generating DivideByZero checks when divisor is held in two * separate registers. - * @param reg_lo The register holding the lower 32-bits. - * @param reg_hi The register holding the upper 32-bits. + * @param reg The register holding the pair of 32-bit values. */ - virtual void GenDivZeroCheck(RegStorage reg) = 0; + virtual void GenDivZeroCheckWide(RegStorage reg) = 0; virtual void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) = 0; virtual void GenExitSequence() = 0; @@ -1220,6 +1227,11 @@ class Mir2Lir : public Backend { */ bool GenSpecialIdentity(MIR* mir, const InlineMethod& special); + void AddDivZeroCheckSlowPath(LIR* branch); + + // Copy arg0 and arg1 to kArg0 and kArg1 safely, possibly using + // kArg2 as temp. + void CopyToArgumentRegs(RegStorage arg0, RegStorage arg1); public: // TODO: add accessors for these. diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index af2a140296..b802591347 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -135,7 +135,9 @@ class X86Mir2Lir FINAL : public Mir2Lir { RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenDivZeroCheck(RegStorage reg); + void GenDivZeroCheckWide(RegStorage reg); + void GenArrayBoundsCheck(RegStorage index, RegStorage array_base, int32_t len_offset); + void GenArrayBoundsCheck(int32_t index, RegStorage array_base, int32_t len_offset); void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); void GenExitSequence(); void GenSpecialExitSequence(); diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index c1d1e01145..a23a3bf6b8 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -629,7 +629,7 @@ RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, if (check_zero) { // Handle division by zero case. - GenImmedCheck(kCondEq, rs_r1, 0, kThrowDivZero); + GenDivZeroCheck(rs_r1); } // Have to catch 0x80000000/-1 case, or we will get an exception! @@ -876,7 +876,7 @@ void X86Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, } } -void X86Mir2Lir::GenDivZeroCheck(RegStorage reg) { +void X86Mir2Lir::GenDivZeroCheckWide(RegStorage reg) { DCHECK(reg.IsPair()); // TODO: allow 64BitSolo. // We are not supposed to clobber the incoming storage, so allocate a temporary. RegStorage t_reg = AllocTemp(); @@ -885,12 +885,92 @@ void X86Mir2Lir::GenDivZeroCheck(RegStorage reg) { OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh()); // In case of zero, throw ArithmeticException. - GenCheck(kCondEq, kThrowDivZero); + GenDivZeroCheck(kCondEq); // The temp is no longer needed so free it at this time. FreeTemp(t_reg); } +void X86Mir2Lir::GenArrayBoundsCheck(RegStorage index, + RegStorage array_base, + int len_offset) { + class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, + RegStorage index, RegStorage array_base, int32_t len_offset) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch), + index_(index), array_base_(array_base), len_offset_(len_offset) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + + RegStorage new_index = index_; + // Move index out of kArg1, either directly to kArg0, or to kArg2. + if (index_.GetReg() == m2l_->TargetReg(kArg1).GetReg()) { + if (array_base_.GetReg() == m2l_->TargetReg(kArg0).GetReg()) { + m2l_->OpRegCopy(m2l_->TargetReg(kArg2), index_); + new_index = m2l_->TargetReg(kArg2); + } else { + m2l_->OpRegCopy(m2l_->TargetReg(kArg0), index_); + new_index = m2l_->TargetReg(kArg0); + } + } + // Load array length to kArg1. + m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1), array_base_, len_offset_); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), + new_index, m2l_->TargetReg(kArg1), true); + } + + private: + const RegStorage index_; + const RegStorage array_base_; + const int32_t len_offset_; + }; + + OpRegMem(kOpCmp, index, array_base, len_offset); + LIR* branch = OpCondBranch(kCondUge, nullptr); + AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, + index, array_base, len_offset)); +} + +void X86Mir2Lir::GenArrayBoundsCheck(int32_t index, + RegStorage array_base, + int32_t len_offset) { + class ArrayBoundsCheckSlowPath : public Mir2Lir::LIRSlowPath { + public: + ArrayBoundsCheckSlowPath(Mir2Lir* m2l, LIR* branch, + int32_t index, RegStorage array_base, int32_t len_offset) + : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch), + index_(index), array_base_(array_base), len_offset_(len_offset) { + } + + void Compile() OVERRIDE { + m2l_->ResetRegPool(); + m2l_->ResetDefTracking(); + GenerateTargetLabel(); + + // Load array length to kArg1. + m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1), array_base_, len_offset_); + m2l_->LoadConstant(m2l_->TargetReg(kArg0), index_); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), + m2l_->TargetReg(kArg0), m2l_->TargetReg(kArg1), true); + } + + private: + const int32_t index_; + const RegStorage array_base_; + const int32_t len_offset_; + }; + + NewLIR3(IS_SIMM8(index) ? kX86Cmp32MI8 : kX86Cmp32MI, array_base.GetReg(), len_offset, index); + LIR* branch = OpCondBranch(kCondLs, nullptr); + AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, + index, array_base, len_offset)); +} + // Test suspend flag, return target of taken suspend branch LIR* X86Mir2Lir::OpTestSuspend(LIR* target) { OpTlsCmp(Thread::ThreadFlagsOffset<4>(), 0); @@ -1348,10 +1428,9 @@ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) { if (constant_index) { - GenMemImmedCheck(kCondLs, rl_array.reg, len_offset, - constant_index_value, kThrowConstantArrayBounds); + GenArrayBoundsCheck(constant_index_value, rl_array.reg, len_offset); } else { - GenRegMemCheck(kCondUge, rl_index.reg, rl_array.reg, len_offset, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, rl_array.reg, len_offset); } } rl_result = EvalLoc(rl_dest, reg_class, true); @@ -1400,10 +1479,9 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) { if (constant_index) { - GenMemImmedCheck(kCondLs, rl_array.reg, len_offset, - constant_index_value, kThrowConstantArrayBounds); + GenArrayBoundsCheck(constant_index_value, rl_array.reg, len_offset); } else { - GenRegMemCheck(kCondUge, rl_index.reg, rl_array.reg, len_offset, kThrowArrayBounds); + GenArrayBoundsCheck(rl_index.reg, rl_array.reg, len_offset); } } if ((size == kLong) || (size == kDouble)) { @@ -2056,6 +2134,8 @@ void X86Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, // Can we do this directly into memory? rl_result = UpdateLoc(rl_dest); if (rl_result.location == kLocPhysReg) { + // Ensure res is in a core reg + rl_result = EvalLoc(rl_dest, kCoreReg, true); // Can we do this from memory directly? rl_rhs = UpdateLoc(rl_rhs); if (rl_rhs.location != kLocPhysReg) { diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index dcc5d9b73e..5a8ad7a2b4 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -1064,6 +1064,7 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { LoadWordDisp(rs_rDX, count_offset, rs_rCX); LIR *length_compare = nullptr; int start_value = 0; + bool is_index_on_stack = false; if (zero_based) { // We have to handle an empty string. Use special instruction JECXZ. length_compare = NewLIR0(kX86Jecxz8); @@ -1084,14 +1085,32 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { // Runtime start index. rl_start = UpdateLoc(rl_start); if (rl_start.location == kLocPhysReg) { + // Handle "start index < 0" case. + OpRegReg(kOpXor, rs_rBX, rs_rBX); + OpRegReg(kOpCmp, rl_start.reg, rs_rBX); + OpCondRegReg(kOpCmov, kCondLt, rl_start.reg, rs_rBX); + + // The length of the string should be greater than the start index. length_compare = OpCmpBranch(kCondLe, rs_rCX, rl_start.reg, nullptr); OpRegReg(kOpSub, rs_rCX, rl_start.reg); + if (rl_start.reg == rs_rDI) { + // The special case. We will use EDI further, so lets put start index to stack. + NewLIR1(kX86Push32R, rDI); + is_index_on_stack = true; + } } else { - // Compare to memory to avoid a register load. Handle pushed EDI. + // Load the start index from stack, remembering that we pushed EDI. int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t); - OpRegMem(kOpCmp, rs_rCX, rs_rX86_SP, displacement); - length_compare = NewLIR2(kX86Jcc8, 0, kX86CondLe); - OpRegMem(kOpSub, rs_rCX, rs_rX86_SP, displacement); + LoadWordDisp(rs_rX86_SP, displacement, rs_rBX); + OpRegReg(kOpXor, rs_rDI, rs_rDI); + OpRegReg(kOpCmp, rs_rBX, rs_rDI); + OpCondRegReg(kOpCmov, kCondLt, rs_rBX, rs_rDI); + + length_compare = OpCmpBranch(kCondLe, rs_rCX, rs_rBX, nullptr); + OpRegReg(kOpSub, rs_rCX, rs_rBX); + // Put the start index to stack. + NewLIR1(kX86Push32R, rBX); + is_index_on_stack = true; } } } @@ -1113,21 +1132,12 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { NewLIR3(kX86Lea32RM, rDI, rBX, 2 * start_value); } } else { - if (rl_start.location == kLocPhysReg) { - if (rl_start.reg.GetReg() == rDI) { - // We have a slight problem here. We are already using RDI! - // Grab the value from the stack. - LoadWordDisp(rs_rX86_SP, 0, rs_rDX); - OpLea(rs_rDI, rs_rBX, rs_rDX, 1, 0); - } else { - OpLea(rs_rDI, rs_rBX, rl_start.reg, 1, 0); - } - } else { - OpRegCopy(rs_rDI, rs_rBX); - // Load the start index from stack, remembering that we pushed EDI. - int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t); - LoadWordDisp(rs_rX86_SP, displacement, rs_rDX); + if (is_index_on_stack == true) { + // Load the start index from stack. + NewLIR1(kX86Pop32R, rDX); OpLea(rs_rDI, rs_rBX, rs_rDX, 1, 0); + } else { + OpLea(rs_rDI, rs_rBX, rl_start.reg, 1, 0); } } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index b12b6a7291..0ad30be3fe 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -19,10 +19,8 @@ #define ATRACE_TAG ATRACE_TAG_DALVIK #include <utils/Trace.h> -#include <fstream> #include <vector> #include <unistd.h> -#include <utility> #include "base/stl_util.h" #include "base/timing_logger.h" @@ -341,7 +339,6 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, compiler_(Compiler::Create(compiler_kind)), instruction_set_(instruction_set), instruction_set_features_(instruction_set_features), - instruction_set_is_64_bit_(instruction_set == kX86_64 || instruction_set == kArm64), freezing_constructor_lock_("freezing constructor lock"), compiled_classes_lock_("compiled classes lock"), compiled_methods_lock_("compiled method lock"), @@ -372,7 +369,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, // Read the profile file if one is provided. if (profile_file != "") { - profile_ok_ = ReadProfile(profile_file); + profile_ok_ = ProfileHelper::LoadProfileMap(profile_map_, profile_file); } dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX); @@ -450,7 +447,7 @@ CompilerTls* CompilerDriver::GetTls() { } #define CREATE_TRAMPOLINE(type, abi, offset) \ - if (instruction_set_is_64_bit_) { \ + if (Is64BitInstructionSet(instruction_set_)) { \ return CreateTrampoline64(instruction_set_, abi, \ type ## _ENTRYPOINT_OFFSET(8, offset)); \ } else { \ @@ -1898,8 +1895,9 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t compiled_method = compiler_->Compile( *this, code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); - } else if (dex_to_dex_compilation_level != kDontDexToDexCompile) { - // TODO: add a mode to disable DEX-to-DEX compilation ? + } + if (compiled_method == nullptr && dex_to_dex_compilation_level != kDontDexToDexCompile) { + // TODO: add a command-line option to disable DEX-to-DEX compilation ? (*dex_to_dex_compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file, @@ -2035,86 +2033,9 @@ void CompilerDriver::InstructionSetToLLVMTarget(InstructionSet instruction_set, } } -bool CompilerDriver::ReadProfile(const std::string& filename) { - VLOG(compiler) << "reading profile file " << filename; - struct stat st; - int err = stat(filename.c_str(), &st); - if (err == -1) { - VLOG(compiler) << "not found"; - return false; - } - std::ifstream in(filename.c_str()); - if (!in) { - VLOG(compiler) << "profile file " << filename << " exists but can't be opened"; - VLOG(compiler) << "file owner: " << st.st_uid << ":" << st.st_gid; - VLOG(compiler) << "me: " << getuid() << ":" << getgid(); - VLOG(compiler) << "file permissions: " << std::oct << st.st_mode; - VLOG(compiler) << "errno: " << errno; - return false; - } - // The first line contains summary information. - std::string line; - std::getline(in, line); - if (in.eof()) { - return false; - } - std::vector<std::string> summary_info; - Split(line, '/', summary_info); - if (summary_info.size() != 3) { - // Bad summary info. It should be count/total/bootpath. - return false; - } - // This is the number of hits in all methods. - uint32_t total_count = 0; - for (int i = 0 ; i < 3; ++i) { - total_count += atoi(summary_info[i].c_str()); - } - - // Now read each line until the end of file. Each line consists of 3 fields separated by '/'. - // Store the info in descending order given by the most used methods. - typedef std::set<std::pair<int, std::vector<std::string>>> ProfileSet; - ProfileSet countSet; - while (!in.eof()) { - std::getline(in, line); - if (in.eof()) { - break; - } - std::vector<std::string> info; - Split(line, '/', info); - if (info.size() != 3) { - // Malformed. - break; - } - int count = atoi(info[1].c_str()); - countSet.insert(std::make_pair(-count, info)); - } - - uint32_t curTotalCount = 0; - ProfileSet::iterator end = countSet.end(); - const ProfileData* prevData = nullptr; - for (ProfileSet::iterator it = countSet.begin(); it != end ; it++) { - const std::string& methodname = it->second[0]; - uint32_t count = -it->first; - uint32_t size = atoi(it->second[2].c_str()); - double usedPercent = (count * 100.0) / total_count; - - curTotalCount += count; - // Methods with the same count should be part of the same top K percentage bucket. - double topKPercentage = (prevData != nullptr) && (prevData->GetCount() == count) - ? prevData->GetTopKUsedPercentage() - : 100 * static_cast<double>(curTotalCount) / static_cast<double>(total_count); - - // Add it to the profile map. - ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage); - profile_map_[methodname] = curData; - prevData = &curData; - } - return true; -} - bool CompilerDriver::SkipCompilation(const std::string& method_name) { if (!profile_ok_) { - return true; + return false; } // Methods that comprise topKPercentThreshold % of the total samples will be compiled. double topKPercentThreshold = 90.0; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 802f859da4..d7d40d554a 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -32,6 +32,7 @@ #include "invoke_type.h" #include "method_reference.h" #include "os.h" +#include "profiler.h" #include "runtime.h" #include "safe_map.h" #include "thread_pool.h" @@ -594,43 +595,9 @@ class CompilerDriver { return cfi_info_.get(); } - // Profile data. This is generated from previous runs of the program and stored - // in a file. It is used to determine whether to compile a particular method or not. - class ProfileData { - public: - ProfileData() : count_(0), method_size_(0), usedPercent_(0) {} - ProfileData(const std::string& method_name, uint32_t count, uint32_t method_size, - double usedPercent, double topKUsedPercentage) : - method_name_(method_name), count_(count), method_size_(method_size), - usedPercent_(usedPercent), topKUsedPercentage_(topKUsedPercentage) { - // TODO: currently method_size_ and count_ are unused. - UNUSED(method_size_); - UNUSED(count_); - } - - bool IsAbove(double v) const { return usedPercent_ >= v; } - double GetUsedPercent() const { return usedPercent_; } - uint32_t GetCount() const { return count_; } - double GetTopKUsedPercentage() const { return topKUsedPercentage_; } - - private: - std::string method_name_; // Method name. - uint32_t count_; // Number of times it has been called. - uint32_t method_size_; // Size of the method on dex instructions. - double usedPercent_; // Percentage of how many times this method was called. - double topKUsedPercentage_; // The percentage of the group that comprise K% of the total used - // methods this methods belongs to. - }; - - // Profile data is stored in a map, indexed by the full method name. - typedef std::map<const std::string, ProfileData> ProfileMap; ProfileMap profile_map_; bool profile_ok_; - // Read the profile data from the given file. Calculates the percentage for each method. - // Returns false if there was no profile file or it was malformed. - bool ReadProfile(const std::string& filename); - // Should the compiler run on this method given profile information? bool SkipCompilation(const std::string& method_name); @@ -725,7 +692,6 @@ class CompilerDriver { const InstructionSet instruction_set_; const InstructionSetFeatures instruction_set_features_; - const bool instruction_set_is_64_bit_; // All class references that require mutable ReaderWriterMutex freezing_constructor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index f6a324f8e8..e88ed42380 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -367,6 +367,8 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, elf_header.e_ident[EI_ABIVERSION] = 0; elf_header.e_type = ET_DYN; switch (compiler_driver_->GetInstructionSet()) { + case kArm: + // Fall through. case kThumb2: { elf_header.e_machine = EM_ARM; elf_header.e_flags = EF_ARM_EABI_VER5; @@ -396,7 +398,6 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, EF_MIPS_ARCH_32R2); break; } - case kArm: default: { LOG(FATAL) << "Unknown instruction set: " << compiler_driver_->GetInstructionSet(); break; diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index 8175c35077..864dadc963 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -50,7 +50,11 @@ TEST_F(ElfWriterTest, dlsym) { CHECK(host_dir != NULL); elf_filename = StringPrintf("%s/framework/core.oat", host_dir); } else { +#ifdef __LP64__ + elf_filename = "/data/art-test64/core.oat"; +#else elf_filename = "/data/art-test/core.oat"; +#endif } LOG(INFO) << "elf_filename=" << elf_filename; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 05d6693f70..7c5741bb23 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -164,7 +164,7 @@ TEST_F(ImageTest, WriteRead) { EXPECT_TRUE(reinterpret_cast<byte*>(klass) >= image_end || reinterpret_cast<byte*>(klass) < image_begin) << descriptor; } - EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord())); + EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false))); } } diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 0405198350..3400b01994 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -179,7 +179,7 @@ void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { image_bitmap_->Set(obj); // Before we stomp over the lock word, save the hash code for later. Monitor::Deflate(Thread::Current(), object);; - LockWord lw(object->GetLockWord()); + LockWord lw(object->GetLockWord(false)); switch (lw.GetState()) { case LockWord::kFatLocked: { LOG(FATAL) << "Fat locked object " << obj << " found during object copy"; @@ -199,7 +199,7 @@ void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { LOG(FATAL) << "Unreachable."; break; } - object->SetLockWord(LockWord::FromForwardingAddress(offset)); + object->SetLockWord(LockWord::FromForwardingAddress(offset), false); DCHECK(IsImageOffsetAssigned(object)); } @@ -212,13 +212,13 @@ void ImageWriter::AssignImageOffset(mirror::Object* object) { bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const { DCHECK(object != nullptr); - return object->GetLockWord().GetState() == LockWord::kForwardingAddress; + return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress; } size_t ImageWriter::GetImageOffset(mirror::Object* object) const { DCHECK(object != nullptr); DCHECK(IsImageOffsetAssigned(object)); - LockWord lock_word = object->GetLockWord(); + LockWord lock_word = object->GetLockWord(false); size_t offset = lock_word.ForwardingAddress(); DCHECK_LT(offset, image_end_); return offset; @@ -235,8 +235,8 @@ bool ImageWriter::AllocMemory() { } // Create the image bitmap. - image_bitmap_.reset(gc::accounting::SpaceBitmap::Create("image bitmap", image_->Begin(), - length)); + image_bitmap_.reset(gc::accounting::ContinuousSpaceBitmap::Create("image bitmap", image_->Begin(), + length)); if (image_bitmap_.get() == nullptr) { LOG(ERROR) << "Failed to allocate memory for image bitmap"; return false; @@ -525,7 +525,7 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d // Return to write header at start of image with future location of image_roots. At this point, // image_end_ is the size of the image (excluding bitmaps). - const size_t heap_bytes_per_bitmap_byte = kBitsPerByte * gc::accounting::SpaceBitmap::kAlignment; + const size_t heap_bytes_per_bitmap_byte = kBitsPerByte * kObjectAlignment; const size_t bitmap_bytes = RoundUp(image_end_, heap_bytes_per_bitmap_byte) / heap_bytes_per_bitmap_byte; ImageHeader image_header(PointerToLowMemUInt32(image_begin_), @@ -555,15 +555,15 @@ void ImageWriter::CopyAndFixupObjects() heap->VisitObjects(CopyAndFixupObjectsCallback, this); // Fix up the object previously had hash codes. for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) { - hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second)); + hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second), false); } saved_hashes_.clear(); self->EndAssertNoThreadSuspension(old_cause); } void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { - DCHECK(obj != NULL); - DCHECK(arg != NULL); + DCHECK(obj != nullptr); + DCHECK(arg != nullptr); ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); // see GetLocalAddress for similar computation size_t offset = image_writer->GetImageOffset(obj); @@ -575,7 +575,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { Object* copy = reinterpret_cast<Object*>(dst); // Write in a hash code of objects which have inflated monitors or a hash code in their monitor // word. - copy->SetLockWord(LockWord()); + copy->SetLockWord(LockWord(), false); image_writer->FixupObject(obj, copy); } diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 92b24f6067..ee241cb02f 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -173,7 +173,7 @@ class ImageWriter { const byte* oat_data_begin_; // Image bitmap which lets us know where the objects inside of the image reside. - UniquePtr<gc::accounting::SpaceBitmap> image_bitmap_; + UniquePtr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_; // Offset from oat_data_begin_ to the stubs. uint32_t interpreter_to_interpreter_bridge_offset_; diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 3204282e21..6b5e55efa8 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -1268,4 +1268,227 @@ TEST_F(JniCompilerTest, MaxParamNumber) { env_->CallNonvirtualVoidMethodA(jobj_, jklass_, jmethod_, args); } +TEST_F(JniCompilerTest, WithoutImplementation) { + TEST_DISABLED_FOR_PORTABLE(); + SetUpForTest(false, "withoutImplementation", "()V", nullptr); + + env_->CallVoidMethod(jobj_, jmethod_); + + EXPECT_TRUE(Thread::Current()->IsExceptionPending()); + EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE); +} + +template <typename U, typename V> V convert(U in) { + DCHECK_LE(sizeof(U), sizeof(V)); + union { U u; V v; } tmp; + tmp.u = in; + return tmp.v; +} + +void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv* env, jclass klass, jint i1, jint i2, jint i3, + jint i4, jint i5, jint i6, jint i7, jint i8, jint i9, + jint i10, jfloat f1, jfloat f2, jfloat f3, jfloat f4, + jfloat f5, jfloat f6, jfloat f7, jfloat f8, jfloat f9, + jfloat f10) { + EXPECT_EQ(i1, 1); + EXPECT_EQ(i2, 2); + EXPECT_EQ(i3, 3); + EXPECT_EQ(i4, 4); + EXPECT_EQ(i5, 5); + EXPECT_EQ(i6, 6); + EXPECT_EQ(i7, 7); + EXPECT_EQ(i8, 8); + EXPECT_EQ(i9, 9); + EXPECT_EQ(i10, 10); + + jint i11 = convert<jfloat, jint>(f1); + EXPECT_EQ(i11, 11); + jint i12 = convert<jfloat, jint>(f2); + EXPECT_EQ(i12, 12); + jint i13 = convert<jfloat, jint>(f3); + EXPECT_EQ(i13, 13); + jint i14 = convert<jfloat, jint>(f4); + EXPECT_EQ(i14, 14); + jint i15 = convert<jfloat, jint>(f5); + EXPECT_EQ(i15, 15); + jint i16 = convert<jfloat, jint>(f6); + EXPECT_EQ(i16, 16); + jint i17 = convert<jfloat, jint>(f7); + EXPECT_EQ(i17, 17); + jint i18 = convert<jfloat, jint>(f8); + EXPECT_EQ(i18, 18); + jint i19 = convert<jfloat, jint>(f9); + EXPECT_EQ(i19, 19); + jint i20 = convert<jfloat, jint>(f10); + EXPECT_EQ(i20, 20); +} + +TEST_F(JniCompilerTest, StackArgsIntsFirst) { + TEST_DISABLED_FOR_PORTABLE(); + SetUpForTest(true, "stackArgsIntsFirst", "(IIIIIIIIIIFFFFFFFFFF)V", + reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsIntsFirst)); + + jint i1 = 1; + jint i2 = 2; + jint i3 = 3; + jint i4 = 4; + jint i5 = 5; + jint i6 = 6; + jint i7 = 7; + jint i8 = 8; + jint i9 = 9; + jint i10 = 10; + + jfloat f1 = convert<jint, jfloat>(11); + jfloat f2 = convert<jint, jfloat>(12); + jfloat f3 = convert<jint, jfloat>(13); + jfloat f4 = convert<jint, jfloat>(14); + jfloat f5 = convert<jint, jfloat>(15); + jfloat f6 = convert<jint, jfloat>(16); + jfloat f7 = convert<jint, jfloat>(17); + jfloat f8 = convert<jint, jfloat>(18); + jfloat f9 = convert<jint, jfloat>(19); + jfloat f10 = convert<jint, jfloat>(20); + + env_->CallStaticVoidMethod(jklass_, jmethod_, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, f1, f2, + f3, f4, f5, f6, f7, f8, f9, f10); +} + +void Java_MyClassNatives_stackArgsFloatsFirst(JNIEnv* env, jclass klass, jfloat f1, jfloat f2, + jfloat f3, jfloat f4, jfloat f5, jfloat f6, jfloat f7, + jfloat f8, jfloat f9, jfloat f10, jint i1, jint i2, + jint i3, jint i4, jint i5, jint i6, jint i7, jint i8, + jint i9, jint i10) { + EXPECT_EQ(i1, 1); + EXPECT_EQ(i2, 2); + EXPECT_EQ(i3, 3); + EXPECT_EQ(i4, 4); + EXPECT_EQ(i5, 5); + EXPECT_EQ(i6, 6); + EXPECT_EQ(i7, 7); + EXPECT_EQ(i8, 8); + EXPECT_EQ(i9, 9); + EXPECT_EQ(i10, 10); + + jint i11 = convert<jfloat, jint>(f1); + EXPECT_EQ(i11, 11); + jint i12 = convert<jfloat, jint>(f2); + EXPECT_EQ(i12, 12); + jint i13 = convert<jfloat, jint>(f3); + EXPECT_EQ(i13, 13); + jint i14 = convert<jfloat, jint>(f4); + EXPECT_EQ(i14, 14); + jint i15 = convert<jfloat, jint>(f5); + EXPECT_EQ(i15, 15); + jint i16 = convert<jfloat, jint>(f6); + EXPECT_EQ(i16, 16); + jint i17 = convert<jfloat, jint>(f7); + EXPECT_EQ(i17, 17); + jint i18 = convert<jfloat, jint>(f8); + EXPECT_EQ(i18, 18); + jint i19 = convert<jfloat, jint>(f9); + EXPECT_EQ(i19, 19); + jint i20 = convert<jfloat, jint>(f10); + EXPECT_EQ(i20, 20); +} + +TEST_F(JniCompilerTest, StackArgsFloatsFirst) { + TEST_DISABLED_FOR_PORTABLE(); + SetUpForTest(true, "stackArgsFloatsFirst", "(FFFFFFFFFFIIIIIIIIII)V", + reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsFloatsFirst)); + + jint i1 = 1; + jint i2 = 2; + jint i3 = 3; + jint i4 = 4; + jint i5 = 5; + jint i6 = 6; + jint i7 = 7; + jint i8 = 8; + jint i9 = 9; + jint i10 = 10; + + jfloat f1 = convert<jint, jfloat>(11); + jfloat f2 = convert<jint, jfloat>(12); + jfloat f3 = convert<jint, jfloat>(13); + jfloat f4 = convert<jint, jfloat>(14); + jfloat f5 = convert<jint, jfloat>(15); + jfloat f6 = convert<jint, jfloat>(16); + jfloat f7 = convert<jint, jfloat>(17); + jfloat f8 = convert<jint, jfloat>(18); + jfloat f9 = convert<jint, jfloat>(19); + jfloat f10 = convert<jint, jfloat>(20); + + env_->CallStaticVoidMethod(jklass_, jmethod_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, i1, i2, i3, + i4, i5, i6, i7, i8, i9, i10); +} + +void Java_MyClassNatives_stackArgsMixed(JNIEnv* env, jclass klass, jint i1, jfloat f1, jint i2, + jfloat f2, jint i3, jfloat f3, jint i4, jfloat f4, jint i5, + jfloat f5, jint i6, jfloat f6, jint i7, jfloat f7, jint i8, + jfloat f8, jint i9, jfloat f9, jint i10, jfloat f10) { + EXPECT_EQ(i1, 1); + EXPECT_EQ(i2, 2); + EXPECT_EQ(i3, 3); + EXPECT_EQ(i4, 4); + EXPECT_EQ(i5, 5); + EXPECT_EQ(i6, 6); + EXPECT_EQ(i7, 7); + EXPECT_EQ(i8, 8); + EXPECT_EQ(i9, 9); + EXPECT_EQ(i10, 10); + + jint i11 = convert<jfloat, jint>(f1); + EXPECT_EQ(i11, 11); + jint i12 = convert<jfloat, jint>(f2); + EXPECT_EQ(i12, 12); + jint i13 = convert<jfloat, jint>(f3); + EXPECT_EQ(i13, 13); + jint i14 = convert<jfloat, jint>(f4); + EXPECT_EQ(i14, 14); + jint i15 = convert<jfloat, jint>(f5); + EXPECT_EQ(i15, 15); + jint i16 = convert<jfloat, jint>(f6); + EXPECT_EQ(i16, 16); + jint i17 = convert<jfloat, jint>(f7); + EXPECT_EQ(i17, 17); + jint i18 = convert<jfloat, jint>(f8); + EXPECT_EQ(i18, 18); + jint i19 = convert<jfloat, jint>(f9); + EXPECT_EQ(i19, 19); + jint i20 = convert<jfloat, jint>(f10); + EXPECT_EQ(i20, 20); +} + +TEST_F(JniCompilerTest, StackArgsMixed) { + TEST_DISABLED_FOR_PORTABLE(); + SetUpForTest(true, "stackArgsMixed", "(IFIFIFIFIFIFIFIFIFIF)V", + reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsMixed)); + + jint i1 = 1; + jint i2 = 2; + jint i3 = 3; + jint i4 = 4; + jint i5 = 5; + jint i6 = 6; + jint i7 = 7; + jint i8 = 8; + jint i9 = 9; + jint i10 = 10; + + jfloat f1 = convert<jint, jfloat>(11); + jfloat f2 = convert<jint, jfloat>(12); + jfloat f3 = convert<jint, jfloat>(13); + jfloat f4 = convert<jint, jfloat>(14); + jfloat f5 = convert<jint, jfloat>(15); + jfloat f6 = convert<jint, jfloat>(16); + jfloat f7 = convert<jint, jfloat>(17); + jfloat f8 = convert<jint, jfloat>(18); + jfloat f9 = convert<jint, jfloat>(19); + jfloat f10 = convert<jint, jfloat>(20); + + env_->CallStaticVoidMethod(jklass_, jmethod_, i1, f1, i2, f2, i3, f3, i4, f4, i5, f5, i6, f6, i7, + f7, i8, f8, i9, f9, i10, f10); +} + } // namespace art diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc index ab39d6baae..ae18d2e944 100644 --- a/compiler/jni/quick/arm/calling_convention_arm.cc +++ b/compiler/jni/quick/arm/calling_convention_arm.cc @@ -145,7 +145,7 @@ size_t ArmJniCallingConvention::FrameSize() { // Method*, LR and callee save area size, local reference segment state size_t frame_data_size = (3 + CalleeSaveRegisters().size()) * kFramePointerSize; // References plus 2 words for SIRT header - size_t sirt_size = (ReferenceCount() + 2) * sirt_pointer_size_; + size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(kFramePointerSize, ReferenceCount()); // Plus return value spill area size return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(), kStackAlignment); } diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc index c408fa97c3..6212a23a74 100644 --- a/compiler/jni/quick/arm64/calling_convention_arm64.cc +++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc @@ -21,14 +21,29 @@ namespace art { namespace arm64 { -// Calling convention +static const Register kCoreArgumentRegisters[] = { + X0, X1, X2, X3, X4, X5, X6, X7 +}; + +static const WRegister kWArgumentRegisters[] = { + W0, W1, W2, W3, W4, W5, W6, W7 +}; + +static const DRegister kDArgumentRegisters[] = { + D0, D1, D2, D3, D4, D5, D6, D7 +}; + +static const SRegister kSArgumentRegisters[] = { + S0, S1, S2, S3, S4, S5, S6, S7 +}; +// Calling convention ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() { - return Arm64ManagedRegister::FromCoreRegister(IP0); // X16 + return Arm64ManagedRegister::FromCoreRegister(X20); // saved on entry restored on exit } ManagedRegister Arm64JniCallingConvention::InterproceduralScratchRegister() { - return Arm64ManagedRegister::FromCoreRegister(IP0); // X16 + return Arm64ManagedRegister::FromCoreRegister(X20); // saved on entry restored on exit } static ManagedRegister ReturnRegisterForShorty(const char* shorty) { @@ -79,64 +94,64 @@ ManagedRegister Arm64ManagedRuntimeCallingConvention::CurrentParamRegister() { FrameOffset Arm64ManagedRuntimeCallingConvention::CurrentParamStackOffset() { CHECK(IsCurrentParamOnStack()); FrameOffset result = - FrameOffset(displacement_.Int32Value() + // displacement + FrameOffset(displacement_.Int32Value() + // displacement kFramePointerSize + // Method* - (itr_slots_ * kFramePointerSize)); // offset into in args + (itr_slots_ * sizeof(uint32_t))); // offset into in args return result; } const ManagedRegisterEntrySpills& Arm64ManagedRuntimeCallingConvention::EntrySpills() { // We spill the argument registers on ARM64 to free them up for scratch use, we then assume // all arguments are on the stack. - if (entry_spills_.size() == 0) { - // TODO Need fp regs spilled too. - // - size_t num_spills = NumArgs(); - - // TODO Floating point need spilling too. - if (num_spills > 0) { - entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X1)); - if (num_spills > 1) { - entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X2)); - if (num_spills > 2) { - entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X3)); - if (num_spills > 3) { - entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X5)); - if (num_spills > 4) { - entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X6)); - if (num_spills > 5) { - entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X7)); - } + if ((entry_spills_.size() == 0) && (NumArgs() > 0)) { + int gp_reg_index = 1; // we start from X1/W1, X0 holds ArtMethod*. + int fp_reg_index = 0; // D0/S0. + + // We need to choose the correct register (D/S or X/W) since the managed + // stack uses 32bit stack slots. + ResetIterator(FrameOffset(0)); + while (HasNext()) { + if (IsCurrentParamAFloatOrDouble()) { // FP regs. + if (fp_reg_index < 8) { + if (!IsCurrentParamADouble()) { + entry_spills_.push_back(Arm64ManagedRegister::FromSRegister(kSArgumentRegisters[fp_reg_index])); + } else { + entry_spills_.push_back(Arm64ManagedRegister::FromDRegister(kDArgumentRegisters[fp_reg_index])); + } + fp_reg_index++; + } else { // just increase the stack offset. + if (!IsCurrentParamADouble()) { + entry_spills_.push_back(ManagedRegister::NoRegister(), 4); + } else { + entry_spills_.push_back(ManagedRegister::NoRegister(), 8); } } + } else { // GP regs. + if (gp_reg_index < 8) { + if (IsCurrentParamALong() && (!IsCurrentParamAReference())) { + entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(kCoreArgumentRegisters[gp_reg_index])); + } else { + entry_spills_.push_back(Arm64ManagedRegister::FromWRegister(kWArgumentRegisters[gp_reg_index])); + } + gp_reg_index++; + } else { // just increase the stack offset. + if (IsCurrentParamALong() && (!IsCurrentParamAReference())) { + entry_spills_.push_back(ManagedRegister::NoRegister(), 8); + } else { + entry_spills_.push_back(ManagedRegister::NoRegister(), 4); + } } } + Next(); } } - return entry_spills_; } -// JNI calling convention +// JNI calling convention Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty) : JniCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) { - // TODO This needs to be converted to 64bit. - // Compute padding to ensure longs and doubles are not split in AAPCS. Ignore the 'this' jobject - // or jclass for static methods and the JNIEnv. We start at the aligned register r2. -// size_t padding = 0; -// for (size_t cur_arg = IsStatic() ? 0 : 1, cur_reg = 2; cur_arg < NumArgs(); cur_arg++) { -// if (IsParamALongOrDouble(cur_arg)) { -// if ((cur_reg & 1) != 0) { -// padding += 4; -// cur_reg++; // additional bump to ensure alignment -// } -// cur_reg++; // additional bump to skip extra long word -// } -// cur_reg++; // bump the iterator for every argument -// } - padding_ =0; - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X19)); callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X20)); callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X21)); @@ -162,83 +177,87 @@ Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_syn uint32_t Arm64JniCallingConvention::CoreSpillMask() const { // Compute spill mask to agree with callee saves initialized in the constructor uint32_t result = 0; - result = 1 << X19 | 1 << X20 | 1 << X21 | 1 << X22 | 1 << X23 | 1 << X24 | 1 << X25 - | 1 << X26 | 1 << X27 | 1 << X28 | 1<< X29 | 1 << LR; + result = 1 << X19 | 1 << X20 | 1 << X21 | 1 << X22 | 1 << X23 | 1 << X24 | + 1 << X25 | 1 << X26 | 1 << X27 | 1 << X28 | 1 << X29 | 1 << LR; + return result; +} + +uint32_t Arm64JniCallingConvention::FpSpillMask() const { + // Compute spill mask to agree with callee saves initialized in the constructor + uint32_t result = 0; + result = 1 << D8 | 1 << D9 | 1 << D10 | 1 << D11 | 1 << D12 | 1 << D13 | + 1 << D14 | 1 << D15; return result; } ManagedRegister Arm64JniCallingConvention::ReturnScratchRegister() const { - return Arm64ManagedRegister::FromCoreRegister(X9); + return ManagedRegister::NoRegister(); } size_t Arm64JniCallingConvention::FrameSize() { - // Method*, LR and callee save area size, local reference segment state - size_t frame_data_size = (3 + CalleeSaveRegisters().size()) * kFramePointerSize; + // Method*, callee save area size, local reference segment state + size_t frame_data_size = ((1 + CalleeSaveRegisters().size()) * kFramePointerSize) + sizeof(uint32_t); // References plus 2 words for SIRT header - size_t sirt_size = (ReferenceCount() + 2) * sirt_pointer_size_; + size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(kFramePointerSize, ReferenceCount()); // Plus return value spill area size return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(), kStackAlignment); } size_t Arm64JniCallingConvention::OutArgSize() { - return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize + padding_, - kStackAlignment); -} - -// JniCallingConvention ABI follows AAPCS where longs and doubles must occur -// in even register numbers and stack slots -void Arm64JniCallingConvention::Next() { - JniCallingConvention::Next(); - size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(); - if ((itr_args_ >= 2) && - (arg_pos < NumArgs()) && - IsParamALongOrDouble(arg_pos)) { - // itr_slots_ needs to be an even number, according to AAPCS. - if ((itr_slots_ & 0x1u) != 0) { - itr_slots_++; - } - } + return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize, kStackAlignment); } bool Arm64JniCallingConvention::IsCurrentParamInRegister() { - return itr_slots_ < 4; + if (IsCurrentParamAFloatOrDouble()) { + return (itr_float_and_doubles_ < 8); + } else { + return ((itr_args_ - itr_float_and_doubles_) < 8); + } } bool Arm64JniCallingConvention::IsCurrentParamOnStack() { return !IsCurrentParamInRegister(); } -// TODO and floating point? - -static const Register kJniArgumentRegisters[] = { - X0, X1, X2, X3, X4, X5, X6, X7 -}; ManagedRegister Arm64JniCallingConvention::CurrentParamRegister() { - CHECK_LT(itr_slots_, 4u); - int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(); - // TODO Floating point & 64bit registers. - if ((itr_args_ >= 2) && IsParamALongOrDouble(arg_pos)) { - CHECK_EQ(itr_slots_, 2u); - return Arm64ManagedRegister::FromCoreRegister(X1); + CHECK(IsCurrentParamInRegister()); + if (IsCurrentParamAFloatOrDouble()) { + CHECK_LT(itr_float_and_doubles_, 8u); + if (IsCurrentParamADouble()) { + return Arm64ManagedRegister::FromDRegister(kDArgumentRegisters[itr_float_and_doubles_]); + } else { + return Arm64ManagedRegister::FromSRegister(kSArgumentRegisters[itr_float_and_doubles_]); + } } else { - return - Arm64ManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]); + int gp_reg = itr_args_ - itr_float_and_doubles_; + CHECK_LT(static_cast<unsigned int>(gp_reg), 8u); + if (IsCurrentParamALong() || IsCurrentParamAReference() || IsCurrentParamJniEnv()) { + return Arm64ManagedRegister::FromCoreRegister(kCoreArgumentRegisters[gp_reg]); + } else { + return Arm64ManagedRegister::FromWRegister(kWArgumentRegisters[gp_reg]); + } } } FrameOffset Arm64JniCallingConvention::CurrentParamStackOffset() { - CHECK_GE(itr_slots_, 4u); - size_t offset = displacement_.Int32Value() - OutArgSize() + ((itr_slots_ - 4) * kFramePointerSize); + CHECK(IsCurrentParamOnStack()); + size_t args_on_stack = itr_args_ + - std::min(8u, itr_float_and_doubles_) + - std::min(8u, (itr_args_ - itr_float_and_doubles_)); + size_t offset = displacement_.Int32Value() - OutArgSize() + (args_on_stack * kFramePointerSize); CHECK_LT(offset, OutArgSize()); return FrameOffset(offset); } size_t Arm64JniCallingConvention::NumberOfOutgoingStackArgs() { - size_t static_args = IsStatic() ? 1 : 0; // count jclass - // regular argument parameters and this - size_t param_args = NumArgs() + NumLongOrDoubleArgs(); - // count JNIEnv* less arguments in registers - return static_args + param_args + 1 - 4; + // all arguments including JNI args + size_t all_args = NumArgs() + NumberOfExtraArgumentsForJni(); + + size_t all_stack_args = all_args - + std::min(8u, static_cast<unsigned int>(NumFloatOrDoubleArgs())) - + std::min(8u, static_cast<unsigned int>((all_args - NumFloatOrDoubleArgs()))); + + return all_stack_args; } } // namespace arm64 diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.h b/compiler/jni/quick/arm64/calling_convention_arm64.h index c18cd2b0ce..92f547c533 100644 --- a/compiler/jni/quick/arm64/calling_convention_arm64.h +++ b/compiler/jni/quick/arm64/calling_convention_arm64.h @@ -55,7 +55,6 @@ class Arm64JniCallingConvention FINAL : public JniCallingConvention { ManagedRegister IntReturnRegister() OVERRIDE; ManagedRegister InterproceduralScratchRegister() OVERRIDE; // JNI calling convention - void Next() OVERRIDE; // Override default behavior for AAPCS size_t FrameSize() OVERRIDE; size_t OutArgSize() OVERRIDE; const std::vector<ManagedRegister>& CalleeSaveRegisters() const OVERRIDE { @@ -63,9 +62,7 @@ class Arm64JniCallingConvention FINAL : public JniCallingConvention { } ManagedRegister ReturnScratchRegister() const OVERRIDE; uint32_t CoreSpillMask() const OVERRIDE; - uint32_t FpSpillMask() const OVERRIDE { - return 0; // Floats aren't spilled in JNI down call - } + uint32_t FpSpillMask() const OVERRIDE; bool IsCurrentParamInRegister() OVERRIDE; bool IsCurrentParamOnStack() OVERRIDE; ManagedRegister CurrentParamRegister() OVERRIDE; @@ -78,9 +75,6 @@ class Arm64JniCallingConvention FINAL : public JniCallingConvention { // TODO: these values aren't unique and can be shared amongst instances std::vector<ManagedRegister> callee_save_regs_; - // Padding to ensure longs and doubles are not split in AAPCS - size_t padding_; - DISALLOW_COPY_AND_ASSIGN(Arm64JniCallingConvention); }; diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc index 8efdcdaab2..a99a4c2480 100644 --- a/compiler/jni/quick/calling_convention.cc +++ b/compiler/jni/quick/calling_convention.cc @@ -90,6 +90,14 @@ bool ManagedRuntimeCallingConvention::IsCurrentParamAFloatOrDouble() { return IsParamAFloatOrDouble(itr_args_); } +bool ManagedRuntimeCallingConvention::IsCurrentParamADouble() { + return IsParamADouble(itr_args_); +} + +bool ManagedRuntimeCallingConvention::IsCurrentParamALong() { + return IsParamALong(itr_args_); +} + // JNI calling convention JniCallingConvention* JniCallingConvention::Create(bool is_static, bool is_synchronized, @@ -168,6 +176,10 @@ bool JniCallingConvention::IsCurrentParamAReference() { } } +bool JniCallingConvention::IsCurrentParamJniEnv() { + return (itr_args_ == kJniEnv); +} + bool JniCallingConvention::IsCurrentParamAFloatOrDouble() { switch (itr_args_) { case kJniEnv: @@ -181,6 +193,32 @@ bool JniCallingConvention::IsCurrentParamAFloatOrDouble() { } } +bool JniCallingConvention::IsCurrentParamADouble() { + switch (itr_args_) { + case kJniEnv: + return false; // JNIEnv* + case kObjectOrClass: + return false; // jobject or jclass + default: { + int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(); + return IsParamADouble(arg_pos); + } + } +} + +bool JniCallingConvention::IsCurrentParamALong() { + switch (itr_args_) { + case kJniEnv: + return false; // JNIEnv* + case kObjectOrClass: + return false; // jobject or jclass + default: { + int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(); + return IsParamALong(arg_pos); + } + } +} + // Return position of SIRT entry holding reference at the current iterator // position FrameOffset JniCallingConvention::CurrentParamSirtEntryOffset() { diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h index 7e1cf630c6..4d25d1ce96 100644 --- a/compiler/jni/quick/calling_convention.h +++ b/compiler/jni/quick/calling_convention.h @@ -126,6 +126,24 @@ class CallingConvention { char ch = shorty_[param]; return (ch == 'F' || ch == 'D'); } + bool IsParamADouble(unsigned int param) const { + DCHECK_LT(param, NumArgs()); + if (IsStatic()) { + param++; // 0th argument must skip return value at start of the shorty + } else if (param == 0) { + return false; // this argument + } + return shorty_[param] == 'D'; + } + bool IsParamALong(unsigned int param) const { + DCHECK_LT(param, NumArgs()); + if (IsStatic()) { + param++; // 0th argument must skip return value at start of the shorty + } else if (param == 0) { + return true; // this argument + } + return shorty_[param] == 'J'; + } bool IsParamAReference(unsigned int param) const { DCHECK_LT(param, NumArgs()); if (IsStatic()) { @@ -214,6 +232,8 @@ class ManagedRuntimeCallingConvention : public CallingConvention { void Next(); bool IsCurrentParamAReference(); bool IsCurrentParamAFloatOrDouble(); + bool IsCurrentParamADouble(); + bool IsCurrentParamALong(); bool IsCurrentArgExplicit(); // ie a non-implict argument such as this bool IsCurrentArgPossiblyNull(); size_t CurrentParamSize(); @@ -283,6 +303,9 @@ class JniCallingConvention : public CallingConvention { virtual void Next(); bool IsCurrentParamAReference(); bool IsCurrentParamAFloatOrDouble(); + bool IsCurrentParamADouble(); + bool IsCurrentParamALong(); + bool IsCurrentParamJniEnv(); size_t CurrentParamSize(); virtual bool IsCurrentParamInRegister() = 0; virtual bool IsCurrentParamOnStack() = 0; @@ -299,17 +322,17 @@ class JniCallingConvention : public CallingConvention { FrameOffset SirtLinkOffset() const { return FrameOffset(SirtOffset().Int32Value() + - StackIndirectReferenceTable::LinkOffset()); + StackIndirectReferenceTable::LinkOffset(frame_pointer_size_)); } FrameOffset SirtNumRefsOffset() const { return FrameOffset(SirtOffset().Int32Value() + - StackIndirectReferenceTable::NumberOfReferencesOffset()); + StackIndirectReferenceTable::NumberOfReferencesOffset(frame_pointer_size_)); } FrameOffset SirtReferencesOffset() const { return FrameOffset(SirtOffset().Int32Value() + - StackIndirectReferenceTable::ReferencesOffset()); + StackIndirectReferenceTable::ReferencesOffset(frame_pointer_size_)); } virtual ~JniCallingConvention() {} diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index dcdcdd19c2..93b1b5a155 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -29,6 +29,7 @@ #include "utils/assembler.h" #include "utils/managed_register.h" #include "utils/arm/managed_register_arm.h" +#include "utils/arm64/managed_register_arm64.h" #include "utils/mips/managed_register_mips.h" #include "utils/x86/managed_register_x86.h" #include "thread.h" @@ -63,6 +64,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, if (instruction_set == kThumb2) { instruction_set = kArm; } + const bool is_64_bit_target = Is64BitInstructionSet(instruction_set); // Calling conventions used to iterate over parameters to method UniquePtr<JniCallingConvention> main_jni_conv( JniCallingConvention::Create(is_static, is_synchronized, shorty, instruction_set)); @@ -73,11 +75,17 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, // Calling conventions to call into JNI method "end" possibly passing a returned reference, the // method and the current thread. - size_t jni_end_arg_count = 0; - if (reference_return) { jni_end_arg_count++; } - if (is_synchronized) { jni_end_arg_count++; } - const char* jni_end_shorty = jni_end_arg_count == 0 ? "I" - : (jni_end_arg_count == 1 ? "II" : "III"); + const char* jni_end_shorty; + if (reference_return && is_synchronized) { + jni_end_shorty = "ILL"; + } else if (reference_return) { + jni_end_shorty = "IL"; + } else if (is_synchronized) { + jni_end_shorty = "VL"; + } else { + jni_end_shorty = "V"; + } + UniquePtr<JniCallingConvention> end_jni_conv( JniCallingConvention::Create(is_static, is_synchronized, jni_end_shorty, instruction_set)); @@ -101,12 +109,22 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, __ StoreImmediateToFrame(main_jni_conv->SirtNumRefsOffset(), main_jni_conv->ReferenceCount(), mr_conv->InterproceduralScratchRegister()); - __ CopyRawPtrFromThread32(main_jni_conv->SirtLinkOffset(), - Thread::TopSirtOffset<4>(), - mr_conv->InterproceduralScratchRegister()); - __ StoreStackOffsetToThread32(Thread::TopSirtOffset<4>(), - main_jni_conv->SirtOffset(), - mr_conv->InterproceduralScratchRegister()); + + if (is_64_bit_target) { + __ CopyRawPtrFromThread64(main_jni_conv->SirtLinkOffset(), + Thread::TopSirtOffset<8>(), + mr_conv->InterproceduralScratchRegister()); + __ StoreStackOffsetToThread64(Thread::TopSirtOffset<8>(), + main_jni_conv->SirtOffset(), + mr_conv->InterproceduralScratchRegister()); + } else { + __ CopyRawPtrFromThread32(main_jni_conv->SirtLinkOffset(), + Thread::TopSirtOffset<4>(), + mr_conv->InterproceduralScratchRegister()); + __ StoreStackOffsetToThread32(Thread::TopSirtOffset<4>(), + main_jni_conv->SirtOffset(), + mr_conv->InterproceduralScratchRegister()); + } // 3. Place incoming reference arguments into SIRT main_jni_conv->Next(); // Skip JNIEnv* @@ -154,9 +172,15 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, } // 4. Write out the end of the quick frames. - __ StoreStackPointerToThread32(Thread::TopOfManagedStackOffset<4>()); - __ StoreImmediateToThread32(Thread::TopOfManagedStackPcOffset<4>(), 0, - mr_conv->InterproceduralScratchRegister()); + if (is_64_bit_target) { + __ StoreStackPointerToThread64(Thread::TopOfManagedStackOffset<8>()); + __ StoreImmediateToThread64(Thread::TopOfManagedStackPcOffset<8>(), 0, + mr_conv->InterproceduralScratchRegister()); + } else { + __ StoreStackPointerToThread32(Thread::TopOfManagedStackOffset<4>()); + __ StoreImmediateToThread32(Thread::TopOfManagedStackPcOffset<4>(), 0, + mr_conv->InterproceduralScratchRegister()); + } // 5. Move frame down to allow space for out going args. const size_t main_out_arg_size = main_jni_conv->OutArgSize(); @@ -164,13 +188,14 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, const size_t max_out_arg_size = std::max(main_out_arg_size, end_out_arg_size); __ IncreaseFrameSize(max_out_arg_size); - // 6. Call into appropriate JniMethodStart passing Thread* so that transition out of Runnable // can occur. The result is the saved JNI local state that is restored by the exit call. We // abuse the JNI calling convention here, that is guaranteed to support passing 2 pointer // arguments. - ThreadOffset<4> jni_start = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(4, pJniMethodStartSynchronized) - : QUICK_ENTRYPOINT_OFFSET(4, pJniMethodStart); + ThreadOffset<4> jni_start32 = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(4, pJniMethodStartSynchronized) + : QUICK_ENTRYPOINT_OFFSET(4, pJniMethodStart); + ThreadOffset<8> jni_start64 = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(8, pJniMethodStartSynchronized) + : QUICK_ENTRYPOINT_OFFSET(8, pJniMethodStart); main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size)); FrameOffset locked_object_sirt_offset(0); if (is_synchronized) { @@ -192,12 +217,21 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, } if (main_jni_conv->IsCurrentParamInRegister()) { __ GetCurrentThread(main_jni_conv->CurrentParamRegister()); - __ Call(main_jni_conv->CurrentParamRegister(), Offset(jni_start), - main_jni_conv->InterproceduralScratchRegister()); + if (is_64_bit_target) { + __ Call(main_jni_conv->CurrentParamRegister(), Offset(jni_start64), + main_jni_conv->InterproceduralScratchRegister()); + } else { + __ Call(main_jni_conv->CurrentParamRegister(), Offset(jni_start32), + main_jni_conv->InterproceduralScratchRegister()); + } } else { __ GetCurrentThread(main_jni_conv->CurrentParamStackOffset(), main_jni_conv->InterproceduralScratchRegister()); - __ CallFromThread32(jni_start, main_jni_conv->InterproceduralScratchRegister()); + if (is_64_bit_target) { + __ CallFromThread64(jni_start64, main_jni_conv->InterproceduralScratchRegister()); + } else { + __ CallFromThread32(jni_start32, main_jni_conv->InterproceduralScratchRegister()); + } } if (is_synchronized) { // Check for exceptions from monitor enter. __ ExceptionPoll(main_jni_conv->InterproceduralScratchRegister(), main_out_arg_size); @@ -259,11 +293,20 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, if (main_jni_conv->IsCurrentParamInRegister()) { ManagedRegister jni_env = main_jni_conv->CurrentParamRegister(); DCHECK(!jni_env.Equals(main_jni_conv->InterproceduralScratchRegister())); - __ LoadRawPtrFromThread32(jni_env, Thread::JniEnvOffset<4>()); + if (is_64_bit_target) { + __ LoadRawPtrFromThread64(jni_env, Thread::JniEnvOffset<8>()); + } else { + __ LoadRawPtrFromThread32(jni_env, Thread::JniEnvOffset<4>()); + } } else { FrameOffset jni_env = main_jni_conv->CurrentParamStackOffset(); - __ CopyRawPtrFromThread32(jni_env, Thread::JniEnvOffset<4>(), + if (is_64_bit_target) { + __ CopyRawPtrFromThread64(jni_env, Thread::JniEnvOffset<8>(), + main_jni_conv->InterproceduralScratchRegister()); + } else { + __ CopyRawPtrFromThread32(jni_env, Thread::JniEnvOffset<4>(), main_jni_conv->InterproceduralScratchRegister()); + } } // 9. Plant call to native code associated with method. @@ -289,25 +332,29 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, if (instruction_set == kMips && main_jni_conv->GetReturnType() == Primitive::kPrimDouble && return_save_location.Uint32Value() % 8 != 0) { // Ensure doubles are 8-byte aligned for MIPS - return_save_location = FrameOffset(return_save_location.Uint32Value() + kPointerSize); + return_save_location = FrameOffset(return_save_location.Uint32Value() + kMipsPointerSize); } CHECK_LT(return_save_location.Uint32Value(), frame_size+main_out_arg_size); __ Store(return_save_location, main_jni_conv->ReturnRegister(), main_jni_conv->SizeOfReturnValue()); } - // 12. Call into JNI method end possibly passing a returned reference, the method and the current // thread. end_jni_conv->ResetIterator(FrameOffset(end_out_arg_size)); - ThreadOffset<4> jni_end(-1); + ThreadOffset<4> jni_end32(-1); + ThreadOffset<8> jni_end64(-1); if (reference_return) { // Pass result. - jni_end = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(4, pJniMethodEndWithReferenceSynchronized) - : QUICK_ENTRYPOINT_OFFSET(4, pJniMethodEndWithReference); + jni_end32 = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(4, pJniMethodEndWithReferenceSynchronized) + : QUICK_ENTRYPOINT_OFFSET(4, pJniMethodEndWithReference); + jni_end64 = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(8, pJniMethodEndWithReferenceSynchronized) + : QUICK_ENTRYPOINT_OFFSET(8, pJniMethodEndWithReference); SetNativeParameter(jni_asm.get(), end_jni_conv.get(), end_jni_conv->ReturnRegister()); end_jni_conv->Next(); } else { - jni_end = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(4, pJniMethodEndSynchronized) - : QUICK_ENTRYPOINT_OFFSET(4, pJniMethodEnd); + jni_end32 = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(4, pJniMethodEndSynchronized) + : QUICK_ENTRYPOINT_OFFSET(4, pJniMethodEnd); + jni_end64 = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(8, pJniMethodEndSynchronized) + : QUICK_ENTRYPOINT_OFFSET(8, pJniMethodEnd); } // Pass saved local reference state. if (end_jni_conv->IsCurrentParamOnStack()) { @@ -334,12 +381,21 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, } if (end_jni_conv->IsCurrentParamInRegister()) { __ GetCurrentThread(end_jni_conv->CurrentParamRegister()); - __ Call(end_jni_conv->CurrentParamRegister(), Offset(jni_end), - end_jni_conv->InterproceduralScratchRegister()); + if (is_64_bit_target) { + __ Call(end_jni_conv->CurrentParamRegister(), Offset(jni_end64), + end_jni_conv->InterproceduralScratchRegister()); + } else { + __ Call(end_jni_conv->CurrentParamRegister(), Offset(jni_end32), + end_jni_conv->InterproceduralScratchRegister()); + } } else { __ GetCurrentThread(end_jni_conv->CurrentParamStackOffset(), end_jni_conv->InterproceduralScratchRegister()); - __ CallFromThread32(ThreadOffset<4>(jni_end), end_jni_conv->InterproceduralScratchRegister()); + if (is_64_bit_target) { + __ CallFromThread64(ThreadOffset<8>(jni_end64), end_jni_conv->InterproceduralScratchRegister()); + } else { + __ CallFromThread32(ThreadOffset<4>(jni_end32), end_jni_conv->InterproceduralScratchRegister()); + } } // 13. Reload return value @@ -360,6 +416,10 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, // 17. Finalize code generation __ EmitSlowPaths(); size_t cs = __ CodeSize(); + if (instruction_set == kArm64) { + // Test that we do not exceed the buffer size. + CHECK(cs < arm64::kBufferSizeArm64); + } std::vector<uint8_t> managed_code(cs); MemoryRegion code(&managed_code[0], managed_code.size()); __ FinalizeInstructions(code); diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc index 51a3f54888..8e1c0c7a73 100644 --- a/compiler/jni/quick/mips/calling_convention_mips.cc +++ b/compiler/jni/quick/mips/calling_convention_mips.cc @@ -149,7 +149,7 @@ size_t MipsJniCallingConvention::FrameSize() { // Method*, LR and callee save area size, local reference segment state size_t frame_data_size = (3 + CalleeSaveRegisters().size()) * kFramePointerSize; // References plus 2 words for SIRT header - size_t sirt_size = (ReferenceCount() + 2) * sirt_pointer_size_; + size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(kFramePointerSize, ReferenceCount()); // Plus return value spill area size return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(), kStackAlignment); } diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc index 8b440eda48..153f9538dd 100644 --- a/compiler/jni/quick/x86/calling_convention_x86.cc +++ b/compiler/jni/quick/x86/calling_convention_x86.cc @@ -126,7 +126,7 @@ size_t X86JniCallingConvention::FrameSize() { // Method*, return address and callee save area size, local reference segment state size_t frame_data_size = (3 + CalleeSaveRegisters().size()) * kFramePointerSize; // References plus 2 words for SIRT header - size_t sirt_size = (ReferenceCount() + 2) * sirt_pointer_size_; + size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(kFramePointerSize, ReferenceCount()); // Plus return value spill area size return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(), kStackAlignment); } 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 21e0bd7f61..4dfa29a46f 100644 --- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc +++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc @@ -141,7 +141,7 @@ size_t X86_64JniCallingConvention::FrameSize() { // Method*, return address and callee save area size, local reference segment state size_t frame_data_size = (3 + CalleeSaveRegisters().size()) * kFramePointerSize; // References plus link_ (pointer) and number_of_references_ (uint32_t) for SIRT header - size_t sirt_size = kFramePointerSize + sizeof(uint32_t) + (ReferenceCount() * sirt_pointer_size_); + size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(kFramePointerSize, ReferenceCount()); // Plus return value spill area size return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(), kStackAlignment); } diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc index 1d027f9d3b..fe609593dc 100644 --- a/compiler/llvm/llvm_compilation_unit.cc +++ b/compiler/llvm/llvm_compilation_unit.cc @@ -314,23 +314,8 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea // section if the section alignment is greater than kArchAlignment. void LlvmCompilationUnit::CheckCodeAlign(uint32_t align) const { InstructionSet insn_set = GetInstructionSet(); - switch (insn_set) { - case kThumb2: - case kArm: - CHECK_LE(align, static_cast<uint32_t>(kArmAlignment)); - break; - - case kX86: - CHECK_LE(align, static_cast<uint32_t>(kX86Alignment)); - break; - - case kMips: - CHECK_LE(align, static_cast<uint32_t>(kMipsAlignment)); - break; - - default: - LOG(FATAL) << "Unknown instruction set: " << insn_set; - } + size_t insn_set_align = GetInstructionSetAlignment(insn_set); + CHECK_LE(align, static_cast<uint32_t>(insn_set_align)); } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 2d45a2f65f..dc66e9c108 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -376,7 +376,9 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, } else { CHECK(quick_code != nullptr); offset = compiled_method->AlignCode(offset); - DCHECK_ALIGNED(offset, kArmAlignment); + DCHECK_ALIGNED_PARAM(offset, + GetInstructionSetAlignment(compiled_method->GetInstructionSet())); + uint32_t code_size = quick_code->size() * sizeof(uint8_t); CHECK_NE(code_size, 0U); uint32_t thumb_offset = compiled_method->CodeDelta(); @@ -508,11 +510,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, refs++; } } - InstructionSet trg_isa = compiler_driver_->GetInstructionSet(); - size_t pointer_size = 4; - if (trg_isa == kArm64 || trg_isa == kX86_64) { - pointer_size = 8; - } + size_t pointer_size = GetInstructionSetPointerSize(compiler_driver_->GetInstructionSet()); size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs); // Get the generic spill masks and base frame size. @@ -826,7 +824,9 @@ size_t OatWriter::WriteCodeMethod(OutputStream* out, const size_t file_offset, relative_offset += aligned_code_delta; DCHECK_OFFSET(); } - DCHECK_ALIGNED(relative_offset, kArmAlignment); + DCHECK_ALIGNED_PARAM(relative_offset, + GetInstructionSetAlignment(compiled_method->GetInstructionSet())); + uint32_t code_size = quick_code->size() * sizeof(uint8_t); CHECK_NE(code_size, 0U); diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 05548761e0..1efdd389d8 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -25,7 +25,8 @@ namespace art { -void HGraphBuilder::InitializeLocals(int count) { +void HGraphBuilder::InitializeLocals(uint16_t count) { + graph_->SetNumberOfVRegs(count); locals_.SetSize(count); for (int i = 0; i < count; i++) { HLocal* local = new (arena_) HLocal(i); @@ -34,15 +35,81 @@ void HGraphBuilder::InitializeLocals(int count) { } } +bool HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) { + // dex_compilation_unit_ is null only when unit testing. + if (dex_compilation_unit_ == nullptr) { + return true; + } + + graph_->SetNumberOfInVRegs(number_of_parameters); + const char* shorty = dex_compilation_unit_->GetShorty(); + int locals_index = locals_.Size() - number_of_parameters; + int parameter_index = 0; + + if (!dex_compilation_unit_->IsStatic()) { + // Add the implicit 'this' argument, not expressed in the signature. + HParameterValue* parameter = + new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot); + entry_block_->AddInstruction(parameter); + HLocal* local = GetLocalAt(locals_index++); + entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter)); + number_of_parameters--; + } + + uint32_t pos = 1; + for (int i = 0; i < number_of_parameters; i++) { + switch (shorty[pos++]) { + case 'F': + case 'D': { + return false; + } + + default: { + // integer and reference parameters. + HParameterValue* parameter = + new (arena_) HParameterValue(parameter_index++, Primitive::GetType(shorty[pos - 1])); + entry_block_->AddInstruction(parameter); + HLocal* local = GetLocalAt(locals_index++); + // Store the parameter value in the local that the dex code will use + // to reference that parameter. + entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter)); + if (parameter->GetType() == Primitive::kPrimLong) { + i++; + locals_index++; + parameter_index++; + } + break; + } + } + } + return true; +} + static bool CanHandleCodeItem(const DexFile::CodeItem& code_item) { if (code_item.tries_size_ > 0) { return false; - } else if (code_item.ins_size_ > 0) { - return false; } return true; } +template<typename T> +void HGraphBuilder::If_22t(const Instruction& instruction, int32_t dex_offset, bool is_not) { + HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt); + HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); + current_block_->AddInstruction(new (arena_) T(first, second)); + if (is_not) { + current_block_->AddInstruction(new (arena_) HNot(current_block_->GetLastInstruction())); + } + current_block_->AddInstruction(new (arena_) HIf(current_block_->GetLastInstruction())); + HBasicBlock* target = FindBlockStartingAt(instruction.GetTargetOffset() + dex_offset); + DCHECK(target != nullptr); + current_block_->AddSuccessor(target); + target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits()); + DCHECK(target != nullptr); + current_block_->AddSuccessor(target); + current_block_ = nullptr; +} + HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { if (!CanHandleCodeItem(code_item)) { return nullptr; @@ -66,6 +133,10 @@ HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { // start a new block, and create these blocks. ComputeBranchTargets(code_ptr, code_end); + if (!InitializeParameters(code_item.ins_size_)) { + return nullptr; + } + size_t dex_offset = 0; while (code_ptr < code_end) { // Update the current block if dex_offset starts a new block. @@ -139,6 +210,112 @@ HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t index) const { return branch_targets_.Get(index); } +template<typename T> +void HGraphBuilder::Binop_32x(const Instruction& instruction, Primitive::Type type) { + HInstruction* first = LoadLocal(instruction.VRegB(), type); + HInstruction* second = LoadLocal(instruction.VRegC(), type); + current_block_->AddInstruction(new (arena_) T(type, first, second)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + +template<typename T> +void HGraphBuilder::Binop_12x(const Instruction& instruction, Primitive::Type type) { + HInstruction* first = LoadLocal(instruction.VRegA(), type); + HInstruction* second = LoadLocal(instruction.VRegB(), type); + current_block_->AddInstruction(new (arena_) T(type, first, second)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + +template<typename T> +void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse) { + HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); + HInstruction* second = GetIntConstant(instruction.VRegC_22s()); + if (reverse) { + std::swap(first, second); + } + current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + +template<typename T> +void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse) { + HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); + HInstruction* second = GetIntConstant(instruction.VRegC_22b()); + if (reverse) { + std::swap(first, second); + } + current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + +void HGraphBuilder::BuildReturn(const Instruction& instruction, Primitive::Type type) { + if (type == Primitive::kPrimVoid) { + current_block_->AddInstruction(new (arena_) HReturnVoid()); + } else { + HInstruction* value = LoadLocal(instruction.VRegA(), type); + current_block_->AddInstruction(new (arena_) HReturn(value)); + } + current_block_->AddSuccessor(exit_block_); + current_block_ = nullptr; +} + +bool HGraphBuilder::BuildInvoke(const Instruction& instruction, + uint32_t dex_offset, + uint32_t method_idx, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index) { + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); + const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(method_id.proto_idx_); + const char* descriptor = dex_file_->StringDataByIdx(proto_id.shorty_idx_); + Primitive::Type return_type = Primitive::GetType(descriptor[0]); + bool is_instance_call = + instruction.Opcode() != Instruction::INVOKE_STATIC + && instruction.Opcode() != Instruction::INVOKE_STATIC_RANGE; + const size_t number_of_arguments = strlen(descriptor) - (is_instance_call ? 0 : 1); + + // Treat invoke-direct like static calls for now. + HInvoke* invoke = new (arena_) HInvokeStatic( + arena_, number_of_arguments, return_type, dex_offset, method_idx); + + size_t start_index = 0; + if (is_instance_call) { + HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot); + invoke->SetArgumentAt(0, arg); + start_index = 1; + } + + uint32_t descriptor_index = 1; + uint32_t argument_index = start_index; + for (size_t i = start_index; i < number_of_vreg_arguments; i++, argument_index++) { + Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]); + switch (type) { + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + return false; + + default: { + if (!is_range && type == Primitive::kPrimLong && args[i] + 1 != args[i + 1]) { + LOG(WARNING) << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol() + << " at " << dex_offset; + // We do not implement non sequential register pair. + return false; + } + HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type); + invoke->SetArgumentAt(argument_index, arg); + if (type == Primitive::kPrimLong) { + i++; + } + } + } + } + + DCHECK_EQ(argument_index, number_of_arguments); + current_block_->AddInstruction(invoke); + return true; +} + bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) { if (current_block_ == nullptr) { return true; // Dead code @@ -147,30 +324,57 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ switch (instruction.Opcode()) { case Instruction::CONST_4: { int32_t register_index = instruction.VRegA(); - HIntConstant* constant = GetConstant(instruction.VRegB_11n()); + HIntConstant* constant = GetIntConstant(instruction.VRegB_11n()); + UpdateLocal(register_index, constant); + break; + } + + case Instruction::CONST_16: { + int32_t register_index = instruction.VRegA(); + HIntConstant* constant = GetIntConstant(instruction.VRegB_21s()); UpdateLocal(register_index, constant); break; } + case Instruction::CONST_WIDE_16: { + int32_t register_index = instruction.VRegA(); + HLongConstant* constant = GetLongConstant(instruction.VRegB_21s()); + UpdateLocal(register_index, constant); + break; + } + + case Instruction::CONST_WIDE_32: { + int32_t register_index = instruction.VRegA(); + HLongConstant* constant = GetLongConstant(instruction.VRegB_31i()); + UpdateLocal(register_index, constant); + break; + } + + case Instruction::CONST_WIDE: { + int32_t register_index = instruction.VRegA(); + HLongConstant* constant = GetLongConstant(instruction.VRegB_51l()); + UpdateLocal(register_index, constant); + break; + } + + case Instruction::MOVE: { + HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); + UpdateLocal(instruction.VRegA(), value); + break; + } + case Instruction::RETURN_VOID: { - current_block_->AddInstruction(new (arena_) HReturnVoid()); - current_block_->AddSuccessor(exit_block_); - current_block_ = nullptr; + BuildReturn(instruction, Primitive::kPrimVoid); break; } case Instruction::IF_EQ: { - HInstruction* first = LoadLocal(instruction.VRegA()); - HInstruction* second = LoadLocal(instruction.VRegB()); - current_block_->AddInstruction(new (arena_) HEqual(first, second)); - current_block_->AddInstruction(new (arena_) HIf(current_block_->GetLastInstruction())); - HBasicBlock* target = FindBlockStartingAt(instruction.GetTargetOffset() + dex_offset); - DCHECK(target != nullptr); - current_block_->AddSuccessor(target); - target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits()); - DCHECK(target != nullptr); - current_block_->AddSuccessor(target); - current_block_ = nullptr; + If_22t<HEqual>(instruction, dex_offset, false); + break; + } + + case Instruction::IF_NE: { + If_22t<HEqual>(instruction, dex_offset, true); break; } @@ -186,93 +390,112 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ } case Instruction::RETURN: { - HInstruction* value = LoadLocal(instruction.VRegA()); - current_block_->AddInstruction(new (arena_) HReturn(value)); - current_block_->AddSuccessor(exit_block_); - current_block_ = nullptr; + BuildReturn(instruction, Primitive::kPrimInt); break; } - case Instruction::INVOKE_STATIC: { - uint32_t method_idx = instruction.VRegB_35c(); - const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx); - const size_t number_of_arguments = instruction.VRegA_35c(); - - if (Primitive::GetType(descriptor[0]) != Primitive::kPrimVoid) { - return false; - } + case Instruction::RETURN_OBJECT: { + BuildReturn(instruction, Primitive::kPrimNot); + break; + } - HInvokeStatic* invoke = new (arena_) HInvokeStatic( - arena_, number_of_arguments, dex_offset, method_idx); + case Instruction::RETURN_WIDE: { + BuildReturn(instruction, Primitive::kPrimLong); + break; + } + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_DIRECT: { + uint32_t method_idx = instruction.VRegB_35c(); + uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); uint32_t args[5]; instruction.GetArgs(args); - - for (size_t i = 0; i < number_of_arguments; i++) { - HInstruction* arg = LoadLocal(args[i]); - HInstruction* push = new (arena_) HPushArgument(arg, i); - current_block_->AddInstruction(push); - invoke->SetArgumentAt(i, push); + if (!BuildInvoke(instruction, dex_offset, method_idx, number_of_vreg_arguments, false, args, -1)) { + return false; } - - current_block_->AddInstruction(invoke); break; } - case Instruction::INVOKE_STATIC_RANGE: { + case Instruction::INVOKE_STATIC_RANGE: + case Instruction::INVOKE_DIRECT_RANGE: { uint32_t method_idx = instruction.VRegB_3rc(); - const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx); - const size_t number_of_arguments = instruction.VRegA_3rc(); - - if (Primitive::GetType(descriptor[0]) != Primitive::kPrimVoid) { + uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); + uint32_t register_index = instruction.VRegC(); + if (!BuildInvoke(instruction, dex_offset, method_idx, + number_of_vreg_arguments, true, nullptr, register_index)) { return false; } - - HInvokeStatic* invoke = new (arena_) HInvokeStatic( - arena_, number_of_arguments, dex_offset, method_idx); - int32_t register_index = instruction.VRegC(); - for (size_t i = 0; i < number_of_arguments; i++) { - HInstruction* arg = LoadLocal(register_index + i); - HInstruction* push = new (arena_) HPushArgument(arg, i); - current_block_->AddInstruction(push); - invoke->SetArgumentAt(i, push); - } - current_block_->AddInstruction(invoke); break; } case Instruction::ADD_INT: { - HInstruction* first = LoadLocal(instruction.VRegB()); - HInstruction* second = LoadLocal(instruction.VRegC()); - current_block_->AddInstruction(new (arena_) HAdd(Primitive::kPrimInt, first, second)); - UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + Binop_32x<HAdd>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::ADD_LONG: { + Binop_32x<HAdd>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::SUB_INT: { + Binop_32x<HSub>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::SUB_LONG: { + Binop_32x<HSub>(instruction, Primitive::kPrimLong); break; } case Instruction::ADD_INT_2ADDR: { - HInstruction* first = LoadLocal(instruction.VRegA()); - HInstruction* second = LoadLocal(instruction.VRegB()); - current_block_->AddInstruction(new (arena_) HAdd(Primitive::kPrimInt, first, second)); - UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + Binop_12x<HAdd>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::ADD_LONG_2ADDR: { + Binop_12x<HAdd>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::SUB_INT_2ADDR: { + Binop_12x<HSub>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::SUB_LONG_2ADDR: { + Binop_12x<HSub>(instruction, Primitive::kPrimLong); break; } case Instruction::ADD_INT_LIT16: { - HInstruction* first = LoadLocal(instruction.VRegB()); - HInstruction* second = GetConstant(instruction.VRegC_22s()); - current_block_->AddInstruction(new (arena_) HAdd(Primitive::kPrimInt, first, second)); - UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + Binop_22s<HAdd>(instruction, false); + break; + } + + case Instruction::RSUB_INT: { + Binop_22s<HSub>(instruction, true); break; } case Instruction::ADD_INT_LIT8: { - HInstruction* first = LoadLocal(instruction.VRegB()); - HInstruction* second = GetConstant(instruction.VRegC_22b()); - current_block_->AddInstruction(new (arena_) HAdd(Primitive::kPrimInt, first, second)); + Binop_22b<HAdd>(instruction, false); + break; + } + + case Instruction::RSUB_INT_LIT8: { + Binop_22b<HSub>(instruction, true); + break; + } + + case Instruction::NEW_INSTANCE: { + current_block_->AddInstruction( + new (arena_) HNewInstance(dex_offset, instruction.VRegB_21c())); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + break; + } + + case Instruction::MOVE_RESULT_WIDE: { UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); break; } @@ -286,7 +509,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ return true; } -HIntConstant* HGraphBuilder::GetConstant0() { +HIntConstant* HGraphBuilder::GetIntConstant0() { if (constant0_ != nullptr) { return constant0_; } @@ -295,7 +518,7 @@ HIntConstant* HGraphBuilder::GetConstant0() { return constant0_; } -HIntConstant* HGraphBuilder::GetConstant1() { +HIntConstant* HGraphBuilder::GetIntConstant1() { if (constant1_ != nullptr) { return constant1_; } @@ -304,10 +527,10 @@ HIntConstant* HGraphBuilder::GetConstant1() { return constant1_; } -HIntConstant* HGraphBuilder::GetConstant(int constant) { +HIntConstant* HGraphBuilder::GetIntConstant(int32_t constant) { switch (constant) { - case 0: return GetConstant0(); - case 1: return GetConstant1(); + case 0: return GetIntConstant0(); + case 1: return GetIntConstant1(); default: { HIntConstant* instruction = new (arena_) HIntConstant(constant); entry_block_->AddInstruction(instruction); @@ -316,6 +539,12 @@ HIntConstant* HGraphBuilder::GetConstant(int constant) { } } +HLongConstant* HGraphBuilder::GetLongConstant(int64_t constant) { + HLongConstant* instruction = new (arena_) HLongConstant(constant); + entry_block_->AddInstruction(instruction); + return instruction; +} + HLocal* HGraphBuilder::GetLocalAt(int register_index) const { return locals_.Get(register_index); } @@ -325,9 +554,9 @@ void HGraphBuilder::UpdateLocal(int register_index, HInstruction* instruction) c current_block_->AddInstruction(new (arena_) HStoreLocal(local, instruction)); } -HInstruction* HGraphBuilder::LoadLocal(int register_index) const { +HInstruction* HGraphBuilder::LoadLocal(int register_index, Primitive::Type type) const { HLocal* local = GetLocalAt(register_index); - current_block_->AddInstruction(new (arena_) HLoadLocal(local)); + current_block_->AddInstruction(new (arena_) HLoadLocal(local, type)); return current_block_->GetLastInstruction(); } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 46ca9aabd7..108514a632 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -19,6 +19,7 @@ #include "dex_file.h" #include "driver/dex_compilation_unit.h" +#include "primitive.h" #include "utils/allocation.h" #include "utils/growable_array.h" @@ -29,13 +30,14 @@ class Instruction; class HBasicBlock; class HGraph; class HIntConstant; +class HLongConstant; class HInstruction; class HLocal; class HGraphBuilder : public ValueObject { public: HGraphBuilder(ArenaAllocator* arena, - const DexCompilationUnit* dex_compilation_unit = nullptr, + DexCompilationUnit* dex_compilation_unit = nullptr, const DexFile* dex_file = nullptr) : arena_(arena), branch_targets_(arena, 0), @@ -63,13 +65,43 @@ class HGraphBuilder : public ValueObject { void MaybeUpdateCurrentBlock(size_t index); HBasicBlock* FindBlockStartingAt(int32_t index) const; - HIntConstant* GetConstant0(); - HIntConstant* GetConstant1(); - HIntConstant* GetConstant(int constant); - void InitializeLocals(int count); + HIntConstant* GetIntConstant0(); + HIntConstant* GetIntConstant1(); + HIntConstant* GetIntConstant(int32_t constant); + HLongConstant* GetLongConstant(int64_t constant); + void InitializeLocals(uint16_t count); HLocal* GetLocalAt(int register_index) const; void UpdateLocal(int register_index, HInstruction* instruction) const; - HInstruction* LoadLocal(int register_index) const; + HInstruction* LoadLocal(int register_index, Primitive::Type type) const; + + // Temporarily returns whether the compiler supports the parameters + // of the method. + bool InitializeParameters(uint16_t number_of_parameters); + + template<typename T> + void Binop_32x(const Instruction& instruction, Primitive::Type type); + + template<typename T> + void Binop_12x(const Instruction& instruction, Primitive::Type type); + + template<typename T> + void Binop_22b(const Instruction& instruction, bool reverse); + + template<typename T> + void Binop_22s(const Instruction& instruction, bool reverse); + + template<typename T> void If_22t(const Instruction& instruction, int32_t dex_offset, bool is_not); + + void BuildReturn(const Instruction& instruction, Primitive::Type type); + + // Builds an invocation node and returns whether the instruction is supported. + bool BuildInvoke(const Instruction& instruction, + uint32_t dex_offset, + uint32_t method_idx, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index); ArenaAllocator* const arena_; @@ -89,7 +121,7 @@ class HGraphBuilder : public ValueObject { HIntConstant* constant1_; const DexFile* const dex_file_; - const DexCompilationUnit* const dex_compilation_unit_; + DexCompilationUnit* const dex_compilation_unit_; DISALLOW_COPY_AND_ASSIGN(HGraphBuilder); }; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 05e5d7b8ef..7e63c69f5c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -30,12 +30,11 @@ namespace art { void CodeGenerator::Compile(CodeAllocator* allocator) { - frame_size_ = GetGraph()->GetMaximumNumberOfOutVRegs() * kWordSize; const GrowableArray<HBasicBlock*>* blocks = GetGraph()->GetBlocks(); DCHECK(blocks->Get(0) == GetGraph()->GetEntryBlock()); DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks->Get(1))); - CompileEntryBlock(); - for (size_t i = 1; i < blocks->Size(); i++) { + GenerateFrameEntry(); + for (size_t i = 0; i < blocks->Size(); i++) { CompileBlock(blocks->Get(i)); } size_t code_size = GetAssembler()->CodeSize(); @@ -44,32 +43,11 @@ void CodeGenerator::Compile(CodeAllocator* allocator) { GetAssembler()->FinalizeInstructions(code); } -void CodeGenerator::CompileEntryBlock() { - HGraphVisitor* location_builder = GetLocationBuilder(); - HGraphVisitor* instruction_visitor = GetInstructionVisitor(); - // The entry block contains all locals for this method. By visiting the entry block, - // we're computing the required frame size. - for (HInstructionIterator it(GetGraph()->GetEntryBlock()); !it.Done(); it.Advance()) { - HInstruction* current = it.Current(); - // Instructions in the entry block should not generate code. - if (kIsDebugBuild) { - current->Accept(location_builder); - DCHECK(current->GetLocations() == nullptr); - } - current->Accept(instruction_visitor); - } - GenerateFrameEntry(); -} - void CodeGenerator::CompileBlock(HBasicBlock* block) { Bind(GetLabelOf(block)); HGraphVisitor* location_builder = GetLocationBuilder(); HGraphVisitor* instruction_visitor = GetInstructionVisitor(); for (HInstructionIterator it(block); !it.Done(); it.Advance()) { - // For each instruction, we emulate a stack-based machine, where the inputs are popped from - // the runtime stack, and the result is pushed on the stack. We currently can do this because - // we do not perform any code motion, and the Dex format does not reference individual - // instructions but uses registers instead (our equivalent of HLocal). HInstruction* current = it.Current(); current->Accept(location_builder); InitLocations(current); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 01bbcc0bb6..5c7cac1e5c 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_H_ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_H_ +#include "base/bit_field.h" #include "globals.h" #include "instruction_set.h" #include "memory_region.h" @@ -25,6 +26,8 @@ namespace art { +static size_t constexpr kVRegSize = 4; + class DexCompilationUnit; class CodeAllocator { @@ -49,30 +52,149 @@ struct PcInfo { */ class Location : public ValueObject { public: - template<typename T> - T reg() const { return static_cast<T>(reg_); } + enum Kind { + kInvalid = 0, + kStackSlot = 1, // Word size slot. + kDoubleStackSlot = 2, // 64bit stack slot. + kRegister = 3, + // On 32bits architectures, quick can pass a long where the + // low bits are in the last parameter register, and the high + // bits are in a stack slot. The kQuickParameter kind is for + // handling this special case. + kQuickParameter = 4, + }; + + Location() : value_(kInvalid) { + DCHECK(!IsValid()); + } + + Location(const Location& other) : ValueObject(), value_(other.value_) {} + + Location& operator=(const Location& other) { + value_ = other.value_; + return *this; + } - Location() : reg_(kInvalid) { } - explicit Location(uword reg) : reg_(reg) { } + bool IsValid() const { + return value_ != kInvalid; + } - static Location RegisterLocation(uword reg) { - return Location(reg); + // Register locations. + static Location RegisterLocation(ManagedRegister reg) { + return Location(kRegister, reg.RegId()); } - bool IsValid() const { return reg_ != kInvalid; } + bool IsRegister() const { + return GetKind() == kRegister; + } - Location(const Location& other) : reg_(other.reg_) { } + ManagedRegister reg() const { + DCHECK(IsRegister()); + return static_cast<ManagedRegister>(GetPayload()); + } - Location& operator=(const Location& other) { - reg_ = other.reg_; - return *this; + static uword EncodeStackIndex(intptr_t stack_index) { + DCHECK(-kStackIndexBias <= stack_index); + DCHECK(stack_index < kStackIndexBias); + return static_cast<uword>(kStackIndexBias + stack_index); + } + + static Location StackSlot(intptr_t stack_index) { + uword payload = EncodeStackIndex(stack_index); + Location loc(kStackSlot, payload); + // Ensure that sign is preserved. + DCHECK_EQ(loc.GetStackIndex(), stack_index); + return loc; + } + + bool IsStackSlot() const { + return GetKind() == kStackSlot; + } + + static Location DoubleStackSlot(intptr_t stack_index) { + uword payload = EncodeStackIndex(stack_index); + Location loc(kDoubleStackSlot, payload); + // Ensure that sign is preserved. + DCHECK_EQ(loc.GetStackIndex(), stack_index); + return loc; + } + + bool IsDoubleStackSlot() const { + return GetKind() == kDoubleStackSlot; + } + + intptr_t GetStackIndex() const { + DCHECK(IsStackSlot() || IsDoubleStackSlot()); + // Decode stack index manually to preserve sign. + return GetPayload() - kStackIndexBias; + } + + intptr_t GetHighStackIndex(uintptr_t word_size) const { + DCHECK(IsDoubleStackSlot()); + // Decode stack index manually to preserve sign. + return GetPayload() - kStackIndexBias + word_size; + } + + static Location QuickParameter(uint32_t parameter_index) { + return Location(kQuickParameter, parameter_index); + } + + uint32_t GetQuickParameterIndex() const { + DCHECK(IsQuickParameter()); + return GetPayload(); + } + + bool IsQuickParameter() const { + return GetKind() == kQuickParameter; + } + + arm::ArmManagedRegister AsArm() const; + x86::X86ManagedRegister AsX86() const; + + Kind GetKind() const { + return KindField::Decode(value_); + } + + bool Equals(Location other) const { + return value_ == other.value_; + } + + const char* DebugString() const { + switch (GetKind()) { + case kInvalid: return "?"; + case kRegister: return "R"; + case kStackSlot: return "S"; + case kDoubleStackSlot: return "DS"; + case kQuickParameter: return "Q"; + } + return "?"; } private: - // The target register for that location. - // TODO: Support stack location. - uword reg_; - static const uword kInvalid = -1; + // Number of bits required to encode Kind value. + static constexpr uint32_t kBitsForKind = 4; + static constexpr uint32_t kBitsForPayload = kWordSize * kBitsPerByte - kBitsForKind; + + explicit Location(uword value) : value_(value) {} + + Location(Kind kind, uword payload) + : value_(KindField::Encode(kind) | PayloadField::Encode(payload)) {} + + uword GetPayload() const { + return PayloadField::Decode(value_); + } + + typedef BitField<Kind, 0, kBitsForKind> KindField; + typedef BitField<uword, kBitsForKind, kBitsForPayload> PayloadField; + + // Layout for stack slots. + static const intptr_t kStackIndexBias = + static_cast<intptr_t>(1) << (kBitsForPayload - 1); + + // Location either contains kind and payload fields or a tagged handle for + // a constant locations. Values of enumeration Kind are selected in such a + // way that none of them can be interpreted as a kConstant tag. + uword value_; }; /** @@ -145,6 +267,7 @@ class CodeGenerator : public ArenaObject { virtual HGraphVisitor* GetLocationBuilder() = 0; virtual HGraphVisitor* GetInstructionVisitor() = 0; virtual Assembler* GetAssembler() = 0; + virtual size_t GetWordSize() const = 0; uint32_t GetFrameSize() const { return frame_size_; } void SetFrameSize(uint32_t size) { frame_size_ = size; } @@ -179,7 +302,6 @@ class CodeGenerator : public ArenaObject { private: void InitLocations(HInstruction* instruction); void CompileBlock(HBasicBlock* block); - void CompileEntryBlock(); HGraph* const graph_; @@ -203,11 +325,10 @@ class CallingConvention { return registers_[index]; } - uint8_t GetStackOffsetOf(size_t index) const { - DCHECK_GE(index, number_of_registers_); + uint8_t GetStackOffsetOf(size_t index, size_t word_size) const { // We still reserve the space for parameters passed by registers. - // Add kWordSize for the method pointer. - return index * kWordSize + kWordSize; + // Add word_size for the method pointer. + return index * kVRegSize + word_size; } private: diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 09d6f7b36a..27691ac080 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -17,6 +17,7 @@ #include "code_generator_arm.h" #include "utils/assembler.h" #include "utils/arm/assembler_arm.h" +#include "utils/arm/managed_register_arm.h" #include "mirror/array.h" #include "mirror/art_method.h" @@ -24,11 +25,20 @@ #define __ reinterpret_cast<ArmAssembler*>(GetAssembler())-> namespace art { + +arm::ArmManagedRegister Location::AsArm() const { + return reg().AsArm(); +} + namespace arm { static constexpr int kNumberOfPushedRegistersAtEntry = 1; static constexpr int kCurrentMethodStackOffset = 0; +static Location ArmCoreLocation(Register reg) { + return Location::RegisterLocation(ArmManagedRegister::FromCoreRegister(reg)); +} + InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), @@ -38,15 +48,19 @@ void CodeGeneratorARM::GenerateFrameEntry() { core_spill_mask_ |= (1 << LR); __ PushList((1 << LR)); - // Add the current ART method to the frame size and the return PC. - SetFrameSize(RoundUp(GetFrameSize() + 2 * kWordSize, kStackAlignment)); - // The retrn PC has already been pushed on the stack. - __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kWordSize)); + SetFrameSize(RoundUp( + (GetGraph()->GetMaximumNumberOfOutVRegs() + GetGraph()->GetNumberOfVRegs()) * kVRegSize + + kVRegSize // filler + + kArmWordSize // Art method + + kNumberOfPushedRegistersAtEntry * kArmWordSize, + kStackAlignment)); + // The return PC has already been pushed on the stack. + __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize)); __ str(R0, Address(SP, 0)); } void CodeGeneratorARM::GenerateFrameExit() { - __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kWordSize); + __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize); __ PopList((1 << PC)); } @@ -55,21 +69,210 @@ void CodeGeneratorARM::Bind(Label* label) { } int32_t CodeGeneratorARM::GetStackSlot(HLocal* local) const { - return (GetGraph()->GetMaximumNumberOfOutVRegs() + local->GetRegNumber()) * kWordSize; + uint16_t reg_number = local->GetRegNumber(); + uint16_t number_of_vregs = GetGraph()->GetNumberOfVRegs(); + uint16_t number_of_in_vregs = GetGraph()->GetNumberOfInVRegs(); + if (reg_number >= number_of_vregs - number_of_in_vregs) { + // Local is a parameter of the method. It is stored in the caller's frame. + return GetFrameSize() + kArmWordSize // ART method + + (reg_number - number_of_vregs + number_of_in_vregs) * kVRegSize; + } else { + // Local is a temporary in this method. It is stored in this method's frame. + return GetFrameSize() - (kNumberOfPushedRegistersAtEntry * kArmWordSize) + - kVRegSize // filler. + - (number_of_vregs * kVRegSize) + + (reg_number * kVRegSize); + } +} + +Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { + switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + uint32_t index = gp_index_++; + if (index < calling_convention.GetNumberOfRegisters()) { + return ArmCoreLocation(calling_convention.GetRegisterAt(index)); + } else { + return Location::StackSlot(calling_convention.GetStackOffsetOf(index, kArmWordSize)); + } + } + + case Primitive::kPrimLong: { + uint32_t index = gp_index_; + gp_index_ += 2; + if (index + 1 < calling_convention.GetNumberOfRegisters()) { + return Location::RegisterLocation(ArmManagedRegister::FromRegisterPair( + calling_convention.GetRegisterPairAt(index))); + } else if (index + 1 == calling_convention.GetNumberOfRegisters()) { + return Location::QuickParameter(index); + } else { + return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index, kArmWordSize)); + } + } + + case Primitive::kPrimDouble: + case Primitive::kPrimFloat: + LOG(FATAL) << "Unimplemented parameter type " << type; + break; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unexpected parameter type " << type; + break; + } + return Location(); +} + +void CodeGeneratorARM::Move32(Location destination, Location source) { + if (source.Equals(destination)) { + return; + } + if (destination.IsRegister()) { + if (source.IsRegister()) { + __ Mov(destination.AsArm().AsCoreRegister(), source.AsArm().AsCoreRegister()); + } else { + __ ldr(destination.AsArm().AsCoreRegister(), Address(SP, source.GetStackIndex())); + } + } else { + DCHECK(destination.IsStackSlot()); + if (source.IsRegister()) { + __ str(source.AsArm().AsCoreRegister(), Address(SP, destination.GetStackIndex())); + } else { + __ ldr(R0, Address(SP, source.GetStackIndex())); + __ str(R0, Address(SP, destination.GetStackIndex())); + } + } +} + +void CodeGeneratorARM::Move64(Location destination, Location source) { + if (source.Equals(destination)) { + return; + } + if (destination.IsRegister()) { + if (source.IsRegister()) { + __ Mov(destination.AsArm().AsRegisterPairLow(), source.AsArm().AsRegisterPairLow()); + __ Mov(destination.AsArm().AsRegisterPairHigh(), source.AsArm().AsRegisterPairHigh()); + } else if (source.IsQuickParameter()) { + uint32_t argument_index = source.GetQuickParameterIndex(); + InvokeDexCallingConvention calling_convention; + __ Mov(destination.AsArm().AsRegisterPairLow(), + calling_convention.GetRegisterAt(argument_index)); + __ ldr(destination.AsArm().AsRegisterPairHigh(), + Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1, kArmWordSize) + GetFrameSize())); + } else { + DCHECK(source.IsDoubleStackSlot()); + if (destination.AsArm().AsRegisterPair() == R1_R2) { + __ ldr(R1, Address(SP, source.GetStackIndex())); + __ ldr(R2, Address(SP, source.GetHighStackIndex(kArmWordSize))); + } else { + __ LoadFromOffset(kLoadWordPair, destination.AsArm().AsRegisterPairLow(), + SP, source.GetStackIndex()); + } + } + } else if (destination.IsQuickParameter()) { + InvokeDexCallingConvention calling_convention; + uint32_t argument_index = destination.GetQuickParameterIndex(); + if (source.IsRegister()) { + __ Mov(calling_convention.GetRegisterAt(argument_index), source.AsArm().AsRegisterPairLow()); + __ str(source.AsArm().AsRegisterPairHigh(), + Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1, kArmWordSize))); + } else { + DCHECK(source.IsDoubleStackSlot()); + __ ldr(calling_convention.GetRegisterAt(argument_index), Address(SP, source.GetStackIndex())); + __ ldr(R0, Address(SP, source.GetHighStackIndex(kArmWordSize))); + __ str(R0, Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1, kArmWordSize))); + } + } else { + DCHECK(destination.IsDoubleStackSlot()); + if (source.IsRegister()) { + if (source.AsArm().AsRegisterPair() == R1_R2) { + __ str(R1, Address(SP, destination.GetStackIndex())); + __ str(R2, Address(SP, destination.GetHighStackIndex(kArmWordSize))); + } else { + __ StoreToOffset(kStoreWordPair, source.AsArm().AsRegisterPairLow(), + SP, destination.GetStackIndex()); + } + } else if (source.IsQuickParameter()) { + InvokeDexCallingConvention calling_convention; + uint32_t argument_index = source.GetQuickParameterIndex(); + __ str(calling_convention.GetRegisterAt(argument_index), + Address(SP, destination.GetStackIndex())); + __ ldr(R0, + Address(SP, calling_convention.GetStackOffsetOf(argument_index + 1, kArmWordSize) + GetFrameSize())); + __ str(R0, Address(SP, destination.GetHighStackIndex(kArmWordSize))); + } else { + DCHECK(source.IsDoubleStackSlot()); + __ ldr(R0, Address(SP, source.GetStackIndex())); + __ str(R0, Address(SP, destination.GetStackIndex())); + __ ldr(R0, Address(SP, source.GetHighStackIndex(kArmWordSize))); + __ str(R0, Address(SP, destination.GetHighStackIndex(kArmWordSize))); + } + } } void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) { if (instruction->AsIntConstant() != nullptr) { - __ LoadImmediate(location.reg<Register>(), instruction->AsIntConstant()->GetValue()); + int32_t value = instruction->AsIntConstant()->GetValue(); + if (location.IsRegister()) { + __ LoadImmediate(location.AsArm().AsCoreRegister(), value); + } else { + __ LoadImmediate(R0, value); + __ str(R0, Address(SP, location.GetStackIndex())); + } + } else if (instruction->AsLongConstant() != nullptr) { + int64_t value = instruction->AsLongConstant()->GetValue(); + if (location.IsRegister()) { + __ LoadImmediate(location.AsArm().AsRegisterPairLow(), Low32Bits(value)); + __ LoadImmediate(location.AsArm().AsRegisterPairHigh(), High32Bits(value)); + } else { + __ LoadImmediate(R0, Low32Bits(value)); + __ str(R0, Address(SP, location.GetStackIndex())); + __ LoadImmediate(R0, High32Bits(value)); + __ str(R0, Address(SP, location.GetHighStackIndex(kArmWordSize))); + } } else if (instruction->AsLoadLocal() != nullptr) { - __ LoadFromOffset(kLoadWord, location.reg<Register>(), - SP, GetStackSlot(instruction->AsLoadLocal()->GetLocal())); + uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + Move32(location, Location::StackSlot(stack_slot)); + break; + + case Primitive::kPrimLong: + Move64(location, Location::DoubleStackSlot(stack_slot)); + break; + + default: + LOG(FATAL) << "Unimplemented type " << instruction->GetType(); + } } else { // This can currently only happen when the instruction that requests the move // is the next to be compiled. DCHECK_EQ(instruction->GetNext(), move_for); - __ mov(location.reg<Register>(), - ShifterOperand(instruction->GetLocations()->Out().reg<Register>())); + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimNot: + case Primitive::kPrimInt: + Move32(location, instruction->GetLocations()->Out()); + break; + + case Primitive::kPrimLong: + Move64(location, instruction->GetLocations()->Out()); + break; + + default: + LOG(FATAL) << "Unimplemented type " << instruction->GetType(); + } } } @@ -99,13 +302,13 @@ void InstructionCodeGeneratorARM::VisitExit(HExit* exit) { void LocationsBuilderARM::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, Location(R0)); + locations->SetInAt(0, ArmCoreLocation(R0)); if_instr->SetLocations(locations); } void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { // TODO: Generate the input as a condition, instead of materializing in a register. - __ cmp(if_instr->GetLocations()->InAt(0).reg<Register>(), ShifterOperand(0)); + __ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(0)); __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()), EQ); if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfTrueSuccessor())) { __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); @@ -114,18 +317,18 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { void LocationsBuilderARM::VisitEqual(HEqual* equal) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal); - locations->SetInAt(0, Location(R0)); - locations->SetInAt(1, Location(R1)); - locations->SetOut(Location(R0)); + locations->SetInAt(0, ArmCoreLocation(R0)); + locations->SetInAt(1, ArmCoreLocation(R1)); + locations->SetOut(ArmCoreLocation(R0)); equal->SetLocations(locations); } void InstructionCodeGeneratorARM::VisitEqual(HEqual* equal) { LocationSummary* locations = equal->GetLocations(); - __ teq(locations->InAt(0).reg<Register>(), - ShifterOperand(locations->InAt(1).reg<Register>())); - __ mov(locations->Out().reg<Register>(), ShifterOperand(1), EQ); - __ mov(locations->Out().reg<Register>(), ShifterOperand(0), NE); + __ teq(locations->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1), EQ); + __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0), NE); } void LocationsBuilderARM::VisitLocal(HLocal* local) { @@ -134,7 +337,6 @@ void LocationsBuilderARM::VisitLocal(HLocal* local) { void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) { DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); - codegen_->SetFrameSize(codegen_->GetFrameSize() + kWordSize); } void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) { @@ -147,14 +349,27 @@ void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load) { void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store); - locations->SetInAt(1, Location(R0)); + switch (store->InputAt(1)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + case Primitive::kPrimLong: + locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + default: + LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType(); + } store->SetLocations(locations); } void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) { - LocationSummary* locations = store->GetLocations(); - __ StoreToOffset(kStoreWord, locations->InAt(1).reg<Register>(), - SP, codegen_->GetStackSlot(store->GetLocal())); } void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { @@ -165,6 +380,14 @@ void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) { // Will be generated at use site. } +void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { + constant->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) { + // Will be generated at use site. +} + void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } @@ -175,56 +398,83 @@ void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret) { void LocationsBuilderARM::VisitReturn(HReturn* ret) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret); - locations->SetInAt(0, Location(R0)); + switch (ret->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + locations->SetInAt(0, ArmCoreLocation(R0)); + break; + + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); + break; + + default: + LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType(); + } + ret->SetLocations(locations); } void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { - DCHECK_EQ(ret->GetLocations()->InAt(0).reg<Register>(), R0); + if (kIsDebugBuild) { + switch (ret->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsCoreRegister(), R0); + break; + + case Primitive::kPrimLong: + DCHECK_EQ(ret->GetLocations()->InAt(0).AsArm().AsRegisterPair(), R0_R1); + break; + + default: + LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType(); + } + } codegen_->GenerateFrameExit(); } -static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 }; -static constexpr int kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); +void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); + locations->AddTemp(ArmCoreLocation(R0)); -class InvokeStaticCallingConvention : public CallingConvention<Register> { - public: - InvokeStaticCallingConvention() - : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {} + InvokeDexCallingConventionVisitor calling_convention_visitor; + for (int i = 0; i < invoke->InputCount(); i++) { + HInstruction* input = invoke->InputAt(i); + locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); + } - private: - DISALLOW_COPY_AND_ASSIGN(InvokeStaticCallingConvention); -}; + switch (invoke->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + locations->SetOut(ArmCoreLocation(R0)); + break; -void LocationsBuilderARM::VisitPushArgument(HPushArgument* argument) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(argument); - InvokeStaticCallingConvention calling_convention; - if (argument->GetArgumentIndex() < calling_convention.GetNumberOfRegisters()) { - Location location = Location(calling_convention.GetRegisterAt(argument->GetArgumentIndex())); - locations->SetInAt(0, location); - locations->SetOut(location); - } else { - locations->SetInAt(0, Location(R0)); - } - argument->SetLocations(locations); -} + case Primitive::kPrimLong: + locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); + break; -void InstructionCodeGeneratorARM::VisitPushArgument(HPushArgument* argument) { - uint8_t argument_index = argument->GetArgumentIndex(); - InvokeStaticCallingConvention calling_convention; - size_t parameter_registers = calling_convention.GetNumberOfRegisters(); - LocationSummary* locations = argument->GetLocations(); - if (argument_index >= parameter_registers) { - uint8_t offset = calling_convention.GetStackOffsetOf(argument_index); - __ StoreToOffset(kStoreWord, locations->InAt(0).reg<Register>(), SP, offset); - } else { - DCHECK_EQ(locations->Out().reg<Register>(), locations->InAt(0).reg<Register>()); + case Primitive::kPrimVoid: + break; + + case Primitive::kPrimDouble: + case Primitive::kPrimFloat: + LOG(FATAL) << "Unimplemented return type " << invoke->GetType(); + break; } -} -void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); - locations->AddTemp(Location(R0)); invoke->SetLocations(locations); } @@ -233,9 +483,9 @@ void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) { } void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { - Register temp = invoke->GetLocations()->GetTemp(0).reg<Register>(); + Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister(); size_t index_in_cache = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - invoke->GetIndexInDexCache() * kWordSize; + invoke->GetIndexInDexCache() * kArmWordSize; // TODO: Implement all kinds of calls: // 1) boot -> boot @@ -263,13 +513,30 @@ void LocationsBuilderARM::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add); switch (add->GetResultType()) { case Primitive::kPrimInt: { - locations->SetInAt(0, Location(R0)); - locations->SetInAt(1, Location(R1)); - locations->SetOut(Location(R0)); + locations->SetInAt(0, ArmCoreLocation(R0)); + locations->SetInAt(1, ArmCoreLocation(R1)); + locations->SetOut(ArmCoreLocation(R0)); + break; + } + + case Primitive::kPrimLong: { + locations->SetInAt( + 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); + locations->SetInAt( + 1, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R2_R3))); + locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); break; } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected add type " << add->GetResultType(); + break; + default: - LOG(FATAL) << "Unimplemented"; + LOG(FATAL) << "Unimplemented add type " << add->GetResultType(); } add->SetLocations(locations); } @@ -278,13 +545,153 @@ void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) { LocationSummary* locations = add->GetLocations(); switch (add->GetResultType()) { case Primitive::kPrimInt: - __ add(locations->Out().reg<Register>(), - locations->InAt(0).reg<Register>(), - ShifterOperand(locations->InAt(1).reg<Register>())); + __ add(locations->Out().AsArm().AsCoreRegister(), + locations->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + break; + + case Primitive::kPrimLong: + __ adds(locations->Out().AsArm().AsRegisterPairLow(), + locations->InAt(0).AsArm().AsRegisterPairLow(), + ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow())); + __ adc(locations->Out().AsArm().AsRegisterPairHigh(), + locations->InAt(0).AsArm().AsRegisterPairHigh(), + ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh())); + break; + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected add type " << add->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented add type " << add->GetResultType(); + } +} + +void LocationsBuilderARM::VisitSub(HSub* sub) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub); + switch (sub->GetResultType()) { + case Primitive::kPrimInt: { + locations->SetInAt(0, ArmCoreLocation(R0)); + locations->SetInAt(1, ArmCoreLocation(R1)); + locations->SetOut(ArmCoreLocation(R0)); + break; + } + + case Primitive::kPrimLong: { + locations->SetInAt( + 0, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); + locations->SetInAt( + 1, Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R2_R3))); + locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1))); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + } + sub->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { + LocationSummary* locations = sub->GetLocations(); + switch (sub->GetResultType()) { + case Primitive::kPrimInt: + __ sub(locations->Out().AsArm().AsCoreRegister(), + locations->InAt(0).AsArm().AsCoreRegister(), + ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister())); + break; + + case Primitive::kPrimLong: + __ subs(locations->Out().AsArm().AsRegisterPairLow(), + locations->InAt(0).AsArm().AsRegisterPairLow(), + ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairLow())); + __ sbc(locations->Out().AsArm().AsRegisterPairHigh(), + locations->InAt(0).AsArm().AsRegisterPairHigh(), + ShifterOperand(locations->InAt(1).AsArm().AsRegisterPairHigh())); break; + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + break; + default: - LOG(FATAL) << "Unimplemented"; + LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + } +} + +static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1 }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + +void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetOut(ArmCoreLocation(R0)); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocObjectWithAccessCheck).Int32Value(); + __ ldr(LR, Address(TR, offset)); + __ blx(LR); + + codegen_->RecordPcInfo(instruction->GetDexPc()); +} + +void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); + if (location.IsStackSlot()) { + location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } else if (location.IsDoubleStackSlot()) { + location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); } + locations->SetOut(location); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) { + // Nothing to do, the parameter is already at its location. +} + +void LocationsBuilderARM::VisitNot(HNot* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, ArmCoreLocation(R0)); + locations->SetOut(ArmCoreLocation(R0)); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) { + LocationSummary* locations = instruction->GetLocations(); + __ eor(locations->Out().AsArm().AsCoreRegister(), + locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(1)); } } // namespace arm diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 52d6b2e641..ed35f94e2b 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -22,15 +22,47 @@ #include "utils/arm/assembler_arm.h" namespace art { +namespace arm { + +class CodeGeneratorARM; -class Assembler; -class Label; +static constexpr size_t kArmWordSize = 4; -namespace arm { +static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 }; +static constexpr RegisterPair kParameterCorePairRegisters[] = { R1_R2, R2_R3 }; +static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); + +class InvokeDexCallingConvention : public CallingConvention<Register> { + public: + InvokeDexCallingConvention() + : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {} + + RegisterPair GetRegisterPairAt(size_t argument_index) { + DCHECK_LT(argument_index + 1, GetNumberOfRegisters()); + return kParameterCorePairRegisters[argument_index]; + } + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention); +}; + +class InvokeDexCallingConventionVisitor { + public: + InvokeDexCallingConventionVisitor() : gp_index_(0) {} + + Location GetNextLocation(Primitive::Type type); + + private: + InvokeDexCallingConvention calling_convention; + uint32_t gp_index_; + + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor); +}; class LocationsBuilderARM : public HGraphVisitor { public: - explicit LocationsBuilderARM(HGraph* graph) : HGraphVisitor(graph) { } + explicit LocationsBuilderARM(HGraph* graph, CodeGeneratorARM* codegen) + : HGraphVisitor(graph), codegen_(codegen) {} #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr); @@ -40,11 +72,12 @@ class LocationsBuilderARM : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION private: + CodeGeneratorARM* const codegen_; + InvokeDexCallingConventionVisitor parameter_visitor_; + DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARM); }; -class CodeGeneratorARM; - class InstructionCodeGeneratorARM : public HGraphVisitor { public: InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen); @@ -70,7 +103,7 @@ class CodeGeneratorARM : public CodeGenerator { public: explicit CodeGeneratorARM(HGraph* graph) : CodeGenerator(graph), - location_builder_(graph), + location_builder_(graph, this), instruction_visitor_(graph, this) { } virtual ~CodeGeneratorARM() { } @@ -79,6 +112,10 @@ class CodeGeneratorARM : public CodeGenerator { virtual void Bind(Label* label) OVERRIDE; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; + virtual size_t GetWordSize() const OVERRIDE { + return kArmWordSize; + } + virtual HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } @@ -94,6 +131,11 @@ class CodeGeneratorARM : public CodeGenerator { int32_t GetStackSlot(HLocal* local) const; private: + // Helper method to move a 32bits value between two locations. + void Move32(Location destination, Location source); + // Helper method to move a 64bits value between two locations. + void Move64(Location destination, Location source); + LocationsBuilderARM location_builder_; InstructionCodeGeneratorARM instruction_visitor_; ArmAssembler assembler_; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 7b0a087356..114263161d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -17,6 +17,7 @@ #include "code_generator_x86.h" #include "utils/assembler.h" #include "utils/x86/assembler_x86.h" +#include "utils/x86/managed_register_x86.h" #include "mirror/array.h" #include "mirror/art_method.h" @@ -24,11 +25,20 @@ #define __ reinterpret_cast<X86Assembler*>(GetAssembler())-> namespace art { + +x86::X86ManagedRegister Location::AsX86() const { + return reg().AsX86(); +} + namespace x86 { static constexpr int kNumberOfPushedRegistersAtEntry = 1; static constexpr int kCurrentMethodStackOffset = 0; +static Location X86CpuLocation(Register reg) { + return Location::RegisterLocation(X86ManagedRegister::FromCpuRegister(reg)); +} + InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), @@ -39,15 +49,20 @@ void CodeGeneratorX86::GenerateFrameEntry() { static const int kFakeReturnRegister = 8; core_spill_mask_ |= (1 << kFakeReturnRegister); - // Add the current ART method to the frame size and the return PC. - SetFrameSize(RoundUp(GetFrameSize() + 2 * kWordSize, kStackAlignment)); + SetFrameSize(RoundUp( + (GetGraph()->GetMaximumNumberOfOutVRegs() + GetGraph()->GetNumberOfVRegs()) * kVRegSize + + kVRegSize // filler + + kX86WordSize // Art method + + kNumberOfPushedRegistersAtEntry * kX86WordSize, + kStackAlignment)); + // The return PC has already been pushed on the stack. - __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kWordSize)); - __ movl(Address(ESP, 0), EAX); + __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize)); + __ movl(Address(ESP, kCurrentMethodStackOffset), EAX); } void CodeGeneratorX86::GenerateFrameExit() { - __ addl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kWordSize)); + __ addl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize)); } void CodeGeneratorX86::Bind(Label* label) { @@ -59,21 +74,214 @@ void InstructionCodeGeneratorX86::LoadCurrentMethod(Register reg) { } int32_t CodeGeneratorX86::GetStackSlot(HLocal* local) const { - return (GetGraph()->GetMaximumNumberOfOutVRegs() + local->GetRegNumber()) * kWordSize; + uint16_t reg_number = local->GetRegNumber(); + uint16_t number_of_vregs = GetGraph()->GetNumberOfVRegs(); + uint16_t number_of_in_vregs = GetGraph()->GetNumberOfInVRegs(); + if (reg_number >= number_of_vregs - number_of_in_vregs) { + // Local is a parameter of the method. It is stored in the caller's frame. + return GetFrameSize() + kX86WordSize // ART method + + (reg_number - number_of_vregs + number_of_in_vregs) * kVRegSize; + } else { + // Local is a temporary in this method. It is stored in this method's frame. + return GetFrameSize() - (kNumberOfPushedRegistersAtEntry * kX86WordSize) + - kVRegSize // filler. + - (number_of_vregs * kVRegSize) + + (reg_number * kVRegSize); + } +} + +static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + +Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { + switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + uint32_t index = gp_index_++; + if (index < calling_convention.GetNumberOfRegisters()) { + return X86CpuLocation(calling_convention.GetRegisterAt(index)); + } else { + return Location::StackSlot(calling_convention.GetStackOffsetOf(index, kX86WordSize)); + } + } + + case Primitive::kPrimLong: { + uint32_t index = gp_index_; + gp_index_ += 2; + if (index + 1 < calling_convention.GetNumberOfRegisters()) { + return Location::RegisterLocation(X86ManagedRegister::FromRegisterPair( + calling_convention.GetRegisterPairAt(index))); + } else if (index + 1 == calling_convention.GetNumberOfRegisters()) { + return Location::QuickParameter(index); + } else { + return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index, kX86WordSize)); + } + } + + case Primitive::kPrimDouble: + case Primitive::kPrimFloat: + LOG(FATAL) << "Unimplemented parameter type " << type; + break; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unexpected parameter type " << type; + break; + } + return Location(); +} + +void CodeGeneratorX86::Move32(Location destination, Location source) { + if (source.Equals(destination)) { + return; + } + if (destination.IsRegister()) { + if (source.IsRegister()) { + __ movl(destination.AsX86().AsCpuRegister(), source.AsX86().AsCpuRegister()); + } else { + DCHECK(source.IsStackSlot()); + __ movl(destination.AsX86().AsCpuRegister(), Address(ESP, source.GetStackIndex())); + } + } else { + if (source.IsRegister()) { + __ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsCpuRegister()); + } else { + DCHECK(source.IsStackSlot()); + __ movl(EAX, Address(ESP, source.GetStackIndex())); + __ movl(Address(ESP, destination.GetStackIndex()), EAX); + } + } +} + +void CodeGeneratorX86::Move64(Location destination, Location source) { + if (source.Equals(destination)) { + return; + } + if (destination.IsRegister()) { + if (source.IsRegister()) { + __ movl(destination.AsX86().AsRegisterPairLow(), source.AsX86().AsRegisterPairLow()); + __ movl(destination.AsX86().AsRegisterPairHigh(), source.AsX86().AsRegisterPairHigh()); + } else if (source.IsQuickParameter()) { + uint32_t argument_index = source.GetQuickParameterIndex(); + InvokeDexCallingConvention calling_convention; + __ movl(destination.AsX86().AsRegisterPairLow(), + calling_convention.GetRegisterAt(argument_index)); + __ movl(destination.AsX86().AsRegisterPairHigh(), Address(ESP, + calling_convention.GetStackOffsetOf(argument_index + 1, kX86WordSize) + GetFrameSize())); + } else { + DCHECK(source.IsDoubleStackSlot()); + __ movl(destination.AsX86().AsRegisterPairLow(), Address(ESP, source.GetStackIndex())); + __ movl(destination.AsX86().AsRegisterPairHigh(), + Address(ESP, source.GetHighStackIndex(kX86WordSize))); + } + } else if (destination.IsQuickParameter()) { + InvokeDexCallingConvention calling_convention; + uint32_t argument_index = destination.GetQuickParameterIndex(); + if (source.IsRegister()) { + __ movl(calling_convention.GetRegisterAt(argument_index), source.AsX86().AsRegisterPairLow()); + __ movl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1, kX86WordSize)), + source.AsX86().AsRegisterPairHigh()); + } else { + DCHECK(source.IsDoubleStackSlot()); + __ movl(calling_convention.GetRegisterAt(argument_index), + Address(ESP, source.GetStackIndex())); + __ movl(EAX, Address(ESP, source.GetHighStackIndex(kX86WordSize))); + __ movl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1, kX86WordSize)), EAX); + } + } else { + if (source.IsRegister()) { + __ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsRegisterPairLow()); + __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)), + source.AsX86().AsRegisterPairHigh()); + } else if (source.IsQuickParameter()) { + InvokeDexCallingConvention calling_convention; + uint32_t argument_index = source.GetQuickParameterIndex(); + __ movl(Address(ESP, destination.GetStackIndex()), + calling_convention.GetRegisterAt(argument_index)); + __ movl(EAX, Address(ESP, + calling_convention.GetStackOffsetOf(argument_index + 1, kX86WordSize) + GetFrameSize())); + __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)), EAX); + } else { + DCHECK(source.IsDoubleStackSlot()); + __ movl(EAX, Address(ESP, source.GetStackIndex())); + __ movl(Address(ESP, destination.GetStackIndex()), EAX); + __ movl(EAX, Address(ESP, source.GetHighStackIndex(kX86WordSize))); + __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)), EAX); + } + } } void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstruction* move_for) { if (instruction->AsIntConstant() != nullptr) { - __ movl(location.reg<Register>(), Immediate(instruction->AsIntConstant()->GetValue())); + Immediate imm(instruction->AsIntConstant()->GetValue()); + if (location.IsRegister()) { + __ movl(location.AsX86().AsCpuRegister(), imm); + } else { + __ movl(Address(ESP, location.GetStackIndex()), imm); + } + } else if (instruction->AsLongConstant() != nullptr) { + int64_t value = instruction->AsLongConstant()->GetValue(); + if (location.IsRegister()) { + __ movl(location.AsX86().AsRegisterPairLow(), Immediate(Low32Bits(value))); + __ movl(location.AsX86().AsRegisterPairHigh(), Immediate(High32Bits(value))); + } else { + __ movl(Address(ESP, location.GetStackIndex()), Immediate(Low32Bits(value))); + __ movl(Address(ESP, location.GetHighStackIndex(kX86WordSize)), Immediate(High32Bits(value))); + } } else if (instruction->AsLoadLocal() != nullptr) { - __ movl(location.reg<Register>(), - Address(ESP, GetStackSlot(instruction->AsLoadLocal()->GetLocal()))); + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + Move32(location, Location::StackSlot(GetStackSlot(instruction->AsLoadLocal()->GetLocal()))); + break; + + case Primitive::kPrimLong: + Move64(location, Location::DoubleStackSlot( + GetStackSlot(instruction->AsLoadLocal()->GetLocal()))); + break; + + default: + LOG(FATAL) << "Unimplemented local type " << instruction->GetType(); + } } else { // This can currently only happen when the instruction that requests the move // is the next to be compiled. DCHECK_EQ(instruction->GetNext(), move_for); - __ movl(location.reg<Register>(), - instruction->GetLocations()->Out().reg<Register>()); + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + Move32(location, instruction->GetLocations()->Out()); + break; + + case Primitive::kPrimLong: + Move64(location, instruction->GetLocations()->Out()); + break; + + default: + LOG(FATAL) << "Unimplemented type " << instruction->GetType(); + } } } @@ -103,13 +311,13 @@ void InstructionCodeGeneratorX86::VisitExit(HExit* exit) { void LocationsBuilderX86::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, Location(EAX)); + locations->SetInAt(0, X86CpuLocation(EAX)); if_instr->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) { // TODO: Generate the input as a condition, instead of materializing in a register. - __ cmpl(if_instr->GetLocations()->InAt(0).reg<Register>(), Immediate(0)); + __ cmpl(if_instr->GetLocations()->InAt(0).AsX86().AsCpuRegister(), Immediate(0)); __ j(kEqual, codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfTrueSuccessor())) { __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); @@ -122,7 +330,6 @@ void LocationsBuilderX86::VisitLocal(HLocal* local) { void InstructionCodeGeneratorX86::VisitLocal(HLocal* local) { DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); - codegen_->SetFrameSize(codegen_->GetFrameSize() + kWordSize); } void LocationsBuilderX86::VisitLoadLocal(HLoadLocal* local) { @@ -133,29 +340,43 @@ void InstructionCodeGeneratorX86::VisitLoadLocal(HLoadLocal* load) { // Nothing to do, this is driven by the code generator. } -void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* local) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(local); - locations->SetInAt(1, Location(EAX)); - local->SetLocations(locations); +void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store); + switch (store->InputAt(1)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + case Primitive::kPrimLong: + locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + default: + LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType(); + } + store->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) { - __ movl(Address(ESP, codegen_->GetStackSlot(store->GetLocal())), - store->GetLocations()->InAt(1).reg<Register>()); } void LocationsBuilderX86::VisitEqual(HEqual* equal) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(equal); - locations->SetInAt(0, Location(EAX)); - locations->SetInAt(1, Location(ECX)); - locations->SetOut(Location(EAX)); + locations->SetInAt(0, X86CpuLocation(EAX)); + locations->SetInAt(1, X86CpuLocation(ECX)); + locations->SetOut(X86CpuLocation(EAX)); equal->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitEqual(HEqual* equal) { - __ cmpl(equal->GetLocations()->InAt(0).reg<Register>(), - equal->GetLocations()->InAt(1).reg<Register>()); - __ setb(kEqual, equal->GetLocations()->Out().reg<Register>()); + __ cmpl(equal->GetLocations()->InAt(0).AsX86().AsCpuRegister(), + equal->GetLocations()->InAt(1).AsX86().AsCpuRegister()); + __ setb(kEqual, equal->GetLocations()->Out().AsX86().AsCpuRegister()); } void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) { @@ -166,6 +387,14 @@ void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) { // Will be generated at use site. } +void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) { + constant->SetLocations(nullptr); +} + +void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant) { + // Will be generated at use site. +} + void LocationsBuilderX86::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } @@ -177,66 +406,91 @@ void InstructionCodeGeneratorX86::VisitReturnVoid(HReturnVoid* ret) { void LocationsBuilderX86::VisitReturn(HReturn* ret) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret); - locations->SetInAt(0, Location(EAX)); + switch (ret->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + locations->SetInAt(0, X86CpuLocation(EAX)); + break; + + case Primitive::kPrimLong: + locations->SetInAt( + 0, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + break; + + default: + LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType(); + } ret->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { - DCHECK_EQ(ret->GetLocations()->InAt(0).reg<Register>(), EAX); + if (kIsDebugBuild) { + switch (ret->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + DCHECK_EQ(ret->GetLocations()->InAt(0).AsX86().AsCpuRegister(), EAX); + break; + + case Primitive::kPrimLong: + DCHECK_EQ(ret->GetLocations()->InAt(0).AsX86().AsRegisterPair(), EAX_EDX); + break; + + default: + LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType(); + } + } codegen_->GenerateFrameExit(); __ ret(); } -static constexpr Register kParameterCoreRegisters[] = { ECX, EDX, EBX }; -static constexpr int kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); +void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); + locations->AddTemp(X86CpuLocation(EAX)); -class InvokeStaticCallingConvention : public CallingConvention<Register> { - public: - InvokeStaticCallingConvention() - : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {} + InvokeDexCallingConventionVisitor calling_convention_visitor; + for (int i = 0; i < invoke->InputCount(); i++) { + HInstruction* input = invoke->InputAt(i); + locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); + } - private: - DISALLOW_COPY_AND_ASSIGN(InvokeStaticCallingConvention); -}; + switch (invoke->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + locations->SetOut(X86CpuLocation(EAX)); + break; -void LocationsBuilderX86::VisitPushArgument(HPushArgument* argument) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(argument); - InvokeStaticCallingConvention calling_convention; - if (argument->GetArgumentIndex() < calling_convention.GetNumberOfRegisters()) { - Location location = Location(calling_convention.GetRegisterAt(argument->GetArgumentIndex())); - locations->SetInAt(0, location); - locations->SetOut(location); - } else { - locations->SetInAt(0, Location(EAX)); - } - argument->SetLocations(locations); -} + case Primitive::kPrimLong: + locations->SetOut(Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + break; -void InstructionCodeGeneratorX86::VisitPushArgument(HPushArgument* argument) { - uint8_t argument_index = argument->GetArgumentIndex(); - InvokeStaticCallingConvention calling_convention; - size_t parameter_registers = calling_convention.GetNumberOfRegisters(); - if (argument_index >= parameter_registers) { - uint8_t offset = calling_convention.GetStackOffsetOf(argument_index); - __ movl(Address(ESP, offset), - argument->GetLocations()->InAt(0).reg<Register>()); + case Primitive::kPrimVoid: + break; - } else { - DCHECK_EQ(argument->GetLocations()->Out().reg<Register>(), - argument->GetLocations()->InAt(0).reg<Register>()); + case Primitive::kPrimDouble: + case Primitive::kPrimFloat: + LOG(FATAL) << "Unimplemented return type " << invoke->GetType(); + break; } -} -void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke); - locations->AddTemp(Location(EAX)); invoke->SetLocations(locations); } void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { - Register temp = invoke->GetLocations()->GetTemp(0).reg<Register>(); + Register temp = invoke->GetLocations()->GetTemp(0).AsX86().AsCpuRegister(); size_t index_in_cache = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - invoke->GetIndexInDexCache() * kWordSize; + invoke->GetIndexInDexCache() * kX86WordSize; // TODO: Implement all kinds of calls: // 1) boot -> boot @@ -261,13 +515,29 @@ void LocationsBuilderX86::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add); switch (add->GetResultType()) { case Primitive::kPrimInt: { - locations->SetInAt(0, Location(EAX)); - locations->SetInAt(1, Location(ECX)); - locations->SetOut(Location(EAX)); + locations->SetInAt(0, X86CpuLocation(EAX)); + locations->SetInAt(1, X86CpuLocation(ECX)); + locations->SetOut(X86CpuLocation(EAX)); + break; + } + case Primitive::kPrimLong: { + locations->SetInAt( + 0, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + locations->SetInAt( + 1, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(ECX_EBX))); + locations->SetOut(Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); break; } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected add type " << add->GetResultType(); + break; + default: - LOG(FATAL) << "Unimplemented"; + LOG(FATAL) << "Unimplemented add type " << add->GetResultType(); } add->SetLocations(locations); } @@ -275,13 +545,146 @@ void LocationsBuilderX86::VisitAdd(HAdd* add) { void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { LocationSummary* locations = add->GetLocations(); switch (add->GetResultType()) { - case Primitive::kPrimInt: - DCHECK_EQ(locations->InAt(0).reg<Register>(), locations->Out().reg<Register>()); - __ addl(locations->InAt(0).reg<Register>(), locations->InAt(1).reg<Register>()); + case Primitive::kPrimInt: { + DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), + locations->Out().AsX86().AsCpuRegister()); + __ addl(locations->InAt(0).AsX86().AsCpuRegister(), + locations->InAt(1).AsX86().AsCpuRegister()); + break; + } + + case Primitive::kPrimLong: { + DCHECK_EQ(locations->InAt(0).AsX86().AsRegisterPair(), + locations->Out().AsX86().AsRegisterPair()); + __ addl(locations->InAt(0).AsX86().AsRegisterPairLow(), + locations->InAt(1).AsX86().AsRegisterPairLow()); + __ adcl(locations->InAt(0).AsX86().AsRegisterPairHigh(), + locations->InAt(1).AsX86().AsRegisterPairHigh()); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected add type " << add->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented add type " << add->GetResultType(); + } +} + +void LocationsBuilderX86::VisitSub(HSub* sub) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub); + switch (sub->GetResultType()) { + case Primitive::kPrimInt: { + locations->SetInAt(0, X86CpuLocation(EAX)); + locations->SetInAt(1, X86CpuLocation(ECX)); + locations->SetOut(X86CpuLocation(EAX)); break; + } + + case Primitive::kPrimLong: { + locations->SetInAt( + 0, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + locations->SetInAt( + 1, Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(ECX_EBX))); + locations->SetOut(Location::RegisterLocation(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + break; + default: - LOG(FATAL) << "Unimplemented"; + LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); } + sub->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { + LocationSummary* locations = sub->GetLocations(); + switch (sub->GetResultType()) { + case Primitive::kPrimInt: { + DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), + locations->Out().AsX86().AsCpuRegister()); + __ subl(locations->InAt(0).AsX86().AsCpuRegister(), + locations->InAt(1).AsX86().AsCpuRegister()); + break; + } + + case Primitive::kPrimLong: { + DCHECK_EQ(locations->InAt(0).AsX86().AsRegisterPair(), + locations->Out().AsX86().AsRegisterPair()); + __ subl(locations->InAt(0).AsX86().AsRegisterPairLow(), + locations->InAt(1).AsX86().AsRegisterPairLow()); + __ sbbl(locations->InAt(0).AsX86().AsRegisterPairHigh(), + locations->InAt(1).AsX86().AsRegisterPairHigh()); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + } +} + +void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetOut(X86CpuLocation(EAX)); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ movl(calling_convention.GetRegisterAt(0), + Immediate(instruction->GetTypeIndex())); + + __ fs()->call( + Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck))); + + codegen_->RecordPcInfo(instruction->GetDexPc()); +} + +void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); + if (location.IsStackSlot()) { + location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } else if (location.IsDoubleStackSlot()) { + location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } + locations->SetOut(location); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) { + // Nothing to do, the parameter is already at its location. +} + +void LocationsBuilderX86::VisitNot(HNot* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, X86CpuLocation(EAX)); + locations->SetOut(X86CpuLocation(EAX)); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) { + LocationSummary* locations = instruction->GetLocations(); + DCHECK_EQ(locations->InAt(0).AsX86().AsCpuRegister(), locations->Out().AsX86().AsCpuRegister()); + __ xorl(locations->Out().AsX86().AsCpuRegister(), Immediate(1)); } } // namespace x86 diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index dd5044f4dc..f22890e708 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -22,12 +22,47 @@ #include "utils/x86/assembler_x86.h" namespace art { - namespace x86 { +static constexpr size_t kX86WordSize = 4; + +class CodeGeneratorX86; + +static constexpr Register kParameterCoreRegisters[] = { ECX, EDX, EBX }; +static constexpr RegisterPair kParameterCorePairRegisters[] = { ECX_EDX, EDX_EBX }; +static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); + +class InvokeDexCallingConvention : public CallingConvention<Register> { + public: + InvokeDexCallingConvention() + : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {} + + RegisterPair GetRegisterPairAt(size_t argument_index) { + DCHECK_LT(argument_index + 1, GetNumberOfRegisters()); + return kParameterCorePairRegisters[argument_index]; + } + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention); +}; + +class InvokeDexCallingConventionVisitor { + public: + InvokeDexCallingConventionVisitor() : gp_index_(0) {} + + Location GetNextLocation(Primitive::Type type); + + private: + InvokeDexCallingConvention calling_convention; + uint32_t gp_index_; + + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor); +}; + class LocationsBuilderX86 : public HGraphVisitor { public: - explicit LocationsBuilderX86(HGraph* graph) : HGraphVisitor(graph) { } + LocationsBuilderX86(HGraph* graph, CodeGeneratorX86* codegen) + : HGraphVisitor(graph), codegen_(codegen) {} #define DECLARE_VISIT_INSTRUCTION(name) \ virtual void Visit##name(H##name* instr); @@ -37,11 +72,12 @@ class LocationsBuilderX86 : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION private: + CodeGeneratorX86* const codegen_; + InvokeDexCallingConventionVisitor parameter_visitor_; + DISALLOW_COPY_AND_ASSIGN(LocationsBuilderX86); }; -class CodeGeneratorX86; - class InstructionCodeGeneratorX86 : public HGraphVisitor { public: InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen); @@ -68,7 +104,7 @@ class CodeGeneratorX86 : public CodeGenerator { public: explicit CodeGeneratorX86(HGraph* graph) : CodeGenerator(graph), - location_builder_(graph), + location_builder_(graph, this), instruction_visitor_(graph, this) { } virtual ~CodeGeneratorX86() { } @@ -77,6 +113,10 @@ class CodeGeneratorX86 : public CodeGenerator { virtual void Bind(Label* label) OVERRIDE; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; + virtual size_t GetWordSize() const OVERRIDE { + return kX86WordSize; + } + virtual HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } @@ -92,6 +132,11 @@ class CodeGeneratorX86 : public CodeGenerator { int32_t GetStackSlot(HLocal* local) const; private: + // Helper method to move a 32bits value between two locations. + void Move32(Location destination, Location source); + // Helper method to move a 64bits value between two locations. + void Move64(Location destination, Location source); + LocationsBuilderX86 location_builder_; InstructionCodeGeneratorX86 instruction_visitor_; X86Assembler assembler_; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 2b21905224..3da9ed9461 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -42,6 +42,8 @@ class HGraph : public ArenaObject { blocks_(arena, kDefaultNumberOfBlocks), dominator_order_(arena, kDefaultNumberOfBlocks), maximum_number_of_out_vregs_(0), + number_of_vregs_(0), + number_of_in_vregs_(0), current_instruction_id_(0) { } ArenaAllocator* GetArena() const { return arena_; } @@ -68,6 +70,23 @@ class HGraph : public ArenaObject { maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_); } + void SetNumberOfVRegs(uint16_t number_of_vregs) { + number_of_vregs_ = number_of_vregs; + } + + uint16_t GetNumberOfVRegs() const { + return number_of_vregs_; + } + + void SetNumberOfInVRegs(uint16_t value) { + number_of_in_vregs_ = value; + } + + uint16_t GetNumberOfInVRegs() const { + return number_of_in_vregs_; + } + + private: HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const; void VisitBlockForDominatorTree(HBasicBlock* block, @@ -90,9 +109,15 @@ class HGraph : public ArenaObject { HBasicBlock* entry_block_; HBasicBlock* exit_block_; - // The maximum number of arguments passed to a HInvoke in this graph. + // The maximum number of virtual registers arguments passed to a HInvoke in this graph. uint16_t maximum_number_of_out_vregs_; + // The number of virtual registers in this method. Contains the parameters. + uint16_t number_of_vregs_; + + // The number of virtual registers used by parameters of this method. + uint16_t number_of_in_vregs_; + // The current id to assign to a newly added instruction. See HInstruction.id_. int current_instruction_id_; @@ -201,10 +226,14 @@ class HBasicBlock : public ArenaObject { M(InvokeStatic) \ M(LoadLocal) \ M(Local) \ - M(PushArgument) \ + M(LongConstant) \ + M(NewInstance) \ + M(Not) \ + M(ParameterValue) \ M(Return) \ M(ReturnVoid) \ M(StoreLocal) \ + M(Sub) \ #define FORWARD_DECLARATION(type) class H##type; FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) @@ -254,6 +283,8 @@ class HInstruction : public ArenaObject { virtual void Accept(HGraphVisitor* visitor) = 0; virtual const char* DebugName() const = 0; + virtual Primitive::Type GetType() const { return Primitive::kPrimVoid; } + void AddUse(HInstruction* user) { uses_ = new (block_->GetGraph()->GetArena()) HUseListNode(user, uses_); } @@ -505,6 +536,7 @@ class HBinaryOperation : public HTemplateInstruction<2> { Primitive::Type GetResultType() const { return result_type_; } virtual bool IsCommutative() { return false; } + virtual Primitive::Type GetType() const { return GetResultType(); } private: const Primitive::Type result_type_; @@ -521,6 +553,8 @@ class HEqual : public HBinaryOperation { virtual bool IsCommutative() { return true; } + virtual Primitive::Type GetType() const { return Primitive::kPrimBoolean; } + DECLARE_INSTRUCTION(Equal) private: @@ -546,15 +580,19 @@ class HLocal : public HTemplateInstruction<0> { // Load a given local. The local is an input of this instruction. class HLoadLocal : public HTemplateInstruction<1> { public: - explicit HLoadLocal(HLocal* local) { + explicit HLoadLocal(HLocal* local, Primitive::Type type) : type_(type) { SetRawInputAt(0, local); } + virtual Primitive::Type GetType() const { return type_; } + HLocal* GetLocal() const { return reinterpret_cast<HLocal*>(InputAt(0)); } DECLARE_INSTRUCTION(LoadLocal) private: + const Primitive::Type type_; + DISALLOW_COPY_AND_ASSIGN(HLoadLocal); }; @@ -582,6 +620,7 @@ class HIntConstant : public HTemplateInstruction<0> { explicit HIntConstant(int32_t value) : value_(value) { } int32_t GetValue() const { return value_; } + virtual Primitive::Type GetType() const { return Primitive::kPrimInt; } DECLARE_INSTRUCTION(IntConstant) @@ -591,10 +630,30 @@ class HIntConstant : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; +class HLongConstant : public HTemplateInstruction<0> { + public: + explicit HLongConstant(int64_t value) : value_(value) { } + + int64_t GetValue() const { return value_; } + + virtual Primitive::Type GetType() const { return Primitive::kPrimLong; } + + DECLARE_INSTRUCTION(LongConstant) + + private: + const int64_t value_; + + DISALLOW_COPY_AND_ASSIGN(HLongConstant); +}; + class HInvoke : public HInstruction { public: - HInvoke(ArenaAllocator* arena, uint32_t number_of_arguments, int32_t dex_pc) + HInvoke(ArenaAllocator* arena, + uint32_t number_of_arguments, + Primitive::Type return_type, + uint32_t dex_pc) : inputs_(arena, number_of_arguments), + return_type_(return_type), dex_pc_(dex_pc) { inputs_.SetSize(number_of_arguments); } @@ -606,11 +665,14 @@ class HInvoke : public HInstruction { inputs_.Put(index, argument); } - int32_t GetDexPc() const { return dex_pc_; } + virtual Primitive::Type GetType() const { return return_type_; } + + uint32_t GetDexPc() const { return dex_pc_; } protected: GrowableArray<HInstruction*> inputs_; - const int32_t dex_pc_; + const Primitive::Type return_type_; + const uint32_t dex_pc_; private: DISALLOW_COPY_AND_ASSIGN(HInvoke); @@ -620,9 +682,11 @@ class HInvokeStatic : public HInvoke { public: HInvokeStatic(ArenaAllocator* arena, uint32_t number_of_arguments, - int32_t dex_pc, - int32_t index_in_dex_cache) - : HInvoke(arena, number_of_arguments, dex_pc), index_in_dex_cache_(index_in_dex_cache) {} + Primitive::Type return_type, + uint32_t dex_pc, + uint32_t index_in_dex_cache) + : HInvoke(arena, number_of_arguments, return_type, dex_pc), + index_in_dex_cache_(index_in_dex_cache) {} uint32_t GetIndexInDexCache() const { return index_in_dex_cache_; } @@ -634,22 +698,22 @@ class HInvokeStatic : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeStatic); }; -// HPushArgument nodes are inserted after the evaluation of an argument -// of a call. Their mere purpose is to ease the code generator's work. -class HPushArgument : public HTemplateInstruction<1> { +class HNewInstance : public HTemplateInstruction<0> { public: - HPushArgument(HInstruction* argument, uint8_t argument_index) : argument_index_(argument_index) { - SetRawInputAt(0, argument); - } + HNewInstance(uint32_t dex_pc, uint16_t type_index) : dex_pc_(dex_pc), type_index_(type_index) {} + + uint32_t GetDexPc() const { return dex_pc_; } + uint16_t GetTypeIndex() const { return type_index_; } - uint8_t GetArgumentIndex() const { return argument_index_; } + virtual Primitive::Type GetType() const { return Primitive::kPrimNot; } - DECLARE_INSTRUCTION(PushArgument) + DECLARE_INSTRUCTION(NewInstance) private: - const uint8_t argument_index_; + const uint32_t dex_pc_; + const uint16_t type_index_; - DISALLOW_COPY_AND_ASSIGN(HPushArgument); + DISALLOW_COPY_AND_ASSIGN(HNewInstance); }; class HAdd : public HBinaryOperation { @@ -665,6 +729,56 @@ class HAdd : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HAdd); }; +class HSub : public HBinaryOperation { + public: + HSub(Primitive::Type result_type, HInstruction* left, HInstruction* right) + : HBinaryOperation(result_type, left, right) {} + + virtual bool IsCommutative() { return false; } + + DECLARE_INSTRUCTION(Sub); + + private: + DISALLOW_COPY_AND_ASSIGN(HSub); +}; + +// The value of a parameter in this method. Its location depends on +// the calling convention. +class HParameterValue : public HTemplateInstruction<0> { + public: + HParameterValue(uint8_t index, Primitive::Type parameter_type) + : index_(index), parameter_type_(parameter_type) {} + + uint8_t GetIndex() const { return index_; } + + virtual Primitive::Type GetType() const { return parameter_type_; } + + DECLARE_INSTRUCTION(ParameterValue); + + private: + // The index of this parameter in the parameters list. Must be less + // than HGraph::number_of_in_vregs_; + const uint8_t index_; + + const Primitive::Type parameter_type_; + + DISALLOW_COPY_AND_ASSIGN(HParameterValue); +}; + +class HNot : public HTemplateInstruction<1> { + public: + explicit HNot(HInstruction* input) { + SetRawInputAt(0, input); + } + + virtual Primitive::Type GetType() const { return Primitive::kPrimBoolean; } + + DECLARE_INSTRUCTION(Not); + + private: + DISALLOW_COPY_AND_ASSIGN(HNot); +}; + class HGraphVisitor : public ValueObject { public: explicit HGraphVisitor(HGraph* graph) : graph_(graph) { } diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index a11c2da19e..1d87eaaa60 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -50,11 +50,11 @@ void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) { } void Arm64Assembler::GetCurrentThread(ManagedRegister tr) { - ___ Mov(reg_x(tr.AsArm64().AsCoreRegister()), reg_x(TR)); + ___ Mov(reg_x(tr.AsArm64().AsCoreRegister()), reg_x(TR1)); } void Arm64Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister /* scratch */) { - StoreToOffset(TR, SP, offset.Int32Value()); + StoreToOffset(TR1, SP, offset.Int32Value()); } // See Arm64 PCS Section 5.2.2.1. @@ -138,7 +138,8 @@ void Arm64Assembler::Store(FrameOffset offs, ManagedRegister m_src, size_t size) void Arm64Assembler::StoreRef(FrameOffset offs, ManagedRegister m_src) { Arm64ManagedRegister src = m_src.AsArm64(); CHECK(src.IsCoreRegister()) << src; - StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value()); + StoreWToOffset(kStoreWord, src.AsOverlappingCoreRegisterLow(), SP, + offs.Int32Value()); } void Arm64Assembler::StoreRawPtr(FrameOffset offs, ManagedRegister m_src) { @@ -152,30 +153,31 @@ void Arm64Assembler::StoreImmediateToFrame(FrameOffset offs, uint32_t imm, Arm64ManagedRegister scratch = m_scratch.AsArm64(); CHECK(scratch.IsCoreRegister()) << scratch; LoadImmediate(scratch.AsCoreRegister(), imm); - StoreToOffset(scratch.AsCoreRegister(), SP, offs.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), SP, + offs.Int32Value()); } -void Arm64Assembler::StoreImmediateToThread32(ThreadOffset<4> offs, uint32_t imm, +void Arm64Assembler::StoreImmediateToThread64(ThreadOffset<8> offs, uint32_t imm, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); CHECK(scratch.IsCoreRegister()) << scratch; LoadImmediate(scratch.AsCoreRegister(), imm); - StoreToOffset(scratch.AsCoreRegister(), TR, offs.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), TR1, offs.Int32Value()); } -void Arm64Assembler::StoreStackOffsetToThread32(ThreadOffset<4> tr_offs, +void Arm64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> tr_offs, FrameOffset fr_offs, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); CHECK(scratch.IsCoreRegister()) << scratch; AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), TR, tr_offs.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), TR1, tr_offs.Int32Value()); } -void Arm64Assembler::StoreStackPointerToThread32(ThreadOffset<4> tr_offs) { +void Arm64Assembler::StoreStackPointerToThread64(ThreadOffset<8> tr_offs) { // Arm64 does not support: "str sp, [dest]" therefore we use IP1 as a temp reg. ___ Mov(reg_x(IP1), reg_x(SP)); - StoreToOffset(IP1, TR, tr_offs.Int32Value()); + StoreToOffset(IP1, TR1, tr_offs.Int32Value()); } void Arm64Assembler::StoreSpanning(FrameOffset dest_off, ManagedRegister m_source, @@ -254,9 +256,13 @@ void Arm64Assembler::Load(Arm64ManagedRegister dest, Register base, CHECK_EQ(4u, size) << dest; ___ Ldr(reg_w(dest.AsWRegister()), MEM_OP(reg_x(base), offset)); } else if (dest.IsCoreRegister()) { - CHECK_EQ(8u, size) << dest; CHECK_NE(dest.AsCoreRegister(), SP) << dest; - ___ Ldr(reg_x(dest.AsCoreRegister()), MEM_OP(reg_x(base), offset)); + if (size == 4u) { + ___ Ldr(reg_w(dest.AsOverlappingCoreRegisterLow()), MEM_OP(reg_x(base), offset)); + } else { + CHECK_EQ(8u, size) << dest; + ___ Ldr(reg_x(dest.AsCoreRegister()), MEM_OP(reg_x(base), offset)); + } } else if (dest.IsSRegister()) { ___ Ldr(reg_s(dest.AsSRegister()), MEM_OP(reg_x(base), offset)); } else { @@ -269,14 +275,14 @@ void Arm64Assembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) { return Load(m_dst.AsArm64(), SP, src.Int32Value(), size); } -void Arm64Assembler::LoadFromThread32(ManagedRegister m_dst, ThreadOffset<4> src, size_t size) { - return Load(m_dst.AsArm64(), TR, src.Int32Value(), size); +void Arm64Assembler::LoadFromThread64(ManagedRegister m_dst, ThreadOffset<8> src, size_t size) { + return Load(m_dst.AsArm64(), TR1, src.Int32Value(), size); } void Arm64Assembler::LoadRef(ManagedRegister m_dst, FrameOffset offs) { Arm64ManagedRegister dst = m_dst.AsArm64(); CHECK(dst.IsCoreRegister()) << dst; - LoadFromOffset(dst.AsCoreRegister(), SP, offs.Int32Value()); + LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), SP, offs.Int32Value()); } void Arm64Assembler::LoadRef(ManagedRegister m_dst, ManagedRegister m_base, @@ -284,7 +290,8 @@ void Arm64Assembler::LoadRef(ManagedRegister m_dst, ManagedRegister m_base, Arm64ManagedRegister dst = m_dst.AsArm64(); Arm64ManagedRegister base = m_base.AsArm64(); CHECK(dst.IsCoreRegister() && base.IsCoreRegister()); - LoadFromOffset(dst.AsCoreRegister(), base.AsCoreRegister(), offs.Int32Value()); + LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), base.AsCoreRegister(), + offs.Int32Value()); } void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) { @@ -294,10 +301,10 @@ void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, O LoadFromOffset(dst.AsCoreRegister(), base.AsCoreRegister(), offs.Int32Value()); } -void Arm64Assembler::LoadRawPtrFromThread32(ManagedRegister m_dst, ThreadOffset<4> offs) { +void Arm64Assembler::LoadRawPtrFromThread64(ManagedRegister m_dst, ThreadOffset<8> offs) { Arm64ManagedRegister dst = m_dst.AsArm64(); CHECK(dst.IsCoreRegister()) << dst; - LoadFromOffset(dst.AsCoreRegister(), TR, offs.Int32Value()); + LoadFromOffset(dst.AsCoreRegister(), TR1, offs.Int32Value()); } // Copying routines. @@ -306,8 +313,16 @@ void Arm64Assembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t s Arm64ManagedRegister src = m_src.AsArm64(); if (!dst.Equals(src)) { if (dst.IsCoreRegister()) { - CHECK(src.IsCoreRegister()) << src; - ___ Mov(reg_x(dst.AsCoreRegister()), reg_x(src.AsCoreRegister())); + if (size == 4) { + CHECK(src.IsWRegister()); + ___ Mov(reg_x(dst.AsCoreRegister()), reg_w(src.AsWRegister())); + } else { + if (src.IsCoreRegister()) { + ___ Mov(reg_x(dst.AsCoreRegister()), reg_x(src.AsCoreRegister())); + } else { + ___ Mov(reg_x(dst.AsCoreRegister()), reg_w(src.AsWRegister())); + } + } } else if (dst.IsWRegister()) { CHECK(src.IsWRegister()) << src; ___ Mov(reg_w(dst.AsWRegister()), reg_w(src.AsWRegister())); @@ -322,40 +337,42 @@ void Arm64Assembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t s } } -void Arm64Assembler::CopyRawPtrFromThread32(FrameOffset fr_offs, - ThreadOffset<4> tr_offs, +void Arm64Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs, + ThreadOffset<8> tr_offs, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); CHECK(scratch.IsCoreRegister()) << scratch; - LoadFromOffset(scratch.AsCoreRegister(), TR, tr_offs.Int32Value()); + LoadFromOffset(scratch.AsCoreRegister(), TR1, tr_offs.Int32Value()); StoreToOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); } -void Arm64Assembler::CopyRawPtrToThread32(ThreadOffset<4> tr_offs, +void Arm64Assembler::CopyRawPtrToThread64(ThreadOffset<8> tr_offs, FrameOffset fr_offs, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); CHECK(scratch.IsCoreRegister()) << scratch; LoadFromOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), TR, tr_offs.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), TR1, tr_offs.Int32Value()); } void Arm64Assembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); CHECK(scratch.IsCoreRegister()) << scratch; - LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); + LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), + SP, src.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), + SP, dest.Int32Value()); } void Arm64Assembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister m_scratch, size_t size) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; + CHECK(scratch.IsCoreRegister()) << scratch; CHECK(size == 4 || size == 8) << size; if (size == 4) { - LoadWFromOffset(kLoadWord, scratch.AsWRegister(), SP, src.Int32Value()); - StoreWToOffset(kStoreWord, scratch.AsWRegister(), SP, dest.Int32Value()); + LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, src.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), SP, dest.Int32Value()); } else if (size == 8) { LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); @@ -418,10 +435,17 @@ void Arm64Assembler::Copy(ManagedRegister m_dest, Offset dest_offset, CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; CHECK(size == 4 || size == 8) << size; if (size == 4) { - LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsCoreRegister(), + if (scratch.IsWRegister()) { + LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsCoreRegister(), src_offset.Int32Value()); - StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsCoreRegister(), + StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsCoreRegister(), dest_offset.Int32Value()); + } else { + LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), src.AsCoreRegister(), + src_offset.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), dest.AsCoreRegister(), + dest_offset.Int32Value()); + } } else if (size == 8) { LoadFromOffset(scratch.AsCoreRegister(), src.AsCoreRegister(), src_offset.Int32Value()); StoreToOffset(scratch.AsCoreRegister(), dest.AsCoreRegister(), dest_offset.Int32Value()); @@ -486,7 +510,7 @@ void Arm64Assembler::Call(FrameOffset base, Offset offs, ManagedRegister m_scrat ___ Blr(reg_x(scratch.AsCoreRegister())); } -void Arm64Assembler::CallFromThread32(ThreadOffset<4> /*offset*/, ManagedRegister /*scratch*/) { +void Arm64Assembler::CallFromThread64(ThreadOffset<8> /*offset*/, ManagedRegister /*scratch*/) { UNIMPLEMENTED(FATAL) << "Unimplemented Call() variant"; } @@ -502,10 +526,11 @@ void Arm64Assembler::CreateSirtEntry(ManagedRegister m_out_reg, FrameOffset sirt // the address in the SIRT holding the reference. // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset) if (in_reg.IsNoRegister()) { - LoadFromOffset(out_reg.AsCoreRegister(), SP, sirt_offs.Int32Value()); + LoadWFromOffset(kLoadWord, out_reg.AsOverlappingCoreRegisterLow(), SP, + sirt_offs.Int32Value()); in_reg = out_reg; } - ___ Cmp(reg_x(in_reg.AsCoreRegister()), 0); + ___ Cmp(reg_w(in_reg.AsOverlappingCoreRegisterLow()), 0); if (!out_reg.Equals(in_reg)) { LoadImmediate(out_reg.AsCoreRegister(), 0, EQ); } @@ -520,11 +545,12 @@ void Arm64Assembler::CreateSirtEntry(FrameOffset out_off, FrameOffset sirt_offse Arm64ManagedRegister scratch = m_scratch.AsArm64(); CHECK(scratch.IsCoreRegister()) << scratch; if (null_allowed) { - LoadFromOffset(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value()); + LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, + sirt_offset.Int32Value()); // Null values get a SIRT entry value of 0. Otherwise, the sirt entry is // the address in the SIRT holding the reference. // e.g. scratch = (scratch == 0) ? 0 : (SP+sirt_offset) - ___ Cmp(reg_x(scratch.AsCoreRegister()), 0); + ___ Cmp(reg_w(scratch.AsOverlappingCoreRegisterLow()), 0); // Move this logic in add constants with flags. AddConstant(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value(), NE); } else { @@ -555,7 +581,7 @@ void Arm64Assembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjus Arm64ManagedRegister scratch = m_scratch.AsArm64(); Arm64Exception *current_exception = new Arm64Exception(scratch, stack_adjust); exception_blocks_.push_back(current_exception); - LoadFromOffset(scratch.AsCoreRegister(), TR, Thread::ExceptionOffset<4>().Int32Value()); + LoadFromOffset(scratch.AsCoreRegister(), TR1, Thread::ExceptionOffset<8>().Int32Value()); ___ Cmp(reg_x(scratch.AsCoreRegister()), 0); ___ B(current_exception->Entry(), COND_OP(NE)); } @@ -569,7 +595,11 @@ void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) { // Pass exception object as argument. // Don't care about preserving X0 as this won't return. ___ Mov(reg_x(X0), reg_x(exception->scratch_.AsCoreRegister())); - LoadFromOffset(IP1, TR, QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value()); + LoadFromOffset(IP1, TR1, QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value()); + + // FIXME: Temporary fix for TR (XSELF). + ___ Mov(reg_x(TR), reg_x(TR1)); + ___ Blr(reg_x(IP1)); // Call should never return. ___ Brk(); @@ -590,6 +620,9 @@ void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, CHECK_EQ(callee_save_regs.size(), kCalleeSavedRegsSize); ___ PushCalleeSavedRegisters(); + // FIXME: Temporary fix for TR (XSELF). + ___ Mov(reg_x(TR1), reg_x(TR)); + // Increate frame to required size - must be at least space to push Method*. CHECK_GT(frame_size, kCalleeSavedRegsSize * kFramePointerSize); size_t adjust = frame_size - (kCalleeSavedRegsSize * kFramePointerSize); @@ -598,11 +631,27 @@ void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, // Write Method*. StoreToOffset(X0, SP, 0); - // Write out entry spills, treated as X regs. - // TODO: we can implement a %2 STRP variant of StoreToOffset. + // Write out entry spills + int32_t offset = frame_size + kFramePointerSize; for (size_t i = 0; i < entry_spills.size(); ++i) { - Register reg = entry_spills.at(i).AsArm64().AsCoreRegister(); - StoreToOffset(reg, SP, frame_size + kFramePointerSize + (i * kFramePointerSize)); + Arm64ManagedRegister reg = entry_spills.at(i).AsArm64(); + if (reg.IsNoRegister()) { + // only increment stack offset. + ManagedRegisterSpill spill = entry_spills.at(i); + offset += spill.getSize(); + } else if (reg.IsCoreRegister()) { + StoreToOffset(reg.AsCoreRegister(), SP, offset); + offset += 8; + } else if (reg.IsWRegister()) { + StoreWToOffset(kStoreWord, reg.AsWRegister(), SP, offset); + offset += 4; + } else if (reg.IsDRegister()) { + StoreDToOffset(reg.AsDRegister(), SP, offset); + offset += 8; + } else if (reg.IsSRegister()) { + StoreSToOffset(reg.AsSRegister(), SP, offset); + offset += 4; + } } } @@ -618,6 +667,9 @@ void Arm64Assembler::RemoveFrame(size_t frame_size, const std::vector<ManagedReg size_t adjust = frame_size - (kCalleeSavedRegsSize * kFramePointerSize); DecreaseFrameSize(adjust); + // FIXME: Temporary fix for TR (XSELF). + ___ Mov(reg_x(TR), reg_x(TR1)); + // Pop callee saved and return to LR. ___ PopCalleeSavedRegisters(); ___ Ret(); diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h index 022072461c..97fb93af82 100644 --- a/compiler/utils/arm64/assembler_arm64.h +++ b/compiler/utils/arm64/assembler_arm64.h @@ -81,8 +81,8 @@ class Arm64Exception; class Arm64Assembler FINAL : public Assembler { public: - Arm64Assembler() : vixl_buf_(new byte[BUF_SIZE]), - vixl_masm_(new vixl::MacroAssembler(vixl_buf_, BUF_SIZE)) {} + Arm64Assembler() : vixl_buf_(new byte[kBufferSizeArm64]), + vixl_masm_(new vixl::MacroAssembler(vixl_buf_, kBufferSizeArm64)) {} virtual ~Arm64Assembler() { delete[] vixl_buf_; @@ -114,27 +114,27 @@ class Arm64Assembler FINAL : public Assembler { void StoreRef(FrameOffset dest, ManagedRegister src) OVERRIDE; void StoreRawPtr(FrameOffset dest, ManagedRegister src) OVERRIDE; void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, ManagedRegister scratch) OVERRIDE; - void StoreImmediateToThread32(ThreadOffset<4> dest, uint32_t imm, ManagedRegister scratch) + void StoreImmediateToThread64(ThreadOffset<8> dest, uint32_t imm, ManagedRegister scratch) OVERRIDE; - void StoreStackOffsetToThread32(ThreadOffset<4> thr_offs, FrameOffset fr_offs, + void StoreStackOffsetToThread64(ThreadOffset<8> thr_offs, FrameOffset fr_offs, ManagedRegister scratch) OVERRIDE; - void StoreStackPointerToThread32(ThreadOffset<4> thr_offs) OVERRIDE; + void StoreStackPointerToThread64(ThreadOffset<8> thr_offs) OVERRIDE; void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off, ManagedRegister scratch) OVERRIDE; // Load routines. void Load(ManagedRegister dest, FrameOffset src, size_t size) OVERRIDE; - void LoadFromThread32(ManagedRegister dest, ThreadOffset<4> src, size_t size) OVERRIDE; + void LoadFromThread64(ManagedRegister dest, ThreadOffset<8> src, size_t size) OVERRIDE; void LoadRef(ManagedRegister dest, FrameOffset src) OVERRIDE; void LoadRef(ManagedRegister dest, ManagedRegister base, MemberOffset offs) OVERRIDE; void LoadRawPtr(ManagedRegister dest, ManagedRegister base, Offset offs) OVERRIDE; - void LoadRawPtrFromThread32(ManagedRegister dest, ThreadOffset<4> offs) OVERRIDE; + void LoadRawPtrFromThread64(ManagedRegister dest, ThreadOffset<8> offs) OVERRIDE; // Copying routines. void Move(ManagedRegister dest, ManagedRegister src, size_t size) OVERRIDE; - void CopyRawPtrFromThread32(FrameOffset fr_offs, ThreadOffset<4> thr_offs, + void CopyRawPtrFromThread64(FrameOffset fr_offs, ThreadOffset<8> thr_offs, ManagedRegister scratch) OVERRIDE; - void CopyRawPtrToThread32(ThreadOffset<4> thr_offs, FrameOffset fr_offs, ManagedRegister scratch) + void CopyRawPtrToThread64(ThreadOffset<8> thr_offs, FrameOffset fr_offs, ManagedRegister scratch) OVERRIDE; void CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister scratch) OVERRIDE; void Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch, size_t size) OVERRIDE; @@ -183,7 +183,7 @@ class Arm64Assembler FINAL : public Assembler { // Call to address held at [base+offset]. void Call(ManagedRegister base, Offset offset, ManagedRegister scratch) OVERRIDE; void Call(FrameOffset base, Offset offset, ManagedRegister scratch) OVERRIDE; - void CallFromThread32(ThreadOffset<4> offset, ManagedRegister scratch) OVERRIDE; + void CallFromThread64(ThreadOffset<8> offset, ManagedRegister scratch) OVERRIDE; // Jump to address (not setting link register) void JumpTo(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch); @@ -197,6 +197,8 @@ class Arm64Assembler FINAL : public Assembler { CHECK(code < kNumberOfCoreRegisters) << code; if (code == SP) { return vixl::sp; + } else if (code == XZR) { + return vixl::xzr; } return vixl::Register::XRegFromCode(code); } @@ -232,9 +234,6 @@ class Arm64Assembler FINAL : public Assembler { void AddConstant(Register rd, int32_t value, Condition cond = AL); void AddConstant(Register rd, Register rn, int32_t value, Condition cond = AL); - // Vixl buffer size. - static constexpr size_t BUF_SIZE = 4096; - // Vixl buffer. byte* vixl_buf_; @@ -243,6 +242,9 @@ class Arm64Assembler FINAL : public Assembler { // List of exception blocks to generate at the end of the code cache. std::vector<Arm64Exception*> exception_blocks_; + + // Used for testing. + friend class Arm64ManagedRegister_VixlRegisters_Test; }; class Arm64Exception { diff --git a/compiler/utils/arm64/constants_arm64.h b/compiler/utils/arm64/constants_arm64.h index ecf9fbe1d9..2a08c95654 100644 --- a/compiler/utils/arm64/constants_arm64.h +++ b/compiler/utils/arm64/constants_arm64.h @@ -31,6 +31,9 @@ namespace arm64 { constexpr unsigned int kCalleeSavedRegsSize = 20; +// Vixl buffer size. +constexpr size_t kBufferSizeArm64 = 4096*2; + } // arm64 } // art diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc index de5cb8cd8d..8977313256 100644 --- a/compiler/utils/arm64/managed_register_arm64.cc +++ b/compiler/utils/arm64/managed_register_arm64.cc @@ -53,7 +53,7 @@ int Arm64ManagedRegister::RegNo() const { CHECK(!IsNoRegister()); int no; if (IsCoreRegister()) { - if (IsStackPointer()) { + if (IsZeroRegister()) { no = static_cast<int>(X31); } else { no = static_cast<int>(AsCoreRegister()); diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h index 80f17f5eb1..a0f520f6a7 100644 --- a/compiler/utils/arm64/managed_register_arm64.h +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -24,7 +24,7 @@ namespace art { namespace arm64 { -const int kNumberOfCoreRegIds = 32; +const int kNumberOfCoreRegIds = kNumberOfCoreRegisters; const int kNumberOfWRegIds = kNumberOfWRegisters; const int kNumberOfDRegIds = kNumberOfDRegisters; const int kNumberOfSRegIds = kNumberOfSRegisters; @@ -78,7 +78,7 @@ class Arm64ManagedRegister : public ManagedRegister { WRegister AsOverlappingCoreRegisterLow() const { CHECK(IsValidManagedRegister()); - if (IsStackPointer()) return W31; + if (IsZeroRegister()) return W31; return static_cast<WRegister>(AsCoreRegister()); } @@ -189,6 +189,10 @@ class Arm64ManagedRegister : public ManagedRegister { return IsCoreRegister() && (id_ == SP); } + bool IsZeroRegister() const { + return IsCoreRegister() && (id_ == XZR); + } + int RegId() const { CHECK(!IsNoRegister()); return id_; diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc index 88c01ee793..f149f1bcf1 100644 --- a/compiler/utils/arm64/managed_register_arm64_test.cc +++ b/compiler/utils/arm64/managed_register_arm64_test.cc @@ -15,6 +15,7 @@ */ #include "globals.h" +#include "assembler_arm64.h" #include "managed_register_arm64.h" #include "gtest/gtest.h" @@ -295,9 +296,8 @@ TEST(Arm64ManagedRegister, Equals) { Arm64ManagedRegister reg_X31 = Arm64ManagedRegister::FromCoreRegister(X31); EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::NoRegister())); - // TODO: Fix the infrastructure, then re-enable. - // EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(SP))); - // EXPECT_TRUE(reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(XZR))); + EXPECT_TRUE(reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(XZR))); EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromWRegister(W31))); EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromWRegister(WZR))); EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromSRegister(S0))); @@ -305,8 +305,7 @@ TEST(Arm64ManagedRegister, Equals) { Arm64ManagedRegister reg_SP = Arm64ManagedRegister::FromCoreRegister(SP); EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::NoRegister())); - // TODO: We expect these to pass - SP has a different semantic than X31/XZR. - // EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(X31))); + EXPECT_TRUE(reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(X31))); EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(XZR))); EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromWRegister(W31))); EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromSRegister(S0))); @@ -453,17 +452,17 @@ TEST(Arm64ManagedRegister, Overlaps) { reg = Arm64ManagedRegister::FromCoreRegister(XZR); reg_o = Arm64ManagedRegister::FromWRegister(WZR); - // TODO: Overlap not implemented, yet - // EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - // EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); - // EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W19))); EXPECT_EQ(X31, reg_o.AsOverlappingWRegisterCore()); - // TODO: XZR is not a core register right now. - // EXPECT_EQ(W31, reg.AsOverlappingCoreRegisterLow()); + EXPECT_EQ(SP, reg_o.AsOverlappingWRegisterCore()); + EXPECT_NE(XZR, reg_o.AsOverlappingWRegisterCore()); + EXPECT_EQ(W31, reg.AsOverlappingCoreRegisterLow()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -610,5 +609,154 @@ TEST(Arm64ManagedRegister, Overlaps) { EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D20))); } +TEST(Arm64ManagedRegister, VixlRegisters) { + // X Registers. + EXPECT_TRUE(vixl::x0.Is(Arm64Assembler::reg_x(X0))); + EXPECT_TRUE(vixl::x1.Is(Arm64Assembler::reg_x(X1))); + EXPECT_TRUE(vixl::x2.Is(Arm64Assembler::reg_x(X2))); + EXPECT_TRUE(vixl::x3.Is(Arm64Assembler::reg_x(X3))); + EXPECT_TRUE(vixl::x4.Is(Arm64Assembler::reg_x(X4))); + EXPECT_TRUE(vixl::x5.Is(Arm64Assembler::reg_x(X5))); + EXPECT_TRUE(vixl::x6.Is(Arm64Assembler::reg_x(X6))); + EXPECT_TRUE(vixl::x7.Is(Arm64Assembler::reg_x(X7))); + EXPECT_TRUE(vixl::x8.Is(Arm64Assembler::reg_x(X8))); + EXPECT_TRUE(vixl::x9.Is(Arm64Assembler::reg_x(X9))); + EXPECT_TRUE(vixl::x10.Is(Arm64Assembler::reg_x(X10))); + EXPECT_TRUE(vixl::x11.Is(Arm64Assembler::reg_x(X11))); + EXPECT_TRUE(vixl::x12.Is(Arm64Assembler::reg_x(X12))); + EXPECT_TRUE(vixl::x13.Is(Arm64Assembler::reg_x(X13))); + EXPECT_TRUE(vixl::x14.Is(Arm64Assembler::reg_x(X14))); + EXPECT_TRUE(vixl::x15.Is(Arm64Assembler::reg_x(X15))); + EXPECT_TRUE(vixl::x16.Is(Arm64Assembler::reg_x(X16))); + EXPECT_TRUE(vixl::x17.Is(Arm64Assembler::reg_x(X17))); + EXPECT_TRUE(vixl::x18.Is(Arm64Assembler::reg_x(X18))); + EXPECT_TRUE(vixl::x19.Is(Arm64Assembler::reg_x(X19))); + EXPECT_TRUE(vixl::x20.Is(Arm64Assembler::reg_x(X20))); + EXPECT_TRUE(vixl::x21.Is(Arm64Assembler::reg_x(X21))); + EXPECT_TRUE(vixl::x22.Is(Arm64Assembler::reg_x(X22))); + EXPECT_TRUE(vixl::x23.Is(Arm64Assembler::reg_x(X23))); + EXPECT_TRUE(vixl::x24.Is(Arm64Assembler::reg_x(X24))); + EXPECT_TRUE(vixl::x25.Is(Arm64Assembler::reg_x(X25))); + EXPECT_TRUE(vixl::x26.Is(Arm64Assembler::reg_x(X26))); + EXPECT_TRUE(vixl::x27.Is(Arm64Assembler::reg_x(X27))); + EXPECT_TRUE(vixl::x28.Is(Arm64Assembler::reg_x(X28))); + EXPECT_TRUE(vixl::x29.Is(Arm64Assembler::reg_x(X29))); + EXPECT_TRUE(vixl::x30.Is(Arm64Assembler::reg_x(X30))); + // FIXME: Take a look here. + EXPECT_TRUE(vixl::sp.Is(Arm64Assembler::reg_x(X31))); + EXPECT_TRUE(!vixl::x31.Is(Arm64Assembler::reg_x(X31))); + + EXPECT_TRUE(vixl::x18.Is(Arm64Assembler::reg_x(TR))); + EXPECT_TRUE(vixl::ip0.Is(Arm64Assembler::reg_x(IP0))); + EXPECT_TRUE(vixl::ip1.Is(Arm64Assembler::reg_x(IP1))); + EXPECT_TRUE(vixl::x29.Is(Arm64Assembler::reg_x(FP))); + EXPECT_TRUE(vixl::lr.Is(Arm64Assembler::reg_x(LR))); + EXPECT_TRUE(vixl::sp.Is(Arm64Assembler::reg_x(SP))); + EXPECT_TRUE(vixl::xzr.Is(Arm64Assembler::reg_x(XZR))); + + // W Registers. + EXPECT_TRUE(vixl::w0.Is(Arm64Assembler::reg_w(W0))); + EXPECT_TRUE(vixl::w1.Is(Arm64Assembler::reg_w(W1))); + EXPECT_TRUE(vixl::w2.Is(Arm64Assembler::reg_w(W2))); + EXPECT_TRUE(vixl::w3.Is(Arm64Assembler::reg_w(W3))); + EXPECT_TRUE(vixl::w4.Is(Arm64Assembler::reg_w(W4))); + EXPECT_TRUE(vixl::w5.Is(Arm64Assembler::reg_w(W5))); + EXPECT_TRUE(vixl::w6.Is(Arm64Assembler::reg_w(W6))); + EXPECT_TRUE(vixl::w7.Is(Arm64Assembler::reg_w(W7))); + EXPECT_TRUE(vixl::w8.Is(Arm64Assembler::reg_w(W8))); + EXPECT_TRUE(vixl::w9.Is(Arm64Assembler::reg_w(W9))); + EXPECT_TRUE(vixl::w10.Is(Arm64Assembler::reg_w(W10))); + EXPECT_TRUE(vixl::w11.Is(Arm64Assembler::reg_w(W11))); + EXPECT_TRUE(vixl::w12.Is(Arm64Assembler::reg_w(W12))); + EXPECT_TRUE(vixl::w13.Is(Arm64Assembler::reg_w(W13))); + EXPECT_TRUE(vixl::w14.Is(Arm64Assembler::reg_w(W14))); + EXPECT_TRUE(vixl::w15.Is(Arm64Assembler::reg_w(W15))); + EXPECT_TRUE(vixl::w16.Is(Arm64Assembler::reg_w(W16))); + EXPECT_TRUE(vixl::w17.Is(Arm64Assembler::reg_w(W17))); + EXPECT_TRUE(vixl::w18.Is(Arm64Assembler::reg_w(W18))); + EXPECT_TRUE(vixl::w19.Is(Arm64Assembler::reg_w(W19))); + EXPECT_TRUE(vixl::w20.Is(Arm64Assembler::reg_w(W20))); + EXPECT_TRUE(vixl::w21.Is(Arm64Assembler::reg_w(W21))); + EXPECT_TRUE(vixl::w22.Is(Arm64Assembler::reg_w(W22))); + EXPECT_TRUE(vixl::w23.Is(Arm64Assembler::reg_w(W23))); + EXPECT_TRUE(vixl::w24.Is(Arm64Assembler::reg_w(W24))); + EXPECT_TRUE(vixl::w25.Is(Arm64Assembler::reg_w(W25))); + EXPECT_TRUE(vixl::w26.Is(Arm64Assembler::reg_w(W26))); + EXPECT_TRUE(vixl::w27.Is(Arm64Assembler::reg_w(W27))); + EXPECT_TRUE(vixl::w28.Is(Arm64Assembler::reg_w(W28))); + EXPECT_TRUE(vixl::w29.Is(Arm64Assembler::reg_w(W29))); + EXPECT_TRUE(vixl::w30.Is(Arm64Assembler::reg_w(W30))); + EXPECT_TRUE(vixl::w31.Is(Arm64Assembler::reg_w(W31))); + EXPECT_TRUE(vixl::wzr.Is(Arm64Assembler::reg_w(WZR))); + + // D Registers. + EXPECT_TRUE(vixl::d0.Is(Arm64Assembler::reg_d(D0))); + EXPECT_TRUE(vixl::d1.Is(Arm64Assembler::reg_d(D1))); + EXPECT_TRUE(vixl::d2.Is(Arm64Assembler::reg_d(D2))); + EXPECT_TRUE(vixl::d3.Is(Arm64Assembler::reg_d(D3))); + EXPECT_TRUE(vixl::d4.Is(Arm64Assembler::reg_d(D4))); + EXPECT_TRUE(vixl::d5.Is(Arm64Assembler::reg_d(D5))); + EXPECT_TRUE(vixl::d6.Is(Arm64Assembler::reg_d(D6))); + EXPECT_TRUE(vixl::d7.Is(Arm64Assembler::reg_d(D7))); + EXPECT_TRUE(vixl::d8.Is(Arm64Assembler::reg_d(D8))); + EXPECT_TRUE(vixl::d9.Is(Arm64Assembler::reg_d(D9))); + EXPECT_TRUE(vixl::d10.Is(Arm64Assembler::reg_d(D10))); + EXPECT_TRUE(vixl::d11.Is(Arm64Assembler::reg_d(D11))); + EXPECT_TRUE(vixl::d12.Is(Arm64Assembler::reg_d(D12))); + EXPECT_TRUE(vixl::d13.Is(Arm64Assembler::reg_d(D13))); + EXPECT_TRUE(vixl::d14.Is(Arm64Assembler::reg_d(D14))); + EXPECT_TRUE(vixl::d15.Is(Arm64Assembler::reg_d(D15))); + EXPECT_TRUE(vixl::d16.Is(Arm64Assembler::reg_d(D16))); + EXPECT_TRUE(vixl::d17.Is(Arm64Assembler::reg_d(D17))); + EXPECT_TRUE(vixl::d18.Is(Arm64Assembler::reg_d(D18))); + EXPECT_TRUE(vixl::d19.Is(Arm64Assembler::reg_d(D19))); + EXPECT_TRUE(vixl::d20.Is(Arm64Assembler::reg_d(D20))); + EXPECT_TRUE(vixl::d21.Is(Arm64Assembler::reg_d(D21))); + EXPECT_TRUE(vixl::d22.Is(Arm64Assembler::reg_d(D22))); + EXPECT_TRUE(vixl::d23.Is(Arm64Assembler::reg_d(D23))); + EXPECT_TRUE(vixl::d24.Is(Arm64Assembler::reg_d(D24))); + EXPECT_TRUE(vixl::d25.Is(Arm64Assembler::reg_d(D25))); + EXPECT_TRUE(vixl::d26.Is(Arm64Assembler::reg_d(D26))); + EXPECT_TRUE(vixl::d27.Is(Arm64Assembler::reg_d(D27))); + EXPECT_TRUE(vixl::d28.Is(Arm64Assembler::reg_d(D28))); + EXPECT_TRUE(vixl::d29.Is(Arm64Assembler::reg_d(D29))); + EXPECT_TRUE(vixl::d30.Is(Arm64Assembler::reg_d(D30))); + EXPECT_TRUE(vixl::d31.Is(Arm64Assembler::reg_d(D31))); + + // S Registers. + EXPECT_TRUE(vixl::s0.Is(Arm64Assembler::reg_s(S0))); + EXPECT_TRUE(vixl::s1.Is(Arm64Assembler::reg_s(S1))); + EXPECT_TRUE(vixl::s2.Is(Arm64Assembler::reg_s(S2))); + EXPECT_TRUE(vixl::s3.Is(Arm64Assembler::reg_s(S3))); + EXPECT_TRUE(vixl::s4.Is(Arm64Assembler::reg_s(S4))); + EXPECT_TRUE(vixl::s5.Is(Arm64Assembler::reg_s(S5))); + EXPECT_TRUE(vixl::s6.Is(Arm64Assembler::reg_s(S6))); + EXPECT_TRUE(vixl::s7.Is(Arm64Assembler::reg_s(S7))); + EXPECT_TRUE(vixl::s8.Is(Arm64Assembler::reg_s(S8))); + EXPECT_TRUE(vixl::s9.Is(Arm64Assembler::reg_s(S9))); + EXPECT_TRUE(vixl::s10.Is(Arm64Assembler::reg_s(S10))); + EXPECT_TRUE(vixl::s11.Is(Arm64Assembler::reg_s(S11))); + EXPECT_TRUE(vixl::s12.Is(Arm64Assembler::reg_s(S12))); + EXPECT_TRUE(vixl::s13.Is(Arm64Assembler::reg_s(S13))); + EXPECT_TRUE(vixl::s14.Is(Arm64Assembler::reg_s(S14))); + EXPECT_TRUE(vixl::s15.Is(Arm64Assembler::reg_s(S15))); + EXPECT_TRUE(vixl::s16.Is(Arm64Assembler::reg_s(S16))); + EXPECT_TRUE(vixl::s17.Is(Arm64Assembler::reg_s(S17))); + EXPECT_TRUE(vixl::s18.Is(Arm64Assembler::reg_s(S18))); + EXPECT_TRUE(vixl::s19.Is(Arm64Assembler::reg_s(S19))); + EXPECT_TRUE(vixl::s20.Is(Arm64Assembler::reg_s(S20))); + EXPECT_TRUE(vixl::s21.Is(Arm64Assembler::reg_s(S21))); + EXPECT_TRUE(vixl::s22.Is(Arm64Assembler::reg_s(S22))); + EXPECT_TRUE(vixl::s23.Is(Arm64Assembler::reg_s(S23))); + EXPECT_TRUE(vixl::s24.Is(Arm64Assembler::reg_s(S24))); + EXPECT_TRUE(vixl::s25.Is(Arm64Assembler::reg_s(S25))); + EXPECT_TRUE(vixl::s26.Is(Arm64Assembler::reg_s(S26))); + EXPECT_TRUE(vixl::s27.Is(Arm64Assembler::reg_s(S27))); + EXPECT_TRUE(vixl::s28.Is(Arm64Assembler::reg_s(S28))); + EXPECT_TRUE(vixl::s29.Is(Arm64Assembler::reg_s(S29))); + EXPECT_TRUE(vixl::s30.Is(Arm64Assembler::reg_s(S30))); + EXPECT_TRUE(vixl::s31.Is(Arm64Assembler::reg_s(S31))); +} + } // namespace arm64 } // namespace art diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h index f007d2804a..bfb2829a32 100644 --- a/compiler/utils/managed_register.h +++ b/compiler/utils/managed_register.h @@ -70,11 +70,13 @@ class ManagedRegister { return ManagedRegister(); } + int RegId() const { return id_; } + explicit ManagedRegister(int reg_id) : id_(reg_id) { } + protected: static const int kNoRegister = -1; ManagedRegister() : id_(kNoRegister) { } - explicit ManagedRegister(int reg_id) : id_(reg_id) { } int id_; }; @@ -89,6 +91,9 @@ class ManagedRegisterSpill : public ManagedRegister { explicit ManagedRegisterSpill(const ManagedRegister& other) : ManagedRegister(other), size_(-1), spill_offset_(-1) { } + explicit ManagedRegisterSpill(const ManagedRegister& other, int32_t size) + : ManagedRegister(other), size_(size), spill_offset_(-1) { } + int32_t getSpillOffset() { return spill_offset_; } @@ -111,6 +116,11 @@ class ManagedRegisterEntrySpills : public std::vector<ManagedRegisterSpill> { std::vector<ManagedRegisterSpill>::push_back(spill); } + void push_back(ManagedRegister __x, int32_t __size) { + ManagedRegisterSpill spill(__x, __size); + std::vector<ManagedRegisterSpill>::push_back(spill); + } + void push_back(ManagedRegisterSpill __x) { std::vector<ManagedRegisterSpill>::push_back(__x); } diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 6043c17dd9..6a3efc5431 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -863,6 +863,10 @@ void X86Assembler::xorl(Register dst, Register src) { EmitOperand(dst, Operand(src)); } +void X86Assembler::xorl(Register dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(6, Operand(dst), imm); +} void X86Assembler::addl(Register reg, const Immediate& imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index f8fc4c0e70..057c80af91 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -354,6 +354,7 @@ class X86Assembler FINAL : public Assembler { void orl(Register dst, Register src); void xorl(Register dst, Register src); + void xorl(Register dst, const Immediate& imm); void addl(Register dst, Register src); void addl(Register reg, const Immediate& imm); diff --git a/compiler/utils/x86/managed_register_x86.cc b/compiler/utils/x86/managed_register_x86.cc index 7fae7a8b6f..034a795622 100644 --- a/compiler/utils/x86/managed_register_x86.cc +++ b/compiler/utils/x86/managed_register_x86.cc @@ -33,7 +33,8 @@ namespace x86 { P(EDX, EDI) \ P(ECX, EBX) \ P(ECX, EDI) \ - P(EBX, EDI) + P(EBX, EDI) \ + P(ECX, EDX) struct RegisterPairDescriptor { diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h index 0201a96ad0..09d2b4919d 100644 --- a/compiler/utils/x86/managed_register_x86.h +++ b/compiler/utils/x86/managed_register_x86.h @@ -37,7 +37,8 @@ enum RegisterPair { ECX_EBX = 7, ECX_EDI = 8, EBX_EDI = 9, - kNumberOfRegisterPairs = 10, + ECX_EDX = 10, // Dalvik style passing + kNumberOfRegisterPairs = 11, kNoRegisterPair = -1, }; @@ -121,6 +122,12 @@ class X86ManagedRegister : public ManagedRegister { return FromRegId(AllocIdHigh()).AsCpuRegister(); } + RegisterPair AsRegisterPair() const { + CHECK(IsRegisterPair()); + return static_cast<RegisterPair>(id_ - + (kNumberOfCpuRegIds + kNumberOfXmmRegIds + kNumberOfX87RegIds)); + } + bool IsCpuRegister() const { CHECK(IsValidManagedRegister()); return (0 <= id_) && (id_ < kNumberOfCpuRegIds); |