|
| 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 | + |
0 commit comments