1
Fork 0

Auto merge of #38302 - Mark-Simulacrum:trans-cleanup, r=eddyb

Cleanup old trans

This is a cleanup of old trans, with the following main points:
 - Remove the `build.rs` API (prefer using `Builder` directly, which is now passed where needed through `BlockAndBuilder`).
 - Remove `Block` (inlining it into `BlockAndBuilder`)
 - Remove `Callee::call`, primarily through inlining and simplification of code.
 - Thinned `FunctionContext`:
   - `mir`, `debug_scopes`, `scopes`, and `fn_ty` are moved to `MirContext`.
   - `param_env` is moved to `SharedCrateContext` and renamed to `empty_param_env`.
   - `llretslotptr` is removed, replaced with more careful management of the return values in calls.
   - `landingpad_alloca` is inlined into cleanup.
   - `param_substs` are moved to `MirContext`.
   - `span` is removed, it was never set to anything but `None`.
   - `block_arena` and `lpad_arena` are removed, since neither was necessary (landing pads and block are quite small, and neither needs arena allocation).
 - Fixed `drop_in_place` not running other destructors in the same function.

Fixes #35566 (thanks to @est31 for confirming).
This commit is contained in:
bors 2016-12-21 10:38:22 +00:00
commit 1b38776c1f
37 changed files with 1910 additions and 5026 deletions

View file

@ -127,6 +127,7 @@ pub fn usable_size(size: usize, align: usize) -> usize {
pub const EMPTY: *mut () = 0x1 as *mut (); pub const EMPTY: *mut () = 0x1 as *mut ();
/// The allocator for unique pointers. /// The allocator for unique pointers.
// This function must not unwind. If it does, MIR trans will fail.
#[cfg(not(test))] #[cfg(not(test))]
#[lang = "exchange_malloc"] #[lang = "exchange_malloc"]
#[inline] #[inline]

View file

@ -710,6 +710,7 @@ extern "C" {
// Operations on instructions // Operations on instructions
pub fn LLVMGetInstructionParent(Inst: ValueRef) -> BasicBlockRef; pub fn LLVMGetInstructionParent(Inst: ValueRef) -> BasicBlockRef;
pub fn LLVMGetFirstBasicBlock(Fn: ValueRef) -> BasicBlockRef;
pub fn LLVMGetFirstInstruction(BB: BasicBlockRef) -> ValueRef; pub fn LLVMGetFirstInstruction(BB: BasicBlockRef) -> ValueRef;
pub fn LLVMInstructionEraseFromParent(Inst: ValueRef); pub fn LLVMInstructionEraseFromParent(Inst: ValueRef);

View file

@ -10,7 +10,6 @@
use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace}; use llvm::{self, ValueRef, Integer, Pointer, Float, Double, Struct, Array, Vector, AttributePlace};
use base; use base;
use build::AllocaFcx;
use common::{type_is_fat_ptr, BlockAndBuilder, C_uint}; use common::{type_is_fat_ptr, BlockAndBuilder, C_uint};
use context::CrateContext; use context::CrateContext;
use cabi_x86; use cabi_x86;
@ -99,21 +98,11 @@ impl ArgAttributes {
self self
} }
pub fn unset(&mut self, attr: ArgAttribute) -> &mut Self {
self.regular = self.regular - attr;
self
}
pub fn set_dereferenceable(&mut self, bytes: u64) -> &mut Self { pub fn set_dereferenceable(&mut self, bytes: u64) -> &mut Self {
self.dereferenceable_bytes = bytes; self.dereferenceable_bytes = bytes;
self self
} }
pub fn unset_dereferenceable(&mut self) -> &mut Self {
self.dereferenceable_bytes = 0;
self
}
pub fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) { pub fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) {
unsafe { unsafe {
self.regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); self.regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn));
@ -246,7 +235,7 @@ impl ArgType {
if self.is_ignore() { if self.is_ignore() {
return; return;
} }
let ccx = bcx.ccx(); let ccx = bcx.ccx;
if self.is_indirect() { if self.is_indirect() {
let llsz = llsize_of(ccx, self.ty); let llsz = llsize_of(ccx, self.ty);
let llalign = llalign_of_min(ccx, self.ty); let llalign = llalign_of_min(ccx, self.ty);
@ -278,7 +267,7 @@ impl ArgType {
// bitcasting to the struct type yields invalid cast errors. // bitcasting to the struct type yields invalid cast errors.
// We instead thus allocate some scratch space... // We instead thus allocate some scratch space...
let llscratch = AllocaFcx(bcx.fcx(), ty, "abi_cast"); let llscratch = bcx.fcx().alloca(ty, "abi_cast");
base::Lifetime::Start.call(bcx, llscratch); base::Lifetime::Start.call(bcx, llscratch);
// ...where we first store the value... // ...where we first store the value...
@ -431,7 +420,7 @@ impl FnType {
let ret_ty = sig.output(); let ret_ty = sig.output();
let mut ret = arg_of(ret_ty, true); let mut ret = arg_of(ret_ty, true);
if !type_is_fat_ptr(ccx.tcx(), ret_ty) { if !type_is_fat_ptr(ccx, ret_ty) {
// The `noalias` attribute on the return value is useful to a // The `noalias` attribute on the return value is useful to a
// function ptr caller. // function ptr caller.
if let ty::TyBox(_) = ret_ty.sty { if let ty::TyBox(_) = ret_ty.sty {
@ -496,7 +485,7 @@ impl FnType {
for ty in inputs.iter().chain(extra_args.iter()) { for ty in inputs.iter().chain(extra_args.iter()) {
let mut arg = arg_of(ty, false); let mut arg = arg_of(ty, false);
if type_is_fat_ptr(ccx.tcx(), ty) { if type_is_fat_ptr(ccx, ty) {
let original_tys = arg.original_ty.field_types(); let original_tys = arg.original_ty.field_types();
let sizing_tys = arg.ty.field_types(); let sizing_tys = arg.ty.field_types();
assert_eq!((original_tys.len(), sizing_tys.len()), (2, 2)); assert_eq!((original_tys.len(), sizing_tys.len()), (2, 2));
@ -569,7 +558,7 @@ impl FnType {
}; };
// Fat pointers are returned by-value. // Fat pointers are returned by-value.
if !self.ret.is_ignore() { if !self.ret.is_ignore() {
if !type_is_fat_ptr(ccx.tcx(), sig.output()) { if !type_is_fat_ptr(ccx, sig.output()) {
fixup(&mut self.ret); fixup(&mut self.ret);
} }
} }

View file

@ -48,9 +48,7 @@ use std;
use llvm::{ValueRef, True, IntEQ, IntNE}; use llvm::{ValueRef, True, IntEQ, IntNE};
use rustc::ty::layout; use rustc::ty::layout;
use rustc::ty::{self, Ty, AdtKind}; use rustc::ty::{self, Ty, AdtKind};
use build::*;
use common::*; use common::*;
use debuginfo::DebugLoc;
use glue; use glue;
use base; use base;
use machine; use machine;
@ -295,7 +293,7 @@ fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fields: &Vec<Ty<'tcx>>
sizing: bool, dst: bool) -> Vec<Type> { sizing: bool, dst: bool) -> Vec<Type> {
let fields = variant.field_index_by_increasing_offset().map(|i| fields[i as usize]); let fields = variant.field_index_by_increasing_offset().map(|i| fields[i as usize]);
if sizing { if sizing {
fields.filter(|ty| !dst || type_is_sized(cx.tcx(), *ty)) fields.filter(|ty| !dst || cx.shared().type_is_sized(*ty))
.map(|ty| type_of::sizing_type_of(cx, ty)).collect() .map(|ty| type_of::sizing_type_of(cx, ty)).collect()
} else { } else {
fields.map(|ty| type_of::in_memory_type_of(cx, ty)).collect() fields.map(|ty| type_of::in_memory_type_of(cx, ty)).collect()
@ -304,12 +302,13 @@ fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fields: &Vec<Ty<'tcx>>
/// Obtain a representation of the discriminant sufficient to translate /// Obtain a representation of the discriminant sufficient to translate
/// destructuring; this may or may not involve the actual discriminant. /// destructuring; this may or may not involve the actual discriminant.
pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn trans_switch<'a, 'tcx>(
t: Ty<'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
scrutinee: ValueRef, t: Ty<'tcx>,
range_assert: bool) scrutinee: ValueRef,
-> (BranchKind, Option<ValueRef>) { range_assert: bool
let l = bcx.ccx().layout_of(t); ) -> (BranchKind, Option<ValueRef>) {
let l = bcx.ccx.layout_of(t);
match *l { match *l {
layout::CEnum { .. } | layout::General { .. } | layout::CEnum { .. } | layout::General { .. } |
layout::RawNullablePointer { .. } | layout::StructWrappedNullablePointer { .. } => { layout::RawNullablePointer { .. } | layout::StructWrappedNullablePointer { .. } => {
@ -331,34 +330,37 @@ pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool {
} }
/// Obtain the actual discriminant of a value. /// Obtain the actual discriminant of a value.
pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, pub fn trans_get_discr<'a, 'tcx>(
scrutinee: ValueRef, cast_to: Option<Type>, bcx: &BlockAndBuilder<'a, 'tcx>,
range_assert: bool) t: Ty<'tcx>,
-> ValueRef { scrutinee: ValueRef,
cast_to: Option<Type>,
range_assert: bool
) -> ValueRef {
let (def, substs) = match t.sty { let (def, substs) = match t.sty {
ty::TyAdt(ref def, substs) if def.adt_kind() == AdtKind::Enum => (def, substs), ty::TyAdt(ref def, substs) if def.adt_kind() == AdtKind::Enum => (def, substs),
_ => bug!("{} is not an enum", t) _ => bug!("{} is not an enum", t)
}; };
debug!("trans_get_discr t: {:?}", t); debug!("trans_get_discr t: {:?}", t);
let l = bcx.ccx().layout_of(t); let l = bcx.ccx.layout_of(t);
let val = match *l { let val = match *l {
layout::CEnum { discr, min, max, .. } => { layout::CEnum { discr, min, max, .. } => {
load_discr(bcx, discr, scrutinee, min, max, range_assert) load_discr(bcx, discr, scrutinee, min, max, range_assert)
} }
layout::General { discr, .. } => { layout::General { discr, .. } => {
let ptr = StructGEP(bcx, scrutinee, 0); let ptr = bcx.struct_gep(scrutinee, 0);
load_discr(bcx, discr, ptr, 0, def.variants.len() as u64 - 1, load_discr(bcx, discr, ptr, 0, def.variants.len() as u64 - 1,
range_assert) range_assert)
} }
layout::Univariant { .. } | layout::UntaggedUnion { .. } => C_u8(bcx.ccx(), 0), layout::Univariant { .. } | layout::UntaggedUnion { .. } => C_u8(bcx.ccx, 0),
layout::RawNullablePointer { nndiscr, .. } => { layout::RawNullablePointer { nndiscr, .. } => {
let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
let llptrty = type_of::sizing_type_of(bcx.ccx(), let llptrty = type_of::sizing_type_of(bcx.ccx,
monomorphize::field_ty(bcx.ccx().tcx(), substs, monomorphize::field_ty(bcx.ccx.tcx(), substs,
&def.variants[nndiscr as usize].fields[0])); &def.variants[nndiscr as usize].fields[0]));
ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty), DebugLoc::None) bcx.icmp(cmp, bcx.load(scrutinee), C_null(llptrty))
} }
layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee) struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee)
@ -367,24 +369,28 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>,
}; };
match cast_to { match cast_to {
None => val, None => val,
Some(llty) => if is_discr_signed(&l) { SExt(bcx, val, llty) } else { ZExt(bcx, val, llty) } Some(llty) => if is_discr_signed(&l) { bcx.sext(val, llty) } else { bcx.zext(val, llty) }
} }
} }
fn struct_wrapped_nullable_bitdiscr(bcx: Block, nndiscr: u64, discrfield: &layout::FieldPath, fn struct_wrapped_nullable_bitdiscr(
scrutinee: ValueRef) -> ValueRef { bcx: &BlockAndBuilder,
let llptrptr = GEPi(bcx, scrutinee, nndiscr: u64,
discrfield: &layout::FieldPath,
scrutinee: ValueRef
) -> ValueRef {
let llptrptr = bcx.gepi(scrutinee,
&discrfield.iter().map(|f| *f as usize).collect::<Vec<_>>()[..]); &discrfield.iter().map(|f| *f as usize).collect::<Vec<_>>()[..]);
let llptr = Load(bcx, llptrptr); let llptr = bcx.load(llptrptr);
let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
ICmp(bcx, cmp, llptr, C_null(val_ty(llptr)), DebugLoc::None) bcx.icmp(cmp, llptr, C_null(val_ty(llptr)))
} }
/// Helper for cases where the discriminant is simply loaded. /// Helper for cases where the discriminant is simply loaded.
fn load_discr(bcx: Block, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64, fn load_discr(bcx: &BlockAndBuilder, ity: layout::Integer, ptr: ValueRef, min: u64, max: u64,
range_assert: bool) range_assert: bool)
-> ValueRef { -> ValueRef {
let llty = Type::from_integer(bcx.ccx(), ity); let llty = Type::from_integer(bcx.ccx, ity);
assert_eq!(val_ty(ptr), llty.ptr_to()); assert_eq!(val_ty(ptr), llty.ptr_to());
let bits = ity.size().bits(); let bits = ity.size().bits();
assert!(bits <= 64); assert!(bits <= 64);
@ -397,11 +403,11 @@ fn load_discr(bcx: Block, ity: layout::Integer, ptr: ValueRef, min: u64, max: u6
// rejected by the LLVM verifier (it would mean either an // rejected by the LLVM verifier (it would mean either an
// empty set, which is impossible, or the entire range of the // empty set, which is impossible, or the entire range of the
// type, which is pointless). // type, which is pointless).
Load(bcx, ptr) bcx.load(ptr)
} else { } else {
// llvm::ConstantRange can deal with ranges that wrap around, // llvm::ConstantRange can deal with ranges that wrap around,
// so an overflow on (max + 1) is fine. // so an overflow on (max + 1) is fine.
LoadRangeAssert(bcx, ptr, min, max.wrapping_add(1), /* signed: */ True) bcx.load_range_assert(ptr, min, max.wrapping_add(1), /* signed: */ True)
} }
} }
@ -409,18 +415,17 @@ fn load_discr(bcx: Block, ity: layout::Integer, ptr: ValueRef, min: u64, max: u6
/// discriminant-like value returned by `trans_switch`. /// discriminant-like value returned by `trans_switch`.
/// ///
/// This should ideally be less tightly tied to `_match`. /// This should ideally be less tightly tied to `_match`.
pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, value: Disr) pub fn trans_case<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, value: Disr) -> ValueRef {
-> ValueRef { let l = bcx.ccx.layout_of(t);
let l = bcx.ccx().layout_of(t);
match *l { match *l {
layout::CEnum { discr, .. } layout::CEnum { discr, .. }
| layout::General { discr, .. }=> { | layout::General { discr, .. }=> {
C_integral(Type::from_integer(bcx.ccx(), discr), value.0, true) C_integral(Type::from_integer(bcx.ccx, discr), value.0, true)
} }
layout::RawNullablePointer { .. } | layout::RawNullablePointer { .. } |
layout::StructWrappedNullablePointer { .. } => { layout::StructWrappedNullablePointer { .. } => {
assert!(value == Disr(0) || value == Disr(1)); assert!(value == Disr(0) || value == Disr(1));
C_bool(bcx.ccx(), value != Disr(0)) C_bool(bcx.ccx, value != Disr(0))
} }
_ => { _ => {
bug!("{} does not have a discriminant. Represented as {:#?}", t, l); bug!("{} does not have a discriminant. Represented as {:#?}", t, l);
@ -430,18 +435,19 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, value: Disr)
/// Set the discriminant for a new value of the given case of the given /// Set the discriminant for a new value of the given case of the given
/// representation. /// representation.
pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, pub fn trans_set_discr<'a, 'tcx>(
val: ValueRef, to: Disr) { bcx: &BlockAndBuilder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr
let l = bcx.ccx().layout_of(t); ) {
let l = bcx.ccx.layout_of(t);
match *l { match *l {
layout::CEnum{ discr, min, max, .. } => { layout::CEnum{ discr, min, max, .. } => {
assert_discr_in_range(Disr(min), Disr(max), to); assert_discr_in_range(Disr(min), Disr(max), to);
Store(bcx, C_integral(Type::from_integer(bcx.ccx(), discr), to.0, true), bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true),
val); val);
} }
layout::General{ discr, .. } => { layout::General{ discr, .. } => {
Store(bcx, C_integral(Type::from_integer(bcx.ccx(), discr), to.0, true), bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true),
StructGEP(bcx, val, 0)); bcx.struct_gep(val, 0));
} }
layout::Univariant { .. } layout::Univariant { .. }
| layout::UntaggedUnion { .. } | layout::UntaggedUnion { .. }
@ -449,10 +455,10 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>,
assert_eq!(to, Disr(0)); assert_eq!(to, Disr(0));
} }
layout::RawNullablePointer { nndiscr, .. } => { layout::RawNullablePointer { nndiscr, .. } => {
let nnty = compute_fields(bcx.ccx(), t, nndiscr as usize, false)[0]; let nnty = compute_fields(bcx.ccx, t, nndiscr as usize, false)[0];
if to.0 != nndiscr { if to.0 != nndiscr {
let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty); let llptrty = type_of::sizing_type_of(bcx.ccx, nnty);
Store(bcx, C_null(llptrty), val); bcx.store(C_null(llptrty), val);
} }
} }
layout::StructWrappedNullablePointer { nndiscr, ref discrfield, ref nonnull, .. } => { layout::StructWrappedNullablePointer { nndiscr, ref discrfield, ref nonnull, .. } => {
@ -461,17 +467,16 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>,
// Issue #34427: As workaround for LLVM bug on // Issue #34427: As workaround for LLVM bug on
// ARM, use memset of 0 on whole struct rather // ARM, use memset of 0 on whole struct rather
// than storing null to single target field. // than storing null to single target field.
let b = B(bcx); let llptr = bcx.pointercast(val, Type::i8(bcx.ccx).ptr_to());
let llptr = b.pointercast(val, Type::i8(b.ccx).ptr_to()); let fill_byte = C_u8(bcx.ccx, 0);
let fill_byte = C_u8(b.ccx, 0); let size = C_uint(bcx.ccx, nonnull.stride().bytes());
let size = C_uint(b.ccx, nonnull.stride().bytes()); let align = C_i32(bcx.ccx, nonnull.align.abi() as i32);
let align = C_i32(b.ccx, nonnull.align.abi() as i32); base::call_memset(bcx, llptr, fill_byte, size, align, false);
base::call_memset(&b, llptr, fill_byte, size, align, false);
} else { } else {
let path = discrfield.iter().map(|&i| i as usize).collect::<Vec<_>>(); let path = discrfield.iter().map(|&i| i as usize).collect::<Vec<_>>();
let llptrptr = GEPi(bcx, val, &path[..]); let llptrptr = bcx.gepi(val, &path[..]);
let llptrty = val_ty(llptrptr).element_type(); let llptrty = val_ty(llptrptr).element_type();
Store(bcx, C_null(llptrty), llptrptr); bcx.store(C_null(llptrty), llptrptr);
} }
} }
} }
@ -479,7 +484,7 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>,
} }
} }
fn target_sets_discr_via_memset<'blk, 'tcx>(bcx: Block<'blk, 'tcx>) -> bool { fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>) -> bool {
bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64" bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64"
} }
@ -492,19 +497,15 @@ fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) {
} }
/// Access a field, at a point when the value's case is known. /// Access a field, at a point when the value's case is known.
pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, pub fn trans_field_ptr<'a, 'tcx>(
val: MaybeSizedValue, discr: Disr, ix: usize) -> ValueRef { bcx: &BlockAndBuilder<'a, 'tcx>,
trans_field_ptr_builder(&bcx.build(), t, val, discr, ix) t: Ty<'tcx>,
} val: MaybeSizedValue,
discr: Disr,
/// Access a field, at a point when the value's case is known. ix: usize
pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, ) -> ValueRef {
t: Ty<'tcx>, let l = bcx.ccx.layout_of(t);
val: MaybeSizedValue, debug!("trans_field_ptr on {} represented as {:#?}", t, l);
discr: Disr, ix: usize)
-> ValueRef {
let l = bcx.ccx().layout_of(t);
debug!("trans_field_ptr_builder on {} represented as {:#?}", t, l);
// Note: if this ever needs to generate conditionals (e.g., if we // Note: if this ever needs to generate conditionals (e.g., if we
// decide to do some kind of cdr-coding-like non-unique repr // decide to do some kind of cdr-coding-like non-unique repr
// someday), it will need to return a possibly-new bcx as well. // someday), it will need to return a possibly-new bcx as well.
@ -512,7 +513,7 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
layout::Univariant { ref variant, .. } => { layout::Univariant { ref variant, .. } => {
assert_eq!(discr, Disr(0)); assert_eq!(discr, Disr(0));
struct_field_ptr(bcx, &variant, struct_field_ptr(bcx, &variant,
&compute_fields(bcx.ccx(), t, 0, false), &compute_fields(bcx.ccx, t, 0, false),
val, ix, false) val, ix, false)
} }
layout::Vector { count, .. } => { layout::Vector { count, .. } => {
@ -521,57 +522,53 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
bcx.struct_gep(val.value, ix) bcx.struct_gep(val.value, ix)
} }
layout::General { discr: d, ref variants, .. } => { layout::General { discr: d, ref variants, .. } => {
let mut fields = compute_fields(bcx.ccx(), t, discr.0 as usize, false); let mut fields = compute_fields(bcx.ccx, t, discr.0 as usize, false);
fields.insert(0, d.to_ty(&bcx.ccx().tcx(), false)); fields.insert(0, d.to_ty(&bcx.ccx.tcx(), false));
struct_field_ptr(bcx, &variants[discr.0 as usize], struct_field_ptr(bcx, &variants[discr.0 as usize],
&fields, &fields,
val, ix + 1, true) val, ix + 1, true)
} }
layout::UntaggedUnion { .. } => { layout::UntaggedUnion { .. } => {
let fields = compute_fields(bcx.ccx(), t, 0, false); let fields = compute_fields(bcx.ccx, t, 0, false);
let ty = type_of::in_memory_type_of(bcx.ccx(), fields[ix]); let ty = type_of::in_memory_type_of(bcx.ccx, fields[ix]);
if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
bcx.pointercast(val.value, ty.ptr_to()) bcx.pointercast(val.value, ty.ptr_to())
} }
layout::RawNullablePointer { nndiscr, .. } | layout::RawNullablePointer { nndiscr, .. } |
layout::StructWrappedNullablePointer { nndiscr, .. } if discr.0 != nndiscr => { layout::StructWrappedNullablePointer { nndiscr, .. } if discr.0 != nndiscr => {
let nullfields = compute_fields(bcx.ccx(), t, (1-nndiscr) as usize, false); let nullfields = compute_fields(bcx.ccx, t, (1-nndiscr) as usize, false);
// The unit-like case might have a nonzero number of unit-like fields. // The unit-like case might have a nonzero number of unit-like fields.
// (e.d., Result of Either with (), as one side.) // (e.d., Result of Either with (), as one side.)
let ty = type_of::type_of(bcx.ccx(), nullfields[ix]); let ty = type_of::type_of(bcx.ccx, nullfields[ix]);
assert_eq!(machine::llsize_of_alloc(bcx.ccx(), ty), 0); assert_eq!(machine::llsize_of_alloc(bcx.ccx, ty), 0);
// The contents of memory at this pointer can't matter, but use
// the value that's "reasonable" in case of pointer comparison.
if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
bcx.pointercast(val.value, ty.ptr_to()) bcx.pointercast(val.value, ty.ptr_to())
} }
layout::RawNullablePointer { nndiscr, .. } => { layout::RawNullablePointer { nndiscr, .. } => {
let nnty = compute_fields(bcx.ccx(), t, nndiscr as usize, false)[0]; let nnty = compute_fields(bcx.ccx, t, nndiscr as usize, false)[0];
assert_eq!(ix, 0); assert_eq!(ix, 0);
assert_eq!(discr.0, nndiscr); assert_eq!(discr.0, nndiscr);
let ty = type_of::type_of(bcx.ccx(), nnty); let ty = type_of::type_of(bcx.ccx, nnty);
if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
bcx.pointercast(val.value, ty.ptr_to()) bcx.pointercast(val.value, ty.ptr_to())
} }
layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
assert_eq!(discr.0, nndiscr); assert_eq!(discr.0, nndiscr);
struct_field_ptr(bcx, &nonnull, struct_field_ptr(bcx, &nonnull,
&compute_fields(bcx.ccx(), t, discr.0 as usize, false), &compute_fields(bcx.ccx, t, discr.0 as usize, false),
val, ix, false) val, ix, false)
} }
_ => bug!("element access in type without elements: {} represented as {:#?}", t, l) _ => bug!("element access in type without elements: {} represented as {:#?}", t, l)
} }
} }
fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, fn struct_field_ptr<'a, 'tcx>(
st: &layout::Struct, fields: &Vec<Ty<'tcx>>, val: MaybeSizedValue, bcx: &BlockAndBuilder<'a, 'tcx>,
ix: usize, needs_cast: bool) -> ValueRef { st: &layout::Struct,
fields: &Vec<Ty<'tcx>>,
val: MaybeSizedValue,
ix: usize,
needs_cast: bool
) -> ValueRef {
let fty = fields[ix]; let fty = fields[ix];
let ccx = bcx.ccx(); let ccx = bcx.ccx;
let ll_fty = type_of::in_memory_type_of(bcx.ccx(), fty);
if bcx.is_unreachable() {
return C_undef(ll_fty.ptr_to());
}
let ptr_val = if needs_cast { let ptr_val = if needs_cast {
let fields = st.field_index_by_increasing_offset().map(|i| { let fields = st.field_index_by_increasing_offset().map(|i| {
@ -587,7 +584,8 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
// * First field - Always aligned properly // * First field - Always aligned properly
// * Packed struct - There is no alignment padding // * Packed struct - There is no alignment padding
// * Field is sized - pointer is properly aligned already // * Field is sized - pointer is properly aligned already
if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed || type_is_sized(bcx.tcx(), fty) { if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed ||
bcx.ccx.shared().type_is_sized(fty) {
return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize); return bcx.struct_gep(ptr_val, st.memory_index[ix] as usize);
} }
@ -607,8 +605,6 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
return bcx.struct_gep(ptr_val, ix); return bcx.struct_gep(ptr_val, ix);
} }
let dbloc = DebugLoc::None;
// We need to get the pointer manually now. // We need to get the pointer manually now.
// We do this by casting to a *i8, then offsetting it by the appropriate amount. // We do this by casting to a *i8, then offsetting it by the appropriate amount.
// We do this instead of, say, simply adjusting the pointer from the result of a GEP // We do this instead of, say, simply adjusting the pointer from the result of a GEP
@ -628,7 +624,7 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
let offset = st.offsets[ix].bytes(); let offset = st.offsets[ix].bytes();
let unaligned_offset = C_uint(bcx.ccx(), offset); let unaligned_offset = C_uint(bcx.ccx, offset);
// Get the alignment of the field // Get the alignment of the field
let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta); let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta);
@ -639,19 +635,18 @@ fn struct_field_ptr<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
// (unaligned offset + (align - 1)) & -align // (unaligned offset + (align - 1)) & -align
// Calculate offset // Calculate offset
dbloc.apply(bcx.fcx()); let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx, 1u64));
let align_sub_1 = bcx.sub(align, C_uint(bcx.ccx(), 1u64));
let offset = bcx.and(bcx.add(unaligned_offset, align_sub_1), let offset = bcx.and(bcx.add(unaligned_offset, align_sub_1),
bcx.neg(align)); bcx.neg(align));
debug!("struct_field_ptr: DST field offset: {:?}", Value(offset)); debug!("struct_field_ptr: DST field offset: {:?}", Value(offset));
// Cast and adjust pointer // Cast and adjust pointer
let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx())); let byte_ptr = bcx.pointercast(ptr_val, Type::i8p(bcx.ccx));
let byte_ptr = bcx.gep(byte_ptr, &[offset]); let byte_ptr = bcx.gep(byte_ptr, &[offset]);
// Finally, cast back to the type expected // Finally, cast back to the type expected
let ll_fty = type_of::in_memory_type_of(bcx.ccx(), fty); let ll_fty = type_of::in_memory_type_of(bcx.ccx, fty);
debug!("struct_field_ptr: Field type is {:?}", ll_fty); debug!("struct_field_ptr: Field type is {:?}", ll_fty);
bcx.pointercast(byte_ptr, ll_fty.ptr_to()) bcx.pointercast(byte_ptr, ll_fty.ptr_to())
} }

View file

@ -12,7 +12,6 @@
use llvm::{self, ValueRef}; use llvm::{self, ValueRef};
use base; use base;
use build::*;
use common::*; use common::*;
use type_of; use type_of;
use type_::Type; use type_::Type;
@ -25,10 +24,12 @@ use syntax::ast::AsmDialect;
use libc::{c_uint, c_char}; use libc::{c_uint, c_char};
// Take an inline assembly expression and splat it out via LLVM // Take an inline assembly expression and splat it out via LLVM
pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn trans_inline_asm<'a, 'tcx>(
ia: &hir::InlineAsm, bcx: &BlockAndBuilder<'a, 'tcx>,
outputs: Vec<(ValueRef, Ty<'tcx>)>, ia: &hir::InlineAsm,
mut inputs: Vec<ValueRef>) { outputs: Vec<(ValueRef, Ty<'tcx>)>,
mut inputs: Vec<ValueRef>
) {
let mut ext_constraints = vec![]; let mut ext_constraints = vec![];
let mut output_types = vec![]; let mut output_types = vec![];
@ -47,7 +48,7 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
if out.is_indirect { if out.is_indirect {
indirect_outputs.push(val.unwrap()); indirect_outputs.push(val.unwrap());
} else { } else {
output_types.push(type_of::type_of(bcx.ccx(), ty)); output_types.push(type_of::type_of(bcx.ccx, ty));
} }
} }
if !indirect_outputs.is_empty() { if !indirect_outputs.is_empty() {
@ -78,9 +79,9 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// Depending on how many outputs we have, the return type is different // Depending on how many outputs we have, the return type is different
let num_outputs = output_types.len(); let num_outputs = output_types.len();
let output_type = match num_outputs { let output_type = match num_outputs {
0 => Type::void(bcx.ccx()), 0 => Type::void(bcx.ccx),
1 => output_types[0], 1 => output_types[0],
_ => Type::struct_(bcx.ccx(), &output_types[..], false) _ => Type::struct_(bcx.ccx, &output_types[..], false)
}; };
let dialect = match ia.dialect { let dialect = match ia.dialect {
@ -90,32 +91,33 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap(); let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap();
let constraint_cstr = CString::new(all_constraints).unwrap(); let constraint_cstr = CString::new(all_constraints).unwrap();
let r = InlineAsmCall(bcx, let r = bcx.inline_asm_call(
asm.as_ptr(), asm.as_ptr(),
constraint_cstr.as_ptr(), constraint_cstr.as_ptr(),
&inputs, &inputs,
output_type, output_type,
ia.volatile, ia.volatile,
ia.alignstack, ia.alignstack,
dialect); dialect
);
// Again, based on how many outputs we have // Again, based on how many outputs we have
let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect);
for (i, (_, &(val, _))) in outputs.enumerate() { for (i, (_, &(val, _))) in outputs.enumerate() {
let v = if num_outputs == 1 { r } else { ExtractValue(bcx, r, i) }; let v = if num_outputs == 1 { r } else { bcx.extract_value(r, i) };
Store(bcx, v, val); bcx.store(v, val);
} }
// Store expn_id in a metadata node so we can map LLVM errors // Store expn_id in a metadata node so we can map LLVM errors
// back to source locations. See #17552. // back to source locations. See #17552.
unsafe { unsafe {
let key = "srcloc"; let key = "srcloc";
let kind = llvm::LLVMGetMDKindIDInContext(bcx.ccx().llcx(), let kind = llvm::LLVMGetMDKindIDInContext(bcx.ccx.llcx(),
key.as_ptr() as *const c_char, key.len() as c_uint); key.as_ptr() as *const c_char, key.len() as c_uint);
let val: llvm::ValueRef = C_i32(bcx.ccx(), ia.expn_id.into_u32() as i32); let val: llvm::ValueRef = C_i32(bcx.ccx, ia.expn_id.into_u32() as i32);
llvm::LLVMSetMetadata(r, kind, llvm::LLVMSetMetadata(r, kind,
llvm::LLVMMDNodeInContext(bcx.ccx().llcx(), &val, 1)); llvm::LLVMMDNodeInContext(bcx.ccx.llcx(), &val, 1));
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,58 +0,0 @@
// Copyright 2013 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.
use llvm;
use llvm::BasicBlockRef;
use value::{Users, Value};
use std::iter::{Filter, Map};
#[derive(Copy, Clone)]
pub struct BasicBlock(pub BasicBlockRef);
pub type Preds = Map<Filter<Users, fn(&Value) -> bool>, fn(Value) -> BasicBlock>;
/// Wrapper for LLVM BasicBlockRef
impl BasicBlock {
pub fn get(&self) -> BasicBlockRef {
let BasicBlock(v) = *self; v
}
pub fn as_value(self) -> Value {
unsafe {
Value(llvm::LLVMBasicBlockAsValue(self.get()))
}
}
pub fn pred_iter(self) -> Preds {
fn is_a_terminator_inst(user: &Value) -> bool { user.is_a_terminator_inst() }
let is_a_terminator_inst: fn(&Value) -> bool = is_a_terminator_inst;
fn get_parent(user: Value) -> BasicBlock { user.get_parent().unwrap() }
let get_parent: fn(Value) -> BasicBlock = get_parent;
self.as_value().user_iter()
.filter(is_a_terminator_inst)
.map(get_parent)
}
pub fn get_single_predecessor(self) -> Option<BasicBlock> {
let mut iter = self.pred_iter();
match (iter.next(), iter.next()) {
(Some(first), None) => Some(first),
_ => None
}
}
pub fn delete(self) {
unsafe {
llvm::LLVMDeleteBasicBlock(self.0);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -14,12 +14,10 @@ use llvm;
use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect};
use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef}; use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef};
use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef};
use base;
use common::*; use common::*;
use machine::llalign_of_pref; use machine::llalign_of_pref;
use type_::Type; use type_::Type;
use value::Value; use value::Value;
use util::nodemap::FxHashMap;
use libc::{c_uint, c_char}; use libc::{c_uint, c_char};
use std::borrow::Cow; use std::borrow::Cow;
@ -32,65 +30,40 @@ pub struct Builder<'a, 'tcx: 'a> {
pub ccx: &'a CrateContext<'a, 'tcx>, pub ccx: &'a CrateContext<'a, 'tcx>,
} }
impl<'a, 'tcx> Drop for Builder<'a, 'tcx> {
fn drop(&mut self) {
unsafe {
llvm::LLVMDisposeBuilder(self.llbuilder);
}
}
}
// This is a really awful way to get a zero-length c-string, but better (and a // This is a really awful way to get a zero-length c-string, but better (and a
// lot more efficient) than doing str::as_c_str("", ...) every time. // lot more efficient) than doing str::as_c_str("", ...) every time.
pub fn noname() -> *const c_char { fn noname() -> *const c_char {
static CNULL: c_char = 0; static CNULL: c_char = 0;
&CNULL &CNULL
} }
impl<'a, 'tcx> Builder<'a, 'tcx> { impl<'a, 'tcx> Builder<'a, 'tcx> {
pub fn new(ccx: &'a CrateContext<'a, 'tcx>) -> Builder<'a, 'tcx> { pub fn with_ccx(ccx: &'a CrateContext<'a, 'tcx>) -> Self {
// Create a fresh builder from the crate context.
let llbuilder = unsafe {
llvm::LLVMCreateBuilderInContext(ccx.llcx())
};
Builder { Builder {
llbuilder: ccx.raw_builder(), llbuilder: llbuilder,
ccx: ccx, ccx: ccx,
} }
} }
pub fn count_insn(&self, category: &str) { fn count_insn(&self, category: &str) {
if self.ccx.sess().trans_stats() { if self.ccx.sess().trans_stats() {
self.ccx.stats().n_llvm_insns.set(self.ccx self.ccx.stats().n_llvm_insns.set(self.ccx.stats().n_llvm_insns.get() + 1);
.stats()
.n_llvm_insns
.get() + 1);
} }
self.ccx.count_llvm_insn();
if self.ccx.sess().count_llvm_insns() { if self.ccx.sess().count_llvm_insns() {
base::with_insn_ctxt(|v| { let mut h = self.ccx.stats().llvm_insns.borrow_mut();
let mut h = self.ccx.stats().llvm_insns.borrow_mut(); *h.entry(category.to_string()).or_insert(0) += 1;
// Build version of path with cycles removed.
// Pass 1: scan table mapping str -> rightmost pos.
let mut mm = FxHashMap();
let len = v.len();
let mut i = 0;
while i < len {
mm.insert(v[i], i);
i += 1;
}
// Pass 2: concat strings for each elt, skipping
// forwards over any cycles by advancing to rightmost
// occurrence of each element in path.
let mut s = String::from(".");
i = 0;
while i < len {
i = mm[v[i]];
s.push('/');
s.push_str(v[i]);
i += 1;
}
s.push('/');
s.push_str(category);
let n = match h.get(&s) {
Some(&n) => n,
_ => 0
};
h.insert(s, n+1);
})
} }
} }
@ -462,7 +435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { pub fn dynamic_alloca(&self, ty: Type, name: &str) -> ValueRef {
self.count_insn("alloca"); self.count_insn("alloca");
unsafe { unsafe {
if name.is_empty() { if name.is_empty() {
@ -1103,6 +1076,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
pub fn add_case(&self, s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) {
unsafe {
if llvm::LLVMIsUndef(s) == llvm::True { return; }
llvm::LLVMAddCase(s, on_val, dest)
}
}
pub fn add_incoming_to_phi(&self, phi: ValueRef, val: ValueRef, bb: BasicBlockRef) {
unsafe {
if llvm::LLVMIsUndef(phi) == llvm::True { return; }
llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint);
}
}
/// Returns the ptr value that should be used for storing `val`. /// Returns the ptr value that should be used for storing `val`.
fn check_store<'b>(&self, fn check_store<'b>(&self,
val: ValueRef, val: ValueRef,

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(non_upper_case_globals)]
use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
use abi::{self, align_up_to, FnType, ArgType}; use abi::{self, align_up_to, FnType, ArgType};
use context::CrateContext; use context::CrateContext;

View file

@ -16,7 +16,6 @@
pub use self::CalleeData::*; pub use self::CalleeData::*;
use arena::TypedArena;
use llvm::{self, ValueRef, get_params}; use llvm::{self, ValueRef, get_params};
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
@ -25,10 +24,10 @@ use abi::{Abi, FnType};
use attributes; use attributes;
use base; use base;
use base::*; use base::*;
use build::*; use common::{
use common::{self, Block, Result, CrateContext, FunctionContext, SharedCrateContext}; self, CrateContext, FunctionContext, SharedCrateContext
};
use consts; use consts;
use debuginfo::DebugLoc;
use declare; use declare;
use value::Value; use value::Value;
use meth; use meth;
@ -71,25 +70,8 @@ impl<'tcx> Callee<'tcx> {
} }
} }
/// Trait or impl method call.
pub fn method_call<'blk>(bcx: Block<'blk, 'tcx>,
method_call: ty::MethodCall)
-> Callee<'tcx> {
let method = bcx.tcx().tables().method_map[&method_call];
Callee::method(bcx, method)
}
/// Trait or impl method.
pub fn method<'blk>(bcx: Block<'blk, 'tcx>,
method: ty::MethodCallee<'tcx>) -> Callee<'tcx> {
let substs = bcx.fcx.monomorphize(&method.substs);
Callee::def(bcx.ccx(), method.def_id, substs)
}
/// Function or method definition. /// Function or method definition.
pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, pub fn def<'a>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>)
def_id: DefId,
substs: &'tcx Substs<'tcx>)
-> Callee<'tcx> { -> Callee<'tcx> {
let tcx = ccx.tcx(); let tcx = ccx.tcx();
@ -196,25 +178,6 @@ impl<'tcx> Callee<'tcx> {
fn_ty fn_ty
} }
/// This behemoth of a function translates function calls. Unfortunately, in
/// order to generate more efficient LLVM output at -O0, it has quite a complex
/// signature (refactoring this into two functions seems like a good idea).
///
/// In particular, for lang items, it is invoked with a dest of None, and in
/// that case the return value contains the result of the fn. The lang item must
/// not return a structural type or else all heck breaks loose.
///
/// For non-lang items, `dest` is always Some, and hence the result is written
/// into memory somewhere. Nonetheless we return the actual return value of the
/// function.
pub fn call<'a, 'blk>(self, bcx: Block<'blk, 'tcx>,
debug_loc: DebugLoc,
args: &[ValueRef],
dest: Option<ValueRef>)
-> Result<'blk, 'tcx> {
trans_call_inner(bcx, debug_loc, self, args, dest)
}
/// Turn the callee into a function pointer. /// Turn the callee into a function pointer.
pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef { pub fn reify<'a>(self, ccx: &CrateContext<'a, 'tcx>) -> ValueRef {
match self.data { match self.data {
@ -267,8 +230,6 @@ fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
// then adapt the self type // then adapt the self type
let llfn_closure_kind = ccx.tcx().closure_kind(def_id); let llfn_closure_kind = ccx.tcx().closure_kind(def_id);
let _icx = push_ctxt("trans_closure_adapter_shim");
debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \ debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
trait_closure_kind={:?}, llfn={:?})", trait_closure_kind={:?}, llfn={:?})",
llfn_closure_kind, trait_closure_kind, Value(llfn)); llfn_closure_kind, trait_closure_kind, Value(llfn));
@ -367,23 +328,28 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty); let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
attributes::set_frame_pointer_elimination(ccx, lloncefn); attributes::set_frame_pointer_elimination(ccx, lloncefn);
let (block_arena, fcx): (TypedArena<_>, FunctionContext); let orig_fn_ty = fn_ty;
block_arena = TypedArena::new(); let fcx = FunctionContext::new(ccx, lloncefn);
fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena); let mut bcx = fcx.get_entry_block();
let mut bcx = fcx.init(false);
let callee = Callee {
data: Fn(llreffn),
ty: llref_fn_ty
};
// the first argument (`self`) will be the (by value) closure env. // the first argument (`self`) will be the (by value) closure env.
let mut llargs = get_params(fcx.llfn); let mut llargs = get_params(fcx.llfn);
let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize; let fn_ret = callee.ty.fn_ret();
let env_arg = &fcx.fn_ty.args[0]; let fn_ty = callee.direct_fn_type(bcx.ccx, &[]);
let self_idx = fn_ty.ret.is_indirect() as usize;
let env_arg = &orig_fn_ty.args[0];
let llenv = if env_arg.is_indirect() { let llenv = if env_arg.is_indirect() {
llargs[self_idx] llargs[self_idx]
} else { } else {
let scratch = alloc_ty(bcx, closure_ty, "self"); let scratch = alloc_ty(&bcx, closure_ty, "self");
let mut llarg_idx = self_idx; let mut llarg_idx = self_idx;
env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, scratch); env_arg.store_fn_arg(&bcx, &mut llarg_idx, scratch);
scratch scratch
}; };
@ -391,33 +357,37 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
// Adjust llargs such that llargs[self_idx..] has the call arguments. // Adjust llargs such that llargs[self_idx..] has the call arguments.
// For zero-sized closures that means sneaking in a new argument. // For zero-sized closures that means sneaking in a new argument.
if env_arg.is_ignore() { if env_arg.is_ignore() {
if self_idx > 0 { llargs.insert(self_idx, llenv);
self_idx -= 1;
llargs[self_idx] = llenv;
} else {
llargs.insert(0, llenv);
}
} else { } else {
llargs[self_idx] = llenv; llargs[self_idx] = llenv;
} }
let dest = fcx.llretslotptr.get();
let callee = Callee {
data: Fn(llreffn),
ty: llref_fn_ty
};
// Call the by-ref closure body with `self` in a cleanup scope, // Call the by-ref closure body with `self` in a cleanup scope,
// to drop `self` when the body returns, or in case it unwinds. // to drop `self` when the body returns, or in case it unwinds.
let self_scope = fcx.push_custom_cleanup_scope(); let self_scope = fcx.schedule_drop_mem(llenv, closure_ty);
fcx.schedule_drop_mem(self_scope, llenv, closure_ty);
bcx = callee.call(bcx, DebugLoc::None, &llargs[self_idx..], dest).bcx; let llfn = callee.reify(bcx.ccx);
let llret;
if let Some(landing_pad) = self_scope.landing_pad {
let normal_bcx = bcx.fcx().build_new_block("normal-return");
llret = bcx.invoke(llfn, &llargs[..], normal_bcx.llbb(), landing_pad, None);
bcx = normal_bcx;
} else {
llret = bcx.call(llfn, &llargs[..], None);
}
fn_ty.apply_attrs_callsite(llret);
fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope); if fn_ret.0.is_never() {
bcx.unreachable();
} else {
self_scope.trans(&bcx);
fcx.finish(bcx, DebugLoc::None); if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() {
bcx.ret_void();
} else {
bcx.ret(llret);
}
}
ccx.instances().borrow_mut().insert(method_instance, lloncefn); ccx.instances().borrow_mut().insert(method_instance, lloncefn);
@ -443,7 +413,6 @@ fn trans_fn_pointer_shim<'a, 'tcx>(
bare_fn_ty: Ty<'tcx>) bare_fn_ty: Ty<'tcx>)
-> ValueRef -> ValueRef
{ {
let _icx = push_ctxt("trans_fn_pointer_shim");
let tcx = ccx.tcx(); let tcx = ccx.tcx();
// Normalize the type for better caching. // Normalize the type for better caching.
@ -519,32 +488,39 @@ fn trans_fn_pointer_shim<'a, 'tcx>(
let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty); let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
attributes::set_frame_pointer_elimination(ccx, llfn); attributes::set_frame_pointer_elimination(ccx, llfn);
// //
let (block_arena, fcx): (TypedArena<_>, FunctionContext); let fcx = FunctionContext::new(ccx, llfn);
block_arena = TypedArena::new(); let bcx = fcx.get_entry_block();
fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena);
let mut bcx = fcx.init(false);
let llargs = get_params(fcx.llfn); let mut llargs = get_params(fcx.llfn);
let self_idx = fcx.fn_ty.ret.is_indirect() as usize; let self_arg = llargs.remove(fn_ty.ret.is_indirect() as usize);
let llfnpointer = llfnpointer.unwrap_or_else(|| { let llfnpointer = llfnpointer.unwrap_or_else(|| {
// the first argument (`self`) will be ptr to the fn pointer // the first argument (`self`) will be ptr to the fn pointer
if is_by_ref { if is_by_ref {
Load(bcx, llargs[self_idx]) bcx.load(self_arg)
} else { } else {
llargs[self_idx] self_arg
} }
}); });
let dest = fcx.llretslotptr.get();
let callee = Callee { let callee = Callee {
data: Fn(llfnpointer), data: Fn(llfnpointer),
ty: bare_fn_ty ty: bare_fn_ty
}; };
bcx = callee.call(bcx, DebugLoc::None, &llargs[(self_idx + 1)..], dest).bcx; let fn_ret = callee.ty.fn_ret();
let fn_ty = callee.direct_fn_type(ccx, &[]);
let llret = bcx.call(llfnpointer, &llargs, None);
fn_ty.apply_attrs_callsite(llret);
fcx.finish(bcx, DebugLoc::None); if fn_ret.0.is_never() {
bcx.unreachable();
} else {
if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() {
bcx.ret_void();
} else {
bcx.ret(llret);
}
}
ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn); ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn);
@ -649,87 +625,3 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
(llfn, fn_ty) (llfn, fn_ty)
} }
// ______________________________________________________________________
// Translating calls
fn trans_call_inner<'a, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
debug_loc: DebugLoc,
callee: Callee<'tcx>,
args: &[ValueRef],
opt_llretslot: Option<ValueRef>)
-> Result<'blk, 'tcx> {
// Introduce a temporary cleanup scope that will contain cleanups
// for the arguments while they are being evaluated. The purpose
// this cleanup is to ensure that, should a panic occur while
// evaluating argument N, the values for arguments 0...N-1 are all
// cleaned up. If no panic occurs, the values are handed off to
// the callee, and hence none of the cleanups in this temporary
// scope will ever execute.
let fcx = bcx.fcx;
let ccx = fcx.ccx;
let fn_ret = callee.ty.fn_ret();
let fn_ty = callee.direct_fn_type(ccx, &[]);
let mut callee = match callee.data {
NamedTupleConstructor(_) | Intrinsic => {
bug!("{:?} calls should not go through Callee::call", callee);
}
f => f
};
// If there no destination, return must be direct, with no cast.
if opt_llretslot.is_none() {
assert!(!fn_ty.ret.is_indirect() && fn_ty.ret.cast.is_none());
}
let mut llargs = Vec::new();
if fn_ty.ret.is_indirect() {
let mut llretslot = opt_llretslot.unwrap();
if let Some(ty) = fn_ty.ret.cast {
llretslot = PointerCast(bcx, llretslot, ty.ptr_to());
}
llargs.push(llretslot);
}
match callee {
Virtual(idx) => {
llargs.push(args[0]);
let fn_ptr = meth::get_virtual_method(bcx, args[1], idx);
let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
callee = Fn(PointerCast(bcx, fn_ptr, llty));
llargs.extend_from_slice(&args[2..]);
}
_ => llargs.extend_from_slice(args)
}
let llfn = match callee {
Fn(f) => f,
_ => bug!("expected fn pointer callee, found {:?}", callee)
};
let (llret, bcx) = base::invoke(bcx, llfn, &llargs, debug_loc);
if !bcx.unreachable.get() {
fn_ty.apply_attrs_callsite(llret);
// If the function we just called does not use an outpointer,
// store the result into the rust outpointer. Cast the outpointer
// type to match because some ABIs will use a different type than
// the Rust type. e.g., a {u32,u32} struct could be returned as
// u64.
if !fn_ty.ret.is_indirect() {
if let Some(llretslot) = opt_llretslot {
fn_ty.ret.store(&bcx.build(), llret, llretslot);
}
}
}
if fn_ret.0.is_never() {
Unreachable(bcx);
}
Result::new(bcx, llret)
}

View file

@ -11,216 +11,100 @@
//! ## The Cleanup module //! ## The Cleanup module
//! //!
//! The cleanup module tracks what values need to be cleaned up as scopes //! The cleanup module tracks what values need to be cleaned up as scopes
//! are exited, either via panic or just normal control flow. The basic //! are exited, either via panic or just normal control flow.
//! idea is that the function context maintains a stack of cleanup scopes
//! that are pushed/popped as we traverse the AST tree. There is typically
//! at least one cleanup scope per AST node; some AST nodes may introduce
//! additional temporary scopes.
//! //!
//! Cleanup items can be scheduled into any of the scopes on the stack. //! Cleanup items can be scheduled into any of the scopes on the stack.
//! Typically, when a scope is popped, we will also generate the code for //! Typically, when a scope is finished, we generate the cleanup code. This
//! each of its cleanups at that time. This corresponds to a normal exit //! corresponds to a normal exit from a block (for example, an expression
//! from a block (for example, an expression completing evaluation //! completing evaluation successfully without panic).
//! successfully without panic). However, it is also possible to pop a
//! block *without* executing its cleanups; this is typically used to
//! guard intermediate values that must be cleaned up on panic, but not
//! if everything goes right. See the section on custom scopes below for
//! more details.
//!
//! Cleanup scopes come in three kinds:
//!
//! - **AST scopes:** each AST node in a function body has a corresponding
//! AST scope. We push the AST scope when we start generate code for an AST
//! node and pop it once the AST node has been fully generated.
//! - **Loop scopes:** loops have an additional cleanup scope. Cleanups are
//! never scheduled into loop scopes; instead, they are used to record the
//! basic blocks that we should branch to when a `continue` or `break` statement
//! is encountered.
//! - **Custom scopes:** custom scopes are typically used to ensure cleanup
//! of intermediate values.
//!
//! ### When to schedule cleanup
//!
//! Although the cleanup system is intended to *feel* fairly declarative,
//! it's still important to time calls to `schedule_clean()` correctly.
//! Basically, you should not schedule cleanup for memory until it has
//! been initialized, because if an unwind should occur before the memory
//! is fully initialized, then the cleanup will run and try to free or
//! drop uninitialized memory. If the initialization itself produces
//! byproducts that need to be freed, then you should use temporary custom
//! scopes to ensure that those byproducts will get freed on unwind. For
//! example, an expression like `box foo()` will first allocate a box in the
//! heap and then call `foo()` -- if `foo()` should panic, this box needs
//! to be *shallowly* freed.
//!
//! ### Long-distance jumps
//!
//! In addition to popping a scope, which corresponds to normal control
//! flow exiting the scope, we may also *jump out* of a scope into some
//! earlier scope on the stack. This can occur in response to a `return`,
//! `break`, or `continue` statement, but also in response to panic. In
//! any of these cases, we will generate a series of cleanup blocks for
//! each of the scopes that is exited. So, if the stack contains scopes A
//! ... Z, and we break out of a loop whose corresponding cleanup scope is
//! X, we would generate cleanup blocks for the cleanups in X, Y, and Z.
//! After cleanup is done we would branch to the exit point for scope X.
//! But if panic should occur, we would generate cleanups for all the
//! scopes from A to Z and then resume the unwind process afterwards.
//!
//! To avoid generating tons of code, we cache the cleanup blocks that we
//! create for breaks, returns, unwinds, and other jumps. Whenever a new
//! cleanup is scheduled, though, we must clear these cached blocks. A
//! possible improvement would be to keep the cached blocks but simply
//! generate a new block which performs the additional cleanup and then
//! branches to the existing cached blocks.
//!
//! ### AST and loop cleanup scopes
//!
//! AST cleanup scopes are pushed when we begin and end processing an AST
//! node. They are used to house cleanups related to rvalue temporary that
//! get referenced (e.g., due to an expression like `&Foo()`). Whenever an
//! AST scope is popped, we always trans all the cleanups, adding the cleanup
//! code after the postdominator of the AST node.
//!
//! AST nodes that represent breakable loops also push a loop scope; the
//! loop scope never has any actual cleanups, it's just used to point to
//! the basic blocks where control should flow after a "continue" or
//! "break" statement. Popping a loop scope never generates code.
//!
//! ### Custom cleanup scopes
//!
//! Custom cleanup scopes are used for a variety of purposes. The most
//! common though is to handle temporary byproducts, where cleanup only
//! needs to occur on panic. The general strategy is to push a custom
//! cleanup scope, schedule *shallow* cleanups into the custom scope, and
//! then pop the custom scope (without transing the cleanups) when
//! execution succeeds normally. This way the cleanups are only trans'd on
//! unwind, and only up until the point where execution succeeded, at
//! which time the complete value should be stored in an lvalue or some
//! other place where normal cleanup applies.
//!
//! To spell it out, here is an example. Imagine an expression `box expr`.
//! We would basically:
//!
//! 1. Push a custom cleanup scope C.
//! 2. Allocate the box.
//! 3. Schedule a shallow free in the scope C.
//! 4. Trans `expr` into the box.
//! 5. Pop the scope C.
//! 6. Return the box as an rvalue.
//!
//! This way, if a panic occurs while transing `expr`, the custom
//! cleanup scope C is pushed and hence the box will be freed. The trans
//! code for `expr` itself is responsible for freeing any other byproducts
//! that may be in play.
pub use self::EarlyExitLabel::*;
use llvm::{BasicBlockRef, ValueRef}; use llvm::{BasicBlockRef, ValueRef};
use base; use base;
use build; use common::{BlockAndBuilder, FunctionContext, Funclet};
use common;
use common::{Block, FunctionContext, LandingPad};
use debuginfo::{DebugLoc};
use glue; use glue;
use type_::Type; use type_::Type;
use value::Value; use value::Value;
use rustc::ty::Ty; use rustc::ty::Ty;
pub struct CleanupScope<'tcx> { pub struct CleanupScope<'tcx> {
// Cleanups to run upon scope exit. // Cleanup to run upon scope exit.
cleanups: Vec<DropValue<'tcx>>, cleanup: Option<DropValue<'tcx>>,
// The debug location any drop calls generated for this scope will be // Computed on creation if compiling with landing pads (!sess.no_landing_pads)
// associated with. pub landing_pad: Option<BasicBlockRef>,
debug_loc: DebugLoc,
cached_early_exits: Vec<CachedEarlyExit>,
cached_landing_pad: Option<BasicBlockRef>,
}
#[derive(Copy, Clone, Debug)]
pub struct CustomScopeIndex {
index: usize
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum EarlyExitLabel {
UnwindExit(UnwindKind),
}
#[derive(Copy, Clone, Debug)]
pub enum UnwindKind {
LandingPad,
CleanupPad(ValueRef),
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct CachedEarlyExit { pub struct DropValue<'tcx> {
label: EarlyExitLabel, val: ValueRef,
cleanup_block: BasicBlockRef, ty: Ty<'tcx>,
last_cleanup: usize, skip_dtor: bool,
} }
impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> { impl<'tcx> DropValue<'tcx> {
pub fn push_custom_cleanup_scope(&self) -> CustomScopeIndex { fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &BlockAndBuilder<'a, 'tcx>) {
let index = self.scopes_len(); glue::call_drop_glue(bcx, self.val, self.ty, self.skip_dtor, funclet)
debug!("push_custom_cleanup_scope(): {}", index);
// Just copy the debuginfo source location from the enclosing scope
let debug_loc = self.scopes
.borrow()
.last()
.map(|opt_scope| opt_scope.debug_loc)
.unwrap_or(DebugLoc::None);
self.push_scope(CleanupScope::new(debug_loc));
CustomScopeIndex { index: index }
} }
/// Removes the top cleanup scope from the stack without executing its cleanups. The top /// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary
/// cleanup scope must be the temporary scope `custom_scope`. /// for an unwind and then `resume` to continue error propagation:
pub fn pop_custom_cleanup_scope(&self, ///
custom_scope: CustomScopeIndex) { /// landing_pad -> ... cleanups ... -> [resume]
debug!("pop_custom_cleanup_scope({})", custom_scope.index); ///
assert!(self.is_valid_to_pop_custom_scope(custom_scope)); /// This should only be called once per function, as it creates an alloca for the landingpad.
let _ = self.pop_scope(); fn get_landing_pad<'a>(&self, fcx: &FunctionContext<'a, 'tcx>) -> BasicBlockRef {
debug!("get_landing_pad");
let bcx = fcx.build_new_block("cleanup_unwind");
let llpersonality = bcx.ccx.eh_personality();
bcx.set_personality_fn(llpersonality);
if base::wants_msvc_seh(fcx.ccx.sess()) {
let pad = bcx.cleanup_pad(None, &[]);
let funclet = Some(Funclet::new(pad));
self.trans(funclet.as_ref(), &bcx);
bcx.cleanup_ret(pad, None);
} else {
// The landing pad return type (the type being propagated). Not sure
// what this represents but it's determined by the personality
// function and this is what the EH proposal example uses.
let llretty = Type::struct_(fcx.ccx, &[Type::i8p(fcx.ccx), Type::i32(fcx.ccx)], false);
// The only landing pad clause will be 'cleanup'
let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.fcx().llfn);
// The landing pad block is a cleanup
bcx.set_cleanup(llretval);
// Insert cleanup instructions into the cleanup block
self.trans(None, &bcx);
if !bcx.sess().target.target.options.custom_unwind_resume {
bcx.resume(llretval);
} else {
let exc_ptr = bcx.extract_value(llretval, 0);
bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], None);
bcx.unreachable();
}
}
bcx.llbb()
} }
}
/// Removes the top cleanup scope from the stack, which must be a temporary scope, and impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
/// generates the code to do its cleanups for normal exit. /// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty`
pub fn pop_and_trans_custom_cleanup_scope(&self, pub fn schedule_drop_mem(&self, val: ValueRef, ty: Ty<'tcx>) -> CleanupScope<'tcx> {
bcx: Block<'blk, 'tcx>, if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); }
custom_scope: CustomScopeIndex)
-> Block<'blk, 'tcx> {
debug!("pop_and_trans_custom_cleanup_scope({:?})", custom_scope);
assert!(self.is_valid_to_pop_custom_scope(custom_scope));
let scope = self.pop_scope();
self.trans_scope_cleanups(bcx, &scope)
}
/// Schedules a (deep) drop of `val`, which is a pointer to an instance of
/// `ty`
pub fn schedule_drop_mem(&self,
cleanup_scope: CustomScopeIndex,
val: ValueRef,
ty: Ty<'tcx>) {
if !self.type_needs_drop(ty) { return; }
let drop = DropValue { let drop = DropValue {
is_immediate: false,
val: val, val: val,
ty: ty, ty: ty,
skip_dtor: false, skip_dtor: false,
}; };
debug!("schedule_drop_mem({:?}, val={:?}, ty={:?}) skip_dtor={}", debug!("schedule_drop_mem(val={:?}, ty={:?}) skip_dtor={}", Value(val), ty, drop.skip_dtor);
cleanup_scope,
Value(val),
ty,
drop.skip_dtor);
self.schedule_clean(cleanup_scope, drop); CleanupScope::new(self, drop)
} }
/// Issue #23611: Schedules a (deep) drop of the contents of /// Issue #23611: Schedules a (deep) drop of the contents of
@ -228,477 +112,46 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
/// `ty`. The scheduled code handles extracting the discriminant /// `ty`. The scheduled code handles extracting the discriminant
/// and dropping the contents associated with that variant /// and dropping the contents associated with that variant
/// *without* executing any associated drop implementation. /// *without* executing any associated drop implementation.
pub fn schedule_drop_adt_contents(&self, pub fn schedule_drop_adt_contents(&self, val: ValueRef, ty: Ty<'tcx>) -> CleanupScope<'tcx> {
cleanup_scope: CustomScopeIndex,
val: ValueRef,
ty: Ty<'tcx>) {
// `if` below could be "!contents_needs_drop"; skipping drop // `if` below could be "!contents_needs_drop"; skipping drop
// is just an optimization, so sound to be conservative. // is just an optimization, so sound to be conservative.
if !self.type_needs_drop(ty) { return; } if !self.ccx.shared().type_needs_drop(ty) { return CleanupScope::noop(); }
let drop = DropValue { let drop = DropValue {
is_immediate: false,
val: val, val: val,
ty: ty, ty: ty,
skip_dtor: true, skip_dtor: true,
}; };
debug!("schedule_drop_adt_contents({:?}, val={:?}, ty={:?}) skip_dtor={}", debug!("schedule_drop_adt_contents(val={:?}, ty={:?}) skip_dtor={}",
cleanup_scope, Value(val), ty, drop.skip_dtor);
Value(val),
ty,
drop.skip_dtor);
self.schedule_clean(cleanup_scope, drop); CleanupScope::new(self, drop)
}
/// Schedules a (deep) drop of `val`, which is an instance of `ty`
pub fn schedule_drop_immediate(&self,
cleanup_scope: CustomScopeIndex,
val: ValueRef,
ty: Ty<'tcx>) {
if !self.type_needs_drop(ty) { return; }
let drop = DropValue {
is_immediate: true,
val: val,
ty: ty,
skip_dtor: false,
};
debug!("schedule_drop_immediate({:?}, val={:?}, ty={:?}) skip_dtor={}",
cleanup_scope,
Value(val),
ty,
drop.skip_dtor);
self.schedule_clean(cleanup_scope, drop);
}
/// Schedules a cleanup to occur in the top-most scope, which must be a temporary scope.
fn schedule_clean(&self, custom_scope: CustomScopeIndex, cleanup: DropValue<'tcx>) {
debug!("schedule_clean_in_custom_scope(custom_scope={})",
custom_scope.index);
assert!(self.is_valid_custom_scope(custom_scope));
let mut scopes = self.scopes.borrow_mut();
let scope = &mut (*scopes)[custom_scope.index];
scope.cleanups.push(cleanup);
scope.cached_landing_pad = None;
}
/// Returns true if there are pending cleanups that should execute on panic.
pub fn needs_invoke(&self) -> bool {
self.scopes.borrow().iter().rev().any(|s| s.needs_invoke())
}
/// Returns a basic block to branch to in the event of a panic. This block
/// will run the panic cleanups and eventually resume the exception that
/// caused the landing pad to be run.
pub fn get_landing_pad(&'blk self) -> BasicBlockRef {
let _icx = base::push_ctxt("get_landing_pad");
debug!("get_landing_pad");
let orig_scopes_len = self.scopes_len();
assert!(orig_scopes_len > 0);
// Remove any scopes that do not have cleanups on panic:
let mut popped_scopes = vec![];
while !self.top_scope(|s| s.needs_invoke()) {
debug!("top scope does not need invoke");
popped_scopes.push(self.pop_scope());
}
// Check for an existing landing pad in the new topmost scope:
let llbb = self.get_or_create_landing_pad();
// Push the scopes we removed back on:
loop {
match popped_scopes.pop() {
Some(scope) => self.push_scope(scope),
None => break
}
}
assert_eq!(self.scopes_len(), orig_scopes_len);
return llbb;
}
fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool {
self.is_valid_custom_scope(custom_scope) &&
custom_scope.index == self.scopes.borrow().len() - 1
}
fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool {
let scopes = self.scopes.borrow();
custom_scope.index < scopes.len()
}
/// Generates the cleanups for `scope` into `bcx`
fn trans_scope_cleanups(&self, // cannot borrow self, will recurse
bcx: Block<'blk, 'tcx>,
scope: &CleanupScope<'tcx>) -> Block<'blk, 'tcx> {
let mut bcx = bcx;
if !bcx.unreachable.get() {
for cleanup in scope.cleanups.iter().rev() {
bcx = cleanup.trans(bcx, scope.debug_loc);
}
}
bcx
}
fn scopes_len(&self) -> usize {
self.scopes.borrow().len()
}
fn push_scope(&self, scope: CleanupScope<'tcx>) {
self.scopes.borrow_mut().push(scope)
}
fn pop_scope(&self) -> CleanupScope<'tcx> {
debug!("popping cleanup scope {}, {} scopes remaining",
self.top_scope(|s| s.block_name("")),
self.scopes_len() - 1);
self.scopes.borrow_mut().pop().unwrap()
}
fn top_scope<R, F>(&self, f: F) -> R where F: FnOnce(&CleanupScope<'tcx>) -> R {
f(self.scopes.borrow().last().unwrap())
}
/// Used when the caller wishes to jump to an early exit, such as a return,
/// break, continue, or unwind. This function will generate all cleanups
/// between the top of the stack and the exit `label` and return a basic
/// block that the caller can branch to.
///
/// For example, if the current stack of cleanups were as follows:
///
/// AST 22
/// Custom 1
/// AST 23
/// Loop 23
/// Custom 2
/// AST 24
///
/// and the `label` specifies a break from `Loop 23`, then this function
/// would generate a series of basic blocks as follows:
///
/// Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk
///
/// where `break_blk` is the block specified in `Loop 23` as the target for
/// breaks. The return value would be the first basic block in that sequence
/// (`Cleanup(AST 24)`). The caller could then branch to `Cleanup(AST 24)`
/// and it will perform all cleanups and finally branch to the `break_blk`.
fn trans_cleanups_to_exit_scope(&'blk self,
label: EarlyExitLabel)
-> BasicBlockRef {
debug!("trans_cleanups_to_exit_scope label={:?} scopes={}",
label, self.scopes_len());
let orig_scopes_len = self.scopes_len();
let mut prev_llbb;
let mut popped_scopes = vec![];
let mut skip = 0;
// First we pop off all the cleanup stacks that are
// traversed until the exit is reached, pushing them
// onto the side vector `popped_scopes`. No code is
// generated at this time.
//
// So, continuing the example from above, we would wind up
// with a `popped_scopes` vector of `[AST 24, Custom 2]`.
// (Presuming that there are no cached exits)
loop {
if self.scopes_len() == 0 {
match label {
UnwindExit(val) => {
// Generate a block that will resume unwinding to the
// calling function
let bcx = self.new_block("resume");
match val {
UnwindKind::LandingPad => {
let addr = self.landingpad_alloca.get()
.unwrap();
let lp = build::Load(bcx, addr);
base::call_lifetime_end(bcx, addr);
base::trans_unwind_resume(bcx, lp);
}
UnwindKind::CleanupPad(_) => {
let pad = build::CleanupPad(bcx, None, &[]);
build::CleanupRet(bcx, pad, None);
}
}
prev_llbb = bcx.llbb;
break;
}
}
}
// Pop off the scope, since we may be generating
// unwinding code for it.
let top_scope = self.pop_scope();
let cached_exit = top_scope.cached_early_exit(label);
popped_scopes.push(top_scope);
// Check if we have already cached the unwinding of this
// scope for this label. If so, we can stop popping scopes
// and branch to the cached label, since it contains the
// cleanups for any subsequent scopes.
if let Some((exit, last_cleanup)) = cached_exit {
prev_llbb = exit;
skip = last_cleanup;
break;
}
}
debug!("trans_cleanups_to_exit_scope: popped {} scopes",
popped_scopes.len());
// Now push the popped scopes back on. As we go,
// we track in `prev_llbb` the exit to which this scope
// should branch when it's done.
//
// So, continuing with our example, we will start out with
// `prev_llbb` being set to `break_blk` (or possibly a cached
// early exit). We will then pop the scopes from `popped_scopes`
// and generate a basic block for each one, prepending it in the
// series and updating `prev_llbb`. So we begin by popping `Custom 2`
// and generating `Cleanup(Custom 2)`. We make `Cleanup(Custom 2)`
// branch to `prev_llbb == break_blk`, giving us a sequence like:
//
// Cleanup(Custom 2) -> prev_llbb
//
// We then pop `AST 24` and repeat the process, giving us the sequence:
//
// Cleanup(AST 24) -> Cleanup(Custom 2) -> prev_llbb
//
// At this point, `popped_scopes` is empty, and so the final block
// that we return to the user is `Cleanup(AST 24)`.
while let Some(mut scope) = popped_scopes.pop() {
if !scope.cleanups.is_empty() {
let name = scope.block_name("clean");
debug!("generating cleanups for {}", name);
let bcx_in = self.new_block(&name[..]);
let exit_label = label.start(bcx_in);
let mut bcx_out = bcx_in;
let len = scope.cleanups.len();
for cleanup in scope.cleanups.iter().rev().take(len - skip) {
bcx_out = cleanup.trans(bcx_out, scope.debug_loc);
}
skip = 0;
exit_label.branch(bcx_out, prev_llbb);
prev_llbb = bcx_in.llbb;
scope.add_cached_early_exit(exit_label, prev_llbb, len);
}
self.push_scope(scope);
}
debug!("trans_cleanups_to_exit_scope: prev_llbb={:?}", prev_llbb);
assert_eq!(self.scopes_len(), orig_scopes_len);
prev_llbb
}
/// Creates a landing pad for the top scope, if one does not exist. The
/// landing pad will perform all cleanups necessary for an unwind and then
/// `resume` to continue error propagation:
///
/// landing_pad -> ... cleanups ... -> [resume]
///
/// (The cleanups and resume instruction are created by
/// `trans_cleanups_to_exit_scope()`, not in this function itself.)
fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef {
let pad_bcx;
debug!("get_or_create_landing_pad");
// Check if a landing pad block exists; if not, create one.
{
let mut scopes = self.scopes.borrow_mut();
let last_scope = scopes.last_mut().unwrap();
match last_scope.cached_landing_pad {
Some(llbb) => return llbb,
None => {
let name = last_scope.block_name("unwind");
pad_bcx = self.new_block(&name[..]);
last_scope.cached_landing_pad = Some(pad_bcx.llbb);
}
}
};
let llpersonality = pad_bcx.fcx.eh_personality();
let val = if base::wants_msvc_seh(self.ccx.sess()) {
// A cleanup pad requires a personality function to be specified, so
// we do that here explicitly (happens implicitly below through
// creation of the landingpad instruction). We then create a
// cleanuppad instruction which has no filters to run cleanup on all
// exceptions.
build::SetPersonalityFn(pad_bcx, llpersonality);
let llretval = build::CleanupPad(pad_bcx, None, &[]);
UnwindKind::CleanupPad(llretval)
} else {
// The landing pad return type (the type being propagated). Not sure
// what this represents but it's determined by the personality
// function and this is what the EH proposal example uses.
let llretty = Type::struct_(self.ccx,
&[Type::i8p(self.ccx), Type::i32(self.ccx)],
false);
// The only landing pad clause will be 'cleanup'
let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1);
// The landing pad block is a cleanup
build::SetCleanup(pad_bcx, llretval);
let addr = match self.landingpad_alloca.get() {
Some(addr) => addr,
None => {
let addr = base::alloca(pad_bcx, common::val_ty(llretval),
"");
base::call_lifetime_start(pad_bcx, addr);
self.landingpad_alloca.set(Some(addr));
addr
}
};
build::Store(pad_bcx, llretval, addr);
UnwindKind::LandingPad
};
// Generate the cleanup block and branch to it.
let label = UnwindExit(val);
let cleanup_llbb = self.trans_cleanups_to_exit_scope(label);
label.branch(pad_bcx, cleanup_llbb);
return pad_bcx.llbb;
} }
} }
impl<'tcx> CleanupScope<'tcx> { impl<'tcx> CleanupScope<'tcx> {
fn new(debug_loc: DebugLoc) -> CleanupScope<'tcx> { fn new<'a>(fcx: &FunctionContext<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> {
CleanupScope { CleanupScope {
debug_loc: debug_loc, cleanup: Some(drop_val),
cleanups: vec![], landing_pad: if !fcx.ccx.sess().no_landing_pads() {
cached_early_exits: vec![], Some(drop_val.get_landing_pad(fcx))
cached_landing_pad: None, } else {
None
},
} }
} }
fn cached_early_exit(&self, pub fn noop() -> CleanupScope<'tcx> {
label: EarlyExitLabel) CleanupScope {
-> Option<(BasicBlockRef, usize)> { cleanup: None,
self.cached_early_exits.iter().rev(). landing_pad: None,
find(|e| e.label == label).
map(|e| (e.cleanup_block, e.last_cleanup))
}
fn add_cached_early_exit(&mut self,
label: EarlyExitLabel,
blk: BasicBlockRef,
last_cleanup: usize) {
self.cached_early_exits.push(
CachedEarlyExit { label: label,
cleanup_block: blk,
last_cleanup: last_cleanup});
}
/// True if this scope has cleanups that need unwinding
fn needs_invoke(&self) -> bool {
self.cached_landing_pad.is_some() ||
!self.cleanups.is_empty()
}
/// Returns a suitable name to use for the basic block that handles this cleanup scope
fn block_name(&self, prefix: &str) -> String {
format!("{}_custom_", prefix)
}
}
impl EarlyExitLabel {
/// Generates a branch going from `from_bcx` to `to_llbb` where `self` is
/// the exit label attached to the start of `from_bcx`.
///
/// Transitions from an exit label to other exit labels depend on the type
/// of label. For example with MSVC exceptions unwind exit labels will use
/// the `cleanupret` instruction instead of the `br` instruction.
fn branch(&self, from_bcx: Block, to_llbb: BasicBlockRef) {
if let UnwindExit(UnwindKind::CleanupPad(pad)) = *self {
build::CleanupRet(from_bcx, pad, Some(to_llbb));
} else {
build::Br(from_bcx, to_llbb, DebugLoc::None);
} }
} }
/// Generates the necessary instructions at the start of `bcx` to prepare pub fn trans<'a>(self, bcx: &'a BlockAndBuilder<'a, 'tcx>) {
/// for the same kind of early exit label that `self` is. if let Some(cleanup) = self.cleanup {
/// cleanup.trans(None, &bcx);
/// This function will appropriately configure `bcx` based on the kind of
/// label this is. For UnwindExit labels, the `lpad` field of the block will
/// be set to `Some`, and for MSVC exceptions this function will generate a
/// `cleanuppad` instruction at the start of the block so it may be jumped
/// to in the future (e.g. so this block can be cached as an early exit).
///
/// Returns a new label which will can be used to cache `bcx` in the list of
/// early exits.
fn start(&self, bcx: Block) -> EarlyExitLabel {
match *self {
UnwindExit(UnwindKind::CleanupPad(..)) => {
let pad = build::CleanupPad(bcx, None, &[]);
bcx.lpad.set(Some(bcx.fcx.lpad_arena.alloc(LandingPad::msvc(pad))));
UnwindExit(UnwindKind::CleanupPad(pad))
}
UnwindExit(UnwindKind::LandingPad) => {
bcx.lpad.set(Some(bcx.fcx.lpad_arena.alloc(LandingPad::gnu())));
*self
}
} }
} }
} }
impl PartialEq for UnwindKind {
fn eq(&self, val: &UnwindKind) -> bool {
match (*self, *val) {
(UnwindKind::LandingPad, UnwindKind::LandingPad) |
(UnwindKind::CleanupPad(..), UnwindKind::CleanupPad(..)) => true,
_ => false,
}
}
}
///////////////////////////////////////////////////////////////////////////
// Cleanup types
#[derive(Copy, Clone)]
pub struct DropValue<'tcx> {
is_immediate: bool,
val: ValueRef,
ty: Ty<'tcx>,
skip_dtor: bool,
}
impl<'tcx> DropValue<'tcx> {
fn trans<'blk>(&self,
bcx: Block<'blk, 'tcx>,
debug_loc: DebugLoc)
-> Block<'blk, 'tcx> {
let skip_dtor = self.skip_dtor;
let _icx = if skip_dtor {
base::push_ctxt("<DropValue as Cleanup>::trans skip_dtor=true")
} else {
base::push_ctxt("<DropValue as Cleanup>::trans skip_dtor=false")
};
let bcx = if self.is_immediate {
glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc, self.skip_dtor)
} else {
glue::drop_ty_core(bcx, self.val, self.ty, debug_loc, self.skip_dtor)
};
bcx
}
}

View file

@ -208,7 +208,7 @@ use syntax::abi::Abi;
use syntax_pos::DUMMY_SP; use syntax_pos::DUMMY_SP;
use base::custom_coerce_unsize_info; use base::custom_coerce_unsize_info;
use context::SharedCrateContext; use context::SharedCrateContext;
use common::{fulfill_obligation, type_is_sized}; use common::fulfill_obligation;
use glue::{self, DropGlueKind}; use glue::{self, DropGlueKind};
use monomorphize::{self, Instance}; use monomorphize::{self, Instance};
use util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; use util::nodemap::{FxHashSet, FxHashMap, DefIdMap};
@ -337,7 +337,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>,
TransItem::Static(node_id) => { TransItem::Static(node_id) => {
let def_id = scx.tcx().map.local_def_id(node_id); let def_id = scx.tcx().map.local_def_id(node_id);
let ty = scx.tcx().item_type(def_id); let ty = scx.tcx().item_type(def_id);
let ty = glue::get_drop_glue_type(scx.tcx(), ty); let ty = glue::get_drop_glue_type(scx, ty);
neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); neighbors.push(TransItem::DropGlue(DropGlueKind::Ty(ty)));
recursion_depth_reset = None; recursion_depth_reset = None;
@ -542,7 +542,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
self.param_substs, self.param_substs,
&ty); &ty);
assert!(ty.is_normalized_for_trans()); assert!(ty.is_normalized_for_trans());
let ty = glue::get_drop_glue_type(self.scx.tcx(), ty); let ty = glue::get_drop_glue_type(self.scx, ty);
self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty)));
} }
@ -678,7 +678,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
let operand_ty = monomorphize::apply_param_substs(self.scx, let operand_ty = monomorphize::apply_param_substs(self.scx,
self.param_substs, self.param_substs,
&mt.ty); &mt.ty);
let ty = glue::get_drop_glue_type(tcx, operand_ty); let ty = glue::get_drop_glue_type(self.scx, operand_ty);
self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty)));
} else { } else {
bug!("Has the drop_in_place() intrinsic's signature changed?") bug!("Has the drop_in_place() intrinsic's signature changed?")
@ -804,17 +804,17 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
let field_type = monomorphize::apply_param_substs(scx, let field_type = monomorphize::apply_param_substs(scx,
substs, substs,
&field_type); &field_type);
let field_type = glue::get_drop_glue_type(scx.tcx(), field_type); let field_type = glue::get_drop_glue_type(scx, field_type);
if glue::type_needs_drop(scx.tcx(), field_type) { if scx.type_needs_drop(field_type) {
output.push(TransItem::DropGlue(DropGlueKind::Ty(field_type))); output.push(TransItem::DropGlue(DropGlueKind::Ty(field_type)));
} }
} }
} }
ty::TyClosure(def_id, substs) => { ty::TyClosure(def_id, substs) => {
for upvar_ty in substs.upvar_tys(def_id, scx.tcx()) { for upvar_ty in substs.upvar_tys(def_id, scx.tcx()) {
let upvar_ty = glue::get_drop_glue_type(scx.tcx(), upvar_ty); let upvar_ty = glue::get_drop_glue_type(scx, upvar_ty);
if glue::type_needs_drop(scx.tcx(), upvar_ty) { if scx.type_needs_drop(upvar_ty) {
output.push(TransItem::DropGlue(DropGlueKind::Ty(upvar_ty))); output.push(TransItem::DropGlue(DropGlueKind::Ty(upvar_ty)));
} }
} }
@ -822,15 +822,15 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
ty::TyBox(inner_type) | ty::TyBox(inner_type) |
ty::TySlice(inner_type) | ty::TySlice(inner_type) |
ty::TyArray(inner_type, _) => { ty::TyArray(inner_type, _) => {
let inner_type = glue::get_drop_glue_type(scx.tcx(), inner_type); let inner_type = glue::get_drop_glue_type(scx, inner_type);
if glue::type_needs_drop(scx.tcx(), inner_type) { if scx.type_needs_drop(inner_type) {
output.push(TransItem::DropGlue(DropGlueKind::Ty(inner_type))); output.push(TransItem::DropGlue(DropGlueKind::Ty(inner_type)));
} }
} }
ty::TyTuple(args) => { ty::TyTuple(args) => {
for arg in args { for arg in args {
let arg = glue::get_drop_glue_type(scx.tcx(), arg); let arg = glue::get_drop_glue_type(scx, arg);
if glue::type_needs_drop(scx.tcx(), arg) { if scx.type_needs_drop(arg) {
output.push(TransItem::DropGlue(DropGlueKind::Ty(arg))); output.push(TransItem::DropGlue(DropGlueKind::Ty(arg)));
} }
} }
@ -969,7 +969,7 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
&ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => {
let (inner_source, inner_target) = (a, b); let (inner_source, inner_target) = (a, b);
if !type_is_sized(scx.tcx(), inner_source) { if !scx.type_is_sized(inner_source) {
(inner_source, inner_target) (inner_source, inner_target)
} else { } else {
scx.tcx().struct_lockstep_tails(inner_source, inner_target) scx.tcx().struct_lockstep_tails(inner_source, inner_target)
@ -1051,7 +1051,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a,
output.extend(methods); output.extend(methods);
} }
// Also add the destructor // Also add the destructor
let dg_type = glue::get_drop_glue_type(scx.tcx(), impl_ty); let dg_type = glue::get_drop_glue_type(scx, impl_ty);
output.push(TransItem::DropGlue(DropGlueKind::Ty(dg_type))); output.push(TransItem::DropGlue(DropGlueKind::Ty(dg_type)));
} }
} }
@ -1097,7 +1097,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
def_id_to_string(self.scx.tcx(), def_id)); def_id_to_string(self.scx.tcx(), def_id));
let ty = self.scx.tcx().item_type(def_id); let ty = self.scx.tcx().item_type(def_id);
let ty = glue::get_drop_glue_type(self.scx.tcx(), ty); let ty = glue::get_drop_glue_type(self.scx, ty);
self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty))); self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty)));
} }
} }

View file

@ -14,24 +14,16 @@
use session::Session; use session::Session;
use llvm; use llvm;
use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef, TypeKind}; use llvm::{ValueRef, BasicBlockRef, ContextRef, TypeKind};
use llvm::{True, False, Bool, OperandBundleDef}; use llvm::{True, False, Bool, OperandBundleDef};
use rustc::hir::def::Def; use rustc::hir::def::Def;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::hir::map::DefPathData; use rustc::hir::map::DefPathData;
use rustc::infer::TransNormalize;
use rustc::mir::Mir;
use rustc::util::common::MemoizationMap; use rustc::util::common::MemoizationMap;
use middle::lang_items::LangItem; use middle::lang_items::LangItem;
use rustc::ty::subst::Substs;
use abi::{Abi, FnType};
use base; use base;
use build;
use builder::Builder; use builder::Builder;
use callee::Callee;
use cleanup;
use consts; use consts;
use debuginfo::{self, DebugLoc};
use declare; use declare;
use machine; use machine;
use monomorphize; use monomorphize;
@ -40,34 +32,26 @@ use value::Value;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::layout::Layout; use rustc::ty::layout::Layout;
use rustc::traits::{self, SelectionContext, Reveal}; use rustc::traits::{self, SelectionContext, Reveal};
use rustc::ty::fold::TypeFoldable;
use rustc::hir; use rustc::hir;
use arena::TypedArena;
use libc::{c_uint, c_char}; use libc::{c_uint, c_char};
use std::borrow::Cow; use std::borrow::Cow;
use std::iter; use std::iter;
use std::ops::Deref; use std::ops::Deref;
use std::ffi::CString; use std::ffi::CString;
use std::cell::{Cell, RefCell, Ref};
use syntax::ast; use syntax::ast;
use syntax::symbol::{Symbol, InternedString}; use syntax::symbol::{Symbol, InternedString};
use syntax_pos::{DUMMY_SP, Span}; use syntax_pos::Span;
pub use context::{CrateContext, SharedCrateContext}; pub use context::{CrateContext, SharedCrateContext};
/// Is the type's representation size known at compile time? pub fn type_is_fat_ptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
ty.is_sized(tcx, &tcx.empty_parameter_environment(), DUMMY_SP)
}
pub fn type_is_fat_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
match ty.sty { match ty.sty {
ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | ty::TyRawPtr(ty::TypeAndMut{ty, ..}) |
ty::TyRef(_, ty::TypeAndMut{ty, ..}) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyBox(ty) => { ty::TyBox(ty) => {
!type_is_sized(tcx, ty) !ccx.shared().type_is_sized(ty)
} }
_ => { _ => {
false false
@ -79,14 +63,13 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
use machine::llsize_of_alloc; use machine::llsize_of_alloc;
use type_of::sizing_type_of; use type_of::sizing_type_of;
let tcx = ccx.tcx();
let simple = ty.is_scalar() || let simple = ty.is_scalar() ||
ty.is_unique() || ty.is_region_ptr() || ty.is_unique() || ty.is_region_ptr() ||
ty.is_simd(); ty.is_simd();
if simple && !type_is_fat_ptr(tcx, ty) { if simple && !type_is_fat_ptr(ccx, ty) {
return true; return true;
} }
if !type_is_sized(tcx, ty) { if !ccx.shared().type_is_sized(ty) {
return false; return false;
} }
match ty.sty { match ty.sty {
@ -236,416 +219,139 @@ impl<'a, 'tcx> VariantInfo<'tcx> {
} }
} }
pub struct BuilderRef_res { // Function context. Every LLVM function we create will have one of these.
pub b: BuilderRef,
}
impl Drop for BuilderRef_res {
fn drop(&mut self) {
unsafe {
llvm::LLVMDisposeBuilder(self.b);
}
}
}
pub fn BuilderRef_res(b: BuilderRef) -> BuilderRef_res {
BuilderRef_res {
b: b
}
}
pub fn validate_substs(substs: &Substs) {
assert!(!substs.needs_infer());
}
// Function context. Every LLVM function we create will have one of
// these.
pub struct FunctionContext<'a, 'tcx: 'a> { pub struct FunctionContext<'a, 'tcx: 'a> {
// The MIR for this function.
pub mir: Option<Ref<'tcx, Mir<'tcx>>>,
// The ValueRef returned from a call to llvm::LLVMAddFunction; the // The ValueRef returned from a call to llvm::LLVMAddFunction; the
// address of the first instruction in the sequence of // address of the first instruction in the sequence of
// instructions for this function that will go in the .text // instructions for this function that will go in the .text
// section of the executable we're generating. // section of the executable we're generating.
pub llfn: ValueRef, pub llfn: ValueRef,
// always an empty parameter-environment NOTE: @jroesch another use of ParamEnv
pub param_env: ty::ParameterEnvironment<'tcx>,
// A pointer to where to store the return value. If the return type is
// immediate, this points to an alloca in the function. Otherwise, it's a
// pointer to the hidden first parameter of the function. After function
// construction, this should always be Some.
pub llretslotptr: Cell<Option<ValueRef>>,
// These pub elements: "hoisted basic blocks" containing
// administrative activities that have to happen in only one place in
// the function, due to LLVM's quirks.
// A marker for the place where we want to insert the function's static // A marker for the place where we want to insert the function's static
// allocas, so that LLVM will coalesce them into a single alloca call. // allocas, so that LLVM will coalesce them into a single alloca call.
pub alloca_insert_pt: Cell<Option<ValueRef>>, alloca_insert_pt: Option<ValueRef>,
// When working with landingpad-based exceptions this value is alloca'd and
// later loaded when using the resume instruction. This ends up being
// critical to chaining landing pads and resuing already-translated
// cleanups.
//
// Note that for cleanuppad-based exceptions this is not used.
pub landingpad_alloca: Cell<Option<ValueRef>>,
// Describes the return/argument LLVM types and their ABI handling.
pub fn_ty: FnType,
// If this function is being monomorphized, this contains the type
// substitutions used.
pub param_substs: &'tcx Substs<'tcx>,
// The source span and nesting context where this function comes from, for
// error reporting and symbol generation.
pub span: Option<Span>,
// The arena that blocks are allocated from.
pub block_arena: &'a TypedArena<BlockS<'a, 'tcx>>,
// The arena that landing pads are allocated from.
pub lpad_arena: TypedArena<LandingPad>,
// This function's enclosing crate context. // This function's enclosing crate context.
pub ccx: &'a CrateContext<'a, 'tcx>, pub ccx: &'a CrateContext<'a, 'tcx>,
// Used and maintained by the debuginfo module. alloca_builder: Builder<'a, 'tcx>,
pub debug_context: debuginfo::FunctionDebugContext,
// Cleanup scopes.
pub scopes: RefCell<Vec<cleanup::CleanupScope<'tcx>>>,
} }
impl<'a, 'tcx> FunctionContext<'a, 'tcx> { impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
pub fn mir(&self) -> Ref<'tcx, Mir<'tcx>> { /// Create a function context for the given function.
self.mir.as_ref().map(Ref::clone).expect("fcx.mir was empty") /// Call FunctionContext::get_entry_block for the first entry block.
pub fn new(ccx: &'a CrateContext<'a, 'tcx>, llfndecl: ValueRef) -> FunctionContext<'a, 'tcx> {
let mut fcx = FunctionContext {
llfn: llfndecl,
alloca_insert_pt: None,
ccx: ccx,
alloca_builder: Builder::with_ccx(ccx),
};
let val = {
let entry_bcx = fcx.build_new_block("entry-block");
let val = entry_bcx.load(C_null(Type::i8p(ccx)));
fcx.alloca_builder.position_at_start(entry_bcx.llbb());
val
};
// Use a dummy instruction as the insertion point for all allocas.
// This is later removed in the drop of FunctionContext.
fcx.alloca_insert_pt = Some(val);
fcx
} }
pub fn cleanup(&self) { pub fn get_entry_block(&'a self) -> BlockAndBuilder<'a, 'tcx> {
unsafe { BlockAndBuilder::new(unsafe {
llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt llvm::LLVMGetFirstBasicBlock(self.llfn)
.get() }, self)
.unwrap());
}
} }
pub fn new_block(&'a self, pub fn new_block(&'a self, name: &str) -> BasicBlockRef {
name: &str)
-> Block<'a, 'tcx> {
unsafe { unsafe {
let name = CString::new(name).unwrap(); let name = CString::new(name).unwrap();
let llbb = llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx(), llvm::LLVMAppendBasicBlockInContext(
self.llfn, self.ccx.llcx(),
name.as_ptr()); self.llfn,
BlockS::new(llbb, self) name.as_ptr()
)
} }
} }
pub fn monomorphize<T>(&self, value: &T) -> T pub fn build_new_block(&'a self, name: &str) -> BlockAndBuilder<'a, 'tcx> {
where T: TransNormalize<'tcx> BlockAndBuilder::new(self.new_block(name), self)
{
monomorphize::apply_param_substs(self.ccx.shared(),
self.param_substs,
value)
} }
/// This is the same as `common::type_needs_drop`, except that it pub fn alloca(&self, ty: Type, name: &str) -> ValueRef {
/// may use or update caches within this `FunctionContext`. self.alloca_builder.dynamic_alloca(ty, name)
pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool {
self.ccx.tcx().type_needs_drop_given_env(ty, &self.param_env)
}
pub fn eh_personality(&self) -> ValueRef {
// The exception handling personality function.
//
// If our compilation unit has the `eh_personality` lang item somewhere
// within it, then we just need to translate that. Otherwise, we're
// building an rlib which will depend on some upstream implementation of
// this function, so we just codegen a generic reference to it. We don't
// specify any of the types for the function, we just make it a symbol
// that LLVM can later use.
//
// Note that MSVC is a little special here in that we don't use the
// `eh_personality` lang item at all. Currently LLVM has support for
// both Dwarf and SEH unwind mechanisms for MSVC targets and uses the
// *name of the personality function* to decide what kind of unwind side
// tables/landing pads to emit. It looks like Dwarf is used by default,
// injecting a dependency on the `_Unwind_Resume` symbol for resuming
// an "exception", but for MSVC we want to force SEH. This means that we
// can't actually have the personality function be our standard
// `rust_eh_personality` function, but rather we wired it up to the
// CRT's custom personality function, which forces LLVM to consider
// landing pads as "landing pads for SEH".
let ccx = self.ccx;
let tcx = ccx.tcx();
match tcx.lang_items.eh_personality() {
Some(def_id) if !base::wants_msvc_seh(ccx.sess()) => {
Callee::def(ccx, def_id, tcx.intern_substs(&[])).reify(ccx)
}
_ => {
if let Some(llpersonality) = ccx.eh_personality().get() {
return llpersonality
}
let name = if base::wants_msvc_seh(ccx.sess()) {
"__CxxFrameHandler3"
} else {
"rust_eh_personality"
};
let fty = Type::variadic_func(&[], &Type::i32(ccx));
let f = declare::declare_cfn(ccx, name, fty);
ccx.eh_personality().set(Some(f));
f
}
}
}
// Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined,
// otherwise declares it as an external function.
pub fn eh_unwind_resume(&self) -> Callee<'tcx> {
use attributes;
let ccx = self.ccx;
let tcx = ccx.tcx();
assert!(ccx.sess().target.target.options.custom_unwind_resume);
if let Some(def_id) = tcx.lang_items.eh_unwind_resume() {
return Callee::def(ccx, def_id, tcx.intern_substs(&[]));
}
let ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
unsafety: hir::Unsafety::Unsafe,
abi: Abi::C,
sig: ty::Binder(tcx.mk_fn_sig(
iter::once(tcx.mk_mut_ptr(tcx.types.u8)),
tcx.types.never,
false
)),
}));
let unwresume = ccx.eh_unwind_resume();
if let Some(llfn) = unwresume.get() {
return Callee::ptr(llfn, ty);
}
let llfn = declare::declare_fn(ccx, "rust_eh_unwind_resume", ty);
attributes::unwind(llfn, true);
unwresume.set(Some(llfn));
Callee::ptr(llfn, ty)
} }
} }
// Basic block context. We create a block context for each basic block impl<'a, 'tcx> Drop for FunctionContext<'a, 'tcx> {
// (single-entry, single-exit sequence of instructions) we generate from Rust fn drop(&mut self) {
// code. Each basic block we generate is attached to a function, typically unsafe {
// with many basic blocks per function. All the basic blocks attached to a llvm::LLVMInstructionEraseFromParent(self.alloca_insert_pt.unwrap());
// function are organized as a directed graph. }
pub struct BlockS<'blk, 'tcx: 'blk> { }
}
#[must_use]
pub struct BlockAndBuilder<'a, 'tcx: 'a> {
// The BasicBlockRef returned from a call to // The BasicBlockRef returned from a call to
// llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic // llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic
// block to the function pointed to by llfn. We insert // block to the function pointed to by llfn. We insert
// instructions into that block by way of this block context. // instructions into that block by way of this block context.
// The block pointing to this one in the function's digraph. // The block pointing to this one in the function's digraph.
pub llbb: BasicBlockRef, llbb: BasicBlockRef,
pub terminated: Cell<bool>,
pub unreachable: Cell<bool>,
// If this block part of a landing pad, then this is `Some` indicating what
// kind of landing pad its in, otherwise this is none.
pub lpad: Cell<Option<&'blk LandingPad>>,
// The function context for the function to which this block is // The function context for the function to which this block is
// attached. // attached.
pub fcx: &'blk FunctionContext<'blk, 'tcx>, fcx: &'a FunctionContext<'a, 'tcx>,
builder: Builder<'a, 'tcx>,
} }
pub type Block<'blk, 'tcx> = &'blk BlockS<'blk, 'tcx>; impl<'a, 'tcx> BlockAndBuilder<'a, 'tcx> {
pub fn new(llbb: BasicBlockRef, fcx: &'a FunctionContext<'a, 'tcx>) -> Self {
impl<'blk, 'tcx> BlockS<'blk, 'tcx> { let builder = Builder::with_ccx(fcx.ccx);
pub fn new(llbb: BasicBlockRef,
fcx: &'blk FunctionContext<'blk, 'tcx>)
-> Block<'blk, 'tcx> {
fcx.block_arena.alloc(BlockS {
llbb: llbb,
terminated: Cell::new(false),
unreachable: Cell::new(false),
lpad: Cell::new(None),
fcx: fcx
})
}
pub fn ccx(&self) -> &'blk CrateContext<'blk, 'tcx> {
self.fcx.ccx
}
pub fn fcx(&self) -> &'blk FunctionContext<'blk, 'tcx> {
self.fcx
}
pub fn tcx(&self) -> TyCtxt<'blk, 'tcx, 'tcx> {
self.fcx.ccx.tcx()
}
pub fn sess(&self) -> &'blk Session { self.fcx.ccx.sess() }
pub fn lpad(&self) -> Option<&'blk LandingPad> {
self.lpad.get()
}
pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) {
// FIXME: use an IVar?
self.lpad.set(lpad);
}
pub fn set_lpad(&self, lpad: Option<LandingPad>) {
self.set_lpad_ref(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p)))
}
pub fn mir(&self) -> Ref<'tcx, Mir<'tcx>> {
self.fcx.mir()
}
pub fn name(&self, name: ast::Name) -> String {
name.to_string()
}
pub fn node_id_to_string(&self, id: ast::NodeId) -> String {
self.tcx().map.node_to_string(id).to_string()
}
pub fn to_str(&self) -> String {
format!("[block {:p}]", self)
}
pub fn monomorphize<T>(&self, value: &T) -> T
where T: TransNormalize<'tcx>
{
monomorphize::apply_param_substs(self.fcx.ccx.shared(),
self.fcx.param_substs,
value)
}
pub fn build(&'blk self) -> BlockAndBuilder<'blk, 'tcx> {
BlockAndBuilder::new(self, OwnedBuilder::new_with_ccx(self.ccx()))
}
}
pub struct OwnedBuilder<'blk, 'tcx: 'blk> {
builder: Builder<'blk, 'tcx>
}
impl<'blk, 'tcx> OwnedBuilder<'blk, 'tcx> {
pub fn new_with_ccx(ccx: &'blk CrateContext<'blk, 'tcx>) -> Self {
// Create a fresh builder from the crate context.
let llbuilder = unsafe {
llvm::LLVMCreateBuilderInContext(ccx.llcx())
};
OwnedBuilder {
builder: Builder {
llbuilder: llbuilder,
ccx: ccx,
}
}
}
}
impl<'blk, 'tcx> Drop for OwnedBuilder<'blk, 'tcx> {
fn drop(&mut self) {
unsafe {
llvm::LLVMDisposeBuilder(self.builder.llbuilder);
}
}
}
pub struct BlockAndBuilder<'blk, 'tcx: 'blk> {
bcx: Block<'blk, 'tcx>,
owned_builder: OwnedBuilder<'blk, 'tcx>,
}
impl<'blk, 'tcx> BlockAndBuilder<'blk, 'tcx> {
pub fn new(bcx: Block<'blk, 'tcx>, owned_builder: OwnedBuilder<'blk, 'tcx>) -> Self {
// Set the builder's position to this block's end. // Set the builder's position to this block's end.
owned_builder.builder.position_at_end(bcx.llbb); builder.position_at_end(llbb);
BlockAndBuilder { BlockAndBuilder {
bcx: bcx, llbb: llbb,
owned_builder: owned_builder, fcx: fcx,
builder: builder,
} }
} }
pub fn with_block<F, R>(&self, f: F) -> R
where F: FnOnce(Block<'blk, 'tcx>) -> R
{
let result = f(self.bcx);
self.position_at_end(self.bcx.llbb);
result
}
pub fn map_block<F>(self, f: F) -> Self
where F: FnOnce(Block<'blk, 'tcx>) -> Block<'blk, 'tcx>
{
let BlockAndBuilder { bcx, owned_builder } = self;
let bcx = f(bcx);
BlockAndBuilder::new(bcx, owned_builder)
}
pub fn at_start<F, R>(&self, f: F) -> R pub fn at_start<F, R>(&self, f: F) -> R
where F: FnOnce(&BlockAndBuilder<'blk, 'tcx>) -> R where F: FnOnce(&BlockAndBuilder<'a, 'tcx>) -> R
{ {
self.position_at_start(self.bcx.llbb); self.position_at_start(self.llbb);
let r = f(self); let r = f(self);
self.position_at_end(self.bcx.llbb); self.position_at_end(self.llbb);
r r
} }
// Methods delegated to bcx pub fn fcx(&self) -> &'a FunctionContext<'a, 'tcx> {
self.fcx
pub fn is_unreachable(&self) -> bool {
self.bcx.unreachable.get()
} }
pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
pub fn ccx(&self) -> &'blk CrateContext<'blk, 'tcx> { self.ccx.tcx()
self.bcx.ccx()
} }
pub fn fcx(&self) -> &'blk FunctionContext<'blk, 'tcx> { pub fn sess(&self) -> &'a Session {
self.bcx.fcx() self.ccx.sess()
}
pub fn tcx(&self) -> TyCtxt<'blk, 'tcx, 'tcx> {
self.bcx.tcx()
}
pub fn sess(&self) -> &'blk Session {
self.bcx.sess()
} }
pub fn llbb(&self) -> BasicBlockRef { pub fn llbb(&self) -> BasicBlockRef {
self.bcx.llbb self.llbb
}
pub fn mir(&self) -> Ref<'tcx, Mir<'tcx>> {
self.bcx.mir()
}
pub fn monomorphize<T>(&self, value: &T) -> T
where T: TransNormalize<'tcx>
{
self.bcx.monomorphize(value)
}
pub fn set_lpad(&self, lpad: Option<LandingPad>) {
self.bcx.set_lpad(lpad)
}
pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) {
// FIXME: use an IVar?
self.bcx.set_lpad_ref(lpad);
}
pub fn lpad(&self) -> Option<&'blk LandingPad> {
self.bcx.lpad()
} }
} }
impl<'blk, 'tcx> Deref for BlockAndBuilder<'blk, 'tcx> { impl<'a, 'tcx> Deref for BlockAndBuilder<'a, 'tcx> {
type Target = Builder<'blk, 'tcx>; type Target = Builder<'a, 'tcx>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.owned_builder.builder &self.builder
} }
} }
@ -663,53 +369,33 @@ impl<'blk, 'tcx> Deref for BlockAndBuilder<'blk, 'tcx> {
/// When inside of a landing pad, each function call in LLVM IR needs to be /// When inside of a landing pad, each function call in LLVM IR needs to be
/// annotated with which landing pad it's a part of. This is accomplished via /// annotated with which landing pad it's a part of. This is accomplished via
/// the `OperandBundleDef` value created for MSVC landing pads. /// the `OperandBundleDef` value created for MSVC landing pads.
pub struct LandingPad { pub struct Funclet {
cleanuppad: Option<ValueRef>, cleanuppad: ValueRef,
operand: Option<OperandBundleDef>, operand: OperandBundleDef,
} }
impl LandingPad { impl Funclet {
pub fn gnu() -> LandingPad { pub fn new(cleanuppad: ValueRef) -> Funclet {
LandingPad { cleanuppad: None, operand: None } Funclet {
} cleanuppad: cleanuppad,
operand: OperandBundleDef::new("funclet", &[cleanuppad]),
pub fn msvc(cleanuppad: ValueRef) -> LandingPad {
LandingPad {
cleanuppad: Some(cleanuppad),
operand: Some(OperandBundleDef::new("funclet", &[cleanuppad])),
} }
} }
pub fn bundle(&self) -> Option<&OperandBundleDef> { pub fn cleanuppad(&self) -> ValueRef {
self.operand.as_ref()
}
pub fn cleanuppad(&self) -> Option<ValueRef> {
self.cleanuppad self.cleanuppad
} }
}
impl Clone for LandingPad { pub fn bundle(&self) -> &OperandBundleDef {
fn clone(&self) -> LandingPad { &self.operand
LandingPad {
cleanuppad: self.cleanuppad,
operand: self.cleanuppad.map(|p| {
OperandBundleDef::new("funclet", &[p])
}),
}
} }
} }
pub struct Result<'blk, 'tcx: 'blk> { impl Clone for Funclet {
pub bcx: Block<'blk, 'tcx>, fn clone(&self) -> Funclet {
pub val: ValueRef Funclet {
} cleanuppad: self.cleanuppad,
operand: OperandBundleDef::new("funclet", &[self.cleanuppad]),
impl<'b, 'tcx> Result<'b, 'tcx> {
pub fn new(bcx: Block<'b, 'tcx>, val: ValueRef) -> Result<'b, 'tcx> {
Result {
bcx: bcx,
val: val,
} }
} }
} }
@ -1016,43 +702,42 @@ pub fn langcall(tcx: TyCtxt,
// all shifts). For 32- and 64-bit types, this matches the semantics // all shifts). For 32- and 64-bit types, this matches the semantics
// of Java. (See related discussion on #1877 and #10183.) // of Java. (See related discussion on #1877 and #10183.)
pub fn build_unchecked_lshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn build_unchecked_lshift<'a, 'tcx>(
lhs: ValueRef, bcx: &BlockAndBuilder<'a, 'tcx>,
rhs: ValueRef, lhs: ValueRef,
binop_debug_loc: DebugLoc) -> ValueRef { rhs: ValueRef
) -> ValueRef {
let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs); let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs);
// #1877, #10183: Ensure that input is always valid // #1877, #10183: Ensure that input is always valid
let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); let rhs = shift_mask_rhs(bcx, rhs);
build::Shl(bcx, lhs, rhs, binop_debug_loc) bcx.shl(lhs, rhs)
} }
pub fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn build_unchecked_rshift<'a, 'tcx>(
lhs_t: Ty<'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef
lhs: ValueRef, ) -> ValueRef {
rhs: ValueRef,
binop_debug_loc: DebugLoc) -> ValueRef {
let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs); let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs);
// #1877, #10183: Ensure that input is always valid // #1877, #10183: Ensure that input is always valid
let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); let rhs = shift_mask_rhs(bcx, rhs);
let is_signed = lhs_t.is_signed(); let is_signed = lhs_t.is_signed();
if is_signed { if is_signed {
build::AShr(bcx, lhs, rhs, binop_debug_loc) bcx.ashr(lhs, rhs)
} else { } else {
build::LShr(bcx, lhs, rhs, binop_debug_loc) bcx.lshr(lhs, rhs)
} }
} }
fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, fn shift_mask_rhs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, rhs: ValueRef) -> ValueRef {
rhs: ValueRef,
debug_loc: DebugLoc) -> ValueRef {
let rhs_llty = val_ty(rhs); let rhs_llty = val_ty(rhs);
build::And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc) bcx.and(rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false))
} }
pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn shift_mask_val<'a, 'tcx>(
llty: Type, bcx: &BlockAndBuilder<'a, 'tcx>,
mask_llty: Type, llty: Type,
invert: bool) -> ValueRef { mask_llty: Type,
invert: bool
) -> ValueRef {
let kind = llty.kind(); let kind = llty.kind();
match kind { match kind {
TypeKind::Integer => { TypeKind::Integer => {
@ -1066,7 +751,7 @@ pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}, },
TypeKind::Vector => { TypeKind::Vector => {
let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert); let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert);
build::VectorSplat(bcx, mask_llty.vector_length(), mask) bcx.vector_splat(mask_llty.vector_length(), mask)
}, },
_ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind),
} }

View file

@ -16,7 +16,7 @@ use rustc_const_eval::ConstEvalErr;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::hir::map as hir_map; use rustc::hir::map as hir_map;
use {debuginfo, machine}; use {debuginfo, machine};
use base::{self, push_ctxt}; use base;
use trans_item::TransItem; use trans_item::TransItem;
use common::{CrateContext, val_ty}; use common::{CrateContext, val_ty};
use declare; use declare;
@ -221,7 +221,6 @@ pub fn trans_static(ccx: &CrateContext,
attrs: &[ast::Attribute]) attrs: &[ast::Attribute])
-> Result<ValueRef, ConstEvalErr> { -> Result<ValueRef, ConstEvalErr> {
unsafe { unsafe {
let _icx = push_ctxt("trans_static");
let def_id = ccx.tcx().map.local_def_id(id); let def_id = ccx.tcx().map.local_def_id(id);
let g = get_static(ccx, def_id); let g = get_static(ccx, def_id);

View file

@ -9,17 +9,16 @@
// except according to those terms. // except according to those terms.
use llvm; use llvm;
use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef}; use llvm::{ContextRef, ModuleRef, ValueRef};
use rustc::dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig, use rustc::dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig, WorkProduct};
WorkProduct};
use middle::cstore::LinkMeta; use middle::cstore::LinkMeta;
use rustc::hir;
use rustc::hir::def::ExportMap; use rustc::hir::def::ExportMap;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::traits; use rustc::traits;
use base;
use builder::Builder;
use common::BuilderRef_res;
use debuginfo; use debuginfo;
use callee::Callee;
use base;
use declare; use declare;
use glue::DropGlueKind; use glue::DropGlueKind;
use monomorphize::Instance; use monomorphize::Instance;
@ -40,11 +39,13 @@ use std::ffi::{CStr, CString};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ptr; use std::ptr;
use std::iter;
use std::rc::Rc; use std::rc::Rc;
use std::str; use std::str;
use syntax::ast; use syntax::ast;
use syntax::symbol::InternedString; use syntax::symbol::InternedString;
use abi::FnType; use syntax_pos::DUMMY_SP;
use abi::{Abi, FnType};
pub struct Stats { pub struct Stats {
pub n_glues_created: Cell<usize>, pub n_glues_created: Cell<usize>,
@ -71,6 +72,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> {
exported_symbols: NodeSet, exported_symbols: NodeSet,
link_meta: LinkMeta, link_meta: LinkMeta,
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
empty_param_env: ty::ParameterEnvironment<'tcx>,
stats: Stats, stats: Stats,
check_overflow: bool, check_overflow: bool,
@ -140,7 +142,6 @@ pub struct LocalCrateContext<'tcx> {
int_type: Type, int_type: Type,
opaque_vec_type: Type, opaque_vec_type: Type,
str_slice_type: Type, str_slice_type: Type,
builder: BuilderRef_res,
/// Holds the LLVM values for closure IDs. /// Holds the LLVM values for closure IDs.
closure_vals: RefCell<FxHashMap<Instance<'tcx>, ValueRef>>, closure_vals: RefCell<FxHashMap<Instance<'tcx>, ValueRef>>,
@ -153,11 +154,6 @@ pub struct LocalCrateContext<'tcx> {
intrinsics: RefCell<FxHashMap<&'static str, ValueRef>>, intrinsics: RefCell<FxHashMap<&'static str, ValueRef>>,
/// Number of LLVM instructions translated into this `LocalCrateContext`.
/// This is used to perform some basic load-balancing to keep all LLVM
/// contexts around the same size.
n_llvm_insns: Cell<usize>,
/// Depth of the current type-of computation - used to bail out /// Depth of the current type-of computation - used to bail out
type_of_depth: Cell<usize>, type_of_depth: Cell<usize>,
@ -316,38 +312,6 @@ impl<'a, 'tcx> Iterator for CrateContextIterator<'a,'tcx> {
} }
} }
/// The iterator produced by `CrateContext::maybe_iter`.
pub struct CrateContextMaybeIterator<'a, 'tcx: 'a> {
shared: &'a SharedCrateContext<'a, 'tcx>,
local_ccxs: &'a [LocalCrateContext<'tcx>],
index: usize,
single: bool,
origin: usize,
}
impl<'a, 'tcx> Iterator for CrateContextMaybeIterator<'a, 'tcx> {
type Item = (CrateContext<'a, 'tcx>, bool);
fn next(&mut self) -> Option<(CrateContext<'a, 'tcx>, bool)> {
if self.index >= self.local_ccxs.len() {
return None;
}
let index = self.index;
self.index += 1;
if self.single {
self.index = self.local_ccxs.len();
}
let ccx = CrateContext {
shared: self.shared,
index: index,
local_ccxs: self.local_ccxs
};
Some((ccx, index == self.origin))
}
}
pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode { pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode {
let reloc_model_arg = match sess.opts.cg.relocation_model { let reloc_model_arg = match sess.opts.cg.relocation_model {
Some(ref s) => &s[..], Some(ref s) => &s[..],
@ -496,6 +460,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
export_map: export_map, export_map: export_map,
exported_symbols: exported_symbols, exported_symbols: exported_symbols,
link_meta: link_meta, link_meta: link_meta,
empty_param_env: tcx.empty_parameter_environment(),
tcx: tcx, tcx: tcx,
stats: Stats { stats: Stats {
n_glues_created: Cell::new(0), n_glues_created: Cell::new(0),
@ -516,6 +481,14 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
} }
} }
pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool {
self.tcx.type_needs_drop_given_env(ty, &self.empty_param_env)
}
pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
ty.is_sized(self.tcx, &self.empty_param_env, DUMMY_SP)
}
pub fn metadata_llmod(&self) -> ModuleRef { pub fn metadata_llmod(&self) -> ModuleRef {
self.metadata_llmod self.metadata_llmod
} }
@ -638,14 +611,12 @@ impl<'tcx> LocalCrateContext<'tcx> {
int_type: Type::from_ref(ptr::null_mut()), int_type: Type::from_ref(ptr::null_mut()),
opaque_vec_type: Type::from_ref(ptr::null_mut()), opaque_vec_type: Type::from_ref(ptr::null_mut()),
str_slice_type: Type::from_ref(ptr::null_mut()), str_slice_type: Type::from_ref(ptr::null_mut()),
builder: BuilderRef_res(llvm::LLVMCreateBuilderInContext(llcx)),
closure_vals: RefCell::new(FxHashMap()), closure_vals: RefCell::new(FxHashMap()),
dbg_cx: dbg_cx, dbg_cx: dbg_cx,
eh_personality: Cell::new(None), eh_personality: Cell::new(None),
eh_unwind_resume: Cell::new(None), eh_unwind_resume: Cell::new(None),
rust_try_fn: Cell::new(None), rust_try_fn: Cell::new(None),
intrinsics: RefCell::new(FxHashMap()), intrinsics: RefCell::new(FxHashMap()),
n_llvm_insns: Cell::new(0),
type_of_depth: Cell::new(0), type_of_depth: Cell::new(0),
symbol_map: symbol_map, symbol_map: symbol_map,
local_gen_sym_counter: Cell::new(0), local_gen_sym_counter: Cell::new(0),
@ -671,10 +642,6 @@ impl<'tcx> LocalCrateContext<'tcx> {
local_ccx.opaque_vec_type = opaque_vec_type; local_ccx.opaque_vec_type = opaque_vec_type;
local_ccx.str_slice_type = str_slice_ty; local_ccx.str_slice_type = str_slice_ty;
if shared.tcx.sess.count_llvm_insns() {
base::init_insn_ctxt()
}
local_ccx local_ccx
} }
} }
@ -703,26 +670,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
self.shared self.shared
} }
pub fn local(&self) -> &'b LocalCrateContext<'tcx> { fn local(&self) -> &'b LocalCrateContext<'tcx> {
&self.local_ccxs[self.index] &self.local_ccxs[self.index]
} }
/// Either iterate over only `self`, or iterate over all `CrateContext`s in
/// the `SharedCrateContext`. The iterator produces `(ccx, is_origin)`
/// pairs, where `is_origin` is `true` if `ccx` is `self` and `false`
/// otherwise. This method is useful for avoiding code duplication in
/// cases where it may or may not be necessary to translate code into every
/// context.
pub fn maybe_iter(&self, iter_all: bool) -> CrateContextMaybeIterator<'b, 'tcx> {
CrateContextMaybeIterator {
shared: self.shared,
index: if iter_all { 0 } else { self.index },
single: !iter_all,
origin: self.index,
local_ccxs: self.local_ccxs,
}
}
pub fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { pub fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
self.shared.tcx self.shared.tcx
} }
@ -731,14 +682,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.shared.tcx.sess &self.shared.tcx.sess
} }
pub fn builder<'a>(&'a self) -> Builder<'a, 'tcx> {
Builder::new(self)
}
pub fn raw_builder<'a>(&'a self) -> BuilderRef {
self.local().builder.b
}
pub fn get_intrinsic(&self, key: &str) -> ValueRef { pub fn get_intrinsic(&self, key: &str) -> ValueRef {
if let Some(v) = self.intrinsics().borrow().get(key).cloned() { if let Some(v) = self.intrinsics().borrow().get(key).cloned() {
return v; return v;
@ -886,14 +829,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.local().dbg_cx &self.local().dbg_cx
} }
pub fn eh_personality<'a>(&'a self) -> &'a Cell<Option<ValueRef>> {
&self.local().eh_personality
}
pub fn eh_unwind_resume<'a>(&'a self) -> &'a Cell<Option<ValueRef>> {
&self.local().eh_unwind_resume
}
pub fn rust_try_fn<'a>(&'a self) -> &'a Cell<Option<ValueRef>> { pub fn rust_try_fn<'a>(&'a self) -> &'a Cell<Option<ValueRef>> {
&self.local().rust_try_fn &self.local().rust_try_fn
} }
@ -902,10 +837,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.local().intrinsics &self.local().intrinsics
} }
pub fn count_llvm_insn(&self) {
self.local().n_llvm_insns.set(self.local().n_llvm_insns.get() + 1);
}
pub fn obj_size_bound(&self) -> u64 { pub fn obj_size_bound(&self) -> u64 {
self.tcx().data_layout.obj_size_bound() self.tcx().data_layout.obj_size_bound()
} }
@ -974,6 +905,82 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
base_n::push_str(idx as u64, base_n::ALPHANUMERIC_ONLY, &mut name); base_n::push_str(idx as u64, base_n::ALPHANUMERIC_ONLY, &mut name);
name name
} }
pub fn eh_personality(&self) -> ValueRef {
// The exception handling personality function.
//
// If our compilation unit has the `eh_personality` lang item somewhere
// within it, then we just need to translate that. Otherwise, we're
// building an rlib which will depend on some upstream implementation of
// this function, so we just codegen a generic reference to it. We don't
// specify any of the types for the function, we just make it a symbol
// that LLVM can later use.
//
// Note that MSVC is a little special here in that we don't use the
// `eh_personality` lang item at all. Currently LLVM has support for
// both Dwarf and SEH unwind mechanisms for MSVC targets and uses the
// *name of the personality function* to decide what kind of unwind side
// tables/landing pads to emit. It looks like Dwarf is used by default,
// injecting a dependency on the `_Unwind_Resume` symbol for resuming
// an "exception", but for MSVC we want to force SEH. This means that we
// can't actually have the personality function be our standard
// `rust_eh_personality` function, but rather we wired it up to the
// CRT's custom personality function, which forces LLVM to consider
// landing pads as "landing pads for SEH".
if let Some(llpersonality) = self.local().eh_personality.get() {
return llpersonality
}
let tcx = self.tcx();
let llfn = match tcx.lang_items.eh_personality() {
Some(def_id) if !base::wants_msvc_seh(self.sess()) => {
Callee::def(self, def_id, tcx.intern_substs(&[])).reify(self)
}
_ => {
let name = if base::wants_msvc_seh(self.sess()) {
"__CxxFrameHandler3"
} else {
"rust_eh_personality"
};
let fty = Type::variadic_func(&[], &Type::i32(self));
declare::declare_cfn(self, name, fty)
}
};
self.local().eh_personality.set(Some(llfn));
llfn
}
// Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined,
// otherwise declares it as an external function.
pub fn eh_unwind_resume(&self) -> ValueRef {
use attributes;
let unwresume = &self.local().eh_unwind_resume;
if let Some(llfn) = unwresume.get() {
return llfn;
}
let tcx = self.tcx();
assert!(self.sess().target.target.options.custom_unwind_resume);
if let Some(def_id) = tcx.lang_items.eh_unwind_resume() {
let llfn = Callee::def(self, def_id, tcx.intern_substs(&[])).reify(self);
unwresume.set(Some(llfn));
return llfn;
}
let ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
unsafety: hir::Unsafety::Unsafe,
abi: Abi::C,
sig: ty::Binder(tcx.mk_fn_sig(
iter::once(tcx.mk_mut_ptr(tcx.types.u8)),
tcx.types.never,
false
)),
}));
let llfn = declare::declare_fn(self, "rust_eh_unwind_resume", ty);
attributes::unwind(llfn, true);
unwresume.set(Some(llfn));
llfn
}
} }
pub struct TypeOfDepthLock<'a, 'tcx: 'a>(&'a LocalCrateContext<'tcx>); pub struct TypeOfDepthLock<'a, 'tcx: 'a>(&'a LocalCrateContext<'tcx>);

View file

@ -44,8 +44,8 @@ impl MirDebugScope {
/// Produce DIScope DIEs for each MIR Scope which has variables defined in it. /// Produce DIScope DIEs for each MIR Scope which has variables defined in it.
/// If debuginfo is disabled, the returned vector is empty. /// If debuginfo is disabled, the returned vector is empty.
pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, MirDebugScope> { pub fn create_mir_scopes(fcx: &FunctionContext, mir: &Mir, debug_context: &FunctionDebugContext)
let mir = fcx.mir(); -> IndexVec<VisibilityScope, MirDebugScope> {
let null_scope = MirDebugScope { let null_scope = MirDebugScope {
scope_metadata: ptr::null_mut(), scope_metadata: ptr::null_mut(),
file_start_pos: BytePos(0), file_start_pos: BytePos(0),
@ -53,8 +53,8 @@ pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, Mir
}; };
let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes);
let fn_metadata = match fcx.debug_context { let fn_metadata = match *debug_context {
FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata, FunctionDebugContext::RegularContext(ref data) => data.fn_metadata,
FunctionDebugContext::DebugInfoDisabled | FunctionDebugContext::DebugInfoDisabled |
FunctionDebugContext::FunctionWithoutDebugInfo => { FunctionDebugContext::FunctionWithoutDebugInfo => {
return scopes; return scopes;

View file

@ -13,37 +13,26 @@
use llvm; use llvm;
use common::{C_bytes, CrateContext, C_i32}; use common::{C_bytes, CrateContext, C_i32};
use builder::Builder;
use declare; use declare;
use type_::Type; use type_::Type;
use session::config::NoDebugInfo; use session::config::NoDebugInfo;
use std::ffi::CString;
use std::ptr; use std::ptr;
use syntax::attr; use syntax::attr;
/// Inserts a side-effect free instruction sequence that makes sure that the /// Inserts a side-effect free instruction sequence that makes sure that the
/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. /// .debug_gdb_scripts global is referenced, so it isn't removed by the linker.
pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext) { pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext, builder: &Builder) {
if needs_gdb_debug_scripts_section(ccx) { if needs_gdb_debug_scripts_section(ccx) {
let empty = CString::new("").unwrap(); let gdb_debug_scripts_section_global = get_or_insert_gdb_debug_scripts_section_global(ccx);
let gdb_debug_scripts_section_global = // Load just the first byte as that's all that's necessary to force
get_or_insert_gdb_debug_scripts_section_global(ccx); // LLVM to keep around the reference to the global.
let indices = [C_i32(ccx, 0), C_i32(ccx, 0)];
let element = builder.inbounds_gep(gdb_debug_scripts_section_global, &indices);
let volative_load_instruction = builder.volatile_load(element);
unsafe { unsafe {
// Load just the first byte as that's all that's necessary to force
// LLVM to keep around the reference to the global.
let indices = [C_i32(ccx, 0), C_i32(ccx, 0)];
let element =
llvm::LLVMBuildInBoundsGEP(ccx.raw_builder(),
gdb_debug_scripts_section_global,
indices.as_ptr(),
indices.len() as ::libc::c_uint,
empty.as_ptr());
let volative_load_instruction =
llvm::LLVMBuildLoad(ccx.raw_builder(),
element,
empty.as_ptr());
llvm::LLVMSetVolatile(volative_load_instruction, llvm::True);
llvm::LLVMSetAlignment(volative_load_instruction, 1); llvm::LLVMSetAlignment(volative_load_instruction, 1);
} }
} }

View file

@ -27,7 +27,7 @@ use rustc::hir::def_id::DefId;
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use abi::Abi; use abi::Abi;
use common::{CrateContext, FunctionContext, Block, BlockAndBuilder}; use common::{CrateContext, BlockAndBuilder};
use monomorphize::{self, Instance}; use monomorphize::{self, Instance};
use rustc::ty::{self, Ty}; use rustc::ty::{self, Ty};
use rustc::mir; use rustc::mir;
@ -55,6 +55,7 @@ pub use self::create_scope_map::{create_mir_scopes, MirDebugScope};
pub use self::source_loc::start_emitting_source_locations; pub use self::source_loc::start_emitting_source_locations;
pub use self::metadata::create_global_var_metadata; pub use self::metadata::create_global_var_metadata;
pub use self::metadata::extend_scope_to_file; pub use self::metadata::extend_scope_to_file;
pub use self::source_loc::set_source_location;
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
const DW_TAG_auto_variable: c_uint = 0x100; const DW_TAG_auto_variable: c_uint = 0x100;
@ -65,7 +66,6 @@ const DW_TAG_arg_variable: c_uint = 0x101;
pub struct CrateDebugContext<'tcx> { pub struct CrateDebugContext<'tcx> {
llcontext: ContextRef, llcontext: ContextRef,
builder: DIBuilderRef, builder: DIBuilderRef,
current_debug_location: Cell<InternalDebugLocation>,
created_files: RefCell<FxHashMap<String, DIFile>>, created_files: RefCell<FxHashMap<String, DIFile>>,
created_enum_disr_types: RefCell<FxHashMap<(DefId, layout::Integer), DIType>>, created_enum_disr_types: RefCell<FxHashMap<(DefId, layout::Integer), DIType>>,
@ -83,40 +83,33 @@ impl<'tcx> CrateDebugContext<'tcx> {
let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) };
// DIBuilder inherits context from the module, so we'd better use the same one // DIBuilder inherits context from the module, so we'd better use the same one
let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
return CrateDebugContext { CrateDebugContext {
llcontext: llcontext, llcontext: llcontext,
builder: builder, builder: builder,
current_debug_location: Cell::new(InternalDebugLocation::UnknownLocation),
created_files: RefCell::new(FxHashMap()), created_files: RefCell::new(FxHashMap()),
created_enum_disr_types: RefCell::new(FxHashMap()), created_enum_disr_types: RefCell::new(FxHashMap()),
type_map: RefCell::new(TypeMap::new()), type_map: RefCell::new(TypeMap::new()),
namespace_map: RefCell::new(DefIdMap()), namespace_map: RefCell::new(DefIdMap()),
composite_types_completed: RefCell::new(FxHashSet()), composite_types_completed: RefCell::new(FxHashSet()),
}; }
} }
} }
pub enum FunctionDebugContext { pub enum FunctionDebugContext {
RegularContext(Box<FunctionDebugContextData>), RegularContext(FunctionDebugContextData),
DebugInfoDisabled, DebugInfoDisabled,
FunctionWithoutDebugInfo, FunctionWithoutDebugInfo,
} }
impl FunctionDebugContext { impl FunctionDebugContext {
fn get_ref<'a>(&'a self, fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData {
span: Span)
-> &'a FunctionDebugContextData {
match *self { match *self {
FunctionDebugContext::RegularContext(box ref data) => data, FunctionDebugContext::RegularContext(ref data) => data,
FunctionDebugContext::DebugInfoDisabled => { FunctionDebugContext::DebugInfoDisabled => {
span_bug!(span, span_bug!(span, "{}", FunctionDebugContext::debuginfo_disabled_message());
"{}",
FunctionDebugContext::debuginfo_disabled_message());
} }
FunctionDebugContext::FunctionWithoutDebugInfo => { FunctionDebugContext::FunctionWithoutDebugInfo => {
span_bug!(span, span_bug!(span, "{}", FunctionDebugContext::should_be_ignored_message());
"{}",
FunctionDebugContext::should_be_ignored_message());
} }
} }
} }
@ -134,7 +127,6 @@ impl FunctionDebugContext {
pub struct FunctionDebugContextData { pub struct FunctionDebugContextData {
fn_metadata: DISubprogram, fn_metadata: DISubprogram,
source_locations_enabled: Cell<bool>, source_locations_enabled: Cell<bool>,
source_location_override: Cell<bool>,
} }
pub enum VariableAccess<'a> { pub enum VariableAccess<'a> {
@ -197,18 +189,6 @@ pub fn finalize(cx: &CrateContext) {
}; };
} }
/// Creates a function-specific debug context for a function w/o debuginfo.
pub fn empty_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>)
-> FunctionDebugContext {
if cx.sess().opts.debuginfo == NoDebugInfo {
return FunctionDebugContext::DebugInfoDisabled;
}
// Clear the debug location so we don't assign them in the function prelude.
source_loc::set_debug_location(cx, None, UnknownLocation);
FunctionDebugContext::FunctionWithoutDebugInfo
}
/// Creates the function-specific debug context. /// Creates the function-specific debug context.
/// ///
/// Returns the FunctionDebugContext for the function which holds state needed /// Returns the FunctionDebugContext for the function which holds state needed
@ -225,15 +205,18 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
return FunctionDebugContext::DebugInfoDisabled; return FunctionDebugContext::DebugInfoDisabled;
} }
// Clear the debug location so we don't assign them in the function prelude. for attr in cx.tcx().get_attrs(instance.def).iter() {
// Do this here already, in case we do an early exit from this function. if attr.check_name("no_debug") {
source_loc::set_debug_location(cx, None, UnknownLocation); return FunctionDebugContext::FunctionWithoutDebugInfo;
}
}
let containing_scope = get_containing_scope(cx, instance); let containing_scope = get_containing_scope(cx, instance);
let span = mir.span; let span = mir.span;
// This can be the case for functions inlined from another crate // This can be the case for functions inlined from another crate
if span == syntax_pos::DUMMY_SP { if span == syntax_pos::DUMMY_SP {
// FIXME(simulacrum): Probably can't happen; remove.
return FunctionDebugContext::FunctionWithoutDebugInfo; return FunctionDebugContext::FunctionWithoutDebugInfo;
} }
@ -293,10 +276,9 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}; };
// Initialize fn debug context (including scope map and namespace map) // Initialize fn debug context (including scope map and namespace map)
let fn_debug_context = box FunctionDebugContextData { let fn_debug_context = FunctionDebugContextData {
fn_metadata: fn_metadata, fn_metadata: fn_metadata,
source_locations_enabled: Cell::new(false), source_locations_enabled: Cell::new(false),
source_location_override: Cell::new(false),
}; };
return FunctionDebugContext::RegularContext(fn_debug_context); return FunctionDebugContext::RegularContext(fn_debug_context);
@ -441,14 +423,15 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
} }
} }
pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn declare_local<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
variable_name: ast::Name, dbg_context: &FunctionDebugContext,
variable_type: Ty<'tcx>, variable_name: ast::Name,
scope_metadata: DIScope, variable_type: Ty<'tcx>,
variable_access: VariableAccess, scope_metadata: DIScope,
variable_kind: VariableKind, variable_access: VariableAccess,
span: Span) { variable_kind: VariableKind,
let cx: &CrateContext = bcx.ccx(); span: Span) {
let cx = bcx.ccx;
let file = span_start(cx, span).file; let file = span_start(cx, span).file;
let filename = file.name.clone(); let filename = file.name.clone();
@ -483,10 +466,10 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
align as u64, align as u64,
) )
}; };
source_loc::set_debug_location(cx, None, source_loc::set_debug_location(bcx,
InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize()));
unsafe { unsafe {
let debug_loc = llvm::LLVMGetCurrentDebugLocation(cx.raw_builder()); let debug_loc = llvm::LLVMGetCurrentDebugLocation(bcx.llbuilder);
let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
DIB(cx), DIB(cx),
alloca, alloca,
@ -494,38 +477,18 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
address_operations.as_ptr(), address_operations.as_ptr(),
address_operations.len() as c_uint, address_operations.len() as c_uint,
debug_loc, debug_loc,
bcx.llbb); bcx.llbb());
llvm::LLVMSetInstDebugLocation(::build::B(bcx).llbuilder, instr); llvm::LLVMSetInstDebugLocation(bcx.llbuilder, instr);
} }
} }
} }
match variable_kind { match variable_kind {
ArgumentVariable(_) | CapturedVariable => { ArgumentVariable(_) | CapturedVariable => {
assert!(!bcx.fcx assert!(!dbg_context.get_ref(span).source_locations_enabled.get());
.debug_context source_loc::set_debug_location(bcx, UnknownLocation);
.get_ref(span)
.source_locations_enabled
.get());
source_loc::set_debug_location(cx, None, UnknownLocation);
} }
_ => { /* nothing to do */ } _ => { /* nothing to do */ }
} }
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DebugLoc {
ScopeAt(DIScope, Span),
None
}
impl DebugLoc {
pub fn apply(self, fcx: &FunctionContext) {
source_loc::set_source_location(fcx, None, self);
}
pub fn apply_to_bcx(self, bcx: &BlockAndBuilder) {
source_loc::set_source_location(bcx.fcx(), Some(bcx), self);
}
}

View file

@ -11,57 +11,40 @@
use self::InternalDebugLocation::*; use self::InternalDebugLocation::*;
use super::utils::{debug_context, span_start}; use super::utils::{debug_context, span_start};
use super::metadata::{UNKNOWN_COLUMN_NUMBER}; use super::metadata::UNKNOWN_COLUMN_NUMBER;
use super::{FunctionDebugContext, DebugLoc}; use super::FunctionDebugContext;
use llvm; use llvm;
use llvm::debuginfo::DIScope; use llvm::debuginfo::DIScope;
use builder::Builder; use builder::Builder;
use common::{CrateContext, FunctionContext};
use libc::c_uint; use libc::c_uint;
use std::ptr; use std::ptr;
use syntax_pos::Pos; use syntax_pos::{Span, Pos};
/// Sets the current debug location at the beginning of the span. /// Sets the current debug location at the beginning of the span.
/// ///
/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). /// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...).
pub fn set_source_location(fcx: &FunctionContext, pub fn set_source_location(
builder: Option<&Builder>, debug_context: &FunctionDebugContext, builder: &Builder, scope: DIScope, span: Span
debug_loc: DebugLoc) { ) {
let builder = builder.map(|b| b.llbuilder); let function_debug_context = match *debug_context {
let function_debug_context = match fcx.debug_context {
FunctionDebugContext::DebugInfoDisabled => return, FunctionDebugContext::DebugInfoDisabled => return,
FunctionDebugContext::FunctionWithoutDebugInfo => { FunctionDebugContext::FunctionWithoutDebugInfo => {
set_debug_location(fcx.ccx, builder, UnknownLocation); set_debug_location(builder, UnknownLocation);
return; return;
} }
FunctionDebugContext::RegularContext(box ref data) => data FunctionDebugContext::RegularContext(ref data) => data
}; };
if function_debug_context.source_location_override.get() {
// Just ignore any attempts to set a new debug location while
// the override is active.
return;
}
let dbg_loc = if function_debug_context.source_locations_enabled.get() { let dbg_loc = if function_debug_context.source_locations_enabled.get() {
let (scope, span) = match debug_loc { debug!("set_source_location: {}", builder.ccx.sess().codemap().span_to_string(span));
DebugLoc::ScopeAt(scope, span) => (scope, span), let loc = span_start(builder.ccx, span);
DebugLoc::None => {
set_debug_location(fcx.ccx, builder, UnknownLocation);
return;
}
};
debug!("set_source_location: {}",
fcx.ccx.sess().codemap().span_to_string(span));
let loc = span_start(fcx.ccx, span);
InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) InternalDebugLocation::new(scope, loc.line, loc.col.to_usize())
} else { } else {
UnknownLocation UnknownLocation
}; };
set_debug_location(fcx.ccx, builder, dbg_loc); set_debug_location(builder, dbg_loc);
} }
/// Enables emitting source locations for the given functions. /// Enables emitting source locations for the given functions.
@ -70,9 +53,9 @@ pub fn set_source_location(fcx: &FunctionContext,
/// they are disabled when beginning to translate a new function. This functions /// they are disabled when beginning to translate a new function. This functions
/// switches source location emitting on and must therefore be called before the /// switches source location emitting on and must therefore be called before the
/// first real statement/expression of the function is translated. /// first real statement/expression of the function is translated.
pub fn start_emitting_source_locations(fcx: &FunctionContext) { pub fn start_emitting_source_locations(dbg_context: &FunctionDebugContext) {
match fcx.debug_context { match *dbg_context {
FunctionDebugContext::RegularContext(box ref data) => { FunctionDebugContext::RegularContext(ref data) => {
data.source_locations_enabled.set(true) data.source_locations_enabled.set(true)
}, },
_ => { /* safe to ignore */ } _ => { /* safe to ignore */ }
@ -96,15 +79,7 @@ impl InternalDebugLocation {
} }
} }
pub fn set_debug_location(cx: &CrateContext, pub fn set_debug_location(builder: &Builder, debug_location: InternalDebugLocation) {
builder: Option<llvm::BuilderRef>,
debug_location: InternalDebugLocation) {
if builder.is_none() {
if debug_location == debug_context(cx).current_debug_location.get() {
return;
}
}
let metadata_node = match debug_location { let metadata_node = match debug_location {
KnownLocation { scope, line, .. } => { KnownLocation { scope, line, .. } => {
// Always set the column to zero like Clang and GCC // Always set the column to zero like Clang and GCC
@ -113,7 +88,7 @@ pub fn set_debug_location(cx: &CrateContext,
unsafe { unsafe {
llvm::LLVMRustDIBuilderCreateDebugLocation( llvm::LLVMRustDIBuilderCreateDebugLocation(
debug_context(cx).llcontext, debug_context(builder.ccx).llcontext,
line as c_uint, line as c_uint,
col as c_uint, col as c_uint,
scope, scope,
@ -126,12 +101,7 @@ pub fn set_debug_location(cx: &CrateContext,
} }
}; };
if builder.is_none() {
debug_context(cx).current_debug_location.set(debug_location);
}
let builder = builder.unwrap_or_else(|| cx.raw_builder());
unsafe { unsafe {
llvm::LLVMSetCurrentDebugLocation(builder, metadata_node); llvm::LLVMSetCurrentDebugLocation(builder.llbuilder, metadata_node);
} }
} }

View file

@ -19,13 +19,11 @@ use llvm::{ValueRef, get_param};
use middle::lang_items::ExchangeFreeFnLangItem; use middle::lang_items::ExchangeFreeFnLangItem;
use rustc::ty::subst::{Substs}; use rustc::ty::subst::{Substs};
use rustc::traits; use rustc::traits;
use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; use rustc::ty::{self, AdtKind, Ty, TypeFoldable};
use adt; use adt;
use base::*; use base::*;
use build::*; use callee::Callee;
use callee::{Callee};
use common::*; use common::*;
use debuginfo::DebugLoc;
use machine::*; use machine::*;
use monomorphize; use monomorphize;
use trans_item::TransItem; use trans_item::TransItem;
@ -34,69 +32,50 @@ use type_of::{type_of, sizing_type_of, align_of};
use type_::Type; use type_::Type;
use value::Value; use value::Value;
use Disr; use Disr;
use cleanup::CleanupScope;
use arena::TypedArena;
use syntax_pos::DUMMY_SP; use syntax_pos::DUMMY_SP;
pub fn trans_exchange_free_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn trans_exchange_free_dyn<'a, 'tcx>(
v: ValueRef, bcx: &BlockAndBuilder<'a, 'tcx>,
size: ValueRef, v: ValueRef,
align: ValueRef, size: ValueRef,
debug_loc: DebugLoc) align: ValueRef
-> Block<'blk, 'tcx> { ) {
let _icx = push_ctxt("trans_exchange_free");
let def_id = langcall(bcx.tcx(), None, "", ExchangeFreeFnLangItem); let def_id = langcall(bcx.tcx(), None, "", ExchangeFreeFnLangItem);
let args = [PointerCast(bcx, v, Type::i8p(bcx.ccx())), size, align]; let args = [bcx.pointercast(v, Type::i8p(bcx.ccx)), size, align];
Callee::def(bcx.ccx(), def_id, bcx.tcx().intern_substs(&[])) let callee = Callee::def(bcx.ccx, def_id, bcx.tcx().intern_substs(&[]));
.call(bcx, debug_loc, &args, None).bcx
let ccx = bcx.ccx;
let fn_ty = callee.direct_fn_type(ccx, &[]);
let llret = bcx.call(callee.reify(ccx), &args[..], None);
fn_ty.apply_attrs_callsite(llret);
} }
pub fn trans_exchange_free<'blk, 'tcx>(cx: Block<'blk, 'tcx>, pub fn trans_exchange_free_ty<'a, 'tcx>(
v: ValueRef, bcx: &BlockAndBuilder<'a, 'tcx>, ptr: ValueRef, content_ty: Ty<'tcx>
size: u64, ) {
align: u32, assert!(bcx.ccx.shared().type_is_sized(content_ty));
debug_loc: DebugLoc) let sizing_type = sizing_type_of(bcx.ccx, content_ty);
-> Block<'blk, 'tcx> { let content_size = llsize_of_alloc(bcx.ccx, sizing_type);
trans_exchange_free_dyn(cx,
v,
C_uint(cx.ccx(), size),
C_uint(cx.ccx(), align),
debug_loc)
}
pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ptr: ValueRef,
content_ty: Ty<'tcx>,
debug_loc: DebugLoc)
-> Block<'blk, 'tcx> {
assert!(type_is_sized(bcx.ccx().tcx(), content_ty));
let sizing_type = sizing_type_of(bcx.ccx(), content_ty);
let content_size = llsize_of_alloc(bcx.ccx(), sizing_type);
// `Box<ZeroSizeType>` does not allocate. // `Box<ZeroSizeType>` does not allocate.
if content_size != 0 { if content_size != 0 {
let content_align = align_of(bcx.ccx(), content_ty); let content_align = align_of(bcx.ccx, content_ty);
trans_exchange_free(bcx, ptr, content_size, content_align, debug_loc) let ccx = bcx.ccx;
} else { trans_exchange_free_dyn(bcx, ptr, C_uint(ccx, content_size), C_uint(ccx, content_align));
bcx
} }
} }
pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pub fn get_drop_glue_type<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Ty<'tcx> {
ty: Ty<'tcx>) -> bool {
tcx.type_needs_drop_given_env(ty, &tcx.empty_parameter_environment())
}
pub fn get_drop_glue_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
t: Ty<'tcx>) -> Ty<'tcx> {
assert!(t.is_normalized_for_trans()); assert!(t.is_normalized_for_trans());
let t = tcx.erase_regions(&t); let t = scx.tcx().erase_regions(&t);
// Even if there is no dtor for t, there might be one deeper down and we // Even if there is no dtor for t, there might be one deeper down and we
// might need to pass in the vtable ptr. // might need to pass in the vtable ptr.
if !type_is_sized(tcx, t) { if !scx.type_is_sized(t) {
return t; return t;
} }
@ -109,17 +88,16 @@ pub fn get_drop_glue_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// returned `tcx.types.i8` does not appear unsound. The impact on // returned `tcx.types.i8` does not appear unsound. The impact on
// code quality is unknown at this time.) // code quality is unknown at this time.)
if !type_needs_drop(tcx, t) { if !scx.type_needs_drop(t) {
return tcx.types.i8; return scx.tcx().types.i8;
} }
match t.sty { match t.sty {
ty::TyBox(typ) if !type_needs_drop(tcx, typ) ty::TyBox(typ) if !scx.type_needs_drop(typ) && scx.type_is_sized(typ) => {
&& type_is_sized(tcx, typ) => { scx.tcx().infer_ctxt(None, None, traits::Reveal::All).enter(|infcx| {
tcx.infer_ctxt(None, None, traits::Reveal::All).enter(|infcx| {
let layout = t.layout(&infcx).unwrap(); let layout = t.layout(&infcx).unwrap();
if layout.size(&tcx.data_layout).bytes() == 0 { if layout.size(&scx.tcx().data_layout).bytes() == 0 {
// `Box<ZeroSizeType>` does not allocate. // `Box<ZeroSizeType>` does not allocate.
tcx.types.i8 scx.tcx().types.i8
} else { } else {
t t
} }
@ -129,56 +107,37 @@ pub fn get_drop_glue_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
} }
} }
pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, fn drop_ty<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, v: ValueRef, t: Ty<'tcx>) {
v: ValueRef, call_drop_glue(bcx, v, t, false, None)
t: Ty<'tcx>,
debug_loc: DebugLoc) -> Block<'blk, 'tcx> {
drop_ty_core(bcx, v, t, debug_loc, false)
} }
pub fn drop_ty_core<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn call_drop_glue<'a, 'tcx>(
v: ValueRef, bcx: &BlockAndBuilder<'a, 'tcx>,
t: Ty<'tcx>, v: ValueRef,
debug_loc: DebugLoc, t: Ty<'tcx>,
skip_dtor: bool) skip_dtor: bool,
-> Block<'blk, 'tcx> { funclet: Option<&'a Funclet>,
) {
// NB: v is an *alias* of type t here, not a direct value. // NB: v is an *alias* of type t here, not a direct value.
debug!("drop_ty_core(t={:?}, skip_dtor={})", t, skip_dtor); debug!("call_drop_glue(t={:?}, skip_dtor={})", t, skip_dtor);
let _icx = push_ctxt("drop_ty"); if bcx.ccx.shared().type_needs_drop(t) {
if bcx.fcx.type_needs_drop(t) { let ccx = bcx.ccx;
let ccx = bcx.ccx();
let g = if skip_dtor { let g = if skip_dtor {
DropGlueKind::TyContents(t) DropGlueKind::TyContents(t)
} else { } else {
DropGlueKind::Ty(t) DropGlueKind::Ty(t)
}; };
let glue = get_drop_glue_core(ccx, g); let glue = get_drop_glue_core(ccx, g);
let glue_type = get_drop_glue_type(ccx.tcx(), t); let glue_type = get_drop_glue_type(ccx.shared(), t);
let ptr = if glue_type != t { let ptr = if glue_type != t {
PointerCast(bcx, v, type_of(ccx, glue_type).ptr_to()) bcx.pointercast(v, type_of(ccx, glue_type).ptr_to())
} else { } else {
v v
}; };
// No drop-hint ==> call standard drop glue // No drop-hint ==> call standard drop glue
Call(bcx, glue, &[ptr], debug_loc); bcx.call(glue, &[ptr], funclet.map(|b| b.bundle()));
} }
bcx
}
pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
v: ValueRef,
t: Ty<'tcx>,
debug_loc: DebugLoc,
skip_dtor: bool)
-> Block<'blk, 'tcx> {
let _icx = push_ctxt("drop_ty_immediate");
let vp = alloc_ty(bcx, t, "");
call_lifetime_start(bcx, vp);
store_ty(bcx, v, vp, t);
let bcx = drop_ty_core(bcx, vp, t, debug_loc, skip_dtor);
call_lifetime_end(bcx, vp);
bcx
} }
pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ValueRef { pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ValueRef {
@ -212,9 +171,8 @@ impl<'tcx> DropGlueKind<'tcx> {
} }
} }
fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKind<'tcx>) -> ValueRef {
g: DropGlueKind<'tcx>) -> ValueRef { let g = g.map_ty(|t| get_drop_glue_type(ccx.shared(), t));
let g = g.map_ty(|t| get_drop_glue_type(ccx.tcx(), t));
match ccx.drop_glues().borrow().get(&g) { match ccx.drop_glues().borrow().get(&g) {
Some(&(glue, _)) => glue, Some(&(glue, _)) => glue,
None => { None => {
@ -226,17 +184,12 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
} }
} }
pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKind<'tcx>) {
g: DropGlueKind<'tcx>) { assert_eq!(g.ty(), get_drop_glue_type(ccx.shared(), g.ty()));
let tcx = ccx.tcx(); let (llfn, _) = ccx.drop_glues().borrow().get(&g).unwrap().clone();
assert_eq!(g.ty(), get_drop_glue_type(tcx, g.ty()));
let (llfn, fn_ty) = ccx.drop_glues().borrow().get(&g).unwrap().clone();
let (arena, fcx): (TypedArena<_>, FunctionContext); let fcx = FunctionContext::new(ccx, llfn);
arena = TypedArena::new(); let bcx = fcx.get_entry_block();
fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &arena);
let bcx = fcx.init(false);
ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1); ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1);
// All glue functions take values passed *by alias*; this is a // All glue functions take values passed *by alias*; this is a
@ -247,19 +200,91 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// llfn is expected be declared to take a parameter of the appropriate // llfn is expected be declared to take a parameter of the appropriate
// type, so we don't need to explicitly cast the function parameter. // type, so we don't need to explicitly cast the function parameter.
let bcx = make_drop_glue(bcx, get_param(llfn, 0), g); // NB: v0 is an *alias* of type t here, not a direct value.
fcx.finish(bcx, DebugLoc::None); // Only drop the value when it ... well, we used to check for
// non-null, (and maybe we need to continue doing so), but we now
// must definitely check for special bit-patterns corresponding to
// the special dtor markings.
let v0 = get_param(llfn, 0);
let t = g.ty();
let skip_dtor = match g {
DropGlueKind::Ty(_) => false,
DropGlueKind::TyContents(_) => true
};
let bcx = match t.sty {
ty::TyBox(content_ty) => {
// Support for TyBox is built-in and its drop glue is
// special. It may move to library and have Drop impl. As
// a safe-guard, assert TyBox not used with TyContents.
assert!(!skip_dtor);
if !bcx.ccx.shared().type_is_sized(content_ty) {
let llval = get_dataptr(&bcx, v0);
let llbox = bcx.load(llval);
drop_ty(&bcx, v0, content_ty);
// FIXME(#36457) -- we should pass unsized values to drop glue as two arguments
let info = get_meta(&bcx, v0);
let info = bcx.load(info);
let (llsize, llalign) = size_and_align_of_dst(&bcx, content_ty, info);
// `Box<ZeroSizeType>` does not allocate.
let needs_free = bcx.icmp(llvm::IntNE, llsize, C_uint(bcx.ccx, 0u64));
if const_to_opt_uint(needs_free) == Some(0) {
bcx
} else {
let next_cx = bcx.fcx().build_new_block("next");
let cond_cx = bcx.fcx().build_new_block("cond");
bcx.cond_br(needs_free, cond_cx.llbb(), next_cx.llbb());
trans_exchange_free_dyn(&cond_cx, llbox, llsize, llalign);
cond_cx.br(next_cx.llbb());
next_cx
}
} else {
let llval = v0;
let llbox = bcx.load(llval);
drop_ty(&bcx, llbox, content_ty);
trans_exchange_free_ty(&bcx, llbox, content_ty);
bcx
}
}
ty::TyDynamic(..) => {
// No support in vtable for distinguishing destroying with
// versus without calling Drop::drop. Assert caller is
// okay with always calling the Drop impl, if any.
// FIXME(#36457) -- we should pass unsized values to drop glue as two arguments
assert!(!skip_dtor);
let data_ptr = get_dataptr(&bcx, v0);
let vtable_ptr = bcx.load(get_meta(&bcx, v0));
let dtor = bcx.load(vtable_ptr);
bcx.call(dtor, &[bcx.pointercast(bcx.load(data_ptr), Type::i8p(bcx.ccx))], None);
bcx
}
ty::TyAdt(def, ..) if def.dtor_kind().is_present() && !skip_dtor => {
trans_custom_dtor(bcx, t, v0, def.is_union())
}
ty::TyAdt(def, ..) if def.is_union() => {
bcx
}
_ => {
if bcx.ccx.shared().type_needs_drop(t) {
drop_structural_ty(bcx, v0, t)
} else {
bcx
}
}
};
bcx.ret_void();
} }
fn trans_custom_dtor<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, fn trans_custom_dtor<'a, 'tcx>(mut bcx: BlockAndBuilder<'a, 'tcx>,
t: Ty<'tcx>, t: Ty<'tcx>,
v0: ValueRef, v0: ValueRef,
shallow_drop: bool) shallow_drop: bool)
-> Block<'blk, 'tcx> -> BlockAndBuilder<'a, 'tcx>
{ {
debug!("trans_custom_dtor t: {}", t); debug!("trans_custom_dtor t: {}", t);
let tcx = bcx.tcx(); let tcx = bcx.tcx();
let mut bcx = bcx;
let def = t.ty_adt_def().unwrap(); let def = t.ty_adt_def().unwrap();
@ -269,23 +294,23 @@ fn trans_custom_dtor<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// //
// FIXME (#14875) panic-in-drop semantics might be unsupported; we // FIXME (#14875) panic-in-drop semantics might be unsupported; we
// might well consider changing below to more direct code. // might well consider changing below to more direct code.
let contents_scope = bcx.fcx.push_custom_cleanup_scope();
// Issue #23611: schedule cleanup of contents, re-inspecting the // Issue #23611: schedule cleanup of contents, re-inspecting the
// discriminant (if any) in case of variant swap in drop code. // discriminant (if any) in case of variant swap in drop code.
if !shallow_drop { let contents_scope = if !shallow_drop {
bcx.fcx.schedule_drop_adt_contents(contents_scope, v0, t); bcx.fcx().schedule_drop_adt_contents(v0, t)
} } else {
CleanupScope::noop()
};
let (sized_args, unsized_args); let (sized_args, unsized_args);
let args: &[ValueRef] = if type_is_sized(tcx, t) { let args: &[ValueRef] = if bcx.ccx.shared().type_is_sized(t) {
sized_args = [v0]; sized_args = [v0];
&sized_args &sized_args
} else { } else {
// FIXME(#36457) -- we should pass unsized values to drop glue as two arguments // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments
unsized_args = [ unsized_args = [
Load(bcx, get_dataptr(bcx, v0)), bcx.load(get_dataptr(&bcx, v0)),
Load(bcx, get_meta(bcx, v0)) bcx.load(get_meta(&bcx, v0))
]; ];
&unsized_args &unsized_args
}; };
@ -294,39 +319,44 @@ fn trans_custom_dtor<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
def_id: tcx.lang_items.drop_trait().unwrap(), def_id: tcx.lang_items.drop_trait().unwrap(),
substs: tcx.mk_substs_trait(t, &[]) substs: tcx.mk_substs_trait(t, &[])
}); });
let vtbl = match fulfill_obligation(bcx.ccx().shared(), DUMMY_SP, trait_ref) { let vtbl = match fulfill_obligation(bcx.ccx.shared(), DUMMY_SP, trait_ref) {
traits::VtableImpl(data) => data, traits::VtableImpl(data) => data,
_ => bug!("dtor for {:?} is not an impl???", t) _ => bug!("dtor for {:?} is not an impl???", t)
}; };
let dtor_did = def.destructor().unwrap(); let dtor_did = def.destructor().unwrap();
bcx = Callee::def(bcx.ccx(), dtor_did, vtbl.substs) let callee = Callee::def(bcx.ccx, dtor_did, vtbl.substs);
.call(bcx, DebugLoc::None, args, None).bcx; let fn_ty = callee.direct_fn_type(bcx.ccx, &[]);
let llret;
bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, contents_scope) if let Some(landing_pad) = contents_scope.landing_pad {
let normal_bcx = bcx.fcx().build_new_block("normal-return");
llret = bcx.invoke(callee.reify(bcx.ccx), args, normal_bcx.llbb(), landing_pad, None);
bcx = normal_bcx;
} else {
llret = bcx.call(callee.reify(bcx.ccx), args, None);
}
fn_ty.apply_attrs_callsite(llret);
contents_scope.trans(&bcx);
bcx
} }
pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
t: Ty<'tcx>, info: ValueRef) t: Ty<'tcx>, info: ValueRef)
-> (ValueRef, ValueRef) { -> (ValueRef, ValueRef) {
debug!("calculate size of DST: {}; with lost info: {:?}", debug!("calculate size of DST: {}; with lost info: {:?}",
t, Value(info)); t, Value(info));
if type_is_sized(bcx.tcx(), t) { if bcx.ccx.shared().type_is_sized(t) {
let sizing_type = sizing_type_of(bcx.ccx(), t); let sizing_type = sizing_type_of(bcx.ccx, t);
let size = llsize_of_alloc(bcx.ccx(), sizing_type); let size = llsize_of_alloc(bcx.ccx, sizing_type);
let align = align_of(bcx.ccx(), t); let align = align_of(bcx.ccx, t);
debug!("size_and_align_of_dst t={} info={:?} size: {} align: {}", debug!("size_and_align_of_dst t={} info={:?} size: {} align: {}",
t, Value(info), size, align); t, Value(info), size, align);
let size = C_uint(bcx.ccx(), size); let size = C_uint(bcx.ccx, size);
let align = C_uint(bcx.ccx(), align); let align = C_uint(bcx.ccx, align);
return (size, align); return (size, align);
} }
if bcx.is_unreachable() {
let llty = Type::int(bcx.ccx());
return (C_undef(llty), C_undef(llty));
}
match t.sty { match t.sty {
ty::TyAdt(def, substs) => { ty::TyAdt(def, substs) => {
let ccx = bcx.ccx(); let ccx = bcx.ccx;
// First get the size of all statically known fields. // First get the size of all statically known fields.
// Don't use type_of::sizing_type_of because that expects t to be sized, // Don't use type_of::sizing_type_of because that expects t to be sized,
// and it also rounds up to alignment, which we want to avoid, // and it also rounds up to alignment, which we want to avoid,
@ -389,7 +419,7 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
// //
// `(size + (align-1)) & -align` // `(size + (align-1)) & -align`
let addend = bcx.sub(align, C_uint(bcx.ccx(), 1_u64)); let addend = bcx.sub(align, C_uint(bcx.ccx, 1_u64));
let size = bcx.and(bcx.add(size, addend), bcx.neg(align)); let size = bcx.and(bcx.add(size, addend), bcx.neg(align));
(size, align) (size, align)
@ -397,7 +427,7 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
ty::TyDynamic(..) => { ty::TyDynamic(..) => {
// info points to the vtable and the second entry in the vtable is the // info points to the vtable and the second entry in the vtable is the
// dynamic size of the object. // dynamic size of the object.
let info = bcx.pointercast(info, Type::int(bcx.ccx()).ptr_to()); let info = bcx.pointercast(info, Type::int(bcx.ccx).ptr_to());
let size_ptr = bcx.gepi(info, &[1]); let size_ptr = bcx.gepi(info, &[1]);
let align_ptr = bcx.gepi(info, &[2]); let align_ptr = bcx.gepi(info, &[2]);
(bcx.load(size_ptr), bcx.load(align_ptr)) (bcx.load(size_ptr), bcx.load(align_ptr))
@ -406,126 +436,40 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
let unit_ty = t.sequence_element_type(bcx.tcx()); let unit_ty = t.sequence_element_type(bcx.tcx());
// The info in this case is the length of the str, so the size is that // The info in this case is the length of the str, so the size is that
// times the unit size. // times the unit size.
let llunit_ty = sizing_type_of(bcx.ccx(), unit_ty); let llunit_ty = sizing_type_of(bcx.ccx, unit_ty);
let unit_align = llalign_of_min(bcx.ccx(), llunit_ty); let unit_align = llalign_of_min(bcx.ccx, llunit_ty);
let unit_size = llsize_of_alloc(bcx.ccx(), llunit_ty); let unit_size = llsize_of_alloc(bcx.ccx, llunit_ty);
(bcx.mul(info, C_uint(bcx.ccx(), unit_size)), (bcx.mul(info, C_uint(bcx.ccx, unit_size)),
C_uint(bcx.ccx(), unit_align)) C_uint(bcx.ccx, unit_align))
} }
_ => bug!("Unexpected unsized type, found {}", t) _ => bug!("Unexpected unsized type, found {}", t)
} }
} }
fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
v0: ValueRef,
g: DropGlueKind<'tcx>)
-> Block<'blk, 'tcx> {
let t = g.ty();
let skip_dtor = match g { DropGlueKind::Ty(_) => false, DropGlueKind::TyContents(_) => true };
// NB: v0 is an *alias* of type t here, not a direct value.
let _icx = push_ctxt("make_drop_glue");
// Only drop the value when it ... well, we used to check for
// non-null, (and maybe we need to continue doing so), but we now
// must definitely check for special bit-patterns corresponding to
// the special dtor markings.
match t.sty {
ty::TyBox(content_ty) => {
// Support for TyBox is built-in and its drop glue is
// special. It may move to library and have Drop impl. As
// a safe-guard, assert TyBox not used with TyContents.
assert!(!skip_dtor);
if !type_is_sized(bcx.tcx(), content_ty) {
let llval = get_dataptr(bcx, v0);
let llbox = Load(bcx, llval);
let bcx = drop_ty(bcx, v0, content_ty, DebugLoc::None);
// FIXME(#36457) -- we should pass unsized values to drop glue as two arguments
let info = get_meta(bcx, v0);
let info = Load(bcx, info);
let (llsize, llalign) =
size_and_align_of_dst(&bcx.build(), content_ty, info);
// `Box<ZeroSizeType>` does not allocate.
let needs_free = ICmp(bcx,
llvm::IntNE,
llsize,
C_uint(bcx.ccx(), 0u64),
DebugLoc::None);
with_cond(bcx, needs_free, |bcx| {
trans_exchange_free_dyn(bcx, llbox, llsize, llalign, DebugLoc::None)
})
} else {
let llval = v0;
let llbox = Load(bcx, llval);
let bcx = drop_ty(bcx, llbox, content_ty, DebugLoc::None);
trans_exchange_free_ty(bcx, llbox, content_ty, DebugLoc::None)
}
}
ty::TyDynamic(..) => {
// No support in vtable for distinguishing destroying with
// versus without calling Drop::drop. Assert caller is
// okay with always calling the Drop impl, if any.
// FIXME(#36457) -- we should pass unsized values to drop glue as two arguments
assert!(!skip_dtor);
let data_ptr = get_dataptr(bcx, v0);
let vtable_ptr = Load(bcx, get_meta(bcx, v0));
let dtor = Load(bcx, vtable_ptr);
Call(bcx,
dtor,
&[PointerCast(bcx, Load(bcx, data_ptr), Type::i8p(bcx.ccx()))],
DebugLoc::None);
bcx
}
ty::TyAdt(def, ..) if def.dtor_kind().is_present() && !skip_dtor => {
trans_custom_dtor(bcx, t, v0, def.is_union())
}
ty::TyAdt(def, ..) if def.is_union() => {
bcx
}
_ => {
if bcx.fcx.type_needs_drop(t) {
drop_structural_ty(bcx, v0, t)
} else {
bcx
}
}
}
}
// Iterates through the elements of a structural type, dropping them. // Iterates through the elements of a structural type, dropping them.
fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, fn drop_structural_ty<'a, 'tcx>(cx: BlockAndBuilder<'a, 'tcx>,
av: ValueRef, av: ValueRef,
t: Ty<'tcx>) t: Ty<'tcx>)
-> Block<'blk, 'tcx> { -> BlockAndBuilder<'a, 'tcx> {
let _icx = push_ctxt("drop_structural_ty"); fn iter_variant<'a, 'tcx>(cx: &BlockAndBuilder<'a, 'tcx>,
t: Ty<'tcx>,
fn iter_variant<'blk, 'tcx>(cx: Block<'blk, 'tcx>, av: adt::MaybeSizedValue,
t: Ty<'tcx>, variant: &'tcx ty::VariantDef,
av: adt::MaybeSizedValue, substs: &Substs<'tcx>) {
variant: &'tcx ty::VariantDef,
substs: &Substs<'tcx>)
-> Block<'blk, 'tcx> {
let _icx = push_ctxt("iter_variant");
let tcx = cx.tcx(); let tcx = cx.tcx();
let mut cx = cx;
for (i, field) in variant.fields.iter().enumerate() { for (i, field) in variant.fields.iter().enumerate() {
let arg = monomorphize::field_ty(tcx, substs, field); let arg = monomorphize::field_ty(tcx, substs, field);
cx = drop_ty(cx, let field_ptr = adt::trans_field_ptr(&cx, t, av, Disr::from(variant.disr_val), i);
adt::trans_field_ptr(cx, t, av, Disr::from(variant.disr_val), i), drop_ty(&cx, field_ptr, arg);
arg, DebugLoc::None);
} }
return cx;
} }
let value = if type_is_sized(cx.tcx(), t) { let value = if cx.ccx.shared().type_is_sized(t) {
adt::MaybeSizedValue::sized(av) adt::MaybeSizedValue::sized(av)
} else { } else {
// FIXME(#36457) -- we should pass unsized values as two arguments // FIXME(#36457) -- we should pass unsized values as two arguments
let data = Load(cx, get_dataptr(cx, av)); let data = cx.load(get_dataptr(&cx, av));
let info = Load(cx, get_meta(cx, av)); let info = cx.load(get_meta(&cx, av));
adt::MaybeSizedValue::unsized_(data, info) adt::MaybeSizedValue::unsized_(data, info)
}; };
@ -533,67 +477,65 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
match t.sty { match t.sty {
ty::TyClosure(def_id, substs) => { ty::TyClosure(def_id, substs) => {
for (i, upvar_ty) in substs.upvar_tys(def_id, cx.tcx()).enumerate() { for (i, upvar_ty) in substs.upvar_tys(def_id, cx.tcx()).enumerate() {
let llupvar = adt::trans_field_ptr(cx, t, value, Disr(0), i); let llupvar = adt::trans_field_ptr(&cx, t, value, Disr(0), i);
cx = drop_ty(cx, llupvar, upvar_ty, DebugLoc::None); drop_ty(&cx, llupvar, upvar_ty);
} }
} }
ty::TyArray(_, n) => { ty::TyArray(_, n) => {
let base = get_dataptr(cx, value.value); let base = get_dataptr(&cx, value.value);
let len = C_uint(cx.ccx(), n); let len = C_uint(cx.ccx, n);
let unit_ty = t.sequence_element_type(cx.tcx()); let unit_ty = t.sequence_element_type(cx.tcx());
cx = tvec::slice_for_each(cx, base, unit_ty, len, cx = tvec::slice_for_each(&cx, base, unit_ty, len, |bb, vv| drop_ty(bb, vv, unit_ty));
|bb, vv| drop_ty(bb, vv, unit_ty, DebugLoc::None));
} }
ty::TySlice(_) | ty::TyStr => { ty::TySlice(_) | ty::TyStr => {
let unit_ty = t.sequence_element_type(cx.tcx()); let unit_ty = t.sequence_element_type(cx.tcx());
cx = tvec::slice_for_each(cx, value.value, unit_ty, value.meta, cx = tvec::slice_for_each(&cx, value.value, unit_ty, value.meta,
|bb, vv| drop_ty(bb, vv, unit_ty, DebugLoc::None)); |bb, vv| drop_ty(bb, vv, unit_ty));
} }
ty::TyTuple(ref args) => { ty::TyTuple(ref args) => {
for (i, arg) in args.iter().enumerate() { for (i, arg) in args.iter().enumerate() {
let llfld_a = adt::trans_field_ptr(cx, t, value, Disr(0), i); let llfld_a = adt::trans_field_ptr(&cx, t, value, Disr(0), i);
cx = drop_ty(cx, llfld_a, *arg, DebugLoc::None); drop_ty(&cx, llfld_a, *arg);
} }
} }
ty::TyAdt(adt, substs) => match adt.adt_kind() { ty::TyAdt(adt, substs) => match adt.adt_kind() {
AdtKind::Struct => { AdtKind::Struct => {
let VariantInfo { fields, discr } = VariantInfo::from_ty(cx.tcx(), t, None); let VariantInfo { fields, discr } = VariantInfo::from_ty(cx.tcx(), t, None);
for (i, &Field(_, field_ty)) in fields.iter().enumerate() { for (i, &Field(_, field_ty)) in fields.iter().enumerate() {
let llfld_a = adt::trans_field_ptr(cx, t, value, Disr::from(discr), i); let llfld_a = adt::trans_field_ptr(&cx, t, value, Disr::from(discr), i);
let val = if type_is_sized(cx.tcx(), field_ty) { let val = if cx.ccx.shared().type_is_sized(field_ty) {
llfld_a llfld_a
} else { } else {
// FIXME(#36457) -- we should pass unsized values as two arguments // FIXME(#36457) -- we should pass unsized values as two arguments
let scratch = alloc_ty(cx, field_ty, "__fat_ptr_iter"); let scratch = alloc_ty(&cx, field_ty, "__fat_ptr_iter");
Store(cx, llfld_a, get_dataptr(cx, scratch)); cx.store(llfld_a, get_dataptr(&cx, scratch));
Store(cx, value.meta, get_meta(cx, scratch)); cx.store(value.meta, get_meta(&cx, scratch));
scratch scratch
}; };
cx = drop_ty(cx, val, field_ty, DebugLoc::None); drop_ty(&cx, val, field_ty);
} }
} }
AdtKind::Union => { AdtKind::Union => {
bug!("Union in `glue::drop_structural_ty`"); bug!("Union in `glue::drop_structural_ty`");
} }
AdtKind::Enum => { AdtKind::Enum => {
let fcx = cx.fcx;
let ccx = fcx.ccx;
let n_variants = adt.variants.len(); let n_variants = adt.variants.len();
// NB: we must hit the discriminant first so that structural // NB: we must hit the discriminant first so that structural
// comparison know not to proceed when the discriminants differ. // comparison know not to proceed when the discriminants differ.
match adt::trans_switch(cx, t, av, false) { match adt::trans_switch(&cx, t, av, false) {
(adt::BranchKind::Single, None) => { (adt::BranchKind::Single, None) => {
if n_variants != 0 { if n_variants != 0 {
assert!(n_variants == 1); assert!(n_variants == 1);
cx = iter_variant(cx, t, adt::MaybeSizedValue::sized(av), iter_variant(&cx, t, adt::MaybeSizedValue::sized(av),
&adt.variants[0], substs); &adt.variants[0], substs);
} }
} }
(adt::BranchKind::Switch, Some(lldiscrim_a)) => { (adt::BranchKind::Switch, Some(lldiscrim_a)) => {
cx = drop_ty(cx, lldiscrim_a, cx.tcx().types.isize, DebugLoc::None); let tcx = cx.tcx();
drop_ty(&cx, lldiscrim_a, tcx.types.isize);
// Create a fall-through basic block for the "else" case of // Create a fall-through basic block for the "else" case of
// the switch instruction we're about to generate. Note that // the switch instruction we're about to generate. Note that
@ -608,27 +550,23 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
// from the outer function, and any other use case will only // from the outer function, and any other use case will only
// call this for an already-valid enum in which case the `ret // call this for an already-valid enum in which case the `ret
// void` will never be hit. // void` will never be hit.
let ret_void_cx = fcx.new_block("enum-iter-ret-void"); let ret_void_cx = cx.fcx().build_new_block("enum-iter-ret-void");
RetVoid(ret_void_cx, DebugLoc::None); ret_void_cx.ret_void();
let llswitch = Switch(cx, lldiscrim_a, ret_void_cx.llbb, n_variants); let llswitch = cx.switch(lldiscrim_a, ret_void_cx.llbb(), n_variants);
let next_cx = fcx.new_block("enum-iter-next"); let next_cx = cx.fcx().build_new_block("enum-iter-next");
for variant in &adt.variants { for variant in &adt.variants {
let variant_cx = fcx.new_block(&format!("enum-iter-variant-{}", let variant_cx_name = format!("enum-iter-variant-{}",
&variant.disr_val &variant.disr_val.to_string());
.to_string())); let variant_cx = cx.fcx().build_new_block(&variant_cx_name);
let case_val = adt::trans_case(cx, t, Disr::from(variant.disr_val)); let case_val = adt::trans_case(&cx, t, Disr::from(variant.disr_val));
AddCase(llswitch, case_val, variant_cx.llbb); variant_cx.add_case(llswitch, case_val, variant_cx.llbb());
let variant_cx = iter_variant(variant_cx, iter_variant(&variant_cx, t, value, variant, substs);
t, variant_cx.br(next_cx.llbb());
value,
variant,
substs);
Br(variant_cx, next_cx.llbb, DebugLoc::None);
} }
cx = next_cx; cx = next_cx;
} }
_ => ccx.sess().unimpl("value from adt::trans_switch in drop_structural_ty"), _ => cx.ccx.sess().unimpl("value from adt::trans_switch in drop_structural_ty"),
} }
} }
}, },

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,7 @@
#![feature(slice_patterns)] #![feature(slice_patterns)]
#![feature(staged_api)] #![feature(staged_api)]
#![feature(unicode)] #![feature(unicode)]
#![feature(conservative_impl_trait)]
use rustc::dep_graph::WorkProduct; use rustc::dep_graph::WorkProduct;
@ -95,8 +96,6 @@ mod asm;
mod assert_module_sources; mod assert_module_sources;
mod attributes; mod attributes;
mod base; mod base;
mod basic_block;
mod build;
mod builder; mod builder;
mod cabi_aarch64; mod cabi_aarch64;
mod cabi_arm; mod cabi_arm;

View file

@ -9,16 +9,11 @@
// except according to those terms. // except according to those terms.
use attributes; use attributes;
use arena::TypedArena;
use llvm::{ValueRef, get_params}; use llvm::{ValueRef, get_params};
use rustc::traits; use rustc::traits;
use abi::FnType; use callee::{Callee, CalleeData};
use base::*;
use build::*;
use callee::Callee;
use common::*; use common::*;
use consts; use consts;
use debuginfo::DebugLoc;
use declare; use declare;
use glue; use glue;
use machine; use machine;
@ -32,15 +27,15 @@ use rustc::ty;
const VTABLE_OFFSET: usize = 3; const VTABLE_OFFSET: usize = 3;
/// Extracts a method from a trait object's vtable, at the specified index. /// Extracts a method from a trait object's vtable, at the specified index.
pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn get_virtual_method<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
llvtable: ValueRef, llvtable: ValueRef,
vtable_index: usize) vtable_index: usize)
-> ValueRef { -> ValueRef {
// Load the data pointer from the object. // Load the data pointer from the object.
debug!("get_virtual_method(vtable_index={}, llvtable={:?})", debug!("get_virtual_method(vtable_index={}, llvtable={:?})",
vtable_index, Value(llvtable)); vtable_index, Value(llvtable));
Load(bcx, GEPi(bcx, llvtable, &[vtable_index + VTABLE_OFFSET])) bcx.load(bcx.gepi(llvtable, &[vtable_index + VTABLE_OFFSET]))
} }
/// Generate a shim function that allows an object type like `SomeTrait` to /// Generate a shim function that allows an object type like `SomeTrait` to
@ -67,36 +62,47 @@ pub fn get_virtual_method<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
callee: Callee<'tcx>) callee: Callee<'tcx>)
-> ValueRef { -> ValueRef {
let _icx = push_ctxt("trans_object_shim");
let tcx = ccx.tcx();
debug!("trans_object_shim({:?})", callee); debug!("trans_object_shim({:?})", callee);
let (sig, abi, function_name) = match callee.ty.sty { let function_name = match callee.ty.sty {
ty::TyFnDef(def_id, substs, f) => { ty::TyFnDef(def_id, substs, _) => {
let instance = Instance::new(def_id, substs); let instance = Instance::new(def_id, substs);
(&f.sig, f.abi, instance.symbol_name(ccx.shared())) instance.symbol_name(ccx.shared())
} }
_ => bug!() _ => bug!()
}; };
let sig = tcx.erase_late_bound_regions_and_normalize(sig);
let fn_ty = FnType::new(ccx, abi, &sig, &[]);
let llfn = declare::define_internal_fn(ccx, &function_name, callee.ty); let llfn = declare::define_internal_fn(ccx, &function_name, callee.ty);
attributes::set_frame_pointer_elimination(ccx, llfn); attributes::set_frame_pointer_elimination(ccx, llfn);
let (block_arena, fcx): (TypedArena<_>, FunctionContext); let fcx = FunctionContext::new(ccx, llfn);
block_arena = TypedArena::new(); let bcx = fcx.get_entry_block();
fcx = FunctionContext::new(ccx, llfn, fn_ty, None, &block_arena);
let mut bcx = fcx.init(false);
let dest = fcx.llretslotptr.get(); let mut llargs = get_params(fcx.llfn);
let llargs = get_params(fcx.llfn); let fn_ret = callee.ty.fn_ret();
bcx = callee.call(bcx, DebugLoc::None, let fn_ty = callee.direct_fn_type(ccx, &[]);
&llargs[fcx.fn_ty.ret.is_indirect() as usize..], dest).bcx;
fcx.finish(bcx, DebugLoc::None); let fn_ptr = match callee.data {
CalleeData::Virtual(idx) => {
let fn_ptr = get_virtual_method(&bcx,
llargs.remove(fn_ty.ret.is_indirect() as usize + 1), idx);
let llty = fn_ty.llvm_type(bcx.ccx).ptr_to();
bcx.pointercast(fn_ptr, llty)
},
_ => bug!("trans_object_shim called with non-virtual callee"),
};
let llret = bcx.call(fn_ptr, &llargs, None);
fn_ty.apply_attrs_callsite(llret);
if fn_ret.0.is_never() {
bcx.unreachable();
} else {
if fn_ty.ret.is_indirect() || fn_ty.ret.is_ignore() {
bcx.ret_void();
} else {
bcx.ret(llret);
}
}
llfn llfn
} }
@ -115,7 +121,6 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-> ValueRef -> ValueRef
{ {
let tcx = ccx.tcx(); let tcx = ccx.tcx();
let _icx = push_ctxt("meth::get_vtable");
debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref); debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref);

View file

@ -16,31 +16,30 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind}; use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, LvalueContext}; use rustc::mir::visit::{Visitor, LvalueContext};
use rustc::mir::traversal; use rustc::mir::traversal;
use common::{self, Block, BlockAndBuilder}; use common;
use glue; use super::MirContext;
use super::rvalue; use super::rvalue;
pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>, pub fn lvalue_locals<'a, 'tcx>(mircx: &MirContext<'a, 'tcx>) -> BitVector {
mir: &mir::Mir<'tcx>) -> BitVector { let mir = mircx.mir;
let bcx = bcx.build(); let mut analyzer = LocalAnalyzer::new(mircx);
let mut analyzer = LocalAnalyzer::new(mir, &bcx);
analyzer.visit_mir(mir); analyzer.visit_mir(mir);
for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() { for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
let ty = bcx.monomorphize(&ty); let ty = mircx.monomorphize(&ty);
debug!("local {} has type {:?}", index, ty); debug!("local {} has type {:?}", index, ty);
if ty.is_scalar() || if ty.is_scalar() ||
ty.is_unique() || ty.is_unique() ||
ty.is_region_ptr() || ty.is_region_ptr() ||
ty.is_simd() || ty.is_simd() ||
common::type_is_zero_size(bcx.ccx(), ty) common::type_is_zero_size(mircx.ccx, ty)
{ {
// These sorts of types are immediates that we can store // These sorts of types are immediates that we can store
// in an ValueRef without an alloca. // in an ValueRef without an alloca.
assert!(common::type_is_immediate(bcx.ccx(), ty) || assert!(common::type_is_immediate(mircx.ccx, ty) ||
common::type_is_fat_ptr(bcx.tcx(), ty)); common::type_is_fat_ptr(mircx.ccx, ty));
} else if common::type_is_imm_pair(bcx.ccx(), ty) { } else if common::type_is_imm_pair(mircx.ccx, ty) {
// We allow pairs and uses of any of their 2 fields. // We allow pairs and uses of any of their 2 fields.
} else { } else {
// These sorts of types require an alloca. Note that // These sorts of types require an alloca. Note that
@ -56,22 +55,18 @@ pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>,
analyzer.lvalue_locals analyzer.lvalue_locals
} }
struct LocalAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> { struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> {
mir: &'mir mir::Mir<'tcx>, cx: &'mir MirContext<'a, 'tcx>,
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
lvalue_locals: BitVector, lvalue_locals: BitVector,
seen_assigned: BitVector seen_assigned: BitVector
} }
impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> { impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> {
fn new(mir: &'mir mir::Mir<'tcx>, fn new(mircx: &'mir MirContext<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> {
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>)
-> LocalAnalyzer<'mir, 'bcx, 'tcx> {
LocalAnalyzer { LocalAnalyzer {
mir: mir, cx: mircx,
bcx: bcx, lvalue_locals: BitVector::new(mircx.mir.local_decls.len()),
lvalue_locals: BitVector::new(mir.local_decls.len()), seen_assigned: BitVector::new(mircx.mir.local_decls.len())
seen_assigned: BitVector::new(mir.local_decls.len())
} }
} }
@ -87,7 +82,7 @@ impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> {
} }
} }
impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
fn visit_assign(&mut self, fn visit_assign(&mut self,
block: mir::BasicBlock, block: mir::BasicBlock,
lvalue: &mir::Lvalue<'tcx>, lvalue: &mir::Lvalue<'tcx>,
@ -97,7 +92,7 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
if let mir::Lvalue::Local(index) = *lvalue { if let mir::Lvalue::Local(index) = *lvalue {
self.mark_assigned(index); self.mark_assigned(index);
if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) { if !rvalue::rvalue_creates_operand(rvalue) {
self.mark_as_lvalue(index); self.mark_as_lvalue(index);
} }
} else { } else {
@ -117,7 +112,7 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
literal: mir::Literal::Item { def_id, .. }, .. literal: mir::Literal::Item { def_id, .. }, ..
}), }),
ref args, .. ref args, ..
} if Some(def_id) == self.bcx.tcx().lang_items.box_free_fn() => { } if Some(def_id) == self.cx.ccx.tcx().lang_items.box_free_fn() => {
// box_free(x) shares with `drop x` the property that it // box_free(x) shares with `drop x` the property that it
// is not guaranteed to be statically dominated by the // is not guaranteed to be statically dominated by the
// definition of x, so x must always be in an alloca. // definition of x, so x must always be in an alloca.
@ -140,10 +135,10 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
// Allow uses of projections of immediate pair fields. // Allow uses of projections of immediate pair fields.
if let mir::Lvalue::Projection(ref proj) = *lvalue { if let mir::Lvalue::Projection(ref proj) = *lvalue {
if let mir::Lvalue::Local(_) = proj.base { if let mir::Lvalue::Local(_) = proj.base {
let ty = proj.base.ty(self.mir, self.bcx.tcx()); let ty = proj.base.ty(self.cx.mir, self.cx.ccx.tcx());
let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx()));
if common::type_is_imm_pair(self.bcx.ccx(), ty) { if common::type_is_imm_pair(self.cx.ccx, ty) {
if let mir::ProjectionElem::Field(..) = proj.elem { if let mir::ProjectionElem::Field(..) = proj.elem {
if let LvalueContext::Consume = context { if let LvalueContext::Consume = context {
return; return;
@ -171,11 +166,11 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
} }
LvalueContext::Drop => { LvalueContext::Drop => {
let ty = lvalue.ty(self.mir, self.bcx.tcx()); let ty = lvalue.ty(self.cx.mir, self.cx.ccx.tcx());
let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); let ty = self.cx.monomorphize(&ty.to_ty(self.cx.ccx.tcx()));
// Only need the lvalue if we're actually dropping it. // Only need the lvalue if we're actually dropping it.
if glue::type_needs_drop(self.bcx.tcx(), ty) { if self.cx.ccx.shared().type_needs_drop(ty) {
self.mark_as_lvalue(index); self.mark_as_lvalue(index);
} }
} }
@ -200,10 +195,7 @@ pub enum CleanupKind {
Internal { funclet: mir::BasicBlock } Internal { funclet: mir::BasicBlock }
} }
pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>, pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
mir: &mir::Mir<'tcx>)
-> IndexVec<mir::BasicBlock, CleanupKind>
{
fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>, fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
mir: &mir::Mir<'tcx>) { mir: &mir::Mir<'tcx>) {
for (bb, data) in mir.basic_blocks().iter_enumerated() { for (bb, data) in mir.basic_blocks().iter_enumerated() {

View file

@ -8,20 +8,18 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use llvm::{self, ValueRef}; use llvm::{self, ValueRef, BasicBlockRef};
use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err}; use rustc_const_eval::{ErrKind, ConstEvalErr, note_const_eval_err};
use rustc::middle::lang_items; use rustc::middle::lang_items;
use rustc::ty::{self, layout}; use rustc::ty::{self, layout};
use rustc::mir; use rustc::mir;
use abi::{Abi, FnType, ArgType}; use abi::{Abi, FnType, ArgType};
use adt; use adt;
use base; use base::{self, Lifetime};
use build;
use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
use common::{self, Block, BlockAndBuilder, LandingPad}; use common::{self, BlockAndBuilder, Funclet};
use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
use consts; use consts;
use debuginfo::DebugLoc;
use Disr; use Disr;
use machine::{llalign_of_min, llbitsize_of_real}; use machine::{llalign_of_min, llbitsize_of_real};
use meth; use meth;
@ -29,6 +27,7 @@ use type_of;
use glue; use glue;
use type_::Type; use type_::Type;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use syntax::symbol::Symbol; use syntax::symbol::Symbol;
@ -39,21 +38,27 @@ use super::lvalue::{LvalueRef};
use super::operand::OperandRef; use super::operand::OperandRef;
use super::operand::OperandValue::{Pair, Ref, Immediate}; use super::operand::OperandValue::{Pair, Ref, Immediate};
use std::cell::Ref as CellRef; use std::ptr;
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_block(&mut self, bb: mir::BasicBlock) { pub fn trans_block(&mut self, bb: mir::BasicBlock,
let mut bcx = self.bcx(bb); funclets: &IndexVec<mir::BasicBlock, Option<Funclet>>) {
let data = &CellRef::clone(&self.mir)[bb]; let mut bcx = self.build_block(bb);
let data = &self.mir[bb];
debug!("trans_block({:?}={:?})", bb, data); debug!("trans_block({:?}={:?})", bb, data);
let funclet = match self.cleanup_kinds[bb] {
CleanupKind::Internal { funclet } => funclets[funclet].as_ref(),
_ => funclets[bb].as_ref(),
};
// Create the cleanup bundle, if needed. // Create the cleanup bundle, if needed.
let cleanup_pad = bcx.lpad().and_then(|lp| lp.cleanuppad()); let cleanup_pad = funclet.map(|lp| lp.cleanuppad());
let cleanup_bundle = bcx.lpad().and_then(|l| l.bundle()); let cleanup_bundle = funclet.map(|l| l.bundle());
let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| { let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| {
let lltarget = this.blocks[bb].llbb; let lltarget = this.blocks[bb];
if let Some(cp) = cleanup_pad { if let Some(cp) = cleanup_pad {
match this.cleanup_kinds[bb] { match this.cleanup_kinds[bb] {
CleanupKind::Funclet => { CleanupKind::Funclet => {
@ -70,7 +75,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}; };
let llblock = |this: &mut Self, target: mir::BasicBlock| { let llblock = |this: &mut Self, target: mir::BasicBlock| {
let lltarget = this.blocks[target].llbb; let lltarget = this.blocks[target];
if let Some(cp) = cleanup_pad { if let Some(cp) = cleanup_pad {
match this.cleanup_kinds[target] { match this.cleanup_kinds[target] {
@ -79,8 +84,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
debug!("llblock: creating cleanup trampoline for {:?}", target); debug!("llblock: creating cleanup trampoline for {:?}", target);
let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
let trampoline = this.fcx.new_block(name).build(); let trampoline = this.fcx.build_new_block(name);
trampoline.set_personality_fn(this.fcx.eh_personality());
trampoline.cleanup_ret(cp, Some(lltarget)); trampoline.cleanup_ret(cp, Some(lltarget));
trampoline.llbb() trampoline.llbb()
} }
@ -93,7 +97,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
(this.cleanup_kinds[bb], this.cleanup_kinds[target]) (this.cleanup_kinds[bb], this.cleanup_kinds[target])
{ {
// jump *into* cleanup - need a landing pad if GNU // jump *into* cleanup - need a landing pad if GNU
this.landing_pad_to(target).llbb this.landing_pad_to(target)
} else { } else {
lltarget lltarget
} }
@ -108,23 +112,22 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
debug!("trans_block: terminator: {:?}", terminator); debug!("trans_block: terminator: {:?}", terminator);
let span = terminator.source_info.span; let span = terminator.source_info.span;
let debug_loc = self.debug_loc(terminator.source_info); self.set_debug_loc(&bcx, terminator.source_info);
debug_loc.apply_to_bcx(&bcx);
debug_loc.apply(bcx.fcx());
match terminator.kind { match terminator.kind {
mir::TerminatorKind::Resume => { mir::TerminatorKind::Resume => {
if let Some(cleanup_pad) = cleanup_pad { if let Some(cleanup_pad) = cleanup_pad {
bcx.cleanup_ret(cleanup_pad, None); bcx.cleanup_ret(cleanup_pad, None);
} else { } else {
let llpersonality = bcx.fcx().eh_personality();
bcx.set_personality_fn(llpersonality);
let ps = self.get_personality_slot(&bcx); let ps = self.get_personality_slot(&bcx);
let lp = bcx.load(ps); let lp = bcx.load(ps);
bcx.with_block(|bcx| { Lifetime::End.call(&bcx, ps);
base::call_lifetime_end(bcx, ps); if !bcx.sess().target.target.options.custom_unwind_resume {
base::trans_unwind_resume(bcx, lp); bcx.resume(lp);
}); } else {
let exc_ptr = bcx.extract_value(lp, 0);
bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], cleanup_bundle);
bcx.unreachable();
}
} }
} }
@ -143,9 +146,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => { mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => {
let discr_lvalue = self.trans_lvalue(&bcx, discr); let discr_lvalue = self.trans_lvalue(&bcx, discr);
let ty = discr_lvalue.ty.to_ty(bcx.tcx()); let ty = discr_lvalue.ty.to_ty(bcx.tcx());
let discr = bcx.with_block(|bcx| let discr = adt::trans_get_discr(&bcx, ty, discr_lvalue.llval, None, true);
adt::trans_get_discr(bcx, ty, discr_lvalue.llval, None, true)
);
let mut bb_hist = FxHashMap(); let mut bb_hist = FxHashMap();
for target in targets { for target in targets {
@ -162,16 +163,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// We're generating an exhaustive switch, so the else branch // We're generating an exhaustive switch, so the else branch
// can't be hit. Branching to an unreachable instruction // can't be hit. Branching to an unreachable instruction
// lets LLVM know this // lets LLVM know this
_ => (None, self.unreachable_block().llbb) _ => (None, self.unreachable_block())
}; };
let switch = bcx.switch(discr, default_blk, targets.len()); let switch = bcx.switch(discr, default_blk, targets.len());
assert_eq!(adt_def.variants.len(), targets.len()); assert_eq!(adt_def.variants.len(), targets.len());
for (adt_variant, &target) in adt_def.variants.iter().zip(targets) { for (adt_variant, &target) in adt_def.variants.iter().zip(targets) {
if default_bb != Some(target) { if default_bb != Some(target) {
let llbb = llblock(self, target); let llbb = llblock(self, target);
let llval = bcx.with_block(|bcx| adt::trans_case( let llval = adt::trans_case(&bcx, ty, Disr::from(adt_variant.disr_val));
bcx, ty, Disr::from(adt_variant.disr_val))); bcx.add_case(switch, llval, llbb)
build::AddCase(switch, llval, llbb)
} }
} }
} }
@ -179,17 +179,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => {
let (otherwise, targets) = targets.split_last().unwrap(); let (otherwise, targets) = targets.split_last().unwrap();
let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval); let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval);
let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty)); let discr = base::to_immediate(&bcx, discr, switch_ty);
let switch = bcx.switch(discr, llblock(self, *otherwise), values.len()); let switch = bcx.switch(discr, llblock(self, *otherwise), values.len());
for (value, target) in values.iter().zip(targets) { for (value, target) in values.iter().zip(targets) {
let val = Const::from_constval(bcx.ccx(), value.clone(), switch_ty); let val = Const::from_constval(bcx.ccx, value.clone(), switch_ty);
let llbb = llblock(self, *target); let llbb = llblock(self, *target);
build::AddCase(switch, val.llval, llbb) bcx.add_case(switch, val.llval, llbb)
} }
} }
mir::TerminatorKind::Return => { mir::TerminatorKind::Return => {
let ret = bcx.fcx().fn_ty.ret; let ret = self.fn_ty.ret;
if ret.is_ignore() || ret.is_indirect() { if ret.is_ignore() || ret.is_indirect() {
bcx.ret_void(); bcx.ret_void();
return; return;
@ -208,14 +208,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}; };
let llslot = match op.val { let llslot = match op.val {
Immediate(_) | Pair(..) => { Immediate(_) | Pair(..) => {
let llscratch = build::AllocaFcx(bcx.fcx(), ret.original_ty, "ret"); let llscratch = bcx.fcx().alloca(ret.original_ty, "ret");
self.store_operand(&bcx, llscratch, op); self.store_operand(&bcx, llscratch, op);
llscratch llscratch
} }
Ref(llval) => llval Ref(llval) => llval
}; };
let load = bcx.load(bcx.pointercast(llslot, cast_ty.ptr_to())); let load = bcx.load(bcx.pointercast(llslot, cast_ty.ptr_to()));
let llalign = llalign_of_min(bcx.ccx(), ret.ty); let llalign = llalign_of_min(bcx.ccx, ret.ty);
unsafe { unsafe {
llvm::LLVMSetAlignment(load, llalign); llvm::LLVMSetAlignment(load, llalign);
} }
@ -233,21 +233,21 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::TerminatorKind::Drop { ref location, target, unwind } => { mir::TerminatorKind::Drop { ref location, target, unwind } => {
let ty = location.ty(&self.mir, bcx.tcx()).to_ty(bcx.tcx()); let ty = location.ty(&self.mir, bcx.tcx()).to_ty(bcx.tcx());
let ty = bcx.monomorphize(&ty); let ty = self.monomorphize(&ty);
// Double check for necessity to drop // Double check for necessity to drop
if !glue::type_needs_drop(bcx.tcx(), ty) { if !bcx.ccx.shared().type_needs_drop(ty) {
funclet_br(self, bcx, target); funclet_br(self, bcx, target);
return; return;
} }
let lvalue = self.trans_lvalue(&bcx, location); let lvalue = self.trans_lvalue(&bcx, location);
let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); let drop_fn = glue::get_drop_glue(bcx.ccx, ty);
let drop_ty = glue::get_drop_glue_type(bcx.tcx(), ty); let drop_ty = glue::get_drop_glue_type(bcx.ccx.shared(), ty);
let is_sized = common::type_is_sized(bcx.tcx(), ty); let is_sized = bcx.ccx.shared().type_is_sized(ty);
let llvalue = if is_sized { let llvalue = if is_sized {
if drop_ty != ty { if drop_ty != ty {
bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx(), drop_ty).ptr_to()) bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to())
} else { } else {
lvalue.llval lvalue.llval
} }
@ -259,18 +259,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// but I am shooting for a quick fix to #35546 // but I am shooting for a quick fix to #35546
// here that can be cleanly backported to beta, so // here that can be cleanly backported to beta, so
// I want to avoid touching all of trans. // I want to avoid touching all of trans.
bcx.with_block(|bcx| { let scratch = base::alloc_ty(&bcx, ty, "drop");
let scratch = base::alloc_ty(bcx, ty, "drop"); Lifetime::Start.call(&bcx, scratch);
base::call_lifetime_start(bcx, scratch); bcx.store(lvalue.llval, base::get_dataptr(&bcx, scratch));
build::Store(bcx, lvalue.llval, base::get_dataptr(bcx, scratch)); bcx.store(lvalue.llextra, base::get_meta(&bcx, scratch));
build::Store(bcx, lvalue.llextra, base::get_meta(bcx, scratch)); scratch
scratch
})
}; };
if let Some(unwind) = unwind { if let Some(unwind) = unwind {
bcx.invoke(drop_fn, bcx.invoke(drop_fn,
&[llvalue], &[llvalue],
self.blocks[target].llbb, self.blocks[target],
llblock(self, unwind), llblock(self, unwind),
cleanup_bundle); cleanup_bundle);
} else { } else {
@ -290,7 +288,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// NOTE: Unlike binops, negation doesn't have its own // NOTE: Unlike binops, negation doesn't have its own
// checked operation, just a comparison with the minimum // checked operation, just a comparison with the minimum
// value, so we have to check for the assert message. // value, so we have to check for the assert message.
if !bcx.ccx().check_overflow() { if !bcx.ccx.check_overflow() {
use rustc_const_math::ConstMathErr::Overflow; use rustc_const_math::ConstMathErr::Overflow;
use rustc_const_math::Op::Neg; use rustc_const_math::Op::Neg;
@ -306,27 +304,27 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
// Pass the condition through llvm.expect for branch hinting. // Pass the condition through llvm.expect for branch hinting.
let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1"); let expect = bcx.ccx.get_intrinsic(&"llvm.expect.i1");
let cond = bcx.call(expect, &[cond, C_bool(bcx.ccx(), expected)], None); let cond = bcx.call(expect, &[cond, C_bool(bcx.ccx, expected)], None);
// Create the failure block and the conditional branch to it. // Create the failure block and the conditional branch to it.
let lltarget = llblock(self, target); let lltarget = llblock(self, target);
let panic_block = self.fcx.new_block("panic"); let panic_block = self.fcx.build_new_block("panic");
if expected { if expected {
bcx.cond_br(cond, lltarget, panic_block.llbb); bcx.cond_br(cond, lltarget, panic_block.llbb());
} else { } else {
bcx.cond_br(cond, panic_block.llbb, lltarget); bcx.cond_br(cond, panic_block.llbb(), lltarget);
} }
// After this point, bcx is the block for the call to panic. // After this point, bcx is the block for the call to panic.
bcx = panic_block.build(); bcx = panic_block;
debug_loc.apply_to_bcx(&bcx); self.set_debug_loc(&bcx, terminator.source_info);
// Get the location information. // Get the location information.
let loc = bcx.sess().codemap().lookup_char_pos(span.lo); let loc = bcx.sess().codemap().lookup_char_pos(span.lo);
let filename = Symbol::intern(&loc.file.name).as_str(); let filename = Symbol::intern(&loc.file.name).as_str();
let filename = C_str_slice(bcx.ccx(), filename); let filename = C_str_slice(bcx.ccx, filename);
let line = C_u32(bcx.ccx(), loc.line as u32); let line = C_u32(bcx.ccx, loc.line as u32);
// Put together the arguments to the panic entry point. // Put together the arguments to the panic entry point.
let (lang_item, args, const_err) = match *msg { let (lang_item, args, const_err) = match *msg {
@ -343,9 +341,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}) })
}); });
let file_line = C_struct(bcx.ccx(), &[filename, line], false); let file_line = C_struct(bcx.ccx, &[filename, line], false);
let align = llalign_of_min(bcx.ccx(), common::val_ty(file_line)); let align = llalign_of_min(bcx.ccx, common::val_ty(file_line));
let file_line = consts::addr_of(bcx.ccx(), let file_line = consts::addr_of(bcx.ccx,
file_line, file_line,
align, align,
"panic_bounds_check_loc"); "panic_bounds_check_loc");
@ -355,12 +353,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
mir::AssertMessage::Math(ref err) => { mir::AssertMessage::Math(ref err) => {
let msg_str = Symbol::intern(err.description()).as_str(); let msg_str = Symbol::intern(err.description()).as_str();
let msg_str = C_str_slice(bcx.ccx(), msg_str); let msg_str = C_str_slice(bcx.ccx, msg_str);
let msg_file_line = C_struct(bcx.ccx(), let msg_file_line = C_struct(bcx.ccx,
&[msg_str, filename, line], &[msg_str, filename, line],
false); false);
let align = llalign_of_min(bcx.ccx(), common::val_ty(msg_file_line)); let align = llalign_of_min(bcx.ccx, common::val_ty(msg_file_line));
let msg_file_line = consts::addr_of(bcx.ccx(), let msg_file_line = consts::addr_of(bcx.ccx,
msg_file_line, msg_file_line,
align, align,
"panic_loc"); "panic_loc");
@ -384,15 +382,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// Obtain the panic entry point. // Obtain the panic entry point.
let def_id = common::langcall(bcx.tcx(), Some(span), "", lang_item); let def_id = common::langcall(bcx.tcx(), Some(span), "", lang_item);
let callee = Callee::def(bcx.ccx(), def_id, let callee = Callee::def(bcx.ccx, def_id,
bcx.ccx().empty_substs_for_def_id(def_id)); bcx.ccx.empty_substs_for_def_id(def_id));
let llfn = callee.reify(bcx.ccx()); let llfn = callee.reify(bcx.ccx);
// Translate the actual panic invoke/call. // Translate the actual panic invoke/call.
if let Some(unwind) = cleanup { if let Some(unwind) = cleanup {
bcx.invoke(llfn, bcx.invoke(llfn,
&args, &args,
self.unreachable_block().llbb, self.unreachable_block(),
llblock(self, unwind), llblock(self, unwind),
cleanup_bundle); cleanup_bundle);
} else { } else {
@ -411,7 +409,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let (mut callee, abi, sig) = match callee.ty.sty { let (mut callee, abi, sig) = match callee.ty.sty {
ty::TyFnDef(def_id, substs, f) => { ty::TyFnDef(def_id, substs, f) => {
(Callee::def(bcx.ccx(), def_id, substs), f.abi, &f.sig) (Callee::def(bcx.ccx, def_id, substs), f.abi, &f.sig)
} }
ty::TyFnPtr(f) => { ty::TyFnPtr(f) => {
(Callee { (Callee {
@ -443,6 +441,65 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
return; return;
} }
// FIXME: This should proxy to the drop glue in the future when the ABI matches;
// most of the below code was copied from the match arm for TerminatorKind::Drop.
if intrinsic == Some("drop_in_place") {
let &(_, target) = destination.as_ref().unwrap();
let ty = if let ty::TyFnDef(_, substs, _) = callee.ty.sty {
substs.type_at(0)
} else {
bug!("Unexpected ty: {}", callee.ty);
};
// Double check for necessity to drop
if !bcx.ccx.shared().type_needs_drop(ty) {
funclet_br(self, bcx, target);
return;
}
let ptr = self.trans_operand(&bcx, &args[0]);
let (llval, llextra) = match ptr.val {
Immediate(llptr) => (llptr, ptr::null_mut()),
Pair(llptr, llextra) => (llptr, llextra),
Ref(_) => bug!("Deref of by-Ref type {:?}", ptr.ty)
};
let drop_fn = glue::get_drop_glue(bcx.ccx, ty);
let drop_ty = glue::get_drop_glue_type(bcx.ccx.shared(), ty);
let is_sized = bcx.ccx.shared().type_is_sized(ty);
let llvalue = if is_sized {
if drop_ty != ty {
bcx.pointercast(llval, type_of::type_of(bcx.ccx, drop_ty).ptr_to())
} else {
llval
}
} else {
// FIXME(#36457) Currently drop glue takes sized
// values as a `*(data, meta)`, but elsewhere in
// MIR we pass `(data, meta)` as two separate
// arguments. It would be better to fix drop glue,
// but I am shooting for a quick fix to #35546
// here that can be cleanly backported to beta, so
// I want to avoid touching all of trans.
let scratch = base::alloc_ty(&bcx, ty, "drop");
Lifetime::Start.call(&bcx, scratch);
bcx.store(llval, base::get_dataptr(&bcx, scratch));
bcx.store(llextra, base::get_meta(&bcx, scratch));
scratch
};
if let Some(unwind) = *cleanup {
bcx.invoke(drop_fn,
&[llvalue],
self.blocks[target],
llblock(self, unwind),
cleanup_bundle);
} else {
bcx.call(drop_fn, &[llvalue], cleanup_bundle);
funclet_br(self, bcx, target);
}
return;
}
if intrinsic == Some("transmute") { if intrinsic == Some("transmute") {
let &(ref dest, target) = destination.as_ref().unwrap(); let &(ref dest, target) = destination.as_ref().unwrap();
self.with_lvalue_ref(&bcx, dest, |this, dest| { self.with_lvalue_ref(&bcx, dest, |this, dest| {
@ -456,9 +513,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let extra_args = &args[sig.inputs().len()..]; let extra_args = &args[sig.inputs().len()..];
let extra_args = extra_args.iter().map(|op_arg| { let extra_args = extra_args.iter().map(|op_arg| {
let op_ty = op_arg.ty(&self.mir, bcx.tcx()); let op_ty = op_arg.ty(&self.mir, bcx.tcx());
bcx.monomorphize(&op_ty) self.monomorphize(&op_ty)
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
let fn_ty = callee.direct_fn_type(bcx.ccx(), &extra_args); let fn_ty = callee.direct_fn_type(bcx.ccx, &extra_args);
// The arguments we'll be passing. Plus one to account for outptr, if used. // The arguments we'll be passing. Plus one to account for outptr, if used.
let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
@ -519,7 +576,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let fn_ptr = match callee.data { let fn_ptr = match callee.data {
NamedTupleConstructor(_) => { NamedTupleConstructor(_) => {
// FIXME translate this like mir::Rvalue::Aggregate. // FIXME translate this like mir::Rvalue::Aggregate.
callee.reify(bcx.ccx()) callee.reify(bcx.ccx)
} }
Intrinsic => { Intrinsic => {
use intrinsic::trans_intrinsic_call; use intrinsic::trans_intrinsic_call;
@ -537,10 +594,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
bug!("Cannot use direct operand with an intrinsic call") bug!("Cannot use direct operand with an intrinsic call")
}; };
bcx.with_block(|bcx| { trans_intrinsic_call(&bcx, callee.ty, &fn_ty, &llargs, dest,
trans_intrinsic_call(bcx, callee.ty, &fn_ty, terminator.source_info.span);
&llargs, dest, debug_loc);
});
if let ReturnDest::IndirectOperand(dst, _) = ret_dest { if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
// Make a fake operand for store_return // Make a fake operand for store_return
@ -554,8 +609,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
if let Some((_, target)) = *destination { if let Some((_, target)) = *destination {
funclet_br(self, bcx, target); funclet_br(self, bcx, target);
} else { } else {
// trans_intrinsic_call already used Unreachable. bcx.unreachable();
// bcx.unreachable();
} }
return; return;
@ -573,15 +627,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}; };
let invokeret = bcx.invoke(fn_ptr, let invokeret = bcx.invoke(fn_ptr,
&llargs, &llargs,
ret_bcx.llbb, ret_bcx,
llblock(self, cleanup), llblock(self, cleanup),
cleanup_bundle); cleanup_bundle);
fn_ty.apply_attrs_callsite(invokeret); fn_ty.apply_attrs_callsite(invokeret);
if destination.is_some() { if let Some((_, target)) = *destination {
let ret_bcx = ret_bcx.build(); let ret_bcx = self.build_block(target);
ret_bcx.at_start(|ret_bcx| { ret_bcx.at_start(|ret_bcx| {
debug_loc.apply_to_bcx(ret_bcx); self.set_debug_loc(&ret_bcx, terminator.source_info);
let op = OperandRef { let op = OperandRef {
val: Immediate(invokeret), val: Immediate(invokeret),
ty: sig.output(), ty: sig.output(),
@ -608,7 +662,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
fn trans_argument(&mut self, fn trans_argument(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
op: OperandRef<'tcx>, op: OperandRef<'tcx>,
llargs: &mut Vec<ValueRef>, llargs: &mut Vec<ValueRef>,
fn_ty: &FnType, fn_ty: &FnType,
@ -616,14 +670,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
callee: &mut CalleeData) { callee: &mut CalleeData) {
if let Pair(a, b) = op.val { if let Pair(a, b) = op.val {
// Treat the values in a fat pointer separately. // Treat the values in a fat pointer separately.
if common::type_is_fat_ptr(bcx.tcx(), op.ty) { if common::type_is_fat_ptr(bcx.ccx, op.ty) {
let (ptr, meta) = (a, b); let (ptr, meta) = (a, b);
if *next_idx == 0 { if *next_idx == 0 {
if let Virtual(idx) = *callee { if let Virtual(idx) = *callee {
let llfn = bcx.with_block(|bcx| { let llfn = meth::get_virtual_method(bcx, meta, idx);
meth::get_virtual_method(bcx, meta, idx) let llty = fn_ty.llvm_type(bcx.ccx).ptr_to();
});
let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
*callee = Fn(bcx.pointercast(llfn, llty)); *callee = Fn(bcx.pointercast(llfn, llty));
} }
} }
@ -655,7 +707,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let (mut llval, by_ref) = match op.val { let (mut llval, by_ref) = match op.val {
Immediate(_) | Pair(..) => { Immediate(_) | Pair(..) => {
if arg.is_indirect() || arg.cast.is_some() { if arg.is_indirect() || arg.cast.is_some() {
let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg"); let llscratch = bcx.fcx().alloca(arg.original_ty, "arg");
self.store_operand(bcx, llscratch, op); self.store_operand(bcx, llscratch, op);
(llscratch, true) (llscratch, true)
} else { } else {
@ -667,13 +719,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
if by_ref && !arg.is_indirect() { if by_ref && !arg.is_indirect() {
// Have to load the argument, maybe while casting it. // Have to load the argument, maybe while casting it.
if arg.original_ty == Type::i1(bcx.ccx()) { if arg.original_ty == Type::i1(bcx.ccx) {
// We store bools as i8 so we need to truncate to i1. // We store bools as i8 so we need to truncate to i1.
llval = bcx.load_range_assert(llval, 0, 2, llvm::False); llval = bcx.load_range_assert(llval, 0, 2, llvm::False);
llval = bcx.trunc(llval, arg.original_ty); llval = bcx.trunc(llval, arg.original_ty);
} else if let Some(ty) = arg.cast { } else if let Some(ty) = arg.cast {
llval = bcx.load(bcx.pointercast(llval, ty.ptr_to())); llval = bcx.load(bcx.pointercast(llval, ty.ptr_to()));
let llalign = llalign_of_min(bcx.ccx(), arg.ty); let llalign = llalign_of_min(bcx.ccx, arg.ty);
unsafe { unsafe {
llvm::LLVMSetAlignment(llval, llalign); llvm::LLVMSetAlignment(llval, llalign);
} }
@ -686,7 +738,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
fn trans_arguments_untupled(&mut self, fn trans_arguments_untupled(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
operand: &mir::Operand<'tcx>, operand: &mir::Operand<'tcx>,
llargs: &mut Vec<ValueRef>, llargs: &mut Vec<ValueRef>,
fn_ty: &FnType, fn_ty: &FnType,
@ -705,9 +757,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
Ref(llval) => { Ref(llval) => {
let base = adt::MaybeSizedValue::sized(llval); let base = adt::MaybeSizedValue::sized(llval);
for (n, &ty) in arg_types.iter().enumerate() { for (n, &ty) in arg_types.iter().enumerate() {
let ptr = adt::trans_field_ptr_builder(bcx, tuple.ty, base, Disr(0), n); let ptr = adt::trans_field_ptr(bcx, tuple.ty, base, Disr(0), n);
let val = if common::type_is_fat_ptr(bcx.tcx(), ty) { let val = if common::type_is_fat_ptr(bcx.ccx, ty) {
let (lldata, llextra) = base::load_fat_ptr_builder(bcx, ptr, ty); let (lldata, llextra) = base::load_fat_ptr(bcx, ptr, ty);
Pair(lldata, llextra) Pair(lldata, llextra)
} else { } else {
// trans_argument will load this if it needs to // trans_argument will load this if it needs to
@ -722,7 +774,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
Immediate(llval) => { Immediate(llval) => {
let l = bcx.ccx().layout_of(tuple.ty); let l = bcx.ccx.layout_of(tuple.ty);
let v = if let layout::Univariant { ref variant, .. } = *l { let v = if let layout::Univariant { ref variant, .. } = *l {
variant variant
} else { } else {
@ -731,8 +783,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
for (n, &ty) in arg_types.iter().enumerate() { for (n, &ty) in arg_types.iter().enumerate() {
let mut elem = bcx.extract_value(llval, v.memory_index[n] as usize); let mut elem = bcx.extract_value(llval, v.memory_index[n] as usize);
// Truncate bools to i1, if needed // Truncate bools to i1, if needed
if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx()) { if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx) {
elem = bcx.trunc(elem, Type::i1(bcx.ccx())); elem = bcx.trunc(elem, Type::i1(bcx.ccx));
} }
// If the tuple is immediate, the elements are as well // If the tuple is immediate, the elements are as well
let op = OperandRef { let op = OperandRef {
@ -747,8 +799,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
for (n, &ty) in arg_types.iter().enumerate() { for (n, &ty) in arg_types.iter().enumerate() {
let mut elem = elems[n]; let mut elem = elems[n];
// Truncate bools to i1, if needed // Truncate bools to i1, if needed
if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx()) { if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx) {
elem = bcx.trunc(elem, Type::i1(bcx.ccx())); elem = bcx.trunc(elem, Type::i1(bcx.ccx));
} }
// Pair is always made up of immediates // Pair is always made up of immediates
let op = OperandRef { let op = OperandRef {
@ -762,91 +814,61 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
fn get_personality_slot(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>) -> ValueRef { fn get_personality_slot(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>) -> ValueRef {
let ccx = bcx.ccx(); let ccx = bcx.ccx;
if let Some(slot) = self.llpersonalityslot { if let Some(slot) = self.llpersonalityslot {
slot slot
} else { } else {
let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
bcx.with_block(|bcx| { let slot = bcx.fcx().alloca(llretty, "personalityslot");
let slot = base::alloca(bcx, llretty, "personalityslot"); self.llpersonalityslot = Some(slot);
self.llpersonalityslot = Some(slot); Lifetime::Start.call(bcx, slot);
base::call_lifetime_start(bcx, slot); slot
slot
})
} }
} }
/// Return the landingpad wrapper around the given basic block /// Return the landingpad wrapper around the given basic block
/// ///
/// No-op in MSVC SEH scheme. /// No-op in MSVC SEH scheme.
fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> Block<'bcx, 'tcx> fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> BasicBlockRef {
{
if let Some(block) = self.landing_pads[target_bb] { if let Some(block) = self.landing_pads[target_bb] {
return block; return block;
} }
if base::wants_msvc_seh(self.fcx.ccx.sess()) { if base::wants_msvc_seh(self.ccx.sess()) {
return self.blocks[target_bb]; return self.blocks[target_bb];
} }
let target = self.bcx(target_bb); let target = self.build_block(target_bb);
let block = self.fcx.new_block("cleanup"); let bcx = self.fcx.build_new_block("cleanup");
self.landing_pads[target_bb] = Some(block); self.landing_pads[target_bb] = Some(bcx.llbb());
let bcx = block.build(); let ccx = bcx.ccx;
let ccx = bcx.ccx(); let llpersonality = self.ccx.eh_personality();
let llpersonality = self.fcx.eh_personality();
let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
let llretval = bcx.landing_pad(llretty, llpersonality, 1, self.fcx.llfn); let llretval = bcx.landing_pad(llretty, llpersonality, 1, self.fcx.llfn);
bcx.set_cleanup(llretval); bcx.set_cleanup(llretval);
let slot = self.get_personality_slot(&bcx); let slot = self.get_personality_slot(&bcx);
bcx.store(llretval, slot); bcx.store(llretval, slot);
bcx.br(target.llbb()); bcx.br(target.llbb());
block bcx.llbb()
} }
pub fn init_cpad(&mut self, bb: mir::BasicBlock) { fn unreachable_block(&mut self) -> BasicBlockRef {
let bcx = self.bcx(bb);
let data = &self.mir[bb];
debug!("init_cpad({:?})", data);
match self.cleanup_kinds[bb] {
CleanupKind::NotCleanup => {
bcx.set_lpad(None)
}
_ if !base::wants_msvc_seh(bcx.sess()) => {
bcx.set_lpad(Some(LandingPad::gnu()))
}
CleanupKind::Internal { funclet } => {
// FIXME: is this needed?
bcx.set_personality_fn(self.fcx.eh_personality());
bcx.set_lpad_ref(self.bcx(funclet).lpad());
}
CleanupKind::Funclet => {
bcx.set_personality_fn(self.fcx.eh_personality());
DebugLoc::None.apply_to_bcx(&bcx);
let cleanup_pad = bcx.cleanup_pad(None, &[]);
bcx.set_lpad(Some(LandingPad::msvc(cleanup_pad)));
}
};
}
fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> {
self.unreachable_block.unwrap_or_else(|| { self.unreachable_block.unwrap_or_else(|| {
let bl = self.fcx.new_block("unreachable"); let bl = self.fcx.build_new_block("unreachable");
bl.build().unreachable(); bl.unreachable();
self.unreachable_block = Some(bl); self.unreachable_block = Some(bl.llbb());
bl bl.llbb()
}) })
} }
fn bcx(&self, bb: mir::BasicBlock) -> BlockAndBuilder<'bcx, 'tcx> { pub fn build_block(&self, bb: mir::BasicBlock) -> BlockAndBuilder<'a, 'tcx> {
self.blocks[bb].build() BlockAndBuilder::new(self.blocks[bb], self.fcx)
} }
fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>,
dest: &mir::Lvalue<'tcx>, fn_ret_ty: &ArgType, dest: &mir::Lvalue<'tcx>, fn_ret_ty: &ArgType,
llargs: &mut Vec<ValueRef>, is_intrinsic: bool) -> ReturnDest { llargs: &mut Vec<ValueRef>, is_intrinsic: bool) -> ReturnDest {
// If the return is ignored, we can just return a do-nothing ReturnDest // If the return is ignored, we can just return a do-nothing ReturnDest
@ -863,18 +885,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
return if fn_ret_ty.is_indirect() { return if fn_ret_ty.is_indirect() {
// Odd, but possible, case, we have an operand temporary, // Odd, but possible, case, we have an operand temporary,
// but the calling convention has an indirect return. // but the calling convention has an indirect return.
let tmp = bcx.with_block(|bcx| { let tmp = base::alloc_ty(bcx, ret_ty, "tmp_ret");
base::alloc_ty(bcx, ret_ty, "tmp_ret")
});
llargs.push(tmp); llargs.push(tmp);
ReturnDest::IndirectOperand(tmp, index) ReturnDest::IndirectOperand(tmp, index)
} else if is_intrinsic { } else if is_intrinsic {
// Currently, intrinsics always need a location to store // Currently, intrinsics always need a location to store
// the result. so we create a temporary alloca for the // the result. so we create a temporary alloca for the
// result // result
let tmp = bcx.with_block(|bcx| { let tmp = base::alloc_ty(bcx, ret_ty, "tmp_ret");
base::alloc_ty(bcx, ret_ty, "tmp_ret")
});
ReturnDest::IndirectOperand(tmp, index) ReturnDest::IndirectOperand(tmp, index)
} else { } else {
ReturnDest::DirectOperand(index) ReturnDest::DirectOperand(index)
@ -895,27 +913,27 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
} }
fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, fn trans_transmute(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>,
src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) { src: &mir::Operand<'tcx>, dst: LvalueRef<'tcx>) {
let mut val = self.trans_operand(bcx, src); let mut val = self.trans_operand(bcx, src);
if let ty::TyFnDef(def_id, substs, _) = val.ty.sty { if let ty::TyFnDef(def_id, substs, _) = val.ty.sty {
let llouttype = type_of::type_of(bcx.ccx(), dst.ty.to_ty(bcx.tcx())); let llouttype = type_of::type_of(bcx.ccx, dst.ty.to_ty(bcx.tcx()));
let out_type_size = llbitsize_of_real(bcx.ccx(), llouttype); let out_type_size = llbitsize_of_real(bcx.ccx, llouttype);
if out_type_size != 0 { if out_type_size != 0 {
// FIXME #19925 Remove this hack after a release cycle. // FIXME #19925 Remove this hack after a release cycle.
let f = Callee::def(bcx.ccx(), def_id, substs); let f = Callee::def(bcx.ccx, def_id, substs);
let ty = match f.ty.sty { let ty = match f.ty.sty {
ty::TyFnDef(.., f) => bcx.tcx().mk_fn_ptr(f), ty::TyFnDef(.., f) => bcx.tcx().mk_fn_ptr(f),
_ => f.ty _ => f.ty
}; };
val = OperandRef { val = OperandRef {
val: Immediate(f.reify(bcx.ccx())), val: Immediate(f.reify(bcx.ccx)),
ty: ty ty: ty
}; };
} }
} }
let llty = type_of::type_of(bcx.ccx(), val.ty); let llty = type_of::type_of(bcx.ccx, val.ty);
let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to()); let cast_ptr = bcx.pointercast(dst.llval, llty.ptr_to());
self.store_operand(bcx, cast_ptr, val); self.store_operand(bcx, cast_ptr, val);
} }
@ -923,7 +941,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// Stores the return value of a function call into it's final location. // Stores the return value of a function call into it's final location.
fn store_return(&mut self, fn store_return(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
dest: ReturnDest, dest: ReturnDest,
ret_ty: ArgType, ret_ty: ArgType,
op: OperandRef<'tcx>) { op: OperandRef<'tcx>) {
@ -939,9 +957,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
DirectOperand(index) => { DirectOperand(index) => {
// If there is a cast, we have to store and reload. // If there is a cast, we have to store and reload.
let op = if ret_ty.cast.is_some() { let op = if ret_ty.cast.is_some() {
let tmp = bcx.with_block(|bcx| { let tmp = base::alloc_ty(bcx, op.ty, "tmp_ret");
base::alloc_ty(bcx, op.ty, "tmp_ret")
});
ret_ty.store(bcx, op.immediate(), tmp); ret_ty.store(bcx, op.immediate(), tmp);
self.trans_load(bcx, tmp, op.ty) self.trans_load(bcx, tmp, op.ty)
} else { } else {

View file

@ -25,7 +25,7 @@ use rustc::ty::subst::Substs;
use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use {abi, adt, base, Disr, machine}; use {abi, adt, base, Disr, machine};
use callee::Callee; use callee::Callee;
use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty, type_is_sized}; use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty};
use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral}; use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral};
use common::{C_null, C_struct, C_str_slice, C_undef, C_uint}; use common::{C_null, C_struct, C_str_slice, C_undef, C_uint};
use common::{const_to_opt_int, const_to_opt_uint}; use common::{const_to_opt_int, const_to_opt_uint};
@ -401,7 +401,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
.projection_ty(tcx, &projection.elem); .projection_ty(tcx, &projection.elem);
let base = tr_base.to_const(span); let base = tr_base.to_const(span);
let projected_ty = self.monomorphize(&projected_ty).to_ty(tcx); let projected_ty = self.monomorphize(&projected_ty).to_ty(tcx);
let is_sized = common::type_is_sized(tcx, projected_ty); let is_sized = self.ccx.shared().type_is_sized(projected_ty);
let (projected, llextra) = match projection.elem { let (projected, llextra) = match projection.elem {
mir::ProjectionElem::Deref => { mir::ProjectionElem::Deref => {
@ -598,11 +598,11 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
mir::CastKind::Unsize => { mir::CastKind::Unsize => {
// unsize targets other than to a fat pointer currently // unsize targets other than to a fat pointer currently
// can't be in constants. // can't be in constants.
assert!(common::type_is_fat_ptr(tcx, cast_ty)); assert!(common::type_is_fat_ptr(self.ccx, cast_ty));
let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference) let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference)
.expect("consts: unsizing got non-pointer type").ty; .expect("consts: unsizing got non-pointer type").ty;
let (base, old_info) = if !common::type_is_sized(tcx, pointee_ty) { let (base, old_info) = if !self.ccx.shared().type_is_sized(pointee_ty) {
// Normally, the source is a thin pointer and we are // Normally, the source is a thin pointer and we are
// adding extra info to make a fat pointer. The exception // adding extra info to make a fat pointer. The exception
// is when we are upcasting an existing object fat pointer // is when we are upcasting an existing object fat pointer
@ -685,9 +685,9 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
mir::CastKind::Misc => { // Casts from a fat-ptr. mir::CastKind::Misc => { // Casts from a fat-ptr.
let ll_cast_ty = type_of::immediate_type_of(self.ccx, cast_ty); let ll_cast_ty = type_of::immediate_type_of(self.ccx, cast_ty);
let ll_from_ty = type_of::immediate_type_of(self.ccx, operand.ty); let ll_from_ty = type_of::immediate_type_of(self.ccx, operand.ty);
if common::type_is_fat_ptr(tcx, operand.ty) { if common::type_is_fat_ptr(self.ccx, operand.ty) {
let (data_ptr, meta_ptr) = operand.get_fat_ptr(); let (data_ptr, meta_ptr) = operand.get_fat_ptr();
if common::type_is_fat_ptr(tcx, cast_ty) { if common::type_is_fat_ptr(self.ccx, cast_ty) {
let ll_cft = ll_cast_ty.field_types(); let ll_cft = ll_cast_ty.field_types();
let ll_fft = ll_from_ty.field_types(); let ll_fft = ll_from_ty.field_types();
let data_cast = consts::ptrcast(data_ptr, ll_cft[0]); let data_cast = consts::ptrcast(data_ptr, ll_cft[0]);
@ -716,7 +716,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
let base = match tr_lvalue.base { let base = match tr_lvalue.base {
Base::Value(llval) => { Base::Value(llval) => {
// FIXME: may be wrong for &*(&simd_vec as &fmt::Debug) // FIXME: may be wrong for &*(&simd_vec as &fmt::Debug)
let align = if type_is_sized(self.ccx.tcx(), ty) { let align = if self.ccx.shared().type_is_sized(ty) {
type_of::align_of(self.ccx, ty) type_of::align_of(self.ccx, ty)
} else { } else {
self.ccx.tcx().data_layout.pointer_align.abi() as machine::llalign self.ccx.tcx().data_layout.pointer_align.abi() as machine::llalign
@ -731,7 +731,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
Base::Static(llval) => llval Base::Static(llval) => llval
}; };
let ptr = if common::type_is_sized(tcx, ty) { let ptr = if self.ccx.shared().type_is_sized(ty) {
base base
} else { } else {
C_struct(self.ccx, &[base, tr_lvalue.llextra], false) C_struct(self.ccx, &[base, tr_lvalue.llextra], false)
@ -945,40 +945,39 @@ pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
} }
} }
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_constant(&mut self, pub fn trans_constant(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
constant: &mir::Constant<'tcx>) constant: &mir::Constant<'tcx>)
-> Const<'tcx> -> Const<'tcx>
{ {
debug!("trans_constant({:?})", constant); debug!("trans_constant({:?})", constant);
let ty = bcx.monomorphize(&constant.ty); let ty = self.monomorphize(&constant.ty);
let result = match constant.literal.clone() { let result = match constant.literal.clone() {
mir::Literal::Item { def_id, substs } => { mir::Literal::Item { def_id, substs } => {
// Shortcut for zero-sized types, including function item // Shortcut for zero-sized types, including function item
// types, which would not work with MirConstContext. // types, which would not work with MirConstContext.
if common::type_is_zero_size(bcx.ccx(), ty) { if common::type_is_zero_size(bcx.ccx, ty) {
let llty = type_of::type_of(bcx.ccx(), ty); let llty = type_of::type_of(bcx.ccx, ty);
return Const::new(C_null(llty), ty); return Const::new(C_null(llty), ty);
} }
let substs = bcx.monomorphize(&substs); let substs = self.monomorphize(&substs);
let instance = Instance::new(def_id, substs); let instance = Instance::new(def_id, substs);
MirConstContext::trans_def(bcx.ccx(), instance, IndexVec::new()) MirConstContext::trans_def(bcx.ccx, instance, IndexVec::new())
} }
mir::Literal::Promoted { index } => { mir::Literal::Promoted { index } => {
let mir = &self.mir.promoted[index]; let mir = &self.mir.promoted[index];
MirConstContext::new(bcx.ccx(), mir, bcx.fcx().param_substs, MirConstContext::new(bcx.ccx, mir, self.param_substs, IndexVec::new()).trans()
IndexVec::new()).trans()
} }
mir::Literal::Value { value } => { mir::Literal::Value { value } => {
Ok(Const::from_constval(bcx.ccx(), value, ty)) Ok(Const::from_constval(bcx.ccx, value, ty))
} }
}; };
let result = result.unwrap_or_else(|_| { let result = result.unwrap_or_else(|_| {
// We've errored, so we don't have to produce working code. // We've errored, so we don't have to produce working code.
let llty = type_of::type_of(bcx.ccx(), ty); let llty = type_of::type_of(bcx.ccx, ty);
Const::new(C_undef(llty), ty) Const::new(C_undef(llty), ty)
}); });

View file

@ -44,13 +44,13 @@ impl<'tcx> LvalueRef<'tcx> {
LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty } LvalueRef { llval: llval, llextra: ptr::null_mut(), ty: lvalue_ty }
} }
pub fn alloca<'bcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, pub fn alloca<'a>(bcx: &BlockAndBuilder<'a, 'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
name: &str) name: &str)
-> LvalueRef<'tcx> -> LvalueRef<'tcx>
{ {
assert!(!ty.has_erasable_regions()); assert!(!ty.has_erasable_regions());
let lltemp = bcx.with_block(|bcx| base::alloc_ty(bcx, ty, name)); let lltemp = base::alloc_ty(bcx, ty, name);
LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty)) LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
} }
@ -67,14 +67,14 @@ impl<'tcx> LvalueRef<'tcx> {
} }
} }
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_lvalue(&mut self, pub fn trans_lvalue(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
lvalue: &mir::Lvalue<'tcx>) lvalue: &mir::Lvalue<'tcx>)
-> LvalueRef<'tcx> { -> LvalueRef<'tcx> {
debug!("trans_lvalue(lvalue={:?})", lvalue); debug!("trans_lvalue(lvalue={:?})", lvalue);
let ccx = bcx.ccx(); let ccx = bcx.ccx;
let tcx = bcx.tcx(); let tcx = bcx.tcx();
if let mir::Lvalue::Local(index) = *lvalue { if let mir::Lvalue::Local(index) = *lvalue {
@ -103,7 +103,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let ptr = self.trans_consume(bcx, base); let ptr = self.trans_consume(bcx, base);
let projected_ty = LvalueTy::from_ty(ptr.ty) let projected_ty = LvalueTy::from_ty(ptr.ty)
.projection_ty(tcx, &mir::ProjectionElem::Deref); .projection_ty(tcx, &mir::ProjectionElem::Deref);
let projected_ty = bcx.monomorphize(&projected_ty); let projected_ty = self.monomorphize(&projected_ty);
let (llptr, llextra) = match ptr.val { let (llptr, llextra) = match ptr.val {
OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()),
OperandValue::Pair(llptr, llextra) => (llptr, llextra), OperandValue::Pair(llptr, llextra) => (llptr, llextra),
@ -118,14 +118,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::Lvalue::Projection(ref projection) => { mir::Lvalue::Projection(ref projection) => {
let tr_base = self.trans_lvalue(bcx, &projection.base); let tr_base = self.trans_lvalue(bcx, &projection.base);
let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem);
let projected_ty = bcx.monomorphize(&projected_ty); let projected_ty = self.monomorphize(&projected_ty);
let project_index = |llindex| { let project_index = |llindex| {
let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty { let element = if let ty::TySlice(_) = tr_base.ty.to_ty(tcx).sty {
// Slices already point to the array element type. // Slices already point to the array element type.
bcx.inbounds_gep(tr_base.llval, &[llindex]) bcx.inbounds_gep(tr_base.llval, &[llindex])
} else { } else {
let zero = common::C_uint(bcx.ccx(), 0u64); let zero = common::C_uint(bcx.ccx, 0u64);
bcx.inbounds_gep(tr_base.llval, &[zero, llindex]) bcx.inbounds_gep(tr_base.llval, &[zero, llindex])
}; };
element element
@ -140,14 +140,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v, LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v,
}; };
let discr = discr as u64; let discr = discr as u64;
let is_sized = common::type_is_sized(tcx, projected_ty.to_ty(tcx)); let is_sized = self.ccx.shared().type_is_sized(projected_ty.to_ty(tcx));
let base = if is_sized { let base = if is_sized {
adt::MaybeSizedValue::sized(tr_base.llval) adt::MaybeSizedValue::sized(tr_base.llval)
} else { } else {
adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra) adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra)
}; };
let llprojected = adt::trans_field_ptr_builder(bcx, base_ty, base, let llprojected = adt::trans_field_ptr(bcx, base_ty, base, Disr(discr),
Disr(discr), field.index()); field.index());
let llextra = if is_sized { let llextra = if is_sized {
ptr::null_mut() ptr::null_mut()
} else { } else {
@ -162,19 +162,19 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::ProjectionElem::ConstantIndex { offset, mir::ProjectionElem::ConstantIndex { offset,
from_end: false, from_end: false,
min_length: _ } => { min_length: _ } => {
let lloffset = C_uint(bcx.ccx(), offset); let lloffset = C_uint(bcx.ccx, offset);
(project_index(lloffset), ptr::null_mut()) (project_index(lloffset), ptr::null_mut())
} }
mir::ProjectionElem::ConstantIndex { offset, mir::ProjectionElem::ConstantIndex { offset,
from_end: true, from_end: true,
min_length: _ } => { min_length: _ } => {
let lloffset = C_uint(bcx.ccx(), offset); let lloffset = C_uint(bcx.ccx, offset);
let lllen = tr_base.len(bcx.ccx()); let lllen = tr_base.len(bcx.ccx);
let llindex = bcx.sub(lllen, lloffset); let llindex = bcx.sub(lllen, lloffset);
(project_index(llindex), ptr::null_mut()) (project_index(llindex), ptr::null_mut())
} }
mir::ProjectionElem::Subslice { from, to } => { mir::ProjectionElem::Subslice { from, to } => {
let llindex = C_uint(bcx.ccx(), from); let llindex = C_uint(bcx.ccx, from);
let llbase = project_index(llindex); let llbase = project_index(llindex);
let base_ty = tr_base.ty.to_ty(bcx.tcx()); let base_ty = tr_base.ty.to_ty(bcx.tcx());
@ -183,14 +183,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// must cast the lvalue pointer type to the new // must cast the lvalue pointer type to the new
// array type (*[%_; new_len]). // array type (*[%_; new_len]).
let base_ty = self.monomorphized_lvalue_ty(lvalue); let base_ty = self.monomorphized_lvalue_ty(lvalue);
let llbasety = type_of::type_of(bcx.ccx(), base_ty).ptr_to(); let llbasety = type_of::type_of(bcx.ccx, base_ty).ptr_to();
let llbase = bcx.pointercast(llbase, llbasety); let llbase = bcx.pointercast(llbase, llbasety);
(llbase, ptr::null_mut()) (llbase, ptr::null_mut())
} }
ty::TySlice(..) => { ty::TySlice(..) => {
assert!(tr_base.llextra != ptr::null_mut()); assert!(tr_base.llextra != ptr::null_mut());
let lllen = bcx.sub(tr_base.llextra, let lllen = bcx.sub(tr_base.llextra,
C_uint(bcx.ccx(), from+to)); C_uint(bcx.ccx, from+to));
(llbase, lllen) (llbase, lllen)
} }
_ => bug!("unexpected type {:?} in Subslice", base_ty) _ => bug!("unexpected type {:?} in Subslice", base_ty)
@ -214,7 +214,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// Perform an action using the given Lvalue. // Perform an action using the given Lvalue.
// If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot // If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot
// is created first, then used as an operand to update the Lvalue. // is created first, then used as an operand to update the Lvalue.
pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'a, 'tcx>,
lvalue: &mir::Lvalue<'tcx>, f: F) -> U lvalue: &mir::Lvalue<'tcx>, f: F) -> U
where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U
{ {
@ -235,9 +235,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// See comments in LocalRef::new_operand as to why // See comments in LocalRef::new_operand as to why
// we always have Some in a ZST LocalRef::Operand. // we always have Some in a ZST LocalRef::Operand.
let ty = self.monomorphized_lvalue_ty(lvalue); let ty = self.monomorphized_lvalue_ty(lvalue);
if common::type_is_zero_size(bcx.ccx(), ty) { if common::type_is_zero_size(bcx.ccx, ty) {
// Pass an undef pointer as no stores can actually occur. // Pass an undef pointer as no stores can actually occur.
let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to()); let llptr = C_undef(type_of(bcx.ccx, ty).ptr_to());
f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty))) f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty)))
} else { } else {
bug!("Lvalue local already set"); bug!("Lvalue local already set");
@ -255,13 +255,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
/// ///
/// nmatsakis: is this still necessary? Not sure. /// nmatsakis: is this still necessary? Not sure.
fn prepare_index(&mut self, fn prepare_index(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
llindex: ValueRef) llindex: ValueRef)
-> ValueRef -> ValueRef
{ {
let ccx = bcx.ccx(); let ccx = bcx.ccx;
let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex)); let index_size = machine::llbitsize_of_real(bcx.ccx, common::val_ty(llindex));
let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type()); let int_size = machine::llbitsize_of_real(bcx.ccx, ccx.int_type());
if index_size < int_size { if index_size < int_size {
bcx.zext(llindex, ccx.int_type()) bcx.zext(llindex, ccx.int_type())
} else if index_size > int_size { } else if index_size > int_size {
@ -272,8 +272,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
pub fn monomorphized_lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { pub fn monomorphized_lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
let tcx = self.fcx.ccx.tcx(); let tcx = self.ccx.tcx();
let lvalue_ty = lvalue.ty(&self.mir, tcx); let lvalue_ty = lvalue.ty(&self.mir, tcx);
self.fcx.monomorphize(&lvalue_ty.to_ty(tcx)) self.monomorphize(&lvalue_ty.to_ty(tcx))
} }
} }

View file

@ -9,40 +9,50 @@
// except according to those terms. // except according to those terms.
use libc::c_uint; use libc::c_uint;
use llvm::{self, ValueRef}; use llvm::{self, ValueRef, BasicBlockRef};
use llvm::debuginfo::DIScope;
use rustc::ty::{self, layout}; use rustc::ty::{self, layout};
use rustc::mir; use rustc::mir::{self, Mir};
use rustc::mir::tcx::LvalueTy; use rustc::mir::tcx::LvalueTy;
use rustc::ty::subst::Substs;
use rustc::infer::TransNormalize;
use rustc::ty::TypeFoldable;
use session::config::FullDebugInfo; use session::config::FullDebugInfo;
use base; use base;
use common::{self, Block, BlockAndBuilder, CrateContext, FunctionContext, C_null}; use common::{self, BlockAndBuilder, CrateContext, FunctionContext, C_null, Funclet};
use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind, FunctionDebugContext}; use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext};
use monomorphize::{self, Instance};
use abi::FnType;
use type_of; use type_of;
use syntax_pos::{DUMMY_SP, NO_EXPANSION, COMMAND_LINE_EXPN, BytePos}; use syntax_pos::{DUMMY_SP, NO_EXPANSION, COMMAND_LINE_EXPN, BytePos, Span};
use syntax::symbol::keywords; use syntax::symbol::keywords;
use syntax::abi::Abi;
use std::cell::Ref;
use std::iter; use std::iter;
use basic_block::BasicBlock;
use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::bitvec::BitVector;
use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use rustc_data_structures::indexed_vec::{IndexVec, Idx};
pub use self::constant::trans_static_initializer; pub use self::constant::trans_static_initializer;
use self::analyze::CleanupKind;
use self::lvalue::{LvalueRef}; use self::lvalue::{LvalueRef};
use rustc::mir::traversal; use rustc::mir::traversal;
use self::operand::{OperandRef, OperandValue}; use self::operand::{OperandRef, OperandValue};
/// Master context for translating MIR. /// Master context for translating MIR.
pub struct MirContext<'bcx, 'tcx:'bcx> { pub struct MirContext<'a, 'tcx:'a> {
mir: Ref<'tcx, mir::Mir<'tcx>>, mir: &'a mir::Mir<'tcx>,
/// Function context debug_context: debuginfo::FunctionDebugContext,
fcx: &'bcx common::FunctionContext<'bcx, 'tcx>,
fcx: &'a common::FunctionContext<'a, 'tcx>,
ccx: &'a CrateContext<'a, 'tcx>,
fn_ty: FnType,
/// When unwinding is initiated, we have to store this personality /// When unwinding is initiated, we have to store this personality
/// value somewhere so that we can load it and re-use it in the /// value somewhere so that we can load it and re-use it in the
@ -54,17 +64,17 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
llpersonalityslot: Option<ValueRef>, llpersonalityslot: Option<ValueRef>,
/// A `Block` for each MIR `BasicBlock` /// A `Block` for each MIR `BasicBlock`
blocks: IndexVec<mir::BasicBlock, Block<'bcx, 'tcx>>, blocks: IndexVec<mir::BasicBlock, BasicBlockRef>,
/// The funclet status of each basic block /// The funclet status of each basic block
cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>, cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>,
/// This stores the landing-pad block for a given BB, computed lazily on GNU /// This stores the landing-pad block for a given BB, computed lazily on GNU
/// and eagerly on MSVC. /// and eagerly on MSVC.
landing_pads: IndexVec<mir::BasicBlock, Option<Block<'bcx, 'tcx>>>, landing_pads: IndexVec<mir::BasicBlock, Option<BasicBlockRef>>,
/// Cached unreachable block /// Cached unreachable block
unreachable_block: Option<Block<'bcx, 'tcx>>, unreachable_block: Option<BasicBlockRef>,
/// The location where each MIR arg/var/tmp/ret is stored. This is /// The location where each MIR arg/var/tmp/ret is stored. This is
/// usually an `LvalueRef` representing an alloca, but not always: /// usually an `LvalueRef` representing an alloca, but not always:
@ -85,18 +95,28 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
/// Debug information for MIR scopes. /// Debug information for MIR scopes.
scopes: IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>, scopes: IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>,
/// If this function is being monomorphized, this contains the type substitutions used.
param_substs: &'tcx Substs<'tcx>,
} }
impl<'blk, 'tcx> MirContext<'blk, 'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> DebugLoc { pub fn monomorphize<T>(&self, value: &T) -> T
where T: TransNormalize<'tcx> {
monomorphize::apply_param_substs(self.ccx.shared(), self.param_substs, value)
}
pub fn set_debug_loc(&mut self, bcx: &BlockAndBuilder, source_info: mir::SourceInfo) {
let (scope, span) = self.debug_loc(source_info);
debuginfo::set_source_location(&self.debug_context, bcx, scope, span);
}
pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> (DIScope, Span) {
// Bail out if debug info emission is not enabled. // Bail out if debug info emission is not enabled.
match self.fcx.debug_context { match self.debug_context {
FunctionDebugContext::DebugInfoDisabled | FunctionDebugContext::DebugInfoDisabled |
FunctionDebugContext::FunctionWithoutDebugInfo => { FunctionDebugContext::FunctionWithoutDebugInfo => {
// Can't return DebugLoc::None here because intrinsic::trans_intrinsic_call() return (self.scopes[source_info.scope].scope_metadata, source_info.span);
// relies on debug location to obtain span of the call site.
return DebugLoc::ScopeAt(self.scopes[source_info.scope].scope_metadata,
source_info.span);
} }
FunctionDebugContext::RegularContext(_) =>{} FunctionDebugContext::RegularContext(_) =>{}
} }
@ -106,13 +126,12 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> {
// (unless the crate is being compiled with `-Z debug-macros`). // (unless the crate is being compiled with `-Z debug-macros`).
if source_info.span.expn_id == NO_EXPANSION || if source_info.span.expn_id == NO_EXPANSION ||
source_info.span.expn_id == COMMAND_LINE_EXPN || source_info.span.expn_id == COMMAND_LINE_EXPN ||
self.fcx.ccx.sess().opts.debugging_opts.debug_macros { self.ccx.sess().opts.debugging_opts.debug_macros {
let scope_metadata = self.scope_metadata_for_loc(source_info.scope, let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo);
source_info.span.lo); (scope, source_info.span)
DebugLoc::ScopeAt(scope_metadata, source_info.span)
} else { } else {
let cm = self.fcx.ccx.sess().codemap(); let cm = self.ccx.sess().codemap();
// Walk up the macro expansion chain until we reach a non-expanded span. // Walk up the macro expansion chain until we reach a non-expanded span.
let mut span = source_info.span; let mut span = source_info.span;
while span.expn_id != NO_EXPANSION && span.expn_id != COMMAND_LINE_EXPN { while span.expn_id != NO_EXPANSION && span.expn_id != COMMAND_LINE_EXPN {
@ -123,9 +142,9 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> {
break; break;
} }
} }
let scope_metadata = self.scope_metadata_for_loc(source_info.scope, span.lo); let scope = self.scope_metadata_for_loc(source_info.scope, span.lo);
// Use span of the outermost call site, while keeping the original lexical scope // Use span of the outermost call site, while keeping the original lexical scope
DebugLoc::ScopeAt(scope_metadata, span) (scope, span)
} }
} }
@ -138,10 +157,8 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> {
let scope_metadata = self.scopes[scope_id].scope_metadata; let scope_metadata = self.scopes[scope_id].scope_metadata;
if pos < self.scopes[scope_id].file_start_pos || if pos < self.scopes[scope_id].file_start_pos ||
pos >= self.scopes[scope_id].file_end_pos { pos >= self.scopes[scope_id].file_end_pos {
let cm = self.fcx.ccx.sess().codemap(); let cm = self.ccx.sess().codemap();
debuginfo::extend_scope_to_file(self.fcx.ccx, debuginfo::extend_scope_to_file(self.ccx, scope_metadata, &cm.lookup_char_pos(pos).file)
scope_metadata,
&cm.lookup_char_pos(pos).file)
} else { } else {
scope_metadata scope_metadata
} }
@ -154,7 +171,7 @@ enum LocalRef<'tcx> {
} }
impl<'tcx> LocalRef<'tcx> { impl<'tcx> LocalRef<'tcx> {
fn new_operand<'bcx>(ccx: &CrateContext<'bcx, 'tcx>, fn new_operand<'a>(ccx: &CrateContext<'a, 'tcx>,
ty: ty::Ty<'tcx>) -> LocalRef<'tcx> { ty: ty::Ty<'tcx>) -> LocalRef<'tcx> {
if common::type_is_zero_size(ccx, ty) { if common::type_is_zero_size(ccx, ty) {
// Zero-size temporaries aren't always initialized, which // Zero-size temporaries aren't always initialized, which
@ -180,19 +197,22 @@ impl<'tcx> LocalRef<'tcx> {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { pub fn trans_mir<'a, 'tcx: 'a>(
let bcx = fcx.init(true).build(); fcx: &'a FunctionContext<'a, 'tcx>,
let mir = bcx.mir(); fn_ty: FnType,
mir: &'a Mir<'tcx>,
instance: Instance<'tcx>,
sig: &ty::FnSig<'tcx>,
abi: Abi,
) {
let debug_context =
debuginfo::create_function_debug_context(fcx.ccx, instance, sig, abi, fcx.llfn, mir);
let bcx = fcx.get_entry_block();
// Analyze the temps to determine which must be lvalues let cleanup_kinds = analyze::cleanup_kinds(&mir);
// FIXME
let (lvalue_locals, cleanup_kinds) = bcx.with_block(|bcx| {
(analyze::lvalue_locals(bcx, &mir),
analyze::cleanup_kinds(bcx, &mir))
});
// Allocate a `Block` for every basic block // Allocate a `Block` for every basic block
let block_bcxs: IndexVec<mir::BasicBlock, Block<'blk,'tcx>> = let block_bcxs: IndexVec<mir::BasicBlock, BasicBlockRef> =
mir.basic_blocks().indices().map(|bb| { mir.basic_blocks().indices().map(|bb| {
if bb == mir::START_BLOCK { if bb == mir::START_BLOCK {
fcx.new_block("start") fcx.new_block("start")
@ -202,11 +222,13 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
}).collect(); }).collect();
// Compute debuginfo scopes from MIR scopes. // Compute debuginfo scopes from MIR scopes.
let scopes = debuginfo::create_mir_scopes(fcx); let scopes = debuginfo::create_mir_scopes(fcx, mir, &debug_context);
let mut mircx = MirContext { let mut mircx = MirContext {
mir: Ref::clone(&mir), mir: mir,
fcx: fcx, fcx: fcx,
fn_ty: fn_ty,
ccx: fcx.ccx,
llpersonalityslot: None, llpersonalityslot: None,
blocks: block_bcxs, blocks: block_bcxs,
unreachable_block: None, unreachable_block: None,
@ -214,15 +236,22 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()), landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
scopes: scopes, scopes: scopes,
locals: IndexVec::new(), locals: IndexVec::new(),
debug_context: debug_context,
param_substs: {
assert!(!instance.substs.needs_infer());
instance.substs
},
}; };
let lvalue_locals = analyze::lvalue_locals(&mircx);
// Allocate variable and temp allocas // Allocate variable and temp allocas
mircx.locals = { mircx.locals = {
let args = arg_local_refs(&bcx, &mir, &mircx.scopes, &lvalue_locals); let args = arg_local_refs(&bcx, &mircx, &mircx.scopes, &lvalue_locals);
let mut allocate_local = |local| { let mut allocate_local = |local| {
let decl = &mir.local_decls[local]; let decl = &mir.local_decls[local];
let ty = bcx.monomorphize(&decl.ty); let ty = mircx.monomorphize(&decl.ty);
if let Some(name) = decl.name { if let Some(name) = decl.name {
// User variable // User variable
@ -232,27 +261,21 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
if !lvalue_locals.contains(local.index()) && !dbg { if !lvalue_locals.contains(local.index()) && !dbg {
debug!("alloc: {:?} ({}) -> operand", local, name); debug!("alloc: {:?} ({}) -> operand", local, name);
return LocalRef::new_operand(bcx.ccx(), ty); return LocalRef::new_operand(bcx.ccx, ty);
} }
debug!("alloc: {:?} ({}) -> lvalue", local, name); debug!("alloc: {:?} ({}) -> lvalue", local, name);
let lvalue = LvalueRef::alloca(&bcx, ty, &name.as_str()); let lvalue = LvalueRef::alloca(&bcx, ty, &name.as_str());
if dbg { if dbg {
let dbg_loc = mircx.debug_loc(source_info); let (scope, span) = mircx.debug_loc(source_info);
if let DebugLoc::ScopeAt(scope, span) = dbg_loc { declare_local(&bcx, &mircx.debug_context, name, ty, scope,
bcx.with_block(|bcx| { VariableAccess::DirectVariable { alloca: lvalue.llval },
declare_local(bcx, name, ty, scope, VariableKind::LocalVariable, span);
VariableAccess::DirectVariable { alloca: lvalue.llval },
VariableKind::LocalVariable, span);
});
} else {
panic!("Unexpected");
}
} }
LocalRef::Lvalue(lvalue) LocalRef::Lvalue(lvalue)
} else { } else {
// Temporary or return pointer // Temporary or return pointer
if local == mir::RETURN_POINTER && fcx.fn_ty.ret.is_indirect() { if local == mir::RETURN_POINTER && mircx.fn_ty.ret.is_indirect() {
debug!("alloc: {:?} (return pointer) -> lvalue", local); debug!("alloc: {:?} (return pointer) -> lvalue", local);
let llretptr = llvm::get_param(fcx.llfn, 0); let llretptr = llvm::get_param(fcx.llfn, 0);
LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty))) LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty)))
@ -264,7 +287,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
// alloca in advance. Instead we wait until we see the // alloca in advance. Instead we wait until we see the
// definition and update the operand there. // definition and update the operand there.
debug!("alloc: {:?} -> operand", local); debug!("alloc: {:?} -> operand", local);
LocalRef::new_operand(bcx.ccx(), ty) LocalRef::new_operand(bcx.ccx, ty)
} }
} }
}; };
@ -278,57 +301,61 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
// Branch to the START block // Branch to the START block
let start_bcx = mircx.blocks[mir::START_BLOCK]; let start_bcx = mircx.blocks[mir::START_BLOCK];
bcx.br(start_bcx.llbb); bcx.br(start_bcx);
// Up until here, IR instructions for this function have explicitly not been annotated with // Up until here, IR instructions for this function have explicitly not been annotated with
// source code location, so we don't step into call setup code. From here on, source location // source code location, so we don't step into call setup code. From here on, source location
// emitting should be enabled. // emitting should be enabled.
debuginfo::start_emitting_source_locations(fcx); debuginfo::start_emitting_source_locations(&mircx.debug_context);
let funclets: IndexVec<mir::BasicBlock, Option<Funclet>> =
mircx.cleanup_kinds.iter_enumerated().map(|(bb, cleanup_kind)| {
if let CleanupKind::Funclet = *cleanup_kind {
let bcx = mircx.build_block(bb);
bcx.set_personality_fn(mircx.ccx.eh_personality());
if base::wants_msvc_seh(fcx.ccx.sess()) {
return Some(Funclet::new(bcx.cleanup_pad(None, &[])));
}
}
None
}).collect();
let rpo = traversal::reverse_postorder(&mir);
let mut visited = BitVector::new(mir.basic_blocks().len()); let mut visited = BitVector::new(mir.basic_blocks().len());
let mut rpo = traversal::reverse_postorder(&mir);
// Prepare each block for translation.
for (bb, _) in rpo.by_ref() {
mircx.init_cpad(bb);
}
rpo.reset();
// Translate the body of each block using reverse postorder // Translate the body of each block using reverse postorder
for (bb, _) in rpo { for (bb, _) in rpo {
visited.insert(bb.index()); visited.insert(bb.index());
mircx.trans_block(bb); mircx.trans_block(bb, &funclets);
} }
// Remove blocks that haven't been visited, or have no // Remove blocks that haven't been visited, or have no
// predecessors. // predecessors.
for bb in mir.basic_blocks().indices() { for bb in mir.basic_blocks().indices() {
let block = mircx.blocks[bb];
let block = BasicBlock(block.llbb);
// Unreachable block // Unreachable block
if !visited.contains(bb.index()) { if !visited.contains(bb.index()) {
debug!("trans_mir: block {:?} was not visited", bb); debug!("trans_mir: block {:?} was not visited", bb);
block.delete(); unsafe {
llvm::LLVMDeleteBasicBlock(mircx.blocks[bb]);
}
} }
} }
DebugLoc::None.apply(fcx);
fcx.cleanup();
} }
/// Produce, for each argument, a `ValueRef` pointing at the /// Produce, for each argument, a `ValueRef` pointing at the
/// argument's value. As arguments are lvalues, these are always /// argument's value. As arguments are lvalues, these are always
/// indirect. /// indirect.
fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, fn arg_local_refs<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>,
mir: &mir::Mir<'tcx>, mircx: &MirContext<'a, 'tcx>,
scopes: &IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>, scopes: &IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>,
lvalue_locals: &BitVector) lvalue_locals: &BitVector)
-> Vec<LocalRef<'tcx>> { -> Vec<LocalRef<'tcx>> {
let mir = mircx.mir;
let fcx = bcx.fcx(); let fcx = bcx.fcx();
let tcx = bcx.tcx(); let tcx = bcx.tcx();
let mut idx = 0; let mut idx = 0;
let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize; let mut llarg_idx = mircx.fn_ty.ret.is_indirect() as usize;
// Get the argument scope, if it exists and if we need it. // Get the argument scope, if it exists and if we need it.
let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE]; let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE];
@ -340,7 +367,7 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
mir.args_iter().enumerate().map(|(arg_index, local)| { mir.args_iter().enumerate().map(|(arg_index, local)| {
let arg_decl = &mir.local_decls[local]; let arg_decl = &mir.local_decls[local];
let arg_ty = bcx.monomorphize(&arg_decl.ty); let arg_ty = mircx.monomorphize(&arg_decl.ty);
if Some(local) == mir.spread_arg { if Some(local) == mir.spread_arg {
// This argument (e.g. the last argument in the "rust-call" ABI) // This argument (e.g. the last argument in the "rust-call" ABI)
@ -353,22 +380,18 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
_ => bug!("spread argument isn't a tuple?!") _ => bug!("spread argument isn't a tuple?!")
}; };
let lltemp = bcx.with_block(|bcx| { let lltemp = base::alloc_ty(&bcx, arg_ty, &format!("arg{}", arg_index));
base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index))
});
for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() {
let dst = bcx.struct_gep(lltemp, i); let dst = bcx.struct_gep(lltemp, i);
let arg = &fcx.fn_ty.args[idx]; let arg = &mircx.fn_ty.args[idx];
idx += 1; idx += 1;
if common::type_is_fat_ptr(tcx, tupled_arg_ty) { if common::type_is_fat_ptr(bcx.ccx, tupled_arg_ty) {
// We pass fat pointers as two words, but inside the tuple // We pass fat pointers as two words, but inside the tuple
// they are the two sub-fields of a single aggregate field. // they are the two sub-fields of a single aggregate field.
let meta = &fcx.fn_ty.args[idx]; let meta = &mircx.fn_ty.args[idx];
idx += 1; idx += 1;
arg.store_fn_arg(bcx, &mut llarg_idx, arg.store_fn_arg(bcx, &mut llarg_idx, base::get_dataptr(bcx, dst));
base::get_dataptr_builder(bcx, dst)); meta.store_fn_arg(bcx, &mut llarg_idx, base::get_meta(bcx, dst));
meta.store_fn_arg(bcx, &mut llarg_idx,
base::get_meta_builder(bcx, dst));
} else { } else {
arg.store_fn_arg(bcx, &mut llarg_idx, dst); arg.store_fn_arg(bcx, &mut llarg_idx, dst);
} }
@ -376,20 +399,25 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
// Now that we have one alloca that contains the aggregate value, // Now that we have one alloca that contains the aggregate value,
// we can create one debuginfo entry for the argument. // we can create one debuginfo entry for the argument.
bcx.with_block(|bcx| arg_scope.map(|scope| { arg_scope.map(|scope| {
let variable_access = VariableAccess::DirectVariable { let variable_access = VariableAccess::DirectVariable {
alloca: lltemp alloca: lltemp
}; };
declare_local(bcx, arg_decl.name.unwrap_or(keywords::Invalid.name()), declare_local(
arg_ty, scope, variable_access, bcx,
VariableKind::ArgumentVariable(arg_index + 1), &mircx.debug_context,
bcx.fcx().span.unwrap_or(DUMMY_SP)); arg_decl.name.unwrap_or(keywords::Invalid.name()),
})); arg_ty, scope,
variable_access,
VariableKind::ArgumentVariable(arg_index + 1),
DUMMY_SP
);
});
return LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty))); return LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty)));
} }
let arg = &fcx.fn_ty.args[idx]; let arg = &mircx.fn_ty.args[idx];
idx += 1; idx += 1;
let llval = if arg.is_indirect() && bcx.sess().opts.debuginfo != FullDebugInfo { let llval = if arg.is_indirect() && bcx.sess().opts.debuginfo != FullDebugInfo {
// Don't copy an indirect argument to an alloca, the caller // Don't copy an indirect argument to an alloca, the caller
@ -406,7 +434,7 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
!arg.is_indirect() && arg.cast.is_none() && !arg.is_indirect() && arg.cast.is_none() &&
arg_scope.is_none() { arg_scope.is_none() {
if arg.is_ignore() { if arg.is_ignore() {
return LocalRef::new_operand(bcx.ccx(), arg_ty); return LocalRef::new_operand(bcx.ccx, arg_ty);
} }
// We don't have to cast or keep the argument in the alloca. // We don't have to cast or keep the argument in the alloca.
@ -417,8 +445,8 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
} }
let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
llarg_idx += 1; llarg_idx += 1;
let val = if common::type_is_fat_ptr(tcx, arg_ty) { let val = if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
let meta = &fcx.fn_ty.args[idx]; let meta = &mircx.fn_ty.args[idx];
idx += 1; idx += 1;
assert_eq!((meta.cast, meta.pad), (None, None)); assert_eq!((meta.cast, meta.pad), (None, None));
let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint); let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
@ -433,19 +461,15 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
}; };
return LocalRef::Operand(Some(operand.unpack_if_pair(bcx))); return LocalRef::Operand(Some(operand.unpack_if_pair(bcx)));
} else { } else {
let lltemp = bcx.with_block(|bcx| { let lltemp = base::alloc_ty(&bcx, arg_ty, &format!("arg{}", arg_index));
base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)) if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
});
if common::type_is_fat_ptr(tcx, arg_ty) {
// we pass fat pointers as two words, but we want to // we pass fat pointers as two words, but we want to
// represent them internally as a pointer to two words, // represent them internally as a pointer to two words,
// so make an alloca to store them in. // so make an alloca to store them in.
let meta = &fcx.fn_ty.args[idx]; let meta = &mircx.fn_ty.args[idx];
idx += 1; idx += 1;
arg.store_fn_arg(bcx, &mut llarg_idx, arg.store_fn_arg(bcx, &mut llarg_idx, base::get_dataptr(bcx, lltemp));
base::get_dataptr_builder(bcx, lltemp)); meta.store_fn_arg(bcx, &mut llarg_idx, base::get_meta(bcx, lltemp));
meta.store_fn_arg(bcx, &mut llarg_idx,
base::get_meta_builder(bcx, lltemp));
} else { } else {
// otherwise, arg is passed by value, so make a // otherwise, arg is passed by value, so make a
// temporary and store it there // temporary and store it there
@ -453,13 +477,19 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
} }
lltemp lltemp
}; };
bcx.with_block(|bcx| arg_scope.map(|scope| { arg_scope.map(|scope| {
// Is this a regular argument? // Is this a regular argument?
if arg_index > 0 || mir.upvar_decls.is_empty() { if arg_index > 0 || mir.upvar_decls.is_empty() {
declare_local(bcx, arg_decl.name.unwrap_or(keywords::Invalid.name()), arg_ty, declare_local(
scope, VariableAccess::DirectVariable { alloca: llval }, bcx,
VariableKind::ArgumentVariable(arg_index + 1), &mircx.debug_context,
bcx.fcx().span.unwrap_or(DUMMY_SP)); arg_decl.name.unwrap_or(keywords::Invalid.name()),
arg_ty,
scope,
VariableAccess::DirectVariable { alloca: llval },
VariableKind::ArgumentVariable(arg_index + 1),
DUMMY_SP
);
return; return;
} }
@ -483,17 +513,14 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
// doesn't actually strip the offset when splitting the closure // doesn't actually strip the offset when splitting the closure
// environment into its components so it ends up out of bounds. // environment into its components so it ends up out of bounds.
let env_ptr = if !env_ref { let env_ptr = if !env_ref {
use base::*; let alloc = bcx.fcx().alloca(common::val_ty(llval), "__debuginfo_env_ptr");
use build::*; bcx.store(llval, alloc);
use common::*;
let alloc = alloca(bcx, val_ty(llval), "__debuginfo_env_ptr");
Store(bcx, llval, alloc);
alloc alloc
} else { } else {
llval llval
}; };
let layout = bcx.ccx().layout_of(closure_ty); let layout = bcx.ccx.layout_of(closure_ty);
let offsets = match *layout { let offsets = match *layout {
layout::Univariant { ref variant, .. } => &variant.offsets[..], layout::Univariant { ref variant, .. } => &variant.offsets[..],
_ => bug!("Closures are only supposed to be Univariant") _ => bug!("Closures are only supposed to be Univariant")
@ -502,7 +529,6 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() { for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() {
let byte_offset_of_var_in_env = offsets[i].bytes(); let byte_offset_of_var_in_env = offsets[i].bytes();
let ops = unsafe { let ops = unsafe {
[llvm::LLVMRustDIBuilderCreateOpDeref(), [llvm::LLVMRustDIBuilderCreateOpDeref(),
llvm::LLVMRustDIBuilderCreateOpPlus(), llvm::LLVMRustDIBuilderCreateOpPlus(),
@ -527,11 +553,18 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
alloca: env_ptr, alloca: env_ptr,
address_operations: &ops address_operations: &ops
}; };
declare_local(bcx, decl.debug_name, ty, scope, variable_access, declare_local(
VariableKind::CapturedVariable, bcx,
bcx.fcx().span.unwrap_or(DUMMY_SP)); &mircx.debug_context,
decl.debug_name,
ty,
scope,
variable_access,
VariableKind::CapturedVariable,
DUMMY_SP
);
} }
})); });
LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))) LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)))
}).collect() }).collect()
} }

View file

@ -14,7 +14,7 @@ use rustc::mir;
use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::Idx;
use base; use base;
use common::{self, Block, BlockAndBuilder}; use common::{self, BlockAndBuilder};
use value::Value; use value::Value;
use type_of; use type_of;
use type_::Type; use type_::Type;
@ -73,7 +73,7 @@ impl<'tcx> fmt::Debug for OperandRef<'tcx> {
} }
} }
impl<'bcx, 'tcx> OperandRef<'tcx> { impl<'a, 'tcx> OperandRef<'tcx> {
/// Asserts that this operand refers to a scalar and returns /// Asserts that this operand refers to a scalar and returns
/// a reference to its value. /// a reference to its value.
pub fn immediate(self) -> ValueRef { pub fn immediate(self) -> ValueRef {
@ -85,18 +85,18 @@ impl<'bcx, 'tcx> OperandRef<'tcx> {
/// If this operand is a Pair, we return an /// If this operand is a Pair, we return an
/// Immediate aggregate with the two values. /// Immediate aggregate with the two values.
pub fn pack_if_pair(mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>) pub fn pack_if_pair(mut self, bcx: &BlockAndBuilder<'a, 'tcx>)
-> OperandRef<'tcx> { -> OperandRef<'tcx> {
if let OperandValue::Pair(a, b) = self.val { if let OperandValue::Pair(a, b) = self.val {
// Reconstruct the immediate aggregate. // Reconstruct the immediate aggregate.
let llty = type_of::type_of(bcx.ccx(), self.ty); let llty = type_of::type_of(bcx.ccx, self.ty);
let mut llpair = common::C_undef(llty); let mut llpair = common::C_undef(llty);
let elems = [a, b]; let elems = [a, b];
for i in 0..2 { for i in 0..2 {
let mut elem = elems[i]; let mut elem = elems[i];
// Extend boolean i1's to i8. // Extend boolean i1's to i8.
if common::val_ty(elem) == Type::i1(bcx.ccx()) { if common::val_ty(elem) == Type::i1(bcx.ccx) {
elem = bcx.zext(elem, Type::i8(bcx.ccx())); elem = bcx.zext(elem, Type::i8(bcx.ccx));
} }
llpair = bcx.insert_value(llpair, elem, i); llpair = bcx.insert_value(llpair, elem, i);
} }
@ -107,23 +107,23 @@ impl<'bcx, 'tcx> OperandRef<'tcx> {
/// If this operand is a pair in an Immediate, /// If this operand is a pair in an Immediate,
/// we return a Pair with the two halves. /// we return a Pair with the two halves.
pub fn unpack_if_pair(mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>) pub fn unpack_if_pair(mut self, bcx: &BlockAndBuilder<'a, 'tcx>)
-> OperandRef<'tcx> { -> OperandRef<'tcx> {
if let OperandValue::Immediate(llval) = self.val { if let OperandValue::Immediate(llval) = self.val {
// Deconstruct the immediate aggregate. // Deconstruct the immediate aggregate.
if common::type_is_imm_pair(bcx.ccx(), self.ty) { if common::type_is_imm_pair(bcx.ccx, self.ty) {
debug!("Operand::unpack_if_pair: unpacking {:?}", self); debug!("Operand::unpack_if_pair: unpacking {:?}", self);
let mut a = bcx.extract_value(llval, 0); let mut a = bcx.extract_value(llval, 0);
let mut b = bcx.extract_value(llval, 1); let mut b = bcx.extract_value(llval, 1);
let pair_fields = common::type_pair_fields(bcx.ccx(), self.ty); let pair_fields = common::type_pair_fields(bcx.ccx, self.ty);
if let Some([a_ty, b_ty]) = pair_fields { if let Some([a_ty, b_ty]) = pair_fields {
if a_ty.is_bool() { if a_ty.is_bool() {
a = bcx.trunc(a, Type::i1(bcx.ccx())); a = bcx.trunc(a, Type::i1(bcx.ccx));
} }
if b_ty.is_bool() { if b_ty.is_bool() {
b = bcx.trunc(b, Type::i1(bcx.ccx())); b = bcx.trunc(b, Type::i1(bcx.ccx));
} }
} }
@ -134,29 +134,29 @@ impl<'bcx, 'tcx> OperandRef<'tcx> {
} }
} }
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_load(&mut self, pub fn trans_load(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
llval: ValueRef, llval: ValueRef,
ty: Ty<'tcx>) ty: Ty<'tcx>)
-> OperandRef<'tcx> -> OperandRef<'tcx>
{ {
debug!("trans_load: {:?} @ {:?}", Value(llval), ty); debug!("trans_load: {:?} @ {:?}", Value(llval), ty);
let val = if common::type_is_fat_ptr(bcx.tcx(), ty) { let val = if common::type_is_fat_ptr(bcx.ccx, ty) {
let (lldata, llextra) = base::load_fat_ptr_builder(bcx, llval, ty); let (lldata, llextra) = base::load_fat_ptr(bcx, llval, ty);
OperandValue::Pair(lldata, llextra) OperandValue::Pair(lldata, llextra)
} else if common::type_is_imm_pair(bcx.ccx(), ty) { } else if common::type_is_imm_pair(bcx.ccx, ty) {
let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx(), ty).unwrap(); let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx, ty).unwrap();
let a_ptr = bcx.struct_gep(llval, 0); let a_ptr = bcx.struct_gep(llval, 0);
let b_ptr = bcx.struct_gep(llval, 1); let b_ptr = bcx.struct_gep(llval, 1);
OperandValue::Pair( OperandValue::Pair(
base::load_ty_builder(bcx, a_ptr, a_ty), base::load_ty(bcx, a_ptr, a_ty),
base::load_ty_builder(bcx, b_ptr, b_ty) base::load_ty(bcx, b_ptr, b_ty)
) )
} else if common::type_is_immediate(bcx.ccx(), ty) { } else if common::type_is_immediate(bcx.ccx, ty) {
OperandValue::Immediate(base::load_ty_builder(bcx, llval, ty)) OperandValue::Immediate(base::load_ty(bcx, llval, ty))
} else { } else {
OperandValue::Ref(llval) OperandValue::Ref(llval)
}; };
@ -165,7 +165,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
pub fn trans_consume(&mut self, pub fn trans_consume(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
lvalue: &mir::Lvalue<'tcx>) lvalue: &mir::Lvalue<'tcx>)
-> OperandRef<'tcx> -> OperandRef<'tcx>
{ {
@ -197,7 +197,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let llval = [a, b][f.index()]; let llval = [a, b][f.index()];
let op = OperandRef { let op = OperandRef {
val: OperandValue::Immediate(llval), val: OperandValue::Immediate(llval),
ty: bcx.monomorphize(&ty) ty: self.monomorphize(&ty)
}; };
// Handle nested pairs. // Handle nested pairs.
@ -217,7 +217,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
pub fn trans_operand(&mut self, pub fn trans_operand(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
operand: &mir::Operand<'tcx>) operand: &mir::Operand<'tcx>)
-> OperandRef<'tcx> -> OperandRef<'tcx>
{ {
@ -230,7 +230,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::Operand::Constant(ref constant) => { mir::Operand::Constant(ref constant) => {
let val = self.trans_constant(bcx, constant); let val = self.trans_constant(bcx, constant);
let operand = val.to_operand(bcx.ccx()); let operand = val.to_operand(bcx.ccx);
if let OperandValue::Ref(ptr) = operand.val { if let OperandValue::Ref(ptr) = operand.val {
// If this is a OperandValue::Ref to an immediate constant, load it. // If this is a OperandValue::Ref to an immediate constant, load it.
self.trans_load(bcx, ptr, operand.ty) self.trans_load(bcx, ptr, operand.ty)
@ -242,33 +242,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
pub fn store_operand(&mut self, pub fn store_operand(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
lldest: ValueRef, lldest: ValueRef,
operand: OperandRef<'tcx>) operand: OperandRef<'tcx>) {
{ debug!("store_operand: operand={:?}", operand);
debug!("store_operand: operand={:?} lldest={:?}", operand, lldest);
bcx.with_block(|bcx| self.store_operand_direct(bcx, lldest, operand))
}
pub fn store_operand_direct(&mut self,
bcx: Block<'bcx, 'tcx>,
lldest: ValueRef,
operand: OperandRef<'tcx>)
{
// Avoid generating stores of zero-sized values, because the only way to have a zero-sized // Avoid generating stores of zero-sized values, because the only way to have a zero-sized
// value is through `undef`, and store itself is useless. // value is through `undef`, and store itself is useless.
if common::type_is_zero_size(bcx.ccx(), operand.ty) { if common::type_is_zero_size(bcx.ccx, operand.ty) {
return; return;
} }
match operand.val { match operand.val {
OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty), OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty),
OperandValue::Immediate(s) => base::store_ty(bcx, s, lldest, operand.ty), OperandValue::Immediate(s) => base::store_ty(bcx, s, lldest, operand.ty),
OperandValue::Pair(a, b) => { OperandValue::Pair(a, b) => {
use build::*;
let a = base::from_immediate(bcx, a); let a = base::from_immediate(bcx, a);
let b = base::from_immediate(bcx, b); let b = base::from_immediate(bcx, b);
Store(bcx, a, StructGEP(bcx, lldest, 0)); bcx.store(a, bcx.struct_gep(lldest, 0));
Store(bcx, b, StructGEP(bcx, lldest, 1)); bcx.store(b, bcx.struct_gep(lldest, 1));
} }
} }
} }

View file

@ -13,13 +13,13 @@ use rustc::ty::{self, Ty};
use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::layout::Layout; use rustc::ty::layout::Layout;
use rustc::mir; use rustc::mir;
use middle::lang_items::ExchangeMallocFnLangItem;
use asm; use asm;
use base; use base;
use callee::Callee; use callee::Callee;
use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder, Result}; use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder};
use common::{C_integral}; use common::{C_integral};
use debuginfo::DebugLoc;
use adt; use adt;
use machine; use machine;
use type_::Type; use type_::Type;
@ -33,13 +33,12 @@ use super::constant::const_scalar_checked_binop;
use super::operand::{OperandRef, OperandValue}; use super::operand::{OperandRef, OperandValue};
use super::lvalue::{LvalueRef}; use super::lvalue::{LvalueRef};
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_rvalue(&mut self, pub fn trans_rvalue(&mut self,
bcx: BlockAndBuilder<'bcx, 'tcx>, bcx: BlockAndBuilder<'a, 'tcx>,
dest: LvalueRef<'tcx>, dest: LvalueRef<'tcx>,
rvalue: &mir::Rvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>)
debug_loc: DebugLoc) -> BlockAndBuilder<'a, 'tcx>
-> BlockAndBuilder<'bcx, 'tcx>
{ {
debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})", debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})",
Value(dest.llval), rvalue); Value(dest.llval), rvalue);
@ -54,12 +53,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, cast_ty) => { mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, cast_ty) => {
let cast_ty = bcx.monomorphize(&cast_ty); let cast_ty = self.monomorphize(&cast_ty);
if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { if common::type_is_fat_ptr(bcx.ccx, cast_ty) {
// into-coerce of a thin pointer to a fat pointer - just // into-coerce of a thin pointer to a fat pointer - just
// use the operand path. // use the operand path.
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue, debug_loc); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
self.store_operand(&bcx, dest.llval, temp); self.store_operand(&bcx, dest.llval, temp);
return bcx; return bcx;
} }
@ -70,71 +69,57 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// so the (generic) MIR may not be able to expand it. // so the (generic) MIR may not be able to expand it.
let operand = self.trans_operand(&bcx, source); let operand = self.trans_operand(&bcx, source);
let operand = operand.pack_if_pair(&bcx); let operand = operand.pack_if_pair(&bcx);
bcx.with_block(|bcx| { let llref = match operand.val {
match operand.val { OperandValue::Pair(..) => bug!(),
OperandValue::Pair(..) => bug!(), OperandValue::Immediate(llval) => {
OperandValue::Immediate(llval) => { // unsize from an immediate structure. We don't
// unsize from an immediate structure. We don't // really need a temporary alloca here, but
// really need a temporary alloca here, but // avoiding it would require us to have
// avoiding it would require us to have // `coerce_unsized_into` use extractvalue to
// `coerce_unsized_into` use extractvalue to // index into the struct, and this case isn't
// index into the struct, and this case isn't // important enough for it.
// important enough for it. debug!("trans_rvalue: creating ugly alloca");
debug!("trans_rvalue: creating ugly alloca"); let lltemp = base::alloc_ty(&bcx, operand.ty, "__unsize_temp");
let lltemp = base::alloc_ty(bcx, operand.ty, "__unsize_temp"); base::store_ty(&bcx, llval, lltemp, operand.ty);
base::store_ty(bcx, llval, lltemp, operand.ty); lltemp
base::coerce_unsized_into(bcx,
lltemp, operand.ty,
dest.llval, cast_ty);
}
OperandValue::Ref(llref) => {
base::coerce_unsized_into(bcx,
llref, operand.ty,
dest.llval, cast_ty);
}
} }
}); OperandValue::Ref(llref) => llref
};
base::coerce_unsized_into(&bcx, llref, operand.ty, dest.llval, cast_ty);
bcx bcx
} }
mir::Rvalue::Repeat(ref elem, ref count) => { mir::Rvalue::Repeat(ref elem, ref count) => {
let tr_elem = self.trans_operand(&bcx, elem); let tr_elem = self.trans_operand(&bcx, elem);
let size = count.value.as_u64(bcx.tcx().sess.target.uint_type); let size = count.value.as_u64(bcx.tcx().sess.target.uint_type);
let size = C_uint(bcx.ccx(), size); let size = C_uint(bcx.ccx, size);
let base = base::get_dataptr_builder(&bcx, dest.llval); let base = base::get_dataptr(&bcx, dest.llval);
let bcx = bcx.map_block(|block| { tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot| {
tvec::slice_for_each(block, base, tr_elem.ty, size, |block, llslot| { self.store_operand(bcx, llslot, tr_elem);
self.store_operand_direct(block, llslot, tr_elem); })
block
})
});
bcx
} }
mir::Rvalue::Aggregate(ref kind, ref operands) => { mir::Rvalue::Aggregate(ref kind, ref operands) => {
match *kind { match *kind {
mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
let disr = Disr::from(adt_def.variants[variant_index].disr_val); let disr = Disr::from(adt_def.variants[variant_index].disr_val);
bcx.with_block(|bcx| { let dest_ty = dest.ty.to_ty(bcx.tcx());
adt::trans_set_discr(bcx, adt::trans_set_discr(&bcx, dest_ty, dest.llval, Disr::from(disr));
dest.ty.to_ty(bcx.tcx()), dest.llval, Disr::from(disr));
});
for (i, operand) in operands.iter().enumerate() { for (i, operand) in operands.iter().enumerate() {
let op = self.trans_operand(&bcx, operand); let op = self.trans_operand(&bcx, operand);
// Do not generate stores and GEPis for zero-sized fields. // Do not generate stores and GEPis for zero-sized fields.
if !common::type_is_zero_size(bcx.ccx(), op.ty) { if !common::type_is_zero_size(bcx.ccx, op.ty) {
let val = adt::MaybeSizedValue::sized(dest.llval); let val = adt::MaybeSizedValue::sized(dest.llval);
let field_index = active_field_index.unwrap_or(i); let field_index = active_field_index.unwrap_or(i);
let lldest_i = adt::trans_field_ptr_builder(&bcx, let lldest_i = adt::trans_field_ptr(&bcx, dest_ty, val, disr,
dest.ty.to_ty(bcx.tcx()), field_index);
val, disr, field_index);
self.store_operand(&bcx, lldest_i, op); self.store_operand(&bcx, lldest_i, op);
} }
} }
}, },
_ => { _ => {
// If this is a tuple or closure, we need to translate GEP indices. // If this is a tuple or closure, we need to translate GEP indices.
let layout = bcx.ccx().layout_of(dest.ty.to_ty(bcx.tcx())); let layout = bcx.ccx.layout_of(dest.ty.to_ty(bcx.tcx()));
let translation = if let Layout::Univariant { ref variant, .. } = *layout { let translation = if let Layout::Univariant { ref variant, .. } = *layout {
Some(&variant.memory_index) Some(&variant.memory_index)
} else { } else {
@ -143,7 +128,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
for (i, operand) in operands.iter().enumerate() { for (i, operand) in operands.iter().enumerate() {
let op = self.trans_operand(&bcx, operand); let op = self.trans_operand(&bcx, operand);
// Do not generate stores and GEPis for zero-sized fields. // Do not generate stores and GEPis for zero-sized fields.
if !common::type_is_zero_size(bcx.ccx(), op.ty) { if !common::type_is_zero_size(bcx.ccx, op.ty) {
// Note: perhaps this should be StructGep, but // Note: perhaps this should be StructGep, but
// note that in some cases the values here will // note that in some cases the values here will
// not be structs but arrays. // not be structs but arrays.
@ -171,16 +156,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
self.trans_operand(&bcx, input).immediate() self.trans_operand(&bcx, input).immediate()
}).collect(); }).collect();
bcx.with_block(|bcx| { asm::trans_inline_asm(&bcx, asm, outputs, input_vals);
asm::trans_inline_asm(bcx, asm, outputs, input_vals);
});
bcx bcx
} }
_ => { _ => {
assert!(rvalue_creates_operand(&self.mir, &bcx, rvalue)); assert!(rvalue_creates_operand(rvalue));
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue, debug_loc); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
self.store_operand(&bcx, dest.llval, temp); self.store_operand(&bcx, dest.llval, temp);
bcx bcx
} }
@ -188,27 +170,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
pub fn trans_rvalue_operand(&mut self, pub fn trans_rvalue_operand(&mut self,
bcx: BlockAndBuilder<'bcx, 'tcx>, bcx: BlockAndBuilder<'a, 'tcx>,
rvalue: &mir::Rvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>)
debug_loc: DebugLoc) -> (BlockAndBuilder<'a, 'tcx>, OperandRef<'tcx>)
-> (BlockAndBuilder<'bcx, 'tcx>, OperandRef<'tcx>)
{ {
assert!(rvalue_creates_operand(&self.mir, &bcx, rvalue), assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
"cannot trans {:?} to operand", rvalue);
match *rvalue { match *rvalue {
mir::Rvalue::Cast(ref kind, ref source, cast_ty) => { mir::Rvalue::Cast(ref kind, ref source, cast_ty) => {
let operand = self.trans_operand(&bcx, source); let operand = self.trans_operand(&bcx, source);
debug!("cast operand is {:?}", operand); debug!("cast operand is {:?}", operand);
let cast_ty = bcx.monomorphize(&cast_ty); let cast_ty = self.monomorphize(&cast_ty);
let val = match *kind { let val = match *kind {
mir::CastKind::ReifyFnPointer => { mir::CastKind::ReifyFnPointer => {
match operand.ty.sty { match operand.ty.sty {
ty::TyFnDef(def_id, substs, _) => { ty::TyFnDef(def_id, substs, _) => {
OperandValue::Immediate( OperandValue::Immediate(
Callee::def(bcx.ccx(), def_id, substs) Callee::def(bcx.ccx, def_id, substs)
.reify(bcx.ccx())) .reify(bcx.ccx))
} }
_ => { _ => {
bug!("{} cannot be reified to a fn ptr", operand.ty) bug!("{} cannot be reified to a fn ptr", operand.ty)
@ -222,7 +202,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::CastKind::Unsize => { mir::CastKind::Unsize => {
// unsize targets other than to a fat pointer currently // unsize targets other than to a fat pointer currently
// can't be operands. // can't be operands.
assert!(common::type_is_fat_ptr(bcx.tcx(), cast_ty)); assert!(common::type_is_fat_ptr(bcx.ccx, cast_ty));
match operand.val { match operand.val {
OperandValue::Pair(lldata, llextra) => { OperandValue::Pair(lldata, llextra) => {
@ -232,16 +212,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// &'a fmt::Debug+Send => &'a fmt::Debug, // &'a fmt::Debug+Send => &'a fmt::Debug,
// So we need to pointercast the base to ensure // So we need to pointercast the base to ensure
// the types match up. // the types match up.
let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx(), cast_ty); let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx, cast_ty);
let lldata = bcx.pointercast(lldata, llcast_ty); let lldata = bcx.pointercast(lldata, llcast_ty);
OperandValue::Pair(lldata, llextra) OperandValue::Pair(lldata, llextra)
} }
OperandValue::Immediate(lldata) => { OperandValue::Immediate(lldata) => {
// "standard" unsize // "standard" unsize
let (lldata, llextra) = bcx.with_block(|bcx| { let (lldata, llextra) = base::unsize_thin_ptr(&bcx, lldata,
base::unsize_thin_ptr(bcx, lldata, operand.ty, cast_ty);
operand.ty, cast_ty)
});
OperandValue::Pair(lldata, llextra) OperandValue::Pair(lldata, llextra)
} }
OperandValue::Ref(_) => { OperandValue::Ref(_) => {
@ -250,11 +228,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
} }
} }
mir::CastKind::Misc if common::type_is_fat_ptr(bcx.tcx(), operand.ty) => { mir::CastKind::Misc if common::type_is_fat_ptr(bcx.ccx, operand.ty) => {
let ll_cast_ty = type_of::immediate_type_of(bcx.ccx(), cast_ty); let ll_cast_ty = type_of::immediate_type_of(bcx.ccx, cast_ty);
let ll_from_ty = type_of::immediate_type_of(bcx.ccx(), operand.ty); let ll_from_ty = type_of::immediate_type_of(bcx.ccx, operand.ty);
if let OperandValue::Pair(data_ptr, meta_ptr) = operand.val { if let OperandValue::Pair(data_ptr, meta_ptr) = operand.val {
if common::type_is_fat_ptr(bcx.tcx(), cast_ty) { if common::type_is_fat_ptr(bcx.ccx, cast_ty) {
let ll_cft = ll_cast_ty.field_types(); let ll_cft = ll_cast_ty.field_types();
let ll_fft = ll_from_ty.field_types(); let ll_fft = ll_from_ty.field_types();
let data_cast = bcx.pointercast(data_ptr, ll_cft[0]); let data_cast = bcx.pointercast(data_ptr, ll_cft[0]);
@ -271,19 +249,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
} }
mir::CastKind::Misc => { mir::CastKind::Misc => {
debug_assert!(common::type_is_immediate(bcx.ccx(), cast_ty)); debug_assert!(common::type_is_immediate(bcx.ccx, cast_ty));
let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast"); let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast");
let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
let ll_t_in = type_of::immediate_type_of(bcx.ccx(), operand.ty); let ll_t_in = type_of::immediate_type_of(bcx.ccx, operand.ty);
let ll_t_out = type_of::immediate_type_of(bcx.ccx(), cast_ty); let ll_t_out = type_of::immediate_type_of(bcx.ccx, cast_ty);
let (llval, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in { let (llval, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in {
let l = bcx.ccx().layout_of(operand.ty); let l = bcx.ccx.layout_of(operand.ty);
let discr = match operand.val { let discr = match operand.val {
OperandValue::Immediate(llval) => llval, OperandValue::Immediate(llval) => llval,
OperandValue::Ref(llptr) => { OperandValue::Ref(llptr) => {
bcx.with_block(|bcx| { adt::trans_get_discr(&bcx, operand.ty, llptr, None, true)
adt::trans_get_discr(bcx, operand.ty, llptr, None, true)
})
} }
OperandValue::Pair(..) => bug!("Unexpected Pair operand") OperandValue::Pair(..) => bug!("Unexpected Pair operand")
}; };
@ -376,7 +352,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// Note: lvalues are indirect, so storing the `llval` into the // Note: lvalues are indirect, so storing the `llval` into the
// destination effectively creates a reference. // destination effectively creates a reference.
let operand = if common::type_is_sized(bcx.tcx(), ty) { let operand = if bcx.ccx.shared().type_is_sized(ty) {
OperandRef { OperandRef {
val: OperandValue::Immediate(tr_lvalue.llval), val: OperandValue::Immediate(tr_lvalue.llval),
ty: ref_ty, ty: ref_ty,
@ -394,7 +370,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::Rvalue::Len(ref lvalue) => { mir::Rvalue::Len(ref lvalue) => {
let tr_lvalue = self.trans_lvalue(&bcx, lvalue); let tr_lvalue = self.trans_lvalue(&bcx, lvalue);
let operand = OperandRef { let operand = OperandRef {
val: OperandValue::Immediate(tr_lvalue.len(bcx.ccx())), val: OperandValue::Immediate(tr_lvalue.len(bcx.ccx)),
ty: bcx.tcx().types.usize, ty: bcx.tcx().types.usize,
}; };
(bcx, operand) (bcx, operand)
@ -403,7 +379,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
let lhs = self.trans_operand(&bcx, lhs); let lhs = self.trans_operand(&bcx, lhs);
let rhs = self.trans_operand(&bcx, rhs); let rhs = self.trans_operand(&bcx, rhs);
let llresult = if common::type_is_fat_ptr(bcx.tcx(), lhs.ty) { let llresult = if common::type_is_fat_ptr(bcx.ccx, lhs.ty) {
match (lhs.val, rhs.val) { match (lhs.val, rhs.val) {
(OperandValue::Pair(lhs_addr, lhs_extra), (OperandValue::Pair(lhs_addr, lhs_extra),
OperandValue::Pair(rhs_addr, rhs_extra)) => { OperandValue::Pair(rhs_addr, rhs_extra)) => {
@ -461,26 +437,27 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
mir::Rvalue::Box(content_ty) => { mir::Rvalue::Box(content_ty) => {
let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty); let content_ty: Ty<'tcx> = self.monomorphize(&content_ty);
let llty = type_of::type_of(bcx.ccx(), content_ty); let llty = type_of::type_of(bcx.ccx, content_ty);
let llsize = machine::llsize_of(bcx.ccx(), llty); let llsize = machine::llsize_of(bcx.ccx, llty);
let align = type_of::align_of(bcx.ccx(), content_ty); let align = type_of::align_of(bcx.ccx, content_ty);
let llalign = C_uint(bcx.ccx(), align); let llalign = C_uint(bcx.ccx, align);
let llty_ptr = llty.ptr_to(); let llty_ptr = llty.ptr_to();
let box_ty = bcx.tcx().mk_box(content_ty); let box_ty = bcx.tcx().mk_box(content_ty);
let mut llval = None;
let bcx = bcx.map_block(|bcx| { // Allocate space:
let Result { bcx, val } = base::malloc_raw_dyn(bcx, let def_id = match bcx.tcx().lang_items.require(ExchangeMallocFnLangItem) {
llty_ptr, Ok(id) => id,
box_ty, Err(s) => {
llsize, bcx.sess().fatal(&format!("allocation of `{}` {}", box_ty, s));
llalign, }
debug_loc); };
llval = Some(val); let r = Callee::def(bcx.ccx, def_id, bcx.tcx().intern_substs(&[]))
bcx .reify(bcx.ccx);
}); let val = bcx.pointercast(bcx.call(r, &[llsize, llalign], None), llty_ptr);
let operand = OperandRef { let operand = OperandRef {
val: OperandValue::Immediate(llval.unwrap()), val: OperandValue::Immediate(val),
ty: box_ty, ty: box_ty,
}; };
(bcx, operand) (bcx, operand)
@ -500,7 +477,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
pub fn trans_scalar_binop(&mut self, pub fn trans_scalar_binop(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
op: mir::BinOp, op: mir::BinOp,
lhs: ValueRef, lhs: ValueRef,
rhs: ValueRef, rhs: ValueRef,
@ -542,26 +519,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::BinOp::BitOr => bcx.or(lhs, rhs), mir::BinOp::BitOr => bcx.or(lhs, rhs),
mir::BinOp::BitAnd => bcx.and(lhs, rhs), mir::BinOp::BitAnd => bcx.and(lhs, rhs),
mir::BinOp::BitXor => bcx.xor(lhs, rhs), mir::BinOp::BitXor => bcx.xor(lhs, rhs),
mir::BinOp::Shl => { mir::BinOp::Shl => common::build_unchecked_lshift(bcx, lhs, rhs),
bcx.with_block(|bcx| { mir::BinOp::Shr => common::build_unchecked_rshift(bcx, input_ty, lhs, rhs),
common::build_unchecked_lshift(bcx,
lhs,
rhs,
DebugLoc::None)
})
}
mir::BinOp::Shr => {
bcx.with_block(|bcx| {
common::build_unchecked_rshift(bcx,
input_ty,
lhs,
rhs,
DebugLoc::None)
})
}
mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt | mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt |
mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_nil { mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_nil {
C_bool(bcx.ccx(), match op { C_bool(bcx.ccx, match op {
mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false, mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false,
mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true, mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true,
_ => unreachable!() _ => unreachable!()
@ -575,8 +537,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let (lhs, rhs) = if is_bool { let (lhs, rhs) = if is_bool {
// FIXME(#36856) -- extend the bools into `i8` because // FIXME(#36856) -- extend the bools into `i8` because
// LLVM's i1 comparisons are broken. // LLVM's i1 comparisons are broken.
(bcx.zext(lhs, Type::i8(bcx.ccx())), (bcx.zext(lhs, Type::i8(bcx.ccx)),
bcx.zext(rhs, Type::i8(bcx.ccx()))) bcx.zext(rhs, Type::i8(bcx.ccx)))
} else { } else {
(lhs, rhs) (lhs, rhs)
}; };
@ -590,7 +552,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
pub fn trans_fat_ptr_binop(&mut self, pub fn trans_fat_ptr_binop(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
op: mir::BinOp, op: mir::BinOp,
lhs_addr: ValueRef, lhs_addr: ValueRef,
lhs_extra: ValueRef, lhs_extra: ValueRef,
@ -637,7 +599,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
pub fn trans_scalar_checked_binop(&mut self, pub fn trans_scalar_checked_binop(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'a, 'tcx>,
op: mir::BinOp, op: mir::BinOp,
lhs: ValueRef, lhs: ValueRef,
rhs: ValueRef, rhs: ValueRef,
@ -646,9 +608,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// with #[rustc_inherit_overflow_checks] and inlined from // with #[rustc_inherit_overflow_checks] and inlined from
// another crate (mostly core::num generic/#[inline] fns), // another crate (mostly core::num generic/#[inline] fns),
// while the current crate doesn't use overflow checks. // while the current crate doesn't use overflow checks.
if !bcx.ccx().check_overflow() { if !bcx.ccx.check_overflow() {
let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty); let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty);
return OperandValue::Pair(val, C_bool(bcx.ccx(), false)); return OperandValue::Pair(val, C_bool(bcx.ccx, false));
} }
// First try performing the operation on constants, which // First try performing the operation on constants, which
@ -656,7 +618,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// This is necessary to determine when an overflow Assert // This is necessary to determine when an overflow Assert
// will always panic at runtime, and produce a warning. // will always panic at runtime, and produce a warning.
if let Some((val, of)) = const_scalar_checked_binop(bcx.tcx(), op, lhs, rhs, input_ty) { if let Some((val, of)) = const_scalar_checked_binop(bcx.tcx(), op, lhs, rhs, input_ty) {
return OperandValue::Pair(val, C_bool(bcx.ccx(), of)); return OperandValue::Pair(val, C_bool(bcx.ccx, of));
} }
let (val, of) = match op { let (val, of) = match op {
@ -677,9 +639,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::BinOp::Shl | mir::BinOp::Shr => { mir::BinOp::Shl | mir::BinOp::Shr => {
let lhs_llty = val_ty(lhs); let lhs_llty = val_ty(lhs);
let rhs_llty = val_ty(rhs); let rhs_llty = val_ty(rhs);
let invert_mask = bcx.with_block(|bcx| { let invert_mask = common::shift_mask_val(&bcx, lhs_llty, rhs_llty, true);
common::shift_mask_val(bcx, lhs_llty, rhs_llty, true)
});
let outer_bits = bcx.and(rhs, invert_mask); let outer_bits = bcx.and(rhs, invert_mask);
let of = bcx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty)); let of = bcx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty));
@ -696,9 +656,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
} }
pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>, pub fn rvalue_creates_operand(rvalue: &mir::Rvalue) -> bool {
_bcx: &BlockAndBuilder<'bcx, 'tcx>,
rvalue: &mir::Rvalue<'tcx>) -> bool {
match *rvalue { match *rvalue {
mir::Rvalue::Ref(..) | mir::Rvalue::Ref(..) |
mir::Rvalue::Len(..) | mir::Rvalue::Len(..) |
@ -789,5 +747,5 @@ fn get_overflow_intrinsic(oop: OverflowOp, bcx: &BlockAndBuilder, ty: Ty) -> Val
}, },
}; };
bcx.ccx().get_intrinsic(&name) bcx.ccx.get_intrinsic(&name)
} }

View file

@ -18,57 +18,52 @@ use super::LocalRef;
use super::super::adt; use super::super::adt;
use super::super::disr::Disr; use super::super::disr::Disr;
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_statement(&mut self, pub fn trans_statement(&mut self,
bcx: BlockAndBuilder<'bcx, 'tcx>, bcx: BlockAndBuilder<'a, 'tcx>,
statement: &mir::Statement<'tcx>) statement: &mir::Statement<'tcx>)
-> BlockAndBuilder<'bcx, 'tcx> { -> BlockAndBuilder<'a, 'tcx> {
debug!("trans_statement(statement={:?})", statement); debug!("trans_statement(statement={:?})", statement);
let debug_loc = self.debug_loc(statement.source_info); self.set_debug_loc(&bcx, statement.source_info);
debug_loc.apply_to_bcx(&bcx);
debug_loc.apply(bcx.fcx());
match statement.kind { match statement.kind {
mir::StatementKind::Assign(ref lvalue, ref rvalue) => { mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
if let mir::Lvalue::Local(index) = *lvalue { if let mir::Lvalue::Local(index) = *lvalue {
match self.locals[index] { match self.locals[index] {
LocalRef::Lvalue(tr_dest) => { LocalRef::Lvalue(tr_dest) => {
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) self.trans_rvalue(bcx, tr_dest, rvalue)
} }
LocalRef::Operand(None) => { LocalRef::Operand(None) => {
let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue, let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue);
debug_loc);
self.locals[index] = LocalRef::Operand(Some(operand)); self.locals[index] = LocalRef::Operand(Some(operand));
bcx bcx
} }
LocalRef::Operand(Some(_)) => { LocalRef::Operand(Some(_)) => {
let ty = self.monomorphized_lvalue_ty(lvalue); let ty = self.monomorphized_lvalue_ty(lvalue);
if !common::type_is_zero_size(bcx.ccx(), ty) { if !common::type_is_zero_size(bcx.ccx, ty) {
span_bug!(statement.source_info.span, span_bug!(statement.source_info.span,
"operand {:?} already assigned", "operand {:?} already assigned",
rvalue); rvalue);
} else { } else {
// If the type is zero-sized, it's already been set here, // If the type is zero-sized, it's already been set here,
// but we still need to make sure we translate the operand // but we still need to make sure we translate the operand
self.trans_rvalue_operand(bcx, rvalue, debug_loc).0 self.trans_rvalue_operand(bcx, rvalue).0
} }
} }
} }
} else { } else {
let tr_dest = self.trans_lvalue(&bcx, lvalue); let tr_dest = self.trans_lvalue(&bcx, lvalue);
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) self.trans_rvalue(bcx, tr_dest, rvalue)
} }
} }
mir::StatementKind::SetDiscriminant{ref lvalue, variant_index} => { mir::StatementKind::SetDiscriminant{ref lvalue, variant_index} => {
let ty = self.monomorphized_lvalue_ty(lvalue); let ty = self.monomorphized_lvalue_ty(lvalue);
let lvalue_transed = self.trans_lvalue(&bcx, lvalue); let lvalue_transed = self.trans_lvalue(&bcx, lvalue);
bcx.with_block(|bcx| adt::trans_set_discr(&bcx,
adt::trans_set_discr(bcx, ty,
ty, lvalue_transed.llval,
lvalue_transed.llval, Disr::from(variant_index));
Disr::from(variant_index))
);
bcx bcx
} }
mir::StatementKind::StorageLive(ref lvalue) => { mir::StatementKind::StorageLive(ref lvalue) => {
@ -82,10 +77,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} }
fn trans_storage_liveness(&self, fn trans_storage_liveness(&self,
bcx: BlockAndBuilder<'bcx, 'tcx>, bcx: BlockAndBuilder<'a, 'tcx>,
lvalue: &mir::Lvalue<'tcx>, lvalue: &mir::Lvalue<'tcx>,
intrinsic: base::Lifetime) intrinsic: base::Lifetime)
-> BlockAndBuilder<'bcx, 'tcx> { -> BlockAndBuilder<'a, 'tcx> {
if let mir::Lvalue::Local(index) = *lvalue { if let mir::Lvalue::Local(index) = *lvalue {
if let LocalRef::Lvalue(tr_lval) = self.locals[index] { if let LocalRef::Lvalue(tr_lval) = self.locals[index] {
intrinsic.call(&bcx, tr_lval.llval); intrinsic.call(&bcx, tr_lval.llval);

View file

@ -184,7 +184,7 @@ impl<'a, 'tcx> TransItem<'tcx> {
linkage: llvm::Linkage, linkage: llvm::Linkage,
symbol_name: &str) { symbol_name: &str) {
let tcx = ccx.tcx(); let tcx = ccx.tcx();
assert_eq!(dg.ty(), glue::get_drop_glue_type(tcx, dg.ty())); assert_eq!(dg.ty(), glue::get_drop_glue_type(ccx.shared(), dg.ty()));
let t = dg.ty(); let t = dg.ty();
let sig = tcx.mk_fn_sig(iter::once(tcx.mk_mut_ptr(tcx.types.i8)), tcx.mk_nil(), false); let sig = tcx.mk_fn_sig(iter::once(tcx.mk_mut_ptr(tcx.types.i8)), tcx.mk_nil(), false);

View file

@ -8,56 +8,46 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(non_camel_case_types)]
use llvm; use llvm;
use llvm::ValueRef; use llvm::ValueRef;
use base::*;
use build::*;
use common::*; use common::*;
use debuginfo::DebugLoc;
use rustc::ty::Ty; use rustc::ty::Ty;
pub fn slice_for_each<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, pub fn slice_for_each<'a, 'tcx, F>(
data_ptr: ValueRef, bcx: &BlockAndBuilder<'a, 'tcx>,
unit_ty: Ty<'tcx>, data_ptr: ValueRef,
len: ValueRef, unit_ty: Ty<'tcx>,
f: F) len: ValueRef,
-> Block<'blk, 'tcx> where f: F
F: FnOnce(Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>, ) -> BlockAndBuilder<'a, 'tcx> where F: FnOnce(&BlockAndBuilder<'a, 'tcx>, ValueRef) {
{
let _icx = push_ctxt("tvec::slice_for_each");
let fcx = bcx.fcx;
// Special-case vectors with elements of size 0 so they don't go out of bounds (#9890) // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890)
let zst = type_is_zero_size(bcx.ccx(), unit_ty); let zst = type_is_zero_size(bcx.ccx, unit_ty);
let add = |bcx, a, b| if zst { let add = |bcx: &BlockAndBuilder, a, b| if zst {
Add(bcx, a, b, DebugLoc::None) bcx.add(a, b)
} else { } else {
InBoundsGEP(bcx, a, &[b]) bcx.inbounds_gep(a, &[b])
}; };
let header_bcx = fcx.new_block("slice_loop_header"); let body_bcx = bcx.fcx().build_new_block("slice_loop_body");
let body_bcx = fcx.new_block("slice_loop_body"); let next_bcx = bcx.fcx().build_new_block("slice_loop_next");
let next_bcx = fcx.new_block("slice_loop_next"); let header_bcx = bcx.fcx().build_new_block("slice_loop_header");
let start = if zst { let start = if zst {
C_uint(bcx.ccx(), 0 as usize) C_uint(bcx.ccx, 0usize)
} else { } else {
data_ptr data_ptr
}; };
let end = add(bcx, start, len); let end = add(&bcx, start, len);
Br(bcx, header_bcx.llbb, DebugLoc::None); bcx.br(header_bcx.llbb());
let current = Phi(header_bcx, val_ty(start), &[start], &[bcx.llbb]); let current = header_bcx.phi(val_ty(start), &[start], &[bcx.llbb()]);
let keep_going = let keep_going = header_bcx.icmp(llvm::IntNE, current, end);
ICmp(header_bcx, llvm::IntNE, current, end, DebugLoc::None); header_bcx.cond_br(keep_going, body_bcx.llbb(), next_bcx.llbb());
CondBr(header_bcx, keep_going, body_bcx.llbb, next_bcx.llbb, DebugLoc::None);
let body_bcx = f(body_bcx, if zst { data_ptr } else { current }); f(&body_bcx, if zst { data_ptr } else { current });
let next = add(body_bcx, current, C_uint(bcx.ccx(), 1usize)); let next = add(&body_bcx, current, C_uint(bcx.ccx, 1usize));
AddIncomingToPhi(current, next, body_bcx.llbb); header_bcx.add_incoming_to_phi(current, next, body_bcx.llbb());
Br(body_bcx, header_bcx.llbb, DebugLoc::None); body_bcx.br(header_bcx.llbb());
next_bcx next_bcx
} }

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![allow(non_camel_case_types)]
use abi::FnType; use abi::FnType;
use adt; use adt;
use common::*; use common::*;
@ -41,7 +39,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
let _recursion_lock = cx.enter_type_of(t); let _recursion_lock = cx.enter_type_of(t);
let llsizingty = match t.sty { let llsizingty = match t.sty {
_ if !type_is_sized(cx.tcx(), t) => { _ if !cx.shared().type_is_sized(t) => {
Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, t)], false) Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, t)], false)
} }
@ -55,7 +53,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
ty::TyBox(ty) | ty::TyBox(ty) |
ty::TyRef(_, ty::TypeAndMut{ty, ..}) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => { ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => {
if type_is_sized(cx.tcx(), ty) { if cx.shared().type_is_sized(ty) {
Type::i8p(cx) Type::i8p(cx)
} else { } else {
Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, ty)], false) Type::struct_(cx, &[Type::i8p(cx), unsized_info_ty(cx, ty)], false)
@ -104,7 +102,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
// FIXME(eddyb) Temporary sanity check for ty::layout. // FIXME(eddyb) Temporary sanity check for ty::layout.
let layout = cx.layout_of(t); let layout = cx.layout_of(t);
if !type_is_sized(cx.tcx(), t) { if !cx.shared().type_is_sized(t) {
if !layout.is_unsized() { if !layout.is_unsized() {
bug!("layout should be unsized for type `{}` / {:#?}", bug!("layout should be unsized for type `{}` / {:#?}",
t, layout); t, layout);
@ -135,7 +133,7 @@ pub fn fat_ptr_base_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) ->
match ty.sty { match ty.sty {
ty::TyBox(t) | ty::TyBox(t) |
ty::TyRef(_, ty::TypeAndMut { ty: t, .. }) | ty::TyRef(_, ty::TypeAndMut { ty: t, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty: t, .. }) if !type_is_sized(ccx.tcx(), t) => { ty::TyRawPtr(ty::TypeAndMut { ty: t, .. }) if !ccx.shared().type_is_sized(t) => {
in_memory_type_of(ccx, t).ptr_to() in_memory_type_of(ccx, t).ptr_to()
} }
_ => bug!("expected fat ptr ty but got {:?}", ty) _ => bug!("expected fat ptr ty but got {:?}", ty)
@ -172,7 +170,7 @@ pub fn immediate_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
/// is too large for it to be placed in SSA value (by our rules). /// is too large for it to be placed in SSA value (by our rules).
/// For the raw type without far pointer indirection, see `in_memory_type_of`. /// For the raw type without far pointer indirection, see `in_memory_type_of`.
pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type { pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Type {
let ty = if !type_is_sized(cx.tcx(), ty) { let ty = if !cx.shared().type_is_sized(ty) {
cx.tcx().mk_imm_ptr(ty) cx.tcx().mk_imm_ptr(ty)
} else { } else {
ty ty
@ -232,7 +230,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
ty::TyBox(ty) | ty::TyBox(ty) |
ty::TyRef(_, ty::TypeAndMut{ty, ..}) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => { ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => {
if !type_is_sized(cx.tcx(), ty) { if !cx.shared().type_is_sized(ty) {
if let ty::TyStr = ty.sty { if let ty::TyStr = ty.sty {
// This means we get a nicer name in the output (str is always // This means we get a nicer name in the output (str is always
// unsized). // unsized).

View file

@ -9,16 +9,11 @@
// except according to those terms. // except according to those terms.
use llvm; use llvm;
use llvm::{UseRef, ValueRef};
use basic_block::BasicBlock;
use common::Block;
use std::fmt; use std::fmt;
use libc::c_uint;
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct Value(pub ValueRef); pub struct Value(pub llvm::ValueRef);
impl fmt::Debug for Value { impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -27,152 +22,3 @@ impl fmt::Debug for Value {
}).expect("nun-UTF8 value description from LLVM")) }).expect("nun-UTF8 value description from LLVM"))
} }
} }
macro_rules! opt_val { ($e:expr) => (
unsafe {
match $e {
p if !p.is_null() => Some(Value(p)),
_ => None
}
}
) }
/// Wrapper for LLVM ValueRef
impl Value {
/// Returns the native ValueRef
pub fn get(&self) -> ValueRef {
let Value(v) = *self; v
}
/// Returns the BasicBlock that contains this value
pub fn get_parent(self) -> Option<BasicBlock> {
unsafe {
match llvm::LLVMGetInstructionParent(self.get()) {
p if !p.is_null() => Some(BasicBlock(p)),
_ => None
}
}
}
/// Removes this value from its containing BasicBlock
pub fn erase_from_parent(self) {
unsafe {
llvm::LLVMInstructionEraseFromParent(self.get());
}
}
/// Returns the single dominating store to this value, if any
/// This only performs a search for a trivially dominating store. The store
/// must be the only user of this value, and there must not be any conditional
/// branches between the store and the given block.
pub fn get_dominating_store(self, bcx: Block) -> Option<Value> {
match self.get_single_user().and_then(|user| user.as_store_inst()) {
Some(store) => {
store.get_parent().and_then(|store_bb| {
let mut bb = BasicBlock(bcx.llbb);
let mut ret = Some(store);
while bb.get() != store_bb.get() {
match bb.get_single_predecessor() {
Some(pred) => bb = pred,
None => { ret = None; break }
}
}
ret
})
}
_ => None
}
}
/// Returns the first use of this value, if any
pub fn get_first_use(self) -> Option<Use> {
unsafe {
match llvm::LLVMGetFirstUse(self.get()) {
u if !u.is_null() => Some(Use(u)),
_ => None
}
}
}
/// Tests if there are no uses of this value
pub fn has_no_uses(self) -> bool {
self.get_first_use().is_none()
}
/// Returns the single user of this value
/// If there are no users or multiple users, this returns None
pub fn get_single_user(self) -> Option<Value> {
let mut iter = self.user_iter();
match (iter.next(), iter.next()) {
(Some(first), None) => Some(first),
_ => None
}
}
/// Returns an iterator for the users of this value
pub fn user_iter(self) -> Users {
Users {
next: self.get_first_use()
}
}
/// Returns the requested operand of this instruction
/// Returns None, if there's no operand at the given index
pub fn get_operand(self, i: usize) -> Option<Value> {
opt_val!(llvm::LLVMGetOperand(self.get(), i as c_uint))
}
/// Returns the Store represent by this value, if any
pub fn as_store_inst(self) -> Option<Value> {
opt_val!(llvm::LLVMIsAStoreInst(self.get()))
}
/// Tests if this value is a terminator instruction
pub fn is_a_terminator_inst(self) -> bool {
unsafe {
!llvm::LLVMIsATerminatorInst(self.get()).is_null()
}
}
}
/// Wrapper for LLVM UseRef
#[derive(Copy, Clone)]
pub struct Use(UseRef);
impl Use {
pub fn get(&self) -> UseRef {
let Use(v) = *self; v
}
pub fn get_user(self) -> Value {
unsafe {
Value(llvm::LLVMGetUser(self.get()))
}
}
pub fn get_next_use(self) -> Option<Use> {
unsafe {
match llvm::LLVMGetNextUse(self.get()) {
u if !u.is_null() => Some(Use(u)),
_ => None
}
}
}
}
/// Iterator for the users of a value
pub struct Users {
next: Option<Use>
}
impl Iterator for Users {
type Item = Value;
fn next(&mut self) -> Option<Value> {
let current = self.next;
self.next = current.and_then(|u| u.get_next_use());
current.map(|u| u.get_user())
}
}

View file

@ -0,0 +1,14 @@
// Copyright 2016 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() {
assert_eq!((ToString::to_string as fn(&(ToString+'static)) -> String)(&"foo"),
String::from("foo"));
}