Part 1 of well-defined copy-elision (second attempt)#1682
Part 1 of well-defined copy-elision (second attempt)#1682
Conversation
```zig
export fn entry() void {
var x: i32 = undefined;
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca i32, align 4
%0 = bitcast i32* %x to i8*, !dbg !48
call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 -86, i64 4, i1 false), !dbg !48
call void @llvm.dbg.declare(metadata i32* %x, metadata !45, metadata !DIExpression()), !dbg !48
ret void, !dbg !49
}
```
0ceab56 to
1dfca20
Compare
```zig
export fn entry() void {
var x = i32(1234);
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca i32, align 4
store i32 1234, i32* %x, align 4, !dbg !48
%0 = bitcast i32* %x to i8*, !dbg !48
call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 -86, i64 4, i1 false), !dbg !48
call void @llvm.dbg.declare(metadata i32* %x, metadata !45, metadata !DIExpression()), !dbg !48
ret void, !dbg !49
}
```
```zig
export fn entry() void {
var x = Foo.{
.x = 1,
.y = 2,
};
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca %Foo, align 4
%0 = bitcast %Foo* %x to i8*, !dbg !52
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%Foo* @0 to i8*), i64 8, i1 false), !dbg !52
call void @llvm.dbg.declare(metadata %Foo* %x, metadata !45, metadata !DIExpression()), !dbg !52
ret void, !dbg !53
}
```
```zig
export fn entry() void {
var y = foo();
}
fn foo() Foo {
return Foo.{
.x = 1,
.y = 2,
};
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%y = alloca %Foo, align 4
call fastcc void @foo(%Foo* sret %y), !dbg !52
call void @llvm.dbg.declare(metadata %Foo* %y, metadata !45, metadata !DIExpression()), !dbg !53
ret void, !dbg !54
}
define internal fastcc void @foo(%Foo* nonnull sret) unnamed_addr #2 !dbg !55 {
Entry:
%1 = bitcast %Foo* %0 to i8*, !dbg !60
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 bitcast (%Foo* @0 to i8*), i64 8, i1 false), !dbg !60
ret void, !dbg !60
}
```
```zig
export fn entry() void {
var x = bar();
var y = x;
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca %Foo, align 4
%y = alloca %Foo, align 4
call fastcc void @bar(%Foo* sret %x), !dbg !54
call void @llvm.dbg.declare(metadata %Foo* %x, metadata !45, metadata !DIExpression()), !dbg !55
%0 = bitcast %Foo* %x to i8*, !dbg !56
%1 = bitcast %Foo* %y to i8*, !dbg !56
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 %0, i64 8, i1 false), !dbg !56
call void @llvm.dbg.declare(metadata %Foo* %y, metadata !52, metadata !DIExpression()), !dbg !57
ret void, !dbg !58
}
```
```zig
export fn entry() void {
var x: ?Foo = foo();
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca { %Foo, i1 }, align 4
%0 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !57
store i1 true, i1* %0, align 1, !dbg !57
%1 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !57
call fastcc void @foo(%Foo* sret %1), !dbg !58
call void @llvm.dbg.declare(metadata { %Foo, i1 }* %x, metadata !45, metadata !DIExpression()), !dbg !57
ret void, !dbg !59
}
```
```zig
export fn entry() void {
var a: i32 = 1234;
var b: ?i32 = a;
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%a = alloca i32, align 4
%b = alloca { i32, i1 }, align 4
store i32 1234, i32* %a, align 4, !dbg !55
call void @llvm.dbg.declare(metadata i32* %a, metadata !45, metadata !DIExpression()), !dbg !55
%0 = load i32, i32* %a, align 4, !dbg !56
%1 = getelementptr inbounds { i32, i1 }, { i32, i1 }* %b, i32 0, i32 1, !dbg !57
store i1 true, i1* %1, align 1, !dbg !57
%2 = getelementptr inbounds { i32, i1 }, { i32, i1 }* %b, i32 0, i32 0, !dbg !57
store i32 %0, i32* %2, align 4, !dbg !57
call void @llvm.dbg.declare(metadata { i32, i1 }* %b, metadata !48, metadata !DIExpression()), !dbg !57
ret void, !dbg !58
}
```
```zig
export fn entry() void {
var y = true;
var z = bar();
var x = if (y) foo() else z;
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%y = alloca i1, align 1
%z = alloca %Foo, align 4
%x = alloca %Foo, align 4
store i1 true, i1* %y, align 1, !dbg !57
call void @llvm.dbg.declare(metadata i1* %y, metadata !45, metadata !DIExpression()), !dbg !58
call fastcc void @bar(%Foo* sret %z), !dbg !59
call void @llvm.dbg.declare(metadata %Foo* %z, metadata !48, metadata !DIExpression()), !dbg !60
%0 = load i1, i1* %y, align 1, !dbg !61
br i1 %0, label %Then, label %Else, !dbg !61
Then: ; preds = %Entry
call fastcc void @foo(%Foo* sret %x), !dbg !62
br label %EndIf, !dbg !63
Else: ; preds = %Entry
%1 = bitcast %Foo* %z to i8*, !dbg !64
%2 = bitcast %Foo* %x to i8*, !dbg !64
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %2, i8* align 4 %1, i64 8, i1 false), !dbg !64
br label %EndIf, !dbg !63
EndIf: ; preds = %Else, %Then
call void @llvm.dbg.declare(metadata %Foo* %x, metadata !55, metadata !DIExpression()), !dbg !65
ret void, !dbg !66
}
```
```zig
export fn entry() void {
var x: error!Foo = foo();
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca { i16, %Foo }, align 4
%0 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 0, !dbg !56
store i16 0, i16* %0, align 2, !dbg !56
%1 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 1, !dbg !56
call fastcc void @foo(%Foo* sret %1), !dbg !57
call void @llvm.dbg.declare(metadata { i16, %Foo }* %x, metadata !45, metadata !DIExpression()), !dbg !56
ret void, !dbg !58
}
```
```zig
export fn entry() void {
var x: error!Foo = fail();
}
```
```llvm
define void @entry() #2 !dbg !42 {
Entry:
%x = alloca { i16, %Foo }, align 4
%0 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 0, !dbg !57
%1 = call fastcc i16 @fail(), !dbg !58
store i16 %1, i16* %0, align 2, !dbg !58
ret void, !dbg !59
}
```
```zig
export fn entry() void {
var x: error!?Foo = bar();
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca { i16, { %Foo, i1 } }, align 4
%0 = getelementptr inbounds { i16, { %Foo, i1 } }, { i16, { %Foo, i1 } }* %x, i32 0, i32 0, !dbg !61
store i16 0, i16* %0, align 2, !dbg !61
%1 = getelementptr inbounds { i16, { %Foo, i1 } }, { i16, { %Foo, i1 } }* %x, i32 0, i32 1, !dbg !61
%2 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %1, i32 0, i32 1, !dbg !61
store i1 true, i1* %2, align 1, !dbg !61
%3 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %1, i32 0, i32 0, !dbg !61
call fastcc void @bar(%Foo* sret %3), !dbg !62
ret void, !dbg !63
}
```
```zig
export fn entry() void {
var x = Foo.{
.x = 1,
.y = bar(),
};
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca %Foo, align 4
%0 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 0, !dbg !56
store i32 1, i32* %0, align 4, !dbg !57
%1 = getelementptr inbounds %Foo, %Foo* %x, i32 0, i32 1, !dbg !58
call fastcc void @bar(%Bar* sret %1), !dbg !59
ret void, !dbg !60
}
```
```zig
export fn entry() void {
var x: error!Bar = fail();
var z = x catch unreachable;
}
```
```llvm
define void @entry() #2 !dbg !42 {
Entry:
%error_return_trace_addresses = alloca [30 x i64], align 8
%error_return_trace = alloca %StackTrace, align 8
%x = alloca { i16, %Bar }, align 4
%z = alloca %Bar, align 4
%0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0
store i64 0, i64* %0, align 8
%1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1
%2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0
%3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0
store i64* %3, i64** %2, align 8
%4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1
store i64 30, i64* %4, align 8
%5 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !59
%6 = call fastcc i16 @fail(%StackTrace* %error_return_trace), !dbg !60
store i16 %6, i16* %5, align 2, !dbg !60
call void @llvm.dbg.declare(metadata { i16, %Bar }* %x, metadata !46, metadata !DIExpression()), !dbg !59
%7 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 1, !dbg !61
%8 = bitcast %Bar* %7 to i8*, !dbg !61
%9 = bitcast %Bar* %z to i8*, !dbg !61
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %9, i8* align 4 %8, i64 8, i1 false), !dbg !61
%10 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !61
%11 = load i16, i16* %10, align 2, !dbg !61
%12 = icmp eq i16 %11, 0, !dbg !62
br i1 %12, label %UnwrapErrOk, label %UnwrapErrError, !dbg !62
UnwrapErrError: ; preds = %Entry
tail call fastcc void @__zig_fail_unwrap(%StackTrace* %error_return_trace, i16 %11), !dbg !62
unreachable, !dbg !62
UnwrapErrOk: ; preds = %Entry
call void @llvm.dbg.declare(metadata %Bar* %z, metadata !57, metadata !DIExpression()), !dbg !63
ret void, !dbg !64
}
```
This commit also makes `@sizeOf(?error) == @sizeof(error)`. Since 0 is not a valid error code, 0 can be used for the null value, in the same way that 0 is used for the null value of pointers. ```zig export fn entry() void { var x: error!Bar = fail(); var y = x catch bar2(); } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %x = alloca { i16, %Bar }, align 4 %y = alloca %Bar, align 4 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !59 %6 = call fastcc i16 @fail(%StackTrace* %error_return_trace), !dbg !60 store i16 %6, i16* %5, align 2, !dbg !60 call void @llvm.dbg.declare(metadata { i16, %Bar }* %x, metadata !46, metadata !DIExpression()), !dbg !59 %7 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 1, !dbg !61 %8 = bitcast %Bar* %7 to i8*, !dbg !61 %9 = bitcast %Bar* %y to i8*, !dbg !61 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %9, i8* align 4 %8, i64 8, i1 false), !dbg !61 %10 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !61 %11 = load i16, i16* %10, align 2, !dbg !62 %12 = icmp ne i16 %11, 0, !dbg !62 br i1 %12, label %CatchError, label %CatchEnd, !dbg !62 CatchError: ; preds = %Entry call fastcc void @bar2(%Bar* sret %y), !dbg !63 br label %CatchEnd, !dbg !62 CatchEnd: ; preds = %CatchError, %Entry call void @llvm.dbg.declare(metadata %Bar* %y, metadata !57, metadata !DIExpression()), !dbg !64 ret void, !dbg !65 } ```
```zig
export fn entry() void {
var c = true;
var y: i32 = if (c) 1234 else 5678;
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%c = alloca i1, align 1
%y = alloca i32, align 4
store i1 true, i1* %c, align 1, !dbg !51
call void @llvm.dbg.declare(metadata i1* %c, metadata !45, metadata !DIExpression()), !dbg !52
%0 = load i1, i1* %c, align 1, !dbg !53
br i1 %0, label %Then, label %Else, !dbg !53
Then: ; preds = %Entry
store i32 1234, i32* %y, align 4, !dbg !54
br label %EndIf, !dbg !55
Else: ; preds = %Entry
store i32 5678, i32* %y, align 4, !dbg !56
br label %EndIf, !dbg !55
EndIf: ; preds = %Else, %Then
call void @llvm.dbg.declare(metadata i32* %y, metadata !48, metadata !DIExpression()), !dbg !57
ret void, !dbg !58
}
```
```zig
export fn entry() void {
var x: ?Foo = foo();
var y = if (x) |a| a.y else bar();
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca { %Foo, i1 }, align 4
%y = alloca %Bar, align 4
%0 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !64
store i1 true, i1* %0, align 1, !dbg !64
%1 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !64
call fastcc void @foo(%Foo* sret %1), !dbg !65
call void @llvm.dbg.declare(metadata { %Foo, i1 }* %x, metadata !45, metadata !DIExpression()), !dbg !64
%2 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !66
%3 = load i1, i1* %2, align 1, !dbg !66
br i1 %3, label %OptionalThen, label %OptionalElse, !dbg !66
OptionalThen: ; preds = %Entry
%4 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !66
call void @llvm.dbg.declare(metadata %Foo* %4, metadata !61, metadata !DIExpression()), !dbg !66
%5 = getelementptr inbounds %Foo, %Foo* %4, i32 0, i32 1, !dbg !67
%6 = bitcast %Bar* %5 to i8*, !dbg !67
%7 = bitcast %Bar* %y to i8*, !dbg !67
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %7, i8* align 4 %6, i64 8, i1 false), !dbg !67
br label %OptionalEndIf, !dbg !66
OptionalElse: ; preds = %Entry
call fastcc void @bar(%Bar* sret %y), !dbg !69
br label %OptionalEndIf, !dbg !66
OptionalEndIf: ; preds = %OptionalElse, %OptionalThen
call void @llvm.dbg.declare(metadata %Bar* %y, metadata !63, metadata !DIExpression()), !dbg !70
ret void, !dbg !71
}
```
```zig
export fn entry() void {
var x: error!Foo = foo();
var y = if (x) |a| a.y else |e| bar();
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca { i16, %Foo }, align 4
%y = alloca %Bar, align 4
%0 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 0, !dbg !64
store i16 0, i16* %0, align 2, !dbg !64
%1 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 1, !dbg !64
call fastcc void @foo(%Foo* sret %1), !dbg !65
call void @llvm.dbg.declare(metadata { i16, %Foo }* %x, metadata !45, metadata !DIExpression()), !dbg !64
%2 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 0, !dbg !66
%3 = load i16, i16* %2, align 2, !dbg !66
%4 = icmp ne i16 %3, 0, !dbg !66
br i1 %4, label %IfErrElse, label %IfErrThen, !dbg !66
IfErrThen: ; preds = %Entry
%5 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %x, i32 0, i32 1, !dbg !66
call void @llvm.dbg.declare(metadata %Foo* %5, metadata !60, metadata !DIExpression()), !dbg !66
%6 = getelementptr inbounds %Foo, %Foo* %5, i32 0, i32 1, !dbg !67
%7 = bitcast %Bar* %6 to i8*, !dbg !67
%8 = bitcast %Bar* %y to i8*, !dbg !67
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %8, i8* align 4 %7, i64 8, i1 false), !dbg !67
br label %IfErrEnd, !dbg !66
IfErrElse: ; preds = %Entry
%9 = load i16, i16* %2, align 2, !dbg !66
call void @llvm.dbg.declare(metadata i16* %2, metadata !62, metadata !DIExpression()), !dbg !66
call fastcc void @bar(%Bar* sret %y), !dbg !69
br label %IfErrEnd, !dbg !66
IfErrEnd: ; preds = %IfErrElse, %IfErrThen
call void @llvm.dbg.declare(metadata %Bar* %y, metadata !63, metadata !DIExpression()), !dbg !71
ret void, !dbg !72
}
```
```zig
export fn entry() void {
var x = error.Failure;
var y: error!Foo = x;
}
```
```llvm
define void @entry() #2 !dbg !42 {
Entry:
%x = alloca i16, align 2
%y = alloca { i16, %Foo }, align 4
store i16 1, i16* %x, align 2, !dbg !63
call void @llvm.dbg.declare(metadata i16* %x, metadata !46, metadata !DIExpression()), !dbg !64
%0 = load i16, i16* %x, align 2, !dbg !65
%1 = getelementptr inbounds { i16, %Foo }, { i16, %Foo }* %y, i32 0, i32 0, !dbg !66
store i16 %0, i16* %1, align 2, !dbg !65
call void @llvm.dbg.declare(metadata { i16, %Foo }* %y, metadata !48, metadata !DIExpression()), !dbg !66
ret void, !dbg !67
}
```
```zig
export fn entry() void {
var x: error!i32 = 1234;
var y = if (x) |a| a else |e| 5678;
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%x = alloca { i16, i32 }, align 4
%y = alloca i32, align 4
%0 = bitcast { i16, i32 }* %x to i8*, !dbg !56
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast ({ i16, i32 }* @0 to i8*), i64 8, i1 false), !dbg !56
call void @llvm.dbg.declare(metadata { i16, i32 }* %x, metadata !45, metadata !DIExpression()), !dbg !57
%1 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %x, i32 0, i32 0, !dbg !58
%2 = load i16, i16* %1, align 2, !dbg !58
%3 = icmp ne i16 %2, 0, !dbg !58
br i1 %3, label %IfErrElse, label %IfErrThen, !dbg !58
IfErrThen: ; preds = %Entry
%4 = getelementptr inbounds { i16, i32 }, { i16, i32 }* %x, i32 0, i32 1, !dbg !58
call void @llvm.dbg.declare(metadata i32* %4, metadata !52, metadata !DIExpression()), !dbg !58
%5 = load i32, i32* %4, align 4, !dbg !59
store i32 %5, i32* %y, align 4, !dbg !59
br label %IfErrEnd, !dbg !58
IfErrElse: ; preds = %Entry
%6 = load i16, i16* %1, align 2, !dbg !58
call void @llvm.dbg.declare(metadata i16* %1, metadata !54, metadata !DIExpression()), !dbg !58
store i32 5678, i32* %y, align 4, !dbg !61
br label %IfErrEnd, !dbg !58
IfErrEnd: ; preds = %IfErrElse, %IfErrThen
call void @llvm.dbg.declare(metadata i32* %y, metadata !55, metadata !DIExpression()), !dbg !63
ret void, !dbg !64
}
```
```zig
export fn entry() void {
var c = false;
var x: ?Foo = foo();
var z = while (x) |a| {
_ = if (c) break a.y;
} else bar();
}
```
```llvm
define void @entry() #2 !dbg !41 {
Entry:
%c = alloca i1, align 1
%x = alloca { %Foo, i1 }, align 4
%z = alloca %Bar, align 4
store i1 false, i1* %c, align 1, !dbg !66
call void @llvm.dbg.declare(metadata i1* %c, metadata !45, metadata !DIExpression()), !dbg !67
%0 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !68
store i1 true, i1* %0, align 1, !dbg !68
%1 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !68
call fastcc void @foo(%Foo* sret %1), !dbg !69
call void @llvm.dbg.declare(metadata { %Foo, i1 }* %x, metadata !48, metadata !DIExpression()), !dbg !68
br label %WhileCond, !dbg !70
WhileCond: ; preds = %Else, %Entry
%2 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 1, !dbg !71
%3 = load i1, i1* %2, align 1, !dbg !71
br i1 %3, label %WhileBody, label %WhileElse, !dbg !71
WhileBody: ; preds = %WhileCond
%4 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %x, i32 0, i32 0, !dbg !72
call void @llvm.dbg.declare(metadata %Foo* %4, metadata !63, metadata !DIExpression()), !dbg !70
%5 = load i1, i1* %c, align 1, !dbg !74
br i1 %5, label %Then, label %Else, !dbg !74
Then: ; preds = %WhileBody
%6 = getelementptr inbounds %Foo, %Foo* %4, i32 0, i32 1, !dbg !76
%7 = bitcast %Bar* %6 to i8*, !dbg !76
%8 = bitcast %Bar* %z to i8*, !dbg !76
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %8, i8* align 4 %7, i64 8, i1 false), !dbg !76
br label %WhileEnd, !dbg !77
Else: ; preds = %WhileBody
br label %WhileCond, !dbg !72
WhileElse: ; preds = %WhileCond
call fastcc void @bar(%Bar* sret %z), !dbg !78
br label %WhileEnd, !dbg !70
WhileEnd: ; preds = %WhileElse, %Then
call void @llvm.dbg.declare(metadata %Bar* %z, metadata !65, metadata !DIExpression()), !dbg !79
ret void, !dbg !80
}
```
All behavior tests are now passing in the copy elision branch.
...error union function call
also restore default panic handler code
...const slice cast in array literal
...pointer return value I had to put the panic handler stub back in since some code was being incorrectly comptime eliminated.
|
This branch also started going in a design direction and with a code quality that I was not comfortable with. I have backported all the extra stuff from this branch back into master in 581edd6 and when tackling copy elision I will try a third approach. The third approach involves breaking IR pass 2 (the analysis pass) into sub-phases:
With this framework in place, in IR pass 1, result location instructions will be generated to track result locations throughout expressions. However unlike in this pull request, they will be lazy. Pass 1 will only create the tree structure of result locations, correlated to the corresponding instructions. In the dual phased Pass 2, result locations will be resolved with full type information, which is a key difference. The separate is_comptime computation helps when there are "peer" expressions, for example an if expression or a switch expression. Each "prong" has the is_comptime value computed, and if all of them are comptime-known, then all of them are analyzed as comptime. Otherwise all of them are analyzed as runtime. This will also fix this particularly tricky test case, as mentioned in #1682 (comment) export fn entry(cond: bool, a: i8, b: i32) void {
const x = if (cond) a else b;
}So the plan is to close this branch, and then branch again from master. However, I think there are some important things to work on before embarking on a third copy elision attempt, such as merging the outstanding pull requests, and some other features to get into 0.4.0 besides copy elision. Master branch is open for business. |
Not satisfied with how the code was looking in #1652, I'm tossing out that work and starting over. This branch uses the IR system directly to implement copy elision, instead of awkwardly patching over it.
The goals of this branch are the same:
var foo = async bar().This does not solve the problem of how to access the result location of a function, (e.g. #287 (comment)). That is left for a future change.
Note that clang already generates code like this. This PR is actually Zig catching up with Clang in this particular area.
Checklist:
*[N]Tto[]T@sliceToBytesorelsecatchwith identifier expressioncatchwith fn call expressioncatchwith other expression which requires stack allocacatchaccess of the error code.?trycatch unreachablewith identifier expressioncatch unreachablewith fn call expressioncatch unreachablewith other expression which requires stack alloca@noInlineCall/@inlineCallparametersir_lval_wrap@sizeOf(?error)sret? should the error value / null value be a pointer argument?See each commit message in this branch for example Zig/LLVM code.
Some interesting tidbits:
a catch bis no longer syntactic sugar forif (a) |x| x else |_| b. The former provides copy elision; the latter does not. Theifexpression creates a result location fora, whereas thecatchexpression does not create a result location.try ais no longer syntactic sugar forif (a) |x| x else |e| return e. Same explanation.a orelse bis no longer syntactic sugar forif (a) |x| x else b. However,a.?is still syntactic sugar fora orelse unreachable.