Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

implement raw fat pointer operations #28270

Merged
merged 1 commit into from
Sep 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 93 additions & 40 deletions src/librustc_trans/trans/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1682,20 +1682,70 @@ fn trans_addr_of<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
}

// Important to get types for both lhs and rhs, because one might be _|_
// and the other not.
fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
binop_expr: &hir::Expr,
binop_ty: Ty<'tcx>,
op: hir::BinOp,
lhs_t: Ty<'tcx>,
lhs: ValueRef,
rhs_t: Ty<'tcx>,
rhs: ValueRef)
-> DatumBlock<'blk, 'tcx, Expr> {
let _icx = push_ctxt("trans_eager_binop");
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>,
op: hir::BinOp,
lhs: Datum<'tcx, Rvalue>,
rhs: Datum<'tcx, Rvalue>)
-> DatumBlock<'blk, 'tcx, Expr>
{
let _icx = push_ctxt("trans_scalar_binop");

let tcx = bcx.tcx();
let lhs_t = lhs.ty;
assert!(!lhs_t.is_simd());
let is_float = lhs_t.is_fp();
let is_signed = lhs_t.is_signed();
Expand All @@ -1704,6 +1754,8 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let binop_debug_loc = binop_expr.debug_loc();

let mut bcx = bcx;
let lhs = lhs.to_llscalarish(bcx);
let rhs = rhs.to_llscalarish(bcx);
let val = match op.node {
hir::BiAdd => {
if is_float {
Expand Down Expand Up @@ -1745,7 +1797,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
op,
lhs,
rhs,
rhs_t);
lhs_t);
if is_signed {
SDiv(bcx, lhs, rhs, binop_debug_loc)
} else {
Expand Down Expand Up @@ -1796,7 +1848,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// Only zero-check integers; fp %0 is NaN
bcx = base::fail_if_zero_or_overflows(bcx,
expr_info(binop_expr),
op, lhs, rhs, rhs_t);
op, lhs, rhs, lhs_t);
if is_signed {
SRem(bcx, lhs, rhs, binop_debug_loc)
} else {
Expand Down Expand Up @@ -1896,23 +1948,26 @@ fn trans_binary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
_ => {
let mut bcx = bcx;
let lhs_datum = unpack_datum!(bcx, trans(bcx, lhs));
let rhs_datum = unpack_datum!(bcx, trans(bcx, rhs));
let binop_ty = expr_ty(bcx, expr);

debug!("trans_binary (expr {}): lhs_datum={}",
expr.id,
lhs_datum.to_string(ccx));
let lhs_ty = lhs_datum.ty;
let lhs = lhs_datum.to_llscalarish(bcx);

debug!("trans_binary (expr {}): rhs_datum={}",
expr.id,
rhs_datum.to_string(ccx));
let rhs_ty = rhs_datum.ty;
let rhs = rhs_datum.to_llscalarish(bcx);
trans_eager_binop(bcx, expr, binop_ty, op,
lhs_ty, lhs, rhs_ty, rhs)
let lhs = unpack_datum!(bcx, trans(bcx, lhs));
let lhs = unpack_datum!(bcx, lhs.to_rvalue_datum(bcx, "binop_lhs"));
debug!("trans_binary (expr {}): lhs={}",
expr.id, lhs.to_string(ccx));
let rhs = unpack_datum!(bcx, trans(bcx, rhs));
let rhs = unpack_datum!(bcx, rhs.to_rvalue_datum(bcx, "binop_rhs"));
debug!("trans_binary (expr {}): rhs={}",
expr.id, rhs.to_string(ccx));

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)
} else {
assert!(!type_is_fat_ptr(ccx.tcx(), rhs.ty),
"built-in binary operators on fat pointers are homogeneous");
trans_scalar_binop(bcx, expr, binop_ty, op, lhs, rhs)
}
}
}
}
Expand Down Expand Up @@ -2123,21 +2178,19 @@ fn trans_assign_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
assert!(!bcx.tcx().is_method_call(expr.id));

// Evaluate LHS (destination), which should be an lvalue
let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op"));
assert!(!bcx.fcx.type_needs_drop(dst_datum.ty));
let dst_ty = dst_datum.ty;
let dst = load_ty(bcx, dst_datum.val, dst_datum.ty);
let dst = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op"));
assert!(!bcx.fcx.type_needs_drop(dst.ty));
let lhs = load_ty(bcx, dst.val, dst.ty);
let lhs = immediate_rvalue(lhs, dst.ty);

// Evaluate RHS
let rhs_datum = unpack_datum!(bcx, trans(bcx, &*src));
let rhs_ty = rhs_datum.ty;
let rhs = rhs_datum.to_llscalarish(bcx);
// Evaluate RHS - FIXME(#28160) this sucks
let rhs = unpack_datum!(bcx, trans(bcx, &*src));
let rhs = unpack_datum!(bcx, rhs.to_rvalue_datum(bcx, "assign_op_rhs"));

// Perform computation and store the result
let result_datum = unpack_datum!(
bcx, trans_eager_binop(bcx, expr, dst_datum.ty, op,
dst_ty, dst, rhs_ty, rhs));
return result_datum.store_to(bcx, dst_datum.val);
bcx, trans_scalar_binop(bcx, expr, dst.ty, op, lhs, rhs));
return result_datum.store_to(bcx, dst.val);
}

fn auto_ref<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
Expand Down
14 changes: 14 additions & 0 deletions src/test/run-pass/issue-27054-primitive-binary-ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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.

fn main() {
let x = &mut 1;
assert_eq!(*x + { *x=2; 1 }, 2);
}
127 changes: 127 additions & 0 deletions src/test/run-pass/raw-fat-ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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.

// check raw fat pointer ops

use std::mem;

fn assert_inorder<T: PartialEq + PartialOrd>(a: &[T]) {
for i in 0..a.len() {
for j in 0..a.len() {
if i < j {
assert!(a[i] < a[j]);
assert!(a[i] <= a[j]);
assert!(!(a[i] == a[j]));
assert!(a[i] != a[j]);
assert!(!(a[i] >= a[j]));
assert!(!(a[i] > a[j]));
} else if i == j {
assert!(!(a[i] < a[j]));
assert!(a[i] <= a[j]);
assert!(a[i] == a[j]);
assert!(!(a[i] != a[j]));
assert!(a[i] >= a[j]);
assert!(!(a[i] > a[j]));
} else {
assert!(!(a[i] < a[j]));
assert!(!(a[i] <= a[j]));
assert!(!(a[i] == a[j]));
assert!(a[i] != a[j]);
assert!(a[i] >= a[j]);
assert!(a[i] > a[j]);
}
}
}
}

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 mut array = [0,1,2,3,4];
let mut 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);

// check ordering for mut arrays
let mut ptrs: Vec<*mut [u8]> = vec![
&mut array[0..0], &mut array[0..1], &mut array, &mut array[1..]
];

let array_addr = &mut array as *mut [u8] as *mut u8 as usize;
let array2_addr = &mut array2 as *mut [u8] as *mut u8 as usize;
if array2_addr < array_addr {
ptrs.insert(0, &mut array2);
} else {
ptrs.push(&mut array2);
}
assert_inorder(&ptrs);

let mut u8_ = (0u8, 1u8);
let mut 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);

// check ordering for mut ptrs
let buf: &mut [*mut Foo] = &mut [
&mut u8_, &mut u8_.0,
&mut u32_, &mut 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);

// 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]>
]);
}