提交 de3e581e 编写于 作者: E Eduard-Mihai Burtescu

rustc: support u128 discriminant ranges.

上级 018323ff
......@@ -46,6 +46,7 @@
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(drain_filter)]
#![feature(i128)]
#![feature(i128_type)]
#![feature(inclusive_range)]
#![feature(inclusive_range_syntax)]
......
......@@ -20,7 +20,7 @@
use std::cmp;
use std::fmt;
use std::i64;
use std::i128;
use std::iter;
use std::mem;
use std::ops::{Add, Sub, Mul, AddAssign, Deref, RangeInclusive};
......@@ -467,7 +467,7 @@ pub fn to_ty(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, signed: bool) -> Ty<'tcx> {
}
/// Find the smallest Integer type which can represent the signed value.
pub fn fit_signed(x: i64) -> Integer {
pub fn fit_signed(x: i128) -> Integer {
match x {
-0x0000_0000_0000_0001...0x0000_0000_0000_0000 => I1,
-0x0000_0000_0000_0080...0x0000_0000_0000_007f => I8,
......@@ -479,7 +479,7 @@ pub fn fit_signed(x: i64) -> Integer {
}
/// Find the smallest Integer type which can represent the unsigned value.
pub fn fit_unsigned(x: u64) -> Integer {
pub fn fit_unsigned(x: u128) -> Integer {
match x {
0...0x0000_0000_0000_0001 => I1,
0...0x0000_0000_0000_00ff => I8,
......@@ -495,7 +495,7 @@ pub fn for_abi_align<C: HasDataLayout>(cx: C, align: Align) -> Option<Integer> {
let dl = cx.data_layout();
let wanted = align.abi();
for &candidate in &[I8, I16, I32, I64] {
for &candidate in &[I8, I16, I32, I64, I128] {
let ty = Int(candidate, false);
if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
return Some(candidate);
......@@ -522,19 +522,19 @@ pub fn from_attr<C: HasDataLayout>(cx: C, ity: attr::IntType) -> Integer {
/// Find the appropriate Integer type and signedness for the given
/// signed discriminant range and #[repr] attribute.
/// N.B.: u64 values above i64::MAX will be treated as signed, but
/// N.B.: u128 values above i128::MAX will be treated as signed, but
/// that shouldn't affect anything, other than maybe debuginfo.
fn repr_discr(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty: Ty<'tcx>,
repr: &ReprOptions,
min: i64,
max: i64)
min: i128,
max: i128)
-> (Integer, bool) {
// Theoretically, negative values could be larger in unsigned representation
// than the unsigned representation of the signed minimum. However, if there
// are any negative values, the only valid unsigned representation is u64
// which can fit all i64 values, so the result remains unaffected.
let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u64, max as u64));
// are any negative values, the only valid unsigned representation is u128
// which can fit all i128 values, so the result remains unaffected.
let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
let mut min_from_extern = None;
......@@ -782,11 +782,11 @@ pub enum Variants {
Tagged {
discr: Primitive,
/// Inclusive wrap-around range of discriminant values, that is,
/// if min > max, it represents min..=u64::MAX followed by 0..=max.
/// if min > max, it represents min..=u128::MAX followed by 0..=max.
// FIXME(eddyb) always use the shortest range, e.g. by finding
// the largest space between two consecutive discriminants and
// taking everything else as the (shortest) discriminant range.
discr_range: RangeInclusive<u64>,
discr_range: RangeInclusive<u128>,
variants: Vec<CachedLayout>,
},
......@@ -1375,14 +1375,12 @@ enum StructKind {
}
}
let (mut min, mut max) = (i64::max_value(), i64::min_value());
let (mut min, mut max) = (i128::max_value(), i128::min_value());
for discr in def.discriminants(tcx) {
let x = discr.to_u128_unchecked() as i64;
let x = discr.to_u128_unchecked() as i128;
if x < min { min = x; }
if x > max { max = x; }
}
// FIXME: should handle i128? signed-value based impl is weird and hard to
// grok.
let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max);
let mut align = dl.aggregate_align;
......@@ -1479,9 +1477,7 @@ enum StructKind {
tcx.intern_layout(CachedLayout {
variants: Variants::Tagged {
discr,
// FIXME: should be u128?
discr_range: (min as u64)..=(max as u64),
discr_range: (min as u128)..=(max as u128),
variants
},
// FIXME(eddyb): using `FieldPlacement::Arbitrary` here results
......
......@@ -24,6 +24,7 @@
use std::borrow::Cow;
use std::ffi::CString;
use std::ops::Range;
use std::ptr;
use syntax_pos::Span;
......@@ -549,35 +550,26 @@ pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering, align: Align) ->
}
pub fn load_range_assert(&self, ptr: ValueRef, lo: u64,
hi: u64, signed: llvm::Bool,
align: Option<Align>) -> ValueRef {
let value = self.load(ptr, align);
pub fn range_metadata(&self, load: ValueRef, range: Range<u128>) {
unsafe {
let t = llvm::LLVMGetElementType(llvm::LLVMTypeOf(ptr));
let min = llvm::LLVMConstInt(t, lo, signed);
let max = llvm::LLVMConstInt(t, hi, signed);
let v = [min, max];
let llty = val_ty(load);
let v = [
C_uint_big(llty, range.start),
C_uint_big(llty, range.end)
];
llvm::LLVMSetMetadata(value, llvm::MD_range as c_uint,
llvm::LLVMSetMetadata(load, llvm::MD_range as c_uint,
llvm::LLVMMDNodeInContext(self.ccx.llcx(),
v.as_ptr(),
v.len() as c_uint));
}
value
}
pub fn load_nonnull(&self, ptr: ValueRef, align: Option<Align>) -> ValueRef {
let value = self.load(ptr, align);
pub fn nonnull_metadata(&self, load: ValueRef) {
unsafe {
llvm::LLVMSetMetadata(value, llvm::MD_nonnull as c_uint,
llvm::LLVMSetMetadata(load, llvm::MD_nonnull as c_uint,
llvm::LLVMMDNodeInContext(self.ccx.llcx(), ptr::null(), 0));
}
value
}
pub fn store(&self, val: ValueRef, ptr: ValueRef, align: Option<Align>) -> ValueRef {
......
......@@ -178,9 +178,9 @@ pub fn C_uint(t: Type, i: u64) -> ValueRef {
}
}
pub fn C_big_integral(t: Type, u: u128) -> ValueRef {
pub fn C_uint_big(t: Type, u: u128) -> ValueRef {
unsafe {
let words = [u as u64, u.wrapping_shr(64) as u64];
let words = [u as u64, (u >> 64) as u64];
llvm::LLVMConstIntOfArbitraryPrecision(t.to_ref(), 2, words.as_ptr())
}
}
......
......@@ -40,7 +40,8 @@ pub fn get_fn(self, bcx: &Builder<'a, 'tcx>,
debug!("get_fn({:?}, {:?})", Value(llvtable), self);
let llvtable = bcx.pointercast(llvtable, fn_ty.llvm_type(bcx.ccx).ptr_to().ptr_to());
let ptr = bcx.load_nonnull(bcx.inbounds_gep(llvtable, &[C_usize(bcx.ccx, self.0)]), None);
let ptr = bcx.load(bcx.inbounds_gep(llvtable, &[C_usize(bcx.ccx, self.0)]), None);
bcx.nonnull_metadata(ptr);
// Vtable loads are invariant
bcx.set_invariant_load(ptr);
ptr
......
......@@ -666,17 +666,18 @@ fn trans_argument(&mut self,
if by_ref && !arg.is_indirect() {
// Have to load the argument, maybe while casting it.
if arg.layout.ty == bcx.tcx().types.bool {
llval = bcx.load_range_assert(llval, 0, 2, llvm::False, None);
// We store bools as i8 so we need to truncate to i1.
llval = base::to_immediate(bcx, llval, arg.layout);
} else if let Some(ty) = arg.cast {
if let Some(ty) = arg.cast {
llval = bcx.load(bcx.pointercast(llval, ty.llvm_type(bcx.ccx).ptr_to()),
(align | Alignment::Packed(arg.layout.align))
.non_abi());
} else {
llval = bcx.load(llval, align.non_abi());
}
if arg.layout.ty == bcx.tcx().types.bool {
bcx.range_metadata(llval, 0..2);
// We store bools as i8 so we need to truncate to i1.
llval = base::to_immediate(bcx, llval, arg.layout);
}
}
llargs.push(llval);
......
......@@ -28,7 +28,7 @@
use callee;
use builder::Builder;
use common::{self, CrateContext, const_get_elt, val_ty};
use common::{C_array, C_bool, C_bytes, C_int, C_uint, C_big_integral, C_u32, C_u64};
use common::{C_array, C_bool, C_bytes, C_int, C_uint, C_uint_big, C_u32, C_u64};
use common::{C_null, C_struct, C_str_slice, C_undef, C_usize, C_vector, C_fat_ptr};
use common::const_to_opt_u128;
use consts;
......@@ -70,13 +70,13 @@ pub fn from_constint(ccx: &CrateContext<'a, 'tcx>, ci: &ConstInt) -> Const<'tcx>
I16(v) => (C_int(Type::i16(ccx), v as i64), tcx.types.i16),
I32(v) => (C_int(Type::i32(ccx), v as i64), tcx.types.i32),
I64(v) => (C_int(Type::i64(ccx), v as i64), tcx.types.i64),
I128(v) => (C_big_integral(Type::i128(ccx), v as u128), tcx.types.i128),
I128(v) => (C_uint_big(Type::i128(ccx), v as u128), tcx.types.i128),
Isize(v) => (C_int(Type::isize(ccx), v.as_i64()), tcx.types.isize),
U8(v) => (C_uint(Type::i8(ccx), v as u64), tcx.types.u8),
U16(v) => (C_uint(Type::i16(ccx), v as u64), tcx.types.u16),
U32(v) => (C_uint(Type::i32(ccx), v as u64), tcx.types.u32),
U64(v) => (C_uint(Type::i64(ccx), v), tcx.types.u64),
U128(v) => (C_big_integral(Type::i128(ccx), v), tcx.types.u128),
U128(v) => (C_uint_big(Type::i128(ccx), v), tcx.types.u128),
Usize(v) => (C_uint(Type::isize(ccx), v.as_u64()), tcx.types.usize),
};
Const { llval: llval, ty: ty }
......@@ -994,7 +994,7 @@ unsafe fn cast_const_float_to_int(ccx: &CrateContext,
let err = ConstEvalErr { span: span, kind: ErrKind::CannotCast };
err.report(ccx.tcx(), span, "expression");
}
C_big_integral(int_ty, cast_result.value)
C_uint_big(int_ty, cast_result.value)
}
unsafe fn cast_const_int_to_float(ccx: &CrateContext,
......
......@@ -14,10 +14,9 @@
use rustc::mir;
use rustc::mir::tcx::LvalueTy;
use rustc_data_structures::indexed_vec::Idx;
use abi;
use base;
use builder::Builder;
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null, val_ty};
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null};
use consts;
use type_of::LayoutLlvmExt;
use type_::Type;
......@@ -140,30 +139,7 @@ pub fn load(&self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> {
return OperandRef::new_zst(bcx.ccx, self.layout);
}
let val = if common::type_is_fat_ptr(bcx.ccx, self.layout.ty) {
let data = self.project_field(bcx, abi::FAT_PTR_ADDR);
let lldata = if self.layout.ty.is_region_ptr() || self.layout.ty.is_box() {
bcx.load_nonnull(data.llval, data.alignment.non_abi())
} else {
bcx.load(data.llval, data.alignment.non_abi())
};
let extra = self.project_field(bcx, abi::FAT_PTR_EXTRA);
let meta_ty = val_ty(extra.llval);
// If the 'extra' field is a pointer, it's a vtable, so use load_nonnull
// instead
let llextra = if meta_ty.element_type().kind() == llvm::TypeKind::Pointer {
bcx.load_nonnull(extra.llval, extra.alignment.non_abi())
} else {
bcx.load(extra.llval, extra.alignment.non_abi())
};
OperandValue::Pair(lldata, llextra)
} else if common::type_is_imm_pair(bcx.ccx, self.layout.ty) {
OperandValue::Pair(
self.project_field(bcx, 0).load(bcx).pack_if_pair(bcx).immediate(),
self.project_field(bcx, 1).load(bcx).pack_if_pair(bcx).immediate())
} else if self.layout.is_llvm_immediate() {
let val = if self.layout.is_llvm_immediate() {
let mut const_llval = ptr::null_mut();
unsafe {
let global = llvm::LLVMIsAGlobalVariable(self.llval);
......@@ -174,22 +150,26 @@ pub fn load(&self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> {
let llval = if !const_llval.is_null() {
const_llval
} else if self.layout.ty.is_bool() {
bcx.load_range_assert(self.llval, 0, 2, llvm::False,
self.alignment.non_abi())
} else if self.layout.ty.is_char() {
// a char is a Unicode codepoint, and so takes values from 0
// to 0x10FFFF inclusive only.
bcx.load_range_assert(self.llval, 0, 0x10FFFF + 1, llvm::False,
self.alignment.non_abi())
} else if self.layout.ty.is_region_ptr() ||
self.layout.ty.is_box() ||
self.layout.ty.is_fn() {
bcx.load_nonnull(self.llval, self.alignment.non_abi())
} else {
bcx.load(self.llval, self.alignment.non_abi())
let load = bcx.load(self.llval, self.alignment.non_abi());
if self.layout.ty.is_bool() {
bcx.range_metadata(load, 0..2);
} else if self.layout.ty.is_char() {
// a char is a Unicode codepoint, and so takes values from 0
// to 0x10FFFF inclusive only.
bcx.range_metadata(load, 0..0x10FFFF+1);
} else if self.layout.ty.is_region_ptr() ||
self.layout.ty.is_box() ||
self.layout.ty.is_fn() {
bcx.nonnull_metadata(load);
}
load
};
OperandValue::Immediate(base::to_immediate(bcx, llval, self.layout))
} else if common::type_is_imm_pair(bcx.ccx, self.layout.ty) {
OperandValue::Pair(
self.project_field(bcx, 0).load(bcx).pack_if_pair(bcx).immediate(),
self.project_field(bcx, 1).load(bcx).pack_if_pair(bcx).immediate())
} else {
OperandValue::Ref(self.llval, self.alignment)
};
......@@ -314,28 +294,26 @@ pub fn trans_get_discr(self, bcx: &Builder<'a, 'tcx>, cast_to: Ty<'tcx>) -> Valu
layout::Variants::Tagged { ref discr_range, .. } => {
(discr_range.start, discr_range.end)
}
_ => (0, u64::max_value()),
_ => (0, !0),
};
let max_next = max.wrapping_add(1);
let bits = discr_scalar.size(bcx.ccx).bits();
assert!(bits <= 64);
let mask = !0u64 >> (64 - bits);
let lldiscr = match discr_scalar {
assert!(bits <= 128);
let mask = !0u128 >> (128 - bits);
let lldiscr = bcx.load(discr.llval, discr.alignment.non_abi());
match discr_scalar {
// For a (max) discr of -1, max will be `-1 as usize`, which overflows.
// However, that is fine here (it would still represent the full range),
layout::Int(..) if max_next & mask != min & mask => {
// llvm::ConstantRange can deal with ranges that wrap around,
// so an overflow on (max + 1) is fine.
bcx.load_range_assert(discr.llval, min, max_next,
/* signed: */ llvm::True,
discr.alignment.non_abi())
bcx.range_metadata(lldiscr, min..max_next);
}
_ => {
// i.e., if the range is everything. The lo==hi case would be
// rejected by the LLVM verifier (it would mean either an
// empty set, which is impossible, or the entire range of the
// type, which is pointless).
bcx.load(discr.llval, discr.alignment.non_abi())
}
};
match self.layout.variants {
......
......@@ -23,7 +23,7 @@
use builder::Builder;
use callee;
use common::{self, val_ty};
use common::{C_bool, C_u8, C_i32, C_u32, C_u64, C_null, C_usize, C_uint, C_big_integral};
use common::{C_bool, C_u8, C_i32, C_u32, C_u64, C_null, C_usize, C_uint, C_uint_big};
use consts;
use monomorphize;
use type_::Type;
......@@ -289,7 +289,7 @@ pub fn trans_rvalue_operand(&mut self,
base::call_assume(&bcx, bcx.icmp(
llvm::IntULE,
llval,
C_uint(ll_t_in, discr_range.end)
C_uint_big(ll_t_in, discr_range.end)
));
}
_ => {}
......@@ -807,7 +807,7 @@ fn cast_int_to_float(bcx: &Builder,
if is_u128_to_f32 {
// All inputs greater or equal to (f32::MAX + 0.5 ULP) are rounded to infinity,
// and for everything else LLVM's uitofp works just fine.
let max = C_big_integral(int_ty, MAX_F32_PLUS_HALF_ULP);
let max = C_uint_big(int_ty, MAX_F32_PLUS_HALF_ULP);
let overflow = bcx.icmp(llvm::IntUGE, x, max);
let infinity_bits = C_u32(bcx.ccx, ieee::Single::INFINITY.to_bits() as u32);
let infinity = consts::bitcast(infinity_bits, float_ty);
......@@ -934,8 +934,8 @@ fn int_min(signed: bool, int_ty: Type) -> i128 {
// performed is ultimately up to the backend, but at least x86 does perform them.
let less_or_nan = bcx.fcmp(llvm::RealULT, x, f_min);
let greater = bcx.fcmp(llvm::RealOGT, x, f_max);
let int_max = C_big_integral(int_ty, int_max(signed, int_ty));
let int_min = C_big_integral(int_ty, int_min(signed, int_ty) as u128);
let int_max = C_uint_big(int_ty, int_max(signed, int_ty));
let int_min = C_uint_big(int_ty, int_min(signed, int_ty) as u128);
let s0 = bcx.select(less_or_nan, int_min, fptosui_result);
let s1 = bcx.select(greater, int_max, s0);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册