Merge "Quick compiler: fix CmpLong pair handling"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5b83056..ef5819d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -177,7 +177,7 @@
LOCAL_CLANG := $(ART_TARGET_CLANG)
LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) $(ART_TARGET_DEBUG_CFLAGS)
LOCAL_CFLAGS_x86 := $(ART_TARGET_CFLAGS_x86)
- LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils
+ LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixl
LOCAL_STATIC_LIBRARIES += libgtest
LOCAL_MODULE_PATH_32 := $(ART_BASE_NATIVETEST_OUT)
LOCAL_MODULE_PATH_64 := $(ART_BASE_NATIVETEST_OUT)64
@@ -200,7 +200,7 @@
LOCAL_CLANG := $(ART_HOST_CLANG)
LOCAL_CFLAGS += $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libz-host
- LOCAL_STATIC_LIBRARIES += libcutils
+ LOCAL_STATIC_LIBRARIES += libcutils libvixl
ifneq ($(WITHOUT_HOST_CLANG),true)
# GCC host compiled tests fail with this linked, presumably due to destructors that run.
LOCAL_STATIC_LIBRARIES += libgtest_host
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 6c8c85d..38d37b0 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -324,7 +324,6 @@
enum ThrowKind {
kThrowNullPointer,
- kThrowDivZero,
kThrowArrayBounds,
kThrowConstantArrayBounds,
kThrowNoSuchMethod,
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 54c118d..8b4171d 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -916,7 +916,7 @@
RegStorage t_reg = AllocTemp();
NewLIR4(kThumb2OrrRRRs, t_reg.GetReg(), reg.GetLowReg(), reg.GetHighReg(), 0);
FreeTemp(t_reg);
- GenCheck(kCondEq, kThrowDivZero);
+ AddDivZeroSlowPath(kCondEq);
}
// Test suspend flag, return target of taken suspend branch
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index a3fb420..4522379 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -42,17 +42,6 @@
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,38 @@
return branch;
}
+void Mir2Lir::AddDivZeroSlowPath(ConditionCode c_code) {
+ LIR* branch = OpCondBranch(c_code, nullptr);
+ AddDivZeroCheckSlowPath(branch);
+}
+
+void Mir2Lir::AddDivZeroSlowPath(ConditionCode c_code, RegStorage reg, int imm_val) {
+ LIR* branch;
+ if (c_code == kCondAl) {
+ branch = OpUnconditionalBranch(nullptr);
+ } else {
+ branch = OpCmpImmBranch(c_code, reg, imm_val, 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() {
+ m2l_->ResetRegPool();
+ m2l_->ResetDefTracking();
+ GenerateTargetLabel();
+ m2l_->CallRuntimeHelper(QUICK_ENTRYPOINT_OFFSET(4, pThrowDivZero), true);
+ }
+ };
+
+ AddSlowPath(new (arena_) DivZeroCheckSlowPath(this, branch));
+}
/* Perform null-check on a register. */
LIR* Mir2Lir::GenNullCheck(RegStorage m_reg, int opt_flags) {
@@ -689,9 +710,6 @@
}
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 =
@@ -1533,7 +1551,7 @@
rl_src1 = LoadValue(rl_src1, kCoreReg);
rl_src2 = LoadValue(rl_src2, kCoreReg);
if (check_zero) {
- GenImmedCheck(kCondEq, rl_src2.reg, 0, kThrowDivZero);
+ AddDivZeroSlowPath(kCondEq, rl_src2.reg, 0);
}
rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv);
done = true;
@@ -1544,7 +1562,7 @@
rl_src1 = LoadValue(rl_src1, kCoreReg);
rl_src2 = LoadValue(rl_src2, kCoreReg);
if (check_zero) {
- GenImmedCheck(kCondEq, rl_src2.reg, 0, kThrowDivZero);
+ AddDivZeroSlowPath(kCondEq, rl_src2.reg, 0);
}
rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv);
done = true;
@@ -1559,7 +1577,7 @@
RegStorage r_tgt = CallHelperSetup(func_offset);
LoadValueDirectFixed(rl_src1, TargetReg(kArg0));
if (check_zero) {
- GenImmedCheck(kCondEq, TargetReg(kArg1), 0, kThrowDivZero);
+ AddDivZeroSlowPath(kCondEq, TargetReg(kArg1), 0);
}
// NOTE: callout here is not a safepoint.
CallHelper(r_tgt, func_offset, false /* not a safepoint */);
@@ -1784,7 +1802,7 @@
case Instruction::REM_INT_LIT8:
case Instruction::REM_INT_LIT16: {
if (lit == 0) {
- GenImmedCheck(kCondAl, RegStorage::InvalidReg(), 0, kThrowDivZero);
+ AddDivZeroSlowPath(kCondAl, RegStorage::InvalidReg(), 0);
return;
}
if ((opcode == Instruction::DIV_INT) ||
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 396a709..d827568 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -87,6 +87,12 @@
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);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 5fe96d2..0492fdb 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -346,7 +346,7 @@
DCHECK(reg.IsPair()); // TODO: support k64BitSolo.
RegStorage t_reg = AllocTemp();
OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh());
- GenImmedCheck(kCondEq, t_reg, 0, kThrowDivZero);
+ AddDivZeroSlowPath(kCondEq, t_reg, 0);
FreeTemp(t_reg);
}
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 35f948e..6dbeb34 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -562,7 +562,8 @@
void HandleThrowLaunchPads();
void HandleSlowPaths();
void GenBarrier();
- LIR* GenCheck(ConditionCode c_code, ThrowKind kind);
+ void AddDivZeroSlowPath(ConditionCode c_code);
+ void AddDivZeroSlowPath(ConditionCode c_code, RegStorage reg, int imm_val);
void MarkPossibleNullPointerException(int opt_flags);
void MarkPossibleStackOverflowException();
void ForceImplicitNullCheck(RegStorage reg, int opt_flags);
@@ -619,6 +620,7 @@
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,
@@ -1220,6 +1222,7 @@
*/
bool GenSpecialIdentity(MIR* mir, const InlineMethod& special);
+ void AddDivZeroCheckSlowPath(LIR* branch);
public:
// TODO: add accessors for these.
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index c1d1e01..a5f3b61 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -629,7 +629,7 @@
if (check_zero) {
// Handle division by zero case.
- GenImmedCheck(kCondEq, rs_r1, 0, kThrowDivZero);
+ AddDivZeroSlowPath(kCondEq, rs_r1, 0);
}
// Have to catch 0x80000000/-1 case, or we will get an exception!
@@ -885,7 +885,7 @@
OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh());
// In case of zero, throw ArithmeticException.
- GenCheck(kCondEq, kThrowDivZero);
+ AddDivZeroSlowPath(kCondEq);
// The temp is no longer needed so free it at this time.
FreeTemp(t_reg);
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index 0220724..8acd1f9 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -197,6 +197,8 @@
CHECK(code < kNumberOfCoreRegisters) << code;
if (code == SP) {
return vixl::sp;
+ } else if (code == XZR) {
+ return vixl::xzr;
}
return vixl::Register::XRegFromCode(code);
}
@@ -243,6 +245,9 @@
// 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/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc
index de5cb8c..8977313 100644
--- a/compiler/utils/arm64/managed_register_arm64.cc
+++ b/compiler/utils/arm64/managed_register_arm64.cc
@@ -53,7 +53,7 @@
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 80f17f5..a0f520f 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 @@
WRegister AsOverlappingCoreRegisterLow() const {
CHECK(IsValidManagedRegister());
- if (IsStackPointer()) return W31;
+ if (IsZeroRegister()) return W31;
return static_cast<WRegister>(AsCoreRegister());
}
@@ -189,6 +189,10 @@
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 88c01ee..f149f1b 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 @@
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 @@
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 @@
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 @@
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/runtime/arch/arm64/registers_arm64.h b/runtime/arch/arm64/registers_arm64.h
index ca904bc..43c0ad6 100644
--- a/runtime/arch/arm64/registers_arm64.h
+++ b/runtime/arch/arm64/registers_arm64.h
@@ -63,8 +63,8 @@
LR = 30,
SP = 31, // SP is X31 and overlaps with XRZ but we encode it as a
// special register, due to the different instruction semantics.
- XZR = 32, // FIXME This needs to be reconciled with the JNI assembler.
- kNumberOfCoreRegisters = 32,
+ XZR = 32,
+ kNumberOfCoreRegisters = 33,
kNoRegister = -1,
};
std::ostream& operator<<(std::ostream& os, const Register& rhs);
@@ -103,7 +103,6 @@
W29 = 29,
W30 = 30,
W31 = 31,
- WSP = 31,
WZR = 31,
kNumberOfWRegisters = 32,
kNoWRegister = -1,
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 4b881f6..b50c098 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -76,6 +76,7 @@
kClassLinkerClassesLock,
kBreakpointLock,
kMonitorLock,
+ kMonitorListLock,
kThreadListLock,
kBreakpointInvokeLock,
kDeoptimizationLock,
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 920741f..cbefa6a 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -2005,6 +2005,61 @@
}
}
+size_t RosAlloc::ReleasePages() {
+ VLOG(heap) << "RosAlloc::ReleasePages()";
+ DCHECK(!DoesReleaseAllPages());
+ Thread* self = Thread::Current();
+ size_t reclaimed_bytes = 0;
+ size_t i = 0;
+ while (true) {
+ MutexLock mu(self, lock_);
+ // Check the page map size which might have changed due to grow/shrink.
+ size_t pm_end = page_map_size_;
+ if (i >= pm_end) {
+ // Reached the end.
+ break;
+ }
+ byte pm = page_map_[i];
+ switch (pm) {
+ case kPageMapEmpty: {
+ // The start of a free page run. Release pages.
+ FreePageRun* fpr = reinterpret_cast<FreePageRun*>(base_ + i * kPageSize);
+ DCHECK(free_page_runs_.find(fpr) != free_page_runs_.end());
+ size_t fpr_size = fpr->ByteSize(this);
+ DCHECK(IsAligned<kPageSize>(fpr_size));
+ byte* start = reinterpret_cast<byte*>(fpr);
+ if (kIsDebugBuild) {
+ // In the debug build, the first page of a free page run
+ // contains a magic number for debugging. Exclude it.
+ start = reinterpret_cast<byte*>(fpr) + kPageSize;
+ }
+ byte* end = reinterpret_cast<byte*>(fpr) + fpr_size;
+ CHECK_EQ(madvise(start, end - start, MADV_DONTNEED), 0);
+ reclaimed_bytes += fpr_size;
+ size_t num_pages = fpr_size / kPageSize;
+ if (kIsDebugBuild) {
+ for (size_t j = i + 1; j < i + num_pages; ++j) {
+ DCHECK_EQ(page_map_[j], kPageMapEmpty);
+ }
+ }
+ i += num_pages;
+ DCHECK_LE(i, pm_end);
+ break;
+ }
+ case kPageMapLargeObject: // Fall through.
+ case kPageMapLargeObjectPart: // Fall through.
+ case kPageMapRun: // Fall through.
+ case kPageMapRunPart: // Fall through.
+ ++i;
+ break; // Skip.
+ default:
+ LOG(FATAL) << "Unreachable - page map type: " << pm;
+ break;
+ }
+ }
+ return reclaimed_bytes;
+}
+
} // namespace allocator
} // namespace gc
} // namespace art
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 0b4b189..5d9d75c 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -539,6 +539,8 @@
void InspectAll(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg),
void* arg)
LOCKS_EXCLUDED(lock_);
+ // Release empty pages.
+ size_t ReleasePages() LOCKS_EXCLUDED(lock_);
// Returns the current footprint.
size_t Footprint() LOCKS_EXCLUDED(lock_);
// Returns the current capacity, maximum footprint.
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 07951e0..82340f5 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -201,7 +201,7 @@
uint64_t GarbageCollector::GetEstimatedLastIterationThroughput() const {
// Add 1ms to prevent possible division by 0.
- return (freed_bytes_ * 1000) / (NsToMs(GetDurationNs()) + 1);
+ return (static_cast<uint64_t>(freed_bytes_) * 1000) / (NsToMs(GetDurationNs()) + 1);
}
} // namespace collector
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 915e54f..e3fa834 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -914,8 +914,16 @@
// Transition the collector if the desired collector type is not the same as the current
// collector type.
TransitionCollector(desired_collector_type);
- // Do a heap trim if it is needed.
- Trim();
+ if (!CareAboutPauseTimes()) {
+ // Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
+ // about pauses.
+ Runtime* runtime = Runtime::Current();
+ runtime->GetThreadList()->SuspendAll();
+ runtime->GetMonitorList()->DeflateMonitors();
+ runtime->GetThreadList()->ResumeAll();
+ // Do a heap trim if it is needed.
+ Trim();
+ }
}
void Heap::Trim() {
@@ -2663,6 +2671,10 @@
}
void Heap::RequestHeapTrim() {
+ // Request a heap trim only if we do not currently care about pause times.
+ if (CareAboutPauseTimes()) {
+ return;
+ }
// GC completed and now we must decide whether to request a heap trim (advising pages back to the
// kernel) or not. Issuing a request will also cause trimming of the libc heap. As a trim scans
// a space it will hold its lock and can become a cause of jank.
@@ -2684,21 +2696,17 @@
// as we don't hold the lock while requesting the trim).
return;
}
-
- // Request a heap trim only if we do not currently care about pause times.
- if (!CareAboutPauseTimes()) {
- {
- MutexLock mu(self, *heap_trim_request_lock_);
- if (last_trim_time_ + kHeapTrimWait >= NanoTime()) {
- // We have done a heap trim in the last kHeapTrimWait nanosecs, don't request another one
- // just yet.
- return;
- }
- heap_trim_request_pending_ = true;
+ {
+ MutexLock mu(self, *heap_trim_request_lock_);
+ if (last_trim_time_ + kHeapTrimWait >= NanoTime()) {
+ // We have done a heap trim in the last kHeapTrimWait nanosecs, don't request another one
+ // just yet.
+ return;
}
- // Notify the daemon thread which will actually do the heap trim.
- SignalHeapTrimDaemon(self);
+ heap_trim_request_pending_ = true;
}
+ // Notify the daemon thread which will actually do the heap trim.
+ SignalHeapTrimDaemon(self);
}
void Heap::SignalHeapTrimDaemon(Thread* self) {
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 012267b..5c5e7f8 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -222,6 +222,7 @@
}
size_t RosAllocSpace::Trim() {
+ VLOG(heap) << "RosAllocSpace::Trim() ";
{
MutexLock mu(Thread::Current(), lock_);
// Trim to release memory at the end of the space.
@@ -229,10 +230,7 @@
}
// Attempt to release pages if it does not release all empty pages.
if (!rosalloc_->DoesReleaseAllPages()) {
- VLOG(heap) << "RosAllocSpace::Trim() ";
- size_t reclaimed = 0;
- InspectAllRosAlloc(DlmallocMadviseCallback, &reclaimed, false);
- return reclaimed;
+ return rosalloc_->ReleasePages();
}
return 0;
}
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index bcaf8ec..bbc7dd0 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -205,7 +205,7 @@
void Monitor::Lock(Thread* self) {
MutexLock mu(self, monitor_lock_);
while (true) {
- if (owner_ == NULL) { // Unowned.
+ if (owner_ == nullptr) { // Unowned.
owner_ = self;
CHECK_EQ(lock_count_, 0);
// When debugging, save the current monitor holder for future
@@ -223,15 +223,15 @@
uint64_t wait_start_ms = log_contention ? 0 : MilliTime();
mirror::ArtMethod* owners_method = locking_method_;
uint32_t owners_dex_pc = locking_dex_pc_;
+ // Do this before releasing the lock so that we don't get deflated.
+ ++num_waiters_;
monitor_lock_.Unlock(self); // Let go of locks in order.
{
ScopedThreadStateChange tsc(self, kBlocked); // Change to blocked and give up mutator_lock_.
self->SetMonitorEnterObject(obj_);
MutexLock mu2(self, monitor_lock_); // Reacquire monitor_lock_ without mutator_lock_ for Wait.
if (owner_ != NULL) { // Did the owner_ give the lock up?
- ++num_waiters_;
monitor_contenders_.Wait(self); // Still contended so wait.
- --num_waiters_;
// Woken from contention.
if (log_contention) {
uint64_t wait_ms = MilliTime() - wait_start_ms;
@@ -252,6 +252,7 @@
self->SetMonitorEnterObject(nullptr);
}
monitor_lock_.Lock(self); // Reacquire locks in order.
+ --num_waiters_;
}
}
@@ -431,6 +432,7 @@
* not order sensitive as we hold the pthread mutex.
*/
AppendToWaitSet(self);
+ ++num_waiters_;
int prev_lock_count = lock_count_;
lock_count_ = 0;
owner_ = NULL;
@@ -507,6 +509,7 @@
lock_count_ = prev_lock_count;
locking_method_ = saved_method;
locking_dex_pc_ = saved_dex_pc;
+ --num_waiters_;
RemoveFromWaitSet(self);
if (was_interrupted) {
@@ -575,8 +578,12 @@
// If the lock isn't an inflated monitor, then we don't need to deflate anything.
if (lw.GetState() == LockWord::kFatLocked) {
Monitor* monitor = lw.FatLockMonitor();
- CHECK(monitor != nullptr);
+ DCHECK(monitor != nullptr);
MutexLock mu(self, monitor->monitor_lock_);
+ // Can't deflate if we have anybody waiting on the CV.
+ if (monitor->num_waiters_ > 0) {
+ return false;
+ }
Thread* owner = monitor->owner_;
if (owner != nullptr) {
// Can't deflate if we are locked and have a hash code.
@@ -587,17 +594,16 @@
if (monitor->lock_count_ > LockWord::kThinLockMaxCount) {
return false;
}
- // Can't deflate if we have anybody waiting on the CV.
- if (monitor->num_waiters_ > 0) {
- return false;
- }
// Deflate to a thin lock.
- obj->SetLockWord(LockWord::FromThinLockId(owner->GetTid(), monitor->lock_count_));
+ obj->SetLockWord(LockWord::FromThinLockId(owner->GetThreadId(), monitor->lock_count_));
+ VLOG(monitor) << "Deflated " << obj << " to thin lock " << owner->GetTid() << " / " << monitor->lock_count_;
} else if (monitor->HasHashCode()) {
obj->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode()));
+ VLOG(monitor) << "Deflated " << obj << " to hash monitor " << monitor->GetHashCode();
} else {
// No lock and no hash, just put an empty lock word inside the object.
obj->SetLockWord(LockWord());
+ VLOG(monitor) << "Deflated" << obj << " to empty lock word";
}
// The monitor is deflated, mark the object as nullptr so that we know to delete it during the
// next GC.
@@ -1054,7 +1060,7 @@
}
MonitorList::MonitorList()
- : allow_new_monitors_(true), monitor_list_lock_("MonitorList lock"),
+ : allow_new_monitors_(true), monitor_list_lock_("MonitorList lock", kMonitorListLock),
monitor_add_condition_("MonitorList disallow condition", monitor_list_lock_) {
}
@@ -1103,6 +1109,22 @@
}
}
+static mirror::Object* MonitorDeflateCallback(mirror::Object* object, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (Monitor::Deflate(reinterpret_cast<Thread*>(arg), object)) {
+ DCHECK_NE(object->GetLockWord().GetState(), LockWord::kFatLocked);
+ // If we deflated, return nullptr so that the monitor gets removed from the array.
+ return nullptr;
+ }
+ return object; // Monitor was not deflated.
+}
+
+void MonitorList::DeflateMonitors() {
+ Thread* self = Thread::Current();
+ Locks::mutator_lock_->AssertExclusiveHeld(self);
+ SweepMonitorList(MonitorDeflateCallback, reinterpret_cast<Thread*>(self));
+}
+
MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(NULL), entry_count_(0) {
DCHECK(obj != NULL);
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 55504b5..c459278 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -224,9 +224,11 @@
void Add(Monitor* m);
void SweepMonitorList(IsMarkedCallback* callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DisallowNewMonitors();
- void AllowNewMonitors();
+ LOCKS_EXCLUDED(monitor_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DisallowNewMonitors() LOCKS_EXCLUDED(monitor_list_lock_);
+ void AllowNewMonitors() LOCKS_EXCLUDED(monitor_list_lock_);
+ void DeflateMonitors() LOCKS_EXCLUDED(monitor_list_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
bool allow_new_monitors_ GUARDED_BY(monitor_list_lock_);