trans: generalize immediate temporaries to all MIR locals.
This commit is contained in:
parent
bec32eb4ff
commit
7279af86c8
12 changed files with 421 additions and 306 deletions
|
@ -144,6 +144,40 @@ impl<'tcx> Mir<'tcx> {
|
|||
pub fn predecessors_for(&self, bb: BasicBlock) -> Ref<Vec<BasicBlock>> {
|
||||
Ref::map(self.predecessors(), |p| &p[bb])
|
||||
}
|
||||
|
||||
/// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order)
|
||||
/// to their index in the whole list of locals. This is useful if you
|
||||
/// want to treat all locals the same instead of repeating yourself.
|
||||
pub fn local_index(&self, lvalue: &Lvalue<'tcx>) -> Option<Local> {
|
||||
let idx = match *lvalue {
|
||||
Lvalue::Arg(arg) => arg.index(),
|
||||
Lvalue::Var(var) => {
|
||||
self.arg_decls.len() +
|
||||
var.index()
|
||||
}
|
||||
Lvalue::Temp(temp) => {
|
||||
self.arg_decls.len() +
|
||||
self.var_decls.len() +
|
||||
temp.index()
|
||||
}
|
||||
Lvalue::ReturnPointer => {
|
||||
self.arg_decls.len() +
|
||||
self.var_decls.len() +
|
||||
self.temp_decls.len()
|
||||
}
|
||||
Lvalue::Static(_) |
|
||||
Lvalue::Projection(_) => return None
|
||||
};
|
||||
Some(Local::new(idx))
|
||||
}
|
||||
|
||||
/// Counts the number of locals, such that that local_index
|
||||
/// will always return an index smaller than this count.
|
||||
pub fn count_locals(&self) -> usize {
|
||||
self.arg_decls.len() +
|
||||
self.var_decls.len() +
|
||||
self.temp_decls.len() + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Index<BasicBlock> for Mir<'tcx> {
|
||||
|
@ -663,6 +697,7 @@ impl<'tcx> Debug for Statement<'tcx> {
|
|||
newtype_index!(Var, "var");
|
||||
newtype_index!(Temp, "tmp");
|
||||
newtype_index!(Arg, "arg");
|
||||
newtype_index!(Local, "local");
|
||||
|
||||
/// A path to a value; something that can be evaluated without
|
||||
/// changing or disturbing program state.
|
||||
|
|
|
@ -492,6 +492,13 @@ impl<'tcx> FnOutput<'tcx> {
|
|||
ty::FnDiverging => def
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_converging(self) -> Option<Ty<'tcx>> {
|
||||
match self {
|
||||
ty::FnConverging(t) => Some(t),
|
||||
ty::FnDiverging => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type PolyFnOutput<'tcx> = Binder<FnOutput<'tcx>>;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! An analysis to determine which temporaries require allocas and
|
||||
//! An analysis to determine which locals require allocas and
|
||||
//! which do not.
|
||||
|
||||
use rustc_data_structures::bitvec::BitVector;
|
||||
|
@ -21,16 +21,20 @@ use common::{self, Block, BlockAndBuilder};
|
|||
use glue;
|
||||
use super::rvalue;
|
||||
|
||||
pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
|
||||
mir: &mir::Mir<'tcx>) -> BitVector {
|
||||
pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>,
|
||||
mir: &mir::Mir<'tcx>) -> BitVector {
|
||||
let bcx = bcx.build();
|
||||
let mut analyzer = TempAnalyzer::new(mir, &bcx, mir.temp_decls.len());
|
||||
let mut analyzer = LocalAnalyzer::new(mir, &bcx);
|
||||
|
||||
analyzer.visit_mir(mir);
|
||||
|
||||
for (index, temp_decl) in mir.temp_decls.iter().enumerate() {
|
||||
let ty = bcx.monomorphize(&temp_decl.ty);
|
||||
debug!("temp {:?} has type {:?}", index, ty);
|
||||
let local_types = mir.arg_decls.iter().map(|a| a.ty)
|
||||
.chain(mir.var_decls.iter().map(|v| v.ty))
|
||||
.chain(mir.temp_decls.iter().map(|t| t.ty))
|
||||
.chain(mir.return_ty.maybe_converging());
|
||||
for (index, ty) in local_types.enumerate() {
|
||||
let ty = bcx.monomorphize(&ty);
|
||||
debug!("local {} has type {:?}", index, ty);
|
||||
if ty.is_scalar() ||
|
||||
ty.is_unique() ||
|
||||
ty.is_region_ptr() ||
|
||||
|
@ -50,66 +54,87 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
|
|||
// (e.g. structs) into an alloca unconditionally, just so
|
||||
// that we don't have to deal with having two pathways
|
||||
// (gep vs extractvalue etc).
|
||||
analyzer.mark_as_lvalue(index);
|
||||
analyzer.mark_as_lvalue(mir::Local::new(index));
|
||||
}
|
||||
}
|
||||
|
||||
analyzer.lvalue_temps
|
||||
analyzer.lvalue_locals
|
||||
}
|
||||
|
||||
struct TempAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> {
|
||||
struct LocalAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> {
|
||||
mir: &'mir mir::Mir<'tcx>,
|
||||
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
|
||||
lvalue_temps: BitVector,
|
||||
lvalue_locals: BitVector,
|
||||
seen_assigned: BitVector
|
||||
}
|
||||
|
||||
impl<'mir, 'bcx, 'tcx> TempAnalyzer<'mir, 'bcx, 'tcx> {
|
||||
impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> {
|
||||
fn new(mir: &'mir mir::Mir<'tcx>,
|
||||
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
|
||||
temp_count: usize) -> TempAnalyzer<'mir, 'bcx, 'tcx> {
|
||||
TempAnalyzer {
|
||||
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>)
|
||||
-> LocalAnalyzer<'mir, 'bcx, 'tcx> {
|
||||
let local_count = mir.count_locals();
|
||||
LocalAnalyzer {
|
||||
mir: mir,
|
||||
bcx: bcx,
|
||||
lvalue_temps: BitVector::new(temp_count),
|
||||
seen_assigned: BitVector::new(temp_count)
|
||||
lvalue_locals: BitVector::new(local_count),
|
||||
seen_assigned: BitVector::new(local_count)
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_as_lvalue(&mut self, temp: usize) {
|
||||
debug!("marking temp {} as lvalue", temp);
|
||||
self.lvalue_temps.insert(temp);
|
||||
fn mark_as_lvalue(&mut self, local: mir::Local) {
|
||||
debug!("marking {:?} as lvalue", local);
|
||||
self.lvalue_locals.insert(local.index());
|
||||
}
|
||||
|
||||
fn mark_assigned(&mut self, temp: usize) {
|
||||
if !self.seen_assigned.insert(temp) {
|
||||
self.mark_as_lvalue(temp);
|
||||
fn mark_assigned(&mut self, local: mir::Local) {
|
||||
if !self.seen_assigned.insert(local.index()) {
|
||||
self.mark_as_lvalue(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
|
||||
impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
|
||||
fn visit_assign(&mut self,
|
||||
block: mir::BasicBlock,
|
||||
lvalue: &mir::Lvalue<'tcx>,
|
||||
rvalue: &mir::Rvalue<'tcx>) {
|
||||
debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue);
|
||||
|
||||
match *lvalue {
|
||||
mir::Lvalue::Temp(temp) => {
|
||||
self.mark_assigned(temp.index());
|
||||
if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
|
||||
self.mark_as_lvalue(temp.index());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.visit_lvalue(lvalue, LvalueContext::Store);
|
||||
if let Some(index) = self.mir.local_index(lvalue) {
|
||||
self.mark_assigned(index);
|
||||
if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
|
||||
self.mark_as_lvalue(index);
|
||||
}
|
||||
} else {
|
||||
self.visit_lvalue(lvalue, LvalueContext::Store);
|
||||
}
|
||||
|
||||
self.visit_rvalue(rvalue);
|
||||
}
|
||||
|
||||
fn visit_terminator_kind(&mut self,
|
||||
block: mir::BasicBlock,
|
||||
kind: &mir::TerminatorKind<'tcx>) {
|
||||
match *kind {
|
||||
mir::TerminatorKind::Call {
|
||||
func: mir::Operand::Constant(mir::Constant {
|
||||
literal: mir::Literal::Item { def_id, .. }, ..
|
||||
}),
|
||||
ref args, ..
|
||||
} if Some(def_id) == self.bcx.tcx().lang_items.box_free_fn() => {
|
||||
// box_free(x) shares with `drop x` the property that it
|
||||
// is not guaranteed to be statically dominated by the
|
||||
// definition of x, so x must always be in an alloca.
|
||||
if let mir::Operand::Consume(ref lvalue) = args[0] {
|
||||
self.visit_lvalue(lvalue, LvalueContext::Drop);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.super_terminator_kind(block, kind);
|
||||
}
|
||||
|
||||
fn visit_lvalue(&mut self,
|
||||
lvalue: &mir::Lvalue<'tcx>,
|
||||
context: LvalueContext) {
|
||||
|
@ -117,9 +142,9 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
|
|||
|
||||
// Allow uses of projections of immediate pair fields.
|
||||
if let mir::Lvalue::Projection(ref proj) = *lvalue {
|
||||
if let mir::Lvalue::Temp(temp) = proj.base {
|
||||
let ty = self.mir.temp_decls[temp].ty;
|
||||
let ty = self.bcx.monomorphize(&ty);
|
||||
if self.mir.local_index(&proj.base).is_some() {
|
||||
let ty = self.mir.lvalue_ty(self.bcx.tcx(), &proj.base);
|
||||
let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx()));
|
||||
if common::type_is_imm_pair(self.bcx.ccx(), ty) {
|
||||
if let mir::ProjectionElem::Field(..) = proj.elem {
|
||||
if let LvalueContext::Consume = context {
|
||||
|
@ -130,34 +155,30 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
match *lvalue {
|
||||
mir::Lvalue::Temp(temp) => {
|
||||
match context {
|
||||
LvalueContext::Call => {
|
||||
self.mark_assigned(temp.index());
|
||||
}
|
||||
LvalueContext::Consume => {
|
||||
}
|
||||
LvalueContext::Store |
|
||||
LvalueContext::Inspect |
|
||||
LvalueContext::Borrow { .. } |
|
||||
LvalueContext::Slice { .. } |
|
||||
LvalueContext::Projection => {
|
||||
self.mark_as_lvalue(temp.index());
|
||||
}
|
||||
LvalueContext::Drop => {
|
||||
let ty = self.mir.temp_decls[index as usize].ty;
|
||||
let ty = self.bcx.monomorphize(&ty);
|
||||
if let Some(index) = self.mir.local_index(lvalue) {
|
||||
match context {
|
||||
LvalueContext::Call => {
|
||||
self.mark_assigned(index);
|
||||
}
|
||||
LvalueContext::Consume => {
|
||||
}
|
||||
LvalueContext::Store |
|
||||
LvalueContext::Inspect |
|
||||
LvalueContext::Borrow { .. } |
|
||||
LvalueContext::Slice { .. } |
|
||||
LvalueContext::Projection => {
|
||||
self.mark_as_lvalue(index);
|
||||
}
|
||||
LvalueContext::Drop => {
|
||||
let ty = self.mir.lvalue_ty(self.bcx.tcx(), lvalue);
|
||||
let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx()));
|
||||
|
||||
// Only need the lvalue if we're actually dropping it.
|
||||
if glue::type_needs_drop(self.bcx.tcx(), ty) {
|
||||
self.mark_as_lvalue(index as usize);
|
||||
}
|
||||
// Only need the lvalue if we're actually dropping it.
|
||||
if glue::type_needs_drop(self.bcx.tcx(), ty) {
|
||||
self.mark_as_lvalue(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
}
|
||||
|
||||
// A deref projection only reads the pointer, never needs the lvalue.
|
||||
|
|
|
@ -32,7 +32,7 @@ use type_::Type;
|
|||
use rustc_data_structures::fnv::FnvHashMap;
|
||||
use syntax::parse::token;
|
||||
|
||||
use super::{MirContext, TempRef};
|
||||
use super::{MirContext, LocalRef};
|
||||
use super::analyze::CleanupKind;
|
||||
use super::constant::Const;
|
||||
use super::lvalue::{LvalueRef, load_fat_ptr};
|
||||
|
@ -186,9 +186,43 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
|
||||
mir::TerminatorKind::Return => {
|
||||
bcx.with_block(|bcx| {
|
||||
self.fcx.build_return_block(bcx, debug_loc);
|
||||
})
|
||||
let ret = bcx.fcx().fn_ty.ret;
|
||||
if ret.is_ignore() || ret.is_indirect() {
|
||||
bcx.ret_void();
|
||||
return;
|
||||
}
|
||||
|
||||
let llval = if let Some(cast_ty) = ret.cast {
|
||||
let index = mir.local_index(&mir::Lvalue::ReturnPointer).unwrap();
|
||||
let op = match self.locals[index] {
|
||||
LocalRef::Operand(Some(op)) => op,
|
||||
LocalRef::Operand(None) => bug!("use of return before def"),
|
||||
LocalRef::Lvalue(tr_lvalue) => {
|
||||
OperandRef {
|
||||
val: Ref(tr_lvalue.llval),
|
||||
ty: tr_lvalue.ty.to_ty(bcx.tcx())
|
||||
}
|
||||
}
|
||||
};
|
||||
let llslot = match op.val {
|
||||
Immediate(_) | Pair(..) => {
|
||||
let llscratch = build::AllocaFcx(bcx.fcx(), ret.original_ty, "ret");
|
||||
self.store_operand(&bcx, llscratch, op);
|
||||
llscratch
|
||||
}
|
||||
Ref(llval) => llval
|
||||
};
|
||||
let load = bcx.load(bcx.pointercast(llslot, cast_ty.ptr_to()));
|
||||
let llalign = llalign_of_min(bcx.ccx(), ret.ty);
|
||||
unsafe {
|
||||
llvm::LLVMSetAlignment(load, llalign);
|
||||
}
|
||||
load
|
||||
} else {
|
||||
let op = self.trans_consume(&bcx, &mir::Lvalue::ReturnPointer);
|
||||
op.pack_if_pair(&bcx).immediate()
|
||||
};
|
||||
bcx.ret(llval);
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Unreachable => {
|
||||
|
@ -537,7 +571,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
|
||||
fn trans_argument(&mut self,
|
||||
bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
mut op: OperandRef<'tcx>,
|
||||
op: OperandRef<'tcx>,
|
||||
llargs: &mut Vec<ValueRef>,
|
||||
fn_ty: &FnType,
|
||||
next_idx: &mut usize,
|
||||
|
@ -565,8 +599,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, callee);
|
||||
return;
|
||||
}
|
||||
|
||||
op = op.pack_if_pair(bcx);
|
||||
}
|
||||
|
||||
let arg = &fn_ty.args[*next_idx];
|
||||
|
@ -583,14 +615,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
|
||||
// Force by-ref if we have to load through a cast pointer.
|
||||
let (mut llval, by_ref) = match op.val {
|
||||
Immediate(llval) if arg.is_indirect() || arg.cast.is_some() => {
|
||||
let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg");
|
||||
bcx.store(llval, llscratch);
|
||||
(llscratch, true)
|
||||
Immediate(_) | Pair(..) => {
|
||||
if arg.is_indirect() || arg.cast.is_some() {
|
||||
let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg");
|
||||
self.store_operand(bcx, llscratch, op);
|
||||
(llscratch, true)
|
||||
} else {
|
||||
(op.pack_if_pair(bcx).immediate(), false)
|
||||
}
|
||||
}
|
||||
Immediate(llval) => (llval, false),
|
||||
Ref(llval) => (llval, true),
|
||||
Pair(..) => bug!("pairs handled above")
|
||||
Ref(llval) => (llval, true)
|
||||
};
|
||||
|
||||
if by_ref && !arg.is_indirect() {
|
||||
|
@ -776,40 +810,39 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
if fn_ret_ty.is_ignore() {
|
||||
return ReturnDest::Nothing;
|
||||
}
|
||||
let dest = match *dest {
|
||||
mir::Lvalue::Temp(idx) => {
|
||||
let ret_ty = self.lvalue_ty(dest);
|
||||
match self.temps[idx] {
|
||||
TempRef::Lvalue(dest) => dest,
|
||||
TempRef::Operand(None) => {
|
||||
// Handle temporary lvalues, specifically Operand ones, as
|
||||
// they don't have allocas
|
||||
return if fn_ret_ty.is_indirect() {
|
||||
// Odd, but possible, case, we have an operand temporary,
|
||||
// but the calling convention has an indirect return.
|
||||
let tmp = bcx.with_block(|bcx| {
|
||||
base::alloc_ty(bcx, ret_ty, "tmp_ret")
|
||||
});
|
||||
llargs.push(tmp);
|
||||
ReturnDest::IndirectOperand(tmp, idx)
|
||||
} else if is_intrinsic {
|
||||
// Currently, intrinsics always need a location to store
|
||||
// the result. so we create a temporary alloca for the
|
||||
// result
|
||||
let tmp = bcx.with_block(|bcx| {
|
||||
base::alloc_ty(bcx, ret_ty, "tmp_ret")
|
||||
});
|
||||
ReturnDest::IndirectOperand(tmp, idx)
|
||||
} else {
|
||||
ReturnDest::DirectOperand(idx)
|
||||
};
|
||||
}
|
||||
TempRef::Operand(Some(_)) => {
|
||||
bug!("lvalue temp already assigned to");
|
||||
}
|
||||
let dest = if let Some(index) = self.mir.local_index(dest) {
|
||||
let ret_ty = self.lvalue_ty(dest);
|
||||
match self.locals[index] {
|
||||
LocalRef::Lvalue(dest) => dest,
|
||||
LocalRef::Operand(None) => {
|
||||
// Handle temporary lvalues, specifically Operand ones, as
|
||||
// they don't have allocas
|
||||
return if fn_ret_ty.is_indirect() {
|
||||
// Odd, but possible, case, we have an operand temporary,
|
||||
// but the calling convention has an indirect return.
|
||||
let tmp = bcx.with_block(|bcx| {
|
||||
base::alloc_ty(bcx, ret_ty, "tmp_ret")
|
||||
});
|
||||
llargs.push(tmp);
|
||||
ReturnDest::IndirectOperand(tmp, index)
|
||||
} else if is_intrinsic {
|
||||
// Currently, intrinsics always need a location to store
|
||||
// the result. so we create a temporary alloca for the
|
||||
// result
|
||||
let tmp = bcx.with_block(|bcx| {
|
||||
base::alloc_ty(bcx, ret_ty, "tmp_ret")
|
||||
});
|
||||
ReturnDest::IndirectOperand(tmp, index)
|
||||
} else {
|
||||
ReturnDest::DirectOperand(index)
|
||||
};
|
||||
}
|
||||
LocalRef::Operand(Some(_)) => {
|
||||
bug!("lvalue local already assigned to");
|
||||
}
|
||||
}
|
||||
_ => self.trans_lvalue(bcx, dest)
|
||||
} else {
|
||||
self.trans_lvalue(bcx, dest)
|
||||
};
|
||||
if fn_ret_ty.is_indirect() {
|
||||
llargs.push(dest.llval);
|
||||
|
@ -853,11 +886,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
match dest {
|
||||
Nothing => (),
|
||||
Store(dst) => ret_ty.store(bcx, op.immediate(), dst),
|
||||
IndirectOperand(tmp, idx) => {
|
||||
IndirectOperand(tmp, index) => {
|
||||
let op = self.trans_load(bcx, tmp, op.ty);
|
||||
self.temps[idx] = TempRef::Operand(Some(op));
|
||||
self.locals[index] = LocalRef::Operand(Some(op));
|
||||
}
|
||||
DirectOperand(idx) => {
|
||||
DirectOperand(index) => {
|
||||
// If there is a cast, we have to store and reload.
|
||||
let op = if ret_ty.cast.is_some() {
|
||||
let tmp = bcx.with_block(|bcx| {
|
||||
|
@ -868,7 +901,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
} else {
|
||||
op.unpack_if_pair(bcx)
|
||||
};
|
||||
self.temps[idx] = TempRef::Operand(Some(op));
|
||||
self.locals[index] = LocalRef::Operand(Some(op));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -879,8 +912,8 @@ enum ReturnDest {
|
|||
Nothing,
|
||||
// Store the return value to the pointer
|
||||
Store(ValueRef),
|
||||
// Stores an indirect return value to an operand temporary lvalue
|
||||
IndirectOperand(ValueRef, mir::Temp),
|
||||
// Stores a direct return value to an operand temporary lvalue
|
||||
DirectOperand(mir::Temp)
|
||||
// Stores an indirect return value to an operand local lvalue
|
||||
IndirectOperand(ValueRef, mir::Local),
|
||||
// Stores a direct return value to an operand local lvalue
|
||||
DirectOperand(mir::Local)
|
||||
}
|
||||
|
|
|
@ -203,17 +203,8 @@ struct MirConstContext<'a, 'tcx: 'a> {
|
|||
/// Type parameters for const fn and associated constants.
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
|
||||
/// Arguments passed to a const fn.
|
||||
args: IndexVec<mir::Arg, Const<'tcx>>,
|
||||
|
||||
/// Variable values - specifically, argument bindings of a const fn.
|
||||
vars: IndexVec<mir::Var, Option<Const<'tcx>>>,
|
||||
|
||||
/// Temp values.
|
||||
temps: IndexVec<mir::Temp, Option<Const<'tcx>>>,
|
||||
|
||||
/// Value assigned to Return, which is the resulting constant.
|
||||
return_value: Option<Const<'tcx>>
|
||||
/// Values of locals in a constant or const fn.
|
||||
locals: IndexVec<mir::Local, Option<Const<'tcx>>>
|
||||
}
|
||||
|
||||
|
||||
|
@ -223,15 +214,17 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
substs: &'tcx Substs<'tcx>,
|
||||
args: IndexVec<mir::Arg, Const<'tcx>>)
|
||||
-> MirConstContext<'a, 'tcx> {
|
||||
MirConstContext {
|
||||
let mut context = MirConstContext {
|
||||
ccx: ccx,
|
||||
mir: mir,
|
||||
substs: substs,
|
||||
args: args,
|
||||
vars: IndexVec::from_elem(None, &mir.var_decls),
|
||||
temps: IndexVec::from_elem(None, &mir.temp_decls),
|
||||
return_value: None
|
||||
locals: (0..mir.count_locals()).map(|_| None).collect(),
|
||||
};
|
||||
for (i, arg) in args.into_iter().enumerate() {
|
||||
let index = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(i))).unwrap();
|
||||
context.locals[index] = Some(arg);
|
||||
}
|
||||
context
|
||||
}
|
||||
|
||||
fn trans_def(ccx: &'a CrateContext<'a, 'tcx>,
|
||||
|
@ -302,9 +295,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
mir::TerminatorKind::Goto { target } => target,
|
||||
mir::TerminatorKind::Return => {
|
||||
failure?;
|
||||
return Ok(self.return_value.unwrap_or_else(|| {
|
||||
let index = self.mir.local_index(&mir::Lvalue::ReturnPointer).unwrap();
|
||||
return Ok(self.locals[index].unwrap_or_else(|| {
|
||||
span_bug!(span, "no returned value in constant");
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => {
|
||||
|
@ -366,30 +360,28 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) {
|
||||
let dest = match *dest {
|
||||
mir::Lvalue::Var(var) => &mut self.vars[var],
|
||||
mir::Lvalue::Temp(temp) => &mut self.temps[temp],
|
||||
mir::Lvalue::ReturnPointer => &mut self.return_value,
|
||||
_ => span_bug!(span, "assignment to {:?} in constant", dest)
|
||||
};
|
||||
*dest = Some(value);
|
||||
if let Some(index) = self.mir.local_index(dest) {
|
||||
self.locals[index] = Some(value);
|
||||
} else {
|
||||
span_bug!(span, "assignment to {:?} in constant", dest);
|
||||
}
|
||||
}
|
||||
|
||||
fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span)
|
||||
-> Result<ConstLvalue<'tcx>, ConstEvalFailure> {
|
||||
let tcx = self.ccx.tcx();
|
||||
|
||||
if let Some(index) = self.mir.local_index(lvalue) {
|
||||
return Ok(self.locals[index].unwrap_or_else(|| {
|
||||
span_bug!(span, "{:?} not initialized", lvalue)
|
||||
}).as_lvalue());
|
||||
}
|
||||
|
||||
let lvalue = match *lvalue {
|
||||
mir::Lvalue::Var(var) => {
|
||||
self.vars[var].unwrap_or_else(|| {
|
||||
span_bug!(span, "{:?} not initialized", var)
|
||||
}).as_lvalue()
|
||||
}
|
||||
mir::Lvalue::Temp(temp) => {
|
||||
self.temps[temp].unwrap_or_else(|| {
|
||||
span_bug!(span, "{:?} not initialized", temp)
|
||||
}).as_lvalue()
|
||||
}
|
||||
mir::Lvalue::Arg(arg) => self.args[arg].as_lvalue(),
|
||||
mir::Lvalue::Var(_) |
|
||||
mir::Lvalue::Temp(_) |
|
||||
mir::Lvalue::Arg(_) |
|
||||
mir::Lvalue::ReturnPointer => bug!(), // handled above
|
||||
mir::Lvalue::Static(def_id) => {
|
||||
ConstLvalue {
|
||||
base: Base::Static(consts::get_static(self.ccx, def_id).val),
|
||||
|
@ -397,9 +389,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||
ty: self.mir.lvalue_ty(tcx, lvalue).to_ty(tcx)
|
||||
}
|
||||
}
|
||||
mir::Lvalue::ReturnPointer => {
|
||||
span_bug!(span, "accessing Lvalue::ReturnPointer in constant")
|
||||
}
|
||||
mir::Lvalue::Projection(ref projection) => {
|
||||
let tr_base = self.const_lvalue(&projection.base, span)?;
|
||||
let projected_ty = LvalueTy::Ty { ty: tr_base.ty }
|
||||
|
|
|
@ -26,7 +26,7 @@ use Disr;
|
|||
|
||||
use std::ptr;
|
||||
|
||||
use super::{MirContext, TempRef};
|
||||
use super::{MirContext, LocalRef};
|
||||
use super::operand::OperandValue;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -88,40 +88,30 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
-> LvalueRef<'tcx> {
|
||||
debug!("trans_lvalue(lvalue={:?})", lvalue);
|
||||
|
||||
let fcx = bcx.fcx();
|
||||
let ccx = bcx.ccx();
|
||||
let tcx = bcx.tcx();
|
||||
|
||||
if let Some(index) = self.mir.local_index(lvalue) {
|
||||
match self.locals[index] {
|
||||
LocalRef::Lvalue(lvalue) => {
|
||||
return lvalue;
|
||||
}
|
||||
LocalRef::Operand(..) => {
|
||||
bug!("using operand local {:?} as lvalue", lvalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = match *lvalue {
|
||||
mir::Lvalue::Var(var) => self.vars[var],
|
||||
mir::Lvalue::Temp(temp) => match self.temps[temp] {
|
||||
TempRef::Lvalue(lvalue) =>
|
||||
lvalue,
|
||||
TempRef::Operand(..) =>
|
||||
bug!("using operand temp {:?} as lvalue", lvalue),
|
||||
},
|
||||
mir::Lvalue::Arg(arg) => self.args[arg],
|
||||
mir::Lvalue::Var(_) |
|
||||
mir::Lvalue::Temp(_) |
|
||||
mir::Lvalue::Arg(_) |
|
||||
mir::Lvalue::ReturnPointer => bug!(), // handled above
|
||||
mir::Lvalue::Static(def_id) => {
|
||||
let const_ty = self.lvalue_ty(lvalue);
|
||||
LvalueRef::new_sized(consts::get_static(ccx, def_id).val,
|
||||
LvalueTy::from_ty(const_ty))
|
||||
},
|
||||
mir::Lvalue::ReturnPointer => {
|
||||
let llval = if !fcx.fn_ty.ret.is_ignore() {
|
||||
bcx.with_block(|bcx| {
|
||||
fcx.get_ret_slot(bcx, "")
|
||||
})
|
||||
} else {
|
||||
// This is a void return; that is, there’s no place to store the value and
|
||||
// there cannot really be one (or storing into it doesn’t make sense, anyway).
|
||||
// Ergo, we return an undef ValueRef, so we do not have to special-case every
|
||||
// place using lvalues, and could use it the same way you use a regular
|
||||
// ReturnPointer LValue (i.e. store into it, load from it etc).
|
||||
C_undef(fcx.fn_ty.ret.original_ty.ptr_to())
|
||||
};
|
||||
let fn_return_ty = bcx.monomorphize(&self.mir.return_ty);
|
||||
let return_ty = fn_return_ty.unwrap();
|
||||
LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty))
|
||||
},
|
||||
mir::Lvalue::Projection(box mir::Projection {
|
||||
ref base,
|
||||
elem: mir::ProjectionElem::Deref
|
||||
|
@ -240,44 +230,41 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
}
|
||||
|
||||
// Perform an action using the given Lvalue.
|
||||
// If the Lvalue is an empty TempRef::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.
|
||||
pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
lvalue: &mir::Lvalue<'tcx>, f: F) -> U
|
||||
where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U
|
||||
{
|
||||
match *lvalue {
|
||||
mir::Lvalue::Temp(temp) => {
|
||||
match self.temps[temp] {
|
||||
TempRef::Lvalue(lvalue) => f(self, lvalue),
|
||||
TempRef::Operand(None) => {
|
||||
let lvalue_ty = self.lvalue_ty(lvalue);
|
||||
let lvalue = LvalueRef::alloca(bcx,
|
||||
lvalue_ty,
|
||||
"lvalue_temp");
|
||||
let ret = f(self, lvalue);
|
||||
let op = self.trans_load(bcx, lvalue.llval, lvalue_ty);
|
||||
self.temps[temp] = TempRef::Operand(Some(op));
|
||||
ret
|
||||
}
|
||||
TempRef::Operand(Some(_)) => {
|
||||
// See comments in TempRef::new_operand as to why
|
||||
// we always have Some in a ZST TempRef::Operand.
|
||||
let ty = self.lvalue_ty(lvalue);
|
||||
if common::type_is_zero_size(bcx.ccx(), ty) {
|
||||
// Pass an undef pointer as no stores can actually occur.
|
||||
let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to());
|
||||
f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty)))
|
||||
} else {
|
||||
bug!("Lvalue temp already set");
|
||||
}
|
||||
if let Some(index) = self.mir.local_index(lvalue) {
|
||||
match self.locals[index] {
|
||||
LocalRef::Lvalue(lvalue) => f(self, lvalue),
|
||||
LocalRef::Operand(None) => {
|
||||
let lvalue_ty = self.lvalue_ty(lvalue);
|
||||
let lvalue = LvalueRef::alloca(bcx,
|
||||
lvalue_ty,
|
||||
"lvalue_temp");
|
||||
let ret = f(self, lvalue);
|
||||
let op = self.trans_load(bcx, lvalue.llval, lvalue_ty);
|
||||
self.locals[index] = LocalRef::Operand(Some(op));
|
||||
ret
|
||||
}
|
||||
LocalRef::Operand(Some(_)) => {
|
||||
// See comments in LocalRef::new_operand as to why
|
||||
// we always have Some in a ZST LocalRef::Operand.
|
||||
let ty = self.lvalue_ty(lvalue);
|
||||
if common::type_is_zero_size(bcx.ccx(), ty) {
|
||||
// Pass an undef pointer as no stores can actually occur.
|
||||
let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to());
|
||||
f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty)))
|
||||
} else {
|
||||
bug!("Lvalue local already set");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let lvalue = self.trans_lvalue(bcx, lvalue);
|
||||
f(self, lvalue)
|
||||
}
|
||||
} else {
|
||||
let lvalue = self.trans_lvalue(bcx, lvalue);
|
||||
f(self, lvalue)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,16 +84,13 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
|
|||
/// Cached unreachable block
|
||||
unreachable_block: Option<Block<'bcx, 'tcx>>,
|
||||
|
||||
/// An LLVM alloca for each MIR `VarDecl`
|
||||
vars: IndexVec<mir::Var, LvalueRef<'tcx>>,
|
||||
|
||||
/// The location where each MIR `TempDecl` 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:
|
||||
/// sometimes we can skip the alloca and just store the value
|
||||
/// directly using an `OperandRef`, which makes for tighter LLVM
|
||||
/// IR. The conditions for using an `OperandRef` are as follows:
|
||||
///
|
||||
/// - the type of the temporary must be judged "immediate" by `type_is_immediate`
|
||||
/// - the type of the local must be judged "immediate" by `type_is_immediate`
|
||||
/// - the operand must never be referenced indirectly
|
||||
/// - we should not take its address using the `&` operator
|
||||
/// - nor should it appear in an lvalue path like `tmp.a`
|
||||
|
@ -102,12 +99,7 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
|
|||
///
|
||||
/// Avoiding allocs can also be important for certain intrinsics,
|
||||
/// notably `expect`.
|
||||
temps: IndexVec<mir::Temp, TempRef<'tcx>>,
|
||||
|
||||
/// The arguments to the function; as args are lvalues, these are
|
||||
/// always indirect, though we try to avoid creating an alloca
|
||||
/// when we can (and just reuse the pointer the caller provided).
|
||||
args: IndexVec<mir::Arg, LvalueRef<'tcx>>,
|
||||
locals: IndexVec<mir::Local, LocalRef<'tcx>>,
|
||||
|
||||
/// Debug information for MIR scopes.
|
||||
scopes: IndexVec<mir::VisibilityScope, DIScope>
|
||||
|
@ -119,14 +111,14 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
enum TempRef<'tcx> {
|
||||
enum LocalRef<'tcx> {
|
||||
Lvalue(LvalueRef<'tcx>),
|
||||
Operand(Option<OperandRef<'tcx>>),
|
||||
}
|
||||
|
||||
impl<'tcx> TempRef<'tcx> {
|
||||
impl<'tcx> LocalRef<'tcx> {
|
||||
fn new_operand<'bcx>(ccx: &CrateContext<'bcx, 'tcx>,
|
||||
ty: ty::Ty<'tcx>) -> TempRef<'tcx> {
|
||||
ty: ty::Ty<'tcx>) -> LocalRef<'tcx> {
|
||||
if common::type_is_zero_size(ccx, ty) {
|
||||
// Zero-size temporaries aren't always initialized, which
|
||||
// doesn't matter because they don't contain data, but
|
||||
|
@ -142,9 +134,9 @@ impl<'tcx> TempRef<'tcx> {
|
|||
val: val,
|
||||
ty: ty
|
||||
};
|
||||
TempRef::Operand(Some(op))
|
||||
LocalRef::Operand(Some(op))
|
||||
} else {
|
||||
TempRef::Operand(None)
|
||||
LocalRef::Operand(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,8 +149,8 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
|
|||
|
||||
// Analyze the temps to determine which must be lvalues
|
||||
// FIXME
|
||||
let (lvalue_temps, cleanup_kinds) = bcx.with_block(|bcx| {
|
||||
(analyze::lvalue_temps(bcx, &mir),
|
||||
let (lvalue_locals, cleanup_kinds) = bcx.with_block(|bcx| {
|
||||
(analyze::lvalue_locals(bcx, &mir),
|
||||
analyze::cleanup_kinds(bcx, &mir))
|
||||
});
|
||||
|
||||
|
@ -166,37 +158,49 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
|
|||
let scopes = debuginfo::create_mir_scopes(fcx);
|
||||
|
||||
// Allocate variable and temp allocas
|
||||
let args = arg_value_refs(&bcx, &mir, &scopes);
|
||||
let vars = mir.var_decls.iter()
|
||||
.map(|decl| (bcx.monomorphize(&decl.ty), decl))
|
||||
.map(|(mty, decl)| {
|
||||
let lvalue = LvalueRef::alloca(&bcx, mty, &decl.name.as_str());
|
||||
let locals = {
|
||||
let args = arg_local_refs(&bcx, &mir, &scopes, &lvalue_locals);
|
||||
let vars = mir.var_decls.iter().enumerate().map(|(i, decl)| {
|
||||
let ty = bcx.monomorphize(&decl.ty);
|
||||
let scope = scopes[decl.source_info.scope];
|
||||
let dbg = !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo;
|
||||
|
||||
let scope = scopes[decl.source_info.scope];
|
||||
if !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
|
||||
bcx.with_block(|bcx| {
|
||||
declare_local(bcx, decl.name, mty, scope,
|
||||
VariableAccess::DirectVariable { alloca: lvalue.llval },
|
||||
VariableKind::LocalVariable, decl.source_info.span);
|
||||
});
|
||||
}
|
||||
let local = mir.local_index(&mir::Lvalue::Var(mir::Var::new(i))).unwrap();
|
||||
if !lvalue_locals.contains(local.index()) && !dbg {
|
||||
return LocalRef::new_operand(bcx.ccx(), ty);
|
||||
}
|
||||
|
||||
lvalue
|
||||
}).collect();
|
||||
let temps = mir.temp_decls.iter()
|
||||
.map(|decl| bcx.monomorphize(&decl.ty))
|
||||
.enumerate()
|
||||
.map(|(i, mty)| if lvalue_temps.contains(i) {
|
||||
TempRef::Lvalue(LvalueRef::alloca(&bcx,
|
||||
mty,
|
||||
&format!("temp{:?}", i)))
|
||||
} else {
|
||||
// If this is an immediate temp, we do not create an
|
||||
// alloca in advance. Instead we wait until we see the
|
||||
// definition and update the operand there.
|
||||
TempRef::new_operand(bcx.ccx(), mty)
|
||||
})
|
||||
.collect();
|
||||
let lvalue = LvalueRef::alloca(&bcx, ty, &decl.name.as_str());
|
||||
if dbg {
|
||||
bcx.with_block(|bcx| {
|
||||
declare_local(bcx, decl.name, ty, scope,
|
||||
VariableAccess::DirectVariable { alloca: lvalue.llval },
|
||||
VariableKind::LocalVariable, decl.source_info.span);
|
||||
});
|
||||
}
|
||||
LocalRef::Lvalue(lvalue)
|
||||
});
|
||||
|
||||
let locals = mir.temp_decls.iter().enumerate().map(|(i, decl)| {
|
||||
(mir::Lvalue::Temp(mir::Temp::new(i)), decl.ty)
|
||||
}).chain(mir.return_ty.maybe_converging().map(|ty| (mir::Lvalue::ReturnPointer, ty)));
|
||||
|
||||
args.into_iter().chain(vars).chain(locals.map(|(lvalue, ty)| {
|
||||
let ty = bcx.monomorphize(&ty);
|
||||
let local = mir.local_index(&lvalue).unwrap();
|
||||
if lvalue == mir::Lvalue::ReturnPointer && fcx.fn_ty.ret.is_indirect() {
|
||||
let llretptr = llvm::get_param(fcx.llfn, 0);
|
||||
LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty)))
|
||||
} else if lvalue_locals.contains(local.index()) {
|
||||
LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", lvalue)))
|
||||
} else {
|
||||
// If this is an immediate local, we do not create an
|
||||
// alloca in advance. Instead we wait until we see the
|
||||
// definition and update the operand there.
|
||||
LocalRef::new_operand(bcx.ccx(), ty)
|
||||
}
|
||||
})).collect()
|
||||
};
|
||||
|
||||
// Allocate a `Block` for every basic block
|
||||
let block_bcxs: IndexVec<mir::BasicBlock, Block<'blk,'tcx>> =
|
||||
|
@ -225,9 +229,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
|
|||
unreachable_block: None,
|
||||
cleanup_kinds: cleanup_kinds,
|
||||
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
|
||||
vars: vars,
|
||||
temps: temps,
|
||||
args: args,
|
||||
locals: locals,
|
||||
scopes: scopes
|
||||
};
|
||||
|
||||
|
@ -266,10 +268,11 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
|
|||
/// Produce, for each argument, a `ValueRef` pointing at the
|
||||
/// argument's value. As arguments are lvalues, these are always
|
||||
/// indirect.
|
||||
fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
mir: &mir::Mir<'tcx>,
|
||||
scopes: &IndexVec<mir::VisibilityScope, DIScope>)
|
||||
-> IndexVec<mir::Arg, LvalueRef<'tcx>> {
|
||||
scopes: &IndexVec<mir::VisibilityScope, DIScope>,
|
||||
lvalue_locals: &BitVector)
|
||||
-> Vec<LocalRef<'tcx>> {
|
||||
let fcx = bcx.fcx();
|
||||
let tcx = bcx.tcx();
|
||||
let mut idx = 0;
|
||||
|
@ -285,6 +288,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
|||
|
||||
mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| {
|
||||
let arg_ty = bcx.monomorphize(&arg_decl.ty);
|
||||
let local = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(arg_index))).unwrap();
|
||||
if arg_decl.spread {
|
||||
// This argument (e.g. the last argument in the "rust-call" ABI)
|
||||
// is a tuple that was spread at the ABI level and now we have
|
||||
|
@ -305,8 +309,8 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
|||
let arg = &fcx.fn_ty.args[idx];
|
||||
idx += 1;
|
||||
if common::type_is_fat_ptr(tcx, tupled_arg_ty) {
|
||||
// We pass fat pointers as two words, but inside the tuple
|
||||
// they are the two sub-fields of a single aggregate field.
|
||||
// We pass fat pointers as two words, but inside the tuple
|
||||
// they are the two sub-fields of a single aggregate field.
|
||||
let meta = &fcx.fn_ty.args[idx];
|
||||
idx += 1;
|
||||
arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, dst));
|
||||
|
@ -335,7 +339,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
|||
bcx.fcx().span.unwrap_or(DUMMY_SP));
|
||||
}));
|
||||
}
|
||||
return 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];
|
||||
|
@ -345,9 +349,42 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
|||
// already put it in a temporary alloca and gave it up, unless
|
||||
// we emit extra-debug-info, which requires local allocas :(.
|
||||
// FIXME: lifetimes
|
||||
if arg.pad.is_some() {
|
||||
llarg_idx += 1;
|
||||
}
|
||||
let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
|
||||
llarg_idx += 1;
|
||||
llarg
|
||||
} else if !lvalue_locals.contains(local.index()) &&
|
||||
!arg.is_indirect() && arg.cast.is_none() &&
|
||||
arg_scope.is_none() {
|
||||
if arg.is_ignore() {
|
||||
return LocalRef::new_operand(bcx.ccx(), arg_ty);
|
||||
}
|
||||
|
||||
// We don't have to cast or keep the argument in the alloca.
|
||||
// FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
|
||||
// of putting everything in allocas just so we can use llvm.dbg.declare.
|
||||
if arg.pad.is_some() {
|
||||
llarg_idx += 1;
|
||||
}
|
||||
let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
|
||||
llarg_idx += 1;
|
||||
let val = if common::type_is_fat_ptr(tcx, arg_ty) {
|
||||
let meta = &fcx.fn_ty.args[idx];
|
||||
idx += 1;
|
||||
assert_eq!((meta.cast, meta.pad), (None, None));
|
||||
let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
|
||||
llarg_idx += 1;
|
||||
OperandValue::Pair(llarg, llmeta)
|
||||
} else {
|
||||
OperandValue::Immediate(llarg)
|
||||
};
|
||||
let operand = OperandRef {
|
||||
val: val,
|
||||
ty: arg_ty
|
||||
};
|
||||
return LocalRef::Operand(Some(operand.unpack_if_pair(bcx)));
|
||||
} else {
|
||||
let lltemp = bcx.with_block(|bcx| {
|
||||
base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index))
|
||||
|
@ -441,7 +478,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
|||
bcx.fcx().span.unwrap_or(DUMMY_SP));
|
||||
}
|
||||
}));
|
||||
LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))
|
||||
LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)))
|
||||
}).collect()
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ use type_::Type;
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use super::{MirContext, TempRef};
|
||||
use super::{MirContext, LocalRef};
|
||||
|
||||
/// The representation of a Rust value. The enum variant is in fact
|
||||
/// uniquely determined by the value's type, but is kept as a
|
||||
|
@ -112,6 +112,8 @@ impl<'bcx, 'tcx> OperandRef<'tcx> {
|
|||
if let OperandValue::Immediate(llval) = self.val {
|
||||
// Deconstruct the immediate aggregate.
|
||||
if common::type_is_imm_pair(bcx.ccx(), self.ty) {
|
||||
debug!("Operand::unpack_if_pair: unpacking {:?}", self);
|
||||
|
||||
let mut a = bcx.extract_value(llval, 0);
|
||||
let mut b = bcx.extract_value(llval, 1);
|
||||
|
||||
|
@ -171,17 +173,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
{
|
||||
debug!("trans_consume(lvalue={:?})", lvalue);
|
||||
|
||||
// watch out for temporaries that do not have an
|
||||
// watch out for locals that do not have an
|
||||
// alloca; they are handled somewhat differently
|
||||
if let &mir::Lvalue::Temp(index) = lvalue {
|
||||
match self.temps[index] {
|
||||
TempRef::Operand(Some(o)) => {
|
||||
if let Some(index) = self.mir.local_index(lvalue) {
|
||||
match self.locals[index] {
|
||||
LocalRef::Operand(Some(o)) => {
|
||||
return o;
|
||||
}
|
||||
TempRef::Operand(None) => {
|
||||
LocalRef::Operand(None) => {
|
||||
bug!("use of {:?} before def", lvalue);
|
||||
}
|
||||
TempRef::Lvalue(..) => {
|
||||
LocalRef::Lvalue(..) => {
|
||||
// use path below
|
||||
}
|
||||
}
|
||||
|
@ -189,9 +191,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
|
||||
// Moves out of pair fields are trivial.
|
||||
if let &mir::Lvalue::Projection(ref proj) = lvalue {
|
||||
if let mir::Lvalue::Temp(index) = proj.base {
|
||||
let temp_ref = &self.temps[index];
|
||||
if let &TempRef::Operand(Some(o)) = temp_ref {
|
||||
if let Some(index) = self.mir.local_index(&proj.base) {
|
||||
if let LocalRef::Operand(Some(o)) = self.locals[index] {
|
||||
match (o.val, &proj.elem) {
|
||||
(OperandValue::Pair(a, b),
|
||||
&mir::ProjectionElem::Field(ref f, ty)) => {
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc::mir::repr as mir;
|
|||
use common::{self, BlockAndBuilder};
|
||||
|
||||
use super::MirContext;
|
||||
use super::TempRef;
|
||||
use super::LocalRef;
|
||||
|
||||
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
pub fn trans_statement(&mut self,
|
||||
|
@ -27,37 +27,34 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
|||
debug_loc.apply(bcx.fcx());
|
||||
match statement.kind {
|
||||
mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
|
||||
match *lvalue {
|
||||
mir::Lvalue::Temp(index) => {
|
||||
match self.temps[index] {
|
||||
TempRef::Lvalue(tr_dest) => {
|
||||
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
|
||||
}
|
||||
TempRef::Operand(None) => {
|
||||
let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue,
|
||||
debug_loc);
|
||||
self.temps[index] = TempRef::Operand(Some(operand));
|
||||
bcx
|
||||
}
|
||||
TempRef::Operand(Some(_)) => {
|
||||
let ty = self.lvalue_ty(lvalue);
|
||||
if let Some(index) = self.mir.local_index(lvalue) {
|
||||
match self.locals[index] {
|
||||
LocalRef::Lvalue(tr_dest) => {
|
||||
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
|
||||
}
|
||||
LocalRef::Operand(None) => {
|
||||
let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue,
|
||||
debug_loc);
|
||||
self.locals[index] = LocalRef::Operand(Some(operand));
|
||||
bcx
|
||||
}
|
||||
LocalRef::Operand(Some(_)) => {
|
||||
let ty = self.lvalue_ty(lvalue);
|
||||
|
||||
if !common::type_is_zero_size(bcx.ccx(), ty) {
|
||||
span_bug!(statement.source_info.span,
|
||||
"operand {:?} already assigned",
|
||||
rvalue);
|
||||
} else {
|
||||
// If the type is zero-sized, it's already been set here,
|
||||
// but we still need to make sure we translate the operand
|
||||
self.trans_rvalue_operand(bcx, rvalue, debug_loc).0
|
||||
}
|
||||
if !common::type_is_zero_size(bcx.ccx(), ty) {
|
||||
span_bug!(statement.source_info.span,
|
||||
"operand {:?} already assigned",
|
||||
rvalue);
|
||||
} else {
|
||||
// If the type is zero-sized, it's already been set here,
|
||||
// but we still need to make sure we translate the operand
|
||||
self.trans_rvalue_operand(bcx, rvalue, debug_loc).0
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let tr_dest = self.trans_lvalue(&bcx, lvalue);
|
||||
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
|
||||
}
|
||||
} else {
|
||||
let tr_dest = self.trans_lvalue(&bcx, lvalue);
|
||||
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
pub struct Bytes {
|
||||
a: u8,
|
||||
|
@ -21,6 +22,7 @@ pub struct Bytes {
|
|||
|
||||
// CHECK-LABEL: @borrow
|
||||
#[no_mangle]
|
||||
#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
|
||||
pub fn borrow(x: &i32) -> &i32 {
|
||||
// CHECK: load {{(i32\*, )?}}i32** %x{{.*}}, !nonnull
|
||||
x
|
||||
|
@ -28,6 +30,7 @@ pub fn borrow(x: &i32) -> &i32 {
|
|||
|
||||
// CHECK-LABEL: @_box
|
||||
#[no_mangle]
|
||||
#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
|
||||
pub fn _box(x: Box<i32>) -> i32 {
|
||||
// CHECK: load {{(i32\*, )?}}i32** %x{{.*}}, !nonnull
|
||||
*x
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(naked_functions, rustc_attrs)]
|
||||
|
||||
// CHECK: Function Attrs: naked uwtable
|
||||
// CHECK-NEXT: define internal void @naked_empty()
|
||||
|
@ -26,6 +26,7 @@ fn naked_empty() {
|
|||
// CHECK: Function Attrs: naked uwtable
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
|
||||
// CHECK-NEXT: define internal void @naked_with_args(i{{[0-9]+}})
|
||||
fn naked_with_args(a: isize) {
|
||||
// CHECK: %a = alloca i{{[0-9]+}}
|
||||
|
@ -45,6 +46,7 @@ fn naked_with_return() -> isize {
|
|||
// CHECK-NEXT: define internal i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}})
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
|
||||
fn naked_with_args_and_return(a: isize) -> isize {
|
||||
// CHECK: %a = alloca i{{[0-9]+}}
|
||||
// CHECK: ret i{{[0-9]+}} %{{[0-9]+}}
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
|
||||
// error-pattern: overflow representing the type `S`
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
trait Mirror { type It: ?Sized; }
|
||||
impl<T: ?Sized> Mirror for T { type It = Self; }
|
||||
struct S(Option<<S as Mirror>::It>);
|
||||
|
||||
#[rustc_no_mir] // FIXME #27840 MIR tries to represent `std::option::Option<S>` first.
|
||||
fn main() {
|
||||
let _s = S(None);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue