about summary refs log tree commit diff
path: root/src/comp
diff options
context:
space:
mode:
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/back/abi.rs16
-rw-r--r--src/comp/back/upcall.rs21
-rw-r--r--src/comp/middle/shape.rs10
-rw-r--r--src/comp/middle/trans.rs240
-rw-r--r--src/comp/middle/trans_common.rs57
-rw-r--r--src/comp/middle/trans_ivec.rs916
-rw-r--r--src/comp/middle/ty.rs119
-rw-r--r--src/comp/syntax/parse/parser.rs6
8 files changed, 309 insertions, 1076 deletions
diff --git a/src/comp/back/abi.rs b/src/comp/back/abi.rs
index 6055bc1ee52..22e49436440 100644
--- a/src/comp/back/abi.rs
+++ b/src/comp/back/abi.rs
@@ -92,24 +92,12 @@ const closure_elt_bindings: int = 1;
 
 const closure_elt_ty_params: int = 2;
 
-const ivec_default_length: uint = 4u;
+const ivec_elt_fill: uint = 0u;
 
-const ivec_elt_len: uint = 0u;
-
-const ivec_elt_alen: uint = 1u;
+const ivec_elt_alloc: uint = 1u;
 
 const ivec_elt_elems: uint = 2u;
 
-const ivec_heap_stub_elt_zero: uint = 0u;
-
-const ivec_heap_stub_elt_alen: uint = 1u;
-
-const ivec_heap_stub_elt_ptr: uint = 2u;
-
-const ivec_heap_elt_len: uint = 0u;
-
-const ivec_heap_elt_elems: uint = 1u;
-
 const worst_case_glue_call_args: int = 7;
 
 const abi_version: uint = 1u;
diff --git a/src/comp/back/upcall.rs b/src/comp/back/upcall.rs
index f224bafb409..8f0cb959b3d 100644
--- a/src/comp/back/upcall.rs
+++ b/src/comp/back/upcall.rs
@@ -40,10 +40,7 @@ type upcalls =
      new_str: ValueRef,
      evec_append: ValueRef,
      get_type_desc: ValueRef,
-     ivec_resize: ValueRef,
-     ivec_spill: ValueRef,
-     ivec_resize_shared: ValueRef,
-     ivec_spill_shared: ValueRef,
+     ivec_grow: ValueRef,
      ivec_push: ValueRef,
      cmp_type: ValueRef,
      log_type: ValueRef,
@@ -95,19 +92,13 @@ fn declare_upcalls(_tn: type_names, tydesc_type: TypeRef,
               d(~"get_type_desc",
                 [T_ptr(T_nil()), T_size_t(), T_size_t(), T_size_t(),
                  T_ptr(T_ptr(tydesc_type)), T_int()], T_ptr(tydesc_type)),
-          ivec_resize:
-              d(~"ivec_resize", [T_ptr(T_opaque_ivec()), T_int()], T_void()),
-          ivec_spill:
-              d(~"ivec_spill", [T_ptr(T_opaque_ivec()), T_int()], T_void()),
-          ivec_resize_shared:
-              d(~"ivec_resize_shared", [T_ptr(T_opaque_ivec()), T_int()],
-                T_void()),
-          ivec_spill_shared:
-              d(~"ivec_spill_shared", [T_ptr(T_opaque_ivec()), T_int()],
+          ivec_grow:
+              d(~"vec_grow", [T_ptr(T_ptr(T_opaque_ivec())), T_int()],
                 T_void()),
           ivec_push:
-              d(~"ivec_push", [T_ptr(T_opaque_ivec()), T_ptr(tydesc_type),
-                              T_ptr(T_i8())], T_void()),
+              d(~"vec_push",
+                [T_ptr(T_ptr(T_opaque_ivec())), T_ptr(tydesc_type),
+                 T_ptr(T_i8())], T_void()),
           cmp_type:
               dr(~"cmp_type",
                  [T_ptr(T_i1()), taskptr_type, T_ptr(tydesc_type),
diff --git a/src/comp/middle/shape.rs b/src/comp/middle/shape.rs
index 201e4ee0501..3c74e829e9d 100644
--- a/src/comp/middle/shape.rs
+++ b/src/comp/middle/shape.rs
@@ -313,7 +313,6 @@ fn shape_of(ccx: &@crate_ctxt, t: ty::t) -> [u8] {
         s += [shape_ivec];
         add_bool(s, true); // type is POD
         let unit_ty = ty::mk_mach(ccx.tcx, ast::ty_u8);
-        add_size_hint(ccx, s, unit_ty);
         add_substr(s, shape_of(ccx, unit_ty));
       }
 
@@ -365,7 +364,6 @@ fn shape_of(ccx: &@crate_ctxt, t: ty::t) -> [u8] {
       ty::ty_vec(mt) {
         s += [shape_ivec];
         add_bool(s, ty::type_is_pod(ccx.tcx, mt.ty));
-        add_size_hint(ccx, s, mt.ty);
         add_substr(s, shape_of(ccx, mt.ty));
       }
       ty::ty_rec(fields) {
@@ -416,14 +414,6 @@ fn shape_of(ccx: &@crate_ctxt, t: ty::t) -> [u8] {
     ret s;
 }
 
-fn add_size_hint(ccx: &@crate_ctxt, s: &mutable [u8], typ: ty::t) {
-    if ty::type_has_dynamic_size(ccx.tcx, typ) { s += [0u8, 0u8, 0u8]; ret; }
-
-    let llty = trans::type_of(ccx, dummy_sp(), typ);
-    add_u16(s, trans::llsize_of_real(ccx, llty) as u16);
-    s += [trans::llalign_of_real(ccx, llty) as u8];
-}
-
 // FIXME: We might discover other variants as we traverse these. Handle this.
 fn shape_of_variant(ccx: &@crate_ctxt, v: &ty::variant_info) -> [u8] {
     let s = [];
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 27f26d21220..60704036c22 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -205,14 +205,14 @@ fn type_of_inner(cx: &@crate_ctxt, sp: &span, t: ty::t) -> TypeRef {
       }
       ty::ty_char. { llty = T_char(); }
       ty::ty_str. { llty = T_ptr(T_str()); }
-      ty::ty_istr. { llty = T_ivec(T_i8()); }
+      ty::ty_istr. { llty = T_ptr(T_ivec(T_i8())); }
       ty::ty_tag(did, _) { llty = type_of_tag(cx, sp, did, t); }
       ty::ty_box(mt) { llty = T_ptr(T_box(type_of_inner(cx, sp, mt.ty))); }
       ty::ty_uniq(t) { llty = T_ptr(type_of_inner(cx, sp, t)); }
       ty::ty_vec(mt) {
         if ty::type_has_dynamic_size(cx.tcx, mt.ty) {
-            llty = T_opaque_ivec();
-        } else { llty = T_ivec(type_of_inner(cx, sp, mt.ty)); }
+            llty = T_ptr(T_opaque_ivec());
+        } else { llty = T_ptr(T_ivec(type_of_inner(cx, sp, mt.ty))); }
       }
       ty::ty_ptr(mt) { llty = T_ptr(type_of_inner(cx, sp, mt.ty)); }
       ty::ty_rec(fields) {
@@ -631,16 +631,6 @@ fn dynamic_size_of(cx: &@block_ctxt, t: ty::t) -> result {
             } else { max_size_val };
         ret rslt(bcx, total_size);
       }
-      ty::ty_vec(mt) {
-        let rs = size_of(cx, mt.ty);
-        let bcx = rs.bcx;
-        let llunitsz = rs.val;
-        let llsz =
-            bld::Add(bcx, llsize_of(T_opaque_ivec()),
-                          bld::Mul(bcx, llunitsz,
-                                        C_uint(abi::ivec_default_length)));
-        ret rslt(bcx, llsz);
-      }
     }
 }
 
@@ -663,13 +653,6 @@ fn dynamic_align_of(cx: &@block_ctxt, t: ty::t) -> result {
       ty::ty_tag(_, _) {
         ret rslt(cx, C_int(1)); // FIXME: stub
       }
-      ty::ty_vec(tm) {
-        let rs = align_of(cx, tm.ty);
-        let bcx = rs.bcx;
-        let llunitalign = rs.val;
-        let llalign = umax(bcx, llalign_of(T_int()), llunitalign);
-        ret rslt(bcx, llalign);
-      }
       ty::ty_tup(elts) {
         let a = C_int(1);
         let bcx = cx;
@@ -1326,16 +1309,16 @@ fn make_copy_glue(cx: &@block_ctxt, src: ValueRef, dst: ValueRef, t: ty::t) {
 }
 
 fn make_take_glue(cx: &@block_ctxt, v: ValueRef, t: ty::t) {
+    let bcx = cx;
     // NB: v is an *alias* of type t here, not a direct value.
-
-    let bcx;
-
-    if ty::type_is_boxed(bcx_tcx(cx), t) {
-        bcx = incr_refcnt_of_boxed(cx, bld::Load(cx, v)).bcx;
-    } else if ty::type_is_structural(bcx_tcx(cx), t) {
-        bcx = duplicate_heap_parts_if_necessary(cx, v, t).bcx;
+    if ty::type_is_boxed(bcx_tcx(bcx), t) {
+        bcx = incr_refcnt_of_boxed(bcx, bld::Load(bcx, v)).bcx;
+    } else if ty::type_is_structural(bcx_tcx(bcx), t) {
         bcx = iter_structural_ty(bcx, v, t, take_ty).bcx;
-    } else { bcx = cx; }
+    } else if ty::type_is_ivec(bcx_tcx(bcx), t) {
+        bcx = ivec::duplicate(bcx, v);
+        bcx = ivec::iter_ivec(bcx, v, t, take_ty).bcx;
+    }
 
     build_return(bcx);
 }
@@ -1426,51 +1409,17 @@ fn make_free_glue(cx: &@block_ctxt, v0: ValueRef, t: ty::t) {
     build_return(rs.bcx);
 }
 
-fn maybe_free_ivec_heap_part(cx: &@block_ctxt, v0: ValueRef, unit_ty: ty::t)
-   -> result {
-    let llunitty = type_of_or_i8(cx, unit_ty);
-    let stack_len =
-        bld::Load(cx, bld::InBoundsGEP(cx, v0,
-                                           [C_int(0),
-                                            C_uint(abi::ivec_elt_len)]));
-    let maybe_on_heap_cx = new_sub_block_ctxt(cx, ~"maybe_on_heap");
-    let next_cx = new_sub_block_ctxt(cx, ~"next");
-    let maybe_on_heap =
-        bld::ICmp(cx, lib::llvm::LLVMIntEQ, stack_len, C_int(0));
-    bld::CondBr(cx, maybe_on_heap, maybe_on_heap_cx.llbb, next_cx.llbb);
-    // Might be on the heap. Load the heap pointer and free it. (It's ok to
-    // free a null pointer.)
-
-    let stub_ptr =
-        bld::PointerCast(maybe_on_heap_cx, v0, T_ptr(T_ivec_heap(llunitty)));
-    let heap_ptr =
-        {
-            let v = [C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)];
-            let m = bld::InBoundsGEP(maybe_on_heap_cx, stub_ptr, v);
-            bld::Load(maybe_on_heap_cx, m)
-        };
-    let after_free_cx = trans_shared_free(maybe_on_heap_cx, heap_ptr).bcx;
-    bld::Br(after_free_cx, next_cx.llbb);
-    ret rslt(next_cx, C_nil());
-}
-
 fn make_drop_glue(cx: &@block_ctxt, v0: ValueRef, t: ty::t) {
     // NB: v0 is an *alias* of type t here, not a direct value.
     let ccx = bcx_ccx(cx);
     let rs =
         alt ty::struct(ccx.tcx, t) {
           ty::ty_str. { decr_refcnt_maybe_free(cx, v0, v0, t) }
-          ty::ty_vec(tm) {
-            let v1;
-            if ty::type_has_dynamic_size(ccx.tcx, tm.ty) {
-                v1 = bld::PointerCast(cx, v0, T_ptr(T_opaque_ivec()));
-            } else { v1 = v0; }
-            let rslt = iter_structural_ty(cx, v1, t, drop_ty);
-            maybe_free_ivec_heap_part(rslt.bcx, v1, tm.ty)
+          ty::ty_vec(_) {
+            rslt(ivec::make_drop_glue(cx, v0, t), C_nil())
           }
           ty::ty_istr. {
-            maybe_free_ivec_heap_part(cx, v0,
-                                      ty::mk_mach(ccx.tcx, ast::ty_u8))
+            rslt(ivec::make_drop_glue(cx, v0, t), C_nil())
           }
           ty::ty_box(_) { decr_refcnt_maybe_free(cx, v0, v0, t) }
           ty::ty_uniq(_) { trans_shared_free(cx, bld::Load(cx, v0)) }
@@ -1489,7 +1438,7 @@ fn make_drop_glue(cx: &@block_ctxt, v0: ValueRef, t: ty::t) {
           }
           _ {
             if ty::type_has_pointers(ccx.tcx, t) &&
-                   ty::type_is_structural(ccx.tcx, t) {
+               ty::type_is_structural(ccx.tcx, t) {
                 iter_structural_ty(cx, v0, t, drop_ty)
             } else { rslt(cx, C_nil()) }
           }
@@ -1721,7 +1670,8 @@ fn iter_structural_ty(cx: &@block_ctxt, v: ValueRef, t: ty::t,
        -> result {
         ret f(cx, av, t);
     }
-    ret iter_structural_ty_full(cx, v, t, bind adaptor_fn(f, _, _, _));
+    let x = iter_structural_ty_full(cx, v, t, bind adaptor_fn(f, _, _, _));
+    ret x;
 }
 
 fn load_inbounds(cx: &@block_ctxt, p: ValueRef, idxs: &[ValueRef]) ->
@@ -1756,61 +1706,6 @@ fn iter_structural_ty_full(cx: &@block_ctxt, av: ValueRef, t: ty::t,
         ret rslt(next_cx, C_nil());
     }
 
-    fn iter_ivec(bcx: @block_ctxt, av: ValueRef, unit_ty: ty::t,
-                 f: &val_and_ty_fn) -> result {
-        // FIXME: "unimplemented rebinding existing function" workaround
-
-        fn adapter(bcx: &@block_ctxt, av: ValueRef, unit_ty: ty::t,
-                   f: val_and_ty_fn) -> result {
-            ret f(bcx, av, unit_ty);
-        }
-        let llunitty = type_of_or_i8(bcx, unit_ty);
-        let rs = size_of(bcx, unit_ty);
-        let unit_sz = rs.val;
-        bcx = rs.bcx;
-        let a_len_and_data = ivec::get_len_and_data(bcx, av, unit_ty);
-        let len = a_len_and_data.len;
-        let a_elem = a_len_and_data.data;
-        bcx = a_len_and_data.bcx;
-        // Calculate the last pointer address we want to handle.
-        // TODO: Optimize this when the size of the unit type is statically
-        // known to not use pointer casts, which tend to confuse LLVM.
-
-        let a_elem_i8 = bld::PointerCast(bcx, a_elem, T_ptr(T_i8()));
-        let a_end_i8 = bld::GEP(bcx, a_elem_i8, [len]);
-        let a_end = bld::PointerCast(bcx, a_end_i8, T_ptr(llunitty));
-
-        let dest_elem_ptr = alloca(bcx, T_ptr(llunitty));
-        bld::Store(bcx, a_elem, dest_elem_ptr);
-
-        // Now perform the iteration.
-        let loop_header_cx = new_sub_block_ctxt(
-            bcx, ~"iter_ivec_loop_header");
-        bld::Br(bcx, loop_header_cx.llbb);
-        let dest_elem = bld::Load(loop_header_cx, dest_elem_ptr);
-        let not_yet_at_end =
-            bld::ICmp(loop_header_cx, lib::llvm::LLVMIntULT, dest_elem,
-                                      a_end);
-        let loop_body_cx = new_sub_block_ctxt(bcx, ~"iter_ivec_loop_body");
-        let next_cx = new_sub_block_ctxt(bcx, ~"iter_ivec_next");
-        bld::CondBr(loop_header_cx, not_yet_at_end, loop_body_cx.llbb,
-                                    next_cx.llbb);
-
-        rs = f(loop_body_cx, dest_elem, unit_ty);
-
-        loop_body_cx = rs.bcx;
-
-        let increment;
-        if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) {
-            increment = unit_sz;
-        } else { increment = C_int(1); }
-
-        incr_ptr(loop_body_cx, dest_elem, increment, dest_elem_ptr);
-        bld::Br(loop_body_cx, loop_header_cx.llbb);
-
-        ret rslt(next_cx, C_nil());
-    }
-
     fn iter_variant(cx: @block_ctxt, a_tup: ValueRef,
                     variant: &ty::variant_info, tps: &[ty::t],
                     tid: &ast::def_id, f: &val_and_ty_fn) -> result {
@@ -1913,11 +1808,6 @@ fn iter_structural_ty_full(cx: &@block_ctxt, av: ValueRef, t: ty::t,
             bld::GEP(cx, av, [C_int(0), C_int(abi::obj_field_box)]);
         ret iter_boxpp(cx, box_cell_a, f);
       }
-      ty::ty_vec(unit_tm) { ret iter_ivec(cx, av, unit_tm.ty, f); }
-      ty::ty_istr. {
-        let unit_ty = ty::mk_mach(bcx_tcx(cx), ast::ty_u8);
-        ret iter_ivec(cx, av, unit_ty, f);
-      }
       _ { bcx_ccx(cx).sess.unimpl(~"type in iter_structural_ty_full"); }
     }
     ret r;
@@ -1987,25 +1877,21 @@ fn iter_sequence_inner(cx: &@block_ctxt, src: ValueRef,
 // Iterates through the elements of a vec or str.
 fn iter_sequence(cx: @block_ctxt, v: ValueRef, t: ty::t, f: &val_and_ty_fn)
    -> result {
-    fn iter_sequence_body(cx: @block_ctxt, v: ValueRef, elt_ty: ty::t,
+    fn iter_sequence_body(bcx: @block_ctxt, v: ValueRef, elt_ty: ty::t,
                           f: &val_and_ty_fn, trailing_null: bool,
                           interior: bool) -> result {
         let p0;
         let len;
-        let bcx;
+        let llunit_ty = type_of_or_i8(bcx, elt_ty);
         if !interior {
-            p0 = bld::GEP(cx, v, [C_int(0), C_int(abi::vec_elt_data)]);
-            let lp = bld::GEP(cx, v, [C_int(0), C_int(abi::vec_elt_fill)]);
-            len = bld::Load(cx, lp);
-            bcx = cx;
+            p0 = bld::GEP(bcx, v, [C_int(0), C_int(abi::vec_elt_data)]);
+            let lp = bld::GEP(bcx, v, [C_int(0), C_int(abi::vec_elt_fill)]);
+            len = bld::Load(bcx, lp);
         } else {
-            let len_and_data_rslt = ivec::get_len_and_data(cx, v, elt_ty);
-            len = len_and_data_rslt.len;
-            p0 = len_and_data_rslt.data;
-            bcx = len_and_data_rslt.bcx;
+            len = ivec::get_fill(bcx, v);
+            p0 = ivec::get_dataptr(bcx, v, llunit_ty);
         }
 
-        let llunit_ty = type_of_or_i8(cx, elt_ty);
         if trailing_null {
             let unit_sz = size_of(bcx, elt_ty);
             bcx = unit_sz.bcx;
@@ -2277,8 +2163,7 @@ fn compare(cx: &@block_ctxt, lhs: ValueRef, rhs: ValueRef, t: ty::t,
 }
 
 fn take_ty(cx: &@block_ctxt, v: ValueRef, t: ty::t) -> result {
-    if ty::type_has_pointers(bcx_tcx(cx), t) ||
-           ty::type_owns_heap_mem(bcx_tcx(cx), t) {
+    if ty::type_has_pointers(bcx_tcx(cx), t) {
         ret call_tydesc_glue(cx, v, t, abi::tydesc_field_take_glue);
     }
     ret rslt(cx, C_nil());
@@ -2348,19 +2233,6 @@ fn memmove_ty(cx: &@block_ctxt, dst: ValueRef, src: ValueRef, t: ty::t) ->
     } else { ret rslt(cx, bld::Store(cx, bld::Load(cx, src), dst)); }
 }
 
-// Duplicates any heap-owned memory owned by a value of the given type.
-fn duplicate_heap_parts_if_necessary(cx: &@block_ctxt, vptr: ValueRef,
-                                     typ: ty::t) -> result {
-    alt ty::struct(bcx_tcx(cx), typ) {
-      ty::ty_vec(tm) { ret ivec::duplicate_heap_part(cx, vptr, tm.ty); }
-      ty::ty_istr. {
-        ret ivec::duplicate_heap_part(cx, vptr,
-                                      ty::mk_mach(bcx_tcx(cx), ast::ty_u8));
-      }
-      _ { ret rslt(cx, C_nil()); }
-    }
-}
-
 tag copy_action { INIT; DROP_EXISTING; }
 
 // These are the types that are passed by pointer.
@@ -2403,7 +2275,8 @@ fn copy_val_no_check(cx: &@block_ctxt, action: copy_action, dst: ValueRef,
         ret cx;
     } else if ty::type_is_nil(ccx.tcx, t) || ty::type_is_bot(ccx.tcx, t) {
         ret cx;
-    } else if ty::type_is_boxed(ccx.tcx, t) {
+    } else if ty::type_is_boxed(ccx.tcx, t) ||
+              ty::type_is_ivec(ccx.tcx, t) {
         let bcx = if action == DROP_EXISTING {
             drop_ty(cx, dst, t).bcx
         } else { cx };
@@ -2414,12 +2287,8 @@ fn copy_val_no_check(cx: &@block_ctxt, action: copy_action, dst: ValueRef,
         let bcx = if action == DROP_EXISTING {
             drop_ty(cx, dst, t).bcx
         } else { cx };
-        if ty::type_needs_copy_glue(ccx.tcx, t) {
-            ret call_copy_glue(bcx, dst, src, t, true);
-        } else {
-            bcx = memmove_ty(bcx, dst, src, t).bcx;
-            ret take_ty(bcx, dst, t).bcx;
-        }
+        bcx = memmove_ty(bcx, dst, src, t).bcx;
+        ret take_ty(bcx, dst, t).bcx;
     }
     ccx.sess.bug(~"unexpected type in trans::copy_val_no_check: " +
                  ty_to_str(ccx.tcx, t));
@@ -2443,7 +2312,7 @@ fn move_val(cx: @block_ctxt, action: copy_action, dst: ValueRef,
     } else if ty::type_is_nil(tcx, t) || ty::type_is_bot(tcx, t) {
         ret cx;
     } else if ty::type_is_unique(tcx, t) ||
-                  ty::type_is_boxed(tcx, t) {
+              ty::type_is_boxed(tcx, t) {
         if src.is_mem { src_val = bld::Load(cx, src_val); }
         if action == DROP_EXISTING {
             cx = drop_ty(cx, dst, t).bcx;
@@ -2456,11 +2325,7 @@ fn move_val(cx: @block_ctxt, action: copy_action, dst: ValueRef,
         ret cx;
     } else if type_is_structural_or_param(tcx, t) {
         if action == DROP_EXISTING { cx = drop_ty(cx, dst, t).bcx; }
-        if ty::type_needs_copy_glue(tcx, t) {
-            cx = call_copy_glue(cx, dst, src_val, t, false);
-        } else {
-            cx = memmove_ty(cx, dst, src_val, t).bcx;
-        }
+        cx = memmove_ty(cx, dst, src_val, t).bcx;
         if src.is_mem {
             ret zero_alloca(cx, src_val, t).bcx;
         } else { // Temporary value
@@ -2483,26 +2348,6 @@ fn move_val_if_temp(cx: @block_ctxt, action: copy_action, dst: ValueRef,
     ret move_val(cx, action, dst, src, t);
 }
 
-fn trans_lit_istr(cx: &@block_ctxt, s: &istr) -> result {
-    let vec_ty = ty::mk_vec(bcx_tcx(cx),
-                            {ty: ty::mk_mach(bcx_tcx(cx), ast::ty_u8),
-                             mut: ast::imm});
-    let strlen = istr::byte_len(s);
-    let veclen = strlen + 1u; // +1 for \0
-    let alloc_res = trans_ivec::alloc_with_heap(cx, vec_ty, veclen);
-
-    let bcx = alloc_res.bcx;
-    let llvecptr = alloc_res.llptr;
-    let llfirsteltptr = alloc_res.llfirsteltptr;
-
-    let llcstr = C_cstr(bcx_ccx(cx), s);
-
-    // FIXME: We need to avoid this memmove
-    bcx = call_memmove(bcx, llfirsteltptr, llcstr, C_uint(veclen)).bcx;
-
-    ret rslt(bcx, llvecptr);
-}
-
 fn trans_crate_lit(cx: &@crate_ctxt, lit: &ast::lit) -> ValueRef {
     alt lit.node {
       ast::lit_int(i) { ret C_int(i); }
@@ -2544,9 +2389,7 @@ fn trans_crate_lit(cx: &@crate_ctxt, lit: &ast::lit) -> ValueRef {
 
 fn trans_lit(cx: &@block_ctxt, lit: &ast::lit) -> result {
     alt lit.node {
-      ast::lit_str(s, ast::sk_unique.) {
-        ret trans_lit_istr(cx, s);
-      }
+      ast::lit_str(s, ast::sk_unique.) { ret ivec::trans_istr(cx, s); }
       _ { ret rslt(cx, trans_crate_lit(bcx_ccx(cx), lit)); }
     }
 }
@@ -3492,9 +3335,9 @@ fn trans_index(cx: &@block_ctxt, sp: &span, base: &@ast::expr,
     maybe_name_value(bcx_ccx(cx), scaled_ix, ~"scaled_ix");
     let interior_len_and_data;
     if is_interior {
-        let rslt = ivec::get_len_and_data(bcx, v, unit_ty);
-        interior_len_and_data = some({len: rslt.len, data: rslt.data});
-        bcx = rslt.bcx;
+        let len = ivec::get_fill(bcx, v);
+        let data = ivec::get_dataptr(bcx, v, type_of_or_i8(bcx, unit_ty));
+        interior_len_and_data = some({len: len, data: data});
     } else { interior_len_and_data = none; }
     let lim;
     alt interior_len_and_data {
@@ -3986,7 +3829,11 @@ fn trans_arg_expr(cx: &@block_ctxt, arg: &ty::arg, lldestty0: TypeRef,
             val = dst.val;
             add_clean_temp(bcx, val, e_ty);
         } else {
-            if lv.is_mem {
+            if ty::type_is_ivec(ccx.tcx, e_ty) {
+                let arg_copy = do_spill(bcx, bld::Load(bcx, val));
+                bcx = take_ty(bcx, arg_copy, e_ty).bcx;
+                val = bld::Load(bcx, arg_copy);
+            } else if lv.is_mem {
                 bcx = take_ty(bcx, val, e_ty).bcx;
                 val = load_if_immediate(bcx, val, e_ty);
             } else if is_ext_vec_plus {
@@ -4291,7 +4138,7 @@ fn trans_expr(cx: &@block_ctxt, e: &@ast::expr) -> result {
 
 fn trans_expr_out(cx: &@block_ctxt, e: &@ast::expr, output: out_method) ->
    result {
-    // FIXME Fill in cx.sp
+    // Fixme Fill in cx.sp
     alt e.node {
       ast::expr_lit(lit) { ret trans_lit(cx, *lit); }
       ast::expr_unary(op, x) {
@@ -4534,7 +4381,7 @@ fn with_out_method(work: fn(&out_method) -> result, cx: @block_ctxt,
 // immediate-ness of the type.
 fn type_is_immediate(ccx: &@crate_ctxt, t: ty::t) -> bool {
     ret ty::type_is_scalar(ccx.tcx, t) || ty::type_is_boxed(ccx.tcx, t) ||
-            ty::type_is_native(ccx.tcx, t);
+        ty::type_is_native(ccx.tcx, t) || ty::type_is_ivec(ccx.tcx, t);
 }
 
 fn do_spill(cx: &@block_ctxt, v: ValueRef) -> ValueRef {
@@ -5812,9 +5659,6 @@ fn create_main_wrapper(ccx: &@crate_ctxt, sp: &span, main_llfn: ValueRef,
         let args = [lloutputarg, lltaskarg, llenvarg];
         if takes_ivec { args += [llargvarg]; }
         bld::FastCall(bcx, main_llfn, args);
-        // We're responsible for freeing the arg vector
-        bcx = maybe_free_ivec_heap_part(bcx, llargvarg,
-                                        ty::mk_str(ccx.tcx)).bcx;
         build_return(bcx);
 
         finish_fn(fcx, lltop);
diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs
index 0006db2f000..6b2cb1fe533 100644
--- a/src/comp/middle/trans_common.rs
+++ b/src/comp/middle/trans_common.rs
@@ -5,6 +5,7 @@
 
 import std::int;
 import std::vec;
+import std::vec::to_ptr;
 import std::str;
 import std::istr;
 import std::uint;
@@ -454,7 +455,7 @@ fn struct_elt(llstructty: TypeRef, n: uint) -> TypeRef {
     let elt_count = llvm::LLVMCountStructElementTypes(llstructty);
     assert (n < elt_count);
     let elt_tys = std::vec::init_elt(T_nil(), elt_count);
-    llvm::LLVMGetStructElementTypes(llstructty, std::vec::to_ptr(elt_tys));
+    llvm::LLVMGetStructElementTypes(llstructty, to_ptr(elt_tys));
     ret llvm::LLVMGetElementType(elt_tys[n]);
 }
 
@@ -539,7 +540,7 @@ fn T_size_t() -> TypeRef {
 }
 
 fn T_fn(inputs: &[TypeRef], output: TypeRef) -> TypeRef {
-    ret llvm::LLVMFunctionType(output, std::vec::to_ptr(inputs),
+    ret llvm::LLVMFunctionType(output, to_ptr(inputs),
                                std::vec::len::<TypeRef>(inputs), False);
 }
 
@@ -550,8 +551,8 @@ fn T_fn_pair(cx: &crate_ctxt, tfn: TypeRef) -> TypeRef {
 fn T_ptr(t: TypeRef) -> TypeRef { ret llvm::LLVMPointerType(t, 0u); }
 
 fn T_struct(elts: &[TypeRef]) -> TypeRef {
-    ret llvm::LLVMStructType(std::vec::to_ptr(elts), std::vec::len(elts),
-                             False);
+    ret llvm::LLVMStructType(to_ptr(elts),
+                             std::vec::len(elts), False);
 }
 
 fn T_named_struct(name: &istr) -> TypeRef {
@@ -562,8 +563,8 @@ fn T_named_struct(name: &istr) -> TypeRef {
 }
 
 fn set_struct_body(t: TypeRef, elts: &[TypeRef]) {
-    llvm::LLVMStructSetBody(t, std::vec::to_ptr(elts), std::vec::len(elts),
-                            False);
+    llvm::LLVMStructSetBody(t, to_ptr(elts),
+                            std::vec::len(elts), False);
 }
 
 fn T_empty_struct() -> TypeRef { ret T_struct([]); }
@@ -606,7 +607,7 @@ fn T_tydesc_field(cx: &crate_ctxt, field: int) -> TypeRef {
     let tydesc_elts: [TypeRef] =
         std::vec::init_elt::<TypeRef>(T_nil(), abi::n_tydesc_fields as uint);
     llvm::LLVMGetStructElementTypes(cx.tydesc_type,
-                                    std::vec::to_ptr::<TypeRef>(tydesc_elts));
+                                    to_ptr::<TypeRef>(tydesc_elts));
     let t = llvm::LLVMGetElementType(tydesc_elts[field]);
     ret t;
 }
@@ -676,48 +677,14 @@ fn T_opaque_vec_ptr() -> TypeRef { ret T_ptr(T_evec(T_int())); }
 //
 // TODO: Support user-defined vector sizes.
 fn T_ivec(t: TypeRef) -> TypeRef {
-    ret T_struct([T_int(), // Length ("fill"; if zero, heapified)
-                  T_int(), // Alloc
-                  T_array(t, abi::ivec_default_length)]); // Body elements
-
+    ret T_struct([T_int(), // fill
+                  T_int(), // alloc
+                  T_array(t, 0u)]); // elements
 }
 
-
 // Note that the size of this one is in bytes.
 fn T_opaque_ivec() -> TypeRef {
-    ret T_struct([T_int(), // Length ("fill"; if zero, heapified)
-                  T_int(), // Alloc
-                  T_array(T_i8(), 0u)]); // Body elements
-
-}
-
-fn T_ivec_heap_part(t: TypeRef) -> TypeRef {
-    ret T_struct([T_int(), // Real length
-                  T_array(t, 0u)]); // Body elements
-
-}
-
-
-// Interior vector on the heap, also known as the "stub". Cast to this when
-// the allocated length (second element of T_ivec above) is zero.
-fn T_ivec_heap(t: TypeRef) -> TypeRef {
-    ret T_struct([T_int(), // Length (zero)
-                  T_int(), // Alloc
-                  T_ptr(T_ivec_heap_part(t))]); // Pointer
-
-}
-
-fn T_opaque_ivec_heap_part() -> TypeRef {
-    ret T_struct([T_int(), // Real length
-                  T_array(T_i8(), 0u)]); // Body elements
-
-}
-
-fn T_opaque_ivec_heap() -> TypeRef {
-    ret T_struct([T_int(), // Length (zero)
-                  T_int(), // Alloc
-                  T_ptr(T_opaque_ivec_heap_part())]); // Pointer
-
+    ret T_ivec(T_i8());
 }
 
 fn T_str() -> TypeRef { ret T_evec(T_i8()); }
diff --git a/src/comp/middle/trans_ivec.rs b/src/comp/middle/trans_ivec.rs
index 054c453ff9e..ce64ed561f9 100644
--- a/src/comp/middle/trans_ivec.rs
+++ b/src/comp/middle/trans_ivec.rs
@@ -8,768 +8,310 @@ import trans::{call_memmove, trans_shared_malloc, llsize_of,
                alloca, array_alloca, size_of, llderivedtydescs_block_ctxt,
                lazily_emit_tydesc_glue, get_tydesc, load_inbounds,
                move_val_if_temp, trans_lval, node_id_type,
-               new_sub_block_ctxt, tps_normal};
-import bld = trans_build;
+               new_sub_block_ctxt, tps_normal, do_spill};
+import trans_build::*;
 import trans_common::*;
 
-fn alloc_with_heap(bcx: @block_ctxt, typ: &ty::t, vecsz: uint) ->
-    {bcx: @block_ctxt,
-     unit_ty: ty::t,
-     llunitsz: ValueRef,
-     llptr: ValueRef,
-     llfirsteltptr: ValueRef} {
-
-    let unit_ty;
-    alt ty::struct(bcx_tcx(bcx), typ) {
-      ty::ty_vec(mt) { unit_ty = mt.ty; }
-      _ { bcx_ccx(bcx).sess.bug(~"non-ivec type in trans_ivec"); }
-    }
-    let llunitty = type_of_or_i8(bcx, unit_ty);
-
-    let ares = alloc(bcx, unit_ty);
-    bcx = ares.bcx;
-    let llvecptr = ares.llptr;
-    let unit_sz = ares.llunitsz;
-    let llalen = ares.llalen;
-
-    add_clean_temp(bcx, llvecptr, typ);
-
-    let lllen = bld::Mul(bcx, C_uint(vecsz), unit_sz);
-    // Allocate the vector pieces and store length and allocated length.
-
-    let llfirsteltptr;
-    if vecsz > 0u && vecsz <= abi::ivec_default_length {
-        // Interior case.
-
-        bld::Store(bcx, lllen,
-                        bld::InBoundsGEP(bcx, llvecptr,
-                                              [C_int(0),
-                                               C_uint(abi::ivec_elt_len)]));
-        bld::Store(bcx, llalen,
-                        bld::InBoundsGEP(bcx, llvecptr,
-                                              [C_int(0),
-                                               C_uint(abi::ivec_elt_alen)]));
-        llfirsteltptr =
-            bld::InBoundsGEP(bcx, llvecptr,
-                                  [C_int(0), C_uint(abi::ivec_elt_elems),
-                                   C_int(0)]);
-    } else {
-        // Heap case.
+fn get_fill(bcx: &@block_ctxt, vptr: ValueRef) -> ValueRef {
+    Load(bcx, InBoundsGEP(bcx, vptr, [C_int(0), C_uint(abi::ivec_elt_fill)]))
+}
+fn get_alloc(bcx: &@block_ctxt, vptr: ValueRef) -> ValueRef {
+    Load(bcx, InBoundsGEP(bcx, vptr, [C_int(0), C_uint(abi::ivec_elt_alloc)]))
+}
+fn get_dataptr(bcx: &@block_ctxt, vpt: ValueRef,
+               unit_ty: TypeRef) -> ValueRef {
+    let ptr = InBoundsGEP(bcx, vpt, [C_int(0), C_uint(abi::ivec_elt_elems)]);
+    PointerCast(bcx, ptr, T_ptr(unit_ty))
+}
 
-        let stub_z = [C_int(0), C_uint(abi::ivec_heap_stub_elt_zero)];
-        let stub_a = [C_int(0), C_uint(abi::ivec_heap_stub_elt_alen)];
-        let stub_p = [C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)];
-        let llstubty = T_ivec_heap(llunitty);
-        let llstubptr = bld::PointerCast(bcx, llvecptr, T_ptr(llstubty));
-        bld::Store(bcx, C_int(0), bld::InBoundsGEP(bcx, llstubptr, stub_z));
-        let llheapty = T_ivec_heap_part(llunitty);
-        if vecsz == 0u {
-            // Null heap pointer indicates a zero-length vector.
+fn pointer_add(bcx: &@block_ctxt, ptr: ValueRef, bytes: ValueRef)
+    -> ValueRef {
+    let old_ty = val_ty(ptr);
+    let bptr = PointerCast(bcx, ptr, T_ptr(T_i8()));
+    ret PointerCast(bcx, InBoundsGEP(bcx, bptr, [bytes]), old_ty);
+}
 
-            bld::Store(bcx, llalen, bld::InBoundsGEP(bcx, llstubptr, stub_a));
-            bld::Store(bcx, C_null(T_ptr(llheapty)),
-                            bld::InBoundsGEP(bcx, llstubptr, stub_p));
-            llfirsteltptr = C_null(T_ptr(llunitty));
-        } else {
-            bld::Store(bcx, lllen, bld::InBoundsGEP(bcx, llstubptr, stub_a));
+// FIXME factor out a scaling version wrapping a non-scaling version
+fn alloc(bcx: &@block_ctxt, vec_ty: &ty::t, vecsz: ValueRef, is_scaled: bool)
+    -> {bcx: @block_ctxt,
+        val: ValueRef,
+        unit_ty: ty::t,
+        llunitsz: ValueRef,
+        llunitty: TypeRef} {
 
-            let llheapsz = bld::Add(bcx, llsize_of(llheapty), lllen);
-            let rslt = trans_shared_malloc(bcx, T_ptr(llheapty), llheapsz);
-            bcx = rslt.bcx;
-            let llheapptr = rslt.val;
-            bld::Store(bcx, llheapptr,
-                            bld::InBoundsGEP(bcx, llstubptr, stub_p));
-            let heap_l = [C_int(0), C_uint(abi::ivec_heap_elt_len)];
-            bld::Store(bcx, lllen, bld::InBoundsGEP(bcx, llheapptr, heap_l));
-            llfirsteltptr =
-                bld::InBoundsGEP(bcx, llheapptr,
-                                      [C_int(0),
-                                       C_uint(abi::ivec_heap_elt_elems),
-                                       C_int(0)]);
-        }
+    let unit_ty = ty::sequence_element_type(bcx_tcx(bcx), vec_ty);
+    let llunitty = type_of_or_i8(bcx, unit_ty);
+    let llvecty = T_ivec(llunitty);
+    let {bcx, val: unit_sz} = size_of(bcx, unit_ty);
+
+    let fill = if is_scaled { vecsz }
+               else { Mul(bcx, vecsz, unit_sz) };
+    let vecsize = Add(bcx, fill, llsize_of(llvecty));
+    let {bcx, val: vecptr} =
+        trans_shared_malloc(bcx, T_ptr(llvecty), vecsize);
+    add_clean_temp(bcx, vecptr, vec_ty);
+
+    Store(bcx, fill, InBoundsGEP
+          (bcx, vecptr, [C_int(0), C_uint(abi::ivec_elt_fill)]));
+    Store(bcx, fill, InBoundsGEP
+          (bcx, vecptr, [C_int(0), C_uint(abi::ivec_elt_alloc)]));
+    ret {bcx: bcx, val: vecptr,
+         unit_ty: unit_ty, llunitsz: unit_sz, llunitty: llunitty};
+}
+fn duplicate(bcx: &@block_ctxt, vptrptr: ValueRef) -> @block_ctxt {
+    let vptr = Load(bcx, vptrptr);
+    let fill = get_fill(bcx, vptr);
+    let size = Add(bcx, fill, llsize_of(T_opaque_ivec()));
+    let {bcx, val: newptr} = trans_shared_malloc(bcx, val_ty(vptr), size);
+    let bcx = call_memmove(bcx, newptr, vptr, size).bcx;
+    Store(bcx, fill,
+          InBoundsGEP(bcx, newptr, [C_int(0), C_uint(abi::ivec_elt_alloc)]));
+    Store(bcx, newptr, vptrptr);
+    ret bcx;
+}
+fn make_drop_glue(bcx: &@block_ctxt, vptrptr: ValueRef, vec_ty: ty::t)
+    -> @block_ctxt {
+    let unit_ty = ty::sequence_element_type(bcx_tcx(bcx), vec_ty);
+    let vptr = Load(bcx, vptrptr);
+    let drop_cx = new_sub_block_ctxt(bcx, ~"drop");
+    let next_cx = new_sub_block_ctxt(bcx, ~"next");
+    let null_test = IsNull(bcx, vptr);
+    CondBr(bcx, null_test, next_cx.llbb, drop_cx.llbb);
+    if ty::type_needs_drop(bcx_tcx(bcx), unit_ty) {
+        drop_cx = iter_ivec(drop_cx, vptrptr, vec_ty, trans::drop_ty).bcx;
     }
-    ret {
-        bcx: bcx,
-        unit_ty: unit_ty,
-        llunitsz: unit_sz,
-        llptr: llvecptr,
-        llfirsteltptr: llfirsteltptr};
+    drop_cx = trans::trans_shared_free(drop_cx, vptr).bcx;
+    Br(drop_cx, next_cx.llbb);
+    ret next_cx;
 }
 
-fn trans_ivec(bcx: @block_ctxt, args: &[@ast::expr],
+fn trans_ivec(bcx: &@block_ctxt, args: &[@ast::expr],
               id: ast::node_id) -> result {
-
-    let typ = node_id_type(bcx_ccx(bcx), id);
-    let alloc_res = alloc_with_heap(bcx, typ, vec::len(args));
-
-    let bcx = alloc_res.bcx;
-    let unit_ty = alloc_res.unit_ty;
-    let llunitsz = alloc_res.llunitsz;
-    let llvecptr = alloc_res.llptr;
-    let llfirsteltptr = alloc_res.llfirsteltptr;
+    let vec_ty = node_id_type(bcx_ccx(bcx), id);
+    let {bcx, val: vptr, llunitsz, unit_ty, llunitty} =
+        alloc(bcx, vec_ty, C_uint(vec::len(args)), false);
 
     // Store the individual elements.
+    let dataptr = get_dataptr(bcx, vptr, llunitty);
     let i = 0u;
-    for e: @ast::expr in args {
+    for e in args {
         let lv = trans_lval(bcx, e);
         bcx = lv.res.bcx;
-        let lleltptr;
-        if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) {
-            lleltptr =
-                bld::InBoundsGEP(bcx, llfirsteltptr,
-                                      [bld::Mul(bcx, C_uint(i), llunitsz)]);
+        let lleltptr = if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) {
+            InBoundsGEP(bcx, dataptr, [Mul(bcx, C_uint(i), llunitsz)])
         } else {
-            lleltptr = bld::InBoundsGEP(bcx, llfirsteltptr, [C_uint(i)]);
-        }
+            InBoundsGEP(bcx, dataptr, [C_uint(i)])
+        };
         bcx = move_val_if_temp(bcx, INIT, lleltptr, lv, unit_ty);
         i += 1u;
     }
-    ret rslt(bcx, llvecptr);
+    ret rslt(bcx, vptr);
 }
+fn trans_istr(bcx: &@block_ctxt, s: istr) -> result {
+    let veclen = std::istr::byte_len(s) + 1u; // +1 for \0
+    let {bcx, val: sptr, _} =
+        alloc(bcx, ty::mk_istr(bcx_tcx(bcx)), C_uint(veclen), false);
 
-// Returns the length of an interior vector and a pointer to its first
-// element, in that order.
-fn get_len_and_data(bcx: &@block_ctxt, orig_v: ValueRef, unit_ty: ty::t)
-    -> {len: ValueRef, data: ValueRef, bcx: @block_ctxt} {
-    // If this interior vector has dynamic size, we can't assume anything
-    // about the LLVM type of the value passed in, so we cast it to an
-    // opaque vector type.
-    let v;
-    if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) {
-        v = bld::PointerCast(bcx, orig_v, T_ptr(T_opaque_ivec()));
-    } else { v = orig_v; }
-
-    let llunitty = type_of_or_i8(bcx, unit_ty);
-    let stack_len =
-        load_inbounds(bcx, v, [C_int(0), C_uint(abi::ivec_elt_len)]);
-    let stack_elem =
-        bld::InBoundsGEP(bcx, v,
-                              [C_int(0), C_uint(abi::ivec_elt_elems),
-                               C_int(0)]);
-    let on_heap =
-        bld::ICmp(bcx, lib::llvm::LLVMIntEQ, stack_len, C_int(0));
-    let on_heap_cx = new_sub_block_ctxt(bcx, ~"on_heap");
-    let next_cx = new_sub_block_ctxt(bcx, ~"next");
-    bld::CondBr(bcx, on_heap, on_heap_cx.llbb, next_cx.llbb);
-    let heap_stub =
-        bld::PointerCast(on_heap_cx, v, T_ptr(T_ivec_heap(llunitty)));
-    let heap_ptr =
-        load_inbounds(on_heap_cx, heap_stub,
-                      [C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]);
-
-    // Check whether the heap pointer is null. If it is, the vector length
-    // is truly zero.
-
-    let llstubty = T_ivec_heap(llunitty);
-    let llheapptrty = struct_elt(llstubty, abi::ivec_heap_stub_elt_ptr);
-    let heap_ptr_is_null =
-        bld::ICmp(on_heap_cx, lib::llvm::LLVMIntEQ, heap_ptr,
-                              C_null(T_ptr(llheapptrty)));
-    let zero_len_cx = new_sub_block_ctxt(bcx, ~"zero_len");
-    let nonzero_len_cx = new_sub_block_ctxt(bcx, ~"nonzero_len");
-    bld::CondBr(on_heap_cx, heap_ptr_is_null, zero_len_cx.llbb,
-                            nonzero_len_cx.llbb);
-    // Technically this context is unnecessary, but it makes this function
-    // clearer.
-
-    let zero_len = C_int(0);
-    let zero_elem = C_null(T_ptr(llunitty));
-    bld::Br(zero_len_cx, next_cx.llbb);
-    // If we're here, then we actually have a heapified vector.
-
-    let heap_len =
-        load_inbounds(nonzero_len_cx, heap_ptr,
-                      [C_int(0), C_uint(abi::ivec_heap_elt_len)]);
-    let heap_elem =
-        {
-        let v =
-            [C_int(0), C_uint(abi::ivec_heap_elt_elems), C_int(0)];
-        bld::InBoundsGEP(nonzero_len_cx, heap_ptr, v)
-    };
+    let llcstr = C_cstr(bcx_ccx(bcx), s);
+    let bcx = call_memmove(bcx, get_dataptr(bcx, sptr, T_i8()),
+                           llcstr, C_uint(veclen)).bcx;
 
-    bld::Br(nonzero_len_cx, next_cx.llbb);
-    // Now we can figure out the length of `v` and get a pointer to its
-    // first element.
-
-    let len =
-        bld::Phi(next_cx, T_int(), [stack_len, zero_len, heap_len],
-                          [bcx.llbb, zero_len_cx.llbb,
-                           nonzero_len_cx.llbb]);
-    let elem =
-        bld::Phi(next_cx, T_ptr(llunitty),
-                          [stack_elem, zero_elem, heap_elem],
-                          [bcx.llbb, zero_len_cx.llbb,
-                           nonzero_len_cx.llbb]);
-    ret {len: len, data: elem, bcx: next_cx};
+    ret rslt(bcx, sptr);
 }
 
-// Returns a tuple consisting of a pointer to the newly-reserved space and
-// a block context. Updates the length appropriately.
-fn reserve_space(cx: &@block_ctxt, llunitty: TypeRef, v: ValueRef,
-                 len_needed: ValueRef) -> result {
-    let stack_len_ptr =
-        bld::InBoundsGEP(cx, v, [C_int(0), C_uint(abi::ivec_elt_len)]);
-    let stack_len = bld::Load(cx, stack_len_ptr);
-    let alen =
-        load_inbounds(cx, v, [C_int(0), C_uint(abi::ivec_elt_alen)]);
-    // There are four cases we have to consider:
-    // (1) On heap, no resize necessary.
-    // (2) On heap, need to resize.
-    // (3) On stack, no resize necessary.
-    // (4) On stack, need to spill to heap.
-
-    let maybe_on_heap =
-        bld::ICmp(cx, lib::llvm::LLVMIntEQ, stack_len, C_int(0));
-    let maybe_on_heap_cx = new_sub_block_ctxt(cx, ~"maybe_on_heap");
-    let on_stack_cx = new_sub_block_ctxt(cx, ~"on_stack");
-    bld::CondBr(cx, maybe_on_heap, maybe_on_heap_cx.llbb,
-                    on_stack_cx.llbb);
-    let next_cx = new_sub_block_ctxt(cx, ~"next");
-    // We're possibly on the heap, unless the vector is zero-length.
-
-    let stub_p = [C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)];
-    let stub_ptr =
-        bld::PointerCast(maybe_on_heap_cx, v,
-                                           T_ptr(T_ivec_heap(llunitty)));
-    let heap_ptr = load_inbounds(maybe_on_heap_cx, stub_ptr, stub_p);
-    let on_heap =
-        bld::ICmp(maybe_on_heap_cx, lib::llvm::LLVMIntNE, heap_ptr,
-                                    C_null(val_ty(heap_ptr)));
-    let on_heap_cx = new_sub_block_ctxt(cx, ~"on_heap");
-    bld::CondBr(maybe_on_heap_cx, on_heap, on_heap_cx.llbb,
-                                  on_stack_cx.llbb);
-    // We're definitely on the heap. Check whether we need to resize.
-
-    let heap_len_ptr =
-        bld::InBoundsGEP(on_heap_cx, heap_ptr,
-                                     [C_int(0),
-                                      C_uint(abi::ivec_heap_elt_len)]);
-    let heap_len = bld::Load(on_heap_cx, heap_len_ptr);
-    let new_heap_len = bld::Add(on_heap_cx, heap_len, len_needed);
-    let heap_len_unscaled =
-        bld::UDiv(on_heap_cx, heap_len, llsize_of(llunitty));
-    let heap_no_resize_needed =
-        bld::ICmp(on_heap_cx, lib::llvm::LLVMIntULE, new_heap_len, alen);
-    let heap_no_resize_cx = new_sub_block_ctxt(cx, ~"heap_no_resize");
-    let heap_resize_cx = new_sub_block_ctxt(cx, ~"heap_resize");
-    bld::CondBr(on_heap_cx, heap_no_resize_needed, heap_no_resize_cx.llbb,
-                            heap_resize_cx.llbb);
-    // Case (1): We're on the heap and don't need to resize.
-
-    let heap_data_no_resize =
-        {
-        let v =
-            [C_int(0), C_uint(abi::ivec_heap_elt_elems),
-             heap_len_unscaled];
-        bld::InBoundsGEP(heap_no_resize_cx, heap_ptr, v)
-    };
-    bld::Store(heap_no_resize_cx, new_heap_len, heap_len_ptr);
-    bld::Br(heap_no_resize_cx, next_cx.llbb);
-    // Case (2): We're on the heap and need to resize. This path is rare,
-    // so we delegate to cold glue.
-
-    {
-        let p =
-            bld::PointerCast(heap_resize_cx, v, T_ptr(T_opaque_ivec()));
-        let upcall = bcx_ccx(cx).upcalls.ivec_resize_shared;
-        bld::Call(heap_resize_cx, upcall,
-                                  [cx.fcx.lltaskptr, p, new_heap_len]);
-    }
-    let heap_ptr_resize = load_inbounds(heap_resize_cx, stub_ptr, stub_p);
-
-    let heap_data_resize =
-        {
-        let v =
-            [C_int(0), C_uint(abi::ivec_heap_elt_elems),
-             heap_len_unscaled];
-        bld::InBoundsGEP(heap_resize_cx, heap_ptr_resize, v)
-    };
-    bld::Br(heap_resize_cx, next_cx.llbb);
-    // We're on the stack. Check whether we need to spill to the heap.
-
-    let new_stack_len = bld::Add(on_stack_cx, stack_len, len_needed);
-    let stack_no_spill_needed =
-        bld::ICmp(on_stack_cx, lib::llvm::LLVMIntULE, new_stack_len,
-                               alen);
-    let stack_len_unscaled =
-        bld::UDiv(on_stack_cx, stack_len, llsize_of(llunitty));
-    let stack_no_spill_cx = new_sub_block_ctxt(cx, ~"stack_no_spill");
-    let stack_spill_cx = new_sub_block_ctxt(cx, ~"stack_spill");
-    bld::CondBr(on_stack_cx, stack_no_spill_needed,
-                             stack_no_spill_cx.llbb, stack_spill_cx.llbb);
-    // Case (3): We're on the stack and don't need to spill.
-
-    let stack_data_no_spill =
-        bld::InBoundsGEP(stack_no_spill_cx, v,
-                                            [C_int(0),
-                                             C_uint(abi::ivec_elt_elems),
-                                             stack_len_unscaled]);
-    bld::Store(stack_no_spill_cx, new_stack_len, stack_len_ptr);
-    bld::Br(stack_no_spill_cx, next_cx.llbb);
-    // Case (4): We're on the stack and need to spill. Like case (2), this
-    // path is rare, so we delegate to cold glue.
-
-    {
-        let p =
-            bld::PointerCast(stack_spill_cx, v, T_ptr(T_opaque_ivec()));
-        let upcall = bcx_ccx(cx).upcalls.ivec_spill_shared;
-        bld::Call(stack_spill_cx, upcall,
-                                  [cx.fcx.lltaskptr, p, new_stack_len]);
-    }
-    let spill_stub =
-        bld::PointerCast(stack_spill_cx, v, T_ptr(T_ivec_heap(llunitty)));
-
-    let heap_ptr_spill =
-        load_inbounds(stack_spill_cx, spill_stub, stub_p);
-
-    let heap_data_spill =
-        {
-        let v =
-            [C_int(0), C_uint(abi::ivec_heap_elt_elems),
-             stack_len_unscaled];
-        bld::InBoundsGEP(stack_spill_cx, heap_ptr_spill, v)
-    };
-    bld::Br(stack_spill_cx, next_cx.llbb);
-    // Phi together the different data pointers to get the result.
-
-    let data_ptr =
-        bld::Phi(next_cx, T_ptr(llunitty),
-                          [heap_data_no_resize, heap_data_resize,
-                           stack_data_no_spill, heap_data_spill],
-                          [heap_no_resize_cx.llbb, heap_resize_cx.llbb,
-                           stack_no_spill_cx.llbb, stack_spill_cx.llbb]);
-    ret rslt(next_cx, data_ptr);
-}
-fn trans_append(cx: &@block_ctxt, t: ty::t, lhs: ValueRef,
+fn trans_append(cx: &@block_ctxt, vec_ty: ty::t, lhsptr: ValueRef,
                 rhs: ValueRef) -> result {
     // Cast to opaque interior vector types if necessary.
-    if ty::type_has_dynamic_size(bcx_tcx(cx), t) {
-        lhs = bld::PointerCast(cx, lhs, T_ptr(T_opaque_ivec()));
-        rhs = bld::PointerCast(cx, rhs, T_ptr(T_opaque_ivec()));
+    let unit_ty = ty::sequence_element_type(bcx_tcx(cx), vec_ty);
+    let dynamic = ty::type_has_dynamic_size(bcx_tcx(cx), unit_ty);
+    if dynamic {
+        lhsptr = PointerCast(cx, lhsptr, T_ptr(T_ptr(T_opaque_ivec())));
+        rhs = PointerCast(cx, rhs, T_ptr(T_opaque_ivec()));
     }
-
-    let unit_ty = ty::sequence_element_type(bcx_tcx(cx), t);
-    let llunitty = type_of_or_i8(cx, unit_ty);
-
-    let rs = size_of(cx, unit_ty);
-    let bcx = rs.bcx;
-    let unit_sz = rs.val;
-
-    // Gather the various type descriptors we'll need.
-
-    // FIXME (issue #511): This is needed to prevent a leak.
-    let no_tydesc_info = none;
-
-    rs = get_tydesc(bcx, t, false, tps_normal, no_tydesc_info).result;
-    bcx = rs.bcx;
-    rs = get_tydesc(bcx, unit_ty, false, tps_normal, no_tydesc_info).result;
-    bcx = rs.bcx;
-    lazily_emit_tydesc_glue(bcx, abi::tydesc_field_take_glue, none);
-    lazily_emit_tydesc_glue(bcx, abi::tydesc_field_drop_glue, none);
-    lazily_emit_tydesc_glue(bcx, abi::tydesc_field_free_glue, none);
-    lazily_emit_tydesc_glue(bcx, abi::tydesc_field_copy_glue, none);
-    let rhs_len_and_data = get_len_and_data(bcx, rhs, unit_ty);
-    let rhs_len = rhs_len_and_data.len;
-    let rhs_data = rhs_len_and_data.data;
-    bcx = rhs_len_and_data.bcx;
-
-    let have_istrs = alt ty::struct(bcx_tcx(cx), t) {
+    let strings = alt ty::struct(bcx_tcx(cx), vec_ty) {
       ty::ty_istr. { true }
       ty::ty_vec(_) { false }
-      _ { bcx_tcx(cx).sess.bug(~"non-istr/ivec in trans_append"); }
-    };
-
-    let extra_len = if have_istrs {
-        // Only need one of the nulls
-        bld::Sub(bcx, rhs_len, C_uint(1u))
-    } else { rhs_len };
-
-    rs = reserve_space(bcx, llunitty, lhs, extra_len);
-    bcx = rs.bcx;
-
-    let lhs_data = if have_istrs {
-        let lhs_data = rs.val;
-        let lhs_data_without_null_ptr = alloca(bcx, T_ptr(llunitty));
-        incr_ptr(bcx, lhs_data, C_int(-1),
-                 lhs_data_without_null_ptr);
-        bld::Load(bcx, lhs_data_without_null_ptr)
-    } else {
-        rs.val
     };
 
-    // If rhs is lhs then our rhs pointer may have changed
-    rhs_len_and_data = get_len_and_data(bcx, rhs, unit_ty);
-    rhs_data = rhs_len_and_data.data;
-    bcx = rhs_len_and_data.bcx;
-
-    // Work out the end pointer.
-
-    let lhs_unscaled_idx = bld::UDiv(bcx, rhs_len, llsize_of(llunitty));
-    let lhs_end = bld::InBoundsGEP(bcx, lhs_data, [lhs_unscaled_idx]);
-    // Now emit the copy loop.
+    let {bcx, val: unit_sz} = size_of(cx, unit_ty);
+    let llunitty = type_of_or_i8(cx, unit_ty);
 
-    let dest_ptr = alloca(bcx, T_ptr(llunitty));
-    bld::Store(bcx, lhs_data, dest_ptr);
-    let src_ptr = alloca(bcx, T_ptr(llunitty));
-    bld::Store(bcx, rhs_data, src_ptr);
-    let copy_loop_header_cx = new_sub_block_ctxt(bcx, ~"copy_loop_header");
-    bld::Br(bcx, copy_loop_header_cx.llbb);
-    let copy_dest_ptr = bld::Load(copy_loop_header_cx, dest_ptr);
-    let not_yet_at_end =
-        bld::ICmp(copy_loop_header_cx, lib::llvm::LLVMIntNE,
-                                       copy_dest_ptr, lhs_end);
-    let copy_loop_body_cx = new_sub_block_ctxt(bcx, ~"copy_loop_body");
+    let lhs = Load(bcx, lhsptr);
+    let self_append = ICmp(bcx, lib::llvm::LLVMIntEQ, lhs, rhs);
+    let lfill = get_fill(bcx, lhs);
+    let rfill = get_fill(bcx, rhs);
+    let new_fill = Add(bcx, lfill, rfill);
+    if strings { new_fill = Sub(bcx, new_fill, C_int(1)); }
+    let opaque_lhs = PointerCast(bcx, lhsptr, T_ptr(T_ptr(T_opaque_ivec())));
+    Call(bcx, bcx_ccx(cx).upcalls.ivec_grow,
+         [cx.fcx.lltaskptr, opaque_lhs, new_fill]);
+    // Was overwritten if we resized
+    let lhs = Load(bcx, lhsptr);
+    let rhs = Select(bcx, self_append, lhs, rhs);
+
+    let lhs_data = get_dataptr(bcx, lhs, llunitty);
+    let lhs_off = lfill;
+    if strings { lhs_off = Sub(bcx, lfill, C_int(1)); }
+    let write_ptr = pointer_add(bcx, lhs_data, lhs_off);
+    let write_ptr_ptr = do_spill(bcx, write_ptr);
+    let end_ptr = pointer_add(bcx, write_ptr, rfill);
+    let read_ptr_ptr = do_spill(bcx, get_dataptr(bcx, rhs, llunitty));
+
+    let header_cx = new_sub_block_ctxt(bcx, ~"copy_loop_header");
+    Br(bcx, header_cx.llbb);
+    let write_ptr = Load(header_cx, write_ptr_ptr);
+    let not_yet_at_end = ICmp(header_cx, lib::llvm::LLVMIntNE,
+                              write_ptr, end_ptr);
+    let body_cx = new_sub_block_ctxt(bcx, ~"copy_loop_body");
     let next_cx = new_sub_block_ctxt(bcx, ~"next");
-    bld::CondBr(copy_loop_header_cx, not_yet_at_end,
-                                     copy_loop_body_cx.llbb,
-                                     next_cx.llbb);
-
-    let copy_src_ptr = bld::Load(copy_loop_body_cx, src_ptr);
-    let copy_src =
-        load_if_immediate(copy_loop_body_cx, copy_src_ptr, unit_ty);
+    CondBr(header_cx, not_yet_at_end,
+           body_cx.llbb, next_cx.llbb);
 
-    let post_copy_cx = copy_val
-        (copy_loop_body_cx, INIT, copy_dest_ptr, copy_src, unit_ty);
+    let read_ptr = Load(body_cx, read_ptr_ptr);
+    let body_cx = copy_val(body_cx, INIT, write_ptr,
+                           load_if_immediate(body_cx, read_ptr, unit_ty),
+                           unit_ty);
     // Increment both pointers.
-    if ty::type_has_dynamic_size(bcx_tcx(cx), t) {
+    if dynamic {
         // We have to increment by the dynamically-computed size.
-        incr_ptr(post_copy_cx, copy_dest_ptr, unit_sz, dest_ptr);
-        incr_ptr(post_copy_cx, copy_src_ptr, unit_sz, src_ptr);
+        incr_ptr(body_cx, write_ptr, unit_sz, write_ptr_ptr);
+        incr_ptr(body_cx, read_ptr, unit_sz, read_ptr_ptr);
     } else {
-        incr_ptr(post_copy_cx, copy_dest_ptr, C_int(1), dest_ptr);
-        incr_ptr(post_copy_cx, copy_src_ptr, C_int(1), src_ptr);
+        incr_ptr(body_cx, write_ptr, C_int(1), write_ptr_ptr);
+        incr_ptr(body_cx, read_ptr, C_int(1), read_ptr_ptr);
     }
-
-    bld::Br(post_copy_cx, copy_loop_header_cx.llbb);
+    Br(body_cx, header_cx.llbb);
     ret rslt(next_cx, C_nil());
 }
 
-fn trans_append_literal(bcx: &@block_ctxt, v: ValueRef, vec_ty: ty::t,
+fn trans_append_literal(bcx: &@block_ctxt, vptrptr: ValueRef, vec_ty: ty::t,
                         vals: &[@ast::expr]) -> @block_ctxt {
     let elt_ty = ty::sequence_element_type(bcx_tcx(bcx), vec_ty);
     let ti = none;
     let {bcx, val: td} =
         get_tydesc(bcx, elt_ty, false, tps_normal, ti).result;
-    trans::lazily_emit_all_tydesc_glue(bcx, ti);
-    let opaque_v = bld::PointerCast(bcx, v, T_ptr(T_opaque_ivec()));
+    trans::lazily_emit_tydesc_glue(bcx, abi::tydesc_field_take_glue, ti);
+    let opaque_v = PointerCast(bcx, vptrptr, T_ptr(T_ptr(T_opaque_ivec())));
     for val in vals {
         let {bcx: e_bcx, val: elt} = trans::trans_expr(bcx, val);
         bcx = e_bcx;
         let spilled = trans::spill_if_immediate(bcx, elt, elt_ty);
-        bld::Call(bcx, bcx_ccx(bcx).upcalls.ivec_push,
-                       [bcx.fcx.lltaskptr, opaque_v, td,
-                        bld::PointerCast(bcx, spilled, T_ptr(T_i8()))]);
+        Call(bcx, bcx_ccx(bcx).upcalls.ivec_push,
+             [bcx.fcx.lltaskptr, opaque_v, td,
+              PointerCast(bcx, spilled, T_ptr(T_i8()))]);
     }
     ret bcx;
 }
 
-type alloc_result =
-    {bcx: @block_ctxt,
-     llptr: ValueRef,
-     llunitsz: ValueRef,
-     llalen: ValueRef};
-
-fn alloc(cx: &@block_ctxt, unit_ty: ty::t) -> alloc_result {
-    let dynamic = ty::type_has_dynamic_size(bcx_tcx(cx), unit_ty);
-
-    let bcx;
-    if dynamic {
-        bcx = llderivedtydescs_block_ctxt(cx.fcx);
-    } else { bcx = cx; }
-
-    let llunitsz;
-    let rslt = size_of(bcx, unit_ty);
-    bcx = rslt.bcx;
-    llunitsz = rslt.val;
-
-    if dynamic { cx.fcx.llderivedtydescs = bcx.llbb; }
-
-    let llalen =
-        bld::Mul(bcx, llunitsz, C_uint(abi::ivec_default_length));
-
-    let llptr;
-    let llunitty = type_of_or_i8(bcx, unit_ty);
-    let bcx_result;
-    if dynamic {
-        let llarraysz = bld::Add(bcx, llsize_of(T_opaque_ivec()), llalen);
-        let llvecptr = array_alloca(bcx, T_i8(), llarraysz);
-
-        bcx_result = cx;
-        llptr =
-            bld::PointerCast(bcx_result, llvecptr,
-                                         T_ptr(T_opaque_ivec()));
-    } else { llptr = alloca(bcx, T_ivec(llunitty)); bcx_result = bcx; }
-
-    ret {bcx: bcx_result,
-         llptr: llptr,
-         llunitsz: llunitsz,
-         llalen: llalen};
-}
-
-fn trans_add(cx: &@block_ctxt, vec_ty: ty::t, lhs: ValueRef,
+fn trans_add(bcx: &@block_ctxt, vec_ty: ty::t, lhs: ValueRef,
              rhs: ValueRef) -> result {
-    let bcx = cx;
-    let unit_ty = ty::sequence_element_type(bcx_tcx(bcx), vec_ty);
-
-    let ares = alloc(bcx, unit_ty);
-    bcx = ares.bcx;
-    let llvecptr = ares.llptr;
-    let unit_sz = ares.llunitsz;
-    let llalen = ares.llalen;
-
-    add_clean_temp(bcx, llvecptr, vec_ty);
-
-    let llunitty = type_of_or_i8(bcx, unit_ty);
-    let llheappartty = T_ivec_heap_part(llunitty);
-    let lhs_len_and_data = get_len_and_data(bcx, lhs, unit_ty);
-    let lhs_len = lhs_len_and_data.len;
-    let lhs_data = lhs_len_and_data.data;
-    bcx = lhs_len_and_data.bcx;
-
-    lhs_len = alt ty::struct(bcx_tcx(bcx), vec_ty) {
-      ty::ty_istr. {
-        // Forget about the trailing null on the left side
-        bld::Sub(bcx, lhs_len, C_uint(1u))
-      }
-      ty::ty_vec(_) { lhs_len }
-      _ { bcx_tcx(bcx).sess.bug(~"non-istr/ivec in trans_add") }
+    let strings = alt ty::struct(bcx_tcx(bcx), vec_ty) {
+      ty::ty_istr. { true }
+      ty::ty_vec(_) { false }
     };
+    let lhs_fill = get_fill(bcx, lhs);
+    if strings { lhs_fill = Sub(bcx, lhs_fill, C_int(1)); }
+    let rhs_fill = get_fill(bcx, rhs);
+    let new_fill = Add(bcx, lhs_fill, rhs_fill);
+    let {bcx, val: new_vec, unit_ty, llunitsz, llunitty} =
+        alloc(bcx, vec_ty, new_fill, true);
+
+    // Emit the copy loop
+    let write_ptr_ptr = do_spill(bcx, get_dataptr(bcx, new_vec, llunitty));
+    let lhs_ptr = get_dataptr(bcx, lhs, llunitty);
+    let lhs_ptr_ptr = do_spill(bcx, lhs_ptr);
+    let lhs_end_ptr = pointer_add(bcx, lhs_ptr, lhs_fill);
+    let rhs_ptr = get_dataptr(bcx, rhs, llunitty);
+    let rhs_ptr_ptr = do_spill(bcx, rhs_ptr);
+    let rhs_end_ptr = pointer_add(bcx, rhs_ptr, rhs_fill);
 
-    let rhs_len_and_data = get_len_and_data(bcx, rhs, unit_ty);
-    let rhs_len = rhs_len_and_data.len;
-    let rhs_data = rhs_len_and_data.data;
-    bcx = rhs_len_and_data.bcx;
-    let lllen = bld::Add(bcx, lhs_len, rhs_len);
-    // We have three cases to handle here:
-    // (1) Length is zero ([] + []).
-    // (2) Copy onto stack.
-    // (3) Allocate on heap and copy there.
-
-    let len_is_zero =
-        bld::ICmp(bcx, lib::llvm::LLVMIntEQ, lllen, C_int(0));
-    let zero_len_cx = new_sub_block_ctxt(bcx, ~"zero_len");
-    let nonzero_len_cx = new_sub_block_ctxt(bcx, ~"nonzero_len");
-    bld::CondBr(bcx, len_is_zero, zero_len_cx.llbb, nonzero_len_cx.llbb);
-    // Case (1): Length is zero.
-
-    let stub_z = [C_int(0), C_uint(abi::ivec_heap_stub_elt_zero)];
-    let stub_a = [C_int(0), C_uint(abi::ivec_heap_stub_elt_alen)];
-    let stub_p = [C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)];
-
-    let vec_l = [C_int(0), C_uint(abi::ivec_elt_len)];
-    let vec_a = [C_int(0), C_uint(abi::ivec_elt_alen)];
-
-    let stub_ptr_zero =
-        bld::PointerCast(zero_len_cx, llvecptr,
-                                      T_ptr(T_ivec_heap(llunitty)));
-    bld::Store(zero_len_cx, C_int(0),
-                            bld::InBoundsGEP(zero_len_cx, stub_ptr_zero,
-                                                          stub_z));
-    bld::Store(zero_len_cx, llalen,
-                            bld::InBoundsGEP(zero_len_cx, stub_ptr_zero,
-                                                          stub_a));
-    bld::Store(zero_len_cx, C_null(T_ptr(llheappartty)),
-                            bld::InBoundsGEP(zero_len_cx, stub_ptr_zero,
-                                                          stub_p));
-    let next_cx = new_sub_block_ctxt(bcx, ~"next");
-    bld::Br(zero_len_cx, next_cx.llbb);
-    // Determine whether we need to spill to the heap.
-
-    let on_stack =
-        bld::ICmp(nonzero_len_cx, lib::llvm::LLVMIntULE, lllen, llalen);
-    let stack_cx = new_sub_block_ctxt(bcx, ~"stack");
-    let heap_cx = new_sub_block_ctxt(bcx, ~"heap");
-    bld::CondBr(nonzero_len_cx, on_stack, stack_cx.llbb, heap_cx.llbb);
-    // Case (2): Copy onto stack.
-
-    bld::Store(stack_cx, lllen,
-                         bld::InBoundsGEP(stack_cx, llvecptr, vec_l));
-    bld::Store(stack_cx, llalen,
-                         bld::InBoundsGEP(stack_cx, llvecptr, vec_a));
-    let dest_ptr_stack =
-        bld::InBoundsGEP(stack_cx, llvecptr,
-                                   [C_int(0), C_uint(abi::ivec_elt_elems),
-                                    C_int(0)]);
-    let copy_cx = new_sub_block_ctxt(bcx, ~"copy");
-    bld::Br(stack_cx, copy_cx.llbb);
-    // Case (3): Allocate on heap and copy there.
-
-    let stub_ptr_heap =
-        bld::PointerCast(heap_cx, llvecptr, T_ptr(T_ivec_heap(llunitty)));
-    bld::Store(heap_cx, C_int(0),
-                        bld::InBoundsGEP(heap_cx, stub_ptr_heap, stub_z));
-    bld::Store(heap_cx, lllen,
-                        bld::InBoundsGEP(heap_cx, stub_ptr_heap, stub_a));
-    let heap_sz = bld::Add(heap_cx, llsize_of(llheappartty), lllen);
-    let rs = trans_shared_malloc(heap_cx, T_ptr(llheappartty), heap_sz);
-    let heap_part = rs.val;
-    heap_cx = rs.bcx;
-    bld::Store(heap_cx, heap_part,
-                        bld::InBoundsGEP(heap_cx, stub_ptr_heap, stub_p));
-    {
-        let v = [C_int(0), C_uint(abi::ivec_heap_elt_len)];
-        bld::Store(heap_cx, lllen,
-                            bld::InBoundsGEP(heap_cx, heap_part, v));
-    }
-    let dest_ptr_heap =
-        bld::InBoundsGEP(heap_cx, heap_part,
-                                  [C_int(0),
-                                   C_uint(abi::ivec_heap_elt_elems),
-                                   C_int(0)]);
-    bld::Br(heap_cx, copy_cx.llbb);
-    // Emit the copy loop.
-
-    let first_dest_ptr =
-        bld::Phi(copy_cx, T_ptr(llunitty),
-                          [dest_ptr_stack, dest_ptr_heap],
-                          [stack_cx.llbb, heap_cx.llbb]);
-
-    let lhs_end_ptr;
-    let rhs_end_ptr;
-    if ty::type_has_dynamic_size(bcx_tcx(cx), unit_ty) {
-        lhs_end_ptr = bld::InBoundsGEP(copy_cx, lhs_data, [lhs_len]);
-        rhs_end_ptr = bld::InBoundsGEP(copy_cx, rhs_data, [rhs_len]);
-    } else {
-        let lhs_len_unscaled = bld::UDiv(copy_cx, lhs_len, unit_sz);
-        lhs_end_ptr =
-            bld::InBoundsGEP(copy_cx, lhs_data, [lhs_len_unscaled]);
-        let rhs_len_unscaled = bld::UDiv(copy_cx, rhs_len, unit_sz);
-        rhs_end_ptr =
-            bld::InBoundsGEP(copy_cx, rhs_data, [rhs_len_unscaled]);
-    }
-
-    let dest_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
-    bld::Store(copy_cx, first_dest_ptr, dest_ptr_ptr);
-    let lhs_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
-    bld::Store(copy_cx, lhs_data, lhs_ptr_ptr);
-    let rhs_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
-    bld::Store(copy_cx, rhs_data, rhs_ptr_ptr);
-    let lhs_copy_cx = new_sub_block_ctxt(bcx, ~"lhs_copy");
-    bld::Br(copy_cx, lhs_copy_cx.llbb);
     // Copy in elements from the LHS.
-
-    let lhs_ptr = bld::Load(lhs_copy_cx, lhs_ptr_ptr);
+    let lhs_cx = new_sub_block_ctxt(bcx, ~"lhs_copy_header");
+    Br(bcx, lhs_cx.llbb);
+    let lhs_ptr = Load(lhs_cx, lhs_ptr_ptr);
     let not_at_end_lhs =
-        bld::ICmp(lhs_copy_cx, lib::llvm::LLVMIntNE, lhs_ptr,
-                               lhs_end_ptr);
-    let lhs_do_copy_cx = new_sub_block_ctxt(bcx, ~"lhs_do_copy");
-    let rhs_copy_cx = new_sub_block_ctxt(bcx, ~"rhs_copy");
-    bld::CondBr(lhs_copy_cx, not_at_end_lhs, lhs_do_copy_cx.llbb,
-                             rhs_copy_cx.llbb);
-    let dest_ptr_lhs_copy = bld::Load(lhs_do_copy_cx, dest_ptr_ptr);
-    let lhs_val = load_if_immediate(lhs_do_copy_cx, lhs_ptr, unit_ty);
-    lhs_do_copy_cx = copy_val(lhs_do_copy_cx, INIT, dest_ptr_lhs_copy,
-                              lhs_val, unit_ty);
-
+        ICmp(lhs_cx, lib::llvm::LLVMIntNE, lhs_ptr, lhs_end_ptr);
+    let lhs_copy_cx = new_sub_block_ctxt(bcx, ~"lhs_copy_body");
+    let rhs_cx = new_sub_block_ctxt(bcx, ~"rhs_copy_header");
+    CondBr(lhs_cx, not_at_end_lhs, lhs_copy_cx.llbb, rhs_cx.llbb);
+    let write_ptr = Load(lhs_copy_cx, write_ptr_ptr);
+    lhs_copy_cx =
+        copy_val(lhs_copy_cx, INIT, write_ptr,
+                 load_if_immediate(lhs_copy_cx, lhs_ptr, unit_ty), unit_ty);
     // Increment both pointers.
-    if ty::type_has_dynamic_size(bcx_tcx(cx), unit_ty) {
+    if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) {
         // We have to increment by the dynamically-computed size.
-        incr_ptr(lhs_do_copy_cx, dest_ptr_lhs_copy, unit_sz,
-                 dest_ptr_ptr);
-        incr_ptr(lhs_do_copy_cx, lhs_ptr, unit_sz, lhs_ptr_ptr);
+        incr_ptr(lhs_copy_cx, write_ptr, llunitsz, write_ptr_ptr);
+        incr_ptr(lhs_copy_cx, lhs_ptr, llunitsz, lhs_ptr_ptr);
     } else {
-        incr_ptr(lhs_do_copy_cx, dest_ptr_lhs_copy, C_int(1),
-                 dest_ptr_ptr);
-        incr_ptr(lhs_do_copy_cx, lhs_ptr, C_int(1), lhs_ptr_ptr);
+        incr_ptr(lhs_copy_cx, write_ptr, C_int(1), write_ptr_ptr);
+        incr_ptr(lhs_copy_cx, lhs_ptr, C_int(1), lhs_ptr_ptr);
     }
+    Br(lhs_copy_cx, lhs_cx.llbb);
 
-    bld::Br(lhs_do_copy_cx, lhs_copy_cx.llbb);
     // Copy in elements from the RHS.
-
-    let rhs_ptr = bld::Load(rhs_copy_cx, rhs_ptr_ptr);
+    let rhs_ptr = Load(rhs_cx, rhs_ptr_ptr);
     let not_at_end_rhs =
-        bld::ICmp(rhs_copy_cx, lib::llvm::LLVMIntNE, rhs_ptr,
-                               rhs_end_ptr);
-    let rhs_do_copy_cx = new_sub_block_ctxt(bcx, ~"rhs_do_copy");
-    bld::CondBr(rhs_copy_cx, not_at_end_rhs, rhs_do_copy_cx.llbb,
-                             next_cx.llbb);
-    let dest_ptr_rhs_copy = bld::Load(rhs_do_copy_cx, dest_ptr_ptr);
-    let rhs_val = load_if_immediate(rhs_do_copy_cx, rhs_ptr, unit_ty);
-    rhs_do_copy_cx = copy_val(rhs_do_copy_cx, INIT, dest_ptr_rhs_copy,
-                              rhs_val, unit_ty);
-
+        ICmp(rhs_cx, lib::llvm::LLVMIntNE, rhs_ptr, rhs_end_ptr);
+    let rhs_copy_cx = new_sub_block_ctxt(bcx, ~"rhs_copy_body");
+    let next_cx = new_sub_block_ctxt(bcx, ~"next");
+    CondBr(rhs_cx, not_at_end_rhs, rhs_copy_cx.llbb, next_cx.llbb);
+    let write_ptr = Load(rhs_copy_cx, write_ptr_ptr);
+    rhs_copy_cx =
+        copy_val(rhs_copy_cx, INIT, write_ptr,
+                 load_if_immediate(rhs_copy_cx, rhs_ptr, unit_ty), unit_ty);
     // Increment both pointers.
-    if ty::type_has_dynamic_size(bcx_tcx(cx), unit_ty) {
+    if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) {
         // We have to increment by the dynamically-computed size.
-        incr_ptr(rhs_do_copy_cx, dest_ptr_rhs_copy, unit_sz,
-                 dest_ptr_ptr);
-        incr_ptr(rhs_do_copy_cx, rhs_ptr, unit_sz, rhs_ptr_ptr);
+        incr_ptr(rhs_copy_cx, write_ptr, llunitsz, write_ptr_ptr);
+        incr_ptr(rhs_copy_cx, rhs_ptr, llunitsz, rhs_ptr_ptr);
     } else {
-        incr_ptr(rhs_do_copy_cx, dest_ptr_rhs_copy, C_int(1),
-                 dest_ptr_ptr);
-        incr_ptr(rhs_do_copy_cx, rhs_ptr, C_int(1), rhs_ptr_ptr);
+        incr_ptr(rhs_copy_cx, write_ptr, C_int(1), write_ptr_ptr);
+        incr_ptr(rhs_copy_cx, rhs_ptr, C_int(1), rhs_ptr_ptr);
     }
+    Br(rhs_copy_cx, rhs_cx.llbb);
 
-    bld::Br(rhs_do_copy_cx, rhs_copy_cx.llbb);
-    // Finally done!
-
-    ret rslt(next_cx, llvecptr);
+    ret rslt(next_cx, new_vec);
 }
 
-// NB: This does *not* adjust reference counts. The caller must have done
-// this via take_ty() beforehand.
-fn duplicate_heap_part(cx: &@block_ctxt, orig_vptr: ValueRef,
-                       unit_ty: ty::t) -> result {
-    // Cast to an opaque interior vector if we can't trust the pointer
-    // type.
-    let vptr;
-    if ty::type_has_dynamic_size(bcx_tcx(cx), unit_ty) {
-        vptr = bld::PointerCast(cx, orig_vptr, T_ptr(T_opaque_ivec()));
-    } else { vptr = orig_vptr; }
-
-    let llunitty = type_of_or_i8(cx, unit_ty);
-    let llheappartty = T_ivec_heap_part(llunitty);
-
-    // Check to see if the vector is heapified.
-    let stack_len_ptr =
-        bld::InBoundsGEP(cx, vptr, [C_int(0), C_uint(abi::ivec_elt_len)]);
-    let stack_len = bld::Load(cx, stack_len_ptr);
-    let stack_len_is_zero =
-        bld::ICmp(cx, lib::llvm::LLVMIntEQ, stack_len, C_int(0));
-    let maybe_on_heap_cx = new_sub_block_ctxt(cx, ~"maybe_on_heap");
-    let next_cx = new_sub_block_ctxt(cx, ~"next");
-    bld::CondBr(cx, stack_len_is_zero, maybe_on_heap_cx.llbb,
-                    next_cx.llbb);
-
-    let stub_ptr =
-        bld::PointerCast(maybe_on_heap_cx, vptr,
-                                           T_ptr(T_ivec_heap(llunitty)));
-    let heap_ptr_ptr =
-        bld::InBoundsGEP(maybe_on_heap_cx,
-            stub_ptr,
-            [C_int(0),
-             C_uint(abi::ivec_heap_stub_elt_ptr)]);
-    let heap_ptr = bld::Load(maybe_on_heap_cx, heap_ptr_ptr);
-    let heap_ptr_is_nonnull =
-        bld::ICmp(maybe_on_heap_cx, lib::llvm::LLVMIntNE, heap_ptr,
-                                    C_null(T_ptr(llheappartty)));
-    let on_heap_cx = new_sub_block_ctxt(cx, ~"on_heap");
-    bld::CondBr(maybe_on_heap_cx, heap_ptr_is_nonnull, on_heap_cx.llbb,
-                                  next_cx.llbb);
-
-    // Ok, the vector is on the heap. Copy the heap part.
-    let alen_ptr =
-        bld::InBoundsGEP(on_heap_cx, stub_ptr,
-            [C_int(0),
-             C_uint(abi::ivec_heap_stub_elt_alen)]);
-    let alen = bld::Load(on_heap_cx, alen_ptr);
-
-    let heap_part_sz =
-        bld::Add(on_heap_cx, alen, llsize_of(T_opaque_ivec_heap_part()));
-    let rs =
-        trans_shared_malloc(on_heap_cx, T_ptr(llheappartty),
-                            heap_part_sz);
-    on_heap_cx = rs.bcx;
-    let new_heap_ptr = rs.val;
-
-    rs = call_memmove(on_heap_cx, new_heap_ptr, heap_ptr, heap_part_sz);
-    on_heap_cx = rs.bcx;
-
-    bld::Store(on_heap_cx, new_heap_ptr, heap_ptr_ptr);
-    bld::Br(on_heap_cx, next_cx.llbb);
+// FIXME factor out a utility that can be used to create the loops built
+// above
+fn iter_ivec(bcx: &@block_ctxt, vptrptr: ValueRef, vec_ty: ty::t,
+             f: &trans::val_and_ty_fn) -> result {
+    let unit_ty = ty::sequence_element_type(bcx_tcx(bcx), vec_ty);
+    let llunitty = type_of_or_i8(bcx, unit_ty);
+    let {bcx, val: unit_sz} = size_of(bcx, unit_ty);
+
+    let vptr = Load(bcx, PointerCast(bcx, vptrptr,
+                                     T_ptr(T_ptr(T_ivec(llunitty)))));
+    let fill = get_fill(bcx, vptr);
+    let data_ptr = get_dataptr(bcx, vptr, llunitty);
+
+    // Calculate the last pointer address we want to handle.
+    // TODO: Optimize this when the size of the unit type is statically
+    // known to not use pointer casts, which tend to confuse LLVM.
+    let data_end_ptr = pointer_add(bcx, data_ptr, fill);
+    let data_ptr_ptr = do_spill(bcx, data_ptr);
+
+    // Now perform the iteration.
+    let header_cx = new_sub_block_ctxt(bcx, ~"iter_ivec_loop_header");
+    Br(bcx, header_cx.llbb);
+    let data_ptr = Load(header_cx, data_ptr_ptr);
+    let not_yet_at_end = ICmp(header_cx, lib::llvm::LLVMIntULT,
+                              data_ptr, data_end_ptr);
+    let body_cx = new_sub_block_ctxt(bcx, ~"iter_ivec_loop_body");
+    let next_cx = new_sub_block_ctxt(bcx, ~"iter_ivec_next");
+    CondBr(header_cx, not_yet_at_end, body_cx.llbb, next_cx.llbb);
+    body_cx = f(body_cx, data_ptr, unit_ty).bcx;
+    let increment = if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) {
+        unit_sz
+    } else { C_int(1) };
+    incr_ptr(body_cx, data_ptr, increment, data_ptr_ptr);
+    Br(body_cx, header_cx.llbb);
 
     ret rslt(next_cx, C_nil());
 }
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index eec50b5f454..2574a0837dc 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -158,13 +158,13 @@ export type_kind;
 export type_err;
 export type_err_to_str;
 export type_has_dynamic_size;
-export type_needs_copy_glue;
 export type_has_pointers;
 export type_needs_drop;
 export type_is_bool;
 export type_is_bot;
 export type_is_box;
 export type_is_boxed;
+export type_is_ivec;
 export type_is_fp;
 export type_is_integral;
 export type_is_native;
@@ -178,7 +178,6 @@ export type_is_copyable;
 export type_is_tup_like;
 export type_is_str;
 export type_is_unique;
-export type_owns_heap_mem;
 export type_autoderef;
 export type_param;
 export unify;
@@ -226,7 +225,6 @@ type ctxt =
       short_names_cache: hashmap<t, @istr>,
       has_pointer_cache: hashmap<t, bool>,
       kind_cache: hashmap<t, ast::kind>,
-      owns_heap_mem_cache: hashmap<t, bool>,
       ast_ty_to_ty_cache: hashmap<@ast::ty, option::t<t>>};
 
 type ty_ctxt = ctxt;
@@ -418,7 +416,6 @@ fn mk_ctxt(s: session::session, dm: resolve::def_map,
           short_names_cache: map::mk_hashmap(ty::hash_ty, ty::eq_ty),
           has_pointer_cache: map::mk_hashmap(ty::hash_ty, ty::eq_ty),
           kind_cache: map::mk_hashmap(ty::hash_ty, ty::eq_ty),
-          owns_heap_mem_cache: map::mk_hashmap(ty::hash_ty, ty::eq_ty),
           ast_ty_to_ty_cache: map::mk_hashmap(ast_util::hash_ty,
                                               ast_util::eq_ty)};
     populate_type_store(cx);
@@ -828,8 +825,6 @@ fn type_is_structural(cx: &ctxt, ty: t) -> bool {
       ty_fn(_, _, _, _, _) { ret true; }
       ty_obj(_) { ret true; }
       ty_res(_, _, _) { ret true; }
-      ty_vec(_) { ret true; }
-      ty_istr. { ret true; }
       _ { ret false; }
     }
 }
@@ -861,8 +856,6 @@ fn type_is_str(cx: &ctxt, ty: t) -> bool {
 
 fn sequence_is_interior(cx: &ctxt, ty: t) -> bool {
     alt struct(cx, ty) {
-
-
       ty::ty_str. {
         ret false;
       }
@@ -919,8 +912,20 @@ fn type_is_boxed(cx: &ctxt, ty: t) -> bool {
     }
 }
 
+fn type_is_ivec(cx: &ctxt, ty: t) -> bool {
+    ret alt struct(cx, ty) {
+      ty_vec(_) { true }
+      ty_istr. { true }
+      _ { false }
+    };
+}
+
 fn type_is_unique(cx: &ctxt, ty: t) -> bool {
-    alt struct(cx, ty) { ty_uniq(_) { ret true; } _ { ret false; } }
+    alt struct(cx, ty) {
+      ty_uniq(_) { ret true; }
+      ty_vec(_) { true }
+      ty_istr. { true }
+      _ { ret false; } }
 }
 
 fn type_is_scalar(cx: &ctxt, ty: t) -> bool {
@@ -947,13 +952,8 @@ fn type_has_pointers(cx: &ctxt, ty: t) -> bool {
 
     let result = false;
     alt struct(cx, ty) {
-
-
       // scalar types
-      ty_nil. {
-        /* no-op */
-
-      }
+      ty_nil. {/* no-op */ }
       ty_bot. {/* no-op */ }
       ty_bool. {/* no-op */ }
       ty_int. {/* no-op */ }
@@ -996,6 +996,7 @@ fn type_has_pointers(cx: &ctxt, ty: t) -> bool {
 fn type_needs_drop(cx: &ctxt, ty: t) -> bool {
     ret alt struct(cx, ty) {
           ty_res(_, _, _) { true }
+          ty_param(_, _) { true }
           _ { type_has_pointers(cx, ty) }
         };
 }
@@ -1152,7 +1153,6 @@ fn type_structurally_contains(cx: &ctxt, ty: t,
         }
         ret false;
       }
-      ty_vec(mt) { ret type_structurally_contains(cx, mt.ty, test); }
       ty_rec(fields) {
         for field in fields {
             if type_structurally_contains(cx, field.mt.ty, test) { ret true; }
@@ -1182,17 +1182,6 @@ fn type_has_dynamic_size(cx: &ctxt, ty: t) -> bool {
     });
 }
 
-fn type_needs_copy_glue(cx: &ctxt, ty: t) -> bool {
-    ret type_structurally_contains(cx, ty, fn(sty: &sty) -> bool {
-        ret alt sty {
-          ty_param(_, _) { true }
-          ty_vec(_) { true }
-          ty_istr. { true }
-          _ { false }
-        };
-    });
-}
-
 fn type_is_integral(cx: &ctxt, ty: t) -> bool {
     alt struct(cx, ty) {
       ty_int. { ret true; }
@@ -1246,82 +1235,6 @@ fn type_is_signed(cx: &ctxt, ty: t) -> bool {
     }
 }
 
-fn type_owns_heap_mem(cx: &ctxt, ty: t) -> bool {
-    alt cx.owns_heap_mem_cache.find(ty) {
-      some(result) { ret result; }
-      none. {/* fall through */ }
-    }
-
-    let result = false;
-    alt struct(cx, ty) {
-      ty_vec(_) { result = true; }
-      ty_istr. { result = true; }
-
-
-
-      // scalar types
-      ty_nil. {
-        result = false;
-      }
-      ty_bot. { result = false; }
-      ty_bool. { result = false; }
-      ty_int. { result = false; }
-      ty_float. { result = false; }
-      ty_uint. { result = false; }
-      ty_machine(_) { result = false; }
-      ty_char. { result = false; }
-      ty_type. { result = false; }
-      ty_native(_) { result = false; }
-
-
-
-      // boxed types
-      ty_str. {
-        result = false;
-      }
-      ty_box(_) { result = false; }
-      ty_fn(_, _, _, _, _) { result = false; }
-      ty_native_fn(_, _, _) { result = false; }
-      ty_obj(_) { result = false; }
-
-
-
-      // structural types
-      ty_tag(did, tps) {
-        let variants = tag_variants(cx, did);
-        for variant: variant_info in variants {
-            for aty: t in variant.args {
-                // Perform any type parameter substitutions.
-                let arg_ty = substitute_type_params(cx, tps, aty);
-                if type_owns_heap_mem(cx, arg_ty) { result = true; }
-            }
-        }
-      }
-      ty_rec(flds) {
-        for f: field in flds {
-            if type_owns_heap_mem(cx, f.mt.ty) { result = true; }
-        }
-      }
-      ty_tup(elts) {
-        for m in elts { if type_owns_heap_mem(cx, m) { result = true; } }
-      }
-      ty_res(_, inner, tps) {
-        result =
-            type_owns_heap_mem(cx, substitute_type_params(cx, tps, inner));
-      }
-
-
-      ty_ptr(_) {
-        result = false;
-      }
-      ty_var(_) { fail "ty_var in type_owns_heap_mem"; }
-      ty_param(_, _) { result = false; }
-    }
-
-    cx.owns_heap_mem_cache.insert(ty, result);
-    ret result;
-}
-
 // Whether a type is Plain Old Data (i.e. can be safely memmoved).
 fn type_is_pod(cx: &ctxt, ty: t) -> bool {
     let result = true;
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index 0c4b6eac7b9..94f2dd3c99f 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -60,7 +60,7 @@ type parser =
         fn get_sess() -> parse_sess;
     };
 
-fn new_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg, path: &istr,
+fn new_parser_from_file(sess: parse_sess, cfg: &ast::crate_cfg, path: &istr,
                         chpos: uint, byte_pos: uint, ftype: file_type) ->
    parser {
     let src = io::read_whole_file_str(path);
@@ -69,11 +69,10 @@ fn new_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg, path: &istr,
     sess.cm.files += [filemap];
     let itr = @interner::mk(istr::hash, istr::eq);
     let rdr = lexer::new_reader(sess.cm, src, filemap, itr);
-
     ret new_parser(sess, cfg, rdr, ftype);
 }
 
-fn new_parser(sess: parse_sess, cfg: ast::crate_cfg, rdr: lexer::reader,
+fn new_parser(sess: parse_sess, cfg: &ast::crate_cfg, rdr: lexer::reader,
               ftype: file_type) -> parser {
     obj stdio_parser(sess: parse_sess,
                      cfg: ast::crate_cfg,
@@ -141,7 +140,6 @@ fn new_parser(sess: parse_sess, cfg: ast::crate_cfg, rdr: lexer::reader,
         fn get_id() -> node_id { ret next_node_id(sess); }
         fn get_sess() -> parse_sess { ret sess; }
     }
-
     let tok0 = lexer::next_token(rdr);
     let span0 = ast_util::mk_sp(tok0.chpos, rdr.get_chpos());
     ret stdio_parser(sess, cfg, ftype, tok0.tok, span0, span0, [],