Skip to content

Commit c3bf7d0

Browse files
committed
rustc: Implement simple interior vector append translation
1 parent a1bb4a4 commit c3bf7d0

File tree

6 files changed

+305
-35
lines changed

6 files changed

+305
-35
lines changed

src/comp/back/upcall.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import trans::T_i32;
1010
import trans::T_int;
1111
import trans::T_nil;
1212
import trans::T_opaque_chan_ptr;
13+
import trans::T_opaque_ivec;
1314
import trans::T_opaque_port_ptr;
1415
import trans::T_opaque_vec_ptr;
1516
import trans::T_ptr;
@@ -56,7 +57,9 @@ type upcalls = rec(
5657
ValueRef new_task,
5758
ValueRef start_task,
5859
ValueRef new_thread,
59-
ValueRef start_thread
60+
ValueRef start_thread,
61+
ValueRef ivec_resize,
62+
ValueRef ivec_spill
6063
);
6164

6265
fn declare_upcalls(type_names tn, ModuleRef llmod) -> @upcalls {
@@ -119,7 +122,11 @@ fn declare_upcalls(type_names tn, ModuleRef llmod) -> @upcalls {
119122
new_thread=d("new_thread", [T_ptr(T_i8())], T_taskptr(tn)),
120123
start_thread=d("start_thread", [T_taskptr(tn), T_int(), T_int(),
121124
T_int(), T_size_t()],
122-
T_taskptr(tn))
125+
T_taskptr(tn)),
126+
ivec_resize=d("ivec_resize", [T_ptr(T_opaque_ivec()), T_int()],
127+
T_void()),
128+
ivec_spill=d("ivec_spill", [T_ptr(T_opaque_ivec()), T_int()],
129+
T_void())
123130
);
124131
}
125132

src/comp/middle/trans.rs

Lines changed: 238 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -584,20 +584,29 @@ fn T_opaque_ivec() -> TypeRef {
584584
T_array(T_i8(), abi::ivec_default_size)]); // Body elements
585585
}
586586

587-
// Interior vector on the heap. Cast to this when the allocated length (second
588-
// element of T_ivec above) is zero.
587+
fn T_ivec_heap_part(TypeRef t) -> TypeRef {
588+
ret T_struct([T_int(), // Real length
589+
T_array(t, 0u)]); // Body elements
590+
}
591+
592+
// Interior vector on the heap, also known as the "stub". Cast to this when
593+
// the allocated length (second element of T_ivec above) is zero.
589594
fn T_ivec_heap(TypeRef t) -> TypeRef {
590-
ret T_struct([T_int(), // Length (zero)
591-
T_int(), // Alloc
592-
T_ptr(T_struct([T_int(), // Real length
593-
T_array(t, 0u)]))]); // Body elements
595+
ret T_struct([T_int(), // Length (zero)
596+
T_int(), // Alloc
597+
T_ptr(T_ivec_heap_part(t))]); // Pointer
598+
}
599+
600+
fn T_opaque_ivec_heap_part() -> TypeRef {
601+
ret T_struct([T_int(), // Real length
602+
T_array(T_i8(), 0u)]); // Body elements
603+
594604
}
595605

596606
fn T_opaque_ivec_heap() -> TypeRef {
597-
ret T_struct([T_int(), // Length (zero)
598-
T_int(), // Alloc
599-
T_ptr(T_struct([T_int(), // Real length
600-
T_array(T_i8(), 0u)]))]); // Body elements
607+
ret T_struct([T_int(), // Length (zero)
608+
T_int(), // Alloc
609+
T_ptr(T_opaque_ivec_heap_part())]); // Pointer
601610
}
602611

603612
fn T_str() -> TypeRef {
@@ -2670,9 +2679,8 @@ fn get_ivec_len_and_data(&@block_ctxt bcx, ValueRef v, ty::t unit_ty) ->
26702679

26712680
auto stack_len = bcx.build.Load(bcx.build.InBoundsGEP(v,
26722681
[C_int(0), C_uint(abi::ivec_elt_len)]));
2673-
auto stack_elem = bcx.build.InBoundsGEP(v, [C_int(0),
2674-
C_uint(abi::ivec_elt_elems),
2675-
C_int(0)]);
2682+
auto stack_elem = bcx.build.InBoundsGEP(v,
2683+
[C_int(0), C_uint(abi::ivec_elt_elems), C_int(0)]);
26762684

26772685
auto on_heap = bcx.build.ICmp(lib::llvm::LLVMIntEQ, stack_len, C_int(0));
26782686

@@ -3680,6 +3688,201 @@ fn trans_vec_append(&@block_ctxt cx, &ty::t t,
36803688
dst, src, skip_null]));
36813689
}
36823690

3691+
// Returns a tuple consisting of a pointer to the length (to be updated), a
3692+
// pointer to the newly-reserved space, and a block context.
3693+
fn reserve_ivec_space(&@block_ctxt cx, TypeRef llunitty, ValueRef v,
3694+
ValueRef len_needed) -> tup(ValueRef, ValueRef, @block_ctxt) {
3695+
auto stack_len_ptr = cx.build.InBoundsGEP(v, [C_int(0),
3696+
C_uint(abi::ivec_elt_len)]);
3697+
auto stack_len = cx.build.Load(stack_len_ptr);
3698+
auto alen = cx.build.Load(cx.build.InBoundsGEP(v,
3699+
[C_int(0), C_uint(abi::ivec_elt_alen)]));
3700+
3701+
// There are four cases we have to consider:
3702+
// (1) On heap, no resize necessary.
3703+
// (2) On heap, need to resize.
3704+
// (3) On stack, no resize necessary.
3705+
// (4) On stack, need to spill to heap.
3706+
3707+
auto maybe_on_heap = cx.build.ICmp(lib::llvm::LLVMIntEQ, stack_len,
3708+
C_int(0));
3709+
auto maybe_on_heap_cx = new_sub_block_ctxt(cx, "maybe_on_heap");
3710+
auto on_stack_cx = new_sub_block_ctxt(cx, "on_stack");
3711+
cx.build.CondBr(maybe_on_heap, maybe_on_heap_cx.llbb, on_stack_cx.llbb);
3712+
3713+
auto next_cx = new_sub_block_ctxt(cx, "next");
3714+
3715+
// We're possibly on the heap, unless the vector is zero-length.
3716+
auto stub_ptr = maybe_on_heap_cx.build.PointerCast(v,
3717+
T_ptr(T_ivec_heap(llunitty)));
3718+
auto heap_ptr = maybe_on_heap_cx.build.Load(
3719+
maybe_on_heap_cx.build.InBoundsGEP(stub_ptr,
3720+
[C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]));
3721+
auto on_heap = maybe_on_heap_cx.build.ICmp(lib::llvm::LLVMIntNE, heap_ptr,
3722+
C_null(val_ty(heap_ptr)));
3723+
auto on_heap_cx = new_sub_block_ctxt(cx, "on_heap");
3724+
maybe_on_heap_cx.build.CondBr(on_heap, on_heap_cx.llbb, on_stack_cx.llbb);
3725+
3726+
// We're definitely on the heap. Check whether we need to resize.
3727+
auto heap_len_ptr = on_heap_cx.build.InBoundsGEP(heap_ptr, [C_int(0),
3728+
C_int(0)]);
3729+
auto heap_len = on_heap_cx.build.Load(heap_len_ptr);
3730+
auto new_heap_len = on_heap_cx.build.Add(heap_len, len_needed);
3731+
auto heap_no_resize_needed = on_heap_cx.build.ICmp(lib::llvm::LLVMIntULT,
3732+
new_heap_len, alen);
3733+
auto heap_no_resize_cx = new_sub_block_ctxt(cx, "heap_no_resize");
3734+
auto heap_resize_cx = new_sub_block_ctxt(cx, "heap_resize");
3735+
on_heap_cx.build.CondBr(heap_no_resize_needed, heap_no_resize_cx.llbb,
3736+
heap_resize_cx.llbb);
3737+
3738+
// Case (1): We're on the heap and don't need to resize.
3739+
auto heap_len_unscaled = heap_no_resize_cx.build.UDiv(heap_len,
3740+
llsize_of(llunitty));
3741+
auto heap_data_no_resize = heap_no_resize_cx.build.InBoundsGEP(heap_ptr,
3742+
[C_int(0), C_uint(abi::ivec_heap_elt_elems), heap_len_unscaled]);
3743+
heap_no_resize_cx.build.Br(next_cx.llbb);
3744+
3745+
// Case (2): We're on the heap and need to resize. This path is rare, so
3746+
// we delegate to cold glue.
3747+
heap_resize_cx.build.Call(
3748+
cx.fcx.lcx.ccx.upcalls.ivec_resize, [
3749+
cx.fcx.lltaskptr,
3750+
heap_resize_cx.build.PointerCast(v, T_ptr(T_opaque_ivec())),
3751+
new_heap_len
3752+
]);
3753+
auto heap_ptr_resize = heap_resize_cx.build.Load(
3754+
heap_resize_cx.build.InBoundsGEP(stub_ptr,
3755+
[C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]));
3756+
auto heap_data_resize = heap_resize_cx.build.InBoundsGEP(heap_ptr_resize,
3757+
[C_int(0), C_uint(abi::ivec_heap_elt_elems), C_int(0)]);
3758+
heap_resize_cx.build.Br(next_cx.llbb);
3759+
3760+
// We're on the stack. Check whether we need to spill to the heap.
3761+
auto new_stack_len = on_stack_cx.build.Add(stack_len, len_needed);
3762+
auto stack_no_spill_needed = on_stack_cx.build.ICmp(lib::llvm::LLVMIntULT,
3763+
new_stack_len, alen);
3764+
auto stack_no_spill_cx = new_sub_block_ctxt(cx, "stack_no_spill");
3765+
auto stack_spill_cx = new_sub_block_ctxt(cx, "stack_spill");
3766+
on_stack_cx.build.CondBr(stack_no_spill_needed, stack_no_spill_cx.llbb,
3767+
stack_spill_cx.llbb);
3768+
3769+
// Case (3): We're on the stack and don't need to spill.
3770+
auto stack_len_unscaled = stack_no_spill_cx.build.UDiv(stack_len,
3771+
llsize_of(llunitty));
3772+
auto stack_data_no_spill = stack_no_spill_cx.build.InBoundsGEP(v,
3773+
[C_int(0), C_uint(abi::ivec_elt_elems), stack_len_unscaled]);
3774+
stack_no_spill_cx.build.Br(next_cx.llbb);
3775+
3776+
// Case (4): We're on the stack and need to spill. Like case (2), this
3777+
// path is rare, so we delegate to cold glue.
3778+
stack_spill_cx.build.Call(
3779+
cx.fcx.lcx.ccx.upcalls.ivec_spill, [
3780+
cx.fcx.lltaskptr,
3781+
stack_spill_cx.build.PointerCast(v, T_ptr(T_opaque_ivec())),
3782+
new_stack_len
3783+
]);
3784+
auto spill_stub = stack_spill_cx.build.PointerCast(v,
3785+
T_ptr(T_ivec_heap(llunitty)));
3786+
auto heap_ptr_spill = stack_spill_cx.build.Load(
3787+
stack_spill_cx.build.InBoundsGEP(spill_stub,
3788+
[C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]));
3789+
auto heap_len_ptr_spill = stack_spill_cx.build.InBoundsGEP(heap_ptr_spill,
3790+
[C_int(0), C_uint(abi::ivec_heap_elt_len)]);
3791+
auto heap_data_spill = stack_spill_cx.build.InBoundsGEP(heap_ptr_spill,
3792+
[C_int(0), C_uint(abi::ivec_heap_elt_elems), C_int(0)]);
3793+
3794+
stack_spill_cx.build.Br(next_cx.llbb);
3795+
3796+
// Phi together the different data pointers to get the result.
3797+
auto len_ptr = next_cx.build.Phi(T_ptr(T_int()),
3798+
[heap_len_ptr, heap_len_ptr, stack_len_ptr, heap_len_ptr_spill],
3799+
[heap_no_resize_cx.llbb, heap_resize_cx.llbb, stack_no_spill_cx.llbb,
3800+
stack_spill_cx.llbb]);
3801+
auto data_ptr = next_cx.build.Phi(T_ptr(llunitty),
3802+
[heap_data_no_resize, heap_data_resize, stack_data_no_spill,
3803+
heap_data_spill],
3804+
[heap_no_resize_cx.llbb, heap_resize_cx.llbb, stack_no_spill_cx.llbb,
3805+
stack_spill_cx.llbb]);
3806+
ret tup(len_ptr, data_ptr, next_cx);
3807+
}
3808+
3809+
fn trans_ivec_append(&@block_ctxt cx, &ty::t t, ValueRef lhs, ValueRef rhs)
3810+
-> result {
3811+
auto unit_ty = ty::sequence_element_type(cx.fcx.lcx.ccx.tcx, t);
3812+
auto llunitty = type_of_or_i8(cx, unit_ty);
3813+
3814+
auto skip_null;
3815+
alt (ty::struct(cx.fcx.lcx.ccx.tcx, t)) {
3816+
case (ty::ty_istr) { skip_null = true; }
3817+
case (ty::ty_ivec(_)) { skip_null = false; }
3818+
case (_) {
3819+
cx.fcx.lcx.ccx.tcx.sess.bug("non-istr/ivec in trans_ivec_append");
3820+
}
3821+
}
3822+
3823+
// Gather the various type descriptors we'll need.
3824+
auto rslt = get_tydesc(cx, t, false, none);
3825+
auto vec_tydesc = rslt.val;
3826+
auto bcx = rslt.bcx;
3827+
3828+
rslt = get_tydesc(bcx, unit_ty, false, none);
3829+
auto unit_tydesc = rslt.val;
3830+
bcx = rslt.bcx;
3831+
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_take_glue, none);
3832+
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_drop_glue, none);
3833+
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_free_glue, none);
3834+
3835+
auto rhs_len_and_data = get_ivec_len_and_data(bcx, rhs, unit_ty);
3836+
auto rhs_len = rhs_len_and_data._0;
3837+
auto rhs_data = rhs_len_and_data._1;
3838+
bcx = rhs_len_and_data._2;
3839+
3840+
auto lhs_len_ptr_and_data =
3841+
reserve_ivec_space(bcx, llunitty, lhs, rhs_len);
3842+
auto lhs_len_ptr = lhs_len_ptr_and_data._0;
3843+
auto lhs_data = lhs_len_ptr_and_data._1;
3844+
bcx = lhs_len_ptr_and_data._2;
3845+
3846+
// Work out the end pointer.
3847+
auto lhs_unscaled_idx = bcx.build.UDiv(rhs_len, llsize_of(llunitty));
3848+
auto lhs_end = bcx.build.InBoundsGEP(lhs_data, [lhs_unscaled_idx]);
3849+
3850+
// Now emit the copy loop.
3851+
auto dest_ptr = alloca(bcx, T_ptr(llunitty));
3852+
bcx.build.Store(lhs_data, dest_ptr);
3853+
auto src_ptr = alloca(bcx, T_ptr(llunitty));
3854+
bcx.build.Store(rhs_data, src_ptr);
3855+
3856+
auto copy_loop_header_cx = new_sub_block_ctxt(bcx, "copy_loop_header");
3857+
bcx.build.Br(copy_loop_header_cx.llbb);
3858+
3859+
auto copy_dest_ptr = copy_loop_header_cx.build.Load(dest_ptr);
3860+
auto not_yet_at_end = copy_loop_header_cx.build.ICmp(lib::llvm::LLVMIntNE,
3861+
copy_dest_ptr, lhs_end);
3862+
auto copy_loop_body_cx = new_sub_block_ctxt(bcx, "copy_loop_body");
3863+
auto next_cx = new_sub_block_ctxt(bcx, "next");
3864+
copy_loop_header_cx.build.CondBr(not_yet_at_end, copy_loop_body_cx.llbb,
3865+
next_cx.llbb);
3866+
3867+
auto copy_src_ptr = copy_loop_body_cx.build.Load(src_ptr);
3868+
rslt = copy_val(copy_loop_body_cx, INIT, copy_dest_ptr, copy_src_ptr, t);
3869+
auto post_copy_cx = rslt.bcx;
3870+
3871+
// Increment both pointers.
3872+
post_copy_cx.build.Store(post_copy_cx.build.InBoundsGEP(copy_dest_ptr,
3873+
[C_int(1)]), dest_ptr);
3874+
post_copy_cx.build.Store(post_copy_cx.build.InBoundsGEP(copy_src_ptr,
3875+
[C_int(1)]), src_ptr);
3876+
post_copy_cx.build.Br(copy_loop_header_cx.llbb);
3877+
3878+
// Write in the new length.
3879+
auto new_len = next_cx.build.Add(next_cx.build.Load(lhs_len_ptr),
3880+
rhs_len);
3881+
next_cx.build.Store(new_len, lhs_len_ptr);
3882+
3883+
ret res(next_cx, C_nil());
3884+
}
3885+
36833886
fn trans_vec_add(&@block_ctxt cx, &ty::t t,
36843887
ValueRef lhs, ValueRef rhs) -> result {
36853888
auto r = alloc_ty(cx, t);
@@ -4741,14 +4944,8 @@ fn trans_index(&@block_ctxt cx, &span sp, &@ast::expr base,
47414944
auto base_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, base);
47424945
auto base_ty_no_boxes = ty::strip_boxes(cx.fcx.lcx.ccx.tcx, base_ty);
47434946

4744-
auto is_interior;
4745-
alt (ty::struct(cx.fcx.lcx.ccx.tcx, base_ty_no_boxes)) {
4746-
// TODO: Or-patterns
4747-
case (ty::ty_vec(_)) { is_interior = false; }
4748-
case (ty::ty_str) { is_interior = false; }
4749-
case (ty::ty_ivec(_)) { is_interior = true; }
4750-
case (ty::ty_istr) { is_interior = true; }
4751-
};
4947+
auto is_interior = ty::sequence_is_interior(cx.fcx.lcx.ccx.tcx,
4948+
base_ty_no_boxes);
47524949

47534950
auto lv = trans_expr(cx, base);
47544951
lv = autoderef(lv.bcx, lv.val, base_ty);
@@ -5655,40 +5852,43 @@ fn trans_ivec(@block_ctxt bcx, &vec[@ast::expr] args, &ast::ann ann)
56555852
auto llfirsteltptr;
56565853
if (vec::len(args) > 0u && vec::len(args) < abi::ivec_default_size) {
56575854
// Interior case.
5658-
bcx.build.Store(lllen, bcx.build.GEP(llvecptr,
5855+
bcx.build.Store(lllen, bcx.build.InBoundsGEP(llvecptr,
56595856
[C_int(0), C_uint(abi::ivec_elt_len)]));
5660-
bcx.build.Store(C_uint(abi::ivec_elt_alen), bcx.build.GEP(llvecptr,
5661-
[C_int(0), C_uint(abi::ivec_elt_alen)]));
5662-
llfirsteltptr = bcx.build.GEP(llvecptr,
5857+
bcx.build.Store(C_uint(abi::ivec_default_size),
5858+
bcx.build.InBoundsGEP(llvecptr,
5859+
[C_int(0), C_uint(abi::ivec_elt_alen)]));
5860+
llfirsteltptr = bcx.build.InBoundsGEP(llvecptr,
56635861
[C_int(0), C_uint(abi::ivec_elt_elems), C_int(0)]);
56645862
} else {
56655863
// Heap case.
56665864
auto llstubty = T_ivec_heap(llunitty);
56675865
auto llstubptr = bcx.build.PointerCast(llvecptr, T_ptr(llstubty));
56685866

5669-
bcx.build.Store(C_int(0), bcx.build.GEP(llstubptr,
5867+
bcx.build.Store(C_int(0), bcx.build.InBoundsGEP(llstubptr,
56705868
[C_int(0), C_uint(abi::ivec_heap_stub_elt_zero)]));
5671-
bcx.build.Store(C_uint(abi::ivec_elt_alen), bcx.build.GEP(llstubptr,
5672-
[C_int(0), C_uint(abi::ivec_heap_stub_elt_alen)]));
5869+
bcx.build.Store(lllen,
5870+
bcx.build.InBoundsGEP(llstubptr,
5871+
[C_int(0), C_uint(abi::ivec_heap_stub_elt_alen)]));
56735872

56745873
auto llheapty = struct_elt(llstubty, abi::ivec_heap_stub_elt_ptr);
56755874

56765875
if (vec::len(args) == 0u) {
56775876
// Null heap pointer indicates a zero-length vector.
5678-
bcx.build.Store(C_null(T_ptr(llheapty)), bcx.build.GEP(llstubptr,
5679-
[C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]));
5877+
bcx.build.Store(C_null(T_ptr(llheapty)),
5878+
bcx.build.InBoundsGEP(llstubptr,
5879+
[C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]));
56805880
llfirsteltptr = C_null(T_ptr(llunitty));
56815881
} else {
56825882
auto llheapsz = bcx.build.Add(llsize_of(llheapty), lllen);
56835883
rslt = trans_raw_malloc(bcx, llheapty, llheapsz);
56845884
bcx = rslt.bcx;
56855885
auto llheapptr = rslt.val;
56865886

5687-
bcx.build.Store(llheapptr, bcx.build.GEP(llstubptr,
5887+
bcx.build.Store(llheapptr, bcx.build.InBoundsGEP(llstubptr,
56885888
[C_int(0), C_uint(abi::ivec_heap_stub_elt_ptr)]));
5689-
bcx.build.Store(lllen, bcx.build.GEP(llheapptr,
5889+
bcx.build.Store(lllen, bcx.build.InBoundsGEP(llheapptr,
56905890
[C_int(0), C_uint(abi::ivec_heap_elt_len)]));
5691-
llfirsteltptr = bcx.build.GEP(llheapptr,
5891+
llfirsteltptr = bcx.build.InBoundsGEP(llheapptr,
56925892
[C_int(0), C_uint(abi::ivec_heap_elt_elems), C_int(0)]);
56935893
}
56945894
}
@@ -5864,6 +6064,11 @@ fn trans_expr_out(&@block_ctxt cx, &@ast::expr e, out_method output)
58646064
if (ty::type_is_sequence(cx.fcx.lcx.ccx.tcx, t)) {
58656065
alt (op) {
58666066
case (ast::add) {
6067+
if (ty::sequence_is_interior(cx.fcx.lcx.ccx.tcx, t)) {
6068+
ret trans_ivec_append(rhs_res.bcx, t,
6069+
lhs_res.res.val,
6070+
rhs_res.val);
6071+
}
58676072
ret trans_vec_append(rhs_res.bcx, t,
58686073
lhs_res.res.val,
58696074
rhs_res.val);

src/comp/middle/ty.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,19 @@ fn type_is_sequence(&ctxt cx, &t ty) -> bool {
763763
}
764764
}
765765

766+
fn sequence_is_interior(&ctxt cx, &t ty) -> bool {
767+
alt (struct(cx, ty)) {
768+
// TODO: Or-patterns
769+
case (ty::ty_vec(_)) { ret false; }
770+
case (ty::ty_str) { ret false; }
771+
case (ty::ty_ivec(_)) { ret true; }
772+
case (ty::ty_istr) { ret true; }
773+
case (_) {
774+
cx.sess.bug("sequence_is_interior called on non-sequence type");
775+
}
776+
}
777+
}
778+
766779
fn sequence_element_type(&ctxt cx, &t ty) -> t {
767780
alt (struct(cx, ty)) {
768781
case (ty_str) { ret mk_mach(cx, common::ty_u8); }

0 commit comments

Comments
 (0)