rustc: Implement interior vector concatenation

This commit is contained in:
Patrick Walton 2011-06-15 18:02:25 -07:00
parent 260aa408f3
commit 1fa9133b76

View File

@ -3499,6 +3499,172 @@ mod ivec {
post_copy_cx.build.Br(copy_loop_header_cx.llbb);
ret res(next_cx, C_nil());
}
fn alloc(&@block_ctxt bcx, ty::t unit_ty, ValueRef llalen) -> ValueRef {
auto llunitty = type_of_or_i8(bcx, unit_ty);
if (ty::type_has_dynamic_size(bcx.fcx.lcx.ccx.tcx, unit_ty)) {
auto llarraysz = bcx.build.Add(llsize_of(T_opaque_ivec()),
llalen);
auto llvecptr = array_alloca(bcx, T_i8(), llarraysz);
ret bcx.build.PointerCast(llvecptr, T_ptr(T_opaque_ivec()));
}
ret alloca(bcx, T_ivec(llunitty));
}
fn trans_add(&@block_ctxt cx, ty::t vec_ty, ValueRef lhs, ValueRef rhs)
-> result {
auto bcx = cx;
auto unit_ty = ty::sequence_element_type(bcx.fcx.lcx.ccx.tcx, vec_ty);
auto rslt = size_of(bcx, unit_ty);
auto unit_sz = rslt.val;
auto llalen = bcx.build.Mul(unit_sz,
C_uint(abi::ivec_default_length));
auto llvecptr = alloc(bcx, unit_ty, llalen);
auto llunitty = type_of_or_i8(bcx, unit_ty);
auto llheappartty = T_ivec_heap_part(llunitty);
auto lhs_len_and_data = get_len_and_data(bcx, lhs, unit_ty);
auto lhs_len = lhs_len_and_data._0;
auto lhs_data = lhs_len_and_data._1;
bcx = lhs_len_and_data._2;
auto rhs_len_and_data = get_len_and_data(bcx, rhs, unit_ty);
auto rhs_len = rhs_len_and_data._0;
auto rhs_data = rhs_len_and_data._1;
bcx = rhs_len_and_data._2;
auto 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.
auto len_is_zero = bcx.build.ICmp(lib::llvm::LLVMIntEQ, lllen,
C_int(0));
auto zero_len_cx = new_sub_block_ctxt(bcx, "zero_len");
auto 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.
auto 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, [C_int(0), C_uint(abi::ivec_heap_stub_elt_zero)]));
zero_len_cx.build.Store(llalen, zero_len_cx.build.InBoundsGEP(
stub_ptr_zero, [C_int(0), C_uint(abi::ivec_heap_stub_elt_alen)]));
zero_len_cx.build.Store(C_null(T_ptr(llheappartty)),
zero_len_cx.build.InBoundsGEP(stub_ptr_zero,
[C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]));
auto 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.
auto on_stack = nonzero_len_cx.build.ICmp(lib::llvm::LLVMIntULE,
lllen, llalen);
auto stack_cx = new_sub_block_ctxt(bcx, "stack");
auto 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,
[C_int(0), C_uint(abi::ivec_elt_len)]));
stack_cx.build.Store(llalen, stack_cx.build.InBoundsGEP(llvecptr,
[C_int(0), C_uint(abi::ivec_elt_alen)]));
auto dest_ptr_stack = stack_cx.build.InBoundsGEP(llvecptr,
[C_int(0), C_uint(abi::ivec_elt_elems), C_int(0)]);
auto copy_cx = new_sub_block_ctxt(bcx, "copy");
stack_cx.build.Br(copy_cx.llbb);
// Case (3): Allocate on heap and copy there.
auto 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, [C_int(0), C_uint(abi::ivec_heap_stub_elt_zero)]));
heap_cx.build.Store(lllen, heap_cx.build.InBoundsGEP(
stub_ptr_heap, [C_int(0), C_uint(abi::ivec_heap_stub_elt_alen)]));
auto heap_sz = heap_cx.build.Add(llsize_of(llheappartty), lllen);
rslt = trans_raw_malloc(heap_cx, T_ptr(llheappartty), heap_sz);
auto heap_part = rslt.val;
heap_cx = rslt.bcx;
heap_cx.build.Store(heap_part, heap_cx.build.InBoundsGEP(
stub_ptr_heap, [C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]));
heap_cx.build.Store(lllen, heap_cx.build.InBoundsGEP(heap_part,
[C_int(0), C_uint(abi::ivec_heap_elt_len)]));
auto 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.
auto first_dest_ptr = copy_cx.build.Phi(T_ptr(llunitty),
[dest_ptr_stack, dest_ptr_heap], [stack_cx.llbb, heap_cx.llbb]);
auto lhs_len_unscaled = copy_cx.build.UDiv(lhs_len, unit_sz);
auto lhs_end_ptr = copy_cx.build.InBoundsGEP(lhs_data,
[lhs_len_unscaled]);
auto rhs_len_unscaled = copy_cx.build.UDiv(rhs_len, unit_sz);
auto rhs_end_ptr = copy_cx.build.InBoundsGEP(rhs_data,
[rhs_len_unscaled]);
auto dest_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
copy_cx.build.Store(first_dest_ptr, dest_ptr_ptr);
auto lhs_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
copy_cx.build.Store(lhs_data, lhs_ptr_ptr);
auto rhs_ptr_ptr = alloca(copy_cx, T_ptr(llunitty));
copy_cx.build.Store(rhs_data, rhs_ptr_ptr);
auto lhs_copy_cx = new_sub_block_ctxt(bcx, "lhs_copy");
copy_cx.build.Br(lhs_copy_cx.llbb);
// Copy in elements from the LHS.
auto lhs_ptr = lhs_copy_cx.build.Load(lhs_ptr_ptr);
auto not_at_end_lhs = lhs_copy_cx.build.ICmp(lib::llvm::LLVMIntNE,
lhs_ptr, lhs_end_ptr);
auto lhs_do_copy_cx = new_sub_block_ctxt(bcx, "lhs_do_copy");
auto 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);
auto dest_ptr_lhs_copy = lhs_do_copy_cx.build.Load(dest_ptr_ptr);
auto lhs_val = load_if_immediate(lhs_do_copy_cx, lhs_ptr, unit_ty);
rslt = copy_val(lhs_do_copy_cx, INIT, dest_ptr_lhs_copy, lhs_val,
unit_ty);
lhs_do_copy_cx = rslt.bcx;
lhs_do_copy_cx.build.Store(lhs_do_copy_cx.build.InBoundsGEP(
dest_ptr_lhs_copy, [C_int(1)]), dest_ptr_ptr);
lhs_do_copy_cx.build.Store(lhs_do_copy_cx.build.InBoundsGEP(
lhs_ptr, [C_int(1)]), lhs_ptr_ptr);
lhs_do_copy_cx.build.Br(lhs_copy_cx.llbb);
// Copy in elements from the RHS.
auto rhs_ptr = rhs_copy_cx.build.Load(rhs_ptr_ptr);
auto not_at_end_rhs = rhs_copy_cx.build.ICmp(lib::llvm::LLVMIntNE,
rhs_ptr, rhs_end_ptr);
auto 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);
auto dest_ptr_rhs_copy = rhs_do_copy_cx.build.Load(dest_ptr_ptr);
auto rhs_val = load_if_immediate(rhs_do_copy_cx, rhs_ptr, unit_ty);
rslt = copy_val(rhs_do_copy_cx, INIT, dest_ptr_rhs_copy, rhs_val,
unit_ty);
rhs_do_copy_cx = rslt.bcx;
rhs_do_copy_cx.build.Store(rhs_do_copy_cx.build.InBoundsGEP(
dest_ptr_rhs_copy, [C_int(1)]), dest_ptr_ptr);
rhs_do_copy_cx.build.Store(rhs_do_copy_cx.build.InBoundsGEP(
rhs_ptr, [C_int(1)]), rhs_ptr_ptr);
rhs_do_copy_cx.build.Br(rhs_copy_cx.llbb);
// Finally done!
ret res(next_cx, llvecptr);
}
}
@ -3523,6 +3689,9 @@ fn trans_eager_binop(&@block_ctxt cx, ast::binop op, &ty::t intype,
alt (op) {
case (ast::add) {
if (ty::type_is_sequence(cx.fcx.lcx.ccx.tcx, intype)) {
if (ty::sequence_is_interior(cx.fcx.lcx.ccx.tcx, intype)) {
ret ivec::trans_add(cx, intype, lhs, rhs);
}
ret trans_vec_add(cx, intype, lhs, rhs);
}
if (is_float) {
@ -5170,6 +5339,7 @@ fn trans_vec(&@block_ctxt cx, &vec[@ast::expr] args, &ast::ann ann) ->
ret res(bcx, vec_val);
}
// TODO: Move me to ivec::
fn trans_ivec(@block_ctxt bcx, &vec[@ast::expr] args, &ast::ann ann) ->
result {
auto typ = node_ann_type(bcx.fcx.lcx.ccx, ann);
@ -5183,17 +5353,12 @@ fn trans_ivec(@block_ctxt bcx, &vec[@ast::expr] args, &ast::ann ann) ->
bcx = rslt.bcx;
auto llalen = bcx.build.Mul(unit_sz, C_uint(abi::ivec_default_length));
auto llunitty = type_of_or_i8(bcx, unit_ty);
auto llvecptr;
if (ty::type_has_dynamic_size(bcx.fcx.lcx.ccx.tcx, unit_ty)) {
auto array_size = bcx.build.Add(llsize_of(T_opaque_ivec()), llalen);
llvecptr = array_alloca(bcx, T_i8(), array_size);
llvecptr = bcx.build.PointerCast(llvecptr, T_ptr(T_opaque_ivec()));
} else { llvecptr = alloca(bcx, T_ivec(llunitty)); }
auto llvecptr = ivec::alloc(bcx, unit_ty, llalen);
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_length) {
if (vec::len(args) > 0u && vec::len(args) <= abi::ivec_default_length) {
// Interior case.
bcx.build.Store(lllen,
@ -5222,7 +5387,7 @@ fn trans_ivec(@block_ctxt bcx, &vec[@ast::expr] args, &ast::ann ann) ->
bcx.build.InBoundsGEP(llstubptr, stub_z));
bcx.build.Store(lllen,
bcx.build.InBoundsGEP(llstubptr, stub_a));
auto llheapty = struct_elt(llstubty, abi::ivec_heap_stub_elt_ptr);
auto llheapty = T_ivec_heap_part(llunitty);
if (vec::len(args) == 0u) {
// Null heap pointer indicates a zero-length vector.
@ -5236,7 +5401,7 @@ fn trans_ivec(@block_ctxt bcx, &vec[@ast::expr] args, &ast::ann ann) ->
auto llheapptr = rslt.val;
bcx.build.Store(llheapptr,
bcx.build.InBoundsGEP(llstubptr, stub_p));
auto heap_l = [C_uint(abi::ivec_heap_elt_len)];
auto heap_l = [C_int(0), C_uint(abi::ivec_heap_elt_len)];
bcx.build.Store(lllen,
bcx.build.InBoundsGEP(llheapptr, heap_l));
llfirsteltptr =