Add an intrinsic for ptr::metadata

This commit is contained in:
Scott McMurray 2024-04-21 16:11:01 -07:00
parent 7717a306b2
commit 459ce3f6bb
31 changed files with 422 additions and 52 deletions

View File

@ -619,22 +619,34 @@ fn codegen_stmt<'tcx>(
Rvalue::UnaryOp(un_op, ref operand) => { Rvalue::UnaryOp(un_op, ref operand) => {
let operand = codegen_operand(fx, operand); let operand = codegen_operand(fx, operand);
let layout = operand.layout(); let layout = operand.layout();
let val = operand.load_scalar(fx);
let res = match un_op { let res = match un_op {
UnOp::Not => match layout.ty.kind() { UnOp::Not => {
ty::Bool => { let val = operand.load_scalar(fx);
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0); match layout.ty.kind() {
CValue::by_val(res, layout) ty::Bool => {
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0);
CValue::by_val(res, layout)
}
ty::Uint(_) | ty::Int(_) => {
CValue::by_val(fx.bcx.ins().bnot(val), layout)
}
_ => unreachable!("un op Not for {:?}", layout.ty),
} }
ty::Uint(_) | ty::Int(_) => { }
CValue::by_val(fx.bcx.ins().bnot(val), layout) UnOp::Neg => {
let val = operand.load_scalar(fx);
match layout.ty.kind() {
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout),
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout),
_ => unreachable!("un op Neg for {:?}", layout.ty),
} }
_ => unreachable!("un op Not for {:?}", layout.ty), }
}, UnOp::PtrMetadata => match layout.abi {
UnOp::Neg => match layout.ty.kind() { Abi::Scalar(_) => CValue::zst(dest_layout),
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout), Abi::ScalarPair(_, _) => {
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout), CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout)
_ => unreachable!("un op Neg for {:?}", layout.ty), }
_ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"),
}, },
}; };
lval.write_cvalue(fx, res); lval.write_cvalue(fx, res);

View File

@ -100,7 +100,7 @@ pub(crate) fn codegen_const_value<'tcx>(
assert!(layout.is_sized(), "unsized const value"); assert!(layout.is_sized(), "unsized const value");
if layout.is_zst() { if layout.is_zst() {
return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout); return CValue::zst(layout);
} }
match const_val { match const_val {

View File

@ -95,6 +95,14 @@ impl<'tcx> CValue<'tcx> {
CValue(CValueInner::ByValPair(value, extra), layout) CValue(CValueInner::ByValPair(value, extra), layout)
} }
/// Create an instance of a ZST
///
/// The is represented by a dangling pointer of suitable alignment.
pub(crate) fn zst(layout: TyAndLayout<'tcx>) -> CValue<'tcx> {
assert!(layout.is_zst());
CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout)
}
pub(crate) fn layout(&self) -> TyAndLayout<'tcx> { pub(crate) fn layout(&self) -> TyAndLayout<'tcx> {
self.1 self.1
} }

View File

@ -565,6 +565,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
for elem in place_ref.projection.iter() { for elem in place_ref.projection.iter() {
match elem { match elem {
mir::ProjectionElem::Field(ref f, _) => { mir::ProjectionElem::Field(ref f, _) => {
debug_assert!(
!o.layout.ty.is_any_ptr(),
"Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
but tried to access field {f:?} of pointer {o:?}",
);
o = o.extract_field(bx, f.index()); o = o.extract_field(bx, f.index());
} }
mir::ProjectionElem::Index(_) mir::ProjectionElem::Index(_)

View File

@ -480,6 +480,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
cg_base = match *elem { cg_base = match *elem {
mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()), mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()),
mir::ProjectionElem::Field(ref field, _) => { mir::ProjectionElem::Field(ref field, _) => {
debug_assert!(
!cg_base.layout.ty.is_any_ptr(),
"Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
but tried to access field {field:?} of pointer {cg_base:?}",
);
cg_base.project_field(bx, field.index()) cg_base.project_field(bx, field.index())
} }
mir::ProjectionElem::OpaqueCast(ty) => { mir::ProjectionElem::OpaqueCast(ty) => {

View File

@ -623,19 +623,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::UnaryOp(op, ref operand) => { mir::Rvalue::UnaryOp(op, ref operand) => {
let operand = self.codegen_operand(bx, operand); let operand = self.codegen_operand(bx, operand);
let lloperand = operand.immediate();
let is_float = operand.layout.ty.is_floating_point(); let is_float = operand.layout.ty.is_floating_point();
let llval = match op { let (val, layout) = match op {
mir::UnOp::Not => bx.not(lloperand), mir::UnOp::Not => {
let llval = bx.not(operand.immediate());
(OperandValue::Immediate(llval), operand.layout)
}
mir::UnOp::Neg => { mir::UnOp::Neg => {
if is_float { let llval = if is_float {
bx.fneg(lloperand) bx.fneg(operand.immediate())
} else { } else {
bx.neg(lloperand) bx.neg(operand.immediate())
};
(OperandValue::Immediate(llval), operand.layout)
}
mir::UnOp::PtrMetadata => {
debug_assert!(operand.layout.ty.is_unsafe_ptr());
let (_, meta) = operand.val.pointer_parts();
assert_eq!(operand.layout.fields.count() > 1, meta.is_some());
if let Some(meta) = meta {
(OperandValue::Immediate(meta), operand.layout.field(self.cx, 1))
} else {
(OperandValue::ZeroSized, bx.cx().layout_of(bx.tcx().types.unit))
} }
} }
}; };
OperandRef { val: OperandValue::Immediate(llval), layout: operand.layout } debug_assert!(
val.is_expected_variant_for_type(self.cx, layout),
"Made wrong variant {val:?} for type {layout:?}",
);
OperandRef { val, layout }
} }
mir::Rvalue::Discriminant(ref place) => { mir::Rvalue::Discriminant(ref place) => {
@ -718,8 +735,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let values = op.val.immediates_or_place().left_or_else(|p| { let values = op.val.immediates_or_place().left_or_else(|p| {
bug!("Field {field_idx:?} is {p:?} making {layout:?}"); bug!("Field {field_idx:?} is {p:?} making {layout:?}");
}); });
inputs.extend(values);
let scalars = self.value_kind(op.layout).scalars().unwrap(); let scalars = self.value_kind(op.layout).scalars().unwrap();
debug_assert_eq!(values.len(), scalars.len());
inputs.extend(values);
input_scalars.extend(scalars); input_scalars.extend(scalars);
} }

View File

@ -207,7 +207,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
assert!(cast_to.ty.is_unsafe_ptr()); assert!(cast_to.ty.is_unsafe_ptr());
// Handle casting any ptr to raw ptr (might be a fat ptr). // Handle casting any ptr to raw ptr (might be a fat ptr).
if cast_to.size == src.layout.size { if cast_to.size == src.layout.size {
// Thin or fat pointer that just hast the ptr kind of target type changed. // Thin or fat pointer that just has the ptr kind of target type changed.
return Ok(ImmTy::from_immediate(**src, cast_to)); return Ok(ImmTy::from_immediate(**src, cast_to));
} else { } else {
// Casting the metadata away from a fat ptr. // Casting the metadata away from a fat ptr.

View File

@ -9,7 +9,7 @@ use rustc_middle::{bug, span_bug};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use tracing::trace; use tracing::trace;
use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine}; use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> { fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
@ -415,11 +415,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
use rustc_middle::mir::UnOp::*; use rustc_middle::mir::UnOp::*;
let layout = val.layout; let layout = val.layout;
let val = val.to_scalar();
trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty); trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty);
match layout.ty.kind() { match layout.ty.kind() {
ty::Bool => { ty::Bool => {
let val = val.to_scalar();
let val = val.to_bool()?; let val = val.to_bool()?;
let res = match un_op { let res = match un_op {
Not => !val, Not => !val,
@ -428,6 +428,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Ok(ImmTy::from_bool(res, *self.tcx)) Ok(ImmTy::from_bool(res, *self.tcx))
} }
ty::Float(fty) => { ty::Float(fty) => {
let val = val.to_scalar();
// No NaN adjustment here, `-` is a bitwise operation! // No NaN adjustment here, `-` is a bitwise operation!
let res = match (un_op, fty) { let res = match (un_op, fty) {
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?), (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
@ -436,8 +437,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}; };
Ok(ImmTy::from_scalar(res, layout)) Ok(ImmTy::from_scalar(res, layout))
} }
_ => { _ if layout.ty.is_integral() => {
assert!(layout.ty.is_integral()); let val = val.to_scalar();
let val = val.to_bits(layout.size)?; let val = val.to_bits(layout.size)?;
let res = match un_op { let res = match un_op {
Not => self.truncate(!val, layout), // bitwise negation, then truncate Not => self.truncate(!val, layout), // bitwise negation, then truncate
@ -450,9 +451,28 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Truncate to target type. // Truncate to target type.
self.truncate(res, layout) self.truncate(res, layout)
} }
_ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
}; };
Ok(ImmTy::from_uint(res, layout)) Ok(ImmTy::from_uint(res, layout))
} }
ty::RawPtr(..) => {
assert_eq!(un_op, PtrMetadata);
let (_, meta) = val.to_scalar_and_meta();
Ok(match meta {
MemPlaceMeta::Meta(scalar) => {
let ty = un_op.ty(*self.tcx, val.layout.ty);
let layout = self.layout_of(ty)?;
ImmTy::from_scalar(scalar, layout)
}
MemPlaceMeta::None => {
let unit_layout = self.layout_of(self.tcx.types.unit)?;
ImmTy::uninit(unit_layout)
}
})
}
_ => {
bug!("Unexpected unary op argument {val:?}")
}
} }
} }
} }

View File

@ -130,6 +130,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
| sym::is_val_statically_known | sym::is_val_statically_known
| sym::ptr_mask | sym::ptr_mask
| sym::aggregate_raw_ptr | sym::aggregate_raw_ptr
| sym::ptr_metadata
| sym::ub_checks | sym::ub_checks
| sym::fadd_algebraic | sym::fadd_algebraic
| sym::fsub_algebraic | sym::fsub_algebraic
@ -576,6 +577,7 @@ pub fn check_intrinsic_type(
// This type check is not particularly useful, but the `where` bounds // This type check is not particularly useful, but the `where` bounds
// on the definition in `core` do the heavy lifting for checking it. // on the definition in `core` do the heavy lifting for checking it.
sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)), sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)),
sym::ptr_metadata => (2, 1, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool), sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool),

View File

@ -1434,6 +1434,13 @@ pub enum UnOp {
Not, Not,
/// The `-` operator for negation /// The `-` operator for negation
Neg, Neg,
/// Get the metadata `M` from a `*const/mut impl Pointee<Metadata = M>`.
///
/// For example, this will give a `()` from `*const i32`, a `usize` from
/// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`.
///
/// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic.
PtrMetadata,
} }
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]

View File

@ -180,7 +180,10 @@ impl<'tcx> Rvalue<'tcx> {
let rhs_ty = rhs.ty(local_decls, tcx); let rhs_ty = rhs.ty(local_decls, tcx);
op.ty(tcx, lhs_ty, rhs_ty) op.ty(tcx, lhs_ty, rhs_ty)
} }
Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), Rvalue::UnaryOp(op, ref operand) => {
let arg_ty = operand.ty(local_decls, tcx);
op.ty(tcx, arg_ty)
}
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx), Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
tcx.types.usize tcx.types.usize
@ -282,6 +285,27 @@ impl<'tcx> BinOp {
} }
} }
impl<'tcx> UnOp {
pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
match self {
UnOp::Not | UnOp::Neg => arg_ty,
UnOp::PtrMetadata => {
let pointee_ty = arg_ty
.builtin_deref(true)
.unwrap_or_else(|| bug!("PtrMetadata of non-dereferenceable ty {arg_ty:?}"));
if pointee_ty.is_trivially_sized(tcx) {
tcx.types.unit
} else {
let Some(metadata_def_id) = tcx.lang_items().metadata_type() else {
bug!("No metadata_type lang item while looking at {arg_ty:?}")
};
Ty::new_projection(tcx, metadata_def_id, [pointee_ty])
}
}
}
}
}
impl BorrowKind { impl BorrowKind {
pub fn to_mutbl_lossy(self) -> hir::Mutability { pub fn to_mutbl_lossy(self) -> hir::Mutability {
match self { match self {

View File

@ -1579,8 +1579,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
let formatted_op = match op { let formatted_op = match op {
UnOp::Not => "!", UnOp::Not => "!",
UnOp::Neg => "-", UnOp::Neg => "-",
UnOp::PtrMetadata => "PtrMetadata",
}; };
let parenthesized = match ct.kind() { let parenthesized = match ct.kind() {
_ if op == UnOp::PtrMetadata => true,
ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op, ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op,
ty::ConstKind::Expr(_) => true, ty::ConstKind::Expr(_) => true,
_ => false, _ => false,

View File

@ -316,6 +316,23 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
terminator.kind = TerminatorKind::Goto { target }; terminator.kind = TerminatorKind::Goto { target };
} }
sym::ptr_metadata => {
let Ok([ptr]) = <[_; 1]>::try_from(std::mem::take(args)) else {
span_bug!(
terminator.source_info.span,
"Wrong number of arguments for ptr_metadata intrinsic",
);
};
let target = target.unwrap();
block.statements.push(Statement {
source_info: terminator.source_info,
kind: StatementKind::Assign(Box::new((
*destination,
Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node),
))),
});
terminator.kind = TerminatorKind::Goto { target };
}
_ => {} _ => {}
} }
} }

View File

@ -464,7 +464,7 @@ impl<'tcx> Validator<'_, 'tcx> {
Rvalue::UnaryOp(op, operand) => { Rvalue::UnaryOp(op, operand) => {
match op { match op {
// These operations can never fail. // These operations can never fail.
UnOp::Neg | UnOp::Not => {} UnOp::Neg | UnOp::Not | UnOp::PtrMetadata => {}
} }
self.validate_operand(operand)?; self.validate_operand(operand)?;

View File

@ -1109,6 +1109,16 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ty::Int(..) | ty::Uint(..) | ty::Bool ty::Int(..) | ty::Uint(..) | ty::Bool
); );
} }
UnOp::PtrMetadata => {
if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
// It would probably be fine to support this in earlier phases,
// but at the time of writing it's only ever introduced from intrinsic lowering,
// so earlier things can just `bug!` on it.
self.fail(location, "PtrMetadata should be in runtime MIR only");
}
check_kinds!(a, "Cannot PtrMetadata non-pointer type {:?}", ty::RawPtr(..));
}
} }
} }
Rvalue::ShallowInitBox(operand, _) => { Rvalue::ShallowInitBox(operand, _) => {

View File

@ -10,7 +10,7 @@ use rustc_span::Symbol;
use stable_mir::abi::Layout; use stable_mir::abi::Layout;
use stable_mir::mir::alloc::AllocId; use stable_mir::mir::alloc::AllocId;
use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef};
use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety}; use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety, UnOp};
use stable_mir::ty::{ use stable_mir::ty::{
Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const,
DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig,
@ -582,6 +582,18 @@ impl RustcInternal for BinOp {
} }
} }
impl RustcInternal for UnOp {
type T<'tcx> = rustc_middle::mir::UnOp;
fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
match self {
UnOp::Not => rustc_middle::mir::UnOp::Not,
UnOp::Neg => rustc_middle::mir::UnOp::Neg,
UnOp::PtrMetadata => rustc_middle::mir::UnOp::PtrMetadata,
}
}
}
impl<T> RustcInternal for &T impl<T> RustcInternal for &T
where where
T: RustcInternal, T: RustcInternal,

View File

@ -19,7 +19,7 @@ use stable_mir::abi::{FnAbi, Layout, LayoutShape};
use stable_mir::compiler_interface::Context; use stable_mir::compiler_interface::Context;
use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::alloc::GlobalAlloc;
use stable_mir::mir::mono::{InstanceDef, StaticDef}; use stable_mir::mir::mono::{InstanceDef, StaticDef};
use stable_mir::mir::{BinOp, Body, Place}; use stable_mir::mir::{BinOp, Body, Place, UnOp};
use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::target::{MachineInfo, MachineSize};
use stable_mir::ty::{ use stable_mir::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
@ -700,6 +700,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal); let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal);
ty.stable(&mut *tables) ty.stable(&mut *tables)
} }
fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
let arg_internal = arg.internal(&mut *tables, tcx);
let ty = un_op.internal(&mut *tables, tcx).ty(tcx, arg_internal);
ty.stable(&mut *tables)
}
} }
pub struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>); pub struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>);

View File

@ -526,6 +526,7 @@ impl<'tcx> Stable<'tcx> for mir::UnOp {
match self { match self {
UnOp::Not => stable_mir::mir::UnOp::Not, UnOp::Not => stable_mir::mir::UnOp::Not,
UnOp::Neg => stable_mir::mir::UnOp::Neg, UnOp::Neg => stable_mir::mir::UnOp::Neg,
UnOp::PtrMetadata => stable_mir::mir::UnOp::PtrMetadata,
} }
} }
} }

View File

@ -1433,6 +1433,7 @@ symbols! {
ptr_guaranteed_cmp, ptr_guaranteed_cmp,
ptr_is_null, ptr_is_null,
ptr_mask, ptr_mask,
ptr_metadata,
ptr_null, ptr_null,
ptr_null_mut, ptr_null_mut,
ptr_offset_from, ptr_offset_from,

View File

@ -94,7 +94,7 @@ fn check_binop(op: mir::BinOp) -> bool {
fn check_unop(op: mir::UnOp) -> bool { fn check_unop(op: mir::UnOp) -> bool {
use mir::UnOp::*; use mir::UnOp::*;
match op { match op {
Not | Neg => true, Not | Neg | PtrMetadata => true,
} }
} }

View File

@ -8,7 +8,7 @@ use std::cell::Cell;
use crate::abi::{FnAbi, Layout, LayoutShape}; use crate::abi::{FnAbi, Layout, LayoutShape};
use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::alloc::{AllocId, GlobalAlloc};
use crate::mir::mono::{Instance, InstanceDef, StaticDef}; use crate::mir::mono::{Instance, InstanceDef, StaticDef};
use crate::mir::{BinOp, Body, Place}; use crate::mir::{BinOp, Body, Place, UnOp};
use crate::target::MachineInfo; use crate::target::MachineInfo;
use crate::ty::{ use crate::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
@ -226,6 +226,9 @@ pub trait Context {
/// Get the resulting type of binary operation. /// Get the resulting type of binary operation.
fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty;
/// Get the resulting type of unary operation.
fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty;
} }
// A thread local variable that stores a pointer to the tables mapping between TyCtxt // A thread local variable that stores a pointer to the tables mapping between TyCtxt

View File

@ -346,6 +346,15 @@ impl BinOp {
pub enum UnOp { pub enum UnOp {
Not, Not,
Neg, Neg,
PtrMetadata,
}
impl UnOp {
/// Return the type of this operation for the given input Ty.
/// This function does not perform type checking, and it currently doesn't handle SIMD.
pub fn ty(&self, arg_ty: Ty) -> Ty {
with(|ctx| ctx.unop_ty(*self, arg_ty))
}
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
@ -580,7 +589,10 @@ impl Rvalue {
let ty = op.ty(lhs_ty, rhs_ty); let ty = op.ty(lhs_ty, rhs_ty);
Ok(Ty::new_tuple(&[ty, Ty::bool_ty()])) Ok(Ty::new_tuple(&[ty, Ty::bool_ty()]))
} }
Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, operand) => operand.ty(locals), Rvalue::UnaryOp(op, operand) => {
let arg_ty = operand.ty(locals)?;
Ok(op.ty(arg_ty))
}
Rvalue::Discriminant(place) => { Rvalue::Discriminant(place) => {
let place_ty = place.ty(locals)?; let place_ty = place.ty(locals)?;
place_ty place_ty

View File

@ -2821,6 +2821,21 @@ impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
type Metadata = <P as ptr::Pointee>::Metadata; type Metadata = <P as ptr::Pointee>::Metadata;
} }
/// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`.
///
/// This is used to implement functions like `ptr::metadata`.
#[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))]
pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *const P) -> M {
// To implement a fallback we'd have to assume the layout of the pointer,
// but the whole point of this intrinsic is that we shouldn't do that.
unreachable!()
}
// Some functions are defined here because they accidentally got made // Some functions are defined here because they accidentally got made
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>. // available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
// (`transmute` also falls into this category, but it cannot be wrapped due to the // (`transmute` also falls into this category, but it cannot be wrapped due to the

View File

@ -3,6 +3,8 @@
use crate::fmt; use crate::fmt;
use crate::hash::{Hash, Hasher}; use crate::hash::{Hash, Hasher};
use crate::intrinsics::aggregate_raw_ptr; use crate::intrinsics::aggregate_raw_ptr;
#[cfg(not(bootstrap))]
use crate::intrinsics::ptr_metadata;
use crate::marker::Freeze; use crate::marker::Freeze;
/// Provides the pointer metadata type of any pointed-to type. /// Provides the pointer metadata type of any pointed-to type.
@ -94,10 +96,17 @@ pub trait Thin = Pointee<Metadata = ()>;
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline] #[inline]
pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata { pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T #[cfg(bootstrap)]
// and PtrComponents<T> have the same memory layouts. Only std can make this {
// guarantee. // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
unsafe { PtrRepr { const_ptr: ptr }.components.metadata } // and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
}
#[cfg(not(bootstrap))]
{
ptr_metadata(ptr)
}
} }
/// Forms a (possibly-wide) raw pointer from a data pointer and metadata. /// Forms a (possibly-wide) raw pointer from a data pointer and metadata.
@ -132,6 +141,7 @@ pub const fn from_raw_parts_mut<T: ?Sized>(
} }
#[repr(C)] #[repr(C)]
#[cfg(bootstrap)]
union PtrRepr<T: ?Sized> { union PtrRepr<T: ?Sized> {
const_ptr: *const T, const_ptr: *const T,
mut_ptr: *mut T, mut_ptr: *mut T,
@ -139,15 +149,18 @@ union PtrRepr<T: ?Sized> {
} }
#[repr(C)] #[repr(C)]
#[cfg(bootstrap)]
struct PtrComponents<T: ?Sized> { struct PtrComponents<T: ?Sized> {
data_pointer: *const (), data_pointer: *const (),
metadata: <T as Pointee>::Metadata, metadata: <T as Pointee>::Metadata,
} }
// Manual impl needed to avoid `T: Copy` bound. // Manual impl needed to avoid `T: Copy` bound.
#[cfg(bootstrap)]
impl<T: ?Sized> Copy for PtrComponents<T> {} impl<T: ?Sized> Copy for PtrComponents<T> {}
// Manual impl needed to avoid `T: Clone` bound. // Manual impl needed to avoid `T: Clone` bound.
#[cfg(bootstrap)]
impl<T: ?Sized> Clone for PtrComponents<T> { impl<T: ?Sized> Clone for PtrComponents<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
*self *self

View File

@ -1171,3 +1171,15 @@ fn test_ptr_from_raw_parts_in_const() {
assert_eq!(EMPTY_SLICE_PTR.addr(), 123); assert_eq!(EMPTY_SLICE_PTR.addr(), 123);
assert_eq!(EMPTY_SLICE_PTR.len(), 456); assert_eq!(EMPTY_SLICE_PTR.len(), 456);
} }
#[test]
fn test_ptr_metadata_in_const() {
use std::fmt::Debug;
const ARRAY_META: () = std::ptr::metadata::<[u16; 3]>(&[1, 2, 3]);
const SLICE_META: usize = std::ptr::metadata::<[u16]>(&[1, 2, 3]);
const DYN_META: DynMetadata<dyn Debug> = std::ptr::metadata::<dyn Debug>(&[0_u8; 42]);
assert_eq!(ARRAY_META, ());
assert_eq!(SLICE_META, 3);
assert_eq!(DYN_META.size_of(), 42);
}

View File

@ -0,0 +1,36 @@
//@ compile-flags: -O -C no-prepopulate-passes -Z inline-mir
//@ only-64bit (so I don't need to worry about usize)
#![crate_type = "lib"]
#![feature(core_intrinsics)]
use std::intrinsics::ptr_metadata;
// CHECK-LABEL: @thin_metadata(
#[no_mangle]
pub fn thin_metadata(p: *const ()) {
// CHECK: start
// CHECK-NEXT: ret void
ptr_metadata(p)
}
// CHECK-LABEL: @slice_metadata(
#[no_mangle]
pub fn slice_metadata(p: *const [u8]) -> usize {
// CHECK: start
// CHECK-NEXT: ret i64 %p.1
ptr_metadata(p)
}
// CHECK-LABEL: @dyn_byte_offset(
#[no_mangle]
pub unsafe fn dyn_byte_offset(
p: *const dyn std::fmt::Debug,
n: usize,
) -> *const dyn std::fmt::Debug {
// CHECK: %[[Q:.+]] = getelementptr inbounds i8, ptr %p.0, i64 %n
// CHECK: %[[TEMP1:.+]] = insertvalue { ptr, ptr } poison, ptr %[[Q]], 0
// CHECK: %[[TEMP2:.+]] = insertvalue { ptr, ptr } %[[TEMP1]], ptr %p.1, 1
// CHECK: ret { ptr, ptr } %[[TEMP2]]
p.byte_add(n)
}

View File

@ -0,0 +1,63 @@
- // MIR for `get_metadata` before LowerIntrinsics
+ // MIR for `get_metadata` after LowerIntrinsics
fn get_metadata(_1: *const i32, _2: *const [u8], _3: *const dyn Debug) -> () {
debug a => _1;
debug b => _2;
debug c => _3;
let mut _0: ();
let _4: ();
let mut _5: *const i32;
let mut _7: *const [u8];
let mut _9: *const dyn std::fmt::Debug;
scope 1 {
debug _unit => _4;
let _6: usize;
scope 2 {
debug _usize => _6;
let _8: std::ptr::DynMetadata<dyn std::fmt::Debug>;
scope 3 {
debug _vtable => _8;
}
}
}
bb0: {
StorageLive(_4);
StorageLive(_5);
_5 = _1;
- _4 = ptr_metadata::<i32, ()>(move _5) -> [return: bb1, unwind unreachable];
+ _4 = PtrMetadata(move _5);
+ goto -> bb1;
}
bb1: {
StorageDead(_5);
StorageLive(_6);
StorageLive(_7);
_7 = _2;
- _6 = ptr_metadata::<[u8], usize>(move _7) -> [return: bb2, unwind unreachable];
+ _6 = PtrMetadata(move _7);
+ goto -> bb2;
}
bb2: {
StorageDead(_7);
StorageLive(_8);
StorageLive(_9);
_9 = _3;
- _8 = ptr_metadata::<dyn Debug, DynMetadata<dyn Debug>>(move _9) -> [return: bb3, unwind unreachable];
+ _8 = PtrMetadata(move _9);
+ goto -> bb3;
}
bb3: {
StorageDead(_9);
_0 = const ();
StorageDead(_8);
StorageDead(_6);
StorageDead(_4);
return;
}
}

View File

@ -0,0 +1,63 @@
- // MIR for `get_metadata` before LowerIntrinsics
+ // MIR for `get_metadata` after LowerIntrinsics
fn get_metadata(_1: *const i32, _2: *const [u8], _3: *const dyn Debug) -> () {
debug a => _1;
debug b => _2;
debug c => _3;
let mut _0: ();
let _4: ();
let mut _5: *const i32;
let mut _7: *const [u8];
let mut _9: *const dyn std::fmt::Debug;
scope 1 {
debug _unit => _4;
let _6: usize;
scope 2 {
debug _usize => _6;
let _8: std::ptr::DynMetadata<dyn std::fmt::Debug>;
scope 3 {
debug _vtable => _8;
}
}
}
bb0: {
StorageLive(_4);
StorageLive(_5);
_5 = _1;
- _4 = ptr_metadata::<i32, ()>(move _5) -> [return: bb1, unwind unreachable];
+ _4 = PtrMetadata(move _5);
+ goto -> bb1;
}
bb1: {
StorageDead(_5);
StorageLive(_6);
StorageLive(_7);
_7 = _2;
- _6 = ptr_metadata::<[u8], usize>(move _7) -> [return: bb2, unwind unreachable];
+ _6 = PtrMetadata(move _7);
+ goto -> bb2;
}
bb2: {
StorageDead(_7);
StorageLive(_8);
StorageLive(_9);
_9 = _3;
- _8 = ptr_metadata::<dyn Debug, DynMetadata<dyn Debug>>(move _9) -> [return: bb3, unwind unreachable];
+ _8 = PtrMetadata(move _9);
+ goto -> bb3;
}
bb3: {
StorageDead(_9);
_0 = const ();
StorageDead(_8);
StorageDead(_6);
StorageDead(_4);
return;
}
}

View File

@ -258,3 +258,12 @@ pub fn make_pointers(a: *const u8, b: *mut (), n: usize) {
let _slice_const: *const [u16] = aggregate_raw_ptr(a, n); let _slice_const: *const [u16] = aggregate_raw_ptr(a, n);
let _slice_mut: *mut [u64] = aggregate_raw_ptr(b, n); let _slice_mut: *mut [u64] = aggregate_raw_ptr(b, n);
} }
// EMIT_MIR lower_intrinsics.get_metadata.LowerIntrinsics.diff
pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug) {
use std::intrinsics::ptr_metadata;
let _unit = ptr_metadata(a);
let _usize = ptr_metadata(b);
let _vtable = ptr_metadata(c);
}

View File

@ -13,9 +13,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
} }
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) { scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
let mut _5: *const (); let mut _5: *const ();
let mut _7: usize; let mut _6: usize;
scope 5 (inlined std::ptr::metadata::<[u32]>) { scope 5 (inlined std::ptr::metadata::<[u32]>) {
let mut _6: std::ptr::metadata::PtrRepr<[u32]>;
} }
scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) {
} }
@ -30,13 +29,10 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
StorageDead(_3); StorageDead(_3);
StorageLive(_5); StorageLive(_5);
_5 = _4 as *const () (PtrToPtr); _5 = _4 as *const () (PtrToPtr);
StorageLive(_7);
StorageLive(_6); StorageLive(_6);
_6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; _6 = PtrMetadata(_1);
_7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); _0 = *const [u32] from (_5, _6);
StorageDead(_6); StorageDead(_6);
_0 = *const [u32] from (_5, _7);
StorageDead(_7);
StorageDead(_5); StorageDead(_5);
StorageDead(_4); StorageDead(_4);
return; return;

View File

@ -13,9 +13,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
} }
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) { scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
let mut _5: *const (); let mut _5: *const ();
let mut _7: usize; let mut _6: usize;
scope 5 (inlined std::ptr::metadata::<[u32]>) { scope 5 (inlined std::ptr::metadata::<[u32]>) {
let mut _6: std::ptr::metadata::PtrRepr<[u32]>;
} }
scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) {
} }
@ -30,13 +29,10 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
StorageDead(_3); StorageDead(_3);
StorageLive(_5); StorageLive(_5);
_5 = _4 as *const () (PtrToPtr); _5 = _4 as *const () (PtrToPtr);
StorageLive(_7);
StorageLive(_6); StorageLive(_6);
_6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; _6 = PtrMetadata(_1);
_7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); _0 = *const [u32] from (_5, _6);
StorageDead(_6); StorageDead(_6);
_0 = *const [u32] from (_5, _7);
StorageDead(_7);
StorageDead(_5); StorageDead(_5);
StorageDead(_4); StorageDead(_4);
return; return;