From 602cf7ec3c69c5c9b7d04c7baf66b270f565a001 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 9 Nov 2015 02:16:19 +0200 Subject: [PATCH] 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 4a99f2142d790..be4758581e98e 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 f408bb595a2b7..eb9635066f3b5 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 f5fa897bca631..cf1ecc4d6daaa 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 75fb88ab5ee06..c21103fde9644 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 218edc200d4c6..65fb0d91e4dc5 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 0000000000000..20c3357d7d232 --- /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 or the MIT license +// , 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(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 Foo for T { + fn foo(&self) -> usize { + mem::size_of::() + } +} + +struct S(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 _)); +}