summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathieu Chartier <mathieuc@google.com>2014-03-19 22:58:23 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2014-03-19 22:58:24 +0000
commit66e4c3e96dccdec7423d673ad6bbf7821a776651 (patch)
treefea99ef4dea02ccc16474c1b69a2fc1f3b3c2ce1
parent4af7b576e1618a8e31c9429c643e98932aea1d42 (diff)
parent0d507d1e0441e6bd6f3affca3a60774ea920f317 (diff)
downloadandroid_art-66e4c3e96dccdec7423d673ad6bbf7821a776651.tar.gz
android_art-66e4c3e96dccdec7423d673ad6bbf7821a776651.tar.bz2
android_art-66e4c3e96dccdec7423d673ad6bbf7821a776651.zip
Merge "Optimize stack overflow handling."
-rw-r--r--compiler/dex/compiler_enums.h1
-rw-r--r--compiler/dex/quick/arm/call_arm.cc51
-rw-r--r--compiler/dex/quick/gen_common.cc30
-rw-r--r--compiler/dex/quick/mips/call_mips.cc30
-rw-r--r--compiler/dex/quick/x86/call_x86.cc33
-rw-r--r--runtime/thread.h6
6 files changed, 106 insertions, 45 deletions
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 147e840166..718468fb77 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -328,7 +328,6 @@ enum ThrowKind {
kThrowArrayBounds,
kThrowConstantArrayBounds,
kThrowNoSuchMethod,
- kThrowStackOverflow,
};
enum DividePattern {
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index bba3d40409..94f0ca48db 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -358,23 +358,60 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
*/
NewLIR1(kThumb2VPushCS, num_fp_spills_);
}
+
+ // TODO: 64 bit will be different code.
+ const int frame_size_without_spills = frame_size_ - spill_count * 4;
if (!skip_overflow_check) {
if (Runtime::Current()->ExplicitStackOverflowChecks()) {
- OpRegRegImm(kOpSub, rARM_LR, rARM_SP, frame_size_ - (spill_count * 4));
- GenRegRegCheck(kCondUlt, rARM_LR, r12, kThrowStackOverflow);
- OpRegCopy(rARM_SP, rARM_LR); // Establish stack
+ class StackOverflowSlowPath : public LIRSlowPath {
+ public:
+ StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, bool restore_lr, size_t sp_displace)
+ : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, nullptr), restore_lr_(restore_lr),
+ sp_displace_(sp_displace) {
+ }
+ void Compile() OVERRIDE {
+ m2l_->ResetRegPool();
+ m2l_->ResetDefTracking();
+ GenerateTargetLabel();
+ if (restore_lr_) {
+ m2l_->LoadWordDisp(kArmRegSP, sp_displace_ - 4, kArmRegLR);
+ }
+ m2l_->OpRegImm(kOpAdd, kArmRegSP, sp_displace_);
+ m2l_->ClobberCallerSave();
+ ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pThrowStackOverflow);
+ // Load the entrypoint directly into the pc instead of doing a load + branch. Assumes
+ // codegen and target are in thumb2 mode.
+ m2l_->LoadWordDisp(rARM_SELF, func_offset.Int32Value(), rARM_PC);
+ }
+
+ private:
+ const bool restore_lr_;
+ const size_t sp_displace_;
+ };
+ if (static_cast<size_t>(frame_size_) > Thread::kStackOverflowReservedUsableBytes) {
+ OpRegRegImm(kOpSub, rARM_LR, rARM_SP, frame_size_without_spills);
+ LIR* branch = OpCmpBranch(kCondUlt, rARM_LR, r12, nullptr);
+ // Need to restore LR since we used it as a temp.
+ AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, true,
+ frame_size_without_spills));
+ OpRegCopy(rARM_SP, rARM_LR); // Establish stack
+ } else {
+ // If the frame is small enough we are guaranteed to have enough space that remains to
+ // handle signals on the user stack.
+ OpRegRegImm(kOpSub, rARM_SP, rARM_SP, frame_size_without_spills);
+ LIR* branch = OpCmpBranch(kCondUlt, rARM_SP, r12, nullptr);
+ AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, false, frame_size_));
+ }
} else {
// Implicit stack overflow check.
// Generate a load from [sp, #-framesize]. If this is in the stack
// redzone we will get a segmentation fault.
- uint32_t full_frame_size = frame_size_ - (spill_count * 4);
-
- OpRegImm(kOpSub, rARM_SP, full_frame_size);
+ OpRegImm(kOpSub, rARM_SP, frame_size_without_spills);
LoadWordDisp(rARM_SP, 0, rARM_LR);
MarkPossibleStackOverflowException();
}
} else {
- OpRegImm(kOpSub, rARM_SP, frame_size_ - (spill_count * 4));
+ OpRegImm(kOpSub, rARM_SP, frame_size_without_spills);
}
FlushIns(ArgLocs, rl_method);
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 71cc0d9cd6..8c3a11fb6c 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -629,8 +629,6 @@ void Mir2Lir::HandleThrowLaunchPads() {
int v1 = lab->operands[2];
int v2 = lab->operands[3];
const bool target_x86 = cu_->instruction_set == kX86;
- const bool target_arm = cu_->instruction_set == kArm || cu_->instruction_set == kThumb2;
- const bool target_mips = cu_->instruction_set == kMips;
switch (lab->operands[0]) {
case kThrowNullPointer:
func_offset = QUICK_ENTRYPOINT_OFFSET(pThrowNullPointer);
@@ -688,34 +686,6 @@ void Mir2Lir::HandleThrowLaunchPads() {
func_offset =
QUICK_ENTRYPOINT_OFFSET(pThrowNoSuchMethod);
break;
- case kThrowStackOverflow: {
- func_offset = QUICK_ENTRYPOINT_OFFSET(pThrowStackOverflow);
- // Restore stack alignment
- int r_tgt = 0;
- const int spill_size = (num_core_spills_ + num_fp_spills_) * 4;
- if (target_x86) {
- // - 4 to leave link register on stack.
- OpRegImm(kOpAdd, TargetReg(kSp), frame_size_ - 4);
- ClobberCallerSave();
- } else if (target_arm) {
- r_tgt = r12;
- LoadWordDisp(TargetReg(kSp), spill_size - 4, TargetReg(kLr));
- OpRegImm(kOpAdd, TargetReg(kSp), spill_size);
- ClobberCallerSave();
- LoadWordDisp(rARM_SELF, func_offset.Int32Value(), r_tgt);
- } else {
- DCHECK(target_mips);
- DCHECK_EQ(num_fp_spills_, 0); // FP spills currently don't happen on mips.
- // LR is offset 0 since we push in reverse order.
- LoadWordDisp(TargetReg(kSp), 0, TargetReg(kLr));
- OpRegImm(kOpAdd, TargetReg(kSp), spill_size);
- ClobberCallerSave();
- r_tgt = CallHelperSetup(func_offset); // Doesn't clobber LR.
- DCHECK_NE(r_tgt, TargetReg(kLr));
- }
- CallHelper(r_tgt, func_offset, false /* MarkSafepointPC */, false /* UseLink */);
- continue;
- }
default:
LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0];
}
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index 234299e472..95fd6e7941 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -317,12 +317,36 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method)
SpillCoreRegs();
/* NOTE: promotion of FP regs currently unsupported, thus no FP spill */
DCHECK_EQ(num_fp_spills_, 0);
+ const int frame_sub = frame_size_ - spill_count * 4;
if (!skip_overflow_check) {
- OpRegRegImm(kOpSub, new_sp, rMIPS_SP, frame_size_ - (spill_count * 4));
- GenRegRegCheck(kCondUlt, new_sp, check_reg, kThrowStackOverflow);
+ class StackOverflowSlowPath : public LIRSlowPath {
+ public:
+ StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace)
+ : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, nullptr), sp_displace_(sp_displace) {
+ }
+ void Compile() OVERRIDE {
+ m2l_->ResetRegPool();
+ m2l_->ResetDefTracking();
+ GenerateTargetLabel();
+ // LR is offset 0 since we push in reverse order.
+ m2l_->LoadWordDisp(kMipsRegSP, 0, kMipsRegLR);
+ m2l_->OpRegImm(kOpAdd, kMipsRegSP, sp_displace_);
+ m2l_->ClobberCallerSave();
+ ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pThrowStackOverflow);
+ int r_tgt = m2l_->CallHelperSetup(func_offset); // Doesn't clobber LR.
+ m2l_->CallHelper(r_tgt, func_offset, false /* MarkSafepointPC */, false /* UseLink */);
+ }
+
+ private:
+ const size_t sp_displace_;
+ };
+ OpRegRegImm(kOpSub, new_sp, rMIPS_SP, frame_sub);
+ LIR* branch = OpCmpBranch(kCondUlt, new_sp, check_reg, nullptr);
+ AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, spill_count * 4));
+ // TODO: avoid copy for small frame sizes.
OpRegCopy(rMIPS_SP, new_sp); // Establish stack
} else {
- OpRegImm(kOpSub, rMIPS_SP, frame_size_ - (spill_count * 4));
+ OpRegImm(kOpSub, rMIPS_SP, frame_sub);
}
FlushIns(ArgLocs, rl_method);
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 72fc92214d..68e2b6de21 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -213,12 +213,37 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
/* NOTE: promotion of FP regs currently unsupported, thus no FP spill */
DCHECK_EQ(num_fp_spills_, 0);
if (!skip_overflow_check) {
+ class StackOverflowSlowPath : public LIRSlowPath {
+ public:
+ StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace)
+ : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, nullptr), sp_displace_(sp_displace) {
+ }
+ void Compile() OVERRIDE {
+ m2l_->ResetRegPool();
+ m2l_->ResetDefTracking();
+ GenerateTargetLabel();
+ m2l_->OpRegImm(kOpAdd, kX86RegSP, sp_displace_);
+ m2l_->ClobberCallerSave();
+ ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pThrowStackOverflow);
+ // Assumes codegen and target are in thumb2 mode.
+ m2l_->CallHelper(0, func_offset, false /* MarkSafepointPC */, false /* UseLink */);
+ }
+
+ private:
+ const size_t sp_displace_;
+ };
+ // TODO: for large frames we should do something like:
+ // spill ebp
+ // lea ebp, [esp + frame_size]
+ // cmp ebp, fs:[stack_end_]
+ // jcc stack_overflow_exception
+ // mov esp, ebp
+ // in case a signal comes in that's not using an alternate signal stack and the large frame may
+ // have moved us outside of the reserved area at the end of the stack.
// cmp rX86_SP, fs:[stack_end_]; jcc throw_launchpad
- LIR* tgt = RawLIR(0, kPseudoThrowTarget, kThrowStackOverflow, 0, 0, 0, 0);
OpRegThreadMem(kOpCmp, rX86_SP, Thread::StackEndOffset());
- OpCondBranch(kCondUlt, tgt);
- // Remember branch target - will process later
- throw_launchpads_.Insert(tgt);
+ LIR* branch = OpCondBranch(kCondUlt, nullptr);
+ AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, frame_size_ - 4));
}
FlushIns(ArgLocs, rl_method);
diff --git a/runtime/thread.h b/runtime/thread.h
index fdf976d2bd..6cbd3d9664 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -101,6 +101,12 @@ class PACKED(4) Thread {
#else
static constexpr size_t kStackOverflowReservedBytes = 16 * KB;
#endif
+ // How much of the reserved bytes is reserved for incoming signals.
+ static constexpr size_t kStackOverflowSignalReservedBytes = 2 * KB;
+ // How much of the reserved bytes we may temporarily use during stack overflow checks as an
+ // optimization.
+ static constexpr size_t kStackOverflowReservedUsableBytes =
+ kStackOverflowReservedBytes - kStackOverflowSignalReservedBytes;
// Creates a new native thread corresponding to the given managed peer.
// Used to implement Thread.start.