From 00eb3f5798ae77c8af135b86c827a67a8febd932 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 10 Jun 2011 19:35:59 -0700 Subject: [PATCH] rustc: Sketch out translation of interior vector literals and take/drop glue --- src/comp/back/abi.rs | 11 ++ src/comp/middle/trans.rs | 235 +++++++++++++++++++++++++++++++++++++-- src/comp/middle/ty.rs | 28 +++-- 3 files changed, 251 insertions(+), 23 deletions(-) diff --git a/src/comp/back/abi.rs b/src/comp/back/abi.rs index bfe601c15a3..106aba39be6 100644 --- a/src/comp/back/abi.rs +++ b/src/comp/back/abi.rs @@ -66,6 +66,17 @@ const int closure_elt_target = 1; const int closure_elt_bindings = 2; const int closure_elt_ty_params = 3; +const uint ivec_default_size = 16u; + +const uint ivec_elt_len = 0u; +const uint ivec_elt_alen = 1u; +const uint ivec_elt_elems = 2u; +const uint ivec_heap_stub_elt_zero = 0u; +const uint ivec_heap_stub_elt_alen = 1u; +const uint ivec_heap_stub_elt_ptr = 2u; +const uint ivec_heap_elt_len = 0u; +const uint ivec_heap_elt_elems = 1u; + const int worst_case_glue_call_args = 7; diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index cd91dc9b66b..8f891b73f1c 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -572,22 +572,28 @@ fn T_opaque_vec_ptr() -> TypeRef { // Interior vector. // // TODO: Support user-defined vector sizes. -fn T_ivec(TypeRef t) -> TypeRef { - ret T_struct([T_int(), // Length ("fill") - T_int(), // Alloc (if zero, it's heapified) - T_array(t, 16u) // Body elements - ]); +fn T_ivec() -> TypeRef { + ret T_struct([T_int(), // Length ("fill"; if zero, heapified) + T_int(), // Alloc + T_array(T_i8(), abi::ivec_default_size)]); // Body elements } // Interior vector on the heap. Cast to this when the allocated length (second // element of T_ivec above) is zero. fn T_ivec_heap(TypeRef t) -> TypeRef { - ret T_struct([T_int(), // Length ("fill") - T_int(), // Alloc (zero in this case) - T_ptr(T_struct([T_int(), // Real alloc + ret T_struct([T_int(), // Length (zero) + T_int(), // Alloc + T_ptr(T_struct([T_int(), // Real length T_array(t, 0u)]))]); // Body elements } +fn T_opaque_ivec_heap() -> TypeRef { + ret T_struct([T_int(), // Length (zero) + T_int(), // Alloc + T_ptr(T_struct([T_int(), // Real length + T_array(T_i8(), 0u)]))]); // Body elements +} + fn T_str() -> TypeRef { ret T_vec(T_i8()); } @@ -862,7 +868,7 @@ fn type_of_inner(&@crate_ctxt cx, &span sp, &ty::t t) -> TypeRef { } case (ty::ty_char) { llty = T_char(); } case (ty::ty_str) { llty = T_ptr(T_str()); } - case (ty::ty_istr) { llty = T_ivec(T_i8()); } + case (ty::ty_istr) { llty = T_ivec(); } case (ty::ty_tag(_, _)) { if (ty::type_has_dynamic_size(cx.tcx, t)) { llty = T_opaque_tag(cx.tn); @@ -878,7 +884,7 @@ fn type_of_inner(&@crate_ctxt cx, &span sp, &ty::t t) -> TypeRef { llty = T_ptr(T_vec(type_of_inner(cx, sp, mt.ty))); } case (ty::ty_ivec(?mt)) { - llty = T_ivec(type_of_inner(cx, sp, mt.ty)); + llty = T_ivec(); } case (ty::ty_ptr(?mt)) { llty = T_ptr(type_of_inner(cx, sp, mt.ty)); @@ -989,6 +995,13 @@ fn type_of_ty_param_count_and_ty(@local_ctxt lcx, &span sp, ret type_of(lcx.ccx, sp, tpt._1); } +fn type_of_or_i8(&@block_ctxt bcx, ty::t typ) -> TypeRef { + if (ty::type_has_dynamic_size(bcx.fcx.lcx.ccx.tcx, typ)) { + ret T_i8(); + } + ret type_of(bcx.fcx.lcx.ccx, bcx.sp, typ); +} + // Name sanitation. LLVM will happily accept identifiers with weird names, but // gas doesn't! @@ -1058,6 +1071,10 @@ fn C_int(int i) -> ValueRef { ret C_integral(T_int(), i as uint, True); } +fn C_uint(uint i) -> ValueRef { + ret C_integral(T_int(), i, False); +} + fn C_u8(uint i) -> ValueRef { ret C_integral(T_i8(), i, False); } @@ -2635,6 +2652,64 @@ fn make_numerical_cmp_glue(&@block_ctxt cx, ValueRef lhs, ValueRef rhs, r.bcx.build.RetVoid(); } +// Returns the length of an interior vector and a pointer to its first +// element, in that order. +fn get_ivec_len_and_data(&@block_ctxt bcx, ValueRef v, ty::t unit_ty) -> + tup(ValueRef, ValueRef, @block_ctxt) { + auto llunitty = type_of_or_i8(bcx, unit_ty); + + auto stack_len = bcx.build.Load(bcx.build.GEP(v, + [C_int(0), C_uint(abi::ivec_elt_len)])); + auto stack_elem = bcx.build.GEP(v, [C_int(0), + C_uint(abi::ivec_elt_elems)]); + stack_elem = bcx.build.PointerCast(stack_elem, T_ptr(llunitty)); + + auto on_heap = bcx.build.ICmp(lib::llvm::LLVMIntEQ, stack_len, C_int(0)); + + auto on_heap_cx = new_sub_block_ctxt(bcx, "on_heap"); + auto next_cx = new_sub_block_ctxt(bcx, "next"); + bcx.build.CondBr(on_heap, on_heap_cx.llbb, next_cx.llbb); + + auto heap_stub = on_heap_cx.build.PointerCast(v, + T_ptr(T_ivec_heap(llunitty))); + auto heap_ptr = on_heap_cx.build.Load(on_heap_cx.build.GEP( + 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. + auto llstubty = T_ivec_heap(llunitty); + auto llheapptrty = struct_elt(llstubty, abi::ivec_heap_stub_elt_ptr); + auto heap_ptr_is_null = on_heap_cx.build.ICmp(lib::llvm::LLVMIntEQ, + heap_ptr, C_null(T_ptr(llheapptrty))); + + auto zero_len_cx = new_sub_block_ctxt(bcx, "zero_len"); + auto 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. + auto zero_len = C_int(0); + auto 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. + auto heap_len = nonzero_len_cx.build.Load(nonzero_len_cx.build.GEP( + heap_ptr, [C_int(0), C_uint(abi::ivec_heap_elt_len)])); + auto heap_elem = nonzero_len_cx.build.GEP(heap_ptr, + [C_int(0), C_uint(abi::ivec_heap_elt_elems), C_int(0)]); + 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. + auto len = next_cx.build.Phi(T_int(), [stack_len, zero_len, heap_len], + [bcx.llbb, zero_len_cx.llbb, nonzero_len_cx.llbb]); + auto 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 tup(len, elem, next_cx); +} + type val_pair_fn = fn(&@block_ctxt cx, ValueRef dst, ValueRef src) -> result; type val_and_ty_fn = fn(&@block_ctxt cx, ValueRef v, ty::t t) -> result; @@ -2666,7 +2741,6 @@ fn iter_structural_ty_full(&@block_ctxt cx, &ty::t t, &val_pair_and_ty_fn f) -> result { - let result r = res(cx, C_nil()); fn iter_boxpp(@block_ctxt cx, ValueRef box_a_cell, @@ -2687,6 +2761,45 @@ fn iter_structural_ty_full(&@block_ctxt cx, ret res(next_cx, C_nil()); } + fn iter_ivec(@block_ctxt bcx, + ValueRef av, + ValueRef bv, + ty::t unit_ty, + &val_pair_and_ty_fn f) -> result { + // FIXME: "unimplemented rebinding existing function" workaround + fn adapter(&@block_ctxt bcx, ValueRef av, ValueRef bv, ty::t unit_ty, + val_pair_and_ty_fn f) -> result { + ret f(bcx, av, bv, unit_ty); + } + + auto llunitty = type_of_or_i8(bcx, unit_ty); + + auto rslt = size_of(bcx, unit_ty); + auto unit_sz = rslt.val; + bcx = rslt.bcx; + + auto a_len_and_data = get_ivec_len_and_data(bcx, av, unit_ty); + auto a_len = a_len_and_data._0; + auto a_elem = a_len_and_data._1; + bcx = a_len_and_data._2; + + auto b_len_and_data = get_ivec_len_and_data(bcx, bv, unit_ty); + auto b_len = b_len_and_data._0; + auto b_elem = b_len_and_data._1; + bcx = b_len_and_data._2; + + // Calculate the last pointer address we want to handle. + auto len = umin(bcx, a_len, b_len); + auto b_elem_i8 = bcx.build.PointerCast(b_elem, T_ptr(T_i8())); + auto b_end_i8 = bcx.build.GEP(b_elem_i8, [len]); + auto b_end = bcx.build.PointerCast(b_end_i8, T_ptr(llunitty)); + + // Now perform the iteration. + auto vpf = bind adapter(_, _, _, unit_ty, f); + ret iter_sequence_raw(bcx, a_elem, b_elem, b_end, unit_sz, vpf); + } + + let result r = res(cx, C_nil()); alt (ty::struct(cx.fcx.lcx.ccx.tcx, t)) { case (ty::ty_tup(?args)) { let int i = 0; @@ -2831,6 +2944,13 @@ fn iter_structural_ty_full(&@block_ctxt cx, C_int(abi::obj_field_box)]); ret iter_boxpp(cx, box_cell_a, box_cell_b, f); } + case (ty::ty_ivec(?unit_tm)) { + ret iter_ivec(cx, av, bv, unit_tm.ty, f); + } + case (ty::ty_istr) { + auto unit_ty = ty::mk_mach(cx.fcx.lcx.ccx.tcx, common::ty_u8); + ret iter_ivec(cx, av, bv, unit_ty, f); + } case (_) { cx.fcx.lcx.ccx.sess.unimpl("type in iter_structural_ty_full"); } @@ -5475,6 +5595,93 @@ fn trans_vec(&@block_ctxt cx, &vec[@ast::expr] args, ret res(bcx, vec_val); } +fn trans_ivec(@block_ctxt bcx, &vec[@ast::expr] args, &ast::ann ann) + -> result { + auto typ = node_ann_type(bcx.fcx.lcx.ccx, ann); + auto unit_ty; + alt (ty::struct(bcx.fcx.lcx.ccx.tcx, typ)) { + case (ty::ty_ivec(?mt)) { unit_ty = mt.ty; } + case (_) { bcx.fcx.lcx.ccx.sess.bug("non-ivec type in trans_ivec"); } + } + + auto rslt = size_of(bcx, unit_ty); + auto unit_sz = rslt.val; + bcx = rslt.bcx; + rslt = align_of(bcx, unit_ty); + auto unit_align = rslt.val; + bcx = rslt.bcx; + + auto llunitty = type_of_or_i8(bcx, unit_ty); + auto llvecptr = alloca(bcx, T_ivec()); + auto lllen = bcx.build.Mul(C_uint(vec::len(args)), unit_sz); + + // Allocate the vector pieces and store length and allocated length. + auto llfirsteltptr; + if (vec::len(args) > 0u && vec::len(args) < abi::ivec_default_size) { + // Interior case. + bcx.build.Store(lllen, bcx.build.GEP(llvecptr, + [C_int(0), C_uint(abi::ivec_elt_len)])); + bcx.build.Store(C_uint(abi::ivec_elt_alen), bcx.build.GEP(llvecptr, + [C_int(0), C_uint(abi::ivec_elt_alen)])); + llfirsteltptr = bcx.build.GEP(llvecptr, + [C_int(0), C_uint(abi::ivec_elt_elems)]); + } else { + // Heap case. + auto llstubty = T_ivec_heap(llunitty); + auto llstubptr = bcx.build.PointerCast(llvecptr, T_ptr(llstubty)); + + bcx.build.Store(C_int(0), bcx.build.GEP(llstubptr, + [C_int(0), C_uint(abi::ivec_heap_stub_elt_zero)])); + bcx.build.Store(C_uint(abi::ivec_elt_alen), bcx.build.GEP(llstubptr, + [C_int(0), C_uint(abi::ivec_heap_stub_elt_alen)])); + + auto llheapty = struct_elt(llstubty, abi::ivec_heap_stub_elt_ptr); + + if (vec::len(args) == 0u) { + // Null heap pointer indicates a zero-length vector. + bcx.build.Store(C_null(T_ptr(llheapty)), bcx.build.GEP(llstubptr, + [C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)])); + llfirsteltptr = C_null(T_ptr(llunitty)); + } else { + auto llheapsz = bcx.build.Add(llsize_of(llheapty), lllen); + rslt = trans_raw_malloc(bcx, llheapty, llheapsz); + bcx = rslt.bcx; + auto llheapptr = rslt.val; + + bcx.build.Store(llheapptr, bcx.build.GEP(llstubptr, + [C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)])); + bcx.build.Store(lllen, bcx.build.GEP(llheapptr, + [C_int(0), C_uint(abi::ivec_heap_elt_len)])); + llfirsteltptr = bcx.build.GEP(llheapptr, + [C_int(0), C_uint(abi::ivec_heap_elt_elems)]); + } + } + + llfirsteltptr = bcx.build.PointerCast(llfirsteltptr, T_ptr(llunitty)); + + // Store the individual elements. + auto i = 0u; + for (@ast::expr e in args) { + rslt = trans_expr(bcx, e); + bcx = rslt.bcx; + auto llsrc = rslt.val; + + auto lleltptr; + if (ty::type_has_dynamic_size(bcx.fcx.lcx.ccx.tcx, unit_ty)) { + lleltptr = bcx.build.GEP(llfirsteltptr, + [bcx.build.Mul(C_uint(i), unit_align)]); + } else { + lleltptr = bcx.build.GEP(llfirsteltptr, [C_uint(i)]); + } + + bcx = copy_val(bcx, INIT, lleltptr, llsrc, unit_ty).bcx; + + i += 1u; + } + + ret res(bcx, llvecptr); +} + fn trans_rec(&@block_ctxt cx, &vec[ast::field] fields, &option::t[@ast::expr] base, &ast::ann ann) -> result { @@ -5649,10 +5856,14 @@ fn trans_expr_out(&@block_ctxt cx, &@ast::expr e, out_method output) ret trans_cast(cx, e, ann); } - case (ast::expr_vec(?args, _, _, ?ann)) { + case (ast::expr_vec(?args, _, ast::sk_rc, ?ann)) { ret trans_vec(cx, args, ann); } + case (ast::expr_vec(?args, _, ast::sk_unique, ?ann)) { + ret trans_ivec(cx, args, ann); + } + case (ast::expr_tup(?args, ?ann)) { ret trans_tup(cx, args, ann); } diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index d9d15348f36..e2c40b4bfe9 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -742,31 +742,37 @@ fn type_is_bool(&ctxt cx, &t ty) -> bool { fn type_is_structural(&ctxt cx, &t ty) -> bool { alt (struct(cx, ty)) { - case (ty_tup(_)) { ret true; } - case (ty_rec(_)) { ret true; } - case (ty_tag(_,_)) { ret true; } + case (ty_tup(_)) { ret true; } + case (ty_rec(_)) { ret true; } + case (ty_tag(_,_)) { ret true; } case (ty_fn(_,_,_,_,_)) { ret true; } - case (ty_obj(_)) { ret true; } - case (_) { ret false; } + case (ty_obj(_)) { ret true; } + case (ty_ivec(_)) { ret true; } + case (ty_istr) { ret true; } + case (_) { ret false; } } } fn type_is_sequence(&ctxt cx, &t ty) -> bool { alt (struct(cx, ty)) { - case (ty_str) { ret true; } + case (ty_str) { ret true; } + case (ty_istr) { ret true; } case (ty_vec(_)) { ret true; } + case (ty_ivec(_)) { ret true; } case (_) { ret false; } } } fn sequence_element_type(&ctxt cx, &t ty) -> t { alt (struct(cx, ty)) { - case (ty_str) { ret mk_mach(cx, common::ty_u8); } - case (ty_vec(?mt)) { ret mt.ty; } - // NB: This is not exhaustive. + case (ty_str) { ret mk_mach(cx, common::ty_u8); } + case (ty_istr) { ret mk_mach(cx, common::ty_u8); } + case (ty_vec(?mt)) { ret mt.ty; } + case (ty_ivec(?mt)) { ret mt.ty; } + case (_) { + cx.sess.bug("sequence_element_type called on non-sequence value"); + } } - - cx.sess.bug("sequence_element_type called on non-sequence value"); } fn type_is_tup_like(&ctxt cx, &t ty) -> bool {