Skip to content

Commit e70aac5

Browse files
committed
auto merge of #8270 : dotdash/rust/ret_alloca_elim, r=pcwalton
When there is only a single store to the ret slot that dominates the load that gets the value for the "ret" instruction, we can elide the ret slot and directly return the operand of the dominating store instruction. This is the same thing that clang does, except for a special case that doesn't seem to affect us. Fixes #8238
2 parents f0fc9c9 + 4b74b8d commit e70aac5

File tree

7 files changed

+255
-4
lines changed

7 files changed

+255
-4
lines changed

src/librustc/lib/llvm.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,8 @@ pub mod llvm {
15531553
/* Selected entries from the downcasts. */
15541554
#[fast_ffi]
15551555
pub fn LLVMIsATerminatorInst(Inst: ValueRef) -> ValueRef;
1556+
#[fast_ffi]
1557+
pub fn LLVMIsAStoreInst(Inst: ValueRef) -> ValueRef;
15561558

15571559
/** Writes a module to the specified path. Returns 0 on success. */
15581560
#[fast_ffi]

src/librustc/middle/trans/base.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use middle::trans::monomorphize;
5959
use middle::trans::tvec;
6060
use middle::trans::type_of;
6161
use middle::trans::type_of::*;
62+
use middle::trans::value::Value;
6263
use middle::ty;
6364
use util::common::indenter;
6465
use util::ppaux::{Repr, ty_to_str};
@@ -1792,11 +1793,30 @@ pub fn finish_fn(fcx: @mut FunctionContext, last_bcx: @mut Block) {
17921793
// Builds the return block for a function.
17931794
pub fn build_return_block(fcx: &FunctionContext, ret_cx: @mut Block) {
17941795
// Return the value if this function immediate; otherwise, return void.
1795-
if fcx.llretptr.is_some() && fcx.has_immediate_return_value {
1796-
Ret(ret_cx, Load(ret_cx, fcx.llretptr.unwrap()))
1797-
} else {
1798-
RetVoid(ret_cx)
1796+
if fcx.llretptr.is_none() || !fcx.has_immediate_return_value {
1797+
return RetVoid(ret_cx);
17991798
}
1799+
1800+
let retptr = Value(fcx.llretptr.unwrap());
1801+
let retval = match retptr.get_dominating_store(ret_cx) {
1802+
// If there's only a single store to the ret slot, we can directly return
1803+
// the value that was stored and omit the store and the alloca
1804+
Some(s) => {
1805+
let retval = *s.get_operand(0).unwrap();
1806+
s.erase_from_parent();
1807+
1808+
if retptr.has_no_uses() {
1809+
retptr.erase_from_parent();
1810+
}
1811+
1812+
retval
1813+
}
1814+
// Otherwise, load the return value from the ret slot
1815+
None => Load(ret_cx, fcx.llretptr.unwrap())
1816+
};
1817+
1818+
1819+
Ret(ret_cx, retval);
18001820
}
18011821

18021822
pub enum self_arg { impl_self(ty::t, ty::SelfMode), no_self, }
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use lib::llvm::{llvm, BasicBlockRef};
12+
use middle::trans::value::{UserIterator, Value};
13+
use std::iterator::{Filter, Map};
14+
15+
pub struct BasicBlock(BasicBlockRef);
16+
17+
pub type PredIterator<'self> = Map<'self, Value, BasicBlock, Filter<'self, Value, UserIterator>>;
18+
19+
/**
20+
* Wrapper for LLVM BasicBlockRef
21+
*/
22+
impl BasicBlock {
23+
pub fn as_value(self) -> Value {
24+
unsafe {
25+
Value(llvm::LLVMBasicBlockAsValue(*self))
26+
}
27+
}
28+
29+
pub fn pred_iter(self) -> PredIterator {
30+
self.as_value().user_iter()
31+
.filter(|user| user.is_a_terminator_inst())
32+
.transform(|user| user.get_parent().unwrap())
33+
}
34+
35+
pub fn get_single_predecessor(self) -> Option<BasicBlock> {
36+
let mut iter = self.pred_iter();
37+
match (iter.next(), iter.next()) {
38+
(Some(first), None) => Some(first),
39+
_ => None
40+
}
41+
}
42+
}

src/librustc/middle/trans/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@ pub mod machine;
4242
pub mod adt;
4343
pub mod asm;
4444
pub mod type_;
45+
pub mod value;
46+
pub mod basic_block;

src/librustc/middle/trans/value.rs

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use lib::llvm::{llvm, UseRef, ValueRef};
12+
use middle::trans::basic_block::BasicBlock;
13+
use middle::trans::common::Block;
14+
use std::libc::c_uint;
15+
16+
pub struct Value(ValueRef);
17+
18+
macro_rules! opt_val ( ($e:expr) => (
19+
unsafe {
20+
match $e {
21+
p if p.is_not_null() => Some(Value(p)),
22+
_ => None
23+
}
24+
}
25+
))
26+
27+
/**
28+
* Wrapper for LLVM ValueRef
29+
*/
30+
impl Value {
31+
/// Returns the BasicBlock that contains this value
32+
pub fn get_parent(self) -> Option<BasicBlock> {
33+
unsafe {
34+
match llvm::LLVMGetInstructionParent(*self) {
35+
p if p.is_not_null() => Some(BasicBlock(p)),
36+
_ => None
37+
}
38+
}
39+
}
40+
41+
/// Removes this value from its containing BasicBlock
42+
pub fn erase_from_parent(self) {
43+
unsafe {
44+
llvm::LLVMInstructionEraseFromParent(*self);
45+
}
46+
}
47+
48+
/// Returns the single dominating store to this value, if any
49+
/// This only performs a search for a trivially dominating store. The store
50+
/// must be the only user of this value, and there must not be any conditional
51+
/// branches between the store and the given block.
52+
pub fn get_dominating_store(self, bcx: @mut Block) -> Option<Value> {
53+
match self.get_single_user().chain(|user| user.as_store_inst()) {
54+
Some(store) => {
55+
do store.get_parent().chain |store_bb| {
56+
let mut bb = BasicBlock(bcx.llbb);
57+
let mut ret = Some(store);
58+
while *bb != *store_bb {
59+
match bb.get_single_predecessor() {
60+
Some(pred) => bb = pred,
61+
None => { ret = None; break }
62+
}
63+
}
64+
ret
65+
}
66+
}
67+
_ => None
68+
}
69+
}
70+
71+
/// Returns the first use of this value, if any
72+
pub fn get_first_use(self) -> Option<Use> {
73+
unsafe {
74+
match llvm::LLVMGetFirstUse(*self) {
75+
u if u.is_not_null() => Some(Use(u)),
76+
_ => None
77+
}
78+
}
79+
}
80+
81+
/// Tests if there are no uses of this value
82+
pub fn has_no_uses(self) -> bool {
83+
self.get_first_use().is_none()
84+
}
85+
86+
/// Returns the single user of this value
87+
/// If there are no users or multiple users, this returns None
88+
pub fn get_single_user(self) -> Option<Value> {
89+
let mut iter = self.user_iter();
90+
match (iter.next(), iter.next()) {
91+
(Some(first), None) => Some(first),
92+
_ => None
93+
}
94+
}
95+
96+
/// Returns an iterator for the users of this value
97+
pub fn user_iter(self) -> UserIterator {
98+
UserIterator {
99+
next: self.get_first_use()
100+
}
101+
}
102+
103+
/// Returns the requested operand of this instruction
104+
/// Returns None, if there's no operand at the given index
105+
pub fn get_operand(self, i: uint) -> Option<Value> {
106+
opt_val!(llvm::LLVMGetOperand(*self, i as c_uint))
107+
}
108+
109+
/// Returns the Store represent by this value, if any
110+
pub fn as_store_inst(self) -> Option<Value> {
111+
opt_val!(llvm::LLVMIsAStoreInst(*self))
112+
}
113+
114+
/// Tests if this value is a terminator instruction
115+
pub fn is_a_terminator_inst(self) -> bool {
116+
unsafe {
117+
llvm::LLVMIsATerminatorInst(*self).is_not_null()
118+
}
119+
}
120+
}
121+
122+
pub struct Use(UseRef);
123+
124+
/**
125+
* Wrapper for LLVM UseRef
126+
*/
127+
impl Use {
128+
pub fn get_user(self) -> Value {
129+
unsafe {
130+
Value(llvm::LLVMGetUser(*self))
131+
}
132+
}
133+
134+
pub fn get_next_use(self) -> Option<Use> {
135+
unsafe {
136+
match llvm::LLVMGetNextUse(*self) {
137+
u if u.is_not_null() => Some(Use(u)),
138+
_ => None
139+
}
140+
}
141+
}
142+
}
143+
144+
/// Iterator for the users of a value
145+
pub struct UserIterator {
146+
priv next: Option<Use>
147+
}
148+
149+
impl Iterator<Value> for UserIterator {
150+
fn next(&mut self) -> Option<Value> {
151+
let current = self.next;
152+
153+
self.next = do current.chain |u| { u.get_next_use() };
154+
155+
do current.map |u| { u.get_user() }
156+
}
157+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
extern "C"
12+
int test() {
13+
return 5;
14+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[no_mangle]
12+
fn test() -> int {
13+
5
14+
}

0 commit comments

Comments
 (0)