Auto merge of #63079 - RalfJung:ctfe-no-align, r=oli-obk

CTFE: simplify ConstValue by not checking for alignment

I hope the test suite actually covers the problematic cases here?

r? @oli-obk

Fixes https://github.com/rust-lang/rust/issues/61952
This commit is contained in:
bors 2019-08-05 08:16:51 +00:00
commit 4be0675589
13 changed files with 62 additions and 46 deletions

View File

@ -2,7 +2,7 @@ use std::fmt;
use rustc_macros::HashStable;
use rustc_apfloat::{Float, ieee::{Double, Single}};
use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size, Align}, subst::SubstsRef};
use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef};
use crate::ty::PlaceholderConst;
use crate::hir::def_id::DefId;
@ -45,18 +45,11 @@ pub enum ConstValue<'tcx> {
/// A value not represented/representable by `Scalar` or `Slice`
ByRef {
/// The alignment exists to allow `const_field` to have `ByRef` access to nonprimitive
/// fields of `repr(packed)` structs. The alignment may be lower than the type of this
/// constant. This permits reads with lower alignment than what the type would normally
/// require.
/// FIXME(RalfJ,oli-obk): The alignment checks are part of miri, but const eval doesn't
/// really need them. Disabling them may be too hard though.
align: Align,
/// Offset into `alloc`
offset: Size,
/// The backing memory of the value, may contain more memory than needed for just the value
/// in order to share `Allocation`s between values
alloc: &'tcx Allocation,
/// Offset into `alloc`
offset: Size,
},
/// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other

View File

@ -1367,8 +1367,8 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> {
impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
match *self {
ConstValue::ByRef { offset, align, alloc } =>
ConstValue::ByRef { offset, align, alloc },
ConstValue::ByRef { alloc, offset } =>
ConstValue::ByRef { alloc, offset },
ConstValue::Infer(ic) => ConstValue::Infer(ic.fold_with(folder)),
ConstValue::Param(p) => ConstValue::Param(p.fold_with(folder)),
ConstValue::Placeholder(p) => ConstValue::Placeholder(p),

View File

@ -11,7 +11,7 @@ use crate::value::Value;
use rustc_codegen_ssa::traits::*;
use crate::consts::const_alloc_to_llvm;
use rustc::ty::layout::{HasDataLayout, LayoutOf, self, TyLayout, Size, Align};
use rustc::ty::layout::{HasDataLayout, LayoutOf, self, TyLayout, Size};
use rustc::mir::interpret::{Scalar, GlobalAlloc, Allocation};
use rustc_codegen_ssa::mir::place::PlaceRef;
@ -329,12 +329,12 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn from_const_alloc(
&self,
layout: TyLayout<'tcx>,
align: Align,
alloc: &Allocation,
offset: Size,
) -> PlaceRef<'tcx, &'ll Value> {
assert_eq!(alloc.align, layout.align.abi);
let init = const_alloc_to_llvm(self, alloc);
let base_addr = self.static_addr_of(init, align, None);
let base_addr = self.static_addr_of(init, alloc.align, None);
let llval = unsafe { llvm::LLVMConstInBoundsGEP(
self.const_bitcast(base_addr, self.type_i8p()),
@ -342,7 +342,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
1,
)};
let llval = self.const_bitcast(llval, self.type_ptr_to(layout.llvm_type(self)));
PlaceRef::new_sized(llval, layout, align)
PlaceRef::new_sized(llval, layout, alloc.align)
}
fn const_ptrcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {

View File

@ -72,8 +72,8 @@ pub fn codegen_static_initializer(
let alloc = match static_.val {
ConstValue::ByRef {
offset, align, alloc,
} if offset.bytes() == 0 && align == alloc.align => {
alloc, offset,
} if offset.bytes() == 0 => {
alloc
},
_ => bug!("static const eval returned {:#?}", static_),

View File

@ -109,8 +109,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
let b_llval = bx.const_usize((end - start) as u64);
OperandValue::Pair(a_llval, b_llval)
},
ConstValue::ByRef { offset, align, alloc } => {
return bx.load_operand(bx.from_const_alloc(layout, align, alloc, offset));
ConstValue::ByRef { alloc, offset } => {
return bx.load_operand(bx.from_const_alloc(layout, alloc, offset));
},
};

View File

@ -466,8 +466,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let layout = cx.layout_of(self.monomorphize(&ty));
match bx.tcx().const_eval(param_env.and(cid)) {
Ok(val) => match val.val {
mir::interpret::ConstValue::ByRef { offset, align, alloc } => {
bx.cx().from_const_alloc(layout, align, alloc, offset)
mir::interpret::ConstValue::ByRef { alloc, offset } => {
bx.cx().from_const_alloc(layout, alloc, offset)
}
_ => bug!("promoteds should have an allocation: {:?}", val),
},

View File

@ -35,7 +35,6 @@ pub trait ConstMethods<'tcx>: BackendTypes {
fn from_const_alloc(
&self,
layout: layout::TyLayout<'tcx>,
align: layout::Align,
alloc: &Allocation,
offset: layout::Size,
) -> PlaceRef<'tcx, Self::Value>;

View File

@ -98,7 +98,7 @@ fn op_to_const<'tcx>(
Ok(mplace) => {
let ptr = mplace.ptr.to_ptr().unwrap();
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
ConstValue::ByRef { offset: ptr.offset, align: mplace.align, alloc }
ConstValue::ByRef { alloc, offset: ptr.offset }
},
// see comment on `let try_as_immediate` above
Err(ImmTy { imm: Immediate::Scalar(x), .. }) => match x {
@ -112,7 +112,7 @@ fn op_to_const<'tcx>(
let mplace = op.assert_mem_place();
let ptr = mplace.ptr.to_ptr().unwrap();
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
ConstValue::ByRef { offset: ptr.offset, align: mplace.align, alloc }
ConstValue::ByRef { alloc, offset: ptr.offset }
},
},
Err(ImmTy { imm: Immediate::ScalarPair(a, b), .. }) => {
@ -326,6 +326,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
const STATIC_KIND: Option<!> = None; // no copying of statics allowed
// We do not check for alignment to avoid having to carry an `Align`
// in `ConstValue::ByRef`.
const CHECK_ALIGN: bool = false;
#[inline(always)]
fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
false // for now, we don't enforce validity
@ -561,9 +565,8 @@ fn validate_and_turn_into_const<'tcx>(
let ptr = mplace.ptr.to_ptr()?;
Ok(tcx.mk_const(ty::Const {
val: ConstValue::ByRef {
offset: ptr.offset,
align: mplace.align,
alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
offset: ptr.offset,
},
ty: mplace.layout.ty,
}))

View File

@ -218,10 +218,8 @@ impl LiteralExpander<'tcx> {
(ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => {
let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
ConstValue::ByRef {
offset: p.offset,
// FIXME(oli-obk): this should be the type's layout
align: alloc.align,
alloc,
offset: p.offset,
}
},
// unsize array to slice if pattern is array but match value or other patterns are slice

View File

@ -109,6 +109,9 @@ pub trait Machine<'mir, 'tcx>: Sized {
/// that is added to the memory so that the work is not done twice.
const STATIC_KIND: Option<Self::MemoryKinds>;
/// Whether memory accesses should be alignment-checked.
const CHECK_ALIGN: bool;
/// Whether to enforce the validity invariant
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;

View File

@ -306,11 +306,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
///
/// Most of the time you should use `check_mplace_access`, but when you just have a pointer,
/// this method is still appropriate.
#[inline(always)]
pub fn check_ptr_access(
&self,
sptr: Scalar<M::PointerTag>,
size: Size,
align: Align,
) -> InterpResult<'tcx, Option<Pointer<M::PointerTag>>> {
let align = if M::CHECK_ALIGN { Some(align) } else { None };
self.check_ptr_access_align(sptr, size, align)
}
/// Like `check_ptr_access`, but *definitely* checks alignment when `align`
/// is `Some` (overriding `M::CHECK_ALIGN`).
pub(super) fn check_ptr_access_align(
&self,
sptr: Scalar<M::PointerTag>,
size: Size,
align: Option<Align>,
) -> InterpResult<'tcx, Option<Pointer<M::PointerTag>>> {
fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> {
if offset % align.bytes() == 0 {
@ -338,11 +351,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
Ok(bits) => {
let bits = bits as u64; // it's ptr-sized
assert!(size.bytes() == 0);
// Must be non-NULL and aligned.
// Must be non-NULL.
if bits == 0 {
throw_unsup!(InvalidNullPointerUsage)
}
check_offset_align(bits, align)?;
// Must be aligned.
if let Some(align) = align {
check_offset_align(bits, align)?;
}
None
}
Err(ptr) => {
@ -355,18 +371,20 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
end_ptr.check_in_alloc(allocation_size, CheckInAllocMsg::MemoryAccessTest)?;
// Test align. Check this last; if both bounds and alignment are violated
// we want the error to be about the bounds.
if alloc_align.bytes() < align.bytes() {
// The allocation itself is not aligned enough.
// FIXME: Alignment check is too strict, depending on the base address that
// got picked we might be aligned even if this check fails.
// We instead have to fall back to converting to an integer and checking
// the "real" alignment.
throw_unsup!(AlignmentCheckFailed {
has: alloc_align,
required: align,
})
if let Some(align) = align {
if alloc_align.bytes() < align.bytes() {
// The allocation itself is not aligned enough.
// FIXME: Alignment check is too strict, depending on the base address that
// got picked we might be aligned even if this check fails.
// We instead have to fall back to converting to an integer and checking
// the "real" alignment.
throw_unsup!(AlignmentCheckFailed {
has: alloc_align,
required: align,
});
}
check_offset_align(ptr.offset.bytes(), align)?;
}
check_offset_align(ptr.offset.bytes(), align)?;
// We can still be zero-sized in this branch, in which case we have to
// return `None`.

View File

@ -557,12 +557,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.layout_of(self.monomorphize(val.ty)?)
})?;
let op = match val.val {
ConstValue::ByRef { offset, align, alloc } => {
ConstValue::ByRef { alloc, offset } => {
let id = self.tcx.alloc_map.lock().create_memory_alloc(alloc);
// We rely on mutability being set correctly in that allocation to prevent writes
// where none should happen.
let ptr = self.tag_static_base_pointer(Pointer::new(id, offset));
Operand::Indirect(MemPlace::from_ptr(ptr, align))
Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi))
},
ConstValue::Scalar(x) =>
Operand::Immediate(tag_scalar(x).into()),

View File

@ -398,7 +398,9 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// alignment and size determined by the layout (size will be 0,
// alignment should take attributes into account).
.unwrap_or_else(|| (layout.size, layout.align.abi));
let ptr: Option<_> = match self.ecx.memory.check_ptr_access(ptr, size, align) {
let ptr: Option<_> = match
self.ecx.memory.check_ptr_access_align(ptr, size, Some(align))
{
Ok(ptr) => ptr,
Err(err) => {
info!(