提交 dcb64b52 编写于 作者: A Ariel Ben-Yehuda

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.
上级 602cf7ec
......@@ -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()))
......
......@@ -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
......
......@@ -43,7 +43,7 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
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 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
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)
}
......
......@@ -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 @@ pub fn trans_constval(&mut self,
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 @@ pub fn trans_constval(&mut self,
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 {
......
......@@ -18,7 +18,8 @@
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 @@ pub fn alloca<'bcx>(bcx: Block<'bcx, '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 @@ pub fn trans_lvalue(&mut self,
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 @@ pub fn trans_lvalue(&mut self,
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,
}
}
......
......@@ -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()
}
......
......@@ -12,22 +12,64 @@
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 @@ pub fn trans_operand(&mut self,
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 @@ pub fn trans_operand_into(&mut self,
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);
}
}
}
}
......@@ -26,7 +26,7 @@
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 @@ pub fn trans_rvalue(&mut self,
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 @@ pub fn trans_rvalue(&mut self,
_ => {
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 @@ pub fn trans_rvalue_operand(&mut self,
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 @@ pub fn trans_rvalue_operand(&mut self,
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 @@ pub fn trans_rvalue_operand(&mut self,
llalign,
DebugLoc::None);
(bcx, OperandRef {
llval: llval,
val: OperandValue::Imm(llval),
ty: box_ty,
})
}
......@@ -307,6 +235,104 @@ pub fn trans_rvalue_operand(&mut self,
}
}
}
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>,
......
// 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);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册