2013-05-03 22:07:33 -04:00
|
|
|
// Copyright 2012 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.
|
|
|
|
|
|
|
|
//! Logic relating to rooting and write guards for managed values
|
|
|
|
//! (`@` and `@mut`). This code is primarily for use by datum;
|
|
|
|
//! it exists in its own module both to keep datum.rs bite-sized
|
|
|
|
//! and for each in debugging (e.g., so you can use
|
|
|
|
//! `RUST_LOG=rustc::middle::trans::write_guard`).
|
|
|
|
|
2013-05-17 15:28:44 -07:00
|
|
|
|
2013-05-03 22:07:33 -04:00
|
|
|
use lib::llvm::ValueRef;
|
|
|
|
use middle::borrowck::{RootInfo, root_map_key, DynaImm, DynaMut};
|
2013-07-15 20:42:13 -07:00
|
|
|
use middle::lang_items::CheckNotBorrowedFnLangItem;
|
|
|
|
use middle::lang_items::{BorrowAsImmFnLangItem, BorrowAsMutFnLangItem};
|
|
|
|
use middle::lang_items::{RecordBorrowFnLangItem, UnrecordBorrowFnLangItem};
|
|
|
|
use middle::lang_items::ReturnToMutFnLangItem;
|
2013-05-03 22:07:33 -04:00
|
|
|
use middle::trans::base::*;
|
|
|
|
use middle::trans::build::*;
|
|
|
|
use middle::trans::callee;
|
|
|
|
use middle::trans::common::*;
|
|
|
|
use middle::trans::datum::*;
|
|
|
|
use middle::trans::expr;
|
|
|
|
use middle::ty;
|
2013-08-31 18:13:04 +02:00
|
|
|
use syntax::codemap::Span;
|
2013-05-03 22:07:33 -04:00
|
|
|
use syntax::ast;
|
|
|
|
|
2013-06-16 22:52:44 +12:00
|
|
|
use middle::trans::type_::Type;
|
|
|
|
|
2013-05-03 22:07:33 -04:00
|
|
|
pub fn root_and_write_guard(datum: &Datum,
|
2013-07-17 12:12:08 +02:00
|
|
|
mut bcx: @mut Block,
|
2013-08-31 18:13:04 +02:00
|
|
|
span: Span,
|
2013-07-27 10:25:59 +02:00
|
|
|
expr_id: ast::NodeId,
|
2013-07-17 12:12:08 +02:00
|
|
|
derefs: uint) -> @mut Block {
|
2013-05-03 22:07:33 -04:00
|
|
|
let key = root_map_key { id: expr_id, derefs: derefs };
|
2013-09-27 22:38:08 -07:00
|
|
|
debug2!("write_guard::root_and_write_guard(key={:?})", key);
|
2013-05-03 22:07:33 -04:00
|
|
|
|
|
|
|
// root the autoderef'd value, if necessary:
|
|
|
|
//
|
|
|
|
// (Note: root'd values are always boxes)
|
|
|
|
let ccx = bcx.ccx();
|
|
|
|
bcx = match ccx.maps.root_map.find(&key) {
|
|
|
|
None => bcx,
|
|
|
|
Some(&root_info) => root(datum, bcx, span, key, root_info)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Perform the write guard, if necessary.
|
|
|
|
//
|
|
|
|
// (Note: write-guarded values are always boxes)
|
|
|
|
if ccx.maps.write_guard_map.contains(&key) {
|
|
|
|
perform_write_guard(datum, bcx, span)
|
|
|
|
} else {
|
|
|
|
bcx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-17 12:12:08 +02:00
|
|
|
pub fn return_to_mut(mut bcx: @mut Block,
|
2013-05-03 22:07:33 -04:00
|
|
|
root_key: root_map_key,
|
|
|
|
frozen_val_ref: ValueRef,
|
|
|
|
bits_val_ref: ValueRef,
|
|
|
|
filename_val: ValueRef,
|
2013-07-17 12:12:08 +02:00
|
|
|
line_val: ValueRef) -> @mut Block {
|
2013-09-27 22:38:08 -07:00
|
|
|
debug2!("write_guard::return_to_mut(root_key={:?}, {}, {}, {})",
|
2013-05-03 22:07:33 -04:00
|
|
|
root_key,
|
|
|
|
bcx.to_str(),
|
2013-06-15 15:16:03 +12:00
|
|
|
bcx.val_to_str(frozen_val_ref),
|
|
|
|
bcx.val_to_str(bits_val_ref));
|
2013-05-03 22:07:33 -04:00
|
|
|
|
2013-06-16 15:45:48 +12:00
|
|
|
let box_ptr = Load(bcx, PointerCast(bcx, frozen_val_ref, Type::i8p().ptr_to()));
|
2013-05-03 22:07:33 -04:00
|
|
|
|
2013-06-16 15:45:48 +12:00
|
|
|
let bits_val = Load(bcx, bits_val_ref);
|
2013-05-03 22:07:33 -04:00
|
|
|
|
2013-05-15 14:01:54 -07:00
|
|
|
if bcx.tcx().sess.debug_borrows() {
|
2013-06-16 15:45:48 +12:00
|
|
|
bcx = callee::trans_lang_call( bcx,
|
2013-07-15 20:42:13 -07:00
|
|
|
langcall(bcx, None, "unborrow", UnrecordBorrowFnLangItem),
|
2013-05-19 01:07:44 -04:00
|
|
|
[
|
2013-05-03 22:07:33 -04:00
|
|
|
box_ptr,
|
|
|
|
bits_val,
|
|
|
|
filename_val,
|
|
|
|
line_val
|
|
|
|
],
|
2013-07-08 08:12:01 +02:00
|
|
|
Some(expr::Ignore)).bcx;
|
2013-05-03 22:07:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
callee::trans_lang_call(
|
|
|
|
bcx,
|
2013-07-15 20:42:13 -07:00
|
|
|
langcall(bcx, None, "unborrow", ReturnToMutFnLangItem),
|
2013-05-19 01:07:44 -04:00
|
|
|
[
|
2013-05-03 22:07:33 -04:00
|
|
|
box_ptr,
|
|
|
|
bits_val,
|
|
|
|
filename_val,
|
|
|
|
line_val
|
|
|
|
],
|
2013-07-08 08:12:01 +02:00
|
|
|
Some(expr::Ignore)
|
|
|
|
).bcx
|
2013-05-03 22:07:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn root(datum: &Datum,
|
2013-07-17 12:12:08 +02:00
|
|
|
mut bcx: @mut Block,
|
2013-08-31 18:13:04 +02:00
|
|
|
span: Span,
|
2013-05-03 22:07:33 -04:00
|
|
|
root_key: root_map_key,
|
2013-07-17 12:12:08 +02:00
|
|
|
root_info: RootInfo) -> @mut Block {
|
2013-05-03 22:07:33 -04:00
|
|
|
//! In some cases, borrowck will decide that an @T/@[]/@str
|
|
|
|
//! value must be rooted for the program to be safe. In that
|
|
|
|
//! case, we will call this function, which will stash a copy
|
|
|
|
//! away until we exit the scope `scope_id`.
|
|
|
|
|
2013-09-27 22:38:08 -07:00
|
|
|
debug2!("write_guard::root(root_key={:?}, root_info={:?}, datum={:?})",
|
2013-05-03 22:07:33 -04:00
|
|
|
root_key, root_info, datum.to_str(bcx.ccx()));
|
|
|
|
|
|
|
|
if bcx.sess().trace() {
|
|
|
|
trans_trace(
|
|
|
|
bcx, None,
|
2013-09-27 22:38:08 -07:00
|
|
|
(format!("preserving until end of scope {}",
|
2013-06-13 03:02:55 +10:00
|
|
|
root_info.scope)).to_managed());
|
2013-05-03 22:07:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// First, root the datum. Note that we must zero this value,
|
|
|
|
// because sometimes we root on one path but not another.
|
|
|
|
// See e.g. #4904.
|
2013-06-20 15:21:37 -04:00
|
|
|
let scratch = scratch_datum(bcx, datum.ty, "__write_guard", true);
|
2013-05-03 22:07:33 -04:00
|
|
|
datum.copy_to_datum(bcx, INIT, scratch);
|
|
|
|
let cleanup_bcx = find_bcx_for_scope(bcx, root_info.scope);
|
Implement scopes independent of LLVM basic blocks
Currently, scopes are tied to LLVM basic blocks. For each scope, there
are two new basic blocks, which means two extra jumps in the unoptimized
IR. These blocks aren't actually required, but only used to act as the
boundary for cleanups.
By keeping track of the current scope within a single basic block, we
can avoid those extra blocks and jumps, shrinking the pre-optimization
IR quite considerably. For example, the IR for trans_intrinsic goes
from ~22k lines to ~16k lines, almost 30% less.
The impact on the build times of optimized builds is rather small (about
1%), but unoptimized builds are about 11% faster. The testsuite for
unoptimized builds runs between 15% (CPU time) and 7.5% (wallclock time on
my i7) faster.
Also, in some situations this helps LLVM to generate better code by
inlining functions that it previously considered to be too large.
Likely because of the pointless blocks/jumps that were still present at
the time the inlining pass runs.
Refs #7462
2013-07-07 14:53:57 +02:00
|
|
|
add_clean_temp_mem_in_scope(cleanup_bcx, root_info.scope, scratch.val, scratch.ty);
|
2013-05-03 22:07:33 -04:00
|
|
|
|
|
|
|
// Now, consider also freezing it.
|
|
|
|
match root_info.freeze {
|
|
|
|
None => {}
|
|
|
|
Some(freeze_kind) => {
|
|
|
|
let (filename, line) = filename_and_line_num_from_span(bcx, span);
|
|
|
|
|
|
|
|
// in this case, we don't have to zero, because
|
|
|
|
// scratch.val will be NULL should the cleanup get
|
|
|
|
// called without the freezing actually occurring, and
|
|
|
|
// return_to_mut checks for this condition.
|
2013-06-20 15:21:37 -04:00
|
|
|
let scratch_bits = scratch_datum(bcx, ty::mk_uint(),
|
|
|
|
"__write_guard_bits", false);
|
2013-05-03 22:07:33 -04:00
|
|
|
|
2013-07-15 20:42:13 -07:00
|
|
|
let freeze_item = match freeze_kind {
|
|
|
|
DynaImm => BorrowAsImmFnLangItem,
|
|
|
|
DynaMut => BorrowAsMutFnLangItem,
|
2013-05-03 22:07:33 -04:00
|
|
|
};
|
|
|
|
|
2013-06-16 15:45:48 +12:00
|
|
|
let box_ptr = Load(bcx, PointerCast(bcx, scratch.val, Type::i8p().ptr_to()));
|
2013-05-03 22:07:33 -04:00
|
|
|
|
2013-07-08 08:12:01 +02:00
|
|
|
let llresult = unpack_result!(bcx, callee::trans_lang_call(
|
2013-05-03 22:07:33 -04:00
|
|
|
bcx,
|
2013-07-15 20:42:13 -07:00
|
|
|
langcall(bcx, Some(span), "freeze", freeze_item),
|
2013-05-19 01:07:44 -04:00
|
|
|
[
|
2013-05-03 22:07:33 -04:00
|
|
|
box_ptr,
|
|
|
|
filename,
|
|
|
|
line
|
|
|
|
],
|
2013-07-08 08:12:01 +02:00
|
|
|
Some(expr::SaveIn(scratch_bits.val))));
|
2013-05-03 22:07:33 -04:00
|
|
|
|
2013-05-15 14:01:54 -07:00
|
|
|
if bcx.tcx().sess.debug_borrows() {
|
2013-05-03 22:07:33 -04:00
|
|
|
bcx = callee::trans_lang_call(
|
|
|
|
bcx,
|
2013-07-15 20:42:13 -07:00
|
|
|
langcall(bcx, Some(span), "freeze", RecordBorrowFnLangItem),
|
2013-05-19 01:07:44 -04:00
|
|
|
[
|
2013-05-03 22:07:33 -04:00
|
|
|
box_ptr,
|
2013-07-08 08:12:01 +02:00
|
|
|
llresult,
|
2013-05-03 22:07:33 -04:00
|
|
|
filename,
|
|
|
|
line
|
|
|
|
],
|
2013-07-08 08:12:01 +02:00
|
|
|
Some(expr::Ignore)).bcx;
|
2013-05-03 22:07:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
add_clean_return_to_mut(
|
Implement scopes independent of LLVM basic blocks
Currently, scopes are tied to LLVM basic blocks. For each scope, there
are two new basic blocks, which means two extra jumps in the unoptimized
IR. These blocks aren't actually required, but only used to act as the
boundary for cleanups.
By keeping track of the current scope within a single basic block, we
can avoid those extra blocks and jumps, shrinking the pre-optimization
IR quite considerably. For example, the IR for trans_intrinsic goes
from ~22k lines to ~16k lines, almost 30% less.
The impact on the build times of optimized builds is rather small (about
1%), but unoptimized builds are about 11% faster. The testsuite for
unoptimized builds runs between 15% (CPU time) and 7.5% (wallclock time on
my i7) faster.
Also, in some situations this helps LLVM to generate better code by
inlining functions that it previously considered to be too large.
Likely because of the pointless blocks/jumps that were still present at
the time the inlining pass runs.
Refs #7462
2013-07-07 14:53:57 +02:00
|
|
|
cleanup_bcx, root_info.scope, root_key, scratch.val, scratch_bits.val,
|
2013-05-03 22:07:33 -04:00
|
|
|
filename, line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bcx
|
|
|
|
}
|
|
|
|
|
|
|
|
fn perform_write_guard(datum: &Datum,
|
2013-07-17 12:12:08 +02:00
|
|
|
bcx: @mut Block,
|
2013-08-31 18:13:04 +02:00
|
|
|
span: Span) -> @mut Block {
|
2013-09-27 22:38:08 -07:00
|
|
|
debug2!("perform_write_guard");
|
2013-05-03 22:07:33 -04:00
|
|
|
|
|
|
|
let llval = datum.to_value_llval(bcx);
|
|
|
|
let (filename, line) = filename_and_line_num_from_span(bcx, span);
|
|
|
|
|
|
|
|
callee::trans_lang_call(
|
|
|
|
bcx,
|
2013-07-15 20:42:13 -07:00
|
|
|
langcall(bcx, Some(span), "write guard", CheckNotBorrowedFnLangItem),
|
2013-06-16 15:45:48 +12:00
|
|
|
[PointerCast(bcx, llval, Type::i8p()), filename, line],
|
2013-07-08 08:12:01 +02:00
|
|
|
Some(expr::Ignore)).bcx
|
2013-05-03 22:07:33 -04:00
|
|
|
}
|