OSDN Git Service

Optimize stack overflow handling.
authorMathieu Chartier <mathieuc@google.com>
Wed, 19 Mar 2014 17:17:28 +0000 (10:17 -0700)
committerMathieu Chartier <mathieuc@google.com>
Wed, 19 Mar 2014 22:12:04 +0000 (15:12 -0700)
We now subtract the frame size from the stack pointer for methods
which have a frame smaller than a certain size. Also changed code to
use slow paths instead of launchpads.

Delete kStackOverflow launchpad since it is no longer needed.

ARM optimizations:
One less move per stack overflow check (without fault handler for
stack overflows). Use ldr pc instead of ldr r12, b r12.
Code size (boot.oat):
Before: 58405348
After: 57803236

TODO: X86 doesn't have the case for large frames. This could case an
incoming signal to go past the end of the stack (unlikely however).

Change-Id: Ie3a5635cd6fb09de27960e1f8cee45bfae38fb33

compiler/dex/compiler_enums.h
compiler/dex/quick/arm/call_arm.cc
compiler/dex/quick/gen_common.cc
compiler/dex/quick/mips/call_mips.cc
compiler/dex/quick/x86/call_x86.cc
runtime/thread.h

index 147e840..718468f 100644 (file)
@@ -328,7 +328,6 @@ enum ThrowKind {
   kThrowArrayBounds,
   kThrowConstantArrayBounds,
   kThrowNoSuchMethod,
-  kThrowStackOverflow,
 };
 
 enum DividePattern {
index bba3d40..94f0ca4 100644 (file)
@@ -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);
index 71cc0d9..8c3a11f 100644 (file)
@@ -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];
     }
index 234299e..95fd6e7 100644 (file)
@@ -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);
index 72fc922..68e2b6d 100644 (file)
@@ -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);
index fdf976d..6cbd3d9 100644 (file)
@@ -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.