rustc: Sketch out translation of interior vector literals and take/drop glue

This commit is contained in:
Patrick Walton 2011-06-10 19:35:59 -07:00
parent b0a80104a3
commit 00eb3f5798
3 changed files with 251 additions and 23 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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 {