about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-06-01 03:24:54 +0000
committerbors <bors@rust-lang.org>2018-06-01 03:24:54 +0000
commit63cd4a39ead1bdcc6f4df5f11d187916bd8d3ea7 (patch)
treec16da5601fbe4b7cef6a46d53d94fecf004afc34
parentb7a9d4ea744c9a99952bc4576727b57c58c0e958 (diff)
parent97a0d46dece080714a4fe1ac21f84ad54f6ab76d (diff)
downloadrust-63cd4a39ead1bdcc6f4df5f11d187916bd8d3ea7.tar.gz
rust-63cd4a39ead1bdcc6f4df5f11d187916bd8d3ea7.zip
Auto merge of #51171 - faern:const-bswap-ctpop-cttz-ctlz, r=oli-obk
Make some std::intrinsics `const fn`s

Making some rustc intrinsics (`ctpop`, `cttz`, `ctlz` and `bswap`) `const fn`s.

This is a pre-step to being able to make `swap_bytes`, `to_be` and `from_be` constant functions. That in itself could be ergonomic and useful. But even better is that it would allow `Ipv4Addr::new` etc becoming `const fn`s as well. Which might be really useful since I find it quite common to want to define them as constants.

r? @oli-obk
-rw-r--r--src/librustc_mir/interpret/const_eval.rs46
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs10
-rw-r--r--src/test/run-pass/ctfe/bswap-const.rs23
3 files changed, 75 insertions, 4 deletions
diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs
index b1f290d7b61..44832df8240 100644
--- a/src/librustc_mir/interpret/const_eval.rs
+++ b/src/librustc_mir/interpret/const_eval.rs
@@ -3,7 +3,7 @@ use rustc::middle::const_val::{ConstEvalErr, ErrKind};
 use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError};
 use rustc::mir;
 use rustc::ty::{self, TyCtxt, Ty, Instance};
-use rustc::ty::layout::{self, LayoutOf};
+use rustc::ty::layout::{self, LayoutOf, Primitive};
 use rustc::ty::subst::Subst;
 
 use syntax::ast::Mutability;
@@ -307,7 +307,7 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
     fn call_intrinsic<'a>(
         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
         instance: ty::Instance<'tcx>,
-        _args: &[ValTy<'tcx>],
+        args: &[ValTy<'tcx>],
         dest: Place,
         dest_layout: layout::TyLayout<'tcx>,
         target: mir::BasicBlock,
@@ -345,8 +345,28 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
                 };
                 ecx.write_scalar(dest, id_val, dest_layout.ty)?;
             }
+            "ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => {
+                let ty = substs.type_at(0);
+                let layout_of = ecx.layout_of(ty)?;
+                let bits = ecx.value_to_scalar(args[0])?.to_bits(layout_of.size)?;
+                let kind = match layout_of.abi {
+                    ty::layout::Abi::Scalar(ref scalar) => scalar.value,
+                    _ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
+                };
+                let out_val = if intrinsic_name.ends_with("_nonzero") {
+                    if bits == 0 {
+                        return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
+                    }
+                    numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)?
+                } else {
+                    numeric_intrinsic(intrinsic_name, bits, kind)?
+                };
+                ecx.write_scalar(dest, out_val, ty)?;
+            }
 
-            name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
+            name => return Err(
+                ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()
+            ),
         }
 
         ecx.goto_block(target);
@@ -570,3 +590,23 @@ pub fn const_eval_provider<'a, 'tcx>(
         }
     })
 }
+
+fn numeric_intrinsic<'tcx>(
+    name: &str,
+    bits: u128,
+    kind: Primitive,
+) -> EvalResult<'tcx, Scalar> {
+    let defined = match kind {
+        Primitive::Int(integer, _) => integer.size().bits() as u8,
+        _ => bug!("invalid `{}` argument: {:?}", name, bits),
+    };
+    let extra = 128 - defined as u128;
+    let bits_out = match name {
+        "ctpop" => bits.count_ones() as u128,
+        "ctlz" => bits.leading_zeros() as u128 - extra,
+        "cttz" => (bits << extra).trailing_zeros() as u128 - extra,
+        "bswap" => (bits << extra).swap_bytes(),
+        _ => bug!("not a numeric intrinsic: {}", name),
+    };
+    Ok(Scalar::Bits { bits: bits_out, defined })
+}
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 71963012944..03ca85faaff 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -907,7 +907,15 @@ This does not pose a problem by itself because they can't be accessed directly."
                     Abi::PlatformIntrinsic => {
                         assert!(!self.tcx.is_const_fn(def_id));
                         match &self.tcx.item_name(def_id).as_str()[..] {
-                            "size_of" | "min_align_of" | "type_id" => is_const_fn = Some(def_id),
+                            | "size_of"
+                            | "min_align_of"
+                            | "type_id"
+                            | "bswap"
+                            | "ctpop"
+                            | "cttz"
+                            | "cttz_nonzero"
+                            | "ctlz"
+                            | "ctlz_nonzero" => is_const_fn = Some(def_id),
 
                             name if name.starts_with("simd_shuffle") => {
                                 is_shuffle = true;
diff --git a/src/test/run-pass/ctfe/bswap-const.rs b/src/test/run-pass/ctfe/bswap-const.rs
new file mode 100644
index 00000000000..b951a9b72b1
--- /dev/null
+++ b/src/test/run-pass/ctfe/bswap-const.rs
@@ -0,0 +1,23 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(core_intrinsics)]
+
+use std::intrinsics;
+
+const SWAPPED_U8: u8 = unsafe { intrinsics::bswap(0x12_u8) };
+const SWAPPED_U16: u16 = unsafe { intrinsics::bswap(0x12_34_u16) };
+const SWAPPED_I32: i32 = unsafe { intrinsics::bswap(0x12_34_56_78_i32) };
+
+fn main() {
+    assert_eq!(SWAPPED_U8, 0x12);
+    assert_eq!(SWAPPED_U16, 0x34_12);
+    assert_eq!(SWAPPED_I32, 0x78_56_34_12);
+}