Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pointer masking convenience functions #96946

Merged
merged 10 commits into from
Aug 28, 2022
7 changes: 7 additions & 0 deletions compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,13 @@ fn codegen_regular_intrinsic_call<'tcx>(
ret.write_cvalue(fx, CValue::by_val(res, base.layout()));
}

sym::ptr_mask => {
intrinsic_args!(fx, args => (ptr, mask); intrinsic);
let ptr = ptr.load_scalar(fx);
let mask = mask.load_scalar(fx);
fx.bcx.ins().band(ptr, mask);
WaffleLapkin marked this conversation as resolved.
Show resolved Hide resolved
}

sym::transmute => {
intrinsic_args!(fx, args => (from); intrinsic);

Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,18 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
return;
}

sym::ptr_mask => {
let usize_type = self.context.new_type::<usize>();
let void_ptr_type = self.context.new_type::<*const ()>();

let ptr = args[0].immediate();
let mask = args[1].immediate();

let addr = self.bitcast(ptr, usize_type);
let masked = self.and(addr, mask);
self.bitcast(masked, void_ptr_type)
},

_ if name_str.starts_with("simd_") => {
match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
Ok(llval) => llval,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,9 @@ impl<'ll> CodegenCx<'ll, '_> {
ifn!("llvm.dbg.declare", fn(t_metadata, t_metadata) -> void);
ifn!("llvm.dbg.value", fn(t_metadata, t_i64, t_metadata) -> void);
}

ifn!("llvm.ptrmask", fn(i8p, t_isize) -> i8p);

None
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ fn get_simple_intrinsic<'ll>(
sym::nearbyintf64 => "llvm.nearbyint.f64",
sym::roundf32 => "llvm.round.f32",
sym::roundf64 => "llvm.round.f64",
sym::ptr_mask => "llvm.ptrmask",
WaffleLapkin marked this conversation as resolved.
Show resolved Hide resolved
_ => return None,
};
Some(cx.get_intrinsic(llvm_name))
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,7 @@ symbols! {
ptr,
ptr_guaranteed_eq,
ptr_guaranteed_ne,
ptr_mask,
ptr_null,
ptr_null_mut,
ptr_offset_from,
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_typeck/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
| sym::type_name
| sym::forget
| sym::black_box
| sym::variant_count => hir::Unsafety::Normal,
| sym::variant_count
| sym::ptr_mask => hir::Unsafety::Normal,
_ => hir::Unsafety::Unsafe,
}
}
Expand Down Expand Up @@ -203,6 +204,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
],
tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
),
sym::ptr_mask => (
1,
vec![
tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
tcx.types.usize,
],
tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
),

sym::copy | sym::copy_nonoverlapping => (
1,
vec![
Expand Down
11 changes: 11 additions & 0 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,17 @@ extern "rust-intrinsic" {
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;

/// Masks out bits of the pointer according to a mask.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
Comment on lines +1313 to +1316
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understood llvm docs correctly, ptrmask is equivalent to just GEP, not GEP in bounds, so this is safe.

///
/// Consider using [`pointer::mask`] instead.
#[cfg(not(bootstrap))]
pub fn ptr_mask<T>(ptr: *const T, mask: usize) -> *const T;

/// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with
/// a size of `count` * `size_of::<T>()` and an alignment of
/// `min_align_of::<T>()`
Expand Down
15 changes: 15 additions & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,21 @@ impl<T: ?Sized> *const T {
from_raw_parts::<T>(self.cast::<u8>().wrapping_offset(count).cast::<()>(), metadata(self))
}

/// Masks out bits of the pointer according to a mask.
///
/// This is convenience for `ptr.map_addr(|a| a & mask)`.
///
/// For non-`Sized` pointees this operation changes only the data pointer,
/// leaving the metadata untouched.
#[cfg(not(bootstrap))]
#[unstable(feature = "ptr_mask", issue = "98290")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[inline(always)]
pub fn mask(self, mask: usize) -> *const T {
let this = intrinsics::ptr_mask(self.cast::<()>(), mask);
from_raw_parts::<T>(this, metadata(self))
}

/// Calculates the distance between two pointers. The returned value is in
/// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
///
Expand Down
15 changes: 15 additions & 0 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,21 @@ impl<T: ?Sized> *mut T {
)
}

/// Masks out bits of the pointer according to a mask.
///
/// This is convenience for `ptr.map_addr(|a| a & mask)`.
///
/// For non-`Sized` pointees this operation changes only the data pointer,
/// leaving the metadata untouched.
#[cfg(not(bootstrap))]
#[unstable(feature = "ptr_mask", issue = "98290")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[inline(always)]
pub fn mask(self, mask: usize) -> *mut T {
let this = intrinsics::ptr_mask(self.cast::<()>(), mask) as *mut ();
from_raw_parts_mut::<T>(this, metadata(self))
}

/// Returns `None` if the pointer is null, or else returns a unique reference to
/// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_mut`]
/// must be used instead.
Expand Down
11 changes: 11 additions & 0 deletions src/test/codegen/intrinsics/mask.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![crate_type = "lib"]
#![feature(core_intrinsics)]

// CHECK-LABEL: @mask_ptr
// CHECK-SAME: [[WORD:i[0-9]+]] %mask
#[no_mangle]
pub fn mask_ptr(ptr: *const u16, mask: usize) -> *const u16 {
// CHECK: call
// CHECK-SAME: @llvm.ptrmask.{{p0|p0i8}}.[[WORD]]({{ptr|i8\*}} {{%ptr|%0}}, [[WORD]] %mask)
core::intrinsics::ptr_mask(ptr, mask)
}