|
| 1 | +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT |
| 2 | +// file at the top-level directory of this distribution and at |
| 3 | +// http://rust-lang.org/COPYRIGHT. |
| 4 | +// |
| 5 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 7 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 8 | +// option. This file may not be copied, modified, or distributed |
| 9 | +// except according to those terms. |
| 10 | + |
| 11 | +use core::option::{Option, Some, None}; |
| 12 | +use core::vec; |
| 13 | +use lib::llvm::{ValueRef, TypeRef}; |
| 14 | +use middle::trans::_match; |
| 15 | +use middle::trans::build::*; |
| 16 | +use middle::trans::common::*; |
| 17 | +use middle::trans::machine; |
| 18 | +use middle::trans::type_of; |
| 19 | +use middle::ty; |
| 20 | +use syntax::ast; |
| 21 | +use util::ppaux::ty_to_str; |
| 22 | + |
| 23 | + |
| 24 | +// XXX: should this be done with boxed traits instead of ML-style? |
| 25 | +pub enum Repr { |
| 26 | + CEnum, |
| 27 | + Univariant(Struct, Destructor), |
| 28 | + General(~[Struct]) |
| 29 | +} |
| 30 | + |
| 31 | +enum Destructor { |
| 32 | + DtorPresent, |
| 33 | + DtorAbsent, |
| 34 | + NoDtor |
| 35 | +} |
| 36 | + |
| 37 | +struct Struct { |
| 38 | + size: u64, |
| 39 | + align: u64, |
| 40 | + fields: ~[ty::t] |
| 41 | +} |
| 42 | + |
| 43 | + |
| 44 | +pub fn represent_node(bcx: block, node: ast::node_id) |
| 45 | + -> Repr { |
| 46 | + represent_type(bcx.ccx(), node_id_type(bcx, node)) |
| 47 | +} |
| 48 | + |
| 49 | +pub fn represent_type(cx: @CrateContext, t: ty::t) -> Repr { |
| 50 | + debug!("Representing: %s", ty_to_str(cx.tcx, t)); |
| 51 | + // XXX: cache this |
| 52 | + match ty::get(t).sty { |
| 53 | + ty::ty_tup(ref elems) => { |
| 54 | + Univariant(mk_struct(cx, *elems), NoDtor) |
| 55 | + } |
| 56 | + ty::ty_rec(ref fields) => { |
| 57 | + // XXX: Are these in the right order? |
| 58 | + Univariant(mk_struct(cx, fields.map(|f| f.mt.ty)), DtorAbsent) |
| 59 | + } |
| 60 | + ty::ty_struct(def_id, ref substs) => { |
| 61 | + let fields = ty::lookup_struct_fields(cx.tcx, def_id); |
| 62 | + let dt = ty::ty_dtor(cx.tcx, def_id).is_present(); |
| 63 | + Univariant(mk_struct(cx, fields.map(|field| { |
| 64 | + ty::lookup_field_type(cx.tcx, def_id, field.id, substs) |
| 65 | + })), if dt { DtorPresent } else { DtorAbsent }) |
| 66 | + } |
| 67 | + ty::ty_enum(def_id, ref substs) => { |
| 68 | + struct Case { discr: i64, tys: ~[ty::t] }; |
| 69 | + |
| 70 | + let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| { |
| 71 | + let arg_tys = do vi.args.map |&raw_ty| { |
| 72 | + ty::subst(cx.tcx, substs, raw_ty) |
| 73 | + }; |
| 74 | + Case { discr: vi.disr_val /*bad*/as i64, tys: arg_tys } |
| 75 | + }; |
| 76 | + if cases.len() == 0 { |
| 77 | + // Uninhabitable; represent as unit |
| 78 | + Univariant(mk_struct(cx, ~[]), NoDtor) |
| 79 | + } else if cases.len() == 1 && cases[0].discr == 0 { |
| 80 | + // struct, tuple, newtype, etc. |
| 81 | + Univariant(mk_struct(cx, cases[0].tys), NoDtor) |
| 82 | + } else if cases.all(|c| c.tys.len() == 0) { |
| 83 | + CEnum |
| 84 | + } else { |
| 85 | + if !cases.alli(|i,c| c.discr == (i as i64)) { |
| 86 | + cx.sess.bug(fmt!("non-C-like enum %s with specified \ |
| 87 | + discriminants", |
| 88 | + ty::item_path_str(cx.tcx, def_id))) |
| 89 | + } |
| 90 | + General(cases.map(|c| mk_struct(cx, c.tys))) |
| 91 | + } |
| 92 | + } |
| 93 | + _ => cx.sess.bug(~"adt::represent_type called on non-ADT type") |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +fn mk_struct(cx: @CrateContext, tys: &[ty::t]) -> Struct { |
| 98 | + let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty)); |
| 99 | + let llty_rec = T_struct(lltys); |
| 100 | + Struct { |
| 101 | + size: machine::llsize_of_alloc(cx, llty_rec) /*bad*/as u64, |
| 102 | + align: machine::llalign_of_min(cx, llty_rec) /*bad*/as u64, |
| 103 | + fields: vec::from_slice(tys) |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | + |
| 108 | +pub fn sizing_fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] { |
| 109 | + generic_fields_of(cx, r, true) |
| 110 | +} |
| 111 | +pub fn fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] { |
| 112 | + generic_fields_of(cx, r, false) |
| 113 | +} |
| 114 | +fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool) |
| 115 | + -> ~[TypeRef] { |
| 116 | + match *r { |
| 117 | + CEnum => ~[T_enum_discrim(cx)], |
| 118 | + Univariant(ref st, dt) => { |
| 119 | + let f = if sizing { |
| 120 | + st.fields.map(|&ty| type_of::sizing_type_of(cx, ty)) |
| 121 | + } else { |
| 122 | + st.fields.map(|&ty| type_of::type_of(cx, ty)) |
| 123 | + }; |
| 124 | + match dt { |
| 125 | + NoDtor => f, |
| 126 | + DtorAbsent => ~[T_struct(f)], |
| 127 | + DtorPresent => ~[T_struct(f), T_i8()] |
| 128 | + } |
| 129 | + } |
| 130 | + General(ref sts) => { |
| 131 | + ~[T_enum_discrim(cx), |
| 132 | + T_array(T_i8(), sts.map(|st| st.size).max() /*bad*/as uint)] |
| 133 | + } |
| 134 | + } |
| 135 | +} |
| 136 | + |
| 137 | +pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef) -> |
| 138 | + (_match::branch_kind, Option<ValueRef>) { |
| 139 | + // XXX: LoadRangeAssert |
| 140 | + match *r { |
| 141 | + CEnum => { |
| 142 | + (_match::switch, Some(Load(bcx, GEPi(bcx, scrutinee, [0, 0])))) |
| 143 | + } |
| 144 | + Univariant(*) => { |
| 145 | + (_match::single, None) |
| 146 | + } |
| 147 | + General(*) => { |
| 148 | + (_match::switch, Some(Load(bcx, GEPi(bcx, scrutinee, [0, 0])))) |
| 149 | + } |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result { |
| 154 | + match *r { |
| 155 | + CEnum => { |
| 156 | + _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr))) |
| 157 | + } |
| 158 | + Univariant(*) => { |
| 159 | + bcx.ccx().sess.bug(~"no cases for univariants or structs") |
| 160 | + } |
| 161 | + General(*) => { |
| 162 | + _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr))) |
| 163 | + } |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +pub fn trans_set_discr(bcx: block, r: &Repr, val: ValueRef, discr: int) { |
| 168 | + match *r { |
| 169 | + CEnum => { |
| 170 | + Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0])) |
| 171 | + } |
| 172 | + Univariant(_, DtorPresent) => { |
| 173 | + assert discr == 0; |
| 174 | + Store(bcx, C_u8(1), GEPi(bcx, val, [0, 1])) |
| 175 | + } |
| 176 | + Univariant(*) => { |
| 177 | + assert discr == 0; |
| 178 | + } |
| 179 | + General(*) => { |
| 180 | + Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0])) |
| 181 | + } |
| 182 | + } |
| 183 | +} |
| 184 | + |
| 185 | +pub fn num_args(r: &Repr, discr: int) -> uint { |
| 186 | + match *r { |
| 187 | + CEnum => 0, |
| 188 | + Univariant(ref st, _dt) => { assert discr == 0; st.fields.len() } |
| 189 | + General(ref cases) => cases[discr as uint].fields.len() |
| 190 | + } |
| 191 | +} |
| 192 | + |
| 193 | +pub fn trans_GEP(bcx: block, r: &Repr, val: ValueRef, discr: int, ix: uint) |
| 194 | + -> ValueRef { |
| 195 | + // Note: if this ever needs to generate conditionals (e.g., if we |
| 196 | + // decide to do some kind of cdr-coding-like non-unique repr |
| 197 | + // someday), it'll need to return a possibly-new bcx as well. |
| 198 | + match *r { |
| 199 | + CEnum => { |
| 200 | + bcx.ccx().sess.bug(~"element access in C-like enum") |
| 201 | + } |
| 202 | + Univariant(ref st, dt) => { |
| 203 | + assert discr == 0; |
| 204 | + let val = match dt { |
| 205 | + NoDtor => val, |
| 206 | + DtorPresent | DtorAbsent => GEPi(bcx, val, [0, 0]) |
| 207 | + }; |
| 208 | + struct_GEP(bcx, st, val, ix) |
| 209 | + } |
| 210 | + General(ref cases) => { |
| 211 | + struct_GEP(bcx, &cases[discr as uint], |
| 212 | + GEPi(bcx, val, [0, 1]), ix) |
| 213 | + } |
| 214 | + } |
| 215 | +} |
| 216 | + |
| 217 | +fn struct_GEP(bcx: block, st: &Struct, val: ValueRef, ix: uint) |
| 218 | + -> ValueRef { |
| 219 | + let ccx = bcx.ccx(); |
| 220 | + |
| 221 | + let real_llty = T_struct(st.fields.map( |
| 222 | + |&ty| type_of::type_of(ccx, ty))); |
| 223 | + let cast_val = PointerCast(bcx, val, T_ptr(real_llty)); |
| 224 | + |
| 225 | + GEPi(bcx, cast_val, [0, ix]) |
| 226 | +} |
| 227 | + |
| 228 | +pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int, |
| 229 | + vals: &[ValueRef]) -> ValueRef { |
| 230 | + match *r { |
| 231 | + CEnum => { |
| 232 | + assert vals.len() == 0; |
| 233 | + C_int(ccx, discr) |
| 234 | + } |
| 235 | + Univariant(ref st, _dt) => { |
| 236 | + assert discr == 0; |
| 237 | + // consts are never destroyed, so the dtor flag is not needed |
| 238 | + C_struct(build_const_struct(ccx, st, vals)) |
| 239 | + } |
| 240 | + General(ref cases) => { |
| 241 | + let case = &cases[discr as uint]; |
| 242 | + let max_sz = cases.map(|s| s.size).max(); |
| 243 | + let body = build_const_struct(ccx, case, vals); |
| 244 | + |
| 245 | + C_struct([C_int(ccx, discr), |
| 246 | + C_packed_struct([C_struct(body)]), |
| 247 | + padding(max_sz - case.size)]) |
| 248 | + } |
| 249 | + } |
| 250 | +} |
| 251 | + |
| 252 | +fn padding(size: u64) -> ValueRef { |
| 253 | + C_undef(T_array(T_i8(), size /*bad*/as uint)) |
| 254 | +} |
| 255 | + |
| 256 | +fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef]) |
| 257 | + -> ~[ValueRef] { |
| 258 | + assert vals.len() == st.fields.len(); |
| 259 | + |
| 260 | + let mut offset = 0; |
| 261 | + let mut cfields = ~[]; |
| 262 | + for st.fields.eachi |i, &ty| { |
| 263 | + let llty = type_of::sizing_type_of(ccx, ty); |
| 264 | + let type_align = machine::llalign_of_min(ccx, llty) |
| 265 | + /*bad*/as u64; |
| 266 | + let val_align = machine::llalign_of_min(ccx, val_ty(vals[i])) |
| 267 | + /*bad*/as u64; |
| 268 | + let target_offset = roundup(offset, type_align); |
| 269 | + offset = roundup(offset, val_align); |
| 270 | + if (offset != target_offset) { |
| 271 | + cfields.push(padding(target_offset - offset)); |
| 272 | + offset = target_offset; |
| 273 | + } |
| 274 | + assert !is_undef(vals[i]); |
| 275 | + // If that assert fails, could change it to wrap in a struct? |
| 276 | + cfields.push(vals[i]); |
| 277 | + } |
| 278 | + |
| 279 | + return cfields; |
| 280 | +} |
| 281 | + |
| 282 | +#[always_inline] |
| 283 | +fn roundup(x: u64, a: u64) -> u64 { ((x + (a - 1)) / a) * a } |
| 284 | + |
| 285 | + |
| 286 | +pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef) |
| 287 | + -> int { |
| 288 | + match *r { |
| 289 | + CEnum(*) => const_to_int(val) as int, |
| 290 | + Univariant(*) => 0, |
| 291 | + General(*) => const_to_int(const_get_elt(ccx, val, [0])) as int, |
| 292 | + } |
| 293 | +} |
| 294 | + |
| 295 | +pub fn const_get_element(ccx: @CrateContext, r: &Repr, val: ValueRef, |
| 296 | + _discr: int, ix: uint) -> ValueRef { |
| 297 | + // Not to be confused with common::const_get_elt. |
| 298 | + match *r { |
| 299 | + CEnum(*) => ccx.sess.bug(~"element access in C-like enum const"), |
| 300 | + Univariant(*) => const_struct_field(ccx, val, ix), |
| 301 | + General(*) => const_struct_field(ccx, const_get_elt(ccx, val, |
| 302 | + [1, 0]), ix) |
| 303 | + } |
| 304 | +} |
| 305 | + |
| 306 | +fn const_struct_field(ccx: @CrateContext, val: ValueRef, ix: uint) |
| 307 | + -> ValueRef { |
| 308 | + // Get the ix-th non-undef element of the struct. |
| 309 | + let mut real_ix = 0; // actual position in the struct |
| 310 | + let mut ix = ix; // logical index relative to real_ix |
| 311 | + let mut field; |
| 312 | + loop { |
| 313 | + loop { |
| 314 | + field = const_get_elt(ccx, val, [real_ix]); |
| 315 | + if !is_undef(field) { |
| 316 | + break; |
| 317 | + } |
| 318 | + real_ix = real_ix + 1; |
| 319 | + } |
| 320 | + if ix == 0 { |
| 321 | + return field; |
| 322 | + } |
| 323 | + ix = ix - 1; |
| 324 | + real_ix = real_ix + 1; |
| 325 | + } |
| 326 | +} |
0 commit comments