Skip to content

Commit 08eabde

Browse files
committed
rustc: First stab at implementing interior vector concat in the DPS engine
1 parent 7d49035 commit 08eabde

File tree

3 files changed

+267
-27
lines changed

3 files changed

+267
-27
lines changed

src/comp/middle/trans_dps.rs

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ fn mk_const(&@crate_ctxt ccx, &str name, bool exported, ValueRef llval)
6262
}
6363

6464

65+
// Type utilities
66+
67+
fn size_of(&@crate_ctxt ccx, &span sp, ty::t t) -> uint {
68+
if ty::type_has_dynamic_size(ccx.tcx, t) {
69+
ccx.sess.bug("trans_dps::size_of() called on a type with dynamic " +
70+
"size");
71+
}
72+
ret llsize_of(ccx, trans::type_of_inner(ccx, sp, t));
73+
}
74+
75+
6576
// Destination utilities
6677

6778
tag dest {
@@ -93,7 +104,12 @@ fn dest_ptr(&dest dest) -> ValueRef {
93104
alt (dest) {
94105
dst_nil { fail "nil dest in dest_ptr" }
95106
dst_imm(_) { fail "immediate dest in dest_ptr" }
96-
dst_alias(_) { fail "alias dest in dest_ptr" }
107+
dst_alias(?box) {
108+
alt (*box) {
109+
none { fail "alias wasn't filled in prior to dest_ptr" }
110+
some(?llval) { llval }
111+
}
112+
}
97113
dst_copy(?llptr) { llptr }
98114
dst_move(?llptr) { llptr }
99115
}
@@ -137,6 +153,22 @@ fn ccx_tcx(&@crate_ctxt ccx) -> ty::ctxt { ret ccx.tcx; }
137153

138154
// Common operations
139155

156+
fn memmove(&@block_ctxt bcx, ValueRef lldestptr, ValueRef llsrcptr,
157+
ValueRef llsz) {
158+
auto lldestty = llelement_type(trans::val_ty(lldestptr));
159+
auto llsrcty = llelement_type(trans::val_ty(llsrcptr));
160+
auto dest_align = llalign_of(bcx_ccx(bcx), lldestty);
161+
auto src_align = llalign_of(bcx_ccx(bcx), llsrcty);
162+
auto align = uint::min(dest_align, src_align);
163+
auto llfn = bcx_ccx(bcx).intrinsics.get("llvm.memmove.p0i8.p0i8.i32");
164+
auto lldestptr_i8 = bcx.build.PointerCast(lldestptr,
165+
tc::T_ptr(tc::T_i8()));
166+
auto llsrcptr_i8 = bcx.build.PointerCast(llsrcptr,
167+
tc::T_ptr(tc::T_i8()));
168+
bcx.build.Call(llfn, ~[lldestptr_i8, llsrcptr_i8, llsz, tc::C_uint(align),
169+
tc::C_bool(false)]);
170+
}
171+
140172
// If "cast" is true, casts dest appropriately before the store.
141173
fn store_imm(&@block_ctxt bcx, &dest dest, ValueRef llsrc, bool cast)
142174
-> @block_ctxt {
@@ -170,22 +202,9 @@ fn store_ptr(&@block_ctxt bcx, &dest dest, ValueRef llsrcptr) -> @block_ctxt {
170202
*box = some(llsrcptr);
171203
}
172204
dst_copy(?lldestptr) | dst_move(?lldestptr) {
173-
auto lldestty = llelement_type(trans::val_ty(llsrcptr));
174205
auto llsrcty = llelement_type(trans::val_ty(llsrcptr));
175-
auto dest_align = llalign_of(bcx_ccx(bcx), lldestty);
176-
auto src_align = llalign_of(bcx_ccx(bcx), llsrcty);
177-
auto align = uint::min(dest_align, src_align);
178-
auto llfn = bcx_ccx(bcx).intrinsics.get("llvm.memmove.p0i8.p0i8.i32");
179-
auto lldestptr_i8 = bcx.build.PointerCast(lldestptr,
180-
tc::T_ptr(tc::T_i8()));
181-
auto llsrcptr_i8 = bcx.build.PointerCast(llsrcptr,
182-
tc::T_ptr(tc::T_i8()));
183-
bcx.build.Call(llfn,
184-
~[lldestptr_i8,
185-
llsrcptr_i8,
186-
tc::C_uint(llsize_of(bcx_ccx(bcx), llsrcty)),
187-
tc::C_uint(align),
188-
tc::C_bool(false)]);
206+
auto llsz = tc::C_uint(llsize_of(bcx_ccx(bcx), llsrcty));
207+
memmove(bcx, lldestptr, llsrcptr, llsz);
189208
ret bcx;
190209
}
191210
}
@@ -194,6 +213,9 @@ fn store_ptr(&@block_ctxt bcx, &dest dest, ValueRef llsrcptr) -> @block_ctxt {
194213

195214
// Allocates a value of the given LLVM size on either the task heap or the
196215
// shared heap.
216+
//
217+
// TODO: This should *not* use destination-passing style, because doing so
218+
// makes callers incur an extra load.
197219
tag heap { hp_task; hp_shared; }
198220
fn malloc(&@block_ctxt bcx, ValueRef lldest, heap heap,
199221
option[ValueRef] llcustom_size_opt) -> @block_ctxt {
@@ -287,18 +309,18 @@ fn trans_lit(&@block_ctxt cx, &dest dest, &ast::lit lit) -> @block_ctxt {
287309
ret bcx;
288310
}
289311

290-
fn trans_binary(&@block_ctxt cx, &dest in_dest, ast::binop op,
312+
fn trans_binary(&@block_ctxt cx, &dest dest, &span sp, ast::binop op,
291313
&@ast::expr lhs, &@ast::expr rhs) -> @block_ctxt {
292314
auto bcx = cx;
293-
auto r = spill_alias(bcx, in_dest, ty::expr_ty(bcx_tcx(bcx), lhs));
294-
bcx = r._0; auto dest = r._1;
295-
bcx = trans_expr(bcx, dest, lhs);
296-
297-
r = mk_temp(bcx, ty::expr_ty(bcx_tcx(bcx), rhs));
298-
bcx = r._0; auto rhs_tmp = r._1;
299-
bcx = trans_expr(bcx, rhs_tmp, rhs);
300-
301-
ret bcx; // TODO
315+
alt (op) {
316+
ast::add {
317+
bcx = trans_vec::trans_concat(bcx, dest, sp,
318+
ty::expr_ty(bcx_tcx(bcx), rhs), lhs,
319+
rhs);
320+
}
321+
// TODO: Many more to add here.
322+
}
323+
ret bcx;
302324
}
303325

304326
fn trans_log(&@block_ctxt cx, &span sp, int level, &@ast::expr expr)
@@ -408,7 +430,7 @@ fn trans_expr(&@block_ctxt bcx, &dest dest, &@ast::expr expr) -> @block_ctxt {
408430
ret trans_log(bcx, expr.span, level, operand);
409431
}
410432
ast::expr_binary(?op, ?lhs, ?rhs) {
411-
ret trans_binary(bcx, dest, op, lhs, rhs);
433+
ret trans_binary(bcx, dest, expr.span, op, lhs, rhs);
412434
}
413435
_ { fail "unhandled expr type in trans_expr"; }
414436
}

src/comp/middle/trans_vec.rs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// Translation of vector operations to LLVM IR, in destination-passing style.
2+
3+
import back::abi;
4+
import lib::llvm::llvm;
5+
import llvm::ValueRef;
6+
import middle::trans;
7+
import middle::trans_common;
8+
import middle::trans_dps;
9+
import middle::ty;
10+
import syntax::ast;
11+
import syntax::codemap::span;
12+
import trans::alloca;
13+
import trans::block_ctxt;
14+
import trans::load_inbounds;
15+
import trans::new_sub_block_ctxt;
16+
import trans::struct_elt;
17+
import trans::type_of_or_i8;
18+
import trans_common::C_int;
19+
import trans_common::C_null;
20+
import trans_common::C_uint;
21+
import trans_common::T_int;
22+
import trans_common::T_ivec_heap;
23+
import trans_common::T_ivec_heap_part;
24+
import trans_common::T_opaque_ivec;
25+
import trans_common::T_ptr;
26+
import trans_dps::bcx_ccx;
27+
import trans_dps::bcx_tcx;
28+
import trans_dps::dest;
29+
import trans_dps::llsize_of;
30+
import trans_dps::mk_temp;
31+
32+
import std::option::none;
33+
import std::option::some;
34+
import tc = middle::trans_common;
35+
36+
// Returns the length of an interior vector and a pointer to its first
37+
// element, in that order.
38+
//
39+
// TODO: We can optimize this in the cases in which we statically know the
40+
// vector must be on the stack.
41+
fn get_len_and_data(&@block_ctxt cx, ty::t t, ValueRef llvecptr)
42+
-> tup(@block_ctxt, ValueRef, ValueRef) {
43+
auto bcx = cx;
44+
45+
// If this interior vector has dynamic size, we can't assume anything
46+
// about the LLVM type of the value passed in, so we cast it to an
47+
// opaque vector type.
48+
auto unit_ty = ty::sequence_element_type(bcx_tcx(bcx), t);
49+
auto v;
50+
if (ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty)) {
51+
v = bcx.build.PointerCast(llvecptr, T_ptr(T_opaque_ivec()));
52+
} else {
53+
v = llvecptr;
54+
}
55+
56+
auto llunitty = type_of_or_i8(bcx, unit_ty);
57+
auto stack_len = load_inbounds(bcx, v, ~[C_int(0),
58+
C_uint(abi::ivec_elt_len)]);
59+
auto stack_elem =
60+
bcx.build.InBoundsGEP(v,
61+
~[C_int(0), C_uint(abi::ivec_elt_elems),
62+
C_int(0)]);
63+
auto on_heap =
64+
bcx.build.ICmp(lib::llvm::LLVMIntEQ, stack_len, C_int(0));
65+
auto on_heap_cx = new_sub_block_ctxt(bcx, "on_heap");
66+
auto next_cx = new_sub_block_ctxt(bcx, "next");
67+
bcx.build.CondBr(on_heap, on_heap_cx.llbb, next_cx.llbb);
68+
auto heap_stub =
69+
on_heap_cx.build.PointerCast(v, T_ptr(T_ivec_heap(llunitty)));
70+
auto heap_ptr = load_inbounds(on_heap_cx, heap_stub,
71+
~[C_int(0),
72+
C_uint(abi::ivec_heap_stub_elt_ptr)]);
73+
74+
// Check whether the heap pointer is null. If it is, the vector length
75+
// is truly zero.
76+
77+
auto llstubty = T_ivec_heap(llunitty);
78+
auto llheapptrty = struct_elt(llstubty, abi::ivec_heap_stub_elt_ptr);
79+
auto heap_ptr_is_null =
80+
on_heap_cx.build.ICmp(lib::llvm::LLVMIntEQ, heap_ptr,
81+
C_null(T_ptr(llheapptrty)));
82+
auto zero_len_cx = new_sub_block_ctxt(bcx, "zero_len");
83+
auto nonzero_len_cx = new_sub_block_ctxt(bcx, "nonzero_len");
84+
on_heap_cx.build.CondBr(heap_ptr_is_null, zero_len_cx.llbb,
85+
nonzero_len_cx.llbb);
86+
// Technically this context is unnecessary, but it makes this function
87+
// clearer.
88+
89+
auto zero_len = C_int(0);
90+
auto zero_elem = C_null(T_ptr(llunitty));
91+
zero_len_cx.build.Br(next_cx.llbb);
92+
// If we're here, then we actually have a heapified vector.
93+
94+
auto heap_len = load_inbounds(nonzero_len_cx, heap_ptr,
95+
~[C_int(0),
96+
C_uint(abi::ivec_heap_elt_len)]);
97+
auto heap_elem =
98+
{
99+
auto v = ~[C_int(0), C_uint(abi::ivec_heap_elt_elems),
100+
C_int(0)];
101+
nonzero_len_cx.build.InBoundsGEP(heap_ptr,v)
102+
};
103+
104+
nonzero_len_cx.build.Br(next_cx.llbb);
105+
106+
// Now we can figure out the length of |v| and get a pointer to its
107+
// first element.
108+
109+
auto len =
110+
next_cx.build.Phi(T_int(), ~[stack_len, zero_len, heap_len],
111+
~[bcx.llbb, zero_len_cx.llbb,
112+
nonzero_len_cx.llbb]);
113+
auto elem =
114+
next_cx.build.Phi(T_ptr(llunitty),
115+
~[stack_elem, zero_elem, heap_elem],
116+
~[bcx.llbb, zero_len_cx.llbb,
117+
nonzero_len_cx.llbb]);
118+
ret tup(next_cx, len, elem);
119+
}
120+
121+
fn trans_concat(&@block_ctxt cx, &dest in_dest, &span sp, ty::t t,
122+
&@ast::expr lhs, &@ast::expr rhs) -> @block_ctxt {
123+
auto bcx = cx;
124+
125+
// TODO: Skip null if copying strings.
126+
// TODO: Detect "a = a + b" and promote to trans_append.
127+
// TODO: Detect "a + [ literal ]" and optimize to copying the literal
128+
// elements in directly.
129+
130+
// Translate the LHS and RHS. Pull out their length and data.
131+
auto t = ty::expr_ty(bcx_tcx(bcx), lhs);
132+
auto lhs_tmp = trans_dps::dest_alias(bcx_tcx(bcx), t);
133+
bcx = trans_dps::trans_expr(bcx, lhs_tmp, lhs);
134+
auto lllhsptr = trans_dps::dest_ptr(lhs_tmp);
135+
136+
auto rhs_tmp = trans_dps::dest_alias(bcx_tcx(bcx), t);
137+
bcx = trans_dps::trans_expr(bcx, rhs_tmp, rhs);
138+
auto llrhsptr = trans_dps::dest_ptr(rhs_tmp);
139+
140+
auto r0 = get_len_and_data(bcx, t, lllhsptr);
141+
bcx = r0._0; auto lllhslen = r0._1; auto lllhsdata = r0._2;
142+
r0 = get_len_and_data(bcx, t, llrhsptr);
143+
bcx = r0._0; auto llrhslen = r0._1; auto llrhsdata = r0._2;
144+
145+
// Allocate the destination.
146+
auto r1 = trans_dps::spill_alias(bcx, in_dest, t);
147+
bcx = r1._0; auto dest = r1._1;
148+
149+
auto unit_t = ty::sequence_element_type(bcx_tcx(bcx), t);
150+
auto unit_sz = trans_dps::size_of(bcx_ccx(bcx), sp, unit_t);
151+
152+
auto stack_elems_sz = unit_sz * abi::ivec_default_length;
153+
auto lldestptr = trans_dps::dest_ptr(dest);
154+
auto llunitty = trans::type_of(bcx_ccx(bcx), sp, unit_t);
155+
156+
// Decide whether to allocate the result on the stack or on the heap.
157+
auto llnewlen = bcx.build.Add(lllhslen, llrhslen);
158+
auto llonstack = bcx.build.ICmp(lib::llvm::LLVMIntULE, llnewlen,
159+
C_uint(stack_elems_sz));
160+
auto on_stack_bcx = new_sub_block_ctxt(bcx, "on_stack");
161+
auto on_heap_bcx = new_sub_block_ctxt(bcx, "on_heap");
162+
bcx.build.CondBr(llonstack, on_stack_bcx.llbb, on_heap_bcx.llbb);
163+
164+
// On-stack case.
165+
auto next_bcx = new_sub_block_ctxt(bcx, "next");
166+
trans::store_inbounds(on_stack_bcx, llnewlen, lldestptr,
167+
~[C_int(0), C_uint(abi::ivec_elt_len)]);
168+
trans::store_inbounds(on_stack_bcx, C_uint(stack_elems_sz), lldestptr,
169+
~[C_int(0), C_uint(abi::ivec_elt_alen)]);
170+
auto llonstackdataptr =
171+
on_stack_bcx.build.InBoundsGEP(lldestptr,
172+
~[C_int(0),
173+
C_uint(abi::ivec_elt_elems),
174+
C_int(0)]);
175+
on_stack_bcx.build.Br(next_bcx.llbb);
176+
177+
// On-heap case.
178+
auto llheappartty = tc::T_ivec_heap(llunitty);
179+
auto lldeststubptr =
180+
on_heap_bcx.build.PointerCast(lldestptr, tc::T_ptr(llheappartty));
181+
trans::store_inbounds(on_heap_bcx, C_int(0), lldeststubptr,
182+
~[C_int(0), C_uint(abi::ivec_elt_len)]);
183+
trans::store_inbounds(on_heap_bcx, llnewlen, lldeststubptr,
184+
~[C_int(0), C_uint(abi::ivec_elt_alen)]);
185+
186+
auto llheappartptrptr =
187+
on_heap_bcx.build.InBoundsGEP(lldeststubptr,
188+
~[C_int(0),
189+
C_uint(abi::ivec_elt_elems)]);
190+
auto llsizeofint = C_uint(llsize_of(bcx_ccx(bcx), tc::T_int()));
191+
on_heap_bcx = trans_dps::malloc(on_heap_bcx, llheappartptrptr,
192+
trans_dps::hp_shared,
193+
some(on_heap_bcx.build.Add(llnewlen,
194+
llsizeofint)));
195+
auto llheappartptr = on_heap_bcx.build.Load(llheappartptrptr);
196+
trans::store_inbounds(on_heap_bcx, llnewlen, llheappartptr,
197+
~[C_int(0), C_uint(abi::ivec_heap_elt_len)]);
198+
auto llheapdataptr =
199+
on_heap_bcx.build.InBoundsGEP(llheappartptr,
200+
~[C_int(0),
201+
C_uint(abi::ivec_heap_elt_elems),
202+
C_int(0)]);
203+
on_heap_bcx.build.Br(next_bcx.llbb);
204+
205+
// Perform the memmove.
206+
auto lldataptr =
207+
next_bcx.build.Phi(T_ptr(llunitty),
208+
~[llonstackdataptr, llheapdataptr],
209+
~[on_stack_bcx.llbb, on_heap_bcx.llbb]);
210+
trans_dps::memmove(next_bcx, lldataptr, lllhsdata, lllhslen);
211+
trans_dps::memmove(next_bcx,
212+
next_bcx.build.InBoundsGEP(lldataptr, ~[lllhslen]),
213+
llrhsdata, llrhslen);
214+
215+
ret next_bcx;
216+
}
217+

src/comp/rustc.rc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod middle {
2020
mod trans_alt;
2121
mod trans_comm;
2222
mod trans_dps;
23+
mod trans_vec;
2324
mod ty;
2425
mod ast_map;
2526
mod resolve;

0 commit comments

Comments
 (0)