From 376b6407a859bcd5295af693d33cfd11b7172314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 29 May 2018 11:41:10 +0200 Subject: [PATCH 1/5] Make ctpop, cttz, ctlz and bswap const fns --- src/librustc_mir/interpret/const_eval.rs | 63 +++++++++++++++++++- src/librustc_mir/transform/qualify_consts.rs | 10 +++- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index b1f290d7b61..5bb4663d100 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::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 @@ fn eval_fn_call<'a>( 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 @@ fn call_intrinsic<'a>( }; 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 num = 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 num = if intrinsic_name.ends_with("_nonzero") { + if num == 0 { + return err!(Intrinsic(format!("{} called on 0", intrinsic_name))); + } + numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), num, kind)? + } else { + numeric_intrinsic(intrinsic_name, num, kind)? + }; + ecx.write_scalar(dest, num, 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,40 @@ pub fn const_eval_provider<'a, 'tcx>( } }) } + +fn numeric_intrinsic<'tcx>( + name: &str, + bytes: u128, + kind: Primitive, +) -> EvalResult<'tcx, Scalar> { + macro_rules! integer_intrinsic { + ($method:ident) => ({ + use rustc_target::abi::Integer; + let (bits, defined) = match kind { + Primitive::Int(Integer::I8, true) => ((bytes as i8).$method() as u128, 8), + Primitive::Int(Integer::I8, false) => ((bytes as u8).$method() as u128, 8), + Primitive::Int(Integer::I16, true) => ((bytes as i16).$method() as u128, 16), + Primitive::Int(Integer::I16, false) => ((bytes as u16).$method() as u128, 16), + Primitive::Int(Integer::I32, true) => ((bytes as i32).$method() as u128, 32), + Primitive::Int(Integer::I32, false) => ((bytes as u32).$method() as u128, 32), + Primitive::Int(Integer::I64, true) => ((bytes as i64).$method() as u128, 64), + Primitive::Int(Integer::I64, false) => ((bytes as u64).$method() as u128, 64), + Primitive::Int(Integer::I128, true) => ((bytes as i128).$method() as u128, 128), + Primitive::Int(Integer::I128, false) => (bytes.$method() as u128, 128), + _ => bug!("invalid `{}` argument: {:?}", name, bytes), + }; + + Scalar::Bits { bits, defined } + }); + } + + let result_val = match name { + "bswap" => integer_intrinsic!(swap_bytes), + "ctlz" => integer_intrinsic!(leading_zeros), + "ctpop" => integer_intrinsic!(count_ones), + "cttz" => integer_intrinsic!(trailing_zeros), + _ => bug!("not a numeric intrinsic: {}", name), + }; + + Ok(result_val) +} 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 @@ fn visit_terminator_kind(&mut self, 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; From 12c330c4bf53af24d7c10a8e2270e34f3d0b8efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 29 May 2018 16:00:01 +0200 Subject: [PATCH 2/5] Add test for intrinsics::bswap --- src/test/run-pass/ctfe/bswap-const.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/test/run-pass/ctfe/bswap-const.rs 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 or the MIT license +// , 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); +} From 0d7d5e994fca984a71303b170acc24cfbd26474a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Wed, 30 May 2018 23:27:11 +0200 Subject: [PATCH 3/5] Rewrite numeric_intrinsic without macro --- src/librustc_mir/interpret/const_eval.rs | 39 +++++++----------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 5bb4663d100..baaca5f8307 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -596,34 +596,17 @@ fn numeric_intrinsic<'tcx>( bytes: u128, kind: Primitive, ) -> EvalResult<'tcx, Scalar> { - macro_rules! integer_intrinsic { - ($method:ident) => ({ - use rustc_target::abi::Integer; - let (bits, defined) = match kind { - Primitive::Int(Integer::I8, true) => ((bytes as i8).$method() as u128, 8), - Primitive::Int(Integer::I8, false) => ((bytes as u8).$method() as u128, 8), - Primitive::Int(Integer::I16, true) => ((bytes as i16).$method() as u128, 16), - Primitive::Int(Integer::I16, false) => ((bytes as u16).$method() as u128, 16), - Primitive::Int(Integer::I32, true) => ((bytes as i32).$method() as u128, 32), - Primitive::Int(Integer::I32, false) => ((bytes as u32).$method() as u128, 32), - Primitive::Int(Integer::I64, true) => ((bytes as i64).$method() as u128, 64), - Primitive::Int(Integer::I64, false) => ((bytes as u64).$method() as u128, 64), - Primitive::Int(Integer::I128, true) => ((bytes as i128).$method() as u128, 128), - Primitive::Int(Integer::I128, false) => (bytes.$method() as u128, 128), - _ => bug!("invalid `{}` argument: {:?}", name, bytes), - }; - - Scalar::Bits { bits, defined } - }); - } - - let result_val = match name { - "bswap" => integer_intrinsic!(swap_bytes), - "ctlz" => integer_intrinsic!(leading_zeros), - "ctpop" => integer_intrinsic!(count_ones), - "cttz" => integer_intrinsic!(trailing_zeros), + let defined = match kind { + Primitive::Int(integer, _) => integer.size().bits() as u8, + _ => bug!("invalid `{}` argument: {:?}", name, bytes), + }; + let extra = 128 - defined as u128; + let bytes_out = match name { + "ctpop" => bytes.count_ones() as u128, + "ctlz" => bytes.leading_zeros() as u128 - extra, + "cttz" => (bytes << extra).trailing_zeros() as u128 - extra, + "bswap" => (bytes << extra).swap_bytes(), _ => bug!("not a numeric intrinsic: {}", name), }; - - Ok(result_val) + Ok(Scalar::Bits { bits: bytes_out, defined }) } From 8939fe60a1980dc097da19e6de32df51c18944bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Thu, 31 May 2018 19:21:34 +0200 Subject: [PATCH 4/5] Rename bytes -> bits --- src/librustc_mir/interpret/const_eval.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index baaca5f8307..49ad134e593 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -593,20 +593,20 @@ pub fn const_eval_provider<'a, 'tcx>( fn numeric_intrinsic<'tcx>( name: &str, - bytes: u128, + bits: u128, kind: Primitive, ) -> EvalResult<'tcx, Scalar> { let defined = match kind { Primitive::Int(integer, _) => integer.size().bits() as u8, - _ => bug!("invalid `{}` argument: {:?}", name, bytes), + _ => bug!("invalid `{}` argument: {:?}", name, bits), }; let extra = 128 - defined as u128; - let bytes_out = match name { - "ctpop" => bytes.count_ones() as u128, - "ctlz" => bytes.leading_zeros() as u128 - extra, - "cttz" => (bytes << extra).trailing_zeros() as u128 - extra, - "bswap" => (bytes << extra).swap_bytes(), + 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: bytes_out, defined }) + Ok(Scalar::Bits { bits: bits_out, defined }) } From 97a0d46dece080714a4fe1ac21f84ad54f6ab76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Thu, 31 May 2018 19:43:56 +0200 Subject: [PATCH 5/5] Rename num -> bits and num -> out_val --- src/librustc_mir/interpret/const_eval.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 49ad134e593..44832df8240 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -348,20 +348,20 @@ fn call_intrinsic<'a>( "ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => { let ty = substs.type_at(0); let layout_of = ecx.layout_of(ty)?; - let num = ecx.value_to_scalar(args[0])?.to_bits(layout_of.size)?; + 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 num = if intrinsic_name.ends_with("_nonzero") { - if num == 0 { + 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"), num, kind)? + numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)? } else { - numeric_intrinsic(intrinsic_name, num, kind)? + numeric_intrinsic(intrinsic_name, bits, kind)? }; - ecx.write_scalar(dest, num, ty)?; + ecx.write_scalar(dest, out_val, ty)?; } name => return Err(