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.
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.
11 //! # Representation of Algebraic Data Types
13 //! This module determines how to represent enums, structs, and tuples
14 //! based on their monomorphized types; it is responsible both for
15 //! choosing a representation and translating basic operations on
16 //! values of those types. (Note: exporting the representations for
17 //! debuggers is handled in debuginfo.rs, not here.)
19 //! Note that the interface treats everything as a general case of an
20 //! enum, so structs/tuples/etc. have one pseudo-variant with
21 //! discriminant 0; i.e., as if they were a univariant enum.
23 //! Having everything in one place will enable improvements to data
24 //! structure representation; possibilities include:
26 //! - User-specified alignment (e.g., cacheline-aligning parts of
27 //! concurrently accessed data structures); LLVM can't represent this
28 //! directly, so we'd have to insert padding fields in any structure
29 //! that might contain one and adjust GEP indices accordingly. See
32 //! - Store nested enums' discriminants in the same word. Rather, if
33 //! some variants start with enums, and those enums representations
34 //! have unused alignment padding between discriminant and body, the
35 //! outer enum's discriminant can be stored there and those variants
36 //! can start at offset 0. Kind of fancy, and might need work to
37 //! make copies of the inner enum type cooperate, but it could help
38 //! with `Option` or `Result` wrapped around another enum.
40 //! - Tagged pointers would be neat, but given that any type can be
41 //! used unboxed and any field can have pointers (including mutable)
42 //! taken to it, implementing them for Rust seems difficult.
46 use llvm::{ValueRef, True, IntEQ, IntNE};
47 use rustc::ty::{self, Ty};
48 use rustc::ty::layout::{self, LayoutTyper};
57 use mir::lvalue::Alignment;
59 /// Given an enum, struct, closure, or tuple, extracts fields.
60 /// Treats closures as a struct with one variant.
61 /// `empty_if_no_variants` is a switch to deal with empty enums.
62 /// If true, `variant_index` is disregarded and an empty Vec returned in this case.
63 pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
65 empty_if_no_variants: bool) -> Vec<Ty<'tcx>> {
67 ty::TyAdt(ref def, _) if def.variants.len() == 0 && empty_if_no_variants => {
70 ty::TyAdt(ref def, ref substs) => {
71 def.variants[variant_index].fields.iter().map(|f| {
72 monomorphize::field_ty(cx.tcx(), substs, f)
73 }).collect::<Vec<_>>()
75 ty::TyTuple(fields, _) => fields.to_vec(),
76 ty::TyClosure(def_id, substs) => {
77 if variant_index > 0 { bug!("{} is a closure, which only has one variant", t);}
78 substs.upvar_tys(def_id, cx.tcx()).collect()
80 _ => bug!("{} is not a type that can have fields.", t)
84 /// LLVM-level types are a little complicated.
86 /// C-like enums need to be actual ints, not wrapped in a struct,
87 /// because that changes the ABI on some platforms (see issue #10308).
89 /// For nominal types, in some cases, we need to use LLVM named structs
90 /// and fill in the actual contents in a second pass to prevent
91 /// unbounded recursion; see also the comments in `trans::type_of`.
92 pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
93 generic_type_of(cx, t, None, false, false)
96 pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
97 t: Ty<'tcx>, name: &str) -> Type {
98 generic_type_of(cx, t, Some(name), false, false)
101 pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
102 t: Ty<'tcx>, llty: &mut Type) {
103 let l = cx.layout_of(t);
104 debug!("finish_type_of: {} with layout {:#?}", t, l);
106 layout::CEnum { .. } | layout::General { .. }
107 | layout::UntaggedUnion { .. } | layout::RawNullablePointer { .. } => { }
108 layout::Univariant { ..}
109 | layout::StructWrappedNullablePointer { .. } => {
110 let (nonnull_variant_index, nonnull_variant, packed) = match *l {
111 layout::Univariant { ref variant, .. } => (0, variant, variant.packed),
112 layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } =>
113 (nndiscr, nonnull, nonnull.packed),
116 let fields = compute_fields(cx, t, nonnull_variant_index as usize, true);
117 llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant, false, false),
120 _ => bug!("This function cannot handle {} with layout {:#?}", t, l)
124 fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
129 let l = cx.layout_of(t);
130 debug!("adt::generic_type_of t: {:?} name: {:?} sizing: {} dst: {}",
131 t, name, sizing, dst);
133 layout::CEnum { discr, .. } => Type::from_integer(cx, discr),
134 layout::RawNullablePointer { nndiscr, .. } => {
135 let (def, substs) = match t.sty {
136 ty::TyAdt(d, s) => (d, s),
137 _ => bug!("{} is not an ADT", t)
139 let nnty = monomorphize::field_ty(cx.tcx(), substs,
140 &def.variants[nndiscr as usize].fields[0]);
141 if let layout::Scalar { value: layout::Pointer, .. } = *cx.layout_of(nnty) {
144 type_of::type_of(cx, nnty)
147 layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
148 let fields = compute_fields(cx, t, nndiscr as usize, false);
151 Type::struct_(cx, &struct_llfields(cx, &fields, nonnull, sizing, dst),
155 assert_eq!(sizing, false);
156 Type::named_struct(cx, name)
160 layout::Univariant { ref variant, .. } => {
161 // Note that this case also handles empty enums.
162 // Thus the true as the final parameter here.
163 let fields = compute_fields(cx, t, 0, true);
166 let fields = struct_llfields(cx, &fields, &variant, sizing, dst);
167 Type::struct_(cx, &fields, variant.packed)
170 // Hypothesis: named_struct's can never need a
171 // drop flag. (... needs validation.)
172 assert_eq!(sizing, false);
173 Type::named_struct(cx, name)
177 layout::UntaggedUnion { ref variants, .. }=> {
178 // Use alignment-sized ints to fill all the union storage.
179 let size = variants.stride().bytes();
180 let align = variants.align.abi();
181 let fill = union_fill(cx, size, align);
184 Type::struct_(cx, &[fill], variants.packed)
187 let mut llty = Type::named_struct(cx, name);
188 llty.set_struct_body(&[fill], variants.packed);
193 layout::General { discr, size, align, .. } => {
194 // We need a representation that has:
195 // * The alignment of the most-aligned field
196 // * The size of the largest variant (rounded up to that alignment)
197 // * No alignment padding anywhere any variant has actual data
198 // (currently matters only for enums small enough to be immediate)
199 // * The discriminant in an obvious place.
201 // So we start with the discriminant, pad it up to the alignment with
202 // more of its own type, then use alignment-sized ints to get the rest
204 let size = size.bytes();
205 let align = align.abi();
206 assert!(align <= std::u32::MAX as u64);
207 let discr_ty = Type::from_integer(cx, discr);
208 let discr_size = discr.size().bytes();
209 let padded_discr_size = roundup(discr_size, align as u32);
210 let variant_part_size = size-padded_discr_size;
211 let variant_fill = union_fill(cx, variant_part_size, align);
213 assert_eq!(machine::llalign_of_min(cx, variant_fill), align as u32);
214 assert_eq!(padded_discr_size % discr_size, 0); // Ensure discr_ty can fill pad evenly
215 let fields: Vec<Type> =
217 Type::array(&discr_ty, (padded_discr_size - discr_size)/discr_size),
218 variant_fill].iter().cloned().collect();
221 Type::struct_(cx, &fields, false)
224 let mut llty = Type::named_struct(cx, name);
225 llty.set_struct_body(&fields, false);
230 _ => bug!("Unsupported type {} represented as {:#?}", t, l)
234 fn union_fill(cx: &CrateContext, size: u64, align: u64) -> Type {
235 assert_eq!(size%align, 0);
236 assert_eq!(align.count_ones(), 1, "Alignment must be a power fof 2. Got {}", align);
237 let align_units = size/align;
238 let layout_align = layout::Align::from_bytes(align, align).unwrap();
239 if let Some(ity) = layout::Integer::for_abi_align(cx, layout_align) {
240 Type::array(&Type::from_integer(cx, ity), align_units)
242 Type::array(&Type::vector(&Type::i32(cx), align/4),
248 fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fields: &Vec<Ty<'tcx>>,
249 variant: &layout::Struct,
250 sizing: bool, _dst: bool) -> Vec<Type> {
251 let fields = variant.field_index_by_increasing_offset().map(|i| fields[i as usize]);
255 fields.map(|ty| type_of::in_memory_type_of(cx, ty)).collect()
259 pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool {
261 layout::CEnum { signed, .. }=> signed,
266 /// Obtain the actual discriminant of a value.
267 pub fn trans_get_discr<'a, 'tcx>(
268 bcx: &Builder<'a, 'tcx>,
271 alignment: Alignment,
272 cast_to: Option<Type>,
275 debug!("trans_get_discr t: {:?}", t);
276 let l = bcx.ccx.layout_of(t);
279 layout::CEnum { discr, min, max, .. } => {
280 load_discr(bcx, discr, scrutinee, alignment, min, max, range_assert)
282 layout::General { discr, ref variants, .. } => {
283 let ptr = bcx.struct_gep(scrutinee, 0);
284 load_discr(bcx, discr, ptr, alignment,
285 0, variants.len() as u64 - 1,
288 layout::Univariant { .. } | layout::UntaggedUnion { .. } => C_u8(bcx.ccx, 0),
289 layout::RawNullablePointer { nndiscr, .. } => {
290 let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
291 let discr = bcx.load(scrutinee, alignment.to_align());
292 bcx.icmp(cmp, discr, C_null(val_ty(discr)))
294 layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
295 struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee, alignment)
297 _ => bug!("{} is not an enum", t)
301 Some(llty) => bcx.intcast(val, llty, is_discr_signed(&l))
305 fn struct_wrapped_nullable_bitdiscr(
308 discrfield: &layout::FieldPath,
310 alignment: Alignment,
312 let llptrptr = bcx.gepi(scrutinee,
313 &discrfield.iter().map(|f| *f as usize).collect::<Vec<_>>());
314 let llptr = bcx.load(llptrptr, alignment.to_align());
315 let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
316 bcx.icmp(cmp, llptr, C_null(val_ty(llptr)))
319 /// Helper for cases where the discriminant is simply loaded.
320 fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef,
321 alignment: Alignment, min: u64, max: u64,
324 let llty = Type::from_integer(bcx.ccx, ity);
325 assert_eq!(val_ty(ptr), llty.ptr_to());
326 let bits = ity.size().bits();
328 let bits = bits as usize;
329 let mask = !0u64 >> (64 - bits);
330 // For a (max) discr of -1, max will be `-1 as usize`, which overflows.
331 // However, that is fine here (it would still represent the full range),
332 if max.wrapping_add(1) & mask == min & mask || !range_assert {
333 // i.e., if the range is everything. The lo==hi case would be
334 // rejected by the LLVM verifier (it would mean either an
335 // empty set, which is impossible, or the entire range of the
336 // type, which is pointless).
337 bcx.load(ptr, alignment.to_align())
339 // llvm::ConstantRange can deal with ranges that wrap around,
340 // so an overflow on (max + 1) is fine.
341 bcx.load_range_assert(ptr, min, max.wrapping_add(1), /* signed: */ True,
342 alignment.to_align())
346 /// Set the discriminant for a new value of the given case of the given
348 pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: u64) {
349 let l = bcx.ccx.layout_of(t);
351 layout::CEnum{ discr, min, max, .. } => {
352 assert_discr_in_range(min, max, to);
353 bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to, true),
356 layout::General{ discr, .. } => {
357 bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to, true),
358 bcx.struct_gep(val, 0), None);
360 layout::Univariant { .. }
361 | layout::UntaggedUnion { .. }
362 | layout::Vector { .. } => {
365 layout::RawNullablePointer { nndiscr, .. } => {
367 let llptrty = val_ty(val).element_type();
368 bcx.store(C_null(llptrty), val, None);
371 layout::StructWrappedNullablePointer { nndiscr, ref discrfield, ref nonnull, .. } => {
373 if target_sets_discr_via_memset(bcx) {
374 // Issue #34427: As workaround for LLVM bug on
375 // ARM, use memset of 0 on whole struct rather
376 // than storing null to single target field.
377 let llptr = bcx.pointercast(val, Type::i8(bcx.ccx).ptr_to());
378 let fill_byte = C_u8(bcx.ccx, 0);
379 let size = C_uint(bcx.ccx, nonnull.stride().bytes());
380 let align = C_i32(bcx.ccx, nonnull.align.abi() as i32);
381 base::call_memset(bcx, llptr, fill_byte, size, align, false);
383 let path = discrfield.iter().map(|&i| i as usize).collect::<Vec<_>>();
384 let llptrptr = bcx.gepi(val, &path);
385 let llptrty = val_ty(llptrptr).element_type();
386 bcx.store(C_null(llptrty), llptrptr, None);
390 _ => bug!("Cannot handle {} represented as {:#?}", t, l)
394 fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &Builder<'a, 'tcx>) -> bool {
395 bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64"
398 pub fn assert_discr_in_range<D: PartialOrd>(min: D, max: D, discr: D) {
400 assert!(min <= discr && discr <= max)
402 assert!(min <= discr || discr <= max)
406 // FIXME this utility routine should be somewhere more general
408 fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a }
410 /// Extract a field of a constant value, as appropriate for its
413 /// (Not to be confused with `common::const_get_elt`, which operates on
414 /// raw LLVM-level structs and arrays.)
415 pub fn const_get_field<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
417 ix: usize) -> ValueRef {
418 let l = ccx.layout_of(t);
420 layout::CEnum { .. } => bug!("element access in C-like enum const"),
421 layout::Univariant { ref variant, .. } => {
422 const_struct_field(val, variant.memory_index[ix] as usize)
424 layout::Vector { .. } => const_struct_field(val, ix),
425 layout::UntaggedUnion { .. } => const_struct_field(val, 0),
426 _ => bug!("{} does not have fields.", t)
430 /// Extract field of struct-like const, skipping our alignment padding.
431 fn const_struct_field(val: ValueRef, ix: usize) -> ValueRef {
432 // Get the ix-th non-undef element of the struct.
433 let mut real_ix = 0; // actual position in the struct
434 let mut ix = ix; // logical index relative to real_ix
438 field = const_get_elt(val, &[real_ix]);
439 if !is_undef(field) {
442 real_ix = real_ix + 1;
448 real_ix = real_ix + 1;