From dcb64b52a241406f77d84572c90a71a366fc3c37 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 10 Nov 2015 22:05:11 +0200 Subject: [PATCH] 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 be4758581e98..7f258cb845b4 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 cf1ecc4d6daa..fc76d1e787da 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 ccab9a20e75d..181a03790eee 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 43973fe55583..923baf0dcfe1 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 98c156699761..d846307dc789 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 c21103fde964..c8eef7ff4487 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 fb529d8975e1..7daf76a8d31e 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 65fb0d91e4dc..f933252b51e4 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 000000000000..1916e608a8c7 --- /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 or the MIT license +// , 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(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); +}