Skip to content

Commit 4c40ff6

Browse files
committed
Implement rotate using funnel shift on LLVM >= 7
Implement the rotate_left and rotate_right operations using llvm.fshl and llvm.fshr if they are available (LLVM >= 7). Originally I wanted to expose the funnel_shift_left and funnel_shift_right intrinsics and implement rotate_left and rotate_right on top of them. However, emulation of funnel shifts requires emitting a conditional to check for zero shift amount, which is not necessary for rotates. I was uncomfortable doing that here, as I don't want to rely on LLVM to optimize away that conditional (and for variable rotates, I'm not sure it can). We should revisit that question when we raise our minimum version requirement to LLVM 7 and don't need emulation code anymore.
1 parent 2ad8c7b commit 4c40ff6

File tree

10 files changed

+93
-8
lines changed

10 files changed

+93
-8
lines changed

Diff for: src/libcore/intrinsics.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1465,6 +1465,20 @@ extern "rust-intrinsic" {
14651465
/// y < 0 or y >= N, where N is the width of T in bits.
14661466
pub fn unchecked_shr<T>(x: T, y: T) -> T;
14671467

1468+
/// Performs rotate left.
1469+
/// The stabilized versions of this intrinsic are available on the integer
1470+
/// primitives via the `rotate_left` method. For example,
1471+
/// [`std::u32::rotate_left`](../../std/primitive.u32.html#method.rotate_left)
1472+
#[cfg(not(stage0))]
1473+
pub fn rotate_left<T>(x: T, y: T) -> T;
1474+
1475+
/// Performs rotate right.
1476+
/// The stabilized versions of this intrinsic are available on the integer
1477+
/// primitives via the `rotate_right` method. For example,
1478+
/// [`std::u32::rotate_right`](../../std/primitive.u32.html#method.rotate_right)
1479+
#[cfg(not(stage0))]
1480+
pub fn rotate_right<T>(x: T, y: T) -> T;
1481+
14681482
/// Returns (a + b) mod 2<sup>N</sup>, where N is the width of T in bits.
14691483
/// The stabilized versions of this intrinsic are available on the integer
14701484
/// primitives via the `wrapping_add` method. For example,

Diff for: src/libcore/num/mod.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -2301,7 +2301,12 @@ assert_eq!(n.rotate_left(", $rot, "), m);
23012301
#[rustc_const_unstable(feature = "const_int_rotate")]
23022302
#[inline]
23032303
pub const fn rotate_left(self, n: u32) -> Self {
2304-
(self << (n % $BITS)) | (self >> (($BITS - (n % $BITS)) % $BITS))
2304+
#[cfg(not(stage0))] {
2305+
unsafe { intrinsics::rotate_left(self, n as $SelfT) }
2306+
}
2307+
#[cfg(stage0)] {
2308+
(self << (n % $BITS)) | (self >> (($BITS - (n % $BITS)) % $BITS))
2309+
}
23052310
}
23062311
}
23072312

@@ -2326,7 +2331,12 @@ assert_eq!(n.rotate_right(", $rot, "), m);
23262331
#[rustc_const_unstable(feature = "const_int_rotate")]
23272332
#[inline]
23282333
pub const fn rotate_right(self, n: u32) -> Self {
2329-
(self >> (n % $BITS)) | (self << (($BITS - (n % $BITS)) % $BITS))
2334+
#[cfg(not(stage0))] {
2335+
unsafe { intrinsics::rotate_right(self, n as $SelfT) }
2336+
}
2337+
#[cfg(stage0)] {
2338+
(self >> (n % $BITS)) | (self << (($BITS - (n % $BITS)) % $BITS))
2339+
}
23302340
}
23312341
}
23322342

Diff for: src/librustc_codegen_llvm/context.rs

+12
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,18 @@ fn declare_intrinsic(cx: &CodegenCx<'ll, '_>, key: &str) -> Option<&'ll Value> {
726726
ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64);
727727
ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128);
728728

729+
ifn!("llvm.fshl.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
730+
ifn!("llvm.fshl.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
731+
ifn!("llvm.fshl.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
732+
ifn!("llvm.fshl.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
733+
ifn!("llvm.fshl.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
734+
735+
ifn!("llvm.fshr.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
736+
ifn!("llvm.fshr.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
737+
ifn!("llvm.fshr.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
738+
ifn!("llvm.fshr.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
739+
ifn!("llvm.fshr.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
740+
729741
ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1});
730742
ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1});
731743
ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1});

Diff for: src/librustc_codegen_llvm/debuginfo/metadata.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use value::Value;
2323
use llvm;
2424
use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor,
2525
DICompositeType, DILexicalBlock, DIFlags};
26+
use llvm_util;
2627

2728
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
2829
use rustc::hir::CodegenFnAttrFlags;
@@ -1169,9 +1170,8 @@ fn prepare_union_metadata(
11691170
fn use_enum_fallback(cx: &CodegenCx) -> bool {
11701171
// On MSVC we have to use the fallback mode, because LLVM doesn't
11711172
// lower variant parts to PDB.
1172-
return cx.sess().target.target.options.is_like_msvc || unsafe {
1173-
llvm::LLVMRustVersionMajor() < 7
1174-
};
1173+
return cx.sess().target.target.options.is_like_msvc
1174+
|| llvm_util::get_major_version() < 7;
11751175
}
11761176

11771177
// Describes the members of an enum value: An enum is described as a union of

Diff for: src/librustc_codegen_llvm/intrinsic.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use attributes;
1414
use intrinsics::{self, Intrinsic};
1515
use llvm::{self, TypeKind};
16+
use llvm_util;
1617
use abi::{Abi, FnType, LlvmType, PassMode};
1718
use mir::place::PlaceRef;
1819
use mir::operand::{OperandRef, OperandValue};
@@ -284,7 +285,8 @@ pub fn codegen_intrinsic_call(
284285
"ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" |
285286
"bitreverse" | "add_with_overflow" | "sub_with_overflow" |
286287
"mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" |
287-
"unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" => {
288+
"unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" |
289+
"rotate_left" | "rotate_right" => {
288290
let ty = arg_tys[0];
289291
match int_type_width_signed(ty, cx) {
290292
Some((width, signed)) =>
@@ -363,6 +365,27 @@ pub fn codegen_intrinsic_call(
363365
} else {
364366
bx.lshr(args[0].immediate(), args[1].immediate())
365367
},
368+
"rotate_left" | "rotate_right" => {
369+
let is_left = name == "rotate_left";
370+
let val = args[0].immediate();
371+
let raw_shift = args[1].immediate();
372+
if llvm_util::get_major_version() >= 7 {
373+
// rotate = funnel shift with first two args the same
374+
let llvm_name = &format!("llvm.fsh{}.i{}",
375+
if is_left { 'l' } else { 'r' }, width);
376+
let llfn = cx.get_intrinsic(llvm_name);
377+
bx.call(llfn, &[val, val, raw_shift], None)
378+
} else {
379+
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
380+
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
381+
let width = C_uint(Type::ix(cx, width), width);
382+
let shift = bx.urem(raw_shift, width);
383+
let inv_shift = bx.urem(bx.sub(width, raw_shift), width);
384+
let shift1 = bx.shl(val, if is_left { shift } else { inv_shift });
385+
let shift2 = bx.lshr(val, if !is_left { shift } else { inv_shift });
386+
bx.or(shift1, shift2)
387+
}
388+
},
366389
_ => bug!(),
367390
},
368391
None => {

Diff for: src/librustc_codegen_llvm/llvm_util.rs

+4
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ pub fn print_version() {
256256
}
257257
}
258258

259+
pub fn get_major_version() -> u32 {
260+
unsafe { llvm::LLVMRustVersionMajor() }
261+
}
262+
259263
pub fn print_passes() {
260264
// Can be called without initializing LLVM
261265
unsafe { llvm::LLVMRustPrintPasses(); }

Diff for: src/librustc_codegen_llvm/mir/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use common::{C_i32, C_null};
1212
use libc::c_uint;
1313
use llvm::{self, BasicBlock};
1414
use llvm::debuginfo::DIScope;
15+
use llvm_util;
1516
use rustc::ty::{self, Ty, TypeFoldable, UpvarSubsts};
1617
use rustc::ty::layout::{LayoutOf, TyLayout};
1718
use rustc::mir::{self, Mir};
@@ -612,7 +613,7 @@ fn arg_local_refs(
612613
// doesn't actually strip the offset when splitting the closure
613614
// environment into its components so it ends up out of bounds.
614615
// (cuviper) It seems to be fine without the alloca on LLVM 6 and later.
615-
let env_alloca = !env_ref && unsafe { llvm::LLVMRustVersionMajor() < 6 };
616+
let env_alloca = !env_ref && llvm_util::get_major_version() < 6;
616617
let env_ptr = if env_alloca {
617618
let scratch = PlaceRef::alloca(bx,
618619
bx.cx.layout_of(tcx.mk_mut_ptr(arg.layout.ty)),

Diff for: src/librustc_mir/interpret/intrinsics.rs

+18
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,24 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
150150
}
151151
self.write_scalar(val, dest)?;
152152
}
153+
"rotate_left" | "rotate_right" => {
154+
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
155+
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
156+
let layout = self.layout_of(substs.type_at(0))?;
157+
let val_bits = self.read_scalar(args[0])?.to_bits(layout.size)?;
158+
let raw_shift_bits = self.read_scalar(args[1])?.to_bits(layout.size)?;
159+
let width_bits = layout.size.bits() as u128;
160+
let shift_bits = raw_shift_bits % width_bits;
161+
let inv_shift_bits = (width_bits - raw_shift_bits) % width_bits;
162+
let result_bits = if intrinsic_name == "rotate_left" {
163+
(val_bits << shift_bits) | (val_bits >> inv_shift_bits)
164+
} else {
165+
(val_bits >> shift_bits) | (val_bits << inv_shift_bits)
166+
};
167+
let truncated_bits = self.truncate(result_bits, layout);
168+
let result = Scalar::from_uint(truncated_bits, layout.size);
169+
self.write_scalar(result, dest)?;
170+
}
153171
"transmute" => {
154172
self.copy_op_transmute(args[0], dest)?;
155173
}

Diff for: src/librustc_mir/transform/qualify_consts.rs

+2
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
869869
| "overflowing_mul"
870870
| "unchecked_shl"
871871
| "unchecked_shr"
872+
| "rotate_left"
873+
| "rotate_right"
872874
| "add_with_overflow"
873875
| "sub_with_overflow"
874876
| "mul_with_overflow"

Diff for: src/librustc_typeck/check/intrinsic.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
292292

293293
"unchecked_div" | "unchecked_rem" | "exact_div" =>
294294
(1, vec![param(0), param(0)], param(0)),
295-
"unchecked_shl" | "unchecked_shr" =>
295+
"unchecked_shl" | "unchecked_shr" |
296+
"rotate_left" | "rotate_right" =>
296297
(1, vec![param(0), param(0)], param(0)),
297298

298299
"overflowing_add" | "overflowing_sub" | "overflowing_mul" =>

0 commit comments

Comments
 (0)