mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
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.
This commit is contained in:
parent
3beb159809
commit
602cf7ec3c
@ -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));
|
||||
|
@ -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");
|
||||
|
@ -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.
|
||||
|
@ -192,4 +192,3 @@ mod lvalue;
|
||||
mod rvalue;
|
||||
mod operand;
|
||||
mod statement;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
173
src/test/run-pass/mir_raw_fat_ptr.rs
Normal file
173
src/test/run-pass/mir_raw_fat_ptr.rs
Normal file
@ -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 _));
|
||||
}
|
Loading…
Reference in New Issue
Block a user