about summary refs log tree commit diff
path: root/src/rt
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-02-01 18:50:19 -0800
committerNiko Matsakis <niko@alum.mit.edu>2012-02-01 18:50:19 -0800
commit31166438063236e6d2ad021cf2169fce2b3839b4 (patch)
treec42edd27f47c8a78cc212391c093992d3bde7f4b /src/rt
parentc36207bfb82f794f17fc5854d4ae50284eddf329 (diff)
downloadrust-31166438063236e6d2ad021cf2169fce2b3839b4.tar.gz
rust-31166438063236e6d2ad021cf2169fce2b3839b4.zip
Revert "make boxes self-describing (fixes #1493)" until a new
snapshot is prepared.
Diffstat (limited to 'src/rt')
-rw-r--r--src/rt/boxed_region.cpp59
-rw-r--r--src/rt/boxed_region.h39
-rw-r--r--src/rt/memory_region.h8
-rw-r--r--src/rt/rust_builtin.cpp16
-rw-r--r--src/rt/rust_cc.cpp202
-rw-r--r--src/rt/rust_internal.h31
-rw-r--r--src/rt/rust_shape.cpp2
-rw-r--r--src/rt/rust_shape.h104
-rw-r--r--src/rt/rust_task.cpp47
-rw-r--r--src/rt/rust_task.h10
-rw-r--r--src/rt/rust_upcall.cpp60
-rw-r--r--src/rt/rust_upcall.h16
-rw-r--r--src/rt/rustrt.def.in3
-rw-r--r--src/rt/test/rust_test_runtime.cpp2
14 files changed, 313 insertions, 286 deletions
diff --git a/src/rt/boxed_region.cpp b/src/rt/boxed_region.cpp
deleted file mode 100644
index 937069bc38c..00000000000
--- a/src/rt/boxed_region.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#include <assert.h>
-#include "boxed_region.h"
-#include "rust_internal.h"
-
-// #define DUMP_BOXED_REGION
-
-rust_opaque_box *boxed_region::malloc(type_desc *td) {
-    size_t header_size = sizeof(rust_opaque_box);
-    size_t body_size = td->size;
-    size_t body_align = td->align;
-    size_t total_size = align_to(header_size, body_align) + body_size;
-    rust_opaque_box *box =
-      (rust_opaque_box*)backing_region->malloc(total_size, "@");
-    box->td = td;
-    box->ref_count = 1;
-    box->prev = NULL;
-    box->next = live_allocs;
-    if (live_allocs) live_allocs->prev = box;
-    live_allocs = box;
-
-#   ifdef DUMP_BOXED_REGION
-    fprintf(stderr, "Allocated box %p with td %p,"
-            " size %lu==%lu+%lu, align %lu, prev %p, next %p\n",
-            box, td, total_size, header_size, body_size, body_align,
-            box->prev, box->next);
-#   endif
-
-    return box;
-}
-
-rust_opaque_box *boxed_region::calloc(type_desc *td) {
-    rust_opaque_box *box = malloc(td);
-    memset(box_body(box), 0, td->size);
-    return box;
-}
-
-void boxed_region::free(rust_opaque_box *box) {
-    // This turns out to not be true in various situations,
-    // like when we are unwinding after a failure.
-    //
-    // assert(box->ref_count == 0);
-
-    // This however should always be true.  Helps to detect
-    // double frees (kind of).
-    assert(box->td != NULL);
-
-#   ifdef DUMP_BOXED_REGION
-    fprintf(stderr, "Freed box %p with td %p, prev %p, next %p\n",
-            box, box->td, box->prev, box->next);
-#   endif
-
-    if (box->prev) box->prev->next = box->next;
-    if (box->next) box->next->prev = box->prev;
-    if (live_allocs == box) live_allocs = box->next;
-    box->prev = NULL;
-    box->next = NULL;
-    box->td = NULL;
-    backing_region->free(box);
-}
diff --git a/src/rt/boxed_region.h b/src/rt/boxed_region.h
deleted file mode 100644
index bd5312fee99..00000000000
--- a/src/rt/boxed_region.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef BOXED_REGION_H
-#define BOXED_REGION_H
-
-#include <stdlib.h>
-
-struct type_desc;
-class memory_region;
-struct rust_opaque_box;
-
-/* Tracks the data allocated by a particular task in the '@' region.
- * Currently still relies on the standard malloc as a backing allocator, but
- * this could be improved someday if necessary. Every allocation must provide
- * a type descr which describes the payload (what follows the header). */
-class boxed_region {
-private:
-    memory_region *backing_region;
-    rust_opaque_box *live_allocs;
-
-    size_t align_to(size_t v, size_t align) {
-        size_t alignm1 = align - 1;
-        v += alignm1;
-        v &= ~alignm1;
-        return v;
-    }
-
-public:
-    boxed_region(memory_region *br)
-        : backing_region(br)
-        , live_allocs(NULL)
-    {}
-
-    rust_opaque_box *first_live_alloc() { return live_allocs; }
-
-    rust_opaque_box *malloc(type_desc *td);
-    rust_opaque_box *calloc(type_desc *td);
-    void free(rust_opaque_box *box);
-};
-
-#endif /* BOXED_REGION_H */
diff --git a/src/rt/memory_region.h b/src/rt/memory_region.h
index a3d7270e79d..1a3e94c10bd 100644
--- a/src/rt/memory_region.h
+++ b/src/rt/memory_region.h
@@ -50,9 +50,6 @@ private:
     void dec_alloc();
     void maybe_poison(void *mem);
 
-    void release_alloc(void *mem);
-    void claim_alloc(void *mem);
-
 public:
     memory_region(rust_srv *srv, bool synchronized);
     memory_region(memory_region *parent);
@@ -61,7 +58,10 @@ public:
     void *realloc(void *mem, size_t size);
     void free(void *mem);
     virtual ~memory_region();
- };
+
+    void release_alloc(void *mem);
+    void claim_alloc(void *mem);
+};
 
 inline void *operator new(size_t size, memory_region &region,
                           const char *tag) {
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index ea2e8de256c..89d8c6d12f2 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -429,6 +429,22 @@ start_task(rust_task_id id, fn_env_pair *f) {
     target->deref();
 }
 
+extern "C" CDECL void
+migrate_alloc(void *alloc, rust_task_id tid) {
+    rust_task *task = rust_scheduler::get_task();
+    if(!alloc) return;
+    rust_task *target = task->kernel->get_task_by_id(tid);
+    if(target) {
+        const type_desc *tydesc = task->release_alloc(alloc);
+        target->claim_alloc(alloc, tydesc);
+        target->deref();
+    }
+    else {
+        // We couldn't find the target. Maybe we should just free?
+        task->fail();
+    }
+}
+
 extern "C" CDECL int
 sched_threads() {
     rust_task *task = rust_scheduler::get_task();
diff --git a/src/rt/rust_cc.cpp b/src/rt/rust_cc.cpp
index 60b0b8d2fd0..78138434d73 100644
--- a/src/rt/rust_cc.cpp
+++ b/src/rt/rust_cc.cpp
@@ -25,7 +25,7 @@ namespace cc {
 
 // Internal reference count computation
 
-typedef std::map<rust_opaque_box*,uintptr_t> irc_map;
+typedef std::map<void *,uintptr_t> irc_map;
 
 class irc : public shape::data<irc,shape::ptr> {
     friend class shape::data<irc,shape::ptr>;
@@ -118,6 +118,13 @@ class irc : public shape::data<irc,shape::ptr> {
         }
     }
 
+    void walk_obj2() {
+        dp += sizeof(void *); // skip vtable
+        uint8_t *box_ptr = shape::bump_dp<uint8_t *>(dp);
+        shape::ptr ref_count_dp(box_ptr);
+        maybe_record_irc(ref_count_dp);
+    }
+
     void walk_iface2() {
         walk_box2();
     }
@@ -138,32 +145,30 @@ class irc : public shape::data<irc,shape::ptr> {
 
     void walk_uniq_contents2(irc &sub) { sub.walk(); }
 
-    void walk_box_contents2(irc &sub, shape::ptr &box_dp) {
-        maybe_record_irc(box_dp);
+    void walk_box_contents2(irc &sub, shape::ptr &ref_count_dp) {
+        maybe_record_irc(ref_count_dp);
 
         // Do not traverse the contents of this box; it's in the allocation
         // somewhere, so we're guaranteed to come back to it (if we haven't
         // traversed it already).
     }
 
-    void maybe_record_irc(shape::ptr &box_dp) {
-        if (!box_dp)
+    void maybe_record_irc(shape::ptr &ref_count_dp) {
+        if (!ref_count_dp)
             return;
 
-        rust_opaque_box *box_ptr = (rust_opaque_box *) box_dp;
-
         // Bump the internal reference count of the box.
-        if (ircs.find(box_ptr) == ircs.end()) {
+        if (ircs.find((void *)ref_count_dp) == ircs.end()) {
           LOG(task, gc,
               "setting internal reference count for %p to 1",
-              box_ptr);
-          ircs[box_ptr] = 1;
+              (void *)ref_count_dp);
+          ircs[(void *)ref_count_dp] = 1;
         } else {
-          uintptr_t newcount = ircs[box_ptr] + 1;
+          uintptr_t newcount = ircs[(void *)ref_count_dp] + 1;
           LOG(task, gc,
               "bumping internal reference count for %p to %lu",
-              box_ptr, newcount);
-          ircs[box_ptr] = newcount;
+              (void *)ref_count_dp, newcount);
+          ircs[(void *)ref_count_dp] = newcount;
         }
     }
 
@@ -202,25 +207,36 @@ irc::walk_variant2(shape::tag_info &tinfo, uint32_t variant_id,
 
 void
 irc::compute_ircs(rust_task *task, irc_map &ircs) {
-    boxed_region *boxed = &task->boxed;
-    for (rust_opaque_box *box = boxed->first_live_alloc();
-         box != NULL;
-         box = box->next) {
-        type_desc *tydesc = box->td;
-        uint8_t *body = (uint8_t*) box_body(box);
-
-        LOG(task, gc, 
-            "determining internal ref counts: "
-            "box=%p tydesc=%p body=%p",
-            box, tydesc, body);
-        
+    std::map<void *,const type_desc *>::iterator
+        begin(task->local_allocs.begin()), end(task->local_allocs.end());
+    while (begin != end) {
+        uint8_t *p = reinterpret_cast<uint8_t *>(begin->first);
+
+        const type_desc *tydesc = begin->second;
+
+        LOG(task, gc, "determining internal ref counts: %p, tydesc=%p", p,
+            tydesc);
+
         shape::arena arena;
         shape::type_param *params =
-            shape::type_param::from_tydesc_and_data(tydesc, body, arena);
+            shape::type_param::from_tydesc_and_data(tydesc, p, arena);
+
+#if 0
+        shape::print print(task, true, tydesc->shape, params,
+                           tydesc->shape_tables);
+        print.walk();
+
+        shape::log log(task, true, tydesc->shape, params,
+                       tydesc->shape_tables, p + sizeof(uintptr_t),
+                       std::cerr);
+        log.walk();
+#endif
 
         irc irc(task, true, tydesc->shape, params, tydesc->shape_tables,
-                body, ircs);
+                p + sizeof(uintptr_t), ircs);
         irc.walk();
+
+        ++begin;
     }
 }
 
@@ -228,17 +244,17 @@ irc::compute_ircs(rust_task *task, irc_map &ircs) {
 // Root finding
 
 void
-find_roots(rust_task *task, irc_map &ircs,
-           std::vector<rust_opaque_box *> &roots) {
-    boxed_region *boxed = &task->boxed;
-    for (rust_opaque_box *box = boxed->first_live_alloc();
-         box != NULL;
-         box = box->next) {
-        uintptr_t ref_count = box->ref_count;
+find_roots(rust_task *task, irc_map &ircs, std::vector<void *> &roots) {
+    std::map<void *,const type_desc *>::iterator
+        begin(task->local_allocs.begin()), end(task->local_allocs.end());
+    while (begin != end) {
+        void *alloc = begin->first;
+        uintptr_t *ref_count_ptr = reinterpret_cast<uintptr_t *>(alloc);
+        uintptr_t ref_count = *ref_count_ptr;
 
         uintptr_t irc;
-        if (ircs.find(box) != ircs.end())
-            irc = ircs[box];
+        if (ircs.find(alloc) != ircs.end())
+            irc = ircs[alloc];
         else
             irc = 0;
 
@@ -246,14 +262,16 @@ find_roots(rust_task *task, irc_map &ircs,
             // This allocation must be a root, because the internal reference
             // count is smaller than the total reference count.
             LOG(task, gc,"root found: %p, irc %lu, ref count %lu",
-                box, irc, ref_count);
-            roots.push_back(box);
+                alloc, irc, ref_count);
+            roots.push_back(alloc);
         } else {
             LOG(task, gc, "nonroot found: %p, irc %lu, ref count %lu",
-                box, irc, ref_count);
+                alloc, irc, ref_count);
             assert(irc == ref_count && "Internal reference count must be "
                    "less than or equal to the total reference count!");
         }
+
+        ++begin;
     }
 }
 
@@ -263,7 +281,7 @@ find_roots(rust_task *task, irc_map &ircs,
 class mark : public shape::data<mark,shape::ptr> {
     friend class shape::data<mark,shape::ptr>;
 
-    std::set<rust_opaque_box *> &marked;
+    std::set<void *> &marked;
 
     mark(const mark &other, const shape::ptr &in_dp)
     : shape::data<mark,shape::ptr>(other.task, other.align, other.sp,
@@ -301,7 +319,7 @@ class mark : public shape::data<mark,shape::ptr> {
          const shape::type_param *in_params,
          const rust_shape_tables *in_tables,
          uint8_t *in_data,
-         std::set<rust_opaque_box*> &in_marked)
+         std::set<void *> &in_marked)
     : shape::data<mark,shape::ptr>(in_task, in_align, in_sp, in_params,
                                    in_tables, in_data),
       marked(in_marked) {}
@@ -339,7 +357,7 @@ class mark : public shape::data<mark,shape::ptr> {
           case shape::SHAPE_BOX_FN: {
               // Record an irc for the environment box, but don't descend
               // into it since it will be walked via the box's allocation
-              shape::data<mark,shape::ptr>::walk_fn_contents1();
+              shape::data<mark,shape::ptr>::walk_fn_contents1(dp, false);
               break;
           }
           case shape::SHAPE_BARE_FN:        // Does not close over data.
@@ -350,6 +368,10 @@ class mark : public shape::data<mark,shape::ptr> {
         }
     }
 
+    void walk_obj2() {
+        shape::data<mark,shape::ptr>::walk_obj_contents1(dp);
+    }
+
     void walk_res2(const shape::rust_fn *dtor, unsigned n_params,
                   const shape::type_param *params, const uint8_t *end_sp,
                   bool live) {
@@ -370,16 +392,14 @@ class mark : public shape::data<mark,shape::ptr> {
 
     void walk_uniq_contents2(mark &sub) { sub.walk(); }
 
-    void walk_box_contents2(mark &sub, shape::ptr &box_dp) {
-        if (!box_dp)
+    void walk_box_contents2(mark &sub, shape::ptr &ref_count_dp) {
+        if (!ref_count_dp)
             return;
 
-        rust_opaque_box *box_ptr = (rust_opaque_box *) box_dp;
-
-        if (marked.find(box_ptr) != marked.end())
+        if (marked.find((void *)ref_count_dp) != marked.end())
             return; // Skip to avoid chasing cycles.
 
-        marked.insert(box_ptr);
+        marked.insert((void *)ref_count_dp);
         sub.walk();
     }
 
@@ -398,9 +418,8 @@ class mark : public shape::data<mark,shape::ptr> {
     inline void walk_number2() { /* no-op */ }
 
 public:
-    static void do_mark(rust_task *task,
-                        const std::vector<rust_opaque_box *> &roots,
-                        std::set<rust_opaque_box*> &marked);
+    static void do_mark(rust_task *task, const std::vector<void *> &roots,
+                        std::set<void *> &marked);
 };
 
 void
@@ -419,28 +438,35 @@ mark::walk_variant2(shape::tag_info &tinfo, uint32_t variant_id,
 }
 
 void
-mark::do_mark(rust_task *task,
-              const std::vector<rust_opaque_box *> &roots,
-              std::set<rust_opaque_box *> &marked) {
-    std::vector<rust_opaque_box *>::const_iterator 
-      begin(roots.begin()),
-      end(roots.end());
+mark::do_mark(rust_task *task, const std::vector<void *> &roots,
+              std::set<void *> &marked) {
+    std::vector<void *>::const_iterator begin(roots.begin()),
+                                        end(roots.end());
     while (begin != end) {
-        rust_opaque_box *box = *begin;
-        if (marked.find(box) == marked.end()) {
-            marked.insert(box);
+        void *alloc = *begin;
+        if (marked.find(alloc) == marked.end()) {
+            marked.insert(alloc);
 
-            const type_desc *tydesc = box->td;
+            const type_desc *tydesc = task->local_allocs[alloc];
 
-            LOG(task, gc, "marking: %p, tydesc=%p", box, tydesc);
+            LOG(task, gc, "marking: %p, tydesc=%p", alloc, tydesc);
 
-            uint8_t *p = (uint8_t*) box_body(box);
+            uint8_t *p = reinterpret_cast<uint8_t *>(alloc);
             shape::arena arena;
             shape::type_param *params =
                 shape::type_param::from_tydesc_and_data(tydesc, p, arena);
 
+#if 0
+            // We skip over the reference count here.
+            shape::log log(task, true, tydesc->shape, params,
+                           tydesc->shape_tables, p + sizeof(uintptr_t),
+                           std::cerr);
+            log.walk();
+#endif
+
+            // We skip over the reference count here.
             mark mark(task, true, tydesc->shape, params, tydesc->shape_tables,
-                      p, marked);
+                      p + sizeof(uintptr_t), marked);
             mark.walk();
         }
 
@@ -526,9 +552,13 @@ class sweep : public shape::data<sweep,shape::ptr> {
               fn_env_pair pair = *(fn_env_pair*)dp;
 
               // free closed over data:
-              shape::data<sweep,shape::ptr>::walk_fn_contents1();
+              shape::data<sweep,shape::ptr>::walk_fn_contents1(dp, true);
 
               // now free the embedded type descr:
+              //
+              // see comment in walk_fn_contents1() concerning null_td
+              // to understand why this does not occur during the normal
+              // walk.
               upcall_s_free_shared_type_desc((type_desc*)pair.env->td);
 
               // now free the ptr:
@@ -580,7 +610,7 @@ class sweep : public shape::data<sweep,shape::ptr> {
 
     void walk_uniq_contents2(sweep &sub) { sub.walk(); }
 
-    void walk_box_contents2(sweep &sub, shape::ptr &box_dp) {
+    void walk_box_contents2(sweep &sub, shape::ptr &ref_count_dp) {
         return;
     }
 
@@ -607,50 +637,50 @@ class sweep : public shape::data<sweep,shape::ptr> {
     inline void walk_number2() { /* no-op */ }
 
 public:
-    static void do_sweep(rust_task *task,
-                         const std::set<rust_opaque_box*> &marked);
+    static void do_sweep(rust_task *task, const std::set<void *> &marked);
 };
 
 void
-sweep::do_sweep(rust_task *task,
-                const std::set<rust_opaque_box*> &marked) {
-    boxed_region *boxed = &task->boxed;
-    rust_opaque_box *box = boxed->first_live_alloc();
-    while (box != NULL) {
-        // save next ptr as we may be freeing box
-        rust_opaque_box *box_next = box->next;
-        if (marked.find(box) == marked.end()) {
-            LOG(task, gc, "object is part of a cycle: %p", box);
-
-            const type_desc *tydesc = box->td;
-            uint8_t *p = (uint8_t*) box_body(box);
+sweep::do_sweep(rust_task *task, const std::set<void *> &marked) {
+    std::map<void *,const type_desc *>::iterator
+        begin(task->local_allocs.begin()), end(task->local_allocs.end());
+    while (begin != end) {
+        void *alloc = begin->first;
+
+        if (marked.find(alloc) == marked.end()) {
+            LOG(task, gc, "object is part of a cycle: %p", alloc);
+
+            const type_desc *tydesc = begin->second;
+            uint8_t *p = reinterpret_cast<uint8_t *>(alloc);
             shape::arena arena;
             shape::type_param *params =
                 shape::type_param::from_tydesc_and_data(tydesc, p, arena);
 
             sweep sweep(task, true, tydesc->shape,
                         params, tydesc->shape_tables,
-                        p);
+                        p + sizeof(uintptr_t));
             sweep.walk();
 
-            boxed->free(box);
+            // FIXME: Run the destructor, *if* it's a resource.
+            task->free(alloc);
         }
-        box = box_next;
+        ++begin;
     }
 }
 
 
 void
 do_cc(rust_task *task) {
-    LOG(task, gc, "cc");
+    LOG(task, gc, "cc; n allocs = %lu",
+        (long unsigned int)task->local_allocs.size());
 
     irc_map ircs;
     irc::compute_ircs(task, ircs);
 
-    std::vector<rust_opaque_box*> roots;
+    std::vector<void *> roots;
     find_roots(task, ircs, roots);
 
-    std::set<rust_opaque_box*> marked;
+    std::set<void *> marked;
     mark::do_mark(task, roots, marked);
 
     sweep::do_sweep(task, marked);
diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h
index 6fa049054ec..a31aa9b31af 100644
--- a/src/rt/rust_internal.h
+++ b/src/rt/rust_internal.h
@@ -231,36 +231,29 @@ struct rust_shape_tables {
     uint8_t *resources;
 };
 
-typedef unsigned long ref_cnt_t;
-
-// Corresponds to the boxed data in the @ region.  The body follows the
-// header; you can obtain a ptr via box_body() below.
-struct rust_opaque_box {
-    ref_cnt_t ref_count;
-    type_desc *td;
-    rust_opaque_box *prev;
-    rust_opaque_box *next;
-};
+struct rust_opaque_closure;
 
 // The type of functions that we spawn, which fall into two categories:
 // - the main function: has a NULL environment, but uses the void* arg
 // - unique closures of type fn~(): have a non-NULL environment, but
 //   no arguments (and hence the final void*) is harmless
-typedef void (*CDECL spawn_fn)(void*, rust_opaque_box*, void *);
+typedef void (*CDECL spawn_fn)(void*, rust_opaque_closure*, void *);
 
 // corresponds to the layout of a fn(), fn@(), fn~() etc
 struct fn_env_pair {
     spawn_fn f;
-    rust_opaque_box *env;
+    rust_opaque_closure *env;
 };
 
-static inline void *box_body(rust_opaque_box *box) {
-    // Here we take advantage of the fact that the size of a box in 32
-    // (resp. 64) bit is 16 (resp. 32) bytes, and thus always 16-byte aligned.
-    // If this were to change, we would have to update the method
-    // rustc::middle::trans::base::opaque_box_body() as well.
-    return (void*)(box + 1);
-}
+// corresponds the closures generated in trans_closure.rs
+struct rust_opaque_closure {
+    intptr_t ref_count;
+    const type_desc *td;
+    // The size/types of these will vary per closure, so they
+    // cannot be statically expressed.  See trans_closure.rs:
+    const type_desc *captured_tds[0];
+    // struct bound_data;
+};
 
 struct type_desc {
     // First part of type_desc is known to compiler.
diff --git a/src/rt/rust_shape.cpp b/src/rt/rust_shape.cpp
index 875513ca264..37d8a42e95a 100644
--- a/src/rt/rust_shape.cpp
+++ b/src/rt/rust_shape.cpp
@@ -264,7 +264,7 @@ private:
         result = sub.result;
     }
 
-    inline void walk_box_contents2(cmp &sub, ptr_pair &box_dp) {
+    inline void walk_box_contents2(cmp &sub, ptr_pair &ref_count_dp) {
         sub.align = true;
         sub.walk();
         result = sub.result;
diff --git a/src/rt/rust_shape.h b/src/rt/rust_shape.h
index 92f660db741..848e304df30 100644
--- a/src/rt/rust_shape.h
+++ b/src/rt/rust_shape.h
@@ -28,6 +28,7 @@ namespace shape {
 
 typedef unsigned long tag_variant_t;
 typedef unsigned long tag_align_t;
+typedef unsigned long ref_cnt_t;
 
 // Constants
 
@@ -375,6 +376,7 @@ ctxt<T>::walk() {
     case SHAPE_TAG:      walk_tag0();             break;
     case SHAPE_BOX:      walk_box0();             break;
     case SHAPE_STRUCT:   walk_struct0();          break;
+    case SHAPE_OBJ:      WALK_SIMPLE(walk_obj1);      break;
     case SHAPE_RES:      walk_res0();             break;
     case SHAPE_VAR:      walk_var0();             break;
     case SHAPE_UNIQ:     walk_uniq0();            break;
@@ -589,6 +591,7 @@ public:
           default: abort();
         }
     }
+    void walk_obj1() { DPRINT("obj"); }
     void walk_iface1() { DPRINT("iface"); }
 
     void walk_tydesc1(char kind) {
@@ -642,6 +645,7 @@ public:
     void walk_uniq1()       { sa.set(sizeof(void *),   sizeof(void *)); }
     void walk_box1()        { sa.set(sizeof(void *),   sizeof(void *)); }
     void walk_fn1(char)     { sa.set(sizeof(void *)*2, sizeof(void *)); }
+    void walk_obj1()        { sa.set(sizeof(void *)*2, sizeof(void *)); }
     void walk_iface1()      { sa.set(sizeof(void *),   sizeof(void *)); }
     void walk_tydesc1(char) { sa.set(sizeof(void *),   sizeof(void *)); }
     void walk_closure1();
@@ -850,8 +854,9 @@ protected:
 
     void walk_box_contents1();
     void walk_uniq_contents1();
-    void walk_fn_contents1();
-    void walk_iface_contents1();
+    void walk_fn_contents1(ptr &dp, bool null_td);
+    void walk_obj_contents1(ptr &dp);
+    void walk_iface_contents1(ptr &dp);
     void walk_variant1(tag_info &tinfo, tag_variant_t variant);
 
     static std::pair<uint8_t *,uint8_t *> get_vec_data_range(ptr dp);
@@ -889,6 +894,13 @@ public:
         dp = next_dp;
     }
 
+    void walk_obj1() {
+        ALIGN_TO(alignof<void *>());
+        U next_dp = dp + sizeof(void *) * 2;
+        static_cast<T *>(this)->walk_obj2();
+        dp = next_dp;
+    }
+
     void walk_iface1() {
         ALIGN_TO(alignof<void *>());
         U next_dp = dp + sizeof(void *);
@@ -934,17 +946,9 @@ template<typename T,typename U>
 void
 data<T,U>::walk_box_contents1() {
     typename U::template data<uint8_t *>::t box_ptr = bump_dp<uint8_t *>(dp);
-    U box_dp(box_ptr);
-
-    // No need to worry about alignment so long as the box header is
-    // a multiple of 16 bytes.  We can just find the body by adding
-    // the size of header to box_dp.
-    assert ((sizeof(rust_opaque_box) % 16) == 0 ||
-            !"Must align to find the box body");
-
-    U body_dp = box_dp + sizeof(rust_opaque_box);
-    T sub(*static_cast<T *>(this), body_dp);
-    static_cast<T *>(this)->walk_box_contents2(sub, box_dp);
+    U ref_count_dp(box_ptr);
+    T sub(*static_cast<T *>(this), ref_count_dp + sizeof(ref_cnt_t));
+    static_cast<T *>(this)->walk_box_contents2(sub, ref_count_dp);
 }
 
 template<typename T,typename U>
@@ -1006,26 +1010,80 @@ data<T,U>::walk_tag1(tag_info &tinfo) {
 
 template<typename T,typename U>
 void
-data<T,U>::walk_fn_contents1() {
+data<T,U>::walk_fn_contents1(ptr &dp, bool null_td) {
     fn_env_pair pair = bump_dp<fn_env_pair>(dp);
     if (!pair.env)
         return;
 
     arena arena;
     const type_desc *closure_td = pair.env->td;
-    type_param *params = type_param::from_tydesc(closure_td, arena);
-    ptr closure_dp((uintptr_t)box_body(pair.env));
+    type_param *params =
+      type_param::from_tydesc(closure_td, arena);
+    ptr closure_dp((uintptr_t)pair.env);
     T sub(*static_cast<T *>(this), closure_td->shape, params,
           closure_td->shape_tables, closure_dp);
     sub.align = true;
 
+    if (null_td) {
+        // if null_td flag is true, null out the type descr from
+        // the data structure while we walk.  This is used in cycle
+        // collector when we are sweeping up data.  The idea is that
+        // we are using the information in the embedded type desc to
+        // walk the contents, so we do not want to free it during that
+        // walk.  This is not *strictly* necessary today because
+        // type_param::from_tydesc() actually pulls out the "shape"
+        // string and other information and copies it into a new
+        // location that is unaffected by the free.  But it seems
+        // safer, particularly as this pulling out of information will
+        // not cope with nested, derived type descriptors.
+        pair.env->td = NULL;
+    }
+
+    sub.walk();
+
+    if (null_td) {
+        pair.env->td = closure_td;
+    }
+}
+
+template<typename T,typename U>
+void
+data<T,U>::walk_obj_contents1(ptr &dp) {
+    dp += sizeof(void *);   // Skip over the vtable.
+
+    uint8_t *box_ptr = bump_dp<uint8_t *>(dp);
+    type_desc *subtydesc =
+        *reinterpret_cast<type_desc **>(box_ptr + sizeof(void *));
+    ptr obj_closure_dp(box_ptr + sizeof(void *));
+    if (!box_ptr)   // Null check.
+        return;
+
+    arena arena;
+    type_param *params = type_param::from_obj_shape(subtydesc->shape,
+                                                    obj_closure_dp, arena);
+    T sub(*static_cast<T *>(this), subtydesc->shape, params,
+          subtydesc->shape_tables, obj_closure_dp);
+    sub.align = true;
     sub.walk();
 }
 
 template<typename T,typename U>
 void
-data<T,U>::walk_iface_contents1() {
-    walk_box_contents1();
+data<T,U>::walk_iface_contents1(ptr &dp) {
+    uint8_t *box_ptr = bump_dp<uint8_t *>(dp);
+    if (!box_ptr) return;
+    U ref_count_dp(box_ptr);
+    uint8_t *body_ptr = box_ptr + sizeof(void*);
+    type_desc *valtydesc =
+        *reinterpret_cast<type_desc **>(body_ptr);
+    ptr value_dp(body_ptr + sizeof(void*) * 2);
+    // FIXME The 5 is a hard-coded way to skip over a struct shape
+    // header and the first two (number-typed) fields. This is too
+    // fragile, but I didn't see a good way to properly encode it.
+    T sub(*static_cast<T *>(this), valtydesc->shape + 5, NULL, NULL,
+          value_dp);
+    sub.align = true;
+    static_cast<T *>(this)->walk_box_contents2(sub, ref_count_dp);
 }
 
 // Polymorphic logging, for convenience
@@ -1103,13 +1161,19 @@ private:
     void walk_fn2(char kind) {
         out << prefix << "fn";
         prefix = "";
-        data<log,ptr>::walk_fn_contents1();
+        data<log,ptr>::walk_fn_contents1(dp, false);
+    }
+
+    void walk_obj2() {
+        out << prefix << "obj";
+        prefix = "";
+        data<log,ptr>::walk_obj_contents1(dp);
     }
 
     void walk_iface2() {
         out << prefix << "iface(";
         prefix = "";
-        data<log,ptr>::walk_iface_contents1();
+        data<log,ptr>::walk_iface_contents1(dp);
         out << prefix << ")";
     }
 
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index 4acef16a209..86dfb3b666f 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -14,7 +14,6 @@
 #include <algorithm>
 
 #include "globals.h"
-#include "rust_upcall.h"
 
 // The amount of extra space at the end of each stack segment, available
 // to the rt, compiler and dynamic linker for running small functions
@@ -247,7 +246,6 @@ rust_task::rust_task(rust_scheduler *sched, rust_task_list *state,
     running_on(-1),
     pinned_on(-1),
     local_region(&sched->srv->local_region),
-    boxed(&local_region),
     unwinding(false),
     killed(false),
     propagate_failure(true),
@@ -297,7 +295,7 @@ rust_task::~rust_task()
 struct spawn_args {
     rust_task *task;
     spawn_fn f;
-    rust_opaque_box *envptr;
+    rust_opaque_closure *envptr;
     void *argptr;
 };
 
@@ -332,6 +330,8 @@ cleanup_task(cleanup_args *args) {
     }
 }
 
+extern "C" void upcall_shared_free(void* ptr);
+
 // This runs on the Rust stack
 extern "C" CDECL
 void task_start_wrapper(spawn_args *a)
@@ -349,13 +349,12 @@ void task_start_wrapper(spawn_args *a)
         threw_exception = true;
     }
 
-    rust_opaque_box* env = a->envptr;
+    rust_opaque_closure* env = a->envptr;
     if(env) {
-        // free the environment (which should be a unique closure).
+        // free the environment.
         const type_desc *td = env->td;
         LOG(task, task, "Freeing env %p with td %p", env, td);
-        td->drop_glue(NULL, NULL, td->first_param, box_body(env));
-        upcall_free_shared_type_desc(env->td);
+        td->drop_glue(NULL, NULL, td->first_param, env);
         upcall_shared_free(env);
     }
 
@@ -368,7 +367,7 @@ void task_start_wrapper(spawn_args *a)
 
 void
 rust_task::start(spawn_fn spawnee_fn,
-                 rust_opaque_box *envptr,
+                 rust_opaque_closure *envptr,
                  void *argptr)
 {
     LOG(this, task, "starting task from fn 0x%" PRIxPTR
@@ -679,6 +678,38 @@ rust_port *rust_task::get_port_by_id(rust_port_id id) {
     return port;
 }
 
+
+// Temporary routine to allow boxes on one task's shared heap to be reparented
+// to another.
+const type_desc *
+rust_task::release_alloc(void *alloc) {
+    I(sched, !lock.lock_held_by_current_thread());
+    lock.lock();
+
+    assert(local_allocs.find(alloc) != local_allocs.end());
+    const type_desc *tydesc = local_allocs[alloc];
+    local_allocs.erase(alloc);
+
+    local_region.release_alloc(alloc);
+
+    lock.unlock();
+    return tydesc;
+}
+
+// Temporary routine to allow boxes from one task's shared heap to be
+// reparented to this one.
+void
+rust_task::claim_alloc(void *alloc, const type_desc *tydesc) {
+    I(sched, !lock.lock_held_by_current_thread());
+    lock.lock();
+
+    assert(local_allocs.find(alloc) == local_allocs.end());
+    local_allocs[alloc] = tydesc;
+    local_region.claim_alloc(alloc);
+
+    lock.unlock();
+}
+
 void
 rust_task::notify(bool success) {
     // FIXME (1078) Do this in rust code
diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h
index 418eb54cab5..0d7cfed4a32 100644
--- a/src/rt/rust_task.h
+++ b/src/rt/rust_task.h
@@ -14,7 +14,6 @@
 #include "rust_internal.h"
 #include "rust_kernel.h"
 #include "rust_obstack.h"
-#include "boxed_region.h"
 
 // Corresponds to the rust chan (currently _chan) type.
 struct chan_handle {
@@ -107,7 +106,6 @@ rust_task : public kernel_owned<rust_task>, rust_cond
     int pinned_on;
 
     memory_region local_region;
-    boxed_region boxed;
 
     // Indicates that fail() has been called and we are cleaning up.
     // We use this to suppress the "killed" flag during calls to yield.
@@ -123,6 +121,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond
 
     rust_obstack dynastack;
 
+    std::map<void *,const type_desc *> local_allocs;
     uint32_t cc_counter;
 
     debug::task_debug_info debug;
@@ -140,7 +139,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond
     ~rust_task();
 
     void start(spawn_fn spawnee_fn,
-               rust_opaque_box *env,
+               rust_opaque_closure *env,
                void *args);
     void start();
     bool running();
@@ -195,6 +194,11 @@ rust_task : public kernel_owned<rust_task>, rust_cond
     // not at all safe.
     intptr_t get_ref_count() const { return ref_count; }
 
+    // FIXME: These functions only exist to get the tasking system off the
+    // ground. We should never be migrating shared boxes between tasks.
+    const type_desc *release_alloc(void *alloc);
+    void claim_alloc(void *alloc, const type_desc *tydesc);
+
     void notify(bool success);
 
     void *new_stack(size_t stk_sz, void *args_addr, size_t args_sz);
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 09ad8d930c6..65299a7d244 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -16,20 +16,6 @@
 #include <stdint.h>
 
 
-#ifdef __GNUC__
-#define LOG_UPCALL_ENTRY(task)                            \
-    LOG(task, upcall,                                     \
-        "> UPCALL %s - task: %s 0x%" PRIxPTR              \
-        " retpc: x%" PRIxPTR,                             \
-        __FUNCTION__,                                     \
-        (task)->name, (task),                             \
-        __builtin_return_address(0));
-#else
-#define LOG_UPCALL_ENTRY(task)                            \
-    LOG(task, upcall, "> UPCALL task: %s @x%" PRIxPTR,    \
-        (task)->name, (task));
-#endif
-
 // This is called to ensure we've set up our rust stacks
 // correctly. Strategically placed at entry to upcalls because they begin on
 // the rust stack and happen frequently enough to catch most stack changes,
@@ -112,6 +98,7 @@ upcall_fail(char const *expr,
 
 struct s_malloc_args {
     uintptr_t retval;
+    size_t nbytes;
     type_desc *td;
 };
 
@@ -120,27 +107,31 @@ upcall_s_malloc(s_malloc_args *args) {
     rust_task *task = rust_scheduler::get_task();
     LOG_UPCALL_ENTRY(task);
 
-    LOG(task, mem, "upcall malloc(0x%" PRIxPTR ")", args->td);
+    LOG(task, mem,
+        "upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ")",
+        args->nbytes, args->td);
 
     gc::maybe_gc(task);
     cc::maybe_cc(task);
 
-    // FIXME--does this have to be calloc?
-    rust_opaque_box *box = task->boxed.calloc(args->td);
-    void *body = box_body(box);
+    // TODO: Maybe use dladdr here to find a more useful name for the
+    // type_desc.
+
+    void *p = task->malloc(args->nbytes, "tdesc", args->td);
+    memset(p, '\0', args->nbytes);
 
-    debug::maybe_track_origin(task, box);
+    task->local_allocs[p] = args->td;
+    debug::maybe_track_origin(task, p);
 
     LOG(task, mem,
-        "upcall malloc(0x%" PRIxPTR ") = box 0x%" PRIxPTR
-        " with body 0x%" PRIxPTR,
-        args->td, (uintptr_t)box, (uintptr_t)body);
-    args->retval = (uintptr_t) box;
+        "upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ") = 0x%" PRIxPTR,
+        args->nbytes, args->td, (uintptr_t)p);
+    args->retval = (uintptr_t) p;
 }
 
 extern "C" CDECL uintptr_t
-upcall_malloc(type_desc *td) {
-    s_malloc_args args = {0, td};
+upcall_malloc(size_t nbytes, type_desc *td) {
+    s_malloc_args args = {0, nbytes, td};
     UPCALL_SWITCH_STACK(&args, upcall_s_malloc);
     return args.retval;
 }
@@ -164,10 +155,10 @@ upcall_s_free(s_free_args *args) {
              "upcall free(0x%" PRIxPTR ", is_gc=%" PRIdPTR ")",
              (uintptr_t)args->ptr, args->is_gc);
 
+    task->local_allocs.erase(args->ptr);
     debug::maybe_untrack_origin(task, args->ptr);
 
-    rust_opaque_box *box = (rust_opaque_box*) args->ptr;
-    task->boxed.free(box);
+    task->free(args->ptr, (bool) args->is_gc);
 }
 
 extern "C" CDECL void
@@ -177,21 +168,6 @@ upcall_free(void* ptr, uintptr_t is_gc) {
 }
 
 /**********************************************************************
- * Sanity checks on boxes, insert when debugging possible
- * use-after-free bugs.  See maybe_validate_box() in trans.rs.
- */
-
-extern "C" CDECL void
-upcall_validate_box(rust_opaque_box* ptr) {
-    if (ptr) {
-        assert(ptr->ref_count > 0);
-        assert(ptr->td != NULL);
-        assert(ptr->td->align <= 8);
-        assert(ptr->td->size <= 4096); // might not really be true...
-    }
-}
-
-/**********************************************************************
  * Allocate an object in the exchange heap.
  */
 
diff --git a/src/rt/rust_upcall.h b/src/rt/rust_upcall.h
index 46676497d63..e5ee5bb6948 100644
--- a/src/rt/rust_upcall.h
+++ b/src/rt/rust_upcall.h
@@ -1,7 +1,17 @@
 #pragma once
 
-// Upcalls used from C code on occasion:
+#ifdef __GNUC__
+#define LOG_UPCALL_ENTRY(task)                            \
+    LOG(task, upcall,                                     \
+        "> UPCALL %s - task: %s 0x%" PRIxPTR              \
+        " retpc: x%" PRIxPTR,                             \
+        __FUNCTION__,                                     \
+        (task)->name, (task),                             \
+        __builtin_return_address(0));
+#else
+#define LOG_UPCALL_ENTRY(task)                            \
+    LOG(task, upcall, "> UPCALL task: %s @x%" PRIxPTR,    \
+        (task)->name, (task));
+#endif
 
-extern "C" CDECL void upcall_shared_free(void* ptr);
-extern "C" CDECL void upcall_free_shared_type_desc(type_desc *td);
 
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index 3701f73b0a8..50981882ecd 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -17,6 +17,7 @@ get_task_pointer
 get_time
 last_os_error
 leak
+migrate_alloc
 nano_time
 new_port
 new_task
@@ -62,7 +63,6 @@ upcall_dynastack_free
 upcall_dynastack_mark
 upcall_fail
 upcall_free
-upcall_validate_box
 upcall_create_shared_type_desc
 upcall_free_shared_type_desc
 upcall_get_type_desc
@@ -98,3 +98,4 @@ rust_uvtmp_read_start
 rust_uvtmp_timer
 rust_uvtmp_delete_buf
 rust_uvtmp_get_req_id
+
diff --git a/src/rt/test/rust_test_runtime.cpp b/src/rt/test/rust_test_runtime.cpp
index 51ec9219396..815d55acd86 100644
--- a/src/rt/test/rust_test_runtime.cpp
+++ b/src/rt/test/rust_test_runtime.cpp
@@ -39,7 +39,7 @@ rust_domain_test::run() {
     return true;
 }
 
-void task_entry(void *, rust_opaque_box*, void *) {
+void task_entry(void *, rust_opaque_closure *, void *) {
     printf("task entry\n");
 }