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
This commit is contained in:
commit
63cd4a39ea
@ -3,7 +3,7 @@
|
|||||||
use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError};
|
use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError};
|
||||||
use rustc::mir;
|
use rustc::mir;
|
||||||
use rustc::ty::{self, TyCtxt, Ty, Instance};
|
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 rustc::ty::subst::Subst;
|
||||||
|
|
||||||
use syntax::ast::Mutability;
|
use syntax::ast::Mutability;
|
||||||
@ -307,7 +307,7 @@ fn eval_fn_call<'a>(
|
|||||||
fn call_intrinsic<'a>(
|
fn call_intrinsic<'a>(
|
||||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||||
instance: ty::Instance<'tcx>,
|
instance: ty::Instance<'tcx>,
|
||||||
_args: &[ValTy<'tcx>],
|
args: &[ValTy<'tcx>],
|
||||||
dest: Place,
|
dest: Place,
|
||||||
dest_layout: layout::TyLayout<'tcx>,
|
dest_layout: layout::TyLayout<'tcx>,
|
||||||
target: mir::BasicBlock,
|
target: mir::BasicBlock,
|
||||||
@ -345,8 +345,28 @@ fn call_intrinsic<'a>(
|
|||||||
};
|
};
|
||||||
ecx.write_scalar(dest, id_val, dest_layout.ty)?;
|
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);
|
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 })
|
||||||
|
}
|
||||||
|
@ -907,7 +907,15 @@ fn visit_terminator_kind(&mut self,
|
|||||||
Abi::PlatformIntrinsic => {
|
Abi::PlatformIntrinsic => {
|
||||||
assert!(!self.tcx.is_const_fn(def_id));
|
assert!(!self.tcx.is_const_fn(def_id));
|
||||||
match &self.tcx.item_name(def_id).as_str()[..] {
|
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") => {
|
name if name.starts_with("simd_shuffle") => {
|
||||||
is_shuffle = true;
|
is_shuffle = true;
|
||||||
|
23
src/test/run-pass/ctfe/bswap-const.rs
Normal file
23
src/test/run-pass/ctfe/bswap-const.rs
Normal file
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user