Skip to content

Commit 2d3b1e0

Browse files
authored
Rollup merge of #124251 - scottmcm:unop-ptr-metadata, r=oli-obk
Add an intrinsic for `ptr::metadata` The follow-up to #123840, so we can remove `PtrComponents` and `PtrRepr` from libcore entirely (well, after a bootstrap update). As discussed in <https://rust-lang.zulipchat.com/#narrow/stream/189540-t-compiler.2Fwg-mir-opt/topic/.60ptr_metadata.60.20in.20MIR/near/435637808>, this introduces `UnOp::PtrMetadata` taking a raw pointer and returning the associated metadata value. By no longer going through a `union`, this should also help future PRs better optimize pointer operations. r? ``@oli-obk``
2 parents da159eb + 57948c8 commit 2d3b1e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+600
-53
lines changed

compiler/rustc_codegen_cranelift/src/base.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -616,22 +616,34 @@ fn codegen_stmt<'tcx>(
616616
Rvalue::UnaryOp(un_op, ref operand) => {
617617
let operand = codegen_operand(fx, operand);
618618
let layout = operand.layout();
619-
let val = operand.load_scalar(fx);
620619
let res = match un_op {
621-
UnOp::Not => match layout.ty.kind() {
622-
ty::Bool => {
623-
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0);
624-
CValue::by_val(res, layout)
620+
UnOp::Not => {
621+
let val = operand.load_scalar(fx);
622+
match layout.ty.kind() {
623+
ty::Bool => {
624+
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0);
625+
CValue::by_val(res, layout)
626+
}
627+
ty::Uint(_) | ty::Int(_) => {
628+
CValue::by_val(fx.bcx.ins().bnot(val), layout)
629+
}
630+
_ => unreachable!("un op Not for {:?}", layout.ty),
625631
}
626-
ty::Uint(_) | ty::Int(_) => {
627-
CValue::by_val(fx.bcx.ins().bnot(val), layout)
632+
}
633+
UnOp::Neg => {
634+
let val = operand.load_scalar(fx);
635+
match layout.ty.kind() {
636+
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout),
637+
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout),
638+
_ => unreachable!("un op Neg for {:?}", layout.ty),
628639
}
629-
_ => unreachable!("un op Not for {:?}", layout.ty),
630-
},
631-
UnOp::Neg => match layout.ty.kind() {
632-
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout),
633-
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout),
634-
_ => unreachable!("un op Neg for {:?}", layout.ty),
640+
}
641+
UnOp::PtrMetadata => match layout.abi {
642+
Abi::Scalar(_) => CValue::zst(dest_layout),
643+
Abi::ScalarPair(_, _) => {
644+
CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout)
645+
}
646+
_ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"),
635647
},
636648
};
637649
lval.write_cvalue(fx, res);

compiler/rustc_codegen_cranelift/src/constant.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub(crate) fn codegen_const_value<'tcx>(
100100
assert!(layout.is_sized(), "unsized const value");
101101

102102
if layout.is_zst() {
103-
return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout);
103+
return CValue::zst(layout);
104104
}
105105

106106
match const_val {

compiler/rustc_codegen_cranelift/src/value_and_place.rs

+8
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ impl<'tcx> CValue<'tcx> {
9595
CValue(CValueInner::ByValPair(value, extra), layout)
9696
}
9797

98+
/// Create an instance of a ZST
99+
///
100+
/// The is represented by a dangling pointer of suitable alignment.
101+
pub(crate) fn zst(layout: TyAndLayout<'tcx>) -> CValue<'tcx> {
102+
assert!(layout.is_zst());
103+
CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout)
104+
}
105+
98106
pub(crate) fn layout(&self) -> TyAndLayout<'tcx> {
99107
self.1
100108
}

compiler/rustc_codegen_ssa/src/mir/operand.rs

+5
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
565565
for elem in place_ref.projection.iter() {
566566
match elem {
567567
mir::ProjectionElem::Field(ref f, _) => {
568+
debug_assert!(
569+
!o.layout.ty.is_any_ptr(),
570+
"Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
571+
but tried to access field {f:?} of pointer {o:?}",
572+
);
568573
o = o.extract_field(bx, f.index());
569574
}
570575
mir::ProjectionElem::Index(_)

compiler/rustc_codegen_ssa/src/mir/place.rs

+5
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
480480
cg_base = match *elem {
481481
mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()),
482482
mir::ProjectionElem::Field(ref field, _) => {
483+
debug_assert!(
484+
!cg_base.layout.ty.is_any_ptr(),
485+
"Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
486+
but tried to access field {field:?} of pointer {cg_base:?}",
487+
);
483488
cg_base.project_field(bx, field.index())
484489
}
485490
mir::ProjectionElem::OpaqueCast(ty) => {

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -623,19 +623,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
623623

624624
mir::Rvalue::UnaryOp(op, ref operand) => {
625625
let operand = self.codegen_operand(bx, operand);
626-
let lloperand = operand.immediate();
627626
let is_float = operand.layout.ty.is_floating_point();
628-
let llval = match op {
629-
mir::UnOp::Not => bx.not(lloperand),
627+
let (val, layout) = match op {
628+
mir::UnOp::Not => {
629+
let llval = bx.not(operand.immediate());
630+
(OperandValue::Immediate(llval), operand.layout)
631+
}
630632
mir::UnOp::Neg => {
631-
if is_float {
632-
bx.fneg(lloperand)
633+
let llval = if is_float {
634+
bx.fneg(operand.immediate())
635+
} else {
636+
bx.neg(operand.immediate())
637+
};
638+
(OperandValue::Immediate(llval), operand.layout)
639+
}
640+
mir::UnOp::PtrMetadata => {
641+
debug_assert!(operand.layout.ty.is_unsafe_ptr());
642+
let (_, meta) = operand.val.pointer_parts();
643+
assert_eq!(operand.layout.fields.count() > 1, meta.is_some());
644+
if let Some(meta) = meta {
645+
(OperandValue::Immediate(meta), operand.layout.field(self.cx, 1))
633646
} else {
634-
bx.neg(lloperand)
647+
(OperandValue::ZeroSized, bx.cx().layout_of(bx.tcx().types.unit))
635648
}
636649
}
637650
};
638-
OperandRef { val: OperandValue::Immediate(llval), layout: operand.layout }
651+
debug_assert!(
652+
val.is_expected_variant_for_type(self.cx, layout),
653+
"Made wrong variant {val:?} for type {layout:?}",
654+
);
655+
OperandRef { val, layout }
639656
}
640657

641658
mir::Rvalue::Discriminant(ref place) => {
@@ -718,8 +735,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
718735
let values = op.val.immediates_or_place().left_or_else(|p| {
719736
bug!("Field {field_idx:?} is {p:?} making {layout:?}");
720737
});
721-
inputs.extend(values);
722738
let scalars = self.value_kind(op.layout).scalars().unwrap();
739+
debug_assert_eq!(values.len(), scalars.len());
740+
inputs.extend(values);
723741
input_scalars.extend(scalars);
724742
}
725743

compiler/rustc_const_eval/src/interpret/cast.rs

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

compiler/rustc_const_eval/src/interpret/operator.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_middle::{bug, span_bug};
99
use rustc_span::symbol::sym;
1010
use tracing::trace;
1111

12-
use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine};
12+
use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
1313

1414
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
1515
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> {
415415
use rustc_middle::mir::UnOp::*;
416416

417417
let layout = val.layout;
418-
let val = val.to_scalar();
419418
trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty);
420419

421420
match layout.ty.kind() {
422421
ty::Bool => {
422+
let val = val.to_scalar();
423423
let val = val.to_bool()?;
424424
let res = match un_op {
425425
Not => !val,
@@ -428,6 +428,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
428428
Ok(ImmTy::from_bool(res, *self.tcx))
429429
}
430430
ty::Float(fty) => {
431+
let val = val.to_scalar();
431432
// No NaN adjustment here, `-` is a bitwise operation!
432433
let res = match (un_op, fty) {
433434
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
@@ -436,8 +437,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
436437
};
437438
Ok(ImmTy::from_scalar(res, layout))
438439
}
439-
_ => {
440-
assert!(layout.ty.is_integral());
440+
_ if layout.ty.is_integral() => {
441+
let val = val.to_scalar();
441442
let val = val.to_bits(layout.size)?;
442443
let res = match un_op {
443444
Not => self.truncate(!val, layout), // bitwise negation, then truncate
@@ -450,9 +451,28 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
450451
// Truncate to target type.
451452
self.truncate(res, layout)
452453
}
454+
_ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
453455
};
454456
Ok(ImmTy::from_uint(res, layout))
455457
}
458+
ty::RawPtr(..) => {
459+
assert_eq!(un_op, PtrMetadata);
460+
let (_, meta) = val.to_scalar_and_meta();
461+
Ok(match meta {
462+
MemPlaceMeta::Meta(scalar) => {
463+
let ty = un_op.ty(*self.tcx, val.layout.ty);
464+
let layout = self.layout_of(ty)?;
465+
ImmTy::from_scalar(scalar, layout)
466+
}
467+
MemPlaceMeta::None => {
468+
let unit_layout = self.layout_of(self.tcx.types.unit)?;
469+
ImmTy::uninit(unit_layout)
470+
}
471+
})
472+
}
473+
_ => {
474+
bug!("Unexpected unary op argument {val:?}")
475+
}
456476
}
457477
}
458478
}

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
130130
| sym::is_val_statically_known
131131
| sym::ptr_mask
132132
| sym::aggregate_raw_ptr
133+
| sym::ptr_metadata
133134
| sym::ub_checks
134135
| sym::fadd_algebraic
135136
| sym::fsub_algebraic
@@ -576,6 +577,7 @@ pub fn check_intrinsic_type(
576577
// This type check is not particularly useful, but the `where` bounds
577578
// on the definition in `core` do the heavy lifting for checking it.
578579
sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)),
580+
sym::ptr_metadata => (2, 1, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
579581

580582
sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool),
581583

compiler/rustc_middle/src/mir/syntax.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,13 @@ pub enum UnOp {
14341434
Not,
14351435
/// The `-` operator for negation
14361436
Neg,
1437+
/// Get the metadata `M` from a `*const/mut impl Pointee<Metadata = M>`.
1438+
///
1439+
/// For example, this will give a `()` from `*const i32`, a `usize` from
1440+
/// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`.
1441+
///
1442+
/// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic.
1443+
PtrMetadata,
14371444
}
14381445

14391446
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]

compiler/rustc_middle/src/mir/tcx.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,10 @@ impl<'tcx> Rvalue<'tcx> {
180180
let rhs_ty = rhs.ty(local_decls, tcx);
181181
op.ty(tcx, lhs_ty, rhs_ty)
182182
}
183-
Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx),
183+
Rvalue::UnaryOp(op, ref operand) => {
184+
let arg_ty = operand.ty(local_decls, tcx);
185+
op.ty(tcx, arg_ty)
186+
}
184187
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
185188
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
186189
tcx.types.usize
@@ -282,6 +285,27 @@ impl<'tcx> BinOp {
282285
}
283286
}
284287

288+
impl<'tcx> UnOp {
289+
pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
290+
match self {
291+
UnOp::Not | UnOp::Neg => arg_ty,
292+
UnOp::PtrMetadata => {
293+
let pointee_ty = arg_ty
294+
.builtin_deref(true)
295+
.unwrap_or_else(|| bug!("PtrMetadata of non-dereferenceable ty {arg_ty:?}"));
296+
if pointee_ty.is_trivially_sized(tcx) {
297+
tcx.types.unit
298+
} else {
299+
let Some(metadata_def_id) = tcx.lang_items().metadata_type() else {
300+
bug!("No metadata_type lang item while looking at {arg_ty:?}")
301+
};
302+
Ty::new_projection(tcx, metadata_def_id, [pointee_ty])
303+
}
304+
}
305+
}
306+
}
307+
}
308+
285309
impl BorrowKind {
286310
pub fn to_mutbl_lossy(self) -> hir::Mutability {
287311
match self {

compiler/rustc_middle/src/ty/print/pretty.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1579,8 +1579,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
15791579
let formatted_op = match op {
15801580
UnOp::Not => "!",
15811581
UnOp::Neg => "-",
1582+
UnOp::PtrMetadata => "PtrMetadata",
15821583
};
15831584
let parenthesized = match ct.kind() {
1585+
_ if op == UnOp::PtrMetadata => true,
15841586
ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op,
15851587
ty::ConstKind::Expr(_) => true,
15861588
_ => false,

compiler/rustc_mir_build/src/build/custom/parse/instruction.rs

+1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
212212
Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset))))
213213
},
214214
@call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)),
215+
@call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)),
215216
@call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)),
216217
ExprKind::Borrow { borrow_kind, arg } => Ok(
217218
Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)

compiler/rustc_mir_transform/src/lower_intrinsics.rs

+17
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,23 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
316316

317317
terminator.kind = TerminatorKind::Goto { target };
318318
}
319+
sym::ptr_metadata => {
320+
let Ok([ptr]) = <[_; 1]>::try_from(std::mem::take(args)) else {
321+
span_bug!(
322+
terminator.source_info.span,
323+
"Wrong number of arguments for ptr_metadata intrinsic",
324+
);
325+
};
326+
let target = target.unwrap();
327+
block.statements.push(Statement {
328+
source_info: terminator.source_info,
329+
kind: StatementKind::Assign(Box::new((
330+
*destination,
331+
Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node),
332+
))),
333+
});
334+
terminator.kind = TerminatorKind::Goto { target };
335+
}
319336
_ => {}
320337
}
321338
}

compiler/rustc_mir_transform/src/promote_consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ impl<'tcx> Validator<'_, 'tcx> {
464464
Rvalue::UnaryOp(op, operand) => {
465465
match op {
466466
// These operations can never fail.
467-
UnOp::Neg | UnOp::Not => {}
467+
UnOp::Neg | UnOp::Not | UnOp::PtrMetadata => {}
468468
}
469469

470470
self.validate_operand(operand)?;

compiler/rustc_mir_transform/src/validate.rs

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

compiler/rustc_smir/src/rustc_internal/internal.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_span::Symbol;
1010
use stable_mir::abi::Layout;
1111
use stable_mir::mir::alloc::AllocId;
1212
use stable_mir::mir::mono::{Instance, MonoItem, StaticDef};
13-
use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety};
13+
use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety, UnOp};
1414
use stable_mir::ty::{
1515
Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const,
1616
DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig,
@@ -582,6 +582,18 @@ impl RustcInternal for BinOp {
582582
}
583583
}
584584

585+
impl RustcInternal for UnOp {
586+
type T<'tcx> = rustc_middle::mir::UnOp;
587+
588+
fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
589+
match self {
590+
UnOp::Not => rustc_middle::mir::UnOp::Not,
591+
UnOp::Neg => rustc_middle::mir::UnOp::Neg,
592+
UnOp::PtrMetadata => rustc_middle::mir::UnOp::PtrMetadata,
593+
}
594+
}
595+
}
596+
585597
impl<T> RustcInternal for &T
586598
where
587599
T: RustcInternal,

0 commit comments

Comments
 (0)