mozilla/rust — pull request #9297 by eugals (2013-09-18 17:27:58+00:00)
Title: a couple trait method call optimizations

This is my first PR to the project.
It is intended to optimize/beautify the code generated in a few trivial trait operations calls.
Let's take the following code as an example:
```
trait Stuff {
fn bar(&self);
}

fn callBar(s: &Stuff) {
s.bar();
}

struct Foo;

impl Stuff for Foo {
fn bar(&self) {
}
}

pub fn main() {
let o = Foo;
callBar(&o as &Stuff);
}
```

At present it is translated into something like:
```
define void @_ZN7callBar_UUID.0E({ i32, %tydesc*, i8*, i8*, i8 }*, { %tydesc*, i8* }*) #4 {
"function top level":
%__trait_callee = alloca { %tydesc*, i8* }
%__auto_borrow_obj = alloca { %tydesc*, i8* }
%2 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 0
%3 = load %tydesc** %2
%4 = getelementptr inbounds { %tydesc*, i8* }* %__auto_borrow_obj, i32 0, i32 0
store %tydesc* %3, %tydesc** %4
%5 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 1
%6 = load i8** %5
%7 = getelementptr inbounds { %tydesc*, i8* }* %__auto_borrow_obj, i32 0, i32 1
store i8* %6, i8** %7
%8 = bitcast { %tydesc*, i8* }* %__auto_borrow_obj to i8*
%9 = bitcast { %tydesc*, i8* }* %__trait_callee to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %9, i8* %8, i32 8, i32 4, i1 false)
%10 = getelementptr inbounds { %tydesc*, i8* }* %__trait_callee, i32 0, i32 1
%11 = load i8** %10
%12 = bitcast i8* %11 to { i32, %tydesc*, i8*, i8*, i8 }*
%13 = getelementptr inbounds { %tydesc*, i8* }* %__trait_callee, i32 0, i32 0
%14 = bitcast %tydesc** %13 to [1 x i8*]**
%15 = load [1 x i8*]** %14
%16 = getelementptr inbounds [1 x i8*]* %15, i32 0, i32 1
%17 = load i8** %16
%18 = bitcast i8* %17 to void ({ i32, %tydesc*, i8*, i8*, i8 }*)*
call void %18({ i32, %tydesc*, i8*, i8*, i8 }* %12)
ret void
}

...

define void @_ZN4main_UUID.0E({ i32, %tydesc*, i8*, i8*, i8 }*) #4 {
"function top level":
%o = alloca %struct.Foo
%1 = alloca { %tydesc*, i8* }
%__auto_borrow_obj = alloca { %tydesc*, i8* }
%2 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 1
%3 = bitcast i8** %2 to %struct.Foo**
store %struct.Foo* %o, %struct.Foo** %3
%4 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 0
%5 = bitcast %tydesc** %4 to { %tydesc*, void ({ i32, %tydesc*, i8*, i8*, i8 }*)* }**
store { %tydesc*, void ({ i32, %tydesc*, i8*, i8*, i8 }*)* }* @vtable1081, { %tydesc*, void ({ i32, %tydesc*, i8*, i8*, i8 }*)* }** %5
%6 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 0
%7 = load %tydesc** %6
%8 = getelementptr inbounds { %tydesc*, i8* }* %__auto_borrow_obj, i32 0, i32 0
store %tydesc* %7, %tydesc** %8
%9 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 1
%10 = load i8** %9
%11 = getelementptr inbounds { %tydesc*, i8* }* %__auto_borrow_obj, i32 0, i32 1
store i8* %10, i8** %11
call void @_ZN7callBar_UUID.0E({ i32, %tydesc*, i8*, i8*, i8 }* undef, { %tydesc*, i8* }* %__auto_borrow_obj)
ret void
}
```

If you apply my patch, it would become way shorter and cleaner:
```
define void @_ZN7callBar_UUID.0E({ i32, %tydesc*, i8*, i8*, i8 }*, { %tydesc*, i8* }*) #4 {
"function top level":
%2 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 1
%3 = load i8** %2
%4 = bitcast i8* %3 to { i32, %tydesc*, i8*, i8*, i8 }*
%5 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 0
%6 = bitcast %tydesc** %5 to [1 x i8*]**
%7 = load [1 x i8*]** %6
%8 = getelementptr inbounds [1 x i8*]* %7, i32 0, i32 1
%9 = load i8** %8
%10 = bitcast i8* %9 to void ({ i32, %tydesc*, i8*, i8*, i8 }*)*
call void %10({ i32, %tydesc*, i8*, i8*, i8 }* %4)
ret void
}

...

define void @_ZN4main_UUID.0E({ i32, %tydesc*, i8*, i8*, i8 }*) #4 {
"function top level":
%o = alloca %struct.Foo
%1 = alloca { %tydesc*, i8* }
%2 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 1
%3 = bitcast i8** %2 to %struct.Foo**
store %struct.Foo* %o, %struct.Foo** %3
%4 = getelementptr inbounds { %tydesc*, i8* }* %1, i32 0, i32 0
%5 = bitcast %tydesc** %4 to { %tydesc*, void ({ i32, %tydesc*, i8*, i8*, i8 }*)* }**
store { %tydesc*, void ({ i32, %tydesc*, i8*, i8*, i8 }*)* }* @vtable1081, { %tydesc*, void ({ i32, %tydesc*, i8*, i8*, i8 }*)* }** %5
call void @_ZN7callBar_UUID.0E({ i32, %tydesc*, i8*, i8*, i8 }* undef, { %tydesc*, i8* }* %1)
ret void
}
```

Although this chance doesn't increase the compilation speed much (I mentioned only about 1-2% boost on "rustc -O -Z time-passes syntax.rs"), but I still think it's a good thing to do as it greatly simplifies/clarifies LL generated in some cases which would definitely help in the future optimization-related investigations.

I don't provide any new test cases in this patch as it is merely an optimization.
I tried to do "make check" and everything passed well except "run-pass/core-run-destroy.rs", but as far as I can see it doesn't pass on the current branch/master too.