Skip to content

Commit

Permalink
Move trans_const to mir::constant
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark-Simulacrum committed Jan 4, 2017
1 parent ea0ebe4 commit 982b8f4
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 161 deletions.
154 changes: 5 additions & 149 deletions src/librustc_trans/adt.rs
Expand Up @@ -70,9 +70,9 @@ pub enum BranchKind {
/// Treats closures as a struct with one variant.
/// `empty_if_no_variants` is a switch to deal with empty enums.
/// If true, `variant_index` is disregarded and an empty Vec returned in this case.
fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
variant_index: usize,
empty_if_no_variants: bool) -> Vec<Ty<'tcx>> {
pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
variant_index: usize,
empty_if_no_variants: bool) -> Vec<Ty<'tcx>> {
match t.sty {
ty::TyAdt(ref def, _) if def.variants.len() == 0 && empty_if_no_variants => {
Vec::default()
Expand Down Expand Up @@ -412,9 +412,7 @@ pub fn trans_case<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -

/// Set the discriminant for a new value of the given case of the given
/// representation.
pub fn trans_set_discr<'a, 'tcx>(
bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr
) {
pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr) {
let l = bcx.ccx.layout_of(t);
match *l {
layout::CEnum{ discr, min, max, .. } => {
Expand Down Expand Up @@ -465,7 +463,7 @@ fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &Builder<'a, 'tcx>) -> bool {
bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64"
}

fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) {
pub fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) {
if min <= max {
assert!(min <= discr && discr <= max)
} else {
Expand Down Expand Up @@ -630,148 +628,6 @@ fn struct_field_ptr<'a, 'tcx>(
bcx.pointercast(byte_ptr, ll_fty.ptr_to())
}

/// Construct a constant value, suitable for initializing a
/// GlobalVariable, given a case and constant values for its fields.
/// Note that this may have a different LLVM type (and different
/// alignment!) from the representation's `type_of`, so it needs a
/// pointer cast before use.
///
/// The LLVM type system does not directly support unions, and only
/// pointers can be bitcast, so a constant (and, by extension, the
/// GlobalVariable initialized by it) will have a type that can vary
/// depending on which case of an enum it is.
///
/// To understand the alignment situation, consider `enum E { V64(u64),
/// V32(u32, u32) }` on Windows. The type has 8-byte alignment to
/// accommodate the u64, but `V32(x, y)` would have LLVM type `{i32,
/// i32, i32}`, which is 4-byte aligned.
///
/// Currently the returned value has the same size as the type, but
/// this could be changed in the future to avoid allocating unnecessary
/// space after values of shorter-than-maximum cases.
pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: Disr,
vals: &[ValueRef]) -> ValueRef {
let l = ccx.layout_of(t);
let dl = &ccx.tcx().data_layout;
match *l {
layout::CEnum { discr: d, min, max, .. } => {
assert_eq!(vals.len(), 0);
assert_discr_in_range(Disr(min), Disr(max), discr);
C_integral(Type::from_integer(ccx, d), discr.0, true)
}
layout::General { discr: d, ref variants, .. } => {
let variant = &variants[discr.0 as usize];
let lldiscr = C_integral(Type::from_integer(ccx, d), discr.0 as u64, true);
let mut vals_with_discr = vec![lldiscr];
vals_with_discr.extend_from_slice(vals);
let mut contents = build_const_struct(ccx, &variant, &vals_with_discr[..]);
let needed_padding = l.size(dl).bytes() - variant.stride().bytes();
if needed_padding > 0 {
contents.push(padding(ccx, needed_padding));
}
C_struct(ccx, &contents[..], false)
}
layout::UntaggedUnion { ref variants, .. }=> {
assert_eq!(discr, Disr(0));
let contents = build_const_union(ccx, variants, vals[0]);
C_struct(ccx, &contents, variants.packed)
}
layout::Univariant { ref variant, .. } => {
assert_eq!(discr, Disr(0));
let contents = build_const_struct(ccx, &variant, vals);
C_struct(ccx, &contents[..], variant.packed)
}
layout::Vector { .. } => {
C_vector(vals)
}
layout::RawNullablePointer { nndiscr, .. } => {
let nnty = compute_fields(ccx, t, nndiscr as usize, false)[0];
if discr.0 == nndiscr {
assert_eq!(vals.len(), 1);
vals[0]
} else {
C_null(type_of::sizing_type_of(ccx, nnty))
}
}
layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
if discr.0 == nndiscr {
C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), false)
} else {
let fields = compute_fields(ccx, t, nndiscr as usize, false);
let vals = fields.iter().map(|&ty| {
// Always use null even if it's not the `discrfield`th
// field; see #8506.
C_null(type_of::sizing_type_of(ccx, ty))
}).collect::<Vec<ValueRef>>();
C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]), false)
}
}
_ => bug!("trans_const: cannot handle type {} repreented as {:#?}", t, l)
}
}

/// Building structs is a little complicated, because we might need to
/// insert padding if a field's value is less aligned than its type.
///
/// Continuing the example from `trans_const`, a value of type `(u32,
/// E)` should have the `E` at offset 8, but if that field's
/// initializer is 4-byte aligned then simply translating the tuple as
/// a two-element struct will locate it at offset 4, and accesses to it
/// will read the wrong memory.
fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
st: &layout::Struct,
vals: &[ValueRef])
-> Vec<ValueRef> {
assert_eq!(vals.len(), st.offsets.len());

if vals.len() == 0 {
return Vec::new();
}

// offset of current value
let mut offset = 0;
let mut cfields = Vec::new();
cfields.reserve(st.offsets.len()*2);

let parts = st.field_index_by_increasing_offset().map(|i| {
(&vals[i], st.offsets[i].bytes())
});
for (&val, target_offset) in parts {
if offset < target_offset {
cfields.push(padding(ccx, target_offset - offset));
offset = target_offset;
}
assert!(!is_undef(val));
cfields.push(val);
offset += machine::llsize_of_alloc(ccx, val_ty(val));
}

if offset < st.stride().bytes() {
cfields.push(padding(ccx, st.stride().bytes() - offset));
}

cfields
}

fn build_const_union<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
un: &layout::Union,
field_val: ValueRef)
-> Vec<ValueRef> {
let mut cfields = vec![field_val];

let offset = machine::llsize_of_alloc(ccx, val_ty(field_val));
let size = un.stride().bytes();
if offset != size {
cfields.push(padding(ccx, size - offset));
}

cfields
}

fn padding(ccx: &CrateContext, size: u64) -> ValueRef {
C_undef(Type::array(&Type::i8(ccx), size))
}

// FIXME this utility routine should be somewhere more general
#[inline]
fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a }
Expand Down
171 changes: 159 additions & 12 deletions src/librustc_trans/mir/constant.rs
Expand Up @@ -18,7 +18,7 @@ use rustc::hir::def_id::DefId;
use rustc::infer::TransNormalize;
use rustc::mir;
use rustc::mir::tcx::LvalueTy;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::{self, layout, Ty, TyCtxt, TypeFoldable};
use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::subst::Substs;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
Expand All @@ -27,7 +27,7 @@ use callee::Callee;
use builder::Builder;
use common::{self, CrateContext, const_get_elt, val_ty};
use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integral};
use common::{C_null, C_struct, C_str_slice, C_undef, C_uint};
use common::{C_null, C_struct, C_str_slice, C_undef, C_uint, C_vector, is_undef};
use common::const_to_opt_u128;
use consts;
use monomorphize::{self, Instance};
Expand Down Expand Up @@ -549,16 +549,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
mir::AggregateKind::Adt(..) |
mir::AggregateKind::Closure(..) |
mir::AggregateKind::Tuple => {
let disr = match *kind {
mir::AggregateKind::Adt(adt_def, index, _, _) => {
Disr::from(adt_def.variants[index].disr_val)
}
_ => Disr(0)
};
Const::new(
adt::trans_const(self.ccx, dest_ty, disr, &fields),
dest_ty
)
Const::new(trans_const(self.ccx, dest_ty, kind, &fields), dest_ty)
}
}
}
Expand Down Expand Up @@ -946,3 +937,159 @@ pub fn trans_static_initializer(ccx: &CrateContext, def_id: DefId)
let instance = Instance::mono(ccx.shared(), def_id);
MirConstContext::trans_def(ccx, instance, IndexVec::new()).map(|c| c.llval)
}

/// Construct a constant value, suitable for initializing a
/// GlobalVariable, given a case and constant values for its fields.
/// Note that this may have a different LLVM type (and different
/// alignment!) from the representation's `type_of`, so it needs a
/// pointer cast before use.
///
/// The LLVM type system does not directly support unions, and only
/// pointers can be bitcast, so a constant (and, by extension, the
/// GlobalVariable initialized by it) will have a type that can vary
/// depending on which case of an enum it is.
///
/// To understand the alignment situation, consider `enum E { V64(u64),
/// V32(u32, u32) }` on Windows. The type has 8-byte alignment to
/// accommodate the u64, but `V32(x, y)` would have LLVM type `{i32,
/// i32, i32}`, which is 4-byte aligned.
///
/// Currently the returned value has the same size as the type, but
/// this could be changed in the future to avoid allocating unnecessary
/// space after values of shorter-than-maximum cases.
fn trans_const<'a, 'tcx>(
ccx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>,
kind: &mir::AggregateKind,
vals: &[ValueRef]
) -> ValueRef {
let l = ccx.layout_of(t);
let dl = &ccx.tcx().data_layout;
let variant_index = match *kind {
mir::AggregateKind::Adt(_, index, _, _) => index,
_ => 0,
};
match *l {
layout::CEnum { discr: d, min, max, .. } => {
let discr = match *kind {
mir::AggregateKind::Adt(adt_def, _, _, _) => {
Disr::from(adt_def.variants[variant_index].disr_val)
},
_ => Disr(0),
};
assert_eq!(vals.len(), 0);
adt::assert_discr_in_range(Disr(min), Disr(max), discr);
C_integral(Type::from_integer(ccx, d), discr.0, true)
}
layout::General { discr: d, ref variants, .. } => {
let variant = &variants[variant_index];
let lldiscr = C_integral(Type::from_integer(ccx, d), variant_index as u64, true);
let mut vals_with_discr = vec![lldiscr];
vals_with_discr.extend_from_slice(vals);
let mut contents = build_const_struct(ccx, &variant, &vals_with_discr[..]);
let needed_padding = l.size(dl).bytes() - variant.stride().bytes();
if needed_padding > 0 {
contents.push(padding(ccx, needed_padding));
}
C_struct(ccx, &contents[..], false)
}
layout::UntaggedUnion { ref variants, .. }=> {
assert_eq!(variant_index, 0);
let contents = build_const_union(ccx, variants, vals[0]);
C_struct(ccx, &contents, variants.packed)
}
layout::Univariant { ref variant, .. } => {
assert_eq!(variant_index, 0);
let contents = build_const_struct(ccx, &variant, vals);
C_struct(ccx, &contents[..], variant.packed)
}
layout::Vector { .. } => {
C_vector(vals)
}
layout::RawNullablePointer { nndiscr, .. } => {
let nnty = adt::compute_fields(ccx, t, nndiscr as usize, false)[0];
if variant_index as u64 == nndiscr {
assert_eq!(vals.len(), 1);
vals[0]
} else {
C_null(type_of::sizing_type_of(ccx, nnty))
}
}
layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
if variant_index as u64 == nndiscr {
C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), false)
} else {
let fields = adt::compute_fields(ccx, t, nndiscr as usize, false);
let vals = fields.iter().map(|&ty| {
// Always use null even if it's not the `discrfield`th
// field; see #8506.
C_null(type_of::sizing_type_of(ccx, ty))
}).collect::<Vec<ValueRef>>();
C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]), false)
}
}
_ => bug!("trans_const: cannot handle type {} repreented as {:#?}", t, l)
}
}

/// Building structs is a little complicated, because we might need to
/// insert padding if a field's value is less aligned than its type.
///
/// Continuing the example from `trans_const`, a value of type `(u32,
/// E)` should have the `E` at offset 8, but if that field's
/// initializer is 4-byte aligned then simply translating the tuple as
/// a two-element struct will locate it at offset 4, and accesses to it
/// will read the wrong memory.
fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
st: &layout::Struct,
vals: &[ValueRef])
-> Vec<ValueRef> {
assert_eq!(vals.len(), st.offsets.len());

if vals.len() == 0 {
return Vec::new();
}

// offset of current value
let mut offset = 0;
let mut cfields = Vec::new();
cfields.reserve(st.offsets.len()*2);

let parts = st.field_index_by_increasing_offset().map(|i| {
(&vals[i], st.offsets[i].bytes())
});
for (&val, target_offset) in parts {
if offset < target_offset {
cfields.push(padding(ccx, target_offset - offset));
offset = target_offset;
}
assert!(!is_undef(val));
cfields.push(val);
offset += machine::llsize_of_alloc(ccx, val_ty(val));
}

if offset < st.stride().bytes() {
cfields.push(padding(ccx, st.stride().bytes() - offset));
}

cfields
}

fn build_const_union<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
un: &layout::Union,
field_val: ValueRef)
-> Vec<ValueRef> {
let mut cfields = vec![field_val];

let offset = machine::llsize_of_alloc(ccx, val_ty(field_val));
let size = un.stride().bytes();
if offset != size {
cfields.push(padding(ccx, size - offset));
}

cfields
}

fn padding(ccx: &CrateContext, size: u64) -> ValueRef {
C_undef(Type::array(&Type::i8(ccx), size))
}

0 comments on commit 982b8f4

Please sign in to comment.