diff --git a/src/librustc_const_math/int.rs b/src/librustc_const_math/int.rs index 7af61d6f274026b0b7509dd416a71c935687a991..59eb4b70aa8ff4eba799748a6af0718cae9c57b7 100644 --- a/src/librustc_const_math/int.rs +++ b/src/librustc_const_math/int.rs @@ -58,7 +58,8 @@ mod ubounds { bounds!{u128: 0, i8 I8MIN I8MAX i16 I16MIN I16MAX i32 I32MIN I32MAX i64 I64MIN I64MAX i128 I128MIN I128MAX u8 U8MIN U8MAX u16 U16MIN U16MAX u32 U32MIN U32MAX u64 U64MIN U64MAX u128 U128MIN U128MAX - isize IMIN IMAX usize UMIN UMAX + // do not add constants for isize/usize, because these are guaranteed to be wrong for + // arbitrary host/target combinations } } @@ -78,11 +79,42 @@ mod ibounds { bounds!{i128, i8 I8MIN I8MAX i16 I16MIN I16MAX i32 I32MIN I32MAX i64 I64MIN I64MAX i128 I128MIN I128MAX u8 U8MIN U8MAX u16 U16MIN U16MAX u32 U32MIN U32MAX - isize IMIN IMAX usize UMIN UMAX + // do not add constants for isize/usize, because these are guaranteed to be wrong for + // arbitrary host/target combinations } } impl ConstInt { + /// Creates a new unsigned ConstInt with matching type while also checking that overflow does + /// not happen. + pub fn new_unsigned(val: u128, ty: UintTy, usize_ty: UintTy) -> Option { + match ty { + UintTy::U8 if val <= ubounds::U8MAX => Some(U8(val as u8)), + UintTy::U16 if val <= ubounds::U16MAX => Some(U16(val as u16)), + UintTy::U32 if val <= ubounds::U32MAX => Some(U32(val as u32)), + UintTy::U64 if val <= ubounds::U64MAX => Some(U64(val as u64)), + UintTy::Us if val <= ubounds::U64MAX => ConstUsize::new(val as u64, usize_ty).ok() + .map(Usize), + UintTy::U128 => Some(U128(val)), + _ => None + } + } + + /// Creates a new unsigned ConstInt with matching type while also checking that overflow does + /// not happen. + pub fn new_signed(val: i128, ty: IntTy, isize_ty: IntTy) -> Option { + match ty { + IntTy::I8 if val <= ibounds::I8MAX => Some(I8(val as i8)), + IntTy::I16 if val <= ibounds::I16MAX => Some(I16(val as i16)), + IntTy::I32 if val <= ibounds::I32MAX => Some(I32(val as i32)), + IntTy::I64 if val <= ibounds::I64MAX => Some(I64(val as i64)), + IntTy::Is if val <= ibounds::I64MAX => ConstIsize::new(val as i64, isize_ty).ok() + .map(Isize), + IntTy::I128 => Some(I128(val)), + _ => None + } + } + /// If either value is `Infer` or `InferSigned`, try to turn the value into the type of /// the other value. If both values have no type, don't do anything pub fn infer(self, other: Self) -> Result<(Self, Self), ConstMathErr> { diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index acf2bd50847c3a592e5d4b8edc7d86fc52e3e20b..fb188252075e6d99bb5ce4a0c68ef5873e663df2 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -582,6 +582,8 @@ pub fn LLVMStructTypeInContext(C: ContextRef, pub fn LLVMConstReal(RealTy: TypeRef, N: f64) -> ValueRef; pub fn LLVMConstIntGetZExtValue(ConstantVal: ValueRef) -> c_ulonglong; pub fn LLVMConstIntGetSExtValue(ConstantVal: ValueRef) -> c_longlong; + pub fn LLVMRustConstInt128Get(ConstantVal: ValueRef, SExt: bool, + high: *mut u64, low: *mut u64) -> bool; // Operations on composite constants diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 3e564a7c6ed4d0aa15e35fe8498c094accae6fef..f4c7d2973bf66145f0dcfe1f424b6fa84c94a323 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -592,20 +592,34 @@ fn is_const_integral(v: ValueRef) -> bool { } } -pub fn const_to_opt_int(v: ValueRef) -> Option { + +#[cfg(stage0)] +pub fn const_to_opt_u128(v: ValueRef, sign_ext: bool) -> Option { unsafe { if is_const_integral(v) { - Some(llvm::LLVMConstIntGetSExtValue(v)) + if !sign_ext { + Some(llvm::LLVMConstIntGetZExtValue(v)) + } else { + Some(llvm::LLVMConstIntGetSExtValue(v) as u64) + } } else { None } } } -pub fn const_to_opt_uint(v: ValueRef) -> Option { +#[cfg(not(stage0))] +pub fn const_to_opt_u128(v: ValueRef, sign_ext: bool) -> Option { unsafe { if is_const_integral(v) { - Some(llvm::LLVMConstIntGetZExtValue(v)) + let (mut lo, mut hi) = (0u64, 0u64); + let success = llvm::LLVMRustConstInt128Get(v, sign_ext, + &mut hi as *mut u64, &mut lo as *mut u64); + if success { + Some(((hi as u128) << 64) | (lo as u128)) + } else { + None + } } else { None } diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index fd921b3c432908e4c2e11a2fd635b010772da277..2e2644d91bb6c3584aadeec23a53e7b52cbd6b53 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -24,7 +24,6 @@ use type_::Type; use type_of; use rustc::ty; -use rustc_i128::{i128, u128}; use rustc::hir; diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 3989dae553ffca928818fe265bd40b83702ac7f9..62141369caec1cf11a3644e8fef37e6d02ccbc06 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -346,11 +346,12 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). - let align = match (const_to_opt_uint(sized_align), const_to_opt_uint(unsized_align)) { + let align = match (const_to_opt_u128(sized_align, false), + const_to_opt_u128(unsized_align, false)) { (Some(sized_align), Some(unsized_align)) => { // If both alignments are constant, (the sized_align should always be), then // pick the correct alignment statically. - C_uint(ccx, std::cmp::max(sized_align, unsized_align)) + C_uint(ccx, std::cmp::max(sized_align, unsized_align) as u64) } _ => bcx.select(bcx.icmp(llvm::IntUGT, sized_align, unsized_align), sized_align, diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index cc55a2b3e9a5802eda1e40c6a65d24d3f2e7df02..c4129b346e409b4dca0d2a4762ae2035a2e5ca25 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -32,6 +32,8 @@ use rustc::session::Session; use syntax_pos::Span; +use rustc_i128::u128; + use std::cmp::Ordering; use std::iter; @@ -1019,7 +1021,7 @@ fn generic_simd_intrinsic<'a, 'tcx>( in_elem, in_ty, ret_ty, ret_ty.simd_type(tcx)); - let total_len = in_len as u64 * 2; + let total_len = in_len as u128 * 2; let vector = llargs[2]; @@ -1027,7 +1029,7 @@ fn generic_simd_intrinsic<'a, 'tcx>( .map(|i| { let arg_idx = i; let val = const_get_elt(vector, &[i as libc::c_uint]); - match const_to_opt_uint(val) { + match const_to_opt_u128(val, true) { None => { emit_error!("shuffle index #{} is not a constant", arg_idx); None diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 71ac7c0d252048ce818eabb0708251b05c073f72..ecedcd68382d167ad44ef8d6d852472ab8cb112b 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -269,7 +269,7 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock, mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { let cond = self.trans_operand(&bcx, cond).immediate(); - let mut const_cond = common::const_to_opt_uint(cond).map(|c| c == 1); + let mut const_cond = common::const_to_opt_u128(cond, false).map(|c| c == 1); // This case can currently arise only from functions marked // with #[rustc_inherit_overflow_checks] and inlined from @@ -322,14 +322,12 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock, let len = self.trans_operand(&mut bcx, len).immediate(); let index = self.trans_operand(&mut bcx, index).immediate(); - let const_err = common::const_to_opt_uint(len).and_then(|len| { - common::const_to_opt_uint(index).map(|index| { - ErrKind::IndexOutOfBounds { - len: len, - index: index - } - }) - }); + let const_err = common::const_to_opt_u128(len, false) + .and_then(|len| common::const_to_opt_u128(index, false) + .map(|index| ErrKind::IndexOutOfBounds { + len: len as u64, + index: index as u64 + })); let file_line = C_struct(bcx.ccx, &[filename, line], false); let align = llalign_of_min(bcx.ccx, common::val_ty(file_line)); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 737ca634f0860e65576c21f788c9aadf6db4a117..04f1e4fb8640edd34fa349cbc72358201e19fa0e 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -13,7 +13,7 @@ use rustc_const_eval::{ErrKind, ConstEvalErr, report_const_eval_err}; use rustc_const_math::ConstInt::*; use rustc_const_math::ConstFloat::*; -use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstMathErr}; +use rustc_const_math::{ConstInt, ConstMathErr}; use rustc::hir::def_id::DefId; use rustc::infer::TransNormalize; use rustc::mir; @@ -27,16 +27,14 @@ use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty}; use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral, C_big_integral}; use common::{C_null, C_struct, C_str_slice, C_undef, C_uint}; -use common::{const_to_opt_int, const_to_opt_uint}; +use common::{const_to_opt_u128}; use consts; use monomorphize::{self, Instance}; use type_of; use type_::Type; use value::Value; -use syntax::ast; use syntax_pos::Span; -use rustc_i128::u128; use std::fmt; use std::ptr; @@ -44,6 +42,8 @@ use super::operand::{OperandRef, OperandValue}; use super::MirContext; +use rustc_i128::{u128, i128}; + /// A sized constant rvalue. /// The LLVM type might not be the same for a single Rust type, /// e.g. each enum variant would have its own LLVM struct type. @@ -431,7 +431,7 @@ fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span) mir::ProjectionElem::Index(ref index) => { let llindex = self.const_operand(index, span)?.llval; - let iv = if let Some(iv) = common::const_to_opt_uint(llindex) { + let iv = if let Some(iv) = common::const_to_opt_u128(llindex, false) { iv } else { span_bug!(span, "index is not an integer-constant expression") @@ -439,7 +439,7 @@ fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span) // Produce an undef instead of a LLVM assertion on OOB. let len = common::const_to_uint(tr_base.len(self.ccx)); - let llelem = if iv < len { + let llelem = if iv < len as u128 { const_get_elt(base.llval, &[iv as u32]) } else { C_undef(type_of::type_of(self.ccx, projected_ty)) @@ -797,49 +797,14 @@ fn const_rvalue(&self, rvalue: &mir::Rvalue<'tcx>, fn to_const_int(value: ValueRef, t: Ty, tcx: TyCtxt) -> Option { match t.sty { - ty::TyInt(int_type) => const_to_opt_int(value).and_then(|input| match int_type { - ast::IntTy::I8 => { - assert_eq!(input as i8 as i64, input); - Some(ConstInt::I8(input as i8)) - }, - ast::IntTy::I16 => { - assert_eq!(input as i16 as i64, input); - Some(ConstInt::I16(input as i16)) - }, - ast::IntTy::I32 => { - assert_eq!(input as i32 as i64, input); - Some(ConstInt::I32(input as i32)) - }, - ast::IntTy::I64 => { - Some(ConstInt::I64(input)) - }, - ast::IntTy::Is => { - ConstIsize::new(input, tcx.sess.target.int_type) - .ok().map(ConstInt::Isize) - }, - }), - ty::TyUint(uint_type) => const_to_opt_uint(value).and_then(|input| match uint_type { - ast::UintTy::U8 => { - assert_eq!(input as u8 as u64, input); - Some(ConstInt::U8(input as u8)) - }, - ast::UintTy::U16 => { - assert_eq!(input as u16 as u64, input); - Some(ConstInt::U16(input as u16)) - }, - ast::UintTy::U32 => { - assert_eq!(input as u32 as u64, input); - Some(ConstInt::U32(input as u32)) - }, - ast::UintTy::U64 => { - Some(ConstInt::U64(input)) - }, - ast::UintTy::Us => { - ConstUsize::new(input, tcx.sess.target.uint_type) - .ok().map(ConstInt::Usize) - }, - }), - _ => None, + ty::TyInt(int_type) => const_to_opt_u128(value, true) + .and_then(|input| ConstInt::new_signed(input as i128, int_type, + tcx.sess.target.int_type)), + ty::TyUint(uint_type) => const_to_opt_u128(value, false) + .and_then(|input| ConstInt::new_unsigned(input, uint_type, + tcx.sess.target.uint_type)), + _ => None + } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6e9b66d564555ca6fe38dc8a1652269e636447d7..1c8c63a14459da8abd41541ba6f8af47684e8de5 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1653,6 +1653,7 @@ fn from(int_ty: ast::IntTy) -> PrimitiveType { ast::IntTy::I16 => PrimitiveType::I16, ast::IntTy::I32 => PrimitiveType::I32, ast::IntTy::I64 => PrimitiveType::I64, + ast::IntTy::I128 => PrimitiveType::I128, } } } @@ -1665,6 +1666,7 @@ fn from(uint_ty: ast::UintTy) -> PrimitiveType { ast::UintTy::U16 => PrimitiveType::U16, ast::UintTy::U32 => PrimitiveType::U32, ast::UintTy::U64 => PrimitiveType::U64, + ast::UintTy::U128 => PrimitiveType::U128, } } } @@ -2489,7 +2491,7 @@ fn clean(&self, cx: &DocContext) -> Vec { fn build_deref_target_impls(cx: &DocContext, items: &[Item], ret: &mut Vec) { - use PrimitiveType::*; + use self::PrimitiveType::*; let tcx = cx.tcx; for item in items { diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 5d5845213e2301a7add72251249e2967416f9309..81093249318001f8504bcdb122b4a57422ed1c98 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -1456,6 +1456,22 @@ extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) { extern "C" void LLVMRustSetLinkage(LLVMValueRef V, LLVMRustLinkage RustLinkage) { LLVMSetLinkage(V, from_rust(RustLinkage)); + +// Returns true if both high and low were successfully set. Fails in case constant wasn’t any of +// the common sizes (1, 8, 16, 32, 64, 128 bits) +extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *high, uint64_t *low) +{ + auto C = unwrap(CV); + if (C->getBitWidth() > 128) { return false; } + APInt AP; + if (sext) { + AP = C->getValue().sextOrSelf(128); + } else { + AP = C->getValue().zextOrSelf(128); + } + *low = AP.getLoBits(64).getZExtValue(); + *high = AP.getHiBits(64).getZExtValue(); + return true; } extern "C" LLVMContextRef LLVMRustGetValueContext(LLVMValueRef V) {