Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIR] Implement coercions and improve fat pointer support #29781

Merged
merged 6 commits into from
Nov 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions src/librustc_mir/hair/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,14 +386,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
};
}

if let Some(target) = adj.unsize {
expr = Expr {
temp_lifetime: temp_lifetime,
ty: target,
span: self.span,
kind: ExprKind::Unsize { source: expr.to_ref() },
};
} else if let Some(autoref) = adj.autoref {
if let Some(autoref) = adj.autoref {
let adjusted_ty = expr.ty.adjust_for_autoref(cx.tcx, Some(autoref));
match autoref {
ty::adjustment::AutoPtr(r, m) => {
Expand Down Expand Up @@ -433,6 +426,15 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
}
}
}

if let Some(target) = adj.unsize {
expr = Expr {
temp_lifetime: temp_lifetime,
ty: target,
span: self.span,
kind: ExprKind::Unsize { source: expr.to_ref() },
};
}
}
}

Expand Down
63 changes: 63 additions & 0 deletions src/librustc_mir/tcx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use repr::*;
use rustc::middle::subst::Substs;
use rustc::middle::ty::{self, AdtDef, Ty};
use rustc_front::hir;

#[derive(Copy, Clone, Debug)]
pub enum LvalueTy<'tcx> {
Expand Down Expand Up @@ -102,6 +103,31 @@ impl<'tcx> Mir<'tcx> {
}
}

pub fn binop_ty(&self,
tcx: &ty::ctxt<'tcx>,
op: BinOp,
lhs_ty: Ty<'tcx>,
rhs_ty: Ty<'tcx>)
-> Ty<'tcx>
{
// FIXME: handle SIMD correctly
match op {
BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div | BinOp::Rem |
BinOp::BitXor | BinOp::BitAnd | BinOp::BitOr => {
// these should be integers or floats of the same size.
assert_eq!(lhs_ty, rhs_ty);
lhs_ty
}
BinOp::Shl | BinOp::Shr => {
lhs_ty // lhs_ty can be != rhs_ty
}
BinOp::Eq | BinOp::Lt | BinOp::Le |
BinOp::Ne | BinOp::Ge | BinOp::Gt => {
tcx.types.bool
}
}
}

pub fn lvalue_ty(&self,
tcx: &ty::ctxt<'tcx>,
lvalue: &Lvalue<'tcx>)
Expand All @@ -123,3 +149,40 @@ impl<'tcx> Mir<'tcx> {
}
}
}

impl BorrowKind {
pub fn to_mutbl_lossy(self) -> hir::Mutability {
match self {
BorrowKind::Mut => hir::MutMutable,
BorrowKind::Shared => hir::MutImmutable,

// We have no type corresponding to a unique imm borrow, so
// use `&mut`. It gives all the capabilities of an `&uniq`
// and hence is a safe "over approximation".
BorrowKind::Unique => hir::MutMutable,
}
}
}

impl BinOp {
pub fn to_hir_binop(self) -> hir::BinOp_ {
match self {
BinOp::Add => hir::BinOp_::BiAdd,
BinOp::Sub => hir::BinOp_::BiSub,
BinOp::Mul => hir::BinOp_::BiMul,
BinOp::Div => hir::BinOp_::BiDiv,
BinOp::Rem => hir::BinOp_::BiRem,
BinOp::BitXor => hir::BinOp_::BiBitXor,
BinOp::BitAnd => hir::BinOp_::BiBitAnd,
BinOp::BitOr => hir::BinOp_::BiBitOr,
BinOp::Shl => hir::BinOp_::BiShl,
BinOp::Shr => hir::BinOp_::BiShr,
BinOp::Eq => hir::BinOp_::BiEq,
BinOp::Ne => hir::BinOp_::BiNe,
BinOp::Lt => hir::BinOp_::BiLt,
BinOp::Gt => hir::BinOp_::BiGt,
BinOp::Le => hir::BinOp_::BiLe,
BinOp::Ge => hir::BinOp_::BiGe
}
}
}
202 changes: 201 additions & 1 deletion src/librustc_trans/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use trans::builder::{Builder, noname};
use trans::callee;
use trans::cleanup::{self, CleanupMethods, DropHint};
use trans::closure;
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral};
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_uint, C_integral};
use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext};
use trans::common::{Result, NodeIdAndSpan, VariantInfo};
Expand Down Expand Up @@ -312,6 +312,49 @@ pub fn bin_op_to_fcmp_predicate(ccx: &CrateContext, op: hir::BinOp_)
}
}

pub fn compare_fat_ptrs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
lhs_addr: ValueRef,
lhs_extra: ValueRef,
rhs_addr: ValueRef,
rhs_extra: ValueRef,
_t: Ty<'tcx>,
op: hir::BinOp_,
debug_loc: DebugLoc)
-> ValueRef {
match op {
hir::BiEq => {
let addr_eq = ICmp(bcx, llvm::IntEQ, lhs_addr, rhs_addr, debug_loc);
let extra_eq = ICmp(bcx, llvm::IntEQ, lhs_extra, rhs_extra, debug_loc);
And(bcx, addr_eq, extra_eq, debug_loc)
}
hir::BiNe => {
let addr_eq = ICmp(bcx, llvm::IntNE, lhs_addr, rhs_addr, debug_loc);
let extra_eq = ICmp(bcx, llvm::IntNE, lhs_extra, rhs_extra, debug_loc);
Or(bcx, addr_eq, extra_eq, debug_loc)
}
hir::BiLe | hir::BiLt | hir::BiGe | hir::BiGt => {
// a OP b ~ a.0 STRICT(OP) b.0 | (a.0 == b.0 && a.1 OP a.1)
let (op, strict_op) = match op {
hir::BiLt => (llvm::IntULT, llvm::IntULT),
hir::BiLe => (llvm::IntULE, llvm::IntULT),
hir::BiGt => (llvm::IntUGT, llvm::IntUGT),
hir::BiGe => (llvm::IntUGE, llvm::IntUGT),
_ => unreachable!()
};

let addr_eq = ICmp(bcx, llvm::IntEQ, lhs_addr, rhs_addr, debug_loc);
let extra_op = ICmp(bcx, op, lhs_extra, rhs_extra, debug_loc);
let addr_eq_extra_op = And(bcx, addr_eq, extra_op, debug_loc);

let addr_strict = ICmp(bcx, strict_op, lhs_addr, rhs_addr, debug_loc);
Or(bcx, addr_strict, addr_eq_extra_op, debug_loc)
}
_ => {
bcx.tcx().sess.bug("unexpected fat ptr binop");
}
}
}

pub fn compare_scalar_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
lhs: ValueRef,
rhs: ValueRef,
Expand All @@ -336,6 +379,17 @@ pub fn compare_scalar_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ty::TyRawPtr(mt) if common::type_is_sized(bcx.tcx(), mt.ty) => {
ICmp(bcx, bin_op_to_icmp_predicate(bcx.ccx(), op, false), lhs, rhs, debug_loc)
}
ty::TyRawPtr(_) => {
let lhs_addr = Load(bcx, GEPi(bcx, lhs, &[0, abi::FAT_PTR_ADDR]));
let lhs_extra = Load(bcx, GEPi(bcx, lhs, &[0, abi::FAT_PTR_EXTRA]));

let rhs_addr = Load(bcx, GEPi(bcx, rhs, &[0, abi::FAT_PTR_ADDR]));
let rhs_extra = Load(bcx, GEPi(bcx, rhs, &[0, abi::FAT_PTR_EXTRA]));
compare_fat_ptrs(bcx,
lhs_addr, lhs_extra,
rhs_addr, rhs_extra,
t, op, debug_loc)
}
ty::TyInt(_) => {
ICmp(bcx, bin_op_to_icmp_predicate(bcx.ccx(), op, true), lhs, rhs, debug_loc)
}
Expand Down Expand Up @@ -523,6 +577,129 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
return cx;
}


/// Retrieve the information we are losing (making dynamic) in an unsizing
/// adjustment.
///
/// The `old_info` argument is a bit funny. It is intended for use
/// in an upcast, where the new vtable for an object will be drived
/// from the old one.
pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
source: Ty<'tcx>,
target: Ty<'tcx>,
old_info: Option<ValueRef>,
param_substs: &'tcx Substs<'tcx>)
-> ValueRef {
let (source, target) = ccx.tcx().struct_lockstep_tails(source, target);
match (&source.sty, &target.sty) {
(&ty::TyArray(_, len), &ty::TySlice(_)) => C_uint(ccx, len),
(&ty::TyTrait(_), &ty::TyTrait(_)) => {
// For now, upcasts are limited to changes in marker
// traits, and hence never actually require an actual
// change to the vtable.
old_info.expect("unsized_info: missing old info for trait upcast")
}
(_, &ty::TyTrait(box ty::TraitTy { ref principal, .. })) => {
// Note that we preserve binding levels here:
let substs = principal.0.substs.with_self_ty(source).erase_regions();
let substs = ccx.tcx().mk_substs(substs);
let trait_ref = ty::Binder(ty::TraitRef { def_id: principal.def_id(),
substs: substs });
consts::ptrcast(meth::get_vtable(ccx, trait_ref, param_substs),
Type::vtable_ptr(ccx))
}
_ => ccx.sess().bug(&format!("unsized_info: invalid unsizing {:?} -> {:?}",
source,
target))
}
}

/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
pub fn unsize_thin_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
src: ValueRef,
src_ty: Ty<'tcx>,
dst_ty: Ty<'tcx>)
-> (ValueRef, ValueRef) {
debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty);
match (&src_ty.sty, &dst_ty.sty) {
(&ty::TyBox(a), &ty::TyBox(b)) |
(&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }),
&ty::TyRef(_, ty::TypeAndMut { ty: b, .. })) |
(&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }),
&ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) |
(&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }),
&ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => {
assert!(common::type_is_sized(bcx.tcx(), a));
let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), b).ptr_to();
(PointerCast(bcx, src, ptr_ty),
unsized_info(bcx.ccx(), a, b, None, bcx.fcx.param_substs))
}
_ => bcx.sess().bug(
&format!("unsize_thin_ptr: called on bad types"))
}
}

/// Coerce `src`, which is a reference to a value of type `src_ty`,
/// to a value of type `dst_ty` and store the result in `dst`
pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
src: ValueRef,
src_ty: Ty<'tcx>,
dst: ValueRef,
dst_ty: Ty<'tcx>) {
match (&src_ty.sty, &dst_ty.sty) {
(&ty::TyBox(..), &ty::TyBox(..)) |
(&ty::TyRef(..), &ty::TyRef(..)) |
(&ty::TyRef(..), &ty::TyRawPtr(..)) |
(&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => {
let (base, info) = if common::type_is_fat_ptr(bcx.tcx(), src_ty) {
// fat-ptr to fat-ptr unsize preserves the vtable
load_fat_ptr(bcx, src, src_ty)
} else {
let base = load_ty(bcx, src, src_ty);
unsize_thin_ptr(bcx, base, src_ty, dst_ty)
};
store_fat_ptr(bcx, base, info, dst, dst_ty);
}

// This can be extended to enums and tuples in the future.
// (&ty::TyEnum(def_id_a, _), &ty::TyEnum(def_id_b, _)) |
(&ty::TyStruct(def_a, _), &ty::TyStruct(def_b, _)) => {
assert_eq!(def_a, def_b);

let src_repr = adt::represent_type(bcx.ccx(), src_ty);
let src_fields = match &*src_repr {
&adt::Repr::Univariant(ref s, _) => &s.fields,
_ => bcx.sess().bug("struct has non-univariant repr")
};
let dst_repr = adt::represent_type(bcx.ccx(), dst_ty);
let dst_fields = match &*dst_repr {
&adt::Repr::Univariant(ref s, _) => &s.fields,
_ => bcx.sess().bug("struct has non-univariant repr")
};

let iter = src_fields.iter().zip(dst_fields).enumerate();
for (i, (src_fty, dst_fty)) in iter {
if type_is_zero_size(bcx.ccx(), dst_fty) { continue; }

let src_f = adt::trans_field_ptr(bcx, &src_repr, src, 0, i);
let dst_f = adt::trans_field_ptr(bcx, &dst_repr, dst, 0, i);
if src_fty == dst_fty {
memcpy_ty(bcx, dst_f, src_f, src_fty);
} else {
coerce_unsized_into(
bcx,
src_f, src_fty,
dst_f, dst_fty
);
}
}
}
_ => bcx.sess().bug(&format!("coerce_unsized_into: invalid coercion {:?} -> {:?}",
src_ty,
dst_ty))
}
}

pub fn cast_shift_expr_rhs(cx: Block,
op: hir::BinOp_,
lhs: ValueRef,
Expand Down Expand Up @@ -828,6 +1005,10 @@ pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t
return;
}

debug!("store_ty: {} : {:?} <- {}",
cx.val_to_string(dst), t,
cx.val_to_string(v));

if common::type_is_fat_ptr(cx.tcx(), t) {
Store(cx, ExtractValue(cx, v, abi::FAT_PTR_ADDR), expr::get_dataptr(cx, dst));
Store(cx, ExtractValue(cx, v, abi::FAT_PTR_EXTRA), expr::get_meta(cx, dst));
Expand All @@ -839,6 +1020,25 @@ pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t
}
}

pub fn store_fat_ptr<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
data: ValueRef,
extra: ValueRef,
dst: ValueRef,
_ty: Ty<'tcx>) {
// FIXME: emit metadata
Store(cx, data, expr::get_dataptr(cx, dst));
Store(cx, extra, expr::get_meta(cx, dst));
}

pub fn load_fat_ptr<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
src: ValueRef,
_ty: Ty<'tcx>) -> (ValueRef, ValueRef)
{
// FIXME: emit metadata
(Load(cx, expr::get_dataptr(cx, src)),
Load(cx, expr::get_meta(cx, src)))
}

pub fn from_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef {
if ty.is_bool() {
ZExt(bcx, val, Type::i8(bcx.ccx()))
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1223,4 +1223,4 @@ pub fn get_static_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
} else {
base::get_extern_const(ccx, did, ty)
}
}
}
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
.expect("consts: unsizing got non-pointer target type").ty;
let ptr_ty = type_of::in_memory_type_of(cx, unsized_ty).ptr_to();
let base = ptrcast(base, ptr_ty);
let info = expr::unsized_info(cx, pointee_ty, unsized_ty,
let info = base::unsized_info(cx, pointee_ty, unsized_ty,
old_info, param_substs);

if old_info.is_none() {
Expand Down
Loading