From 5be34fe56f72a4fcf00131d4f19bd5ca2c237386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Sun, 2 Feb 2014 18:30:22 +0100 Subject: [PATCH] Use explicit lifetimes for LLVM allocas Currently, stack allocated variables live for the whole duration of the function they're allocated in. This means that LLVM can't put two allocas into the same stack space, even if they're never used at the same time. By using the LLVM intrinsics to declare the lifetime of allocas, we allow LLVM to use the same stack space for allocas that have non-overlapping lifetimes, reducing stack usage. In certain situations this even allows LLVM to remove duplicated code that previously only differed in which part of the stack it used. --- src/librustc/middle/trans/base.rs | 31 +++++++++++++++++++++++++- src/librustc/middle/trans/cleanup.rs | 21 +++++++++++++++++ src/librustc/middle/trans/intrinsic.rs | 2 ++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 7bd2653f86990..4637e637cae0d 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1084,6 +1084,28 @@ pub fn with_cond<'a>( next_cx } +pub fn call_lifetime_start(cx: &Block, ptr: ValueRef, llty: Type) { + let _icx = push_ctxt("lifetime_start"); + let ccx = cx.ccx(); + + // let llty = type_of::type_of(ccx, t); + let size = machine::llsize_of(ccx, llty); + let lifetime_start = ccx.intrinsics.get_copy(&("llvm.lifetime.start")); + let ptr = PointerCast(cx, ptr, Type::i8p()); + Call(cx, lifetime_start, [size, ptr], []); +} + +pub fn call_lifetime_end(cx: &Block, ptr: ValueRef, t: ty::t) { + let _icx = push_ctxt("lifetime_end"); + let ccx = cx.ccx(); + + let llty = type_of::type_of(ccx, t); + let size = machine::llsize_of(ccx, llty); + let lifetime_end = ccx.intrinsics.get_copy(&("llvm.lifetime.end")); + let ptr = PointerCast(cx, ptr, Type::i8p()); + Call(cx, lifetime_end, [size, ptr], []); +} + pub fn call_memcpy(cx: &Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) { let _icx = push_ctxt("call_memcpy"); let ccx = cx.ccx(); @@ -1171,6 +1193,8 @@ pub fn alloca_maybe_zeroed(cx: &Block, ty: Type, name: &str, zero: bool) -> Valu let b = cx.fcx.ccx.builder(); b.position_before(cx.fcx.alloca_insert_pt.get().unwrap()); memzero(&b, p, ty); + } else { + call_lifetime_start(cx, p, ty); } p } @@ -1183,7 +1207,9 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef { } } debuginfo::clear_source_location(cx.fcx); - return ArrayAlloca(cx, ty, v); + let p = ArrayAlloca(cx, ty, v); + call_lifetime_start(cx, p, ty); + p } pub struct BasicBlocks { @@ -2420,6 +2446,9 @@ pub fn declare_intrinsics(llmod: ModuleRef) -> HashMap<&'static str, ValueRef> { ifn!(intrinsics, "llvm.expect.i1", [Type::i1(), Type::i1()], Type::i1()); + ifn!(intrinsics, "llvm.lifetime.start", [Type::i64(), i8p], Type::void()); + ifn!(intrinsics, "llvm.lifetime.end", [Type::i64(), i8p], Type::void()); + return intrinsics; } diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs index acbbdf27d1c26..90cb72c9265d8 100644 --- a/src/librustc/middle/trans/cleanup.rs +++ b/src/librustc/middle/trans/cleanup.rs @@ -244,6 +244,11 @@ impl<'a> CleanupMethods<'a> for FunctionContext<'a> { * instance of `ty` */ + self.schedule_clean(cleanup_scope, ~LifetimeEnd { + val: val, + ty: ty + }); + if !ty::type_needs_drop(self.ccx.tcx, ty) { return; } let drop = ~DropValue { is_immediate: false, @@ -831,6 +836,22 @@ impl Cleanup for DropValue { } } +pub struct LifetimeEnd { + val: ValueRef, + ty: ty::t, +} + +impl Cleanup for LifetimeEnd { + fn clean_on_unwind(&self) -> bool { + false + } + + fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> { + base::call_lifetime_end(bcx, self.val, self.ty); + bcx + } +} + pub struct FreeValue { ptr: ValueRef, heap: common::heap, diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index 49f1b073f873e..3c7e91637a861 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -80,6 +80,8 @@ pub fn get_simple_intrinsic(ccx: @CrateContext, item: &ast::ForeignItem) -> Opti "bswap16" => Some(ccx.intrinsics.get_copy(&("llvm.bswap.i16"))), "bswap32" => Some(ccx.intrinsics.get_copy(&("llvm.bswap.i32"))), "bswap64" => Some(ccx.intrinsics.get_copy(&("llvm.bswap.i64"))), + "lifetime_start" => Some(ccx.intrinsics.get_copy(&("llvm.lifetime.start"))), + "lifetime_end" => Some(ccx.intrinsics.get_copy(&("llvm.lifetime.end"))), _ => None } }