about summary refs log tree commit diff
path: root/src/rt
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-10-19 09:46:18 -0700
committerbors <bors@rust-lang.org>2013-10-19 09:46:18 -0700
commit31a209ca4251126dc03cfbb9f4bbb54f9d296d5d (patch)
treeb31873267870fb2aaf597d1e9bfdd88448e3890a /src/rt
parent5751794d97941176571bdf7c16f909ac9845520c (diff)
parent6d8330afb6c925d1092f27919f61d4ce6a3fb1d4 (diff)
downloadrust-31a209ca4251126dc03cfbb9f4bbb54f9d296d5d.tar.gz
rust-31a209ca4251126dc03cfbb9f4bbb54f9d296d5d.zip
auto merge of #9834 : alexcrichton/rust/morestack, r=brson
This commit re-introduces the functionality of __morestack in a way that it was
not originally anticipated. Rust does not currently have segmented stacks,
rather just large stack segments. We do not detect when these stack segments are
overrun currently, but this commit leverages __morestack in order to check this.

This commit purges a lot of the old __morestack and stack limit C++
functionality, migrating the necessary chunks to rust. The stack limit is now
entirely maintained in rust, and the "main logic bits" of __morestack are now
also implemented in rust as well.

I put my best effort into validating that this currently builds and runs successfully on osx and linux 32/64 bit, but I was unable to get this working on windows. We never did have unwinding through __morestack frames, and although I tried poking at it for a bit, I was unable to understand why we don't get unwinding right now.

A focus of this commit is to implement as much of the logic in rust as possible. This involved some liberal usage of `no_split_stack` in various locations, along with some use of the `asm!` macro (scary). I modified a bit of C++ to stop calling `record_sp_limit` because this is no longer defined in C++, rather in rust.

Another consequence of this commit is that `thread_local_storage::{get, set}` must both be flagged with `#[rust_stack]`. I've briefly looked at the implementations on osx/linux/windows to ensure that they're pretty small stacks, and I'm pretty sure that they're definitely less than 20K stacks, so we probably don't have a lot to worry about.

Other things worthy of note:
* The default stack size is now 4MB instead of 2MB. This is so that when we request 2MB to call a C function you don't immediately overflow because you have consumed any stack at all.
* `asm!` is actually pretty cool, maybe we could actually define context switching with it?
* I wanted to add links to the internet about all this jazz of storing information in TLS, but I was only able to find a link for the windows implementation. Otherwise my suggestion is just "disassemble on that arch and see what happens"
* I put my best effort forward on arm/mips to tweak __morestack correctly, we have no ability to test this so an extra set of eyes would be useful on these spots.
* This is all really tricky stuff, so I tried to put as many comments as I thought were necessary, but if anything is still unclear (or I completely forgot to take something into account), I'm willing to write more!
Diffstat (limited to 'src/rt')
-rw-r--r--src/rt/arch/arm/ccall.S26
-rw-r--r--src/rt/arch/arm/context.cpp39
-rw-r--r--src/rt/arch/arm/context.h44
-rw-r--r--src/rt/arch/arm/gpr.cpp16
-rw-r--r--src/rt/arch/arm/gpr.h23
-rw-r--r--src/rt/arch/arm/morestack.S41
-rw-r--r--src/rt/arch/arm/record_sp.S5
-rw-r--r--src/rt/arch/arm/regs.h21
-rw-r--r--src/rt/arch/arm/sp.h29
-rw-r--r--src/rt/arch/i386/ccall.S52
-rw-r--r--src/rt/arch/i386/context.cpp82
-rw-r--r--src/rt/arch/i386/context.h53
-rw-r--r--src/rt/arch/i386/gpr.cpp22
-rw-r--r--src/rt/arch/i386/gpr.h31
-rw-r--r--src/rt/arch/i386/morestack.S174
-rw-r--r--src/rt/arch/i386/regs.h12
-rw-r--r--src/rt/arch/i386/sp.h71
-rw-r--r--src/rt/arch/mips/ccall.S42
-rw-r--r--src/rt/arch/mips/context.cpp49
-rw-r--r--src/rt/arch/mips/context.h51
-rw-r--r--src/rt/arch/mips/gpr.cpp31
-rw-r--r--src/rt/arch/mips/gpr.h32
-rw-r--r--src/rt/arch/mips/morestack.S55
-rw-r--r--src/rt/arch/mips/record_sp.S12
-rw-r--r--src/rt/arch/mips/regs.h18
-rw-r--r--src/rt/arch/mips/sp.h29
-rw-r--r--src/rt/arch/x86_64/_context.S12
-rw-r--r--src/rt/arch/x86_64/ccall.S59
-rw-r--r--src/rt/arch/x86_64/context.cpp45
-rw-r--r--src/rt/arch/x86_64/context.h52
-rw-r--r--src/rt/arch/x86_64/gpr.cpp24
-rw-r--r--src/rt/arch/x86_64/gpr.h32
-rw-r--r--src/rt/arch/x86_64/morestack.S98
-rw-r--r--src/rt/arch/x86_64/regs.h2
-rw-r--r--src/rt/arch/x86_64/sp.h79
-rw-r--r--src/rt/rust_builtin.cpp25
-rw-r--r--src/rt/rust_gpr_base.h33
-rw-r--r--src/rt/rust_test_helpers.cpp1
-rw-r--r--src/rt/rustrt.def.in1
-rw-r--r--src/rt/sync/rust_thread.cpp9
-rw-r--r--src/rt/sync/rust_thread.h2
41 files changed, 70 insertions, 1464 deletions
diff --git a/src/rt/arch/arm/ccall.S b/src/rt/arch/arm/ccall.S
deleted file mode 100644
index 3350a040f53..00000000000
--- a/src/rt/arch/arm/ccall.S
+++ /dev/null
@@ -1,26 +0,0 @@
-// Mark stack as non-executable
-#if defined(__linux__) && defined(__ELF__)
-.section	.note.GNU-stack, "", %progbits
-#endif
-
-.text
-.code 32
-.arm
-.align
-
-.globl __morestack
-.hidden __morestack
-.type __morestack, %function
-__morestack:
-	.fnstart
-	.save {r4, fp, lr}
-	push {r4, fp, lr}
-    .movsp r4
-	mov r4, sp
-	mov sp, r2
-	mov fp, sp
-	blx r1
-	mov sp, r4
-	pop {r4, fp, lr}
-	mov pc, lr
-	.fnend
diff --git a/src/rt/arch/arm/context.cpp b/src/rt/arch/arm/context.cpp
deleted file mode 100644
index 7d90668aad5..00000000000
--- a/src/rt/arch/arm/context.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// xfail-license
-
-#include "context.h"
-#include "../../rust_globals.h"
-
-extern "C" void CDECL swap_registers(registers_t *oregs,
-                                     registers_t *regs)
-asm ("swap_registers");
-
-context::context()
-{
-    assert((void*)&regs == (void*)this);
-    memset(&regs, 0, sizeof(regs));
-}
-
-void context::swap(context &out)
-{
-    swap_registers(&out.regs, &regs);
-}
-
-void context::call(void *f, void *arg, void *stack)
-{
-  // Get the current context, which we will then modify to call the
-  // given function.
-  swap(*this);
-
-  // set up the stack
-  uint32_t *sp = ( uint32_t *)stack;
-  sp = align_down(sp);
-  // The final return address. 0 indicates the bottom of the stack
-  // sp of arm eabi is 8-byte aligned
-  sp -= 2;
-  *sp = 0;
-
-  regs.data[0] = ( uint32_t )arg; // r0
-  regs.data[13] = ( uint32_t )sp; //#52 sp, r13
-  regs.data[14] = ( uint32_t )f;  //#60 pc, r15 --> lr,
-  // Last base pointer on the stack should be 0
-}
diff --git a/src/rt/arch/arm/context.h b/src/rt/arch/arm/context.h
deleted file mode 100644
index 54f0df7de31..00000000000
--- a/src/rt/arch/arm/context.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// -*- mode: c++ -*-
-// xfail-license
-
-#ifndef CONTEXT_H
-#define CONTEXT_H
-
-#include <cstdlib>
-#include <inttypes.h>
-#include <stdint.h>
-//#include <xmmintrin.h>
-
-#include "vg/memcheck.h"
-
-template<typename T>
-T align_down(T sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return (T)((uint32_t)sp & ~(16 - 1));
-}
-
-// The struct in which we store the saved data.  This is mostly the
-// volatile registers and instruction pointer, but it also includes
-// RCX/RDI which are used to pass arguments.  The indices for each
-// register are found in "regs.h".  Note that the alignment must be
-// 16 bytes so that SSE instructions can be used.
-#include "regs.h"
-struct registers_t {
-    uint32_t data[RUSTRT_MAX];
-} __attribute__((aligned(16)));
-
-class context {
-public:
-    registers_t regs;
-
-    context();
-
-    context *next;
-
-    void swap(context &out);
-    void call(void *f, void *arg, void *sp);
-};
-
-#endif
diff --git a/src/rt/arch/arm/gpr.cpp b/src/rt/arch/arm/gpr.cpp
deleted file mode 100644
index 77ec9d5182a..00000000000
--- a/src/rt/arch/arm/gpr.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// xfail-license
-
-#include "gpr.h"
-
-#define LOAD(rn) do { \
-    uintptr_t tmp; \
-    asm("mov %%" #rn ",%0" : "=r" (tmp) :); \
-    this->rn = tmp; \
-} while (0)
-
-void rust_gpr::load() {
-    LOAD(r0); LOAD(r1); LOAD(r2); LOAD(r3);
-    LOAD(r4); LOAD(r5); LOAD(r6); LOAD(r7);
-    LOAD(r8);  LOAD(r9);  LOAD(r10); LOAD(r11);
-    LOAD(r12); LOAD(r13); LOAD(r14); LOAD(r15);
-}
diff --git a/src/rt/arch/arm/gpr.h b/src/rt/arch/arm/gpr.h
deleted file mode 100644
index c8a3e916a37..00000000000
--- a/src/rt/arch/arm/gpr.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// xfail-license
-// General-purpose registers. This structure is used during stack crawling.
-
-#ifndef GPR_H
-#define GPR_H
-
-#include "rust_gpr_base.h"
-
-class rust_gpr : public rust_gpr_base {
-public:
-    uintptr_t r0, r1, r2, r3, r4, r5, r6, r7;
-    uintptr_t  r8,  r9, r10, r11, r12, r13, r14, r15;
-
-    inline uintptr_t get_fp() { return r11; }
-    inline uintptr_t get_ip() { return r12; }
-
-    inline void set_fp(uintptr_t new_fp) { r11 = new_fp; }
-    inline void set_ip(uintptr_t new_ip) { r12 = new_ip; }
-
-    void load();
-};
-
-#endif
diff --git a/src/rt/arch/arm/morestack.S b/src/rt/arch/arm/morestack.S
index f0ec3f4b7a5..219f0962d77 100644
--- a/src/rt/arch/arm/morestack.S
+++ b/src/rt/arch/arm/morestack.S
@@ -3,13 +3,14 @@
 .section	.note.GNU-stack, "", %progbits
 #endif
 
+/* See i386/morestack.S for the lengthy, general explanation. */
+
 .text
 .code 32
 .arm
 .align
 
-.global upcall_new_stack
-.global upcall_del_stack
+.global rust_stack_exhausted
 .global __morestack
 .hidden __morestack
 
@@ -32,40 +33,8 @@ __morestack:
     // Save argument registers of the original function
     push {r0, r1, r2, r3, lr}
 
-    mov r0, r4         // The amount of stack needed
-    add r1, fp, #20    // Address of stack arguments
-    mov r2, r5         // Size of stack arguments
-
     // Create new stack
-    bl upcall_new_stack@plt
-
-    // Hold new stack pointer
-    mov r5, r0
-
-    // Pop the saved arguments
-    pop {r0, r1, r2, r3, lr}
-
-    // Grab the return pointer
-    add r4, lr, #16    // Skip past the return
-    mov sp, r5         // Swich to the new stack
-    mov lr, pc
-    mov pc, r4         // Call the original function
-
-    // Switch back to rust stack
-    mov sp, r6
-
-    // Save return value
-	mov r4, r0
-	mov r5, r1
-
-    // Remove the new allocated stack
-    bl upcall_del_stack@plt
-
-    // Restore return value
-	mov r0, r4
-	mov r1, r5
+    bl rust_stack_exhausted@plt
 
-    // Return
-    pop {r6, fp, lr}
-    mov pc, lr
+    // the above function ensures that it never returns
     .fnend
diff --git a/src/rt/arch/arm/record_sp.S b/src/rt/arch/arm/record_sp.S
index 3c5c7644beb..6900444c0fe 100644
--- a/src/rt/arch/arm/record_sp.S
+++ b/src/rt/arch/arm/record_sp.S
@@ -11,7 +11,6 @@
 
 .globl record_sp_limit
 .globl get_sp_limit
-.globl get_sp
 
 record_sp_limit:
 	// First, try to read TLS address from coprocessor
@@ -46,7 +45,3 @@ get_sp_limit:
 
 	ldr r0, [r3]
 	mov pc, lr
-
-get_sp:
-	mov r0, sp
-	mov pc, lr
diff --git a/src/rt/arch/arm/regs.h b/src/rt/arch/arm/regs.h
deleted file mode 100644
index 0d1c24e0fb7..00000000000
--- a/src/rt/arch/arm/regs.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// xfail-license
-
-#define RUSTRT_RBX   0
-#define RUSTRT_RSP   1
-#define RUSTRT_RBP   2
-// RCX on Windows, RDI elsewhere
-#define RUSTRT_ARG0  3
-#define RUSTRT_R12   4
-#define RUSTRT_R13   5
-#define RUSTRT_R14   6
-#define RUSTRT_R15   7
-#define RUSTRT_IP    8
-
-#define RUSTRT_MAX  32
-
-// ARG0 is the register in which the first argument goes.
-// Naturally this depends on your operating system.
-#   define RUSTRT_ARG0_S r0
-#   define RUSTRT_ARG1_S r1
-#   define RUSTRT_ARG2_S r2
-#   define RUSTRT_ARG3_S r3
diff --git a/src/rt/arch/arm/sp.h b/src/rt/arch/arm/sp.h
deleted file mode 100644
index cd798847607..00000000000
--- a/src/rt/arch/arm/sp.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Getting the stack pointer and getting/setting sp limit.
-
-#ifndef SP_H
-#define SP_H
-
-#include "../../rust_globals.h"
-
-// Gets a pointer to the vicinity of the current stack pointer
-extern "C" uintptr_t get_sp();
-
-// Gets the pointer to the end of the Rust stack from a platform-
-// specific location in the thread control block
-extern "C" CDECL uintptr_t get_sp_limit();
-
-// Records the pointer to the end of the Rust stack in a platform-
-// specific location in the thread control block
-extern "C" CDECL void record_sp_limit(void *limit);
-
-#endif
diff --git a/src/rt/arch/i386/ccall.S b/src/rt/arch/i386/ccall.S
deleted file mode 100644
index eeee7a4e715..00000000000
--- a/src/rt/arch/i386/ccall.S
+++ /dev/null
@@ -1,52 +0,0 @@
-// Mark stack as non-executable
-#if defined(__linux__) && defined(__ELF__)
-.section	.note.GNU-stack, "", @progbits
-#endif
-
-/*
-	The function for switching to the C stack.  It is called
-	__morestack because gdb allows any frame with that name to
-	move the stack pointer to a different stack, which it usually
-	considers an error.
-*/
-
-	.text
-
-#if defined(__APPLE__) || defined(__WIN32__)
-.globl ___morestack
-___morestack:
-#else
-.globl __morestack
-.hidden __morestack
-__morestack:
-#endif
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
-	.cfi_startproc
-#endif
-
-	pushl %ebp
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
-	.cfi_def_cfa_offset 8
-	.cfi_offset %ebp, -8
-#endif
-
-	movl %esp,%ebp          // save esp
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
-	.cfi_def_cfa_register %ebp
-#endif
-
-	movl 16(%ebp),%esp      // load new esp
-	subl $12,%esp           // maintain 16-byte alignment
-	pushl 8(%ebp)           // push ptr to argument block
-	calll *12(%ebp)
-	movl %ebp,%esp          // would like to use "leave" but it's slower
-	popl %ebp
-
-	ret
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
-	.cfi_endproc
-#endif
diff --git a/src/rt/arch/i386/context.cpp b/src/rt/arch/i386/context.cpp
deleted file mode 100644
index 94e6f0418d0..00000000000
--- a/src/rt/arch/i386/context.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
-#include "context.h"
-#include "../../rust_globals.h"
-
-extern "C" uint32_t CDECL swap_registers(registers_t *oregs,
-                                         registers_t *regs);
-
-context::context()
-{
-    assert((void*)&regs == (void*)this);
-}
-
-void context::swap(context &out)
-{
-  swap_registers(&out.regs, &regs);
-}
-
-void context::call(void *f, void *arg, void *stack) {
-  // Get the current context, which we will then modify to call the
-  // given function.
-  swap(*this);
-
-  // set up the trampoline frame
-  uint32_t *sp = (uint32_t *)stack;
-
-  // Shift the stack pointer so the alignment works out right.
-  sp = align_down(sp) - 3;
-  *--sp = (uint32_t)arg;
-  // The final return address. 0 indicates the bottom of the stack
-  *--sp = 0;
-
-  regs.esp = (uint32_t)sp;
-  regs.eip = (uint32_t)f;
-
-  // Last base pointer on the stack should be 0
-  regs.ebp = 0;
-}
-
-#if 0
-// This is some useful code to check how the registers struct got
-// layed out in memory.
-int main() {
-  registers_t regs;
-
-  printf("Register offsets\n");
-
-#define REG(r) \
-  printf("  %6s: +%ld\n", #r, (intptr_t)&regs.r - (intptr_t)&regs);
-
-  REG(eax);
-  REG(ebx);
-  REG(ecx);
-  REG(edx);
-  REG(ebp);
-  REG(esi);
-  REG(edi);
-  REG(esp);
-
-  REG(cs);
-  REG(ds);
-  REG(ss);
-  REG(es);
-  REG(fs);
-  REG(gs);
-
-  REG(eflags);
-
-  REG(eip);
-
-  return 0;
-}
-#endif
diff --git a/src/rt/arch/i386/context.h b/src/rt/arch/i386/context.h
deleted file mode 100644
index 33352b4a556..00000000000
--- a/src/rt/arch/i386/context.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
-#ifndef CONTEXT_H
-#define CONTEXT_H
-
-#include <cstdlib>
-#include <inttypes.h>
-#include <stdint.h>
-
-#include "vg/memcheck.h"
-
-template<typename T>
-T align_down(T sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return (T)((uint32_t)sp & ~(16 - 1));
-}
-
-struct registers_t {
-  // general purpose registers
-  uint32_t eax, ebx, ecx, edx, ebp, esi, edi, esp;
-
-  // segment registers
-  uint16_t cs, ds, ss, es, fs, gs;
-
-  uint32_t eflags;
-
-  uint32_t eip;
-} __attribute__((aligned(16)));
-
-class context {
-public:
-  registers_t regs;
-
-  context();
-
-  context *next;
-
-  void swap(context &out);
-  void call(void *f, void *arg, void *sp);
-};
-
-#endif
diff --git a/src/rt/arch/i386/gpr.cpp b/src/rt/arch/i386/gpr.cpp
deleted file mode 100644
index e5a59d664b0..00000000000
--- a/src/rt/arch/i386/gpr.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#include "gpr.h"
-
-#define LOAD(rn) do { \
-    uintptr_t tmp; \
-    asm("movl %%" #rn ",%0" : "=r" (tmp) :); \
-    this->rn = tmp; \
-} while (0)
-
-void rust_gpr::load() {
-    LOAD(eax); LOAD(ebx); LOAD(ecx); LOAD(edx);
-    LOAD(esi); LOAD(edi); LOAD(ebp); LOAD(esi);
-}
diff --git a/src/rt/arch/i386/gpr.h b/src/rt/arch/i386/gpr.h
deleted file mode 100644
index 1953170301c..00000000000
--- a/src/rt/arch/i386/gpr.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// General-purpose registers. This structure is used during stack crawling.
-
-#ifndef GPR_H
-#define GPR_H
-
-#include "rust_gpr_base.h"
-
-class rust_gpr : public rust_gpr_base {
-public:
-    uintptr_t eax, ebx, ecx, edx, esi, edi, ebp, eip;
-
-    inline uintptr_t get_fp() { return ebp; }
-    inline uintptr_t get_ip() { return eip; }
-
-    inline void set_fp(uintptr_t new_fp) { ebp = new_fp; }
-    inline void set_ip(uintptr_t new_ip) { eip = new_ip; }
-
-    void load();
-};
-
-#endif
diff --git a/src/rt/arch/i386/morestack.S b/src/rt/arch/i386/morestack.S
index 9598f14579f..25f907f479c 100644
--- a/src/rt/arch/i386/morestack.S
+++ b/src/rt/arch/i386/morestack.S
@@ -6,29 +6,27 @@
 /*
 	__morestack
 
-	This function implements stack growth using the mechanism
-	devised by Ian Lance Taylor for gccgo, described here:
+        This function is normally used to implement stack growth using the
+        mechanism devised by Ian Lance Taylor for gccgo, described here:
 
 	http://gcc.gnu.org/wiki/SplitStacks
 
-	The Rust stack is composed of a linked list of stack segments,
-	and each stack segment contains two parts: the work area,
-	where Rust functions are allowed to execute; and the red zone,
-	where no Rust code can execute, but where short runtime
-	functions (including __morestack), the dynamic linker, signal
-	handlers, and the unwinder can run.
+        Each Rust function contains an LLVM-generated prologue that compares the
+        stack space required for the current function to the space remaining in
+        the current stack segment, maintained in a platform-specific TLS slot.
+        The stack limit is strategically maintained by the Rust runtime so that
+        it is always in place whenever a Rust function is running.
 
-	Each Rust function contains an LLVM-generated prologue that
-	compares the stack space required for the current function to
-	the space remaining in the current stack segment,
-	maintained in a platform-specific TLS slot.  The stack limit
-	is strategically maintained by the Rust runtime so that it is
-	always in place whenever a Rust function is running.
+        In Rust, however, we currently do not use __morestack for stack growth
+        purposes.  Rather each task has one large stack segment. When this
+        __morestack function is run, we interpret this as a "stack overflow"
+        event rather than an event requiring an allocation of a new stack.
 
-	When there is not enough room to run the function, the function
-	prologue makes a call to __morestack to allocate a new stack
-	segment, copy any stack-based arguments to it, switch stacks,
-	then resume execution of the original function.
+        In the early days, this implementation did indeed have all of the fiddly
+        bits in order to manage split stacks in the sense of always growing
+        stacks. For posterity, the implementation can be found at commit
+        c8e77d5586aed50821e0b9361b2e24c96ade816c if we ever need to refer back
+        to it.
 
 	-- The __morestack calling convention --
 
@@ -72,30 +70,20 @@
 .text
 
 #if defined(__APPLE__)
-#define RUST_GET_TASK           L_rust_get_task$stub
-#define UPCALL_NEW_STACK        L_upcall_new_stack$stub
-#define UPCALL_DEL_STACK        L_upcall_del_stack$stub
 #define MORESTACK               ___morestack
+#define EXHAUSTED               _rust_stack_exhausted
 #else
 #if defined(__linux__) || defined(__FreeBSD__)
-#define UPCALL_NEW_STACK        upcall_new_stack
-#define UPCALL_DEL_STACK        upcall_del_stack
-#define RUST_GET_TASK           rust_get_task
 #define MORESTACK               __morestack
+#define EXHAUSTED               rust_stack_exhausted
 #else
-#define UPCALL_NEW_STACK        _upcall_new_stack
-#define UPCALL_DEL_STACK        _upcall_del_stack
-#define RUST_GET_TASK           _rust_get_task
 #define MORESTACK               ___morestack
+#define EXHAUSTED               _rust_stack_exhausted
 #endif
 #endif
 
-#ifndef __APPLE__
-.globl UPCALL_NEW_STACK
-.globl UPCALL_DEL_STACK
-.globl RUST_GET_TASK
-#endif
 .globl MORESTACK
+.globl EXHAUSTED
 
 // FIXME: What about __WIN32__?
 #if defined(__linux__) || defined(__FreeBSD__)
@@ -111,9 +99,7 @@
 #endif
 
 MORESTACK:
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
 	.cfi_startproc
-#endif
 
 	// This base pointer setup differs from most in that we are
 	// telling the unwinder to consider the Canonical Frame
@@ -129,129 +115,21 @@ MORESTACK:
 	// would normally be, accounting for the two arguments to
 	// __morestack, and an extra return address.
 
+        // FIXME(#9854) these cfi directives don't work on windows.
+
 	pushl %ebp
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
 	// The CFA is 20 bytes above the register that it is
 	// associated with for this frame (which will be %ebp)
 	.cfi_def_cfa_offset 20
 	// %ebp is -20 bytes from the CFA
 	.cfi_offset %ebp, -20
-#endif
 	movl %esp, %ebp
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
 	// Calculate the CFA as an offset from %ebp
 	.cfi_def_cfa_register %ebp
-#endif
-
-	// NB: This can be called with the fastcc convention so we
-	// have to preserve any argument registers
-
-	// NB: __morestack is called misaligned by 4 bytes, i.e.
-	// subl $4, %esp would get us to a normal alignment
-
-	subl $28,%esp
-
-	// Save fastcc arguments
-	movl %ecx, 16(%esp)
-	movl %edx, 12(%esp)
-
-	// FIXME (1388): it's possible we also need to save/restore some
-	// SSE2 registers here, if floats-go-in-regs on x86+SSE2. Unclear.
-
-	// FIXME (1226): main is compiled with the split-stack prologue,
-	// causing it to call __morestack, so we have to jump back out
-	calll RUST_GET_TASK
-	testl %eax,%eax
-	jz .L$bail
-
-	// The arguments to upcall_new_stack
-
-	// The size of the stack arguments to copy to the new stack,
-	// and of the arguments to __morestack
-	movl 40(%esp),%eax
-	movl %eax,8(%esp)
-	// The address of the stack arguments to the original function
-	leal 48(%esp),%eax
-	movl %eax,4(%esp)
-	// The amount of stack needed for the original function,
-	// the other argument to __morestack
-	movl 36(%esp),%eax // The amount of stack needed
-	movl %eax,(%esp)
-
-	call UPCALL_NEW_STACK
-
-	// Save the address of the new stack
-	movl %eax, (%esp)
-
-	// Grab the __morestack return pointer
-	movl 32(%esp),%eax
-	// Skip past the ret instruction in the parent fn
-	inc  %eax
-
-	// Restore the fastcc arguments to the original function
-	movl 16(%esp), %ecx
-	movl 12(%esp), %edx
-
-        // Switch stacks
-	movl (%esp),%esp
-        // Re-enter the function that called us
-	call *%eax
-
-	// Now the function that called us has returned, so we need to
-	// delete the old stack space
 
-	// Switch back to the rust stack
-	movl %ebp, %esp
+        // re-align the stack
+        subl $12,%esp
+        calll EXHAUSTED
+        // the exhaustion function guarantees that it can't return
 
-	// Realign stack - remember that __morestack was called misaligned
-	subl $12, %esp
-
-	// Save the return value of the function we allocated space for
-	movl %edx, 4(%esp)
-	movl %eax, (%esp)
-
-	call UPCALL_DEL_STACK
-
-	// And restore it
-	movl (%esp), %eax
-	movl 4(%esp), %edx
-
-	addl $12,%esp
-
-	popl %ebp
-
-	retl $8
-
-.L$bail:
-	movl 32(%esp),%eax
-	inc %eax
-
-	addl $44, %esp
-	popl %ebp
-	addl $4+8,%esp
-
-	jmpl *%eax
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
 	.cfi_endproc
-#endif
-
-#ifdef __APPLE__
-
-.section __IMPORT,__jump_table,symbol_stubs,pure_instructions+self_modifying_code,5
-
-	// Linker will replace the hlts (the ascii) with jmp
-L_rust_get_task$stub:
-	.indirect_symbol _rust_get_task
-	.ascii	 "\364\364\364\364\364"
-
-L_upcall_new_stack$stub:
-	.indirect_symbol _upcall_new_stack
-	.ascii	 "\364\364\364\364\364"
-
-L_upcall_del_stack$stub:
-	.indirect_symbol _upcall_del_stack
-	.ascii	 "\364\364\364\364\364"
-
-	.subsections_via_symbols
-#endif
diff --git a/src/rt/arch/i386/regs.h b/src/rt/arch/i386/regs.h
deleted file mode 100644
index 85c02049edd..00000000000
--- a/src/rt/arch/i386/regs.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// This file is not used by i386, but we keep it here so all
-// architectures have the same set of header files.
diff --git a/src/rt/arch/i386/sp.h b/src/rt/arch/i386/sp.h
deleted file mode 100644
index 4f4c84c8175..00000000000
--- a/src/rt/arch/i386/sp.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Getting the stack pointer and getting/setting sp limit.
-
-#ifndef SP_H
-#define SP_H
-
-#include "../../rust_globals.h"
-
-// Gets a pointer to the vicinity of the current stack pointer
-extern "C" ALWAYS_INLINE uintptr_t get_sp() {
-    uintptr_t sp;
-    asm volatile (
-        "movl %%esp, %0"
-        : "=m"(sp));
-    return sp;
-}
-
-// Gets the pointer to the end of the Rust stack from a platform-
-// specific location in the thread control block
-extern "C" CDECL ALWAYS_INLINE uintptr_t get_sp_limit() {
-    uintptr_t limit;
-
-#if defined(__linux__) || defined(__FreeBSD__)
-    asm volatile (
-        "movl %%gs:48, %0"
-        : "=r"(limit));
-#elif defined(__APPLE__)
-    asm volatile (
-        "movl $0x48+90*4, %%ecx\n\t"
-        "movl %%gs:(%%ecx), %0"
-        :  "=r"(limit)
-        :: "ecx");
-#elif defined(_WIN32)
-    asm volatile (
-        "movl %%fs:0x14, %0"
-        : "=r"(limit));
-#endif
-
-    return limit;
-}
-
-// Records the pointer to the end of the Rust stack in a platform-
-// specific location in the thread control block
-extern "C" CDECL ALWAYS_INLINE void record_sp_limit(void *limit) {
-#if defined(__linux__) || defined(__FreeBSD__)
-    asm volatile (
-        "movl %0, %%gs:48"
-        :: "r"(limit));
-#elif defined(__APPLE__)
-    asm volatile (
-        "movl $0x48+90*4, %%eax\n\t"
-        "movl %0, %%gs:(%%eax)"
-        :: "r"(limit)
-        :  "eax");
-#elif defined(_WIN32)
-    asm volatile (
-        "movl %0, %%fs:0x14"
-        :: "r"(limit));
-#endif
-}
-
-#endif
diff --git a/src/rt/arch/mips/ccall.S b/src/rt/arch/mips/ccall.S
deleted file mode 100644
index cdcdc07db55..00000000000
--- a/src/rt/arch/mips/ccall.S
+++ /dev/null
@@ -1,42 +0,0 @@
-// Mark stack as non-executable
-#if defined(__linux__) && defined(__ELF__)
-.section	.note.GNU-stack, "", @progbits
-#endif
-
-.text
-
-.align 2
-.globl __morestack
-.hidden __morestack
-.cfi_startproc
-.set nomips16
-.ent __morestack
-__morestack:
-        .set noreorder
-        .set nomacro
-
-        addiu $29, $29, -8
-        sw $31, 4($29)
-        sw $30, 0($29)
-
-        .cfi_def_cfa_offset 8
-        .cfi_offset 31, -4
-        .cfi_offset 30, -8
-
-        move $30, $29
-        .cfi_def_cfa_register 30
-
-        move $29, $6
-        move $25, $5
-        jalr $25
-        nop
-        move $29, $30
-
-        lw $30, 0($29)
-        lw $31, 4($29)
-        addiu $29, $29, 8
-
-        jr $31
-        nop
-.end __morestack
-.cfi_endproc
diff --git a/src/rt/arch/mips/context.cpp b/src/rt/arch/mips/context.cpp
deleted file mode 100644
index e1e5776bc1a..00000000000
--- a/src/rt/arch/mips/context.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#include "context.h"
-#include "../../rust_globals.h"
-
-extern "C" void CDECL swap_registers(registers_t *oregs,
-                                     registers_t *regs)
-asm ("swap_registers");
-
-context::context()
-{
-    assert((void*)&regs == (void*)this);
-    memset(&regs, 0, sizeof(regs));
-}
-
-void context::swap(context &out)
-{
-    swap_registers(&out.regs, &regs);
-}
-
-void context::call(void *f, void *arg, void *stack)
-{
-  // Get the current context, which we will then modify to call the
-  // given function.
-  swap(*this);
-
-  // set up the stack
-  uint32_t *sp = (uint32_t *)stack;
-  sp = align_down(sp);
-  // The final return address. 0 indicates the bottom of the stack
-  // sp of mips o32 is 8-byte aligned
-  sp -= 2;
-  *sp = 0;
-
-  regs.data[4] = (uint32_t)arg;
-  regs.data[29] = (uint32_t)sp;
-  regs.data[25] = (uint32_t)f;
-  regs.data[31] = (uint32_t)f;
-
-  // Last base pointer on the stack should be 0
-}
diff --git a/src/rt/arch/mips/context.h b/src/rt/arch/mips/context.h
deleted file mode 100644
index 5e2364437ec..00000000000
--- a/src/rt/arch/mips/context.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#ifndef CONTEXT_H
-#define CONTEXT_H
-
-#include <cstdlib>
-#include <inttypes.h>
-#include <stdint.h>
-//#include <xmmintrin.h>
-
-#include "vg/memcheck.h"
-
-template<typename T>
-T align_down(T sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return (T)((uint32_t)sp & ~(16 - 1));
-}
-
-// The struct in which we store the saved data.  This is mostly the
-// volatile registers and instruction pointer, but it also includes
-// RCX/RDI which are used to pass arguments.  The indices for each
-// register are found in "regs.h".  Note that the alignment must be
-// 16 bytes so that SSE instructions can be used.
-#include "regs.h"
-struct registers_t {
-    uint32_t data[RUSTRT_MAX];
-} __attribute__((aligned(16)));
-
-class context {
-public:
-    registers_t regs;
-
-    context();
-
-    context *next;
-
-    void swap(context &out);
-    void call(void *f, void *arg, void *sp);
-};
-
-#endif
diff --git a/src/rt/arch/mips/gpr.cpp b/src/rt/arch/mips/gpr.cpp
deleted file mode 100644
index da2f515999f..00000000000
--- a/src/rt/arch/mips/gpr.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#include "gpr.h"
-
-#define LOAD(n) do { \
-    uintptr_t tmp; \
-    asm(".set noat; move %0, $" #n : "=r" (tmp) :); \
-    this->r##n = tmp; \
-} while (0)
-
-void rust_gpr::load() {
-              LOAD(1); LOAD(2); LOAD(3);
-    LOAD(4); LOAD(5); LOAD(6); LOAD(7);
-
-    LOAD(8); LOAD(9); LOAD(10); LOAD(11);
-    LOAD(12); LOAD(13); LOAD(14); LOAD(15);
-
-    LOAD(16); LOAD(17); LOAD(18); LOAD(19);
-    LOAD(20); LOAD(21); LOAD(22); LOAD(23);
-
-    LOAD(24); LOAD(25); LOAD(26); LOAD(27);
-    LOAD(28); LOAD(29); LOAD(30); LOAD(31);
-}
diff --git a/src/rt/arch/mips/gpr.h b/src/rt/arch/mips/gpr.h
deleted file mode 100644
index b48c1d4e732..00000000000
--- a/src/rt/arch/mips/gpr.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#ifndef GPR_H
-#define GPR_H
-
-#include "rust_gpr_base.h"
-
-class rust_gpr : public rust_gpr_base {
-public:
-    uintptr_t r0, r1, r2, r3, r4, r5, r6, r7;
-    uintptr_t r8,  r9, r10, r11, r12, r13, r14, r15;
-    uintptr_t r16, r17, r18, r19, r20, r21, r22, r23;
-    uintptr_t r24, r25, r26, r27, r28, r29, r30, r31;
-
-    inline uintptr_t get_fp() { return r30; }
-    inline uintptr_t get_ip() { return r31; }
-
-    inline void set_fp(uintptr_t new_fp) { r30 = new_fp; }
-    inline void set_ip(uintptr_t new_ip) { r31 = new_ip; }
-
-    void load();
-};
-
-#endif
diff --git a/src/rt/arch/mips/morestack.S b/src/rt/arch/mips/morestack.S
index e534ac05913..9cb6ece80ce 100644
--- a/src/rt/arch/mips/morestack.S
+++ b/src/rt/arch/mips/morestack.S
@@ -3,10 +3,11 @@
 .section        .note.GNU-stack, "", @progbits
 #endif
 
+/* See i386/morestack.S for the lengthy, general explanation. */
+
 .text
 
-.globl upcall_new_stack
-.globl upcall_del_stack
+.globl rust_stack_exhausted
 .globl __morestack
 
 .hidden __morestack
@@ -18,6 +19,10 @@ __morestack:
         .set noreorder
         .set nomacro
 
+        // n.b. most of this is probably unnecessary. I know very little mips
+        //      assembly, and I didn't have anything to test on, so I wasn't
+        //      brave enough to try to trim this down.
+
         addiu $29, $29, -12
         sw $31, 8($29)
         sw $30, 4($29)
@@ -45,53 +50,11 @@ __morestack:
         move $6, $15     // The amount of stack needed
 
         move $28, $23
-        lw $25, %call16(upcall_new_stack)($23)
-        jalr $25
-        nop
-
-        // Pop the saved arguments
-        lw $4, 16($29)
-        lw $5, 20($29)
-        lw $6, 24($29)
-        lw $7, 28($29)
-        addiu $29, $29, 32
-
-        lw $24, 8($30)     // Grab the return pointer.
-        addiu $24, $24, 12 // Skip past the `lw`, `jr`, `addiu` in our parent frame
-        move $29, $2       // Switch to the new stack.
-
-        // for PIC
-        lw $2, 12($30)
-        lw $25, 16($30)
-
-        move $28, $23
-        jalr $24           // Reenter the caller function
-        nop
-
-        // Switch back to the rust stack
-        move $29, $30
-
-        // Save the return value
-        addiu $29, $29, -24
-        sw $2, 16($29)
-        sw $3, 20($29)
-
-        move $28, $23
-        lw $25, %call16(upcall_del_stack)($23)
+        lw $25, %call16(rust_stack_exhausted)($23)
         jalr $25
         nop
 
-        // Restore the return value
-        lw $2, 16($29)
-        lw $3, 20($29)
-        addiu $29, $29, 24
+        // the above function make sure that we never get here
 
-        lw $31, 8($29)
-        lw $30, 4($29)
-        lw $23, 0($29)
-        addiu $29, $29, 12
-
-        jr $31
-        nop
 .end __morestack
 .cfi_endproc
diff --git a/src/rt/arch/mips/record_sp.S b/src/rt/arch/mips/record_sp.S
index a88fefead04..a6dfa04edbb 100644
--- a/src/rt/arch/mips/record_sp.S
+++ b/src/rt/arch/mips/record_sp.S
@@ -38,15 +38,3 @@ get_sp_limit:
         jr $31
         nop
 .end get_sp_limit
-
-.globl get_sp
-.align 2
-.set nomips16
-.ent get_sp
-get_sp:
-        .set noreorder
-        .set nomacro
-        move $2, $29
-        jr $31
-        nop
-.end get_sp
diff --git a/src/rt/arch/mips/regs.h b/src/rt/arch/mips/regs.h
deleted file mode 100644
index 2f38e1507ad..00000000000
--- a/src/rt/arch/mips/regs.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#define RUSTRT_MAX  32
-
-// ARG0 is the register in which the first argument goes.
-// Naturally this depends on your operating system.
-#define RUSTRT_ARG0_S r4
-#define RUSTRT_ARG1_S r5
-#define RUSTRT_ARG2_S r6
-#define RUSTRT_ARG3_S r7
diff --git a/src/rt/arch/mips/sp.h b/src/rt/arch/mips/sp.h
deleted file mode 100644
index cd798847607..00000000000
--- a/src/rt/arch/mips/sp.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Getting the stack pointer and getting/setting sp limit.
-
-#ifndef SP_H
-#define SP_H
-
-#include "../../rust_globals.h"
-
-// Gets a pointer to the vicinity of the current stack pointer
-extern "C" uintptr_t get_sp();
-
-// Gets the pointer to the end of the Rust stack from a platform-
-// specific location in the thread control block
-extern "C" CDECL uintptr_t get_sp_limit();
-
-// Records the pointer to the end of the Rust stack in a platform-
-// specific location in the thread control block
-extern "C" CDECL void record_sp_limit(void *limit);
-
-#endif
diff --git a/src/rt/arch/x86_64/_context.S b/src/rt/arch/x86_64/_context.S
index 857fe91c914..a53b1c2d737 100644
--- a/src/rt/arch/x86_64/_context.S
+++ b/src/rt/arch/x86_64/_context.S
@@ -89,12 +89,6 @@ SWAP_REGISTERS:
 #if defined(__MINGW32__) || defined(_WINDOWS)
         mov %rdi, (RUSTRT_RDI*8)(ARG0)
         mov %rsi, (RUSTRT_RSI*8)(ARG0)
-
-        // Save stack range
-        mov %gs:0x08, %r8
-        mov %r8, (RUSTRT_ST1*8)(ARG0)
-        mov %gs:0x10, %r9
-        mov %r9, (RUSTRT_ST2*8)(ARG0)
 #endif
 
         // Save 0th argument register:
@@ -134,12 +128,6 @@ SWAP_REGISTERS:
 #if defined(__MINGW32__) || defined(_WINDOWS)
         mov (RUSTRT_RDI*8)(ARG1), %rdi
         mov (RUSTRT_RSI*8)(ARG1), %rsi
-
-        // Restore stack range
-        mov (RUSTRT_ST1*8)(ARG1), %r8
-        mov %r8, %gs:0x08
-        mov (RUSTRT_ST2*8)(ARG1), %r9
-        mov %r9, %gs:0x10
 #endif
 
         // Restore 0th argument register:
diff --git a/src/rt/arch/x86_64/ccall.S b/src/rt/arch/x86_64/ccall.S
deleted file mode 100644
index dbee5bcdc90..00000000000
--- a/src/rt/arch/x86_64/ccall.S
+++ /dev/null
@@ -1,59 +0,0 @@
-// Mark stack as non-executable
-#if defined(__linux__) && defined(__ELF__)
-.section	.note.GNU-stack, "", @progbits
-#endif
-
-/*
-	The function for switching to the C stack.  It is called
-	__morestack because gdb allows any frame with that name to
-	move the stack pointer to a different stack, which it usually
-	considers an error.
-*/
-
-#include "regs.h"
-
-#define ARG0 RUSTRT_ARG0_S
-#define ARG1 RUSTRT_ARG1_S
-#define ARG2 RUSTRT_ARG2_S
-
-        .text
-
-#if defined(__APPLE__)
-.globl ___morestack
-.private_extern MORESTACK
-___morestack:
-#elif defined(_WIN32)
-.globl __morestack
-__morestack:
-#else
-.globl __morestack
-.hidden __morestack
-__morestack:
-#endif
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-	.cfi_startproc
-#endif
-
-	push %rbp
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-	.cfi_def_cfa_offset 16
-	.cfi_offset %rbp, -16
-#endif
-
-	mov %rsp,%rbp          // save rsp
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-	.cfi_def_cfa_register %rbp
-#endif
-
-	mov ARG2,%rsp          // switch stack
-	call *ARG1             // invoke target address
-	mov %rbp,%rsp
-	pop %rbp
-
-	ret
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-	.cfi_endproc
-#endif
diff --git a/src/rt/arch/x86_64/context.cpp b/src/rt/arch/x86_64/context.cpp
deleted file mode 100644
index 6a265dff761..00000000000
--- a/src/rt/arch/x86_64/context.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
-#include "context.h"
-#include "../../rust_globals.h"
-
-extern "C" void CDECL swap_registers(registers_t *oregs,
-                                     registers_t *regs);
-
-context::context()
-{
-    assert((void*)&regs == (void*)this);
-}
-
-void context::swap(context &out)
-{
-    swap_registers(&out.regs, &regs);
-}
-
-void context::call(void *f, void *arg, void *stack) {
-  // Get the current context, which we will then modify to call the
-  // given function.
-  swap(*this);
-
-  // set up the stack
-  uint64_t *sp = (uint64_t *)stack;
-  sp = align_down(sp);
-  // The final return address. 0 indicates the bottom of the stack
-  *--sp = 0;
-
-  regs.data[RUSTRT_ARG0] = (uint64_t)arg;
-  regs.data[RUSTRT_RSP] = (uint64_t)sp;
-  regs.data[RUSTRT_IP] = (uint64_t)f;
-
-  // Last base pointer on the stack should be 0
-  regs.data[RUSTRT_RBP] = 0;
-}
diff --git a/src/rt/arch/x86_64/context.h b/src/rt/arch/x86_64/context.h
deleted file mode 100644
index b768a1fb7b0..00000000000
--- a/src/rt/arch/x86_64/context.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
-#ifndef CONTEXT_H
-#define CONTEXT_H
-
-#include <cstdlib>
-#include <inttypes.h>
-#include <stdint.h>
-#include <xmmintrin.h>
-
-#include "vg/memcheck.h"
-
-template<typename T>
-T align_down(T sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return (T)((uint64_t)sp & ~(16 - 1));
-}
-
-// The struct in which we store the saved data.  This is mostly the
-// volatile registers and instruction pointer, but it also includes
-// RCX/RDI which are used to pass arguments.  The indices for each
-// register are found in "regs.h".  Note that the alignment must be
-// 16 bytes so that SSE instructions can be used.
-#include "regs.h"
-struct registers_t {
-    uint64_t data[RUSTRT_MAX];
-} __attribute__((aligned(16)));
-
-class context {
-public:
-    registers_t regs;
-
-    context();
-
-    context *next;
-
-    void swap(context &out);
-    void call(void *f, void *arg, void *sp);
-};
-
-#endif
diff --git a/src/rt/arch/x86_64/gpr.cpp b/src/rt/arch/x86_64/gpr.cpp
deleted file mode 100644
index 37247d1dfdc..00000000000
--- a/src/rt/arch/x86_64/gpr.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#include "gpr.h"
-
-#define LOAD(rn) do { \
-    uintptr_t tmp; \
-    asm("movq %%" #rn ",%0" : "=r" (tmp) :); \
-    this->rn = tmp; \
-} while (0)
-
-void rust_gpr::load() {
-    LOAD(rax); LOAD(rbx); LOAD(rcx); LOAD(rdx);
-    LOAD(rsi); LOAD(rdi); LOAD(rbp); LOAD(rsi);
-    LOAD(r8);  LOAD(r9);  LOAD(r10); LOAD(r11);
-    LOAD(r12); LOAD(r13); LOAD(r14); LOAD(r15);
-}
diff --git a/src/rt/arch/x86_64/gpr.h b/src/rt/arch/x86_64/gpr.h
deleted file mode 100644
index 18ef77dbba6..00000000000
--- a/src/rt/arch/x86_64/gpr.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// General-purpose registers. This structure is used during stack crawling.
-
-#ifndef GPR_H
-#define GPR_H
-
-#include "rust_gpr_base.h"
-
-class rust_gpr : public rust_gpr_base {
-public:
-    uintptr_t rax, rbx, rcx, rdx, rsi, rdi, rbp, rip;
-    uintptr_t  r8,  r9, r10, r11, r12, r13, r14, r15;
-
-    inline uintptr_t get_fp() { return rbp; }
-    inline uintptr_t get_ip() { return rip; }
-
-    inline void set_fp(uintptr_t new_fp) { rbp = new_fp; }
-    inline void set_ip(uintptr_t new_ip) { rip = new_ip; }
-
-    void load();
-};
-
-#endif
diff --git a/src/rt/arch/x86_64/morestack.S b/src/rt/arch/x86_64/morestack.S
index b718c9121c5..d248d79d121 100644
--- a/src/rt/arch/x86_64/morestack.S
+++ b/src/rt/arch/x86_64/morestack.S
@@ -3,27 +3,23 @@
 .section	.note.GNU-stack, "", @progbits
 #endif
 
-/*
-	__morestack
-
-	See i386/morestack.S for the lengthy, general explanation.
-*/
+/* See i386/morestack.S for the lengthy, general explanation. */
 
 .text
 
 #if defined(__APPLE__)
-#define UPCALL_NEW_STACK        _upcall_new_stack
-#define UPCALL_DEL_STACK        _upcall_del_stack
 #define MORESTACK               ___morestack
 #else
-#define UPCALL_NEW_STACK        upcall_new_stack
-#define UPCALL_DEL_STACK        upcall_del_stack
 #define MORESTACK               __morestack
 #endif
 
-.globl UPCALL_NEW_STACK
-.globl UPCALL_DEL_STACK
-.globl MORESTACK
+#if defined(__APPLE__)
+#define EXHAUSTED               _rust_stack_exhausted
+#elif defined(__linux__) || defined(__FreeBSD__)
+#define EXHAUSTED               rust_stack_exhausted@PLT
+#else
+#define EXHAUSTED               rust_stack_exhausted
+#endif
 
 #if defined(__linux__) || defined(__FreeBSD__)
 	.hidden MORESTACK
@@ -37,8 +33,7 @@
 	.type MORESTACK,@function
 #endif
 
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
+.globl MORESTACK
 MORESTACK:
 	.cfi_startproc
 
@@ -54,77 +49,12 @@ MORESTACK:
 	// Calculate the CFA as on offset from %ebp
 	.cfi_def_cfa_register %rbp
 
-        subq $56, %rsp
-
-	// Save argument registers of the original function
-	movq %rdi,       (%rsp)
-	movq %rsi,      8(%rsp)
-	movq %rdx,     16(%rsp)
-	movq %rcx,     24(%rsp)
-	movq %r8,      32(%rsp)
-	movq %r9,      40(%rsp)
-
-	// Calculate the address of the stack arguments.
-	// We have the base pointer, __morestack's return address,
-	// and __morestack's caller's return address to skip
-	movq %rbp, %rax
-	addq $24, %rax  // Base pointer, return address x2
-
-	// The arguments to __morestack are passed in %r10 & %r11
-
-	movq %r11, %rdx // Size of stack arguments
-	movq %rax, %rsi // Address of stack arguments
-	movq %r10, %rdi // The amount of stack needed
-
-#ifdef __APPLE__
-	call UPCALL_NEW_STACK
-#endif
-#ifdef __linux__
-	call UPCALL_NEW_STACK@PLT
-#endif
-#ifdef __FreeBSD__
-	call UPCALL_NEW_STACK@PLT
-#endif
-
-	// Pop the saved arguments
-	movq      (%rsp), %rdi
-	movq     8(%rsp), %rsi
-	movq    16(%rsp), %rdx
-	movq    24(%rsp), %rcx
-	movq    32(%rsp), %r8
-	movq    40(%rsp), %r9
-
-	addq $56, %rsp
-
-        movq 8(%rbp),%r10       // Grab the return pointer.
-        incq %r10               // Skip past the `ret` in our parent frame
-        movq %rax,%rsp          // Switch to the new stack.
-
-        call *%r10              // Reenter the caller function
-
-	// Switch back to the rust stack
-	movq %rbp, %rsp
-
-	// Save the return value
-	pushq %rax
+        // re-align the stack
+        subq $8, %rsp
 
-#ifdef __APPLE__
-	call UPCALL_DEL_STACK
-#endif
-#ifdef __linux__
-	call UPCALL_DEL_STACK@PLT
-#endif
-#ifdef __FreeBSD__
-	call UPCALL_DEL_STACK@PLT
-#endif
+        // kill this program
+        call EXHAUSTED
 
-	popq %rax // Restore the return value
-	popq %rbp
-	ret
+        // the exhaustion function guarantees that it can't return
 
 	.cfi_endproc
-
-#else
-MORESTACK:
-	ret
-#endif
diff --git a/src/rt/arch/x86_64/regs.h b/src/rt/arch/x86_64/regs.h
index cff47ac378a..25160ca68a6 100644
--- a/src/rt/arch/x86_64/regs.h
+++ b/src/rt/arch/x86_64/regs.h
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// This is loosely kept in sync with src/libstd/rt/context.rs
+
 #define RUSTRT_RBX   0
 #define RUSTRT_RSP   1
 #define RUSTRT_RBP   2
diff --git a/src/rt/arch/x86_64/sp.h b/src/rt/arch/x86_64/sp.h
deleted file mode 100644
index 764927759fe..00000000000
--- a/src/rt/arch/x86_64/sp.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Getting the stack pointer and getting/setting sp limit.
-
-#ifndef SP_H
-#define SP_H
-
-#include "../../rust_globals.h"
-
-// Gets a pointer to the vicinity of the current stack pointer
-extern "C" ALWAYS_INLINE uintptr_t get_sp() {
-    uintptr_t sp;
-    asm volatile (
-        "movq %%rsp, %0"
-        : "=m"(sp));
-    return sp;
-}
-
-// Gets the pointer to the end of the Rust stack from a platform-
-// specific location in the thread control block
-extern "C" CDECL ALWAYS_INLINE uintptr_t get_sp_limit() {
-    uintptr_t limit;
-
-#if defined(__linux__)
-    asm volatile (
-        "movq %%fs:112, %0"
-        : "=r"(limit));
-#elif defined(__APPLE__)
-    asm volatile (
-        "movq $0x60+90*8, %%rsi\n\t"
-        "movq %%gs:(%%rsi), %0"
-        :  "=r"(limit)
-        :: "rsi");
-#elif defined(__FreeBSD__)
-    asm volatile (
-        "movq %%fs:24, %0"
-        : "=r"(limit));
-#elif defined(_WIN64)
-    asm volatile (
-        "movq %%gs:0x28, %0"
-        : "=r"(limit));
-#endif
-
-    return limit;
-}
-
-// Records the pointer to the end of the Rust stack in a platform-
-// specific location in the thread control block
-extern "C" CDECL ALWAYS_INLINE void record_sp_limit(void *limit) {
-#if defined(__linux__)
-    asm volatile (
-        "movq %0, %%fs:112"
-        :: "r"(limit));
-#elif defined(__APPLE__)
-    asm volatile (
-        "movq $0x60+90*8, %%rsi\n\t"
-        "movq %0, %%gs:(%%rsi)"
-        :: "r"(limit)
-        :  "rsi");
-#elif defined(__FreeBSD__)
-    asm volatile (
-        "movq %0, %%fs:24"
-        :: "r"(limit));
-#elif defined(_WIN64)
-    asm volatile (
-        "movq %0, %%gs:0x28"
-        :: "r"(limit));
-#endif
-}
-
-#endif
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index 9750e22e945..755235a9138 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -16,7 +16,6 @@
 #include "memory_region.h"
 #include "boxed_region.h"
 #include "vg/valgrind.h"
-#include "sp.h"
 
 #include <time.h>
 
@@ -340,22 +339,26 @@ rust_unlock_little_lock(lock_and_signal *lock) {
     lock->unlock();
 }
 
+typedef void(startfn)(void*, void*);
+
 class raw_thread: public rust_thread {
 public:
-    fn_env_pair fn;
+    startfn *raw_start;
+    void *rust_fn;
+    void *rust_env;
 
-    raw_thread(fn_env_pair fn) : fn(fn) { }
+    raw_thread(startfn *raw_start, void *rust_fn, void *rust_env)
+        : raw_start(raw_start), rust_fn(rust_fn), rust_env(rust_env) { }
 
     virtual void run() {
-        record_sp_limit(0);
-        fn.f(fn.env, NULL);
+        raw_start(rust_fn, rust_env);
     }
 };
 
 extern "C" raw_thread*
-rust_raw_thread_start(fn_env_pair *fn) {
-    assert(fn);
-    raw_thread *thread = new raw_thread(*fn);
+rust_raw_thread_start(startfn *raw_start, void *rust_start, void *rust_env) {
+    assert(raw_start && rust_start);
+    raw_thread *thread = new raw_thread(raw_start, rust_start, rust_env);
     thread->start();
     return thread;
 }
@@ -552,12 +555,6 @@ rust_get_global_args_ptr() {
     return &global_args_ptr;
 }
 
-// Used by i386 __morestack
-extern "C" CDECL uintptr_t
-rust_get_task() {
-    return 0;
-}
-
 static lock_and_signal env_lock;
 
 extern "C" CDECL void
diff --git a/src/rt/rust_gpr_base.h b/src/rt/rust_gpr_base.h
deleted file mode 100644
index 7ec2dda9cd4..00000000000
--- a/src/rt/rust_gpr_base.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Base class for architecture-specific general-purpose registers. This
-// structure is used during stack crawling.
-
-#ifndef GPR_BASE_H
-#define GPR_BASE_H
-
-#include <stdint.h>
-
-class rust_gpr_base {
-public:
-    // Returns the value of a register by number.
-    inline uintptr_t &get(uint32_t i) {
-        return reinterpret_cast<uintptr_t *>(this)[i];
-    }
-
-    // Sets the value of a register by number.
-    inline void set(uint32_t i, uintptr_t val) {
-        reinterpret_cast<uintptr_t *>(this)[i] = val;
-    }
-};
-
-
-#endif
diff --git a/src/rt/rust_test_helpers.cpp b/src/rt/rust_test_helpers.cpp
index f10a1f36938..3c6e2d68c2f 100644
--- a/src/rt/rust_test_helpers.cpp
+++ b/src/rt/rust_test_helpers.cpp
@@ -11,7 +11,6 @@
 // Helper functions used only in tests
 
 #include "rust_util.h"
-#include "sync/rust_thread.h"
 #include "sync/lock_and_signal.h"
 
 // These functions are used in the unit tests for C ABI calls.
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index fb9934c7601..aa545c3cdba 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -181,7 +181,6 @@ rust_get_global_args_ptr
 rust_take_global_args_lock
 rust_drop_global_args_lock
 rust_get_test_int
-rust_get_task
 rust_uv_get_loop_from_getaddrinfo_req
 rust_uv_spawn
 rust_uv_process_kill
diff --git a/src/rt/sync/rust_thread.cpp b/src/rt/sync/rust_thread.cpp
index 824642fc435..a78153523d2 100644
--- a/src/rt/sync/rust_thread.cpp
+++ b/src/rt/sync/rust_thread.cpp
@@ -14,11 +14,7 @@
 
 const size_t default_stack_sz = 1024*1024;
 
-rust_thread::rust_thread() : thread(0), stack_sz(default_stack_sz) {
-}
-
-rust_thread::rust_thread(size_t stack_sz)
-  : thread(0), stack_sz(stack_sz) {
+rust_thread::rust_thread() : thread(0) {
 }
 
 rust_thread::~rust_thread() {
@@ -40,10 +36,11 @@ rust_thread_start(void *ptr) {
 void
 rust_thread::start() {
 #if defined(__WIN32__)
-   thread = CreateThread(NULL, stack_sz, rust_thread_start, this, 0, NULL);
+   thread = CreateThread(NULL, default_stack_sz, rust_thread_start, this, 0, NULL);
 #else
    // PTHREAD_STACK_MIN of some system is larger than default size
    // so we check stack_sz to prevent assertion failure.
+   size_t stack_sz = default_stack_sz;
    if (stack_sz < PTHREAD_STACK_MIN) {
       stack_sz = PTHREAD_STACK_MIN;
    }
diff --git a/src/rt/sync/rust_thread.h b/src/rt/sync/rust_thread.h
index 212d237698e..cad87e514b5 100644
--- a/src/rt/sync/rust_thread.h
+++ b/src/rt/sync/rust_thread.h
@@ -23,11 +23,9 @@ class rust_thread {
 #else
     pthread_t thread;
 #endif
-    size_t stack_sz;
  public:
 
     rust_thread();
-    rust_thread(size_t stack_sz);
     virtual ~rust_thread();
 
     void start();