Skip to content

Commit

Permalink
Use it in the library, and InstSimplify it away in the easy places
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmcm committed Apr 12, 2024
1 parent c4e78a3 commit 0d457cb
Show file tree
Hide file tree
Showing 13 changed files with 372 additions and 29 deletions.
22 changes: 21 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::MemFlags;

use rustc_hir as hir;
use rustc_middle::mir;
use rustc_middle::mir::Operand;
use rustc_middle::mir::{AggregateKind, Operand};
use rustc_middle::ty::cast::{CastTy, IntTy};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
Expand Down Expand Up @@ -717,6 +717,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
OperandRef { val: OperandValue::Immediate(static_), layout }
}
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
mir::Rvalue::Aggregate(box mir::AggregateKind::RawPtr(..), ref fields) => {
let ty = rvalue.ty(self.mir, self.cx.tcx());
let layout = self.cx.layout_of(self.monomorphize(ty));
let [data, meta] = &*fields.raw else {
bug!("RawPtr fields: {fields:?}");
};
let data = self.codegen_operand(bx, data);
let meta = self.codegen_operand(bx, meta);
match (data.val, meta.val) {
(p @ OperandValue::Immediate(_), OperandValue::ZeroSized) => {
OperandRef { val: p, layout }
}
(OperandValue::Immediate(p), OperandValue::Immediate(m)) => {
OperandRef { val: OperandValue::Pair(p, m), layout }
}
_ => bug!("RawPtr operands {data:?} {meta:?}"),
}
}
mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => {
// According to `rvalue_creates_operand`, only ZST
// aggregate rvalues are allowed to be operands.
Expand Down Expand Up @@ -1029,6 +1047,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::ThreadLocalRef(_) |
mir::Rvalue::Use(..) => // (*)
true,
// This always produces a `ty::RawPtr`, so will be Immediate or Pair
mir::Rvalue::Aggregate(box AggregateKind::RawPtr(..), ..) => true,
mir::Rvalue::Repeat(..) |
mir::Rvalue::Aggregate(..) => {
let ty = rvalue.ty(self.mir, self.cx.tcx());
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_const_eval/src/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok(self.read_immediate(op)?.to_scalar())
}

pub fn read_mem_place_meta(
&self,
op: &impl Readable<'tcx, M::Provenance>,
) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> {
Ok(if op.layout().is_zst() {
MemPlaceMeta::None
} else {
MemPlaceMeta::Meta(self.read_scalar(op)?)
})
}

// Pointer-sized reads are fairly common and need target layout access, so we wrap them in
// convenience functions.

Expand Down
17 changes: 16 additions & 1 deletion compiler/rustc_const_eval/src/interpret/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_middle::mir;
use rustc_middle::ty::layout::LayoutOf;
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};

use super::{ImmTy, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar};
use super::{ImmTy, Immediate, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar};
use crate::util;

impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Expand Down Expand Up @@ -303,6 +303,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let variant_dest = self.project_downcast(dest, variant_index)?;
(variant_index, variant_dest, active_field_index)
}
mir::AggregateKind::RawPtr(..) => {
// Trying to `project_field` into pointers tends not to work,
// so build the `Immediate` from the parts directly.
let [data, meta] = &operands.raw else {
bug!("{kind:?} should have 2 operands, had {operands:?}");
};
let data = self.eval_operand(data, None)?;
let data = self.read_pointer(&data)?;
let meta = self.eval_operand(meta, None)?;
let meta = self.read_mem_place_meta(&meta)?;
let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self);
let ptr = ImmTy::from_immediate(ptr_imm, dest.layout);
self.copy_op(&ptr, dest)?;
return Ok(());
}
_ => (FIRST_VARIANT, dest.clone(), None),
};
if active_field_index.is_some() {
Expand Down
36 changes: 35 additions & 1 deletion compiler/rustc_mir_transform/src/instsimplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl<'tcx> MirPass<'tcx> for InstSimplify {
ctx.simplify_bool_cmp(&statement.source_info, rvalue);
ctx.simplify_ref_deref(&statement.source_info, rvalue);
ctx.simplify_len(&statement.source_info, rvalue);
ctx.simplify_ptr_aggregate(&statement.source_info, rvalue);
ctx.simplify_cast(rvalue);
}
_ => {}
Expand All @@ -58,8 +59,17 @@ struct InstSimplifyContext<'tcx, 'a> {

impl<'tcx> InstSimplifyContext<'tcx, '_> {
fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
self.should_simplify_custom(source_info, "Rvalue", rvalue)
}

fn should_simplify_custom(
&self,
source_info: &SourceInfo,
label: &str,
value: impl std::fmt::Debug,
) -> bool {
self.tcx.consider_optimizing(|| {
format!("InstSimplify - Rvalue: {rvalue:?} SourceInfo: {source_info:?}")
format!("InstSimplify - {label}: {value:?} SourceInfo: {source_info:?}")
})
}

Expand Down Expand Up @@ -147,6 +157,30 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
}
}

/// Transform "Aggregate(RawPtr, \[p, ()\])" ==> "Cast(PtrToPtr, p)".
fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue
{
let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx);
if meta_ty.is_unit() {
// The mutable borrows we're holding prevent printing `rvalue` here
if !self.should_simplify_custom(
source_info,
"Aggregate::RawPtr",
(&pointee_ty, *mutability, &fields),
) {
return;
}

let mut fields = std::mem::take(fields);
let _meta = fields.pop().unwrap();
let data = fields.pop().unwrap();
let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability);
*rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty);
}
}
}

fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue {
let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
Expand Down
1 change: 1 addition & 0 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2786,6 +2786,7 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
/// change the possible layouts of pointers.
#[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[cfg(not(bootstrap))]
Expand Down
32 changes: 24 additions & 8 deletions library/core/src/ptr/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use crate::fmt;
use crate::hash::{Hash, Hasher};
#[cfg(not(bootstrap))]
use crate::intrinsics::aggregate_raw_ptr;
use crate::marker::Freeze;

/// Provides the pointer metadata type of any pointed-to type.
Expand Down Expand Up @@ -113,10 +115,17 @@ pub const fn from_raw_parts<T: ?Sized>(
data_pointer: *const (),
metadata: <T as Pointee>::Metadata,
) -> *const T {
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr }
#[cfg(bootstrap)]
{
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr }
}
#[cfg(not(bootstrap))]
{
aggregate_raw_ptr(data_pointer, metadata)
}
}

/// Performs the same functionality as [`from_raw_parts`], except that a
Expand All @@ -130,10 +139,17 @@ pub const fn from_raw_parts_mut<T: ?Sized>(
data_pointer: *mut (),
metadata: <T as Pointee>::Metadata,
) -> *mut T {
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr }
#[cfg(bootstrap)]
{
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr }
}
#[cfg(not(bootstrap))]
{
aggregate_raw_ptr(data_pointer, metadata)
}
}

#[repr(C)]
Expand Down
8 changes: 8 additions & 0 deletions library/core/tests/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1163,3 +1163,11 @@ fn test_null_array_as_slice() {
assert!(ptr.is_null());
assert_eq!(ptr.len(), 4);
}

#[test]
fn test_ptr_from_raw_parts_in_const() {
const EMPTY_SLICE_PTR: *const [i32] =
std::ptr::slice_from_raw_parts(std::ptr::without_provenance(123), 456);
assert_eq!(EMPTY_SLICE_PTR.addr(), 123);
assert_eq!(EMPTY_SLICE_PTR.len(), 456);
}
13 changes: 13 additions & 0 deletions library/core/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2678,3 +2678,16 @@ fn test_get_many_mut_duplicate() {
let mut v = vec![1, 2, 3, 4, 5];
assert!(v.get_many_mut([1, 3, 3, 4]).is_err());
}

#[test]
fn test_slice_from_raw_parts_in_const() {
static FANCY: i32 = 4;
static FANCY_SLICE: &[i32] = unsafe { std::slice::from_raw_parts(&FANCY, 1) };
assert_eq!(FANCY_SLICE.as_ptr(), std::ptr::addr_of!(FANCY));
assert_eq!(FANCY_SLICE.len(), 1);

const EMPTY_SLICE: &[i32] =
unsafe { std::slice::from_raw_parts(std::ptr::without_provenance(123456), 0) };
assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456);
assert_eq!(EMPTY_SLICE.len(), 0);
}
9 changes: 9 additions & 0 deletions tests/mir-opt/instsimplify/casts.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ unit-test: InstSimplify
//@ compile-flags: -Zinline-mir
#![crate_type = "lib"]
#![feature(core_intrinsics)]

#[inline(always)]
fn generic_cast<T, U>(x: *const T) -> *const U {
Expand All @@ -23,3 +24,11 @@ pub fn roundtrip(x: *const u8) -> *const u8 {
// CHECK: _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer));
x as *mut u8 as *const u8
}

// EMIT_MIR casts.roundtrip.InstSimplify.diff
pub fn cast_thin_via_aggregate(x: *const u8) -> *const () {
// CHECK-LABEL: fn cast_thin_via_aggregate(
// CHECK: _2 = _1;
// CHECK: _0 = move _2 as *const () (PtrToPtr);
std::intrinsics::aggregate_raw_ptr(x, ())
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,66 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range<usize>) -
debug slice => _1;
debug index => _2;
let mut _0: *const [u32];
let mut _3: usize;
let mut _4: usize;
scope 1 (inlined std::ptr::const_ptr::<impl *const [u32]>::get_unchecked::<std::ops::Range<usize>>) {
debug self => _1;
debug index => _2;
debug ((index: std::ops::Range<usize>).0: usize) => _3;
debug ((index: std::ops::Range<usize>).1: usize) => _4;
scope 2 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked) {
debug ((self: std::ops::Range<usize>).0: usize) => _3;
debug ((self: std::ops::Range<usize>).1: usize) => _4;
debug slice => _1;
let _5: usize;
let mut _6: *const u32;
let mut _7: *const u32;
scope 3 {
debug new_len => _5;
scope 6 (inlined std::ptr::const_ptr::<impl *const [u32]>::as_ptr) {
debug self => _1;
}
scope 7 (inlined std::ptr::const_ptr::<impl *const u32>::add) {
debug self => _6;
debug count => _3;
}
scope 8 (inlined slice_from_raw_parts::<u32>) {
debug data => _7;
debug len => _5;
let mut _8: *const ();
scope 9 (inlined std::ptr::const_ptr::<impl *const u32>::cast::<()>) {
debug self => _7;
}
scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) {
debug data_pointer => _8;
debug metadata => _5;
}
}
}
scope 4 (inlined std::ptr::const_ptr::<impl *const [u32]>::len) {
debug self => _1;
scope 5 (inlined std::ptr::metadata::<[u32]>) {
debug ptr => _1;
}
}
}
}

bb0: {
_0 = <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked(move _2, move _1) -> [return: bb1, unwind unreachable];
}

bb1: {
_3 = move (_2.0: usize);
_4 = move (_2.1: usize);
StorageLive(_5);
_5 = SubUnchecked(_4, _3);
StorageLive(_7);
StorageLive(_6);
_6 = _1 as *const u32 (PtrToPtr);
_7 = Offset(_6, _3);
StorageDead(_6);
StorageLive(_8);
_8 = _7 as *const () (PtrToPtr);
_0 = *const [u32] from (_8, _5);
StorageDead(_8);
StorageDead(_7);
StorageDead(_5);
return;
}
}
Loading

0 comments on commit 0d457cb

Please sign in to comment.