rustc_trans: restrict "immediate pairs" to pairs of scalars.

This commit is contained in:
Eduard-Mihai Burtescu 2017-10-05 04:22:23 +03:00
parent ac60872077
commit f1b7cd9925
10 changed files with 84 additions and 106 deletions

View File

@ -28,7 +28,7 @@ use type_of::LayoutLlvmExt;
use value::Value;
use rustc::traits;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::layout::{self, HasDataLayout, LayoutOf};
use rustc::ty::layout::{HasDataLayout, LayoutOf};
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::hir;
@ -54,25 +54,6 @@ pub fn type_is_fat_ptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) ->
}
}
/// Returns true if the type is represented as a pair of immediates.
pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
-> bool {
let layout = ccx.layout_of(ty);
match layout.fields {
layout::FieldPlacement::Arbitrary { .. } => {
// There must be only 2 fields.
if layout.fields.count() != 2 {
return false;
}
// The two fields must be both immediates.
layout.field(ccx, 0).is_llvm_immediate() &&
layout.field(ccx, 1).is_llvm_immediate()
}
_ => false
}
}
pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
ty.needs_drop(tcx, ty::ParamEnv::empty(traits::Reveal::All))
}

View File

@ -675,10 +675,8 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
let ptr = bcx.pointercast(llresult, ty.llvm_type(ccx).ptr_to());
bcx.store(llval, ptr, Some(ccx.align_of(ret_ty)));
} else {
OperandRef {
val: OperandValue::Immediate(llval),
layout: result.layout
}.unpack_if_pair(bcx).val.store(bcx, result);
OperandRef::from_immediate_or_packed_pair(bcx, llval, result.layout)
.val.store(bcx, result);
}
}
}

View File

@ -19,7 +19,6 @@ use rustc::mir::visit::{Visitor, LvalueContext};
use rustc::mir::traversal;
use rustc::ty;
use rustc::ty::layout::LayoutOf;
use common;
use type_of::LayoutLlvmExt;
use super::MirContext;
@ -32,10 +31,11 @@ pub fn lvalue_locals<'a, 'tcx>(mircx: &MirContext<'a, 'tcx>) -> BitVector {
for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
let ty = mircx.monomorphize(&ty);
debug!("local {} has type {:?}", index, ty);
if mircx.ccx.layout_of(ty).is_llvm_immediate() {
let layout = mircx.ccx.layout_of(ty);
if layout.is_llvm_immediate() {
// These sorts of types are immediates that we can store
// in an ValueRef without an alloca.
} else if common::type_is_imm_pair(mircx.ccx, ty) {
} else if layout.is_llvm_scalar_pair(mircx.ccx) {
// We allow pairs and uses of any of their 2 fields.
} else {
// These sorts of types require an alloca. Note that
@ -145,7 +145,8 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
let ty = proj.base.ty(self.cx.mir, self.cx.ccx.tcx());
let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx()));
if common::type_is_imm_pair(self.cx.ccx, ty) {
let layout = self.cx.ccx.layout_of(ty);
if layout.is_llvm_scalar_pair(self.cx.ccx) {
return;
}
}

View File

@ -135,11 +135,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
if let Some((ret_dest, target)) = destination {
let ret_bcx = this.get_builder(target);
this.set_debug_loc(&ret_bcx, terminator.source_info);
let op = OperandRef {
val: Immediate(invokeret),
layout: fn_ty.ret.layout,
};
this.store_return(&ret_bcx, ret_dest, &fn_ty.ret, op);
this.store_return(&ret_bcx, ret_dest, &fn_ty.ret, invokeret);
}
} else {
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle);
@ -153,11 +149,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
}
if let Some((ret_dest, target)) = destination {
let op = OperandRef {
val: Immediate(llret),
layout: fn_ty.ret.layout,
};
this.store_return(&bcx, ret_dest, &fn_ty.ret, op);
this.store_return(&bcx, ret_dest, &fn_ty.ret, llret);
funclet_br(this, bcx, target);
} else {
bcx.unreachable();
@ -252,7 +244,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
if let Ref(llval, align) = op.val {
bcx.load(llval, align.non_abi())
} else {
op.pack_if_pair(&bcx).immediate()
op.immediate_or_packed_pair(&bcx)
}
};
bcx.ret(llval);
@ -545,12 +537,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
terminator.source_info.span);
if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
// Make a fake operand for store_return
let op = OperandRef {
val: Ref(dst.llval, Alignment::AbiAligned),
layout: fn_ty.ret.layout,
};
self.store_return(&bcx, ret_dest, &fn_ty.ret, op);
self.store_return(&bcx, ret_dest, &fn_ty.ret, dst.llval);
}
if let Some((_, target)) = *destination {
@ -649,7 +636,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
op.val.store(bcx, scratch);
(scratch.llval, Alignment::AbiAligned, true)
} else {
(op.pack_if_pair(bcx).immediate(), Alignment::AbiAligned, false)
(op.immediate_or_packed_pair(bcx), Alignment::AbiAligned, false)
}
}
Ref(llval, align @ Alignment::Packed(_)) if arg.is_indirect() => {
@ -915,12 +902,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
bcx: &Builder<'a, 'tcx>,
dest: ReturnDest<'tcx>,
ret_ty: &ArgType<'tcx>,
op: OperandRef<'tcx>) {
llval: ValueRef) {
use self::ReturnDest::*;
match dest {
Nothing => (),
Store(dst) => ret_ty.store(bcx, op.immediate(), dst),
Store(dst) => ret_ty.store(bcx, llval, dst),
IndirectOperand(tmp, index) => {
let op = tmp.load(bcx);
tmp.storage_dead(bcx);
@ -929,14 +916,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
DirectOperand(index) => {
// If there is a cast, we have to store and reload.
let op = if ret_ty.cast.is_some() {
let tmp = LvalueRef::alloca(bcx, op.layout, "tmp_ret");
let tmp = LvalueRef::alloca(bcx, ret_ty.layout, "tmp_ret");
tmp.storage_live(bcx);
ret_ty.store(bcx, op.immediate(), tmp);
ret_ty.store(bcx, llval, tmp);
let op = tmp.load(bcx);
tmp.storage_dead(bcx);
op
} else {
op.unpack_if_pair(bcx)
OperandRef::from_immediate_or_packed_pair(bcx, llval, ret_ty.layout)
};
self.locals[index] = LocalRef::Operand(Some(op));
}

View File

@ -139,13 +139,14 @@ impl<'a, 'tcx> Const<'tcx> {
}
pub fn to_operand(&self, ccx: &CrateContext<'a, 'tcx>) -> OperandRef<'tcx> {
let llty = ccx.layout_of(self.ty).immediate_llvm_type(ccx);
let layout = ccx.layout_of(self.ty);
let llty = layout.immediate_llvm_type(ccx);
let llvalty = val_ty(self.llval);
let val = if llty == llvalty && common::type_is_imm_pair(ccx, self.ty) {
let val = if llty == llvalty && layout.is_llvm_scalar_pair(ccx) {
let (a, b) = self.get_pair(ccx);
OperandValue::Pair(a, b)
} else if llty == llvalty && ccx.layout_of(self.ty).is_llvm_immediate() {
} else if llty == llvalty && layout.is_llvm_immediate() {
// If the types match, we can use the value directly.
OperandValue::Immediate(self.llval)
} else {

View File

@ -16,7 +16,7 @@ use rustc::mir::tcx::LvalueTy;
use rustc_data_structures::indexed_vec::Idx;
use base;
use builder::Builder;
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null, C_uint_big};
use common::{CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null, C_uint_big};
use consts;
use type_of::LayoutLlvmExt;
use type_::Type;
@ -175,10 +175,10 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
load
};
OperandValue::Immediate(base::to_immediate(bcx, llval, self.layout))
} else if common::type_is_imm_pair(bcx.ccx, self.layout.ty) {
} else if self.layout.is_llvm_scalar_pair(bcx.ccx) {
OperandValue::Pair(
self.project_field(bcx, 0).load(bcx).pack_if_pair(bcx).immediate(),
self.project_field(bcx, 1).load(bcx).pack_if_pair(bcx).immediate())
self.project_field(bcx, 0).load(bcx).immediate(),
self.project_field(bcx, 1).load(bcx).immediate())
} else {
OperandValue::Ref(self.llval, self.alignment)
};

View File

@ -475,11 +475,9 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
bcx.set_value_name(llarg, &name);
llarg_idx += 1;
let operand = OperandRef {
val: OperandValue::Immediate(llarg),
layout: arg.layout
};
return LocalRef::Operand(Some(operand.unpack_if_pair(bcx)));
return LocalRef::Operand(Some(
OperandRef::from_immediate_or_packed_pair(bcx, llarg, arg.layout)
));
} else {
let tmp = LvalueRef::alloca(bcx, arg.layout, &name);
arg.store_fn_arg(bcx, &mut llarg_idx, tmp);

View File

@ -15,7 +15,7 @@ use rustc::mir;
use rustc_data_structures::indexed_vec::Idx;
use base;
use common::{self, CrateContext, C_undef};
use common::{CrateContext, C_undef};
use builder::Builder;
use value::Value;
use type_of::LayoutLlvmExt;
@ -24,7 +24,6 @@ use std::fmt;
use std::ptr;
use super::{MirContext, LocalRef};
use super::constant::Const;
use super::lvalue::{Alignment, LvalueRef};
/// The representation of a Rust value. The enum variant is in fact
@ -84,10 +83,10 @@ impl<'a, 'tcx> OperandRef<'tcx> {
pub fn new_zst(ccx: &CrateContext<'a, 'tcx>,
layout: TyLayout<'tcx>) -> OperandRef<'tcx> {
assert!(layout.is_zst());
let llty = layout.llvm_type(ccx);
// FIXME(eddyb) ZSTs should always be immediate, not pairs.
// This hack only exists to unpack a constant undef pair.
Const::new(C_undef(llty), layout.ty).to_operand(ccx)
OperandRef {
val: OperandValue::Immediate(C_undef(layout.llvm_type(ccx))),
layout
}
}
/// Asserts that this operand refers to a scalar and returns
@ -115,12 +114,13 @@ impl<'a, 'tcx> OperandRef<'tcx> {
}
}
/// If this operand is a Pair, we return an
/// Immediate aggregate with the two values.
pub fn pack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> {
/// If this operand is a `Pair`, we return an aggregate with the two values.
/// For other cases, see `immediate`.
pub fn immediate_or_packed_pair(self, bcx: &Builder<'a, 'tcx>) -> ValueRef {
if let OperandValue::Pair(a, b) = self.val {
let llty = self.layout.llvm_type(bcx.ccx);
debug!("Operand::pack_if_pair: packing {:?} into {:?}", self, llty);
debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}",
self, llty);
// Reconstruct the immediate aggregate.
let mut llpair = C_undef(llty);
let elems = [a, b];
@ -128,29 +128,33 @@ impl<'a, 'tcx> OperandRef<'tcx> {
let elem = base::from_immediate(bcx, elems[i]);
llpair = bcx.insert_value(llpair, elem, self.layout.llvm_field_index(i));
}
self.val = OperandValue::Immediate(llpair);
llpair
} else {
self.immediate()
}
self
}
/// If this operand is a pair in an Immediate,
/// we return a Pair with the two halves.
pub fn unpack_if_pair(mut self, bcx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> {
if let OperandValue::Immediate(llval) = self.val {
/// If the type is a pair, we return a `Pair`, otherwise, an `Immediate`.
pub fn from_immediate_or_packed_pair(bcx: &Builder<'a, 'tcx>,
llval: ValueRef,
layout: TyLayout<'tcx>)
-> OperandRef<'tcx> {
let val = if layout.is_llvm_scalar_pair(bcx.ccx) {
debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}",
llval, layout);
// Deconstruct the immediate aggregate.
if common::type_is_imm_pair(bcx.ccx, self.layout.ty) {
debug!("Operand::unpack_if_pair: unpacking {:?}", self);
let a = bcx.extract_value(llval, layout.llvm_field_index(0));
let a = base::to_immediate(bcx, a, layout.field(bcx.ccx, 0));
let a = bcx.extract_value(llval, self.layout.llvm_field_index(0));
let a = base::to_immediate(bcx, a, self.layout.field(bcx.ccx, 0));
let b = bcx.extract_value(llval, layout.llvm_field_index(1));
let b = base::to_immediate(bcx, b, layout.field(bcx.ccx, 1));
let b = bcx.extract_value(llval, self.layout.llvm_field_index(1));
let b = base::to_immediate(bcx, b, self.layout.field(bcx.ccx, 1));
self.val = OperandValue::Pair(a, b);
}
}
self
OperandValue::Pair(a, b)
} else {
OperandValue::Immediate(llval)
};
OperandRef { val, layout }
}
}
@ -170,16 +174,9 @@ impl<'a, 'tcx> OperandValue {
bcx.store(base::from_immediate(bcx, s), dest.llval, dest.alignment.non_abi());
}
OperandValue::Pair(a, b) => {
// See comment above about zero-sized values.
let dest_a = dest.project_field(bcx, 0);
if !dest_a.layout.is_zst() {
let a = base::from_immediate(bcx, a);
bcx.store(a, dest_a.llval, dest_a.alignment.non_abi());
}
let dest_b = dest.project_field(bcx, 1);
if !dest_b.layout.is_zst() {
let b = base::from_immediate(bcx, b);
bcx.store(b, dest_b.llval, dest_b.alignment.non_abi());
for (i, &x) in [a, b].iter().enumerate() {
OperandValue::Immediate(x)
.store(bcx, dest.project_field(bcx, i));
}
}
}
@ -218,13 +215,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
(OperandValue::Pair(a, b),
&mir::ProjectionElem::Field(ref f, ty)) => {
let llval = [a, b][f.index()];
let op = OperandRef {
return OperandRef {
val: OperandValue::Immediate(llval),
layout: bcx.ccx.layout_of(self.monomorphize(&ty))
};
// Handle nested pairs.
return op.unpack_if_pair(bcx);
}
_ => {}
}

View File

@ -70,9 +70,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
// `CoerceUnsized` can be passed by a where-clause,
// so the (generic) MIR may not be able to expand it.
let operand = self.trans_operand(&bcx, source);
let operand = operand.pack_if_pair(&bcx);
match operand.val {
OperandValue::Pair(..) => bug!(),
OperandValue::Pair(..) |
OperandValue::Immediate(_) => {
// unsize from an immediate structure. We don't
// really need a temporary alloca here, but

View File

@ -174,6 +174,7 @@ pub struct PointeeInfo {
pub trait LayoutLlvmExt<'tcx> {
fn is_llvm_immediate(&self) -> bool;
fn is_llvm_scalar_pair<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> bool;
fn llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type;
fn immediate_llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type;
fn over_align(&self) -> Option<Align>;
@ -192,6 +193,24 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
}
}
fn is_llvm_scalar_pair<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> bool {
match self.fields {
layout::FieldPlacement::Arbitrary { .. } => {
// There must be only 2 fields.
if self.fields.count() != 2 {
return false;
}
// The two fields must be both scalars.
match (&self.field(ccx, 0).abi, &self.field(ccx, 1).abi) {
(&layout::Abi::Scalar(_), &layout::Abi::Scalar(_)) => true,
_ => false
}
}
_ => false
}
}
/// Get the LLVM type corresponding to a Rust type, i.e. `rustc::ty::Ty`.
/// The pointee type of the pointer in `LvalueRef` is always this type.
/// For sized types, it is also the right LLVM type for an `alloca`