From 602cf7ec3c69c5c9b7d04c7baf66b270f565a001 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda <ariel.byd@gmail.com> Date: Mon, 9 Nov 2015 02:16:19 +0200 Subject: [PATCH 1/6] MIR: implement fat raw pointer comparisons The implementation itself only requires changes to trans, but a few additional bugs concerning the handling of fat pointers had to be fixed. --- src/librustc_trans/trans/base.rs | 44 ++++++ src/librustc_trans/trans/expr.rs | 62 ++------- src/librustc_trans/trans/mir/analyze.rs | 9 +- src/librustc_trans/trans/mir/mod.rs | 1 - src/librustc_trans/trans/mir/rvalue.rs | 51 ++++++- src/test/run-pass/mir_raw_fat_ptr.rs | 173 ++++++++++++++++++++++++ 6 files changed, 279 insertions(+), 61 deletions(-) create mode 100644 src/test/run-pass/mir_raw_fat_ptr.rs diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 4a99f2142d7..be4758581e9 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -336,6 +336,46 @@ 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])); + + 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"); + } + } + } ty::TyInt(_) => { ICmp(bcx, bin_op_to_icmp_predicate(bcx.ccx(), op, true), lhs, rhs, debug_loc) } @@ -828,6 +868,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)); diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index f408bb595a2..eb9635066f3 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1725,58 +1725,6 @@ fn trans_addr_of<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } -fn trans_fat_ptr_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - binop_expr: &hir::Expr, - binop_ty: Ty<'tcx>, - op: hir::BinOp, - lhs: Datum<'tcx, Rvalue>, - rhs: Datum<'tcx, Rvalue>) - -> DatumBlock<'blk, 'tcx, Expr> -{ - let debug_loc = binop_expr.debug_loc(); - - let lhs_addr = Load(bcx, GEPi(bcx, lhs.val, &[0, abi::FAT_PTR_ADDR])); - let lhs_extra = Load(bcx, GEPi(bcx, lhs.val, &[0, abi::FAT_PTR_EXTRA])); - - let rhs_addr = Load(bcx, GEPi(bcx, rhs.val, &[0, abi::FAT_PTR_ADDR])); - let rhs_extra = Load(bcx, GEPi(bcx, rhs.val, &[0, abi::FAT_PTR_EXTRA])); - - let val = match op.node { - 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.node { - 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.span_bug(binop_expr.span, "unexpected binop"); - } - }; - - immediate_rvalue_bcx(bcx, val, binop_ty).to_expr_datumblock() -} - fn trans_scalar_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, binop_expr: &hir::Expr, binop_ty: Ty<'tcx>, @@ -2005,7 +1953,15 @@ fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, if type_is_fat_ptr(ccx.tcx(), lhs.ty) { assert!(type_is_fat_ptr(ccx.tcx(), rhs.ty), "built-in binary operators on fat pointers are homogeneous"); - trans_fat_ptr_binop(bcx, expr, binop_ty, op, lhs, rhs) + assert_eq!(binop_ty, bcx.tcx().types.bool); + let val = base::compare_scalar_types( + bcx, + lhs.val, + rhs.val, + lhs.ty, + op.node, + expr.debug_loc()); + immediate_rvalue_bcx(bcx, val, binop_ty).to_expr_datumblock() } else { assert!(!type_is_fat_ptr(ccx.tcx(), rhs.ty), "built-in binary operators on fat pointers are homogeneous"); diff --git a/src/librustc_trans/trans/mir/analyze.rs b/src/librustc_trans/trans/mir/analyze.rs index f5fa897bca6..cf1ecc4d6da 100644 --- a/src/librustc_trans/trans/mir/analyze.rs +++ b/src/librustc_trans/trans/mir/analyze.rs @@ -28,10 +28,11 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, let ty = bcx.monomorphize(&temp_decl.ty); debug!("temp {:?} has type {:?}", index, ty); if - ty.is_scalar() || - ty.is_unique() || - (ty.is_region_ptr() && !common::type_is_fat_ptr(bcx.tcx(), ty)) || - ty.is_simd() + (ty.is_scalar() || + ty.is_unique() || + ty.is_region_ptr() || + ty.is_simd()) + && !common::type_is_fat_ptr(bcx.tcx(), ty) { // These sorts of types are immediates that we can store // in an ValueRef without an alloca. diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 75fb88ab5ee..c21103fde96 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -192,4 +192,3 @@ mod lvalue; mod rvalue; mod operand; mod statement; - diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 218edc200d4..65fb0d91e4d 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -9,7 +9,7 @@ // except according to those terms. use llvm::ValueRef; -use rustc::middle::ty::Ty; +use rustc::middle::ty::{self, Ty}; use rustc_front::hir; use rustc_mir::repr as mir; @@ -45,6 +45,19 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx } + mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, cast_ty) => { + let expr_ty = + bcx.monomorphize(&self.mir.operand_ty(bcx.tcx(), operand)); + let cast_ty = + bcx.monomorphize(&cast_ty); + if expr_ty == cast_ty { + debug!("trans_rvalue: trivial unsize at {:?}", expr_ty); + self.trans_operand_into(bcx, lldest, operand); + return bcx; + } + unimplemented!() + } + mir::Rvalue::Cast(..) => { unimplemented!() } @@ -93,7 +106,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { _ => { assert!(rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - build::Store(bcx, temp.llval, lldest); + base::store_ty(bcx, temp.llval, lldest, temp.ty); bcx } } @@ -112,6 +125,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { (bcx, operand) } + mir::Rvalue::Cast(mir::CastKind::Unsize, _, _) => { + unimplemented!() + } + mir::Rvalue::Cast(..) => { unimplemented!() } @@ -240,7 +257,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }; (bcx, OperandRef { llval: llval, - ty: lhs.ty, + ty: type_of_binop(bcx.tcx(), op, lhs.ty, rhs.ty), }) } @@ -311,3 +328,31 @@ pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { // (*) this is only true if the type is suitable } + +/// FIXME(nikomatsakis): I don't think this function should go here +fn type_of_binop<'tcx>( + tcx: &ty::ctxt<'tcx>, + op: mir::BinOp, + lhs_ty: Ty<'tcx>, + rhs_ty: Ty<'tcx>) + -> Ty<'tcx> +{ + match op { + mir::BinOp::Add | mir::BinOp::Sub | + mir::BinOp::Mul | mir::BinOp::Div | mir::BinOp::Rem | + mir::BinOp::BitXor | mir::BinOp::BitAnd | mir::BinOp::BitOr => { + // these should be integers or floats of the same size. We + // probably want to dump all ops in some intrinsics framework + // someday. + assert_eq!(lhs_ty, rhs_ty); + lhs_ty + } + mir::BinOp::Shl | mir::BinOp::Shr => { + lhs_ty // lhs_ty can be != rhs_ty + } + mir::BinOp::Eq | mir::BinOp::Lt | mir::BinOp::Le | + mir::BinOp::Ne | mir::BinOp::Ge | mir::BinOp::Gt => { + tcx.types.bool + } + } +} diff --git a/src/test/run-pass/mir_raw_fat_ptr.rs b/src/test/run-pass/mir_raw_fat_ptr.rs new file mode 100644 index 00000000000..20c3357d7d2 --- /dev/null +++ b/src/test/run-pass/mir_raw_fat_ptr.rs @@ -0,0 +1,173 @@ +// Copyright 2015 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(rustc_attrs)] + +// check raw fat pointer ops in mir +// FIXME: please improve this when we get monomorphization support + +use std::mem; + +#[derive(Debug, PartialEq, Eq)] +struct ComparisonResults { + lt: bool, + le: bool, + gt: bool, + ge: bool, + eq: bool, + ne: bool +} + +const LT: ComparisonResults = ComparisonResults { + lt: true, + le: true, + gt: false, + ge: false, + eq: false, + ne: true +}; + +const EQ: ComparisonResults = ComparisonResults { + lt: false, + le: true, + gt: false, + ge: true, + eq: true, + ne: false +}; + +const GT: ComparisonResults = ComparisonResults { + lt: false, + le: false, + gt: true, + ge: true, + eq: false, + ne: true +}; + +#[rustc_mir] +fn compare_su8(a: *const S<[u8]>, b: *const S<[u8]>) -> ComparisonResults { + ComparisonResults { + lt: a < b, + le: a <= b, + gt: a > b, + ge: a >= b, + eq: a == b, + ne: a != b + } +} + +#[rustc_mir] +fn compare_au8(a: *const [u8], b: *const [u8]) -> ComparisonResults { + ComparisonResults { + lt: a < b, + le: a <= b, + gt: a > b, + ge: a >= b, + eq: a == b, + ne: a != b + } +} + +#[rustc_mir(graphviz="comparefoo.gv")] +fn compare_foo<'a>(a: *const (Foo+'a), b: *const (Foo+'a)) -> ComparisonResults { + ComparisonResults { + lt: a < b, + le: a <= b, + gt: a > b, + ge: a >= b, + eq: a == b, + ne: a != b + } +} + +#[rustc_mir(graphviz="simpleeq.gv")] +fn simple_eq<'a>(a: *const (Foo+'a), b: *const (Foo+'a)) -> bool { + let result = a == b; + result +} + +fn assert_inorder<T: Copy>(a: &[T], + compare: fn(T, T) -> ComparisonResults) { + for i in 0..a.len() { + for j in 0..a.len() { + let cres = compare(a[i], a[j]); + if i < j { + assert_eq!(cres, LT); + } else if i == j { + assert_eq!(cres, EQ); + } else { + assert_eq!(cres, GT); + } + } + } +} + +trait Foo { fn foo(&self) -> usize; } +impl<T> Foo for T { + fn foo(&self) -> usize { + mem::size_of::<T>() + } +} + +struct S<T:?Sized>(u32, T); + +fn main() { + let array = [0,1,2,3,4]; + let array2 = [5,6,7,8,9]; + + // fat ptr comparison: addr then extra + + // check ordering for arrays + let mut ptrs: Vec<*const [u8]> = vec![ + &array[0..0], &array[0..1], &array, &array[1..] + ]; + + let array_addr = &array as *const [u8] as *const u8 as usize; + let array2_addr = &array2 as *const [u8] as *const u8 as usize; + if array2_addr < array_addr { + ptrs.insert(0, &array2); + } else { + ptrs.push(&array2); + } + assert_inorder(&ptrs, compare_au8); + + let u8_ = (0u8, 1u8); + let u32_ = (4u32, 5u32); + + // check ordering for ptrs + let buf: &mut [*const Foo] = &mut [ + &u8_, &u8_.0, + &u32_, &u32_.0, + ]; + buf.sort_by(|u,v| { + let u : [*const (); 2] = unsafe { mem::transmute(*u) }; + let v : [*const (); 2] = unsafe { mem::transmute(*v) }; + u.cmp(&v) + }); + assert_inorder(buf, compare_foo); + + // check ordering for structs containing arrays + let ss: (S<[u8; 2]>, + S<[u8; 3]>, + S<[u8; 2]>) = ( + S(7, [8, 9]), + S(10, [11, 12, 13]), + S(4, [5, 6]) + ); + assert_inorder(&[ + &ss.0 as *const S<[u8]>, + &ss.1 as *const S<[u8]>, + &ss.2 as *const S<[u8]> + ], compare_su8); + + assert!(simple_eq(&0u8 as *const _, &0u8 as *const _)); + assert!(!simple_eq(&0u8 as *const _, &1u8 as *const _)); +} From dcb64b52a241406f77d84572c90a71a366fc3c37 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda <ariel.byd@gmail.com> Date: Tue, 10 Nov 2015 22:05:11 +0200 Subject: [PATCH 2/6] represent fat ptr operands as 2 separate pointers this does add some complexity, but to do otherwise would require unsized lvalues to have their own allocas, which would be ugly. --- src/librustc_trans/trans/base.rs | 99 ++++++--- src/librustc_trans/trans/mir/analyze.rs | 13 +- src/librustc_trans/trans/mir/block.rs | 4 +- src/librustc_trans/trans/mir/constant.rs | 40 ++-- src/librustc_trans/trans/mir/lvalue.rs | 62 ++++-- src/librustc_trans/trans/mir/mod.rs | 2 +- src/librustc_trans/trans/mir/operand.rs | 92 ++++++-- src/librustc_trans/trans/mir/rvalue.rs | 268 +++++++++++++---------- src/test/run-pass/mir_fat_ptr.rs | 63 ++++++ 9 files changed, 431 insertions(+), 212 deletions(-) create mode 100644 src/test/run-pass/mir_fat_ptr.rs diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index be4758581e9..7f258cb845b 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -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, @@ -342,39 +385,10 @@ pub fn compare_scalar_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, 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])); - - 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"); - } - } + 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) @@ -883,6 +897,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())) diff --git a/src/librustc_trans/trans/mir/analyze.rs b/src/librustc_trans/trans/mir/analyze.rs index cf1ecc4d6da..fc76d1e787d 100644 --- a/src/librustc_trans/trans/mir/analyze.rs +++ b/src/librustc_trans/trans/mir/analyze.rs @@ -27,16 +27,15 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, for (index, temp_decl) in mir.temp_decls.iter().enumerate() { let ty = bcx.monomorphize(&temp_decl.ty); debug!("temp {:?} has type {:?}", index, ty); - if - (ty.is_scalar() || - ty.is_unique() || - ty.is_region_ptr() || - ty.is_simd()) - && !common::type_is_fat_ptr(bcx.tcx(), ty) + if ty.is_scalar() || + ty.is_unique() || + ty.is_region_ptr() || + ty.is_simd() { // These sorts of types are immediates that we can store // in an ValueRef without an alloca. - assert!(common::type_is_immediate(bcx.ccx(), ty)); + assert!(common::type_is_immediate(bcx.ccx(), ty) || + common::type_is_fat_ptr(bcx.tcx(), ty)); } else { // These sorts of types require an alloca. Note that // type_is_immediate() may *still* be true, particularly diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index ccab9a20e75..181a03790ee 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -43,7 +43,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let cond = self.trans_operand(bcx, cond); let lltrue = self.llblock(true_bb); let llfalse = self.llblock(false_bb); - build::CondBr(bcx, cond.llval, lltrue, llfalse, DebugLoc::None); + build::CondBr(bcx, cond.immediate(), lltrue, llfalse, DebugLoc::None); } mir::Terminator::Switch { .. } => { @@ -55,7 +55,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let discr = build::Load(bcx, self.trans_lvalue(bcx, discr).llval); let switch = build::Switch(bcx, discr, self.llblock(*otherwise), values.len()); for (value, target) in values.iter().zip(targets) { - let llval = self.trans_constval(bcx, value, switch_ty); + let llval = self.trans_constval(bcx, value, switch_ty).immediate(); let llbb = self.llblock(*target); build::AddCase(switch, llval, llbb) } diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs index 43973fe5558..923baf0dcfe 100644 --- a/src/librustc_trans/trans/mir/constant.rs +++ b/src/librustc_trans/trans/mir/constant.rs @@ -8,14 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::ValueRef; use middle::ty::Ty; use rustc::middle::const_eval::ConstVal; use rustc_mir::repr as mir; use trans::consts::{self, TrueConst}; use trans::common::{self, Block}; +use trans::common::{C_bool, C_bytes, C_floating_f64, C_integral, C_str_slice}; use trans::type_of; +use super::operand::{OperandRef, OperandValue}; use super::MirContext; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { @@ -23,20 +24,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx: Block<'bcx, 'tcx>, cv: &ConstVal, ty: Ty<'tcx>) - -> ValueRef + -> OperandRef<'tcx> { let ccx = bcx.ccx(); let llty = type_of::type_of(ccx, ty); - match *cv { - ConstVal::Float(v) => common::C_floating_f64(v, llty), - ConstVal::Bool(v) => common::C_bool(ccx, v), - ConstVal::Int(v) => common::C_integral(llty, v as u64, true), - ConstVal::Uint(v) => common::C_integral(llty, v, false), - ConstVal::Str(ref v) => common::C_str_slice(ccx, v.clone()), - ConstVal::ByteStr(ref v) => consts::addr_of(ccx, - common::C_bytes(ccx, v), - 1, - "byte_str"), + let val = match *cv { + ConstVal::Float(v) => OperandValue::Imm(C_floating_f64(v, llty)), + ConstVal::Bool(v) => OperandValue::Imm(C_bool(ccx, v)), + ConstVal::Int(v) => OperandValue::Imm(C_integral(llty, v as u64, true)), + ConstVal::Uint(v) => OperandValue::Imm(C_integral(llty, v, false)), + ConstVal::Str(ref v) => OperandValue::Imm(C_str_slice(ccx, v.clone())), + ConstVal::ByteStr(ref v) => { + OperandValue::Imm(consts::addr_of(ccx, + C_bytes(ccx, v), + 1, + "byte_str")) + } + ConstVal::Struct(id) | ConstVal::Tuple(id) => { let expr = bcx.tcx().map.expect_expr(id); let (llval, _) = match consts::const_expr(ccx, @@ -47,18 +51,26 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { Ok(v) => v, Err(_) => panic!("constant eval failure"), }; - llval + if common::type_is_immediate(bcx.ccx(), ty) { + OperandValue::Imm(llval) + } else { + OperandValue::Ref(llval) + } } ConstVal::Function(_) => { unimplemented!() } + }; + OperandRef { + ty: ty, + val: val } } pub fn trans_constant(&mut self, bcx: Block<'bcx, 'tcx>, constant: &mir::Constant<'tcx>) - -> ValueRef + -> OperandRef<'tcx> { let constant_ty = bcx.monomorphize(&constant.ty); match constant.literal { diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index 98c15669976..d846307dc78 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -18,7 +18,8 @@ use trans::build; use trans::common::{self, Block}; use trans::debuginfo::DebugLoc; use trans::machine; -use trans::tvec; + +use std::ptr; use super::{MirContext, TempRef}; @@ -27,13 +28,16 @@ pub struct LvalueRef<'tcx> { /// Pointer to the contents of the lvalue pub llval: ValueRef, + /// This lvalue's extra data if it is unsized, or null + pub llextra: ValueRef, + /// Monomorphized type of this lvalue, including variant information pub ty: LvalueTy<'tcx>, } impl<'tcx> LvalueRef<'tcx> { - pub fn new(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> { - LvalueRef { llval: llval, ty: lvalue_ty } + pub fn new_sized(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> { + LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty } } pub fn alloca<'bcx>(bcx: Block<'bcx, 'tcx>, @@ -42,11 +46,18 @@ impl<'tcx> LvalueRef<'tcx> { -> LvalueRef<'tcx> { let lltemp = base::alloc_ty(bcx, ty, name); - LvalueRef::new(lltemp, LvalueTy::from_ty(ty)) + LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty)) } } impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn lvalue_len(&mut self, + _bcx: Block<'bcx, 'tcx>, + _lvalue: LvalueRef<'tcx>) + -> ValueRef { + unimplemented!() + } + pub fn trans_lvalue(&mut self, bcx: Block<'bcx, 'tcx>, lvalue: &mir::Lvalue<'tcx>) @@ -72,15 +83,20 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Lvalue::ReturnPointer => { let return_ty = bcx.monomorphize(&self.mir.return_ty); let llval = fcx.get_ret_slot(bcx, return_ty, "return"); - LvalueRef::new(llval, LvalueTy::from_ty(return_ty.unwrap())) + LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty.unwrap())) } mir::Lvalue::Projection(ref projection) => { let tr_base = self.trans_lvalue(bcx, &projection.base); let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); - let llprojected = match projection.elem { + let (llprojected, llextra) = match projection.elem { mir::ProjectionElem::Deref => { let base_ty = tr_base.ty.to_ty(tcx); - base::load_ty(bcx, tr_base.llval, base_ty) + if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) { + (base::load_ty(bcx, tr_base.llval, base_ty), + ptr::null_mut()) + } else { + base::load_fat_ptr(bcx, tr_base.llval, base_ty) + } } mir::ProjectionElem::Field(ref field) => { let base_ty = tr_base.ty.to_ty(tcx); @@ -90,44 +106,44 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v, }; let discr = discr as u64; - adt::trans_field_ptr(bcx, &base_repr, tr_base.llval, discr, field.index()) + (adt::trans_field_ptr(bcx, &base_repr, tr_base.llval, discr, field.index()), + if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) { + ptr::null_mut() + } else { + tr_base.llextra + }) } mir::ProjectionElem::Index(ref index) => { - let base_ty = tr_base.ty.to_ty(tcx); let index = self.trans_operand(bcx, index); - let llindex = self.prepare_index(bcx, index.llval); - let (llbase, _) = tvec::get_base_and_len(bcx, tr_base.llval, base_ty); - build::InBoundsGEP(bcx, llbase, &[llindex]) + let llindex = self.prepare_index(bcx, index.immediate()); + (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]), + ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: false, min_length: _ } => { - let base_ty = tr_base.ty.to_ty(tcx); let lloffset = common::C_u32(bcx.ccx(), offset); let llindex = self.prepare_index(bcx, lloffset); - let (llbase, _) = tvec::get_base_and_len(bcx, - tr_base.llval, - base_ty); - build::InBoundsGEP(bcx, llbase, &[llindex]) + (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]), + ptr::null_mut()) } mir::ProjectionElem::ConstantIndex { offset, from_end: true, min_length: _ } => { let lloffset = common::C_u32(bcx.ccx(), offset); - let base_ty = tr_base.ty.to_ty(tcx); - let (llbase, lllen) = tvec::get_base_and_len(bcx, - tr_base.llval, - base_ty); + let lllen = self.lvalue_len(bcx, tr_base); let llindex = build::Sub(bcx, lllen, lloffset, DebugLoc::None); let llindex = self.prepare_index(bcx, llindex); - build::InBoundsGEP(bcx, llbase, &[llindex]) + (build::InBoundsGEP(bcx, tr_base.llval, &[llindex]), + ptr::null_mut()) } mir::ProjectionElem::Downcast(..) => { - tr_base.llval + (tr_base.llval, tr_base.llextra) } }; LvalueRef { llval: llprojected, + llextra: llextra, ty: projected_ty, } } diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index c21103fde96..c8eef7ff448 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -180,7 +180,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>, base::store_ty(bcx, llarg, lltemp, arg_ty); lltemp }; - LvalueRef::new(llval, LvalueTy::from_ty(arg_ty)) + LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)) }) .collect() } diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs index fb529d8975e..7daf76a8d31 100644 --- a/src/librustc_trans/trans/mir/operand.rs +++ b/src/librustc_trans/trans/mir/operand.rs @@ -12,22 +12,64 @@ use llvm::ValueRef; use rustc::middle::ty::Ty; use rustc_mir::repr as mir; use trans::base; -use trans::build; -use trans::common::Block; +use trans::common::{self, Block}; use trans::datum; use super::{MirContext, TempRef}; +/// The Rust representation of an operand's value. This is uniquely +/// determined by the operand type, but is kept as an enum as a +/// safety check. +#[derive(Copy, Clone)] +pub enum OperandValue { + /// A reference to the actual operand. The data is guaranteed + /// to be valid for the operand's lifetime. + Ref(ValueRef), + /// A single LLVM value. + Imm(ValueRef), + /// A fat pointer. The first ValueRef is the data and the second + /// is the extra. + FatPtr(ValueRef, ValueRef) +} + #[derive(Copy, Clone)] pub struct OperandRef<'tcx> { // This will be "indirect" if `appropriate_rvalue_mode` returns // ByRef, and otherwise ByValue. - pub llval: ValueRef, + pub val: OperandValue, // The type of value being returned. pub ty: Ty<'tcx> } +impl<'tcx> OperandRef<'tcx> { + pub fn immediate(self) -> ValueRef { + match self.val { + OperandValue::Imm(s) => s, + _ => unreachable!() + } + } + + pub fn repr<'bcx>(self, bcx: Block<'bcx, 'tcx>) -> String { + match self.val { + OperandValue::Ref(r) => { + format!("OperandRef(Ref({}) @ {:?})", + bcx.val_to_string(r), self.ty) + } + OperandValue::Imm(i) => { + format!("OperandRef(Imm({}) @ {:?})", + bcx.val_to_string(i), self.ty) + } + OperandValue::FatPtr(a, d) => { + format!("OperandRef(FatPtr({}, {}) @ {:?})", + bcx.val_to_string(a), + bcx.val_to_string(d), + self.ty) + } + } + } +} + impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_operand(&mut self, bcx: Block<'bcx, 'tcx>, @@ -62,23 +104,24 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug!("trans_operand: tr_lvalue={} @ {:?}", bcx.val_to_string(tr_lvalue.llval), ty); - let llval = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) { - datum::ByValue => build::Load(bcx, tr_lvalue.llval), - datum::ByRef => tr_lvalue.llval, + let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) { + datum::ByValue => { + OperandValue::Imm(base::load_ty(bcx, tr_lvalue.llval, ty)) + } + datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => { + let (lldata, llextra) = base::load_fat_ptr(bcx, tr_lvalue.llval, ty); + OperandValue::FatPtr(lldata, llextra) + } + datum::ByRef => OperandValue::Ref(tr_lvalue.llval) }; OperandRef { - llval: llval, + val: val, ty: ty } } mir::Operand::Constant(ref constant) => { - let llval = self.trans_constant(bcx, constant); - let ty = bcx.monomorphize(&constant.ty); - OperandRef { - llval: llval, - ty: ty, - } + self.trans_constant(bcx, constant) } } } @@ -92,10 +135,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx.val_to_string(lldest), operand); + // FIXME: consider not copying constants through the + // stack. + let o = self.trans_operand(bcx, operand); - match datum::appropriate_rvalue_mode(bcx.ccx(), o.ty) { - datum::ByValue => base::store_ty(bcx, o.llval, lldest, o.ty), - datum::ByRef => base::memcpy_ty(bcx, lldest, o.llval, o.ty), - }; + self.store_operand(bcx, lldest, o); + } + + pub fn store_operand(&mut self, + bcx: Block<'bcx, 'tcx>, + lldest: ValueRef, + operand: OperandRef<'tcx>) + { + debug!("store_operand: operand={}", operand.repr(bcx)); + match operand.val { + OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty), + OperandValue::Imm(s) => base::store_ty(bcx, s, lldest, operand.ty), + OperandValue::FatPtr(data, extra) => { + base::store_fat_ptr(bcx, data, extra, lldest, operand.ty); + } + } } } diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 65fb0d91e4d..f933252b51e 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -26,7 +26,7 @@ use trans::type_of; use trans::tvec; use super::MirContext; -use super::operand::OperandRef; +use super::operand::{OperandRef, OperandValue}; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_rvalue(&mut self, @@ -64,11 +64,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Repeat(ref elem, ref count) => { let elem = self.trans_operand(bcx, elem); - let size = self.trans_constant(bcx, count); + let size = self.trans_constant(bcx, count).immediate(); let base = expr::get_dataptr(bcx, lldest); - tvec::iter_vec_raw(bcx, base, elem.ty, size, |b, vref, _| { - build::Store(b, elem.llval, vref); - b + tvec::iter_vec_raw(bcx, base, elem.ty, size, |bcx, llslot, _| { + self.store_operand(bcx, llslot, elem); + bcx }) } @@ -106,7 +106,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { _ => { assert!(rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); - base::store_ty(bcx, temp.llval, lldest, temp.ty); + self.store_operand(bcx, lldest, temp); bcx } } @@ -136,21 +136,27 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Ref(_, _, ref lvalue) => { let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); // Note: lvalues are indirect, so storing the `llval` into the // destination effectively creates a reference. - (bcx, OperandRef { - llval: tr_lvalue.llval, - ty: tr_lvalue.ty.to_ty(bcx.tcx()), - }) + if common::type_is_sized(bcx.tcx(), ty) { + (bcx, OperandRef { + val: OperandValue::Imm(tr_lvalue.llval), + ty: ty, + }) + } else { + (bcx, OperandRef { + val: OperandValue::FatPtr(tr_lvalue.llval, + tr_lvalue.llextra), + ty: ty, + }) + } } mir::Rvalue::Len(ref lvalue) => { let tr_lvalue = self.trans_lvalue(bcx, lvalue); - let (_, lllen) = tvec::get_base_and_len(bcx, - tr_lvalue.llval, - tr_lvalue.ty.to_ty(bcx.tcx())); (bcx, OperandRef { - llval: lllen, + val: OperandValue::Imm(self.lvalue_len(bcx, tr_lvalue)), ty: bcx.tcx().types.usize, }) } @@ -158,123 +164,45 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { let lhs = self.trans_operand(bcx, lhs); let rhs = self.trans_operand(bcx, rhs); - let is_float = lhs.ty.is_fp(); - let is_signed = lhs.ty.is_signed(); - let binop_debug_loc = DebugLoc::None; - let llval = match op { - mir::BinOp::Add => if is_float { - build::FAdd(bcx, lhs.llval, rhs.llval, binop_debug_loc) - } else { - build::Add(bcx, lhs.llval, rhs.llval, binop_debug_loc) - }, - mir::BinOp::Sub => if is_float { - build::FSub(bcx, lhs.llval, rhs.llval, binop_debug_loc) - } else { - build::Sub(bcx, lhs.llval, rhs.llval, binop_debug_loc) - }, - mir::BinOp::Mul => if is_float { - build::FMul(bcx, lhs.llval, rhs.llval, binop_debug_loc) - } else { - build::Mul(bcx, lhs.llval, rhs.llval, binop_debug_loc) - }, - mir::BinOp::Div => if is_float { - build::FDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) - } else if is_signed { - build::SDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) - } else { - build::UDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) - }, - mir::BinOp::Rem => if is_float { - // LLVM currently always lowers the `frem` instructions appropriate - // library calls typically found in libm. Notably f64 gets wired up - // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for - // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's - // instead just an inline function in a header that goes up to a - // f64, uses `fmod`, and then comes back down to a f32. - // - // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will - // still unconditionally lower frem instructions over 32-bit floats - // to a call to `fmodf`. To work around this we special case MSVC - // 32-bit float rem instructions and instead do the call out to - // `fmod` ourselves. - // - // Note that this is currently duplicated with src/libcore/ops.rs - // which does the same thing, and it would be nice to perhaps unify - // these two implementations one day! Also note that we call `fmod` - // for both 32 and 64-bit floats because if we emit any FRem - // instruction at all then LLVM is capable of optimizing it into a - // 32-bit FRem (which we're trying to avoid). - let tcx = bcx.tcx(); - let use_fmod = tcx.sess.target.target.options.is_like_msvc && - tcx.sess.target.target.arch == "x86"; - if use_fmod { - let f64t = Type::f64(bcx.ccx()); - let fty = Type::func(&[f64t, f64t], &f64t); - let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty, - tcx.types.f64); - if lhs.ty == tcx.types.f32 { - let lllhs = build::FPExt(bcx, lhs.llval, f64t); - let llrhs = build::FPExt(bcx, rhs.llval, f64t); - let llres = build::Call(bcx, llfn, &[lllhs, llrhs], - None, binop_debug_loc); - build::FPTrunc(bcx, llres, Type::f32(bcx.ccx())) - } else { - build::Call(bcx, llfn, &[lhs.llval, rhs.llval], - None, binop_debug_loc) - } - } else { - build::FRem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + let llresult = if common::type_is_fat_ptr(bcx.tcx(), lhs.ty) { + match (lhs.val, rhs.val) { + (OperandValue::FatPtr(lhs_addr, lhs_extra), + OperandValue::FatPtr(rhs_addr, rhs_extra)) => { + base::compare_fat_ptrs(bcx, + lhs_addr, lhs_extra, + rhs_addr, rhs_extra, + lhs.ty, cmp_to_hir_cmp(op), + DebugLoc::None) } - } else if is_signed { - build::SRem(bcx, lhs.llval, rhs.llval, binop_debug_loc) - } else { - build::URem(bcx, lhs.llval, rhs.llval, binop_debug_loc) - }, - mir::BinOp::BitOr => build::Or(bcx, lhs.llval, rhs.llval, binop_debug_loc), - mir::BinOp::BitAnd => build::And(bcx, lhs.llval, rhs.llval, binop_debug_loc), - mir::BinOp::BitXor => build::Xor(bcx, lhs.llval, rhs.llval, binop_debug_loc), - mir::BinOp::Shl => common::build_unchecked_lshift(bcx, - lhs.llval, - rhs.llval, - binop_debug_loc), - mir::BinOp::Shr => common::build_unchecked_rshift(bcx, - lhs.ty, - lhs.llval, - rhs.llval, - binop_debug_loc), - mir::BinOp::Eq => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, - hir::BiEq, binop_debug_loc), - mir::BinOp::Lt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, - hir::BiLt, binop_debug_loc), - mir::BinOp::Le => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, - hir::BiLe, binop_debug_loc), - mir::BinOp::Ne => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, - hir::BiNe, binop_debug_loc), - mir::BinOp::Ge => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, - hir::BiGe, binop_debug_loc), - mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, - hir::BiGt, binop_debug_loc), + _ => unreachable!() + } + + } else { + self.trans_scalar_binop(bcx, op, + lhs.immediate(), rhs.immediate(), + lhs.ty, DebugLoc::None) }; (bcx, OperandRef { - llval: llval, + val: OperandValue::Imm(llresult), ty: type_of_binop(bcx.tcx(), op, lhs.ty, rhs.ty), }) } mir::Rvalue::UnaryOp(op, ref operand) => { let operand = self.trans_operand(bcx, operand); + let lloperand = operand.immediate(); let is_float = operand.ty.is_fp(); let debug_loc = DebugLoc::None; let llval = match op { - mir::UnOp::Not => build::Not(bcx, operand.llval, debug_loc), + mir::UnOp::Not => build::Not(bcx, lloperand, debug_loc), mir::UnOp::Neg => if is_float { - build::FNeg(bcx, operand.llval, debug_loc) + build::FNeg(bcx, lloperand, debug_loc) } else { - build::Neg(bcx, operand.llval, debug_loc) + build::Neg(bcx, lloperand, debug_loc) } }; (bcx, OperandRef { - llval: llval, + val: OperandValue::Imm(llval), ty: operand.ty, }) } @@ -294,7 +222,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { llalign, DebugLoc::None); (bcx, OperandRef { - llval: llval, + val: OperandValue::Imm(llval), ty: box_ty, }) } @@ -307,6 +235,104 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } } + + pub fn trans_scalar_binop(&mut self, + bcx: Block<'bcx, 'tcx>, + op: mir::BinOp, + lhs: ValueRef, + rhs: ValueRef, + input_ty: Ty<'tcx>, + debug_loc: DebugLoc) -> ValueRef { + let is_float = input_ty.is_fp(); + let is_signed = input_ty.is_signed(); + match op { + mir::BinOp::Add => if is_float { + build::FAdd(bcx, lhs, rhs, debug_loc) + } else { + build::Add(bcx, lhs, rhs, debug_loc) + }, + mir::BinOp::Sub => if is_float { + build::FSub(bcx, lhs, rhs, debug_loc) + } else { + build::Sub(bcx, lhs, rhs, debug_loc) + }, + mir::BinOp::Mul => if is_float { + build::FMul(bcx, lhs, rhs, debug_loc) + } else { + build::Mul(bcx, lhs, rhs, debug_loc) + }, + mir::BinOp::Div => if is_float { + build::FDiv(bcx, lhs, rhs, debug_loc) + } else if is_signed { + build::SDiv(bcx, lhs, rhs, debug_loc) + } else { + build::UDiv(bcx, lhs, rhs, debug_loc) + }, + mir::BinOp::Rem => if is_float { + // LLVM currently always lowers the `frem` instructions appropriate + // library calls typically found in libm. Notably f64 gets wired up + // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for + // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's + // instead just an inline function in a header that goes up to a + // f64, uses `fmod`, and then comes back down to a f32. + // + // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will + // still unconditionally lower frem instructions over 32-bit floats + // to a call to `fmodf`. To work around this we special case MSVC + // 32-bit float rem instructions and instead do the call out to + // `fmod` ourselves. + // + // Note that this is currently duplicated with src/libcore/ops.rs + // which does the same thing, and it would be nice to perhaps unify + // these two implementations one day! Also note that we call `fmod` + // for both 32 and 64-bit floats because if we emit any FRem + // instruction at all then LLVM is capable of optimizing it into a + // 32-bit FRem (which we're trying to avoid). + let tcx = bcx.tcx(); + let use_fmod = tcx.sess.target.target.options.is_like_msvc && + tcx.sess.target.target.arch == "x86"; + if use_fmod { + let f64t = Type::f64(bcx.ccx()); + let fty = Type::func(&[f64t, f64t], &f64t); + let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty, + tcx.types.f64); + if input_ty == tcx.types.f32 { + let lllhs = build::FPExt(bcx, lhs, f64t); + let llrhs = build::FPExt(bcx, rhs, f64t); + let llres = build::Call(bcx, llfn, &[lllhs, llrhs], + None, debug_loc); + build::FPTrunc(bcx, llres, Type::f32(bcx.ccx())) + } else { + build::Call(bcx, llfn, &[lhs, rhs], + None, debug_loc) + } + } else { + build::FRem(bcx, lhs, rhs, debug_loc) + } + } else if is_signed { + build::SRem(bcx, lhs, rhs, debug_loc) + } else { + build::URem(bcx, lhs, rhs, debug_loc) + }, + mir::BinOp::BitOr => build::Or(bcx, lhs, rhs, debug_loc), + mir::BinOp::BitAnd => build::And(bcx, lhs, rhs, debug_loc), + mir::BinOp::BitXor => build::Xor(bcx, lhs, rhs, debug_loc), + mir::BinOp::Shl => common::build_unchecked_lshift(bcx, + lhs, + rhs, + debug_loc), + mir::BinOp::Shr => common::build_unchecked_rshift(bcx, + input_ty, + lhs, + rhs, + debug_loc), + mir::BinOp::Eq | mir::BinOp::Lt | mir::BinOp::Gt | + mir::BinOp::Ne | mir::BinOp::Le | mir::BinOp::Ge => { + base::compare_scalar_types(bcx, lhs, rhs, input_ty, + cmp_to_hir_cmp(op), debug_loc) + } + } + } } pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { @@ -329,6 +355,18 @@ pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { // (*) this is only true if the type is suitable } +fn cmp_to_hir_cmp(op: mir::BinOp) -> hir::BinOp_ { + match op { + mir::BinOp::Eq => hir::BiEq, + mir::BinOp::Ne => hir::BiNe, + mir::BinOp::Lt => hir::BiLt, + mir::BinOp::Le => hir::BiLe, + mir::BinOp::Gt => hir::BiGt, + mir::BinOp::Ge => hir::BiGe, + _ => unreachable!() + } +} + /// FIXME(nikomatsakis): I don't think this function should go here fn type_of_binop<'tcx>( tcx: &ty::ctxt<'tcx>, diff --git a/src/test/run-pass/mir_fat_ptr.rs b/src/test/run-pass/mir_fat_ptr.rs new file mode 100644 index 00000000000..1916e608a8c --- /dev/null +++ b/src/test/run-pass/mir_fat_ptr.rs @@ -0,0 +1,63 @@ +// Copyright 2015 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. + +// test that ordinary fat pointer operations work. + +#![feature(rustc_attrs)] + +struct Wrapper<T: ?Sized>(u32, T); + +struct FatPtrContainer<'a> { + ptr: &'a [u8] +} + +#[rustc_mir] +fn fat_ptr_project(a: &Wrapper<[u8]>) -> &[u8] { + &a.1 +} + +#[rustc_mir] +fn fat_ptr_simple(a: &[u8]) -> &[u8] { + a +} + +#[rustc_mir] +fn fat_ptr_via_local(a: &[u8]) -> &[u8] { + let x = a; + x +} + +#[rustc_mir] +fn fat_ptr_from_struct(s: FatPtrContainer) -> &[u8] { + s.ptr +} + +#[rustc_mir] +fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer { + FatPtrContainer { ptr: a } +} + +#[rustc_mir] +fn fat_ptr_store_to<'a>(a: &'a [u8], b: &mut &'a [u8]) { + *b = a; +} + +fn main() { + let a = Wrapper(4, [7,6,5]); + + let p = fat_ptr_project(&a); + let p = fat_ptr_simple(p); + let p = fat_ptr_via_local(p); + let p = fat_ptr_from_struct(fat_ptr_to_struct(p)); + + let mut target : &[u8] = &[42]; + fat_ptr_store_to(p, &mut target); + assert_eq!(target, &a.1); +} From c1bfd0ca6b6aedd4fa29e3dddf927f31051f3ea6 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda <ariel.byd@gmail.com> Date: Tue, 10 Nov 2015 22:26:59 +0200 Subject: [PATCH 3/6] implement lvalue_len no tests - sorry --- src/librustc_trans/trans/mir/lvalue.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index d846307dc78..f6aa79393d3 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -9,7 +9,7 @@ // except according to those terms. use llvm::ValueRef; -use rustc::middle::ty::Ty; +use rustc::middle::ty::{self, Ty}; use rustc_mir::repr as mir; use rustc_mir::tcx::LvalueTy; use trans::adt; @@ -52,10 +52,17 @@ impl<'tcx> LvalueRef<'tcx> { impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn lvalue_len(&mut self, - _bcx: Block<'bcx, 'tcx>, - _lvalue: LvalueRef<'tcx>) + bcx: Block<'bcx, 'tcx>, + lvalue: LvalueRef<'tcx>) -> ValueRef { - unimplemented!() + match lvalue.ty.to_ty(bcx.tcx()).sty { + ty::TyArray(_, n) => common::C_uint(bcx.ccx(), n), + ty::TySlice(_) | ty::TyStr => { + assert!(lvalue.llextra != ptr::null_mut()); + lvalue.llextra + } + _ => bcx.sess().bug("unexpected type in get_base_and_len"), + } } pub fn trans_lvalue(&mut self, From cd1585ffb38a4ac911ce510fee92ac6f8ea86a5e Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda <ariel.byd@gmail.com> Date: Wed, 11 Nov 2015 00:07:34 +0200 Subject: [PATCH 4/6] rustc_mir: don't miss the autoref when doing an unsize --- src/librustc_mir/hair/cx/expr.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 56ec0881811..c546a264be1 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -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) => { @@ -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() }, + }; + } } } From e82f5d4f54e9e9c7004c67354a696eacf5f9b155 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda <ariel.byd@gmail.com> Date: Wed, 11 Nov 2015 22:02:51 +0200 Subject: [PATCH 5/6] implement coercions in MIR --- src/librustc_mir/tcx/mod.rs | 15 +++ src/librustc_trans/trans/base.rs | 125 ++++++++++++++++++++++++- src/librustc_trans/trans/common.rs | 2 +- src/librustc_trans/trans/consts.rs | 2 +- src/librustc_trans/trans/expr.rs | 36 ------- src/librustc_trans/trans/mir/lvalue.rs | 4 +- src/librustc_trans/trans/mir/rvalue.rs | 99 ++++++++++++++++---- src/test/run-pass/mir_coercions.rs | 76 +++++++++++++++ 8 files changed, 299 insertions(+), 60 deletions(-) create mode 100644 src/test/run-pass/mir_coercions.rs diff --git a/src/librustc_mir/tcx/mod.rs b/src/librustc_mir/tcx/mod.rs index 3b9d9228a16..69240e9f995 100644 --- a/src/librustc_mir/tcx/mod.rs +++ b/src/librustc_mir/tcx/mod.rs @@ -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> { @@ -123,3 +124,17 @@ 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, + } + } +} diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 7f258cb845b..8023f776dde 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -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}; @@ -577,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, diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index ac3e3beeac8..275f4628788 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -1223,4 +1223,4 @@ pub fn get_static_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } else { base::get_extern_const(ccx, did, ty) } -} +} \ No newline at end of file diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index b849888cf7c..91f17a50e2c 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -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() { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index eb9635066f3..728b53dcd4a 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -326,42 +326,6 @@ pub fn copy_fat_ptr(bcx: Block, src_ptr: ValueRef, dst_ptr: ValueRef) { Store(bcx, Load(bcx, get_meta(bcx, src_ptr)), get_meta(bcx, dst_ptr)); } -/// 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)) - } -} - fn adjustment_required<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &hir::Expr) -> bool { let adjustment = match bcx.tcx().tables.borrow().adjustments.get(&expr.id).cloned() { diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index f6aa79393d3..ff80451d2b1 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -85,7 +85,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Lvalue::Arg(index) => self.args[index as usize], mir::Lvalue::Static(def_id) => { let const_ty = self.mir.lvalue_ty(tcx, lvalue); - LvalueRef::new(common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)), const_ty) + LvalueRef::new_sized( + common::get_static_val(ccx, def_id, const_ty.to_ty(tcx)), + const_ty) }, mir::Lvalue::ReturnPointer => { let return_ty = bcx.monomorphize(&self.mir.return_ty); diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index f933252b51e..8f5496929c2 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -46,20 +46,35 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, cast_ty) => { - let expr_ty = - bcx.monomorphize(&self.mir.operand_ty(bcx.tcx(), operand)); - let cast_ty = - bcx.monomorphize(&cast_ty); - if expr_ty == cast_ty { - debug!("trans_rvalue: trivial unsize at {:?}", expr_ty); - self.trans_operand_into(bcx, lldest, operand); + if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { + let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); + self.store_operand(bcx, lldest, temp); return bcx; } - unimplemented!() - } - mir::Rvalue::Cast(..) => { - unimplemented!() + // Unsize of a nontrivial struct. I would prefer for + // this to be eliminated by MIR translation, but + // `CoerceUnsized` can be passed by a where-clause, + // so the (generic) MIR may not be able to expand it. + let operand = self.trans_operand(bcx, operand); + match operand.val { + OperandValue::FatPtr(..) => unreachable!(), + OperandValue::Imm(llval) => { + // ugly alloca. + debug!("trans_rvalue: creating ugly alloca"); + let lltemp = base::alloc_ty(bcx, operand.ty, "__unsize_temp"); + base::store_ty(bcx, llval, lltemp, operand.ty); + base::coerce_unsized_into(bcx, + lltemp, operand.ty, + lldest, cast_ty); + } + OperandValue::Ref(llref) => { + base::coerce_unsized_into(bcx, + llref, operand.ty, + lldest, cast_ty); + } + } + bcx } mir::Rvalue::Repeat(ref elem, ref count) => { @@ -125,30 +140,74 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { (bcx, operand) } - mir::Rvalue::Cast(mir::CastKind::Unsize, _, _) => { - unimplemented!() + mir::Rvalue::Cast(ref kind, ref operand, cast_ty) => { + let operand = self.trans_operand(bcx, operand); + debug!("cast operand is {}", operand.repr(bcx)); + let cast_ty = bcx.monomorphize(&cast_ty); + + let val = match *kind { + mir::CastKind::ReifyFnPointer | + mir::CastKind::UnsafeFnPointer => { + // these are no-ops at the LLVM level + operand.val + } + mir::CastKind::Unsize => { + // unsize targets other than to a fat pointer currently + // can't be operands. + assert!(common::type_is_fat_ptr(bcx.tcx(), cast_ty)); + + match operand.val { + OperandValue::FatPtr(..) => { + // unsize from a fat pointer - this is a + // "trait-object-to-supertrait" coercion, for + // example, + // &'a fmt::Debug+Send => &'a fmt::Debug, + // and is a no-op at the LLVM level + operand.val + } + OperandValue::Imm(lldata) => { + // "standard" unsize + let (lldata, llextra) = + base::unsize_thin_ptr(bcx, lldata, + operand.ty, cast_ty); + OperandValue::FatPtr(lldata, llextra) + } + OperandValue::Ref(_) => { + bcx.sess().bug( + &format!("by-ref operand {} in trans_rvalue_operand", + operand.repr(bcx))); + } + } + } + mir::CastKind::Misc => unimplemented!() + }; + (bcx, OperandRef { + val: val, + ty: cast_ty + }) } - mir::Rvalue::Cast(..) => { - unimplemented!() - } - - mir::Rvalue::Ref(_, _, ref lvalue) => { + mir::Rvalue::Ref(_, bk, ref lvalue) => { let tr_lvalue = self.trans_lvalue(bcx, lvalue); let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + let ref_ty = bcx.tcx().mk_ref( + bcx.tcx().mk_region(ty::ReStatic), + ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() } + ); + // Note: lvalues are indirect, so storing the `llval` into the // destination effectively creates a reference. if common::type_is_sized(bcx.tcx(), ty) { (bcx, OperandRef { val: OperandValue::Imm(tr_lvalue.llval), - ty: ty, + ty: ref_ty, }) } else { (bcx, OperandRef { val: OperandValue::FatPtr(tr_lvalue.llval, tr_lvalue.llextra), - ty: ty, + ty: ref_ty, }) } } diff --git a/src/test/run-pass/mir_coercions.rs b/src/test/run-pass/mir_coercions.rs new file mode 100644 index 00000000000..c1897f79f22 --- /dev/null +++ b/src/test/run-pass/mir_coercions.rs @@ -0,0 +1,76 @@ +// Copyright 2015 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(rustc_attrs, coerce_unsized, unsize)] + +use std::ops::CoerceUnsized; +use std::marker::Unsize; + +#[rustc_mir] +fn identity_coercion(x: &(Fn(u32)->u32 + Send)) -> &Fn(u32)->u32 { + x +} +#[rustc_mir] +fn fn_coercions(f: &fn(u32) -> u32) -> + (unsafe fn(u32) -> u32, + &(Fn(u32) -> u32+Send)) +{ + (*f, f) +} + +#[rustc_mir] +fn simple_array_coercion(x: &[u8; 3]) -> &[u8] { x } + +fn square(a: u32) -> u32 { a * a } + +#[derive(PartialEq,Eq)] +struct PtrWrapper<'a, T: 'a+?Sized>(u32, u32, (), &'a T); +impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> + CoerceUnsized<PtrWrapper<'a, U>> for PtrWrapper<'a, T> {} + +struct TrivPtrWrapper<'a, T: 'a+?Sized>(&'a T); +impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> + CoerceUnsized<TrivPtrWrapper<'a, U>> for TrivPtrWrapper<'a, T> {} + +#[rustc_mir] +fn coerce_ptr_wrapper(p: PtrWrapper<[u8; 3]>) -> PtrWrapper<[u8]> { + p +} + +#[rustc_mir] +fn coerce_triv_ptr_wrapper(p: TrivPtrWrapper<[u8; 3]>) -> TrivPtrWrapper<[u8]> { + p +} + +#[rustc_mir] +fn coerce_fat_ptr_wrapper(p: PtrWrapper<Fn(u32) -> u32+Send>) + -> PtrWrapper<Fn(u32) -> u32> { + p +} + + +fn main() { + let a = [0,1,2]; + let square_local : fn(u32) -> u32 = square; + let (f,g) = fn_coercions(&square_local); + assert_eq!(f as usize, square as usize); + assert_eq!(g(4), 16); + assert_eq!(identity_coercion(g)(5), 25); + + assert_eq!(simple_array_coercion(&a), &a); + let w = coerce_ptr_wrapper(PtrWrapper(2,3,(),&a)); + assert!(w == PtrWrapper(2,3,(),&a) as PtrWrapper<[u8]>); + + let w = coerce_triv_ptr_wrapper(TrivPtrWrapper(&a)); + assert_eq!(&w.0, &a); + + let z = coerce_fat_ptr_wrapper(PtrWrapper(2,3,(),&square_local)); + assert_eq!((z.3)(6), 36); +} From b9b45a0e96ad86d35aa3be45b7ef9080679abdd3 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda <ariel.byd@gmail.com> Date: Fri, 13 Nov 2015 00:12:50 +0200 Subject: [PATCH 6/6] address review comments --- src/librustc_mir/tcx/mod.rs | 48 ++++++++++++++++ src/librustc_trans/trans/mir/constant.rs | 26 +++++---- src/librustc_trans/trans/mir/operand.rs | 28 ++++++---- src/librustc_trans/trans/mir/rvalue.rs | 70 ++++++------------------ 4 files changed, 98 insertions(+), 74 deletions(-) diff --git a/src/librustc_mir/tcx/mod.rs b/src/librustc_mir/tcx/mod.rs index 69240e9f995..15a49fc9d85 100644 --- a/src/librustc_mir/tcx/mod.rs +++ b/src/librustc_mir/tcx/mod.rs @@ -103,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>) @@ -138,3 +163,26 @@ impl BorrowKind { } } } + +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 + } + } +} diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs index 923baf0dcfe..8c0d8b10bfe 100644 --- a/src/librustc_trans/trans/mir/constant.rs +++ b/src/librustc_trans/trans/mir/constant.rs @@ -16,7 +16,7 @@ use trans::common::{self, Block}; use trans::common::{C_bool, C_bytes, C_floating_f64, C_integral, C_str_slice}; use trans::type_of; -use super::operand::{OperandRef, OperandValue}; +use super::operand::OperandRef; use super::MirContext; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { @@ -26,19 +26,21 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { ty: Ty<'tcx>) -> OperandRef<'tcx> { + use super::operand::OperandValue::{Ref, Immediate}; + let ccx = bcx.ccx(); let llty = type_of::type_of(ccx, ty); let val = match *cv { - ConstVal::Float(v) => OperandValue::Imm(C_floating_f64(v, llty)), - ConstVal::Bool(v) => OperandValue::Imm(C_bool(ccx, v)), - ConstVal::Int(v) => OperandValue::Imm(C_integral(llty, v as u64, true)), - ConstVal::Uint(v) => OperandValue::Imm(C_integral(llty, v, false)), - ConstVal::Str(ref v) => OperandValue::Imm(C_str_slice(ccx, v.clone())), + ConstVal::Float(v) => Immediate(C_floating_f64(v, llty)), + ConstVal::Bool(v) => Immediate(C_bool(ccx, v)), + ConstVal::Int(v) => Immediate(C_integral(llty, v as u64, true)), + ConstVal::Uint(v) => Immediate(C_integral(llty, v, false)), + ConstVal::Str(ref v) => Immediate(C_str_slice(ccx, v.clone())), ConstVal::ByteStr(ref v) => { - OperandValue::Imm(consts::addr_of(ccx, - C_bytes(ccx, v), - 1, - "byte_str")) + Immediate(consts::addr_of(ccx, + C_bytes(ccx, v), + 1, + "byte_str")) } ConstVal::Struct(id) | ConstVal::Tuple(id) => { @@ -52,9 +54,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { Err(_) => panic!("constant eval failure"), }; if common::type_is_immediate(bcx.ccx(), ty) { - OperandValue::Imm(llval) + Immediate(llval) } else { - OperandValue::Ref(llval) + Ref(llval) } } ConstVal::Function(_) => { diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs index 7daf76a8d31..63abdfe2dd9 100644 --- a/src/librustc_trans/trans/mir/operand.rs +++ b/src/librustc_trans/trans/mir/operand.rs @@ -17,8 +17,8 @@ use trans::datum; use super::{MirContext, TempRef}; -/// The Rust representation of an operand's value. This is uniquely -/// determined by the operand type, but is kept as an enum as a +/// The representation of a Rust value. The enum variant is in fact +/// uniquely determined by the value's type, but is kept as a /// safety check. #[derive(Copy, Clone)] pub enum OperandValue { @@ -26,16 +26,22 @@ pub enum OperandValue { /// to be valid for the operand's lifetime. Ref(ValueRef), /// A single LLVM value. - Imm(ValueRef), + Immediate(ValueRef), /// A fat pointer. The first ValueRef is the data and the second /// is the extra. FatPtr(ValueRef, ValueRef) } +/// An `OperandRef` is an "SSA" reference to a Rust value, along with +/// its type. +/// +/// NOTE: unless you know a value's type exactly, you should not +/// generate LLVM opcodes acting on it and instead act via methods, +/// to avoid nasty edge cases. In particular, using `build::Store` +/// directly is sure to cause problems - use `store_operand` instead. #[derive(Copy, Clone)] pub struct OperandRef<'tcx> { - // This will be "indirect" if `appropriate_rvalue_mode` returns - // ByRef, and otherwise ByValue. + // The value. pub val: OperandValue, // The type of value being returned. @@ -43,9 +49,11 @@ pub struct OperandRef<'tcx> { } impl<'tcx> OperandRef<'tcx> { + /// Asserts that this operand refers to a scalar and returns + /// a reference to its value. pub fn immediate(self) -> ValueRef { match self.val { - OperandValue::Imm(s) => s, + OperandValue::Immediate(s) => s, _ => unreachable!() } } @@ -56,8 +64,8 @@ impl<'tcx> OperandRef<'tcx> { format!("OperandRef(Ref({}) @ {:?})", bcx.val_to_string(r), self.ty) } - OperandValue::Imm(i) => { - format!("OperandRef(Imm({}) @ {:?})", + OperandValue::Immediate(i) => { + format!("OperandRef(Immediate({}) @ {:?})", bcx.val_to_string(i), self.ty) } OperandValue::FatPtr(a, d) => { @@ -106,7 +114,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { ty); let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) { datum::ByValue => { - OperandValue::Imm(base::load_ty(bcx, tr_lvalue.llval, ty)) + OperandValue::Immediate(base::load_ty(bcx, tr_lvalue.llval, ty)) } datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => { let (lldata, llextra) = base::load_fat_ptr(bcx, tr_lvalue.llval, ty); @@ -150,7 +158,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug!("store_operand: operand={}", operand.repr(bcx)); match operand.val { OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty), - OperandValue::Imm(s) => base::store_ty(bcx, s, lldest, operand.ty), + OperandValue::Immediate(s) => base::store_ty(bcx, s, lldest, operand.ty), OperandValue::FatPtr(data, extra) => { base::store_fat_ptr(bcx, data, extra, lldest, operand.ty); } diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 8f5496929c2..cce71b25702 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -10,7 +10,6 @@ use llvm::ValueRef; use rustc::middle::ty::{self, Ty}; -use rustc_front::hir; use rustc_mir::repr as mir; use trans::asm; @@ -47,6 +46,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, cast_ty) => { if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { + // into-coerce of a thin pointer to a fat pointer - just + // use the operand path. let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); self.store_operand(bcx, lldest, temp); return bcx; @@ -59,8 +60,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let operand = self.trans_operand(bcx, operand); match operand.val { OperandValue::FatPtr(..) => unreachable!(), - OperandValue::Imm(llval) => { - // ugly alloca. + OperandValue::Immediate(llval) => { + // unsize from an immediate structure. We don't + // really need a temporary alloca here, but + // avoiding it would require us to have + // `coerce_unsized_into` use extractvalue to + // index into the struct, and this case isn't + // important enough for it. debug!("trans_rvalue: creating ugly alloca"); let lltemp = base::alloc_ty(bcx, operand.ty, "__unsize_temp"); base::store_ty(bcx, llval, lltemp, operand.ty); @@ -165,7 +171,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // and is a no-op at the LLVM level operand.val } - OperandValue::Imm(lldata) => { + OperandValue::Immediate(lldata) => { // "standard" unsize let (lldata, llextra) = base::unsize_thin_ptr(bcx, lldata, @@ -200,7 +206,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // destination effectively creates a reference. if common::type_is_sized(bcx.tcx(), ty) { (bcx, OperandRef { - val: OperandValue::Imm(tr_lvalue.llval), + val: OperandValue::Immediate(tr_lvalue.llval), ty: ref_ty, }) } else { @@ -215,7 +221,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Len(ref lvalue) => { let tr_lvalue = self.trans_lvalue(bcx, lvalue); (bcx, OperandRef { - val: OperandValue::Imm(self.lvalue_len(bcx, tr_lvalue)), + val: OperandValue::Immediate(self.lvalue_len(bcx, tr_lvalue)), ty: bcx.tcx().types.usize, }) } @@ -230,7 +236,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { base::compare_fat_ptrs(bcx, lhs_addr, lhs_extra, rhs_addr, rhs_extra, - lhs.ty, cmp_to_hir_cmp(op), + lhs.ty, op.to_hir_binop(), DebugLoc::None) } _ => unreachable!() @@ -242,8 +248,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { lhs.ty, DebugLoc::None) }; (bcx, OperandRef { - val: OperandValue::Imm(llresult), - ty: type_of_binop(bcx.tcx(), op, lhs.ty, rhs.ty), + val: OperandValue::Immediate(llresult), + ty: self.mir.binop_ty(bcx.tcx(), op, lhs.ty, rhs.ty), }) } @@ -261,7 +267,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } }; (bcx, OperandRef { - val: OperandValue::Imm(llval), + val: OperandValue::Immediate(llval), ty: operand.ty, }) } @@ -281,7 +287,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { llalign, DebugLoc::None); (bcx, OperandRef { - val: OperandValue::Imm(llval), + val: OperandValue::Immediate(llval), ty: box_ty, }) } @@ -388,7 +394,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::BinOp::Eq | mir::BinOp::Lt | mir::BinOp::Gt | mir::BinOp::Ne | mir::BinOp::Le | mir::BinOp::Ge => { base::compare_scalar_types(bcx, lhs, rhs, input_ty, - cmp_to_hir_cmp(op), debug_loc) + op.to_hir_binop(), debug_loc) } } } @@ -413,43 +419,3 @@ pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { // (*) this is only true if the type is suitable } - -fn cmp_to_hir_cmp(op: mir::BinOp) -> hir::BinOp_ { - match op { - mir::BinOp::Eq => hir::BiEq, - mir::BinOp::Ne => hir::BiNe, - mir::BinOp::Lt => hir::BiLt, - mir::BinOp::Le => hir::BiLe, - mir::BinOp::Gt => hir::BiGt, - mir::BinOp::Ge => hir::BiGe, - _ => unreachable!() - } -} - -/// FIXME(nikomatsakis): I don't think this function should go here -fn type_of_binop<'tcx>( - tcx: &ty::ctxt<'tcx>, - op: mir::BinOp, - lhs_ty: Ty<'tcx>, - rhs_ty: Ty<'tcx>) - -> Ty<'tcx> -{ - match op { - mir::BinOp::Add | mir::BinOp::Sub | - mir::BinOp::Mul | mir::BinOp::Div | mir::BinOp::Rem | - mir::BinOp::BitXor | mir::BinOp::BitAnd | mir::BinOp::BitOr => { - // these should be integers or floats of the same size. We - // probably want to dump all ops in some intrinsics framework - // someday. - assert_eq!(lhs_ty, rhs_ty); - lhs_ty - } - mir::BinOp::Shl | mir::BinOp::Shr => { - lhs_ty // lhs_ty can be != rhs_ty - } - mir::BinOp::Eq | mir::BinOp::Lt | mir::BinOp::Le | - mir::BinOp::Ne | mir::BinOp::Ge | mir::BinOp::Gt => { - tcx.types.bool - } - } -}