about summary refs log tree commit diff
path: root/src/comp
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2011-08-22 11:48:00 -0700
committerBrian Anderson <banderson@mozilla.com>2011-08-22 12:04:05 -0700
commit309a7534d7ebf39f5a62bac36749d35272fe0a3e (patch)
tree22646dcee13399d42bff440d22ff11f3a7e15719 /src/comp
parent179658e20fa299b4f7405ea72affd1c8d175d95b (diff)
Move trans::ivec to middle::trans_ivec
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/middle/trans.rs692
-rw-r--r--src/comp/middle/trans_ivec.rs703
-rw-r--r--src/comp/rustc.rc1
3 files changed, 705 insertions, 691 deletions
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 65c0475defb..abc473c8cb7 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -74,6 +74,7 @@ import trans_common::*;
 
 import trans_objects::trans_anon_obj;
 import trans_objects::trans_obj;
+import ivec = trans_ivec;
 
 // This function now fails if called on a type with dynamic size (as its
 // return value was always meaningless in that case anyhow). Beware!
@@ -2642,697 +2643,6 @@ fn trans_evec_append(cx: &@block_ctxt, t: ty::t, lhs: ValueRef,
                              llelt_tydesc.val, dst, src, skip_null]));
 }
 
-mod ivec {
-    fn trans_ivec(bcx: @block_ctxt, args: &[@ast::expr], id: ast::node_id) ->
-        result {
-        let typ = node_id_type(bcx_ccx(bcx), id);
-        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 = ivec::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 = bcx.build.Mul(C_uint(std::vec::len(args)), unit_sz);
-        // Allocate the vector pieces and store length and allocated length.
-
-        let llfirsteltptr;
-        if std::vec::len(args) > 0u &&
-            std::vec::len(args) <= abi::ivec_default_length {
-            // Interior case.
-
-            bcx.build.Store(lllen,
-                            bcx.build.InBoundsGEP(llvecptr,
-                                                  [C_int(0),
-                                                   C_uint(abi::ivec_elt_len)]));
-            bcx.build.Store(llalen,
-                            bcx.build.InBoundsGEP(llvecptr,
-                                                  [C_int(0),
-                                                   C_uint(abi::ivec_elt_alen)]));
-            llfirsteltptr =
-                bcx.build.InBoundsGEP(llvecptr,
-                                      [C_int(0), C_uint(abi::ivec_elt_elems),
-                                       C_int(0)]);
-        } else {
-            // Heap case.
-
-            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 = bcx.build.PointerCast(llvecptr, T_ptr(llstubty));
-            bcx.build.Store(C_int(0), bcx.build.InBoundsGEP(llstubptr, stub_z));
-            let llheapty = T_ivec_heap_part(llunitty);
-            if std::vec::len(args) == 0u {
-                // Null heap pointer indicates a zero-length vector.
-
-                bcx.build.Store(llalen, bcx.build.InBoundsGEP(llstubptr, stub_a));
-                bcx.build.Store(C_null(T_ptr(llheapty)),
-                                bcx.build.InBoundsGEP(llstubptr, stub_p));
-                llfirsteltptr = C_null(T_ptr(llunitty));
-            } else {
-                bcx.build.Store(lllen, bcx.build.InBoundsGEP(llstubptr, stub_a));
-
-                let llheapsz = bcx.build.Add(llsize_of(llheapty), lllen);
-                let rslt = trans_shared_malloc(bcx, T_ptr(llheapty), llheapsz);
-                bcx = rslt.bcx;
-                let llheapptr = rslt.val;
-                bcx.build.Store(llheapptr,
-                                bcx.build.InBoundsGEP(llstubptr, stub_p));
-                let heap_l = [C_int(0), C_uint(abi::ivec_heap_elt_len)];
-                bcx.build.Store(lllen, bcx.build.InBoundsGEP(llheapptr, heap_l));
-                llfirsteltptr =
-                    bcx.build.InBoundsGEP(llheapptr,
-                                          [C_int(0),
-                                           C_uint(abi::ivec_heap_elt_elems),
-                                           C_int(0)]);
-            }
-        }
-        // Store the individual elements.
-
-        let i = 0u;
-        for e: @ast::expr 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 =
-                    bcx.build.InBoundsGEP(llfirsteltptr,
-                                          [bcx.build.Mul(C_uint(i), unit_sz)]);
-            } else {
-                lleltptr = bcx.build.InBoundsGEP(llfirsteltptr, [C_uint(i)]);
-            }
-            bcx = move_val_if_temp(bcx, INIT, lleltptr, lv, unit_ty);
-            i += 1u;
-        }
-        ret rslt(bcx, llvecptr);
-    }
-
-    // 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 = bcx.build.PointerCast(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 =
-            bcx.build.InBoundsGEP(v,
-                                  [C_int(0), C_uint(abi::ivec_elt_elems),
-                                   C_int(0)]);
-        let on_heap =
-            bcx.build.ICmp(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");
-        bcx.build.CondBr(on_heap, on_heap_cx.llbb, next_cx.llbb);
-        let heap_stub =
-            on_heap_cx.build.PointerCast(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 =
-            on_heap_cx.build.ICmp(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");
-        on_heap_cx.build.CondBr(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));
-        zero_len_cx.build.Br(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)];
-                nonzero_len_cx.build.InBoundsGEP(heap_ptr, v)
-            };
-
-        nonzero_len_cx.build.Br(next_cx.llbb);
-        // Now we can figure out the length of `v` and get a pointer to its
-        // first element.
-
-        let len =
-            next_cx.build.Phi(T_int(), [stack_len, zero_len, heap_len],
-                              [bcx.llbb, zero_len_cx.llbb,
-                               nonzero_len_cx.llbb]);
-        let elem =
-            next_cx.build.Phi(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};
-    }
-
-    // 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 =
-            cx.build.InBoundsGEP(v, [C_int(0), C_uint(abi::ivec_elt_len)]);
-        let stack_len = cx.build.Load(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 =
-            cx.build.ICmp(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");
-        cx.build.CondBr(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 =
-            maybe_on_heap_cx.build.PointerCast(v,
-                                               T_ptr(T_ivec_heap(llunitty)));
-        let heap_ptr = load_inbounds(maybe_on_heap_cx, stub_ptr, stub_p);
-        let on_heap =
-            maybe_on_heap_cx.build.ICmp(lib::llvm::LLVMIntNE, heap_ptr,
-                                        C_null(val_ty(heap_ptr)));
-        let on_heap_cx = new_sub_block_ctxt(cx, "on_heap");
-        maybe_on_heap_cx.build.CondBr(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 =
-            on_heap_cx.build.InBoundsGEP(heap_ptr,
-                                         [C_int(0),
-                                          C_uint(abi::ivec_heap_elt_len)]);
-        let heap_len = on_heap_cx.build.Load(heap_len_ptr);
-        let new_heap_len = on_heap_cx.build.Add(heap_len, len_needed);
-        let heap_len_unscaled =
-            on_heap_cx.build.UDiv(heap_len, llsize_of(llunitty));
-        let heap_no_resize_needed =
-            on_heap_cx.build.ICmp(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");
-        on_heap_cx.build.CondBr(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];
-                heap_no_resize_cx.build.InBoundsGEP(heap_ptr, v)
-            };
-        heap_no_resize_cx.build.Store(new_heap_len, heap_len_ptr);
-        heap_no_resize_cx.build.Br(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 =
-                heap_resize_cx.build.PointerCast(v, T_ptr(T_opaque_ivec()));
-            let upcall = bcx_ccx(cx).upcalls.ivec_resize_shared;
-            heap_resize_cx.build.Call(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];
-                heap_resize_cx.build.InBoundsGEP(heap_ptr_resize, v)
-            };
-        heap_resize_cx.build.Br(next_cx.llbb);
-        // We're on the stack. Check whether we need to spill to the heap.
-
-        let new_stack_len = on_stack_cx.build.Add(stack_len, len_needed);
-        let stack_no_spill_needed =
-            on_stack_cx.build.ICmp(lib::llvm::LLVMIntULE, new_stack_len,
-                                   alen);
-        let stack_len_unscaled =
-            on_stack_cx.build.UDiv(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");
-        on_stack_cx.build.CondBr(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 =
-            stack_no_spill_cx.build.InBoundsGEP(v,
-                                                [C_int(0),
-                                                 C_uint(abi::ivec_elt_elems),
-                                                 stack_len_unscaled]);
-        stack_no_spill_cx.build.Store(new_stack_len, stack_len_ptr);
-        stack_no_spill_cx.build.Br(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 =
-                stack_spill_cx.build.PointerCast(v, T_ptr(T_opaque_ivec()));
-            let upcall = bcx_ccx(cx).upcalls.ivec_spill_shared;
-            stack_spill_cx.build.Call(upcall,
-                                      [cx.fcx.lltaskptr, p, new_stack_len]);
-        }
-        let spill_stub =
-            stack_spill_cx.build.PointerCast(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];
-                stack_spill_cx.build.InBoundsGEP(heap_ptr_spill, v)
-            };
-        stack_spill_cx.build.Br(next_cx.llbb);
-        // Phi together the different data pointers to get the result.
-
-        let data_ptr =
-            next_cx.build.Phi(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, orig_lhs: ValueRef,
-                    orig_rhs: ValueRef) -> result {
-        // Cast to opaque interior vector types if necessary.
-        let lhs;
-        let rhs;
-        if ty::type_has_dynamic_size(bcx_tcx(cx), t) {
-            lhs = cx.build.PointerCast(orig_lhs, T_ptr(T_opaque_ivec()));
-            rhs = cx.build.PointerCast(orig_rhs, T_ptr(T_opaque_ivec()));
-        } else { lhs = orig_lhs; rhs = orig_rhs; }
-
-        let unit_ty = ty::sequence_element_type(bcx_tcx(cx), t);
-        let llunitty = type_of_or_i8(cx, unit_ty);
-        alt ty::struct(bcx_tcx(cx), t) {
-          ty::ty_istr. { }
-          ty::ty_vec(_) { }
-          _ { bcx_tcx(cx).sess.bug("non-istr/ivec in trans_append"); }
-        }
-
-        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, no_tydesc_info).result;
-        bcx = rs.bcx;
-        rs = get_tydesc(bcx, unit_ty, false, 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;
-        rs = reserve_space(bcx, llunitty, lhs, rhs_len);
-        let lhs_data = rs.val;
-        bcx = rs.bcx;
-        // Work out the end pointer.
-
-        let lhs_unscaled_idx = bcx.build.UDiv(rhs_len, llsize_of(llunitty));
-        let lhs_end = bcx.build.InBoundsGEP(lhs_data, [lhs_unscaled_idx]);
-        // Now emit the copy loop.
-
-        let dest_ptr = alloca(bcx, T_ptr(llunitty));
-        bcx.build.Store(lhs_data, dest_ptr);
-        let src_ptr = alloca(bcx, T_ptr(llunitty));
-        bcx.build.Store(rhs_data, src_ptr);
-        let copy_loop_header_cx = new_sub_block_ctxt(bcx, "copy_loop_header");
-        bcx.build.Br(copy_loop_header_cx.llbb);
-        let copy_dest_ptr = copy_loop_header_cx.build.Load(dest_ptr);
-        let not_yet_at_end =
-            copy_loop_header_cx.build.ICmp(lib::llvm::LLVMIntNE,
-                                           copy_dest_ptr, lhs_end);
-        let copy_loop_body_cx = new_sub_block_ctxt(bcx, "copy_loop_body");
-        let next_cx = new_sub_block_ctxt(bcx, "next");
-        copy_loop_header_cx.build.CondBr(not_yet_at_end,
-                                         copy_loop_body_cx.llbb,
-                                         next_cx.llbb);
-
-        let copy_src_ptr = copy_loop_body_cx.build.Load(src_ptr);
-        let copy_src =
-            load_if_immediate(copy_loop_body_cx, copy_src_ptr, unit_ty);
-
-        let post_copy_cx = copy_val
-            (copy_loop_body_cx, INIT, copy_dest_ptr, copy_src, unit_ty);
-        // Increment both pointers.
-        if ty::type_has_dynamic_size(bcx_tcx(cx), t) {
-            // 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);
-        } 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);
-        }
-
-        post_copy_cx.build.Br(copy_loop_header_cx.llbb);
-        ret rslt(next_cx, C_nil());
-    }
-
-    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 =
-            bcx.build.Mul(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 = bcx.build.Add(llsize_of(T_opaque_ivec()), llalen);
-            let llvecptr = array_alloca(bcx, T_i8(), llarraysz);
-
-            bcx_result = cx;
-            llptr =
-                bcx_result.build.PointerCast(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,
-                 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;
-        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 = bcx.build.Add(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 =
-            bcx.build.ICmp(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");
-        bcx.build.CondBr(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 =
-            zero_len_cx.build.PointerCast(llvecptr,
-                                          T_ptr(T_ivec_heap(llunitty)));
-        zero_len_cx.build.Store(C_int(0),
-                                zero_len_cx.build.InBoundsGEP(stub_ptr_zero,
-                                                              stub_z));
-        zero_len_cx.build.Store(llalen,
-                                zero_len_cx.build.InBoundsGEP(stub_ptr_zero,
-                                                              stub_a));
-        zero_len_cx.build.Store(C_null(T_ptr(llheappartty)),
-                                zero_len_cx.build.InBoundsGEP(stub_ptr_zero,
-                                                              stub_p));
-        let next_cx = new_sub_block_ctxt(bcx, "next");
-        zero_len_cx.build.Br(next_cx.llbb);
-        // Determine whether we need to spill to the heap.
-
-        let on_stack =
-            nonzero_len_cx.build.ICmp(lib::llvm::LLVMIntULE, lllen, llalen);
-        let stack_cx = new_sub_block_ctxt(bcx, "stack");
-        let heap_cx = new_sub_block_ctxt(bcx, "heap");
-        nonzero_len_cx.build.CondBr(on_stack, stack_cx.llbb, heap_cx.llbb);
-        // Case (2): Copy onto stack.
-
-        stack_cx.build.Store(lllen,
-                             stack_cx.build.InBoundsGEP(llvecptr, vec_l));
-        stack_cx.build.Store(llalen,
-                             stack_cx.build.InBoundsGEP(llvecptr, vec_a));
-        let dest_ptr_stack =
-            stack_cx.build.InBoundsGEP(llvecptr,
-                                       [C_int(0), C_uint(abi::ivec_elt_elems),
-                                        C_int(0)]);
-        let copy_cx = new_sub_block_ctxt(bcx, "copy");
-        stack_cx.build.Br(copy_cx.llbb);
-        // Case (3): Allocate on heap and copy there.
-
-        let stub_ptr_heap =
-            heap_cx.build.PointerCast(llvecptr, T_ptr(T_ivec_heap(llunitty)));
-        heap_cx.build.Store(C_int(0),
-                            heap_cx.build.InBoundsGEP(stub_ptr_heap, stub_z));
-        heap_cx.build.Store(lllen,
-                            heap_cx.build.InBoundsGEP(stub_ptr_heap, stub_a));
-        let heap_sz = heap_cx.build.Add(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;
-        heap_cx.build.Store(heap_part,
-                            heap_cx.build.InBoundsGEP(stub_ptr_heap, stub_p));
-        {
-            let v = [C_int(0), C_uint(abi::ivec_heap_elt_len)];
-            heap_cx.build.Store(lllen,
-                                heap_cx.build.InBoundsGEP(heap_part, v));
-        }
-        let dest_ptr_heap =
-            heap_cx.build.InBoundsGEP(heap_part,
-                                      [C_int(0),
-                                       C_uint(abi::ivec_heap_elt_elems),
-                                       C_int(0)]);
-        heap_cx.build.Br(copy_cx.llbb);
-        // Emit the copy loop.
-
-        let first_dest_ptr =
-            copy_cx.build.Phi(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 = copy_cx.build.InBoundsGEP(lhs_data, [lhs_len]);
-            rhs_end_ptr = copy_cx.build.InBoundsGEP(rhs_data, [rhs_len]);
-        } else {
-            let lhs_len_unscaled = copy_cx.build.UDiv(lhs_len, unit_sz);
-            lhs_end_ptr =
-                copy_cx.build.InBoundsGEP(lhs_data, [lhs_len_unscaled]);
-            let rhs_len_unscaled = copy_cx.build.UDiv(rhs_len, unit_sz);
-            rhs_end_ptr =
-                copy_cx.build.InBoundsGEP(rhs_data, [rhs_len_unscaled]);
-        }
-
-        let dest_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
-        copy_cx.build.Store(first_dest_ptr, dest_ptr_ptr);
-        let lhs_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
-        copy_cx.build.Store(lhs_data, lhs_ptr_ptr);
-        let rhs_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
-        copy_cx.build.Store(rhs_data, rhs_ptr_ptr);
-        let lhs_copy_cx = new_sub_block_ctxt(bcx, "lhs_copy");
-        copy_cx.build.Br(lhs_copy_cx.llbb);
-        // Copy in elements from the LHS.
-
-        let lhs_ptr = lhs_copy_cx.build.Load(lhs_ptr_ptr);
-        let not_at_end_lhs =
-            lhs_copy_cx.build.ICmp(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");
-        lhs_copy_cx.build.CondBr(not_at_end_lhs, lhs_do_copy_cx.llbb,
-                                 rhs_copy_cx.llbb);
-        let dest_ptr_lhs_copy = lhs_do_copy_cx.build.Load(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);
-
-        // Increment both pointers.
-        if ty::type_has_dynamic_size(bcx_tcx(cx), 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);
-        } 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);
-        }
-
-        lhs_do_copy_cx.build.Br(lhs_copy_cx.llbb);
-        // Copy in elements from the RHS.
-
-        let rhs_ptr = rhs_copy_cx.build.Load(rhs_ptr_ptr);
-        let not_at_end_rhs =
-            rhs_copy_cx.build.ICmp(lib::llvm::LLVMIntNE, rhs_ptr,
-                                   rhs_end_ptr);
-        let rhs_do_copy_cx = new_sub_block_ctxt(bcx, "rhs_do_copy");
-        rhs_copy_cx.build.CondBr(not_at_end_rhs, rhs_do_copy_cx.llbb,
-                                 next_cx.llbb);
-        let dest_ptr_rhs_copy = rhs_do_copy_cx.build.Load(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);
-
-        // Increment both pointers.
-        if ty::type_has_dynamic_size(bcx_tcx(cx), 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);
-        } 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);
-        }
-
-        rhs_do_copy_cx.build.Br(rhs_copy_cx.llbb);
-        // Finally done!
-
-        ret rslt(next_cx, llvecptr);
-    }
-
-    // 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 = cx.build.PointerCast(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 =
-            cx.build.InBoundsGEP(vptr, [C_int(0), C_uint(abi::ivec_elt_len)]);
-        let stack_len = cx.build.Load(stack_len_ptr);
-        let stack_len_is_zero =
-            cx.build.ICmp(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");
-        cx.build.CondBr(stack_len_is_zero, maybe_on_heap_cx.llbb,
-                        next_cx.llbb);
-
-        let stub_ptr =
-            maybe_on_heap_cx.build.PointerCast(vptr,
-                                               T_ptr(T_ivec_heap(llunitty)));
-        let heap_ptr_ptr =
-            maybe_on_heap_cx.build.InBoundsGEP(
-                stub_ptr,
-                [C_int(0),
-                 C_uint(abi::ivec_heap_stub_elt_ptr)]);
-        let heap_ptr = maybe_on_heap_cx.build.Load(heap_ptr_ptr);
-        let heap_ptr_is_nonnull =
-            maybe_on_heap_cx.build.ICmp(lib::llvm::LLVMIntNE, heap_ptr,
-                                        C_null(T_ptr(llheappartty)));
-        let on_heap_cx = new_sub_block_ctxt(cx, "on_heap");
-        maybe_on_heap_cx.build.CondBr(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 =
-            on_heap_cx.build.InBoundsGEP(
-                stub_ptr,
-                [C_int(0),
-                 C_uint(abi::ivec_heap_stub_elt_alen)]);
-        let alen = on_heap_cx.build.Load(alen_ptr);
-
-        let heap_part_sz =
-            on_heap_cx.build.Add(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;
-
-        on_heap_cx.build.Store(new_heap_ptr, heap_ptr_ptr);
-        on_heap_cx.build.Br(next_cx.llbb);
-
-        ret rslt(next_cx, C_nil());
-    }
-}
-
 fn trans_evec_add(cx: &@block_ctxt, t: ty::t, lhs: ValueRef, rhs: ValueRef)
    -> result {
     let r = alloc_ty(cx, t);
diff --git a/src/comp/middle/trans_ivec.rs b/src/comp/middle/trans_ivec.rs
new file mode 100644
index 00000000000..c9cc35ac9a9
--- /dev/null
+++ b/src/comp/middle/trans_ivec.rs
@@ -0,0 +1,703 @@
+import std::option::none;
+import syntax::ast;
+import lib::llvm::llvm::{ValueRef, TypeRef};
+import back::abi;
+import trans::{call_memmove, trans_shared_malloc, llsize_of,
+               type_of_or_i8, incr_ptr, INIT, copy_val, load_if_immediate,
+               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};
+import trans_common::*;
+
+export trans_ivec, get_len_and_data, duplicate_heap_part, trans_add,
+trans_append;
+
+fn trans_ivec(bcx: @block_ctxt, args: &[@ast::expr], id: ast::node_id) ->
+    result {
+    let typ = node_id_type(bcx_ccx(bcx), id);
+    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 = bcx.build.Mul(C_uint(std::vec::len(args)), unit_sz);
+    // Allocate the vector pieces and store length and allocated length.
+
+    let llfirsteltptr;
+    if std::vec::len(args) > 0u &&
+        std::vec::len(args) <= abi::ivec_default_length {
+        // Interior case.
+
+        bcx.build.Store(lllen,
+                        bcx.build.InBoundsGEP(llvecptr,
+                                              [C_int(0),
+                                               C_uint(abi::ivec_elt_len)]));
+        bcx.build.Store(llalen,
+                        bcx.build.InBoundsGEP(llvecptr,
+                                              [C_int(0),
+                                               C_uint(abi::ivec_elt_alen)]));
+        llfirsteltptr =
+            bcx.build.InBoundsGEP(llvecptr,
+                                  [C_int(0), C_uint(abi::ivec_elt_elems),
+                                   C_int(0)]);
+    } else {
+        // Heap case.
+
+        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 = bcx.build.PointerCast(llvecptr, T_ptr(llstubty));
+        bcx.build.Store(C_int(0), bcx.build.InBoundsGEP(llstubptr, stub_z));
+        let llheapty = T_ivec_heap_part(llunitty);
+        if std::vec::len(args) == 0u {
+            // Null heap pointer indicates a zero-length vector.
+
+            bcx.build.Store(llalen, bcx.build.InBoundsGEP(llstubptr, stub_a));
+            bcx.build.Store(C_null(T_ptr(llheapty)),
+                            bcx.build.InBoundsGEP(llstubptr, stub_p));
+            llfirsteltptr = C_null(T_ptr(llunitty));
+        } else {
+            bcx.build.Store(lllen, bcx.build.InBoundsGEP(llstubptr, stub_a));
+
+            let llheapsz = bcx.build.Add(llsize_of(llheapty), lllen);
+            let rslt = trans_shared_malloc(bcx, T_ptr(llheapty), llheapsz);
+            bcx = rslt.bcx;
+            let llheapptr = rslt.val;
+            bcx.build.Store(llheapptr,
+                            bcx.build.InBoundsGEP(llstubptr, stub_p));
+            let heap_l = [C_int(0), C_uint(abi::ivec_heap_elt_len)];
+            bcx.build.Store(lllen, bcx.build.InBoundsGEP(llheapptr, heap_l));
+            llfirsteltptr =
+                bcx.build.InBoundsGEP(llheapptr,
+                                      [C_int(0),
+                                       C_uint(abi::ivec_heap_elt_elems),
+                                       C_int(0)]);
+        }
+    }
+    // Store the individual elements.
+
+    let i = 0u;
+    for e: @ast::expr 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 =
+                bcx.build.InBoundsGEP(llfirsteltptr,
+                                      [bcx.build.Mul(C_uint(i), unit_sz)]);
+        } else {
+            lleltptr = bcx.build.InBoundsGEP(llfirsteltptr, [C_uint(i)]);
+        }
+        bcx = move_val_if_temp(bcx, INIT, lleltptr, lv, unit_ty);
+        i += 1u;
+    }
+    ret rslt(bcx, llvecptr);
+}
+
+// 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 = bcx.build.PointerCast(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 =
+        bcx.build.InBoundsGEP(v,
+                              [C_int(0), C_uint(abi::ivec_elt_elems),
+                               C_int(0)]);
+    let on_heap =
+        bcx.build.ICmp(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");
+    bcx.build.CondBr(on_heap, on_heap_cx.llbb, next_cx.llbb);
+    let heap_stub =
+        on_heap_cx.build.PointerCast(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 =
+        on_heap_cx.build.ICmp(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");
+    on_heap_cx.build.CondBr(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));
+    zero_len_cx.build.Br(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)];
+        nonzero_len_cx.build.InBoundsGEP(heap_ptr, v)
+    };
+
+    nonzero_len_cx.build.Br(next_cx.llbb);
+    // Now we can figure out the length of `v` and get a pointer to its
+    // first element.
+
+    let len =
+        next_cx.build.Phi(T_int(), [stack_len, zero_len, heap_len],
+                          [bcx.llbb, zero_len_cx.llbb,
+                           nonzero_len_cx.llbb]);
+    let elem =
+        next_cx.build.Phi(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};
+}
+
+// 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 =
+        cx.build.InBoundsGEP(v, [C_int(0), C_uint(abi::ivec_elt_len)]);
+    let stack_len = cx.build.Load(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 =
+        cx.build.ICmp(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");
+    cx.build.CondBr(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 =
+        maybe_on_heap_cx.build.PointerCast(v,
+                                           T_ptr(T_ivec_heap(llunitty)));
+    let heap_ptr = load_inbounds(maybe_on_heap_cx, stub_ptr, stub_p);
+    let on_heap =
+        maybe_on_heap_cx.build.ICmp(lib::llvm::LLVMIntNE, heap_ptr,
+                                    C_null(val_ty(heap_ptr)));
+    let on_heap_cx = new_sub_block_ctxt(cx, "on_heap");
+    maybe_on_heap_cx.build.CondBr(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 =
+        on_heap_cx.build.InBoundsGEP(heap_ptr,
+                                     [C_int(0),
+                                      C_uint(abi::ivec_heap_elt_len)]);
+    let heap_len = on_heap_cx.build.Load(heap_len_ptr);
+    let new_heap_len = on_heap_cx.build.Add(heap_len, len_needed);
+    let heap_len_unscaled =
+        on_heap_cx.build.UDiv(heap_len, llsize_of(llunitty));
+    let heap_no_resize_needed =
+        on_heap_cx.build.ICmp(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");
+    on_heap_cx.build.CondBr(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];
+        heap_no_resize_cx.build.InBoundsGEP(heap_ptr, v)
+    };
+    heap_no_resize_cx.build.Store(new_heap_len, heap_len_ptr);
+    heap_no_resize_cx.build.Br(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 =
+            heap_resize_cx.build.PointerCast(v, T_ptr(T_opaque_ivec()));
+        let upcall = bcx_ccx(cx).upcalls.ivec_resize_shared;
+        heap_resize_cx.build.Call(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];
+        heap_resize_cx.build.InBoundsGEP(heap_ptr_resize, v)
+    };
+    heap_resize_cx.build.Br(next_cx.llbb);
+    // We're on the stack. Check whether we need to spill to the heap.
+
+    let new_stack_len = on_stack_cx.build.Add(stack_len, len_needed);
+    let stack_no_spill_needed =
+        on_stack_cx.build.ICmp(lib::llvm::LLVMIntULE, new_stack_len,
+                               alen);
+    let stack_len_unscaled =
+        on_stack_cx.build.UDiv(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");
+    on_stack_cx.build.CondBr(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 =
+        stack_no_spill_cx.build.InBoundsGEP(v,
+                                            [C_int(0),
+                                             C_uint(abi::ivec_elt_elems),
+                                             stack_len_unscaled]);
+    stack_no_spill_cx.build.Store(new_stack_len, stack_len_ptr);
+    stack_no_spill_cx.build.Br(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 =
+            stack_spill_cx.build.PointerCast(v, T_ptr(T_opaque_ivec()));
+        let upcall = bcx_ccx(cx).upcalls.ivec_spill_shared;
+        stack_spill_cx.build.Call(upcall,
+                                  [cx.fcx.lltaskptr, p, new_stack_len]);
+    }
+    let spill_stub =
+        stack_spill_cx.build.PointerCast(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];
+        stack_spill_cx.build.InBoundsGEP(heap_ptr_spill, v)
+    };
+    stack_spill_cx.build.Br(next_cx.llbb);
+    // Phi together the different data pointers to get the result.
+
+    let data_ptr =
+        next_cx.build.Phi(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, orig_lhs: ValueRef,
+                orig_rhs: ValueRef) -> result {
+    // Cast to opaque interior vector types if necessary.
+    let lhs;
+    let rhs;
+    if ty::type_has_dynamic_size(bcx_tcx(cx), t) {
+        lhs = cx.build.PointerCast(orig_lhs, T_ptr(T_opaque_ivec()));
+        rhs = cx.build.PointerCast(orig_rhs, T_ptr(T_opaque_ivec()));
+    } else { lhs = orig_lhs; rhs = orig_rhs; }
+
+    let unit_ty = ty::sequence_element_type(bcx_tcx(cx), t);
+    let llunitty = type_of_or_i8(cx, unit_ty);
+    alt ty::struct(bcx_tcx(cx), t) {
+      ty::ty_istr. { }
+      ty::ty_vec(_) { }
+      _ { bcx_tcx(cx).sess.bug("non-istr/ivec in trans_append"); }
+    }
+
+    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, no_tydesc_info).result;
+    bcx = rs.bcx;
+    rs = get_tydesc(bcx, unit_ty, false, 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;
+    rs = reserve_space(bcx, llunitty, lhs, rhs_len);
+    let lhs_data = rs.val;
+    bcx = rs.bcx;
+    // Work out the end pointer.
+
+    let lhs_unscaled_idx = bcx.build.UDiv(rhs_len, llsize_of(llunitty));
+    let lhs_end = bcx.build.InBoundsGEP(lhs_data, [lhs_unscaled_idx]);
+    // Now emit the copy loop.
+
+    let dest_ptr = alloca(bcx, T_ptr(llunitty));
+    bcx.build.Store(lhs_data, dest_ptr);
+    let src_ptr = alloca(bcx, T_ptr(llunitty));
+    bcx.build.Store(rhs_data, src_ptr);
+    let copy_loop_header_cx = new_sub_block_ctxt(bcx, "copy_loop_header");
+    bcx.build.Br(copy_loop_header_cx.llbb);
+    let copy_dest_ptr = copy_loop_header_cx.build.Load(dest_ptr);
+    let not_yet_at_end =
+        copy_loop_header_cx.build.ICmp(lib::llvm::LLVMIntNE,
+                                       copy_dest_ptr, lhs_end);
+    let copy_loop_body_cx = new_sub_block_ctxt(bcx, "copy_loop_body");
+    let next_cx = new_sub_block_ctxt(bcx, "next");
+    copy_loop_header_cx.build.CondBr(not_yet_at_end,
+                                     copy_loop_body_cx.llbb,
+                                     next_cx.llbb);
+
+    let copy_src_ptr = copy_loop_body_cx.build.Load(src_ptr);
+    let copy_src =
+        load_if_immediate(copy_loop_body_cx, copy_src_ptr, unit_ty);
+
+    let post_copy_cx = copy_val
+        (copy_loop_body_cx, INIT, copy_dest_ptr, copy_src, unit_ty);
+    // Increment both pointers.
+    if ty::type_has_dynamic_size(bcx_tcx(cx), t) {
+        // 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);
+    } 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);
+    }
+
+    post_copy_cx.build.Br(copy_loop_header_cx.llbb);
+    ret rslt(next_cx, C_nil());
+}
+
+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 =
+        bcx.build.Mul(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 = bcx.build.Add(llsize_of(T_opaque_ivec()), llalen);
+        let llvecptr = array_alloca(bcx, T_i8(), llarraysz);
+
+        bcx_result = cx;
+        llptr =
+            bcx_result.build.PointerCast(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,
+             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;
+    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 = bcx.build.Add(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 =
+        bcx.build.ICmp(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");
+    bcx.build.CondBr(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 =
+        zero_len_cx.build.PointerCast(llvecptr,
+                                      T_ptr(T_ivec_heap(llunitty)));
+    zero_len_cx.build.Store(C_int(0),
+                            zero_len_cx.build.InBoundsGEP(stub_ptr_zero,
+                                                          stub_z));
+    zero_len_cx.build.Store(llalen,
+                            zero_len_cx.build.InBoundsGEP(stub_ptr_zero,
+                                                          stub_a));
+    zero_len_cx.build.Store(C_null(T_ptr(llheappartty)),
+                            zero_len_cx.build.InBoundsGEP(stub_ptr_zero,
+                                                          stub_p));
+    let next_cx = new_sub_block_ctxt(bcx, "next");
+    zero_len_cx.build.Br(next_cx.llbb);
+    // Determine whether we need to spill to the heap.
+
+    let on_stack =
+        nonzero_len_cx.build.ICmp(lib::llvm::LLVMIntULE, lllen, llalen);
+    let stack_cx = new_sub_block_ctxt(bcx, "stack");
+    let heap_cx = new_sub_block_ctxt(bcx, "heap");
+    nonzero_len_cx.build.CondBr(on_stack, stack_cx.llbb, heap_cx.llbb);
+    // Case (2): Copy onto stack.
+
+    stack_cx.build.Store(lllen,
+                         stack_cx.build.InBoundsGEP(llvecptr, vec_l));
+    stack_cx.build.Store(llalen,
+                         stack_cx.build.InBoundsGEP(llvecptr, vec_a));
+    let dest_ptr_stack =
+        stack_cx.build.InBoundsGEP(llvecptr,
+                                   [C_int(0), C_uint(abi::ivec_elt_elems),
+                                    C_int(0)]);
+    let copy_cx = new_sub_block_ctxt(bcx, "copy");
+    stack_cx.build.Br(copy_cx.llbb);
+    // Case (3): Allocate on heap and copy there.
+
+    let stub_ptr_heap =
+        heap_cx.build.PointerCast(llvecptr, T_ptr(T_ivec_heap(llunitty)));
+    heap_cx.build.Store(C_int(0),
+                        heap_cx.build.InBoundsGEP(stub_ptr_heap, stub_z));
+    heap_cx.build.Store(lllen,
+                        heap_cx.build.InBoundsGEP(stub_ptr_heap, stub_a));
+    let heap_sz = heap_cx.build.Add(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;
+    heap_cx.build.Store(heap_part,
+                        heap_cx.build.InBoundsGEP(stub_ptr_heap, stub_p));
+    {
+        let v = [C_int(0), C_uint(abi::ivec_heap_elt_len)];
+        heap_cx.build.Store(lllen,
+                            heap_cx.build.InBoundsGEP(heap_part, v));
+    }
+    let dest_ptr_heap =
+        heap_cx.build.InBoundsGEP(heap_part,
+                                  [C_int(0),
+                                   C_uint(abi::ivec_heap_elt_elems),
+                                   C_int(0)]);
+    heap_cx.build.Br(copy_cx.llbb);
+    // Emit the copy loop.
+
+    let first_dest_ptr =
+        copy_cx.build.Phi(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 = copy_cx.build.InBoundsGEP(lhs_data, [lhs_len]);
+        rhs_end_ptr = copy_cx.build.InBoundsGEP(rhs_data, [rhs_len]);
+    } else {
+        let lhs_len_unscaled = copy_cx.build.UDiv(lhs_len, unit_sz);
+        lhs_end_ptr =
+            copy_cx.build.InBoundsGEP(lhs_data, [lhs_len_unscaled]);
+        let rhs_len_unscaled = copy_cx.build.UDiv(rhs_len, unit_sz);
+        rhs_end_ptr =
+            copy_cx.build.InBoundsGEP(rhs_data, [rhs_len_unscaled]);
+    }
+
+    let dest_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
+    copy_cx.build.Store(first_dest_ptr, dest_ptr_ptr);
+    let lhs_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
+    copy_cx.build.Store(lhs_data, lhs_ptr_ptr);
+    let rhs_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
+    copy_cx.build.Store(rhs_data, rhs_ptr_ptr);
+    let lhs_copy_cx = new_sub_block_ctxt(bcx, "lhs_copy");
+    copy_cx.build.Br(lhs_copy_cx.llbb);
+    // Copy in elements from the LHS.
+
+    let lhs_ptr = lhs_copy_cx.build.Load(lhs_ptr_ptr);
+    let not_at_end_lhs =
+        lhs_copy_cx.build.ICmp(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");
+    lhs_copy_cx.build.CondBr(not_at_end_lhs, lhs_do_copy_cx.llbb,
+                             rhs_copy_cx.llbb);
+    let dest_ptr_lhs_copy = lhs_do_copy_cx.build.Load(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);
+
+    // Increment both pointers.
+    if ty::type_has_dynamic_size(bcx_tcx(cx), 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);
+    } 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);
+    }
+
+    lhs_do_copy_cx.build.Br(lhs_copy_cx.llbb);
+    // Copy in elements from the RHS.
+
+    let rhs_ptr = rhs_copy_cx.build.Load(rhs_ptr_ptr);
+    let not_at_end_rhs =
+        rhs_copy_cx.build.ICmp(lib::llvm::LLVMIntNE, rhs_ptr,
+                               rhs_end_ptr);
+    let rhs_do_copy_cx = new_sub_block_ctxt(bcx, "rhs_do_copy");
+    rhs_copy_cx.build.CondBr(not_at_end_rhs, rhs_do_copy_cx.llbb,
+                             next_cx.llbb);
+    let dest_ptr_rhs_copy = rhs_do_copy_cx.build.Load(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);
+
+    // Increment both pointers.
+    if ty::type_has_dynamic_size(bcx_tcx(cx), 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);
+    } 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);
+    }
+
+    rhs_do_copy_cx.build.Br(rhs_copy_cx.llbb);
+    // Finally done!
+
+    ret rslt(next_cx, llvecptr);
+}
+
+// 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 = cx.build.PointerCast(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 =
+        cx.build.InBoundsGEP(vptr, [C_int(0), C_uint(abi::ivec_elt_len)]);
+    let stack_len = cx.build.Load(stack_len_ptr);
+    let stack_len_is_zero =
+        cx.build.ICmp(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");
+    cx.build.CondBr(stack_len_is_zero, maybe_on_heap_cx.llbb,
+                    next_cx.llbb);
+
+    let stub_ptr =
+        maybe_on_heap_cx.build.PointerCast(vptr,
+                                           T_ptr(T_ivec_heap(llunitty)));
+    let heap_ptr_ptr =
+        maybe_on_heap_cx.build.InBoundsGEP(
+            stub_ptr,
+            [C_int(0),
+             C_uint(abi::ivec_heap_stub_elt_ptr)]);
+    let heap_ptr = maybe_on_heap_cx.build.Load(heap_ptr_ptr);
+    let heap_ptr_is_nonnull =
+        maybe_on_heap_cx.build.ICmp(lib::llvm::LLVMIntNE, heap_ptr,
+                                    C_null(T_ptr(llheappartty)));
+    let on_heap_cx = new_sub_block_ctxt(cx, "on_heap");
+    maybe_on_heap_cx.build.CondBr(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 =
+        on_heap_cx.build.InBoundsGEP(
+            stub_ptr,
+            [C_int(0),
+             C_uint(abi::ivec_heap_stub_elt_alen)]);
+    let alen = on_heap_cx.build.Load(alen_ptr);
+
+    let heap_part_sz =
+        on_heap_cx.build.Add(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;
+
+    on_heap_cx.build.Store(new_heap_ptr, heap_ptr_ptr);
+    on_heap_cx.build.Br(next_cx.llbb);
+
+    ret rslt(next_cx, C_nil());
+}
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
index 1cba8c469a8..c2aa2ed5a62 100644
--- a/src/comp/rustc.rc
+++ b/src/comp/rustc.rc
@@ -18,6 +18,7 @@ mod middle {
     mod trans;
     mod trans_alt;
     mod trans_objects;
+    mod trans_ivec;
     mod ty;
     mod ast_map;
     mod resolve;