summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/Android.mk5
-rw-r--r--compiler/common_compiler_test.h2
-rw-r--r--compiler/compiled_method.cc16
-rw-r--r--compiler/dex/compiler_enums.h4
-rw-r--r--compiler/dex/frontend.cc25
-rw-r--r--compiler/dex/local_value_numbering.cc14
-rw-r--r--compiler/dex/quick/arm/arm_lir.h2
-rw-r--r--compiler/dex/quick/arm/assemble_arm.cc8
-rw-r--r--compiler/dex/quick/arm/codegen_arm.h2
-rw-r--r--compiler/dex/quick/arm/int_arm.cc106
-rw-r--r--compiler/dex/quick/arm/target_arm.cc3
-rw-r--r--compiler/dex/quick/dex_file_method_inliner.cc141
-rw-r--r--compiler/dex/quick/dex_file_method_inliner.h2
-rw-r--r--compiler/dex/quick/gen_common.cc220
-rw-r--r--compiler/dex/quick/gen_invoke.cc33
-rw-r--r--compiler/dex/quick/gen_loadstore.cc7
-rw-r--r--compiler/dex/quick/mips/codegen_mips.h2
-rw-r--r--compiler/dex/quick/mips/int_mips.cc12
-rw-r--r--compiler/dex/quick/mir_to_lir.cc8
-rw-r--r--compiler/dex/quick/mir_to_lir.h20
-rw-r--r--compiler/dex/quick/x86/codegen_x86.h4
-rw-r--r--compiler/dex/quick/x86/int_x86.cc98
-rw-r--r--compiler/dex/quick/x86/target_x86.cc46
-rw-r--r--compiler/driver/compiler_driver.cc91
-rw-r--r--compiler/driver/compiler_driver.h36
-rw-r--r--compiler/elf_writer_quick.cc3
-rw-r--r--compiler/elf_writer_test.cc4
-rw-r--r--compiler/image_test.cc2
-rw-r--r--compiler/image_writer.cc22
-rw-r--r--compiler/image_writer.h2
-rw-r--r--compiler/jni/jni_compiler_test.cc223
-rw-r--r--compiler/jni/quick/arm/calling_convention_arm.cc2
-rw-r--r--compiler/jni/quick/arm64/calling_convention_arm64.cc191
-rw-r--r--compiler/jni/quick/arm64/calling_convention_arm64.h8
-rw-r--r--compiler/jni/quick/calling_convention.cc38
-rw-r--r--compiler/jni/quick/calling_convention.h29
-rw-r--r--compiler/jni/quick/jni_compiler.cc124
-rw-r--r--compiler/jni/quick/mips/calling_convention_mips.cc2
-rw-r--r--compiler/jni/quick/x86/calling_convention_x86.cc2
-rw-r--r--compiler/jni/quick/x86_64/calling_convention_x86_64.cc2
-rw-r--r--compiler/llvm/llvm_compilation_unit.cc19
-rw-r--r--compiler/oat_writer.cc14
-rw-r--r--compiler/optimizing/builder.cc393
-rw-r--r--compiler/optimizing/builder.h46
-rw-r--r--compiler/optimizing/code_generator.cc26
-rw-r--r--compiler/optimizing/code_generator.h161
-rw-r--r--compiler/optimizing/code_generator_arm.cc551
-rw-r--r--compiler/optimizing/code_generator_arm.h56
-rw-r--r--compiler/optimizing/code_generator_x86.cc551
-rw-r--r--compiler/optimizing/code_generator_x86.h55
-rw-r--r--compiler/optimizing/nodes.h152
-rw-r--r--compiler/utils/arm64/assembler_arm64.cc138
-rw-r--r--compiler/utils/arm64/assembler_arm64.h28
-rw-r--r--compiler/utils/arm64/constants_arm64.h3
-rw-r--r--compiler/utils/arm64/managed_register_arm64.cc2
-rw-r--r--compiler/utils/arm64/managed_register_arm64.h8
-rw-r--r--compiler/utils/arm64/managed_register_arm64_test.cc170
-rw-r--r--compiler/utils/managed_register.h12
-rw-r--r--compiler/utils/x86/assembler_x86.cc4
-rw-r--r--compiler/utils/x86/assembler_x86.h1
-rw-r--r--compiler/utils/x86/managed_register_x86.cc3
-rw-r--r--compiler/utils/x86/managed_register_x86.h9
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);