From 301f8681ca1e6b65719bfc0dd3972efab59f7305 Mon Sep 17 00:00:00 2001 From: whataloadofwhat Date: Sun, 25 Jan 2015 02:16:48 +0000 Subject: [PATCH] Add {size/min_align/pref_align}_of_val intrinsic Allows DSTs to be sized generically at runtime. core::mem::{size/align/min_align}_of_val have also been slightly altered to use the new intrinsics, and to also allow DSTs. closes #19063 --- src/libcore/intrinsics.rs | 9 +- src/libcore/mem.rs | 58 +++++++- src/librustc_trans/trans/glue.rs | 33 +++-- src/librustc_trans/trans/intrinsic.rs | 59 +++++++- src/librustc_trans/trans/machine.rs | 15 ++ src/librustc_typeck/check/mod.rs | 7 + src/test/run-pass/size-align-of-val.rs | 181 +++++++++++++++++++++++++ 7 files changed, 339 insertions(+), 23 deletions(-) create mode 100644 src/test/run-pass/size-align-of-val.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index dd488a74216eb..46b2f03d1d3d2 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -187,6 +187,8 @@ extern "rust-intrinsic" { /// and structures there may be additional padding between /// elements. pub fn size_of() -> uint; + #[cfg(not(stage0))] + pub fn size_of_val(val: &T) -> uint; /// Move a value to an uninitialized memory location. /// @@ -194,7 +196,12 @@ extern "rust-intrinsic" { pub fn move_val_init(dst: &mut T, src: T); pub fn min_align_of() -> uint; + #[cfg(not(stage0))] + pub fn min_align_of_val(val: &T) -> uint; + pub fn pref_align_of() -> uint; + #[cfg(not(stage0))] + pub fn pref_align_of_val(val: &T) -> uint; /// Get a static pointer to a type descriptor. #[cfg(not(stage0))] diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 8438c9b206ee7..2331e69eaa388 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -47,6 +47,22 @@ pub fn size_of() -> uint { unsafe { intrinsics::size_of::() } } +/// Returns the size of the type that `val` points to in bytes. +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// assert_eq!(4, mem::size_of_val(&5i32)); +/// ``` +#[inline] +#[stable] +#[cfg(not(stage0))] +pub fn size_of_val(val: &T) -> uint { + unsafe { intrinsics::size_of_val(val) } +} + /// Returns the size of the type that `_val` points to in bytes. /// /// # Examples @@ -58,6 +74,7 @@ pub fn size_of() -> uint { /// ``` #[inline] #[stable] +#[cfg(stage0)] pub fn size_of_val(_val: &T) -> uint { size_of::() } @@ -79,7 +96,7 @@ pub fn min_align_of() -> uint { unsafe { intrinsics::min_align_of::() } } -/// Returns the ABI-required minimum alignment of the type of the value that `_val` points to +/// Returns the ABI-required minimum alignment of the type of the value that `val` points to. /// /// # Examples /// @@ -90,6 +107,23 @@ pub fn min_align_of() -> uint { /// ``` #[inline] #[stable] +#[cfg(not(stage0))] +pub fn min_align_of_val(val: &T) -> uint { + unsafe { intrinsics::min_align_of_val(val) } +} + +/// Returns the ABI-required minimum alignment of the type of the value that `_val` points to. +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// assert_eq!(4, mem::min_align_of_val(&5i32)); +/// ``` +#[inline] +#[stable] +#[cfg(stage0)] pub fn min_align_of_val(_val: &T) -> uint { min_align_of::() } @@ -116,6 +150,25 @@ pub fn align_of() -> uint { unsafe { intrinsics::pref_align_of::() } } +/// Returns the alignment of the type of the value that `val` points to. +/// +/// This is similar to `align_of`, but can handle types such as trait object, returning the +/// alignment for an arbitrary value at runtime. +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// assert_eq!(4, mem::align_of_val(&5i32)); +/// ``` +#[inline] +#[stable] +#[cfg(not(stage0))] +pub fn align_of_val(val: &T) -> uint { + unsafe { intrinsics::pref_align_of_val(val) } +} + /// Returns the alignment of the type of the value that `_val` points to. /// /// This is similar to `align_of`, but function will properly handle types such as trait objects @@ -130,6 +183,7 @@ pub fn align_of() -> uint { /// ``` #[inline] #[stable] +#[cfg(stage0)] pub fn align_of_val(_val: &T) -> uint { align_of::() } diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index fb2ee55940d09..a5a2f87e29cff 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -297,38 +297,42 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }) } -fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, info: ValueRef) +pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, + info: ValueRef, align: AlignType) -> (ValueRef, ValueRef) { debug!("calculate size of DST: {}; with lost info: {}", bcx.ty_to_string(t), bcx.val_to_string(info)); if type_is_sized(bcx.tcx(), t) { let sizing_type = sizing_type_of(bcx.ccx(), t); let size = C_uint(bcx.ccx(), llsize_of_alloc(bcx.ccx(), sizing_type)); - let align = C_uint(bcx.ccx(), align_of(bcx.ccx(), t)); + let align = C_uint(bcx.ccx(), align.align_of(bcx.ccx(), sizing_type)); return (size, align); } match t.sty { ty::ty_struct(id, substs) => { let ccx = bcx.ccx(); - // First get the size of all statically known fields. - // Don't use type_of::sizing_type_of because that expects t to be sized. - assert!(!ty::type_is_simd(bcx.tcx(), t)); - let repr = adt::represent_type(ccx, t); - let sizing_type = adt::sizing_type_of(ccx, &*repr, true); - let sized_size = C_uint(ccx, llsize_of_alloc(ccx, sizing_type)); - let sized_align = C_uint(ccx, llalign_of_min(ccx, sizing_type)); - // Recurse to get the size of the dynamically sized field (must be // the last field). let fields = ty::struct_fields(bcx.tcx(), id, substs); let last_field = fields[fields.len()-1]; let field_ty = last_field.mt.ty; - let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info); + let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, + info, align); + + // First get the size of all statically known fields. + // Don't use type_of::sizing_type_of because that expects t to be sized. + assert!(!ty::type_is_simd(bcx.tcx(), t)); + let repr = adt::represent_type(ccx, t); + let sizing_type = adt::sizing_type_of(ccx, &*repr, true); + let dst_type = type_of(ccx, t); + let sized_size = C_uint(ccx, llelement_offset(ccx, dst_type, + fields.len() - 1)); + let sized_align = C_uint(ccx, align.align_of(ccx, sizing_type)); // Return the sum of sizes and max of aligns. let size = Add(bcx, sized_size, unsized_size, DebugLoc::None); let align = Select(bcx, - ICmp(bcx, llvm::IntULT, sized_align, unsized_align), + ICmp(bcx, llvm::IntUGE, sized_align, unsized_align), sized_align, unsized_align); (size, align) @@ -346,7 +350,7 @@ fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, info: // The info in this case is the length of the str, so the size is that // times the unit size. let llunit_ty = sizing_type_of(bcx.ccx(), unit_ty); - let unit_align = llalign_of_min(bcx.ccx(), llunit_ty); + let unit_align = align.align_of(bcx.ccx(), llunit_ty); let unit_size = llsize_of_alloc(bcx.ccx(), llunit_ty); (Mul(bcx, info, C_uint(bcx.ccx(), unit_size), DebugLoc::None), C_uint(bcx.ccx(), unit_align)) @@ -393,7 +397,8 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: Ty<'tcx>) let bcx = drop_ty(bcx, v0, content_ty, DebugLoc::None); let info = GEPi(bcx, v0, &[0, abi::FAT_PTR_EXTRA]); let info = Load(bcx, info); - let (llsize, llalign) = size_and_align_of_dst(bcx, content_ty, info); + let (llsize, llalign) = size_and_align_of_dst(bcx, content_ty, + info, AlignType::Min); trans_exchange_free_dyn(bcx, llbox, llsize, llalign) }) } diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 9bee2c5bbc61c..c40ca78db468f 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,6 +10,7 @@ #![allow(non_upper_case_globals)] +use back::abi; use llvm; use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeKind}; use middle::subst; @@ -314,17 +315,62 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } (_, "size_of") => { let tp_ty = *substs.types.get(FnSpace, 0); - let lltp_ty = type_of::type_of(ccx, tp_ty); - C_uint(ccx, machine::llsize_of_alloc(ccx, lltp_ty)) + let llty = type_of::sizing_type_of(ccx, tp_ty); + C_uint(ccx, machine::llsize_of_alloc(ccx, llty)) } (_, "min_align_of") => { let tp_ty = *substs.types.get(FnSpace, 0); - C_uint(ccx, type_of::align_of(ccx, tp_ty)) + let llty = type_of::sizing_type_of(ccx, tp_ty); + C_uint(ccx, machine::llalign_of_min(ccx, llty)) } (_, "pref_align_of") => { let tp_ty = *substs.types.get(FnSpace, 0); - let lltp_ty = type_of::type_of(ccx, tp_ty); - C_uint(ccx, machine::llalign_of_pref(ccx, lltp_ty)) + let llty = type_of::sizing_type_of(ccx, tp_ty); + C_uint(ccx, machine::llalign_of_pref(ccx, llty)) + } + (_, "size_of_val") => { + let tp_ty = *substs.types.get(FnSpace, 0); + if type_is_sized(tcx, tp_ty) { + let llty = type_of::sizing_type_of(ccx, tp_ty); + C_uint(ccx, machine::llsize_of_alloc(ccx, llty)) + } else { + let ptr = GEPi(bcx, llargs[0], &[0, abi::FAT_PTR_EXTRA]); + let (size, align) = glue::size_and_align_of_dst(bcx, tp_ty, + Load(bcx, ptr), machine::AlignType::Min); + + // If the size is not aligned to the alignment, then we need to + // round it up to the next number which matches the alignment + let rem = URem(bcx, size, align, call_debug_location); + Select(bcx, + ICmp(bcx, llvm::IntPredicate::IntEQ, rem, C_uint(ccx, 0u64)), + size, + Add(bcx, size, Sub(bcx, align, rem, call_debug_location), + call_debug_location)) + } + } + (_, "min_align_of_val") => { + let tp_ty = *substs.types.get(FnSpace, 0); + if type_is_sized(tcx, tp_ty) { + let llty = type_of::sizing_type_of(ccx, tp_ty); + C_uint(ccx, machine::llalign_of_min(ccx, llty)) + } else { + let ptr = GEPi(bcx, llargs[0], &[0, abi::FAT_PTR_EXTRA]); + let (_, align) = glue::size_and_align_of_dst(bcx, tp_ty, + Load(bcx, ptr), machine::AlignType::Min); + align + } + } + (_, "pref_align_of_val") => { + let tp_ty = *substs.types.get(FnSpace, 0); + if type_is_sized(tcx, tp_ty) { + let llty = type_of::sizing_type_of(ccx, tp_ty); + C_uint(ccx, machine::llalign_of_pref(ccx, llty)) + } else { + let ptr = GEPi(bcx, llargs[0], &[0, abi::FAT_PTR_EXTRA]); + let (_, align) = glue::size_and_align_of_dst(bcx, tp_ty, + Load(bcx, ptr), machine::AlignType::Pref); + align + } } (_, "move_val_init") => { // Create a datum reflecting the value being moved. @@ -877,3 +923,4 @@ fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ret } } + diff --git a/src/librustc_trans/trans/machine.rs b/src/librustc_trans/trans/machine.rs index 1552ac0bea0fe..002105b31b449 100644 --- a/src/librustc_trans/trans/machine.rs +++ b/src/librustc_trans/trans/machine.rs @@ -82,6 +82,21 @@ pub fn llsize_of(cx: &CrateContext, ty: Type) -> ValueRef { return C_uint(cx, llsize_of_alloc(cx, ty)); } +#[derive(Copy)] +pub enum AlignType { + Min, + Pref +} +impl AlignType { + pub fn align_of<'a, 'tcx>(&self, ccx: &CrateContext<'a, 'tcx>, + ty: Type) -> llalign { + match *self { + AlignType::Min => llalign_of_min(ccx, ty), + AlignType::Pref => llalign_of_pref(ccx, ty) + } + } +} + // Returns the preferred alignment of the given type for the current target. // The preferred alignment may be larger than the alignment used when // packing the type into structs. This will be used for things like diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5f8ae09b5bd6f..717590500b2c6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5243,6 +5243,13 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { "breakpoint" => (0, Vec::new(), ty::mk_nil(tcx)), "size_of" | "pref_align_of" | "min_align_of" => (1u, Vec::new(), ccx.tcx.types.uint), + "size_of_val" | "min_align_of_val" | + "pref_align_of_val" => (1u, + vec!(ty::mk_imm_rptr(tcx, + tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), + ty::BrAnon(0))), + param(ccx, 0))), + ccx.tcx.types.uint), "init" => (1u, Vec::new(), param(ccx, 0)), "uninit" => (1u, Vec::new(), param(ccx, 0)), "forget" => (1u, vec!( param(ccx, 0) ), ty::mk_nil(tcx)), diff --git a/src/test/run-pass/size-align-of-val.rs b/src/test/run-pass/size-align-of-val.rs new file mode 100644 index 0000000000000..a341fafdcc201 --- /dev/null +++ b/src/test/run-pass/size-align-of-val.rs @@ -0,0 +1,181 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unstable)] + +extern crate core; + +use core::intrinsics::{size_of_val, min_align_of_val, pref_align_of_val}; + +struct SmallStruct(u8); +struct BigStruct([u64; 100]); +trait Trait { } +impl Trait for SmallStruct { } +impl Trait for BigStruct { } + +trait PackedSize { + fn packed_size(helper: Option) -> usize; +} + +struct UnsizedStruct(i32, i32, str); +struct BigUnsizedStruct(i32, i32, [u64]); +struct SizedPartUS(i32, i32); +impl PackedSize for SizedPartUS { + fn packed_size(_: Option) -> usize { + core::mem::size_of::() * 2 + } +} +struct RecursiveUnsizedStruct(u64, u8, UnsizedStruct); +struct BigRecursiveUnsizedStruct(u64, u8, BigUnsizedStruct); +struct SizedPartRUS(u64, u8, SizedPartUS); +impl PackedSize for SizedPartRUS { + fn packed_size(_: Option) -> usize { + //u64 + u8 + [u8; 3] for padding + packed_size of SizedPartUS + core::mem::size_of::() + core::mem::size_of::() * 4 + + PackedSize::packed_size(None::) + } +} +struct UnsizedStructTrait(i8, i8, Trait + 'static); +struct SizedPartUST(i8, i8); +impl PackedSize for SizedPartUST { + fn packed_size(_: Option) -> usize { + core::mem::size_of::() * 2 + } +} + +static SMALL_STRUCT: SmallStruct = SmallStruct(0); +static BIG_STRUCT: BigStruct = BigStruct([0; 100]); +static STRING: &'static str = "Test String Strictly For Testing Purposes"; +static U8_SLICE: &'static [u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; +static U64_SLICE: &'static [u64] = &[1, 2, 3, 4, 5, 6]; + +fn expected_unsized_struct_size(us_part: &U) -> usize { + let sized_size = PackedSize::packed_size(None::); + let sized_align = core::mem::min_align_of::(); + + let us_size = unsafe { size_of_val(us_part) }; + let us_align = unsafe { min_align_of_val(us_part) }; + + let us_offset = match sized_size % us_align { + 0 => sized_size, + rem => sized_size + (us_align - rem) + }; + let align = core::cmp::max(us_align, sized_align); + + let size = us_offset + us_size; + + match size % align { + 0 => size, + rem => size + (align - rem) + } +} + +fn main() { + //A few helper variables that can't be constructed statically easily. + let unsized_struct = unsafe { + core::mem::transmute::<_, &UnsizedStruct>(STRING) + }; + let recursive_unsized_struct = unsafe { + core::mem::transmute::<_, &RecursiveUnsizedStruct>(unsized_struct) + }; + let big_unsized_struct = unsafe { + core::mem::transmute::<_, &BigUnsizedStruct>(U64_SLICE) + }; + let big_recursive_unsized_struct = unsafe { + core::mem::transmute::<_, &BigRecursiveUnsizedStruct>(big_unsized_struct) + }; + let trait_unsized_struct_b = unsafe { + core::mem::transmute::<_, &UnsizedStructTrait>(&BIG_STRUCT as &Trait) + }; + let trait_unsized_struct_s = unsafe { + core::mem::transmute::<_, &UnsizedStructTrait>(&SMALL_STRUCT as &Trait) + }; + + //Sizes + assert_eq!(STRING.len(), + unsafe { size_of_val(STRING) }); + assert_eq!(U8_SLICE.len() * core::mem::size_of::(), + unsafe { size_of_val(U8_SLICE) }); + assert_eq!(U64_SLICE.len() * core::mem::size_of::(), + unsafe { size_of_val(U64_SLICE) }); + assert_eq!(core::mem::size_of::(), + unsafe { size_of_val(&BIG_STRUCT as &Trait) }); + assert_eq!(core::mem::size_of::(), + unsafe { size_of_val(&SMALL_STRUCT as &Trait) }); + assert_eq!(expected_unsized_struct_size::(STRING), + unsafe { size_of_val(unsized_struct) }); + assert_eq!(expected_unsized_struct_size::(STRING), + unsafe { size_of_val(recursive_unsized_struct) }); + assert_eq!(expected_unsized_struct_size::(U64_SLICE), + unsafe { size_of_val(big_unsized_struct) }); + assert_eq!(expected_unsized_struct_size::(U64_SLICE), + unsafe { size_of_val(big_recursive_unsized_struct) }); + assert_eq!(expected_unsized_struct_size::(&BIG_STRUCT as &Trait), + unsafe { size_of_val(trait_unsized_struct_b) }); + assert_eq!(expected_unsized_struct_size::(&SMALL_STRUCT as &Trait), + unsafe { size_of_val(trait_unsized_struct_s) }); + + //Min alignments + assert_eq!(core::mem::min_align_of::(), + unsafe { min_align_of_val(STRING) }); + assert_eq!(core::mem::min_align_of::(), + unsafe { min_align_of_val(U8_SLICE) }); + assert_eq!(core::mem::min_align_of::(), + unsafe { min_align_of_val(U64_SLICE) }); + assert_eq!(core::mem::min_align_of::(), + unsafe { min_align_of_val(&BIG_STRUCT as &Trait) }); + assert_eq!(core::mem::min_align_of::(), + unsafe { min_align_of_val(&SMALL_STRUCT as &Trait) }); + assert_eq!(core::cmp::max(core::mem::min_align_of::(), + unsafe { min_align_of_val(STRING) }), + unsafe { min_align_of_val(unsized_struct) }); + assert_eq!(core::cmp::max(core::mem::min_align_of::(), + unsafe { min_align_of_val(STRING) }), + unsafe { min_align_of_val(recursive_unsized_struct) }); + assert_eq!(core::cmp::max(core::mem::min_align_of::(), + unsafe { min_align_of_val(U64_SLICE) }), + unsafe { min_align_of_val(big_unsized_struct) }); + assert_eq!(core::cmp::max(core::mem::min_align_of::(), + unsafe { min_align_of_val(U64_SLICE) }), + unsafe { min_align_of_val(big_recursive_unsized_struct) }); + assert_eq!(core::cmp::max(core::mem::min_align_of::(), + unsafe { min_align_of_val(&BIG_STRUCT as &Trait) }), + unsafe { min_align_of_val(trait_unsized_struct_b) }); + assert_eq!(core::cmp::max(core::mem::min_align_of::(), + unsafe { min_align_of_val(&SMALL_STRUCT as &Trait) }), + unsafe { min_align_of_val(trait_unsized_struct_s) }); + + //Pref alignments + assert_eq!(core::mem::align_of::(), + unsafe { pref_align_of_val(STRING) }); + assert_eq!(core::mem::align_of::(), + unsafe { pref_align_of_val(U8_SLICE) }); + assert_eq!(core::mem::align_of::(), + unsafe { pref_align_of_val(U64_SLICE) }); + assert_eq!(core::cmp::max(core::mem::align_of::(), + unsafe { pref_align_of_val(STRING) }), + unsafe { pref_align_of_val(unsized_struct) }); + assert_eq!(core::cmp::max(core::mem::align_of::(), + unsafe { pref_align_of_val(STRING) }), + unsafe { pref_align_of_val(recursive_unsized_struct) }); + assert_eq!(core::cmp::max(core::mem::align_of::(), + unsafe { pref_align_of_val(U64_SLICE) }), + unsafe { pref_align_of_val(big_unsized_struct) }); + assert_eq!(core::cmp::max(core::mem::align_of::(), + unsafe { pref_align_of_val(U64_SLICE) }), + unsafe { pref_align_of_val(big_recursive_unsized_struct) }); + assert_eq!(core::cmp::max(core::mem::align_of::(), + unsafe { pref_align_of_val(&BIG_STRUCT as &Trait) }), + unsafe { pref_align_of_val(trait_unsized_struct_b) }); + assert_eq!(core::cmp::max(core::mem::align_of::(), + unsafe { pref_align_of_val(&SMALL_STRUCT as &Trait) }), + unsafe { pref_align_of_val(trait_unsized_struct_s) }); +} +