Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
| sym::rotate_left
| sym::rotate_right
| sym::saturating_add
| sym::saturating_sub => {
| sym::saturating_sub
| sym::unchecked_funnel_shl
| sym::unchecked_funnel_shr => {
let ty = args[0].layout.ty;
if !ty.is_integral() {
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
Expand Down Expand Up @@ -424,18 +426,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
sym::bitreverse => {
self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()])
}
sym::rotate_left | sym::rotate_right => {
let is_left = name == sym::rotate_left;
let val = args[0].immediate();
let raw_shift = args[1].immediate();
// rotate = funnel shift with first two args the same
sym::rotate_left
| sym::rotate_right
| sym::unchecked_funnel_shl
| sym::unchecked_funnel_shr => {
let is_left = name == sym::rotate_left || name == sym::unchecked_funnel_shl;
let lhs = args[0].immediate();
let (rhs, raw_shift) =
if name == sym::rotate_left || name == sym::rotate_right {
// rotate = funnel shift with first two args the same
(lhs, args[1].immediate())
} else {
(args[1].immediate(), args[2].immediate())
};
let llvm_name = format!("llvm.fsh{}", if is_left { 'l' } else { 'r' });

// llvm expects shift to be the same type as the values, but rust
// always uses `u32`.
let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
let raw_shift = self.intcast(raw_shift, self.val_ty(lhs), false);

self.call_intrinsic(llvm_name, &[llty], &[val, val, raw_shift])
self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs, raw_shift])
}
sym::saturating_add | sym::saturating_sub => {
let is_add = name == sym::saturating_add;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,9 @@ pub(crate) fn check_intrinsic_type(
}
sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)),
sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), tcx.types.u32], param(0)),
sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => {
(1, 0, vec![param(0), param(0), tcx.types.u32], param(0))
}
sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => {
(1, 0, vec![param(0), param(0)], param(0))
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,8 +738,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
)
}

type ProbeRef = &'tcx inspect::Probe<TyCtxt<'tcx>>;
fn mk_probe_ref(self, probe: inspect::Probe<Self>) -> &'tcx inspect::Probe<TyCtxt<'tcx>> {
type Probe = &'tcx inspect::Probe<TyCtxt<'tcx>>;
fn mk_probe(self, probe: inspect::Probe<Self>) -> &'tcx inspect::Probe<TyCtxt<'tcx>> {
self.arena.alloc(probe)
}
fn evaluate_root_goal_for_proof_tree_raw(
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1275,7 +1275,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider<
>(
cx: I,
canonical_goal: CanonicalInput<I>,
) -> (QueryResult<I>, I::ProbeRef) {
) -> (QueryResult<I>, I::Probe) {
let mut inspect = inspect::ProofTreeBuilder::new();
let canonical_result = SearchGraph::<D>::evaluate_root_goal_for_proof_tree(
cx,
Expand All @@ -1284,7 +1284,7 @@ pub fn evaluate_root_goal_for_proof_tree_raw_provider<
&mut inspect,
);
let final_revision = inspect.unwrap();
(canonical_result, cx.mk_probe_ref(final_revision))
(canonical_result, cx.mk_probe(final_revision))
}

/// Evaluate a goal to build a proof tree.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2281,6 +2281,8 @@ symbols! {
unboxed_closures,
unchecked_add,
unchecked_div,
unchecked_funnel_shl,
unchecked_funnel_shr,
unchecked_mul,
unchecked_rem,
unchecked_shl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
llvm_target: "aarch64-pc-windows-msvc".into(),
metadata: TargetMetadata {
description: Some("ARM64 Windows MSVC".into()),
tier: Some(2),
tier: Some(1),
host_tools: Some(true),
std: Some(true),
},
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Borrow;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::Deref;
Expand Down Expand Up @@ -383,12 +384,12 @@ pub trait Interner:
defining_anchor: Self::LocalDefId,
) -> Self::LocalDefIds;

type ProbeRef: Copy + Debug + Hash + Eq + Deref<Target = inspect::Probe<Self>>;
fn mk_probe_ref(self, probe: inspect::Probe<Self>) -> Self::ProbeRef;
type Probe: Debug + Hash + Eq + Borrow<inspect::Probe<Self>>;
fn mk_probe(self, probe: inspect::Probe<Self>) -> Self::Probe;
fn evaluate_root_goal_for_proof_tree_raw(
self,
canonical_goal: CanonicalInput<Self>,
) -> (QueryResult<Self>, Self::ProbeRef);
) -> (QueryResult<Self>, Self::Probe);
}

/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_type_ir/src/solve/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub type CanonicalState<I, T> = Canonical<I, State<I, T>>;
pub struct GoalEvaluation<I: Interner> {
pub uncanonicalized_goal: Goal<I, I::Predicate>,
pub orig_values: Vec<I::GenericArg>,
pub final_revision: I::ProbeRef,
pub final_revision: I::Probe,
pub result: QueryResult<I>,
}

Expand Down
73 changes: 73 additions & 0 deletions library/core/src/intrinsics/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,76 @@ impl_disjoint_bitor! {
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
}

#[const_trait]
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
pub trait FunnelShift: Copy + 'static {
/// See [`super::unchecked_funnel_shl`]; we just need the trait indirection to handle
/// different types since calling intrinsics with generics doesn't work.
unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self;

/// See [`super::unchecked_funnel_shr`]; we just need the trait indirection to handle
/// different types since calling intrinsics with generics doesn't work.
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self;
}

macro_rules! impl_funnel_shifts {
($($type:ident),*) => {$(
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const FunnelShift for $type {
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self {
// This implementation is also used by Miri so we have to check the precondition.
// SAFETY: this is guaranteed by the caller
unsafe { super::assume(shift < $type::BITS) };
if shift == 0 {
self
} else {
// SAFETY:
// - `shift < T::BITS`, which satisfies `unchecked_shl`
// - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked
// above), which satisfies `unchecked_shr`
// - because the types are unsigned, the combination are disjoint bits (this is
// not true if they're signed, since SHR will fill in the empty space with a
// sign bit, not zero)
unsafe {
super::disjoint_bitor(
super::unchecked_shl(self, shift),
super::unchecked_shr(rhs, $type::BITS - shift),
)
}
}
}

#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self {
// This implementation is also used by Miri so we have to check the precondition.
// SAFETY: this is guaranteed by the caller
unsafe { super::assume(shift < $type::BITS) };
if shift == 0 {
rhs
} else {
// SAFETY:
// - `shift < T::BITS`, which satisfies `unchecked_shr`
// - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked
// above), which satisfies `unchecked_shl`
// - because the types are unsigned, the combination are disjoint bits (this is
// not true if they're signed, since SHR will fill in the empty space with a
// sign bit, not zero)
unsafe {
super::disjoint_bitor(
super::unchecked_shl(self, $type::BITS - shift),
super::unchecked_shr(rhs, shift),
)
}
}
}
}
)*};
}

impl_funnel_shifts! {
u8, u16, u32, u64, u128, usize
}
55 changes: 55 additions & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,61 @@ pub const fn saturating_add<T: Copy>(a: T, b: T) -> T;
#[rustc_intrinsic]
pub const fn saturating_sub<T: Copy>(a: T, b: T) -> T;

/// Funnel Shift left.
///
/// Concatenates `a` and `b` (with `a` in the most significant half),
/// creating an integer twice as wide. Then shift this integer left
/// by `shift`), and extract the most significant half. If `a` and `b`
/// are the same, this is equivalent to a rotate left operation.
///
/// It is undefined behavior if `shift` is greater than or equal to the
/// bit size of `T`.
///
/// Safe versions of this intrinsic are available on the integer primitives
/// via the `funnel_shl` method. For example, [`u32::funnel_shl`].
#[rustc_intrinsic]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
#[unstable(feature = "funnel_shifts", issue = "145686")]
#[track_caller]
#[miri::intrinsic_fallback_is_spec]
pub const unsafe fn unchecked_funnel_shl<T: [const] fallback::FunnelShift>(
a: T,
b: T,
shift: u32,
) -> T {
// SAFETY: caller ensures that `shift` is in-range
unsafe { a.unchecked_funnel_shl(b, shift) }
}

/// Funnel Shift right.
///
/// Concatenates `a` and `b` (with `a` in the most significant half),
/// creating an integer twice as wide. Then shift this integer right
/// by `shift` (taken modulo the bit size of `T`), and extract the
/// least significant half. If `a` and `b` are the same, this is equivalent
/// to a rotate right operation.
///
/// It is undefined behavior if `shift` is greater than or equal to the
/// bit size of `T`.
///
/// Safer versions of this intrinsic are available on the integer primitives
/// via the `funnel_shr` method. For example, [`u32::funnel_shr`]
#[rustc_intrinsic]
#[rustc_nounwind]
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
#[unstable(feature = "funnel_shifts", issue = "145686")]
#[track_caller]
#[miri::intrinsic_fallback_is_spec]
pub const unsafe fn unchecked_funnel_shr<T: [const] fallback::FunnelShift>(
a: T,
b: T,
shift: u32,
) -> T {
// SAFETY: caller ensures that `shift` is in-range
unsafe { a.unchecked_funnel_shr(b, shift) }
}

/// This is an implementation detail of [`crate::ptr::read`] and should
/// not be used anywhere else. See its comments for why this exists.
///
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@
#![feature(f128)]
#![feature(freeze_impls)]
#![feature(fundamental)]
#![feature(funnel_shifts)]
#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
#![feature(intrinsics)]
Expand Down
24 changes: 24 additions & 0 deletions library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,9 @@ impl u8 {
rot = 2,
rot_op = "0x82",
rot_result = "0xa",
fsh_op = "0x36",
fshl_result = "0x8",
fshr_result = "0x8d",
swap_op = "0x12",
swapped = "0x12",
reversed = "0x48",
Expand Down Expand Up @@ -1088,6 +1091,9 @@ impl u16 {
rot = 4,
rot_op = "0xa003",
rot_result = "0x3a",
fsh_op = "0x2de",
fshl_result = "0x30",
fshr_result = "0x302d",
swap_op = "0x1234",
swapped = "0x3412",
reversed = "0x2c48",
Expand Down Expand Up @@ -1135,6 +1141,9 @@ impl u32 {
rot = 8,
rot_op = "0x10000b3",
rot_result = "0xb301",
fsh_op = "0x2fe78e45",
fshl_result = "0xb32f",
fshr_result = "0xb32fe78e",
swap_op = "0x12345678",
swapped = "0x78563412",
reversed = "0x1e6a2c48",
Expand All @@ -1158,6 +1167,9 @@ impl u64 {
rot = 12,
rot_op = "0xaa00000000006e1",
rot_result = "0x6e10aa",
fsh_op = "0x2fe78e45983acd98",
fshl_result = "0x6e12fe",
fshr_result = "0x6e12fe78e45983ac",
swap_op = "0x1234567890123456",
swapped = "0x5634129078563412",
reversed = "0x6a2c48091e6a2c48",
Expand All @@ -1181,6 +1193,9 @@ impl u128 {
rot = 16,
rot_op = "0x13f40000000000000000000000004f76",
rot_result = "0x4f7613f4",
fsh_op = "0x2fe78e45983acd98039000008736273",
fshl_result = "0x4f7602fe",
fshr_result = "0x4f7602fe78e45983acd9803900000873",
swap_op = "0x12345678901234567890123456789012",
swapped = "0x12907856341290785634129078563412",
reversed = "0x48091e6a2c48091e6a2c48091e6a2c48",
Expand All @@ -1207,6 +1222,9 @@ impl usize {
rot = 4,
rot_op = "0xa003",
rot_result = "0x3a",
fsh_op = "0x2fe78e45983acd98039000008736273",
fshl_result = "0x4f7602fe",
fshr_result = "0x4f7602fe78e45983acd9803900000873",
swap_op = "0x1234",
swapped = "0x3412",
reversed = "0x2c48",
Expand All @@ -1231,6 +1249,9 @@ impl usize {
rot = 8,
rot_op = "0x10000b3",
rot_result = "0xb301",
fsh_op = "0x2fe78e45",
fshl_result = "0xb32f",
fshr_result = "0xb32fe78e",
swap_op = "0x12345678",
swapped = "0x78563412",
reversed = "0x1e6a2c48",
Expand All @@ -1255,6 +1276,9 @@ impl usize {
rot = 12,
rot_op = "0xaa00000000006e1",
rot_result = "0x6e10aa",
fsh_op = "0x2fe78e45983acd98",
fshl_result = "0x6e12fe",
fshr_result = "0x6e12fe78e45983ac",
swap_op = "0x1234567890123456",
swapped = "0x5634129078563412",
reversed = "0x6a2c48091e6a2c48",
Expand Down
Loading
Loading