Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Part 1 of well-defined copy elision #1652

Closed
wants to merge 18 commits into from
Closed

Part 1 of well-defined copy elision #1652

wants to merge 18 commits into from

Conversation

andrewrk
Copy link
Member

@andrewrk andrewrk commented Oct 11, 2018

This does the following things:

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:

  • variable declaration
  • variable declaration with undefined
  • function returning result of another function
  • assignment of function call
  • assignment of undefined
  • implicit cast optional wrap
  • implicit cast optional wrap when payload is integer
  • return const struct
  • implicit cast payload to error union
  • implicit cast error code to error union
  • double implicit cast
  • implicit *[N]T to []T
  • assignment of non function call
  • double implicit cast when payload is integer, fn call
  • implicit T to *const T (related: remove implicit cast from T to *const T #1465)
  • implicit array to slice
  • double implicit cast when payload is integer, no fn call
  • runtime union init
  • runtime struct init
  • runtime array init
  • fn call in parameter expressions with implicit casting to parameters
  • cmpxchg
  • @sliceToBytes
  • slicing syntax
  • write a new kind of test that looks at the LLVM IR output and add cases for all the above
  • fix test regressions

Here are some examples:

Variable Declaration with Function Call

export fn entry() void {
    var x = foo();
}

before this change

define void @entry() #2 !dbg !41 {
Entry:
  %0 = alloca %Foo, align 4
  %x = alloca %Foo, align 4
  call fastcc void @foo(%Foo* sret %0), !dbg !52
  %1 = bitcast %Foo* %0 to i8*, !dbg !53
  %2 = bitcast %Foo* %x to i8*, !dbg !53
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %2, i8* align 4 %1, i64 8, i1 false), !dbg !53
  call void @llvm.dbg.declare(metadata %Foo* %x, metadata !45, metadata !DIExpression()), !dbg !53
  ret void, !dbg !54
}

after this change

define void @entry() #2 !dbg !41 {
Entry:
  %x = alloca %Foo, align 4
  call fastcc void @foo(%Foo* sret %x), !dbg !52
  call void @llvm.dbg.declare(metadata %Foo* %x, metadata !45, metadata !DIExpression()), !dbg !53
  ret void, !dbg !54
}

Variable Declaration with Function Call + Implicit Cast

export fn entry() void {
    var x: ?Foo = foo();
}

before this change

define void @entry() #2 !dbg !41 {
Entry:
  %0 = alloca %Foo, align 4
  %1 = alloca { %Foo, i1 }, align 4
  %x = alloca { %Foo, i1 }, align 4
  call fastcc void @foo(%Foo* sret %0), !dbg !57
  %2 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %1, i32 0, i32 0, !dbg !57
  %3 = bitcast %Foo* %0 to i8*, !dbg !57
  %4 = bitcast %Foo* %2 to i8*, !dbg !57
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %4, i8* align 4 %3, i64 8, i1 false), !dbg !57
  %5 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %1, i32 0, i32 1, !dbg !57
  store i1 true, i1* %5, align 1, !dbg !57
  %6 = bitcast { %Foo, i1 }* %1 to i8*, !dbg !58
  %7 = bitcast { %Foo, i1 }* %x to i8*, !dbg !58
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %7, i8* align 4 %6, i64 12, i1 false), !dbg !58
  call void @llvm.dbg.declare(metadata { %Foo, i1 }* %x, metadata !45, metadata !DIExpression()), !dbg !58
  ret void, !dbg !59
}

after this change

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 !57
  call void @llvm.dbg.declare(metadata { %Foo, i1 }* %x, metadata !45, metadata !DIExpression()), !dbg !58
  ret void, !dbg !59
}

Function Forwarding

fn foo() Foo {
    return bar();
}

before this change

define internal fastcc void @foo(%Foo* nonnull sret) unnamed_addr #2 !dbg !55 {
Entry:
  %1 = alloca %Foo, align 4
  call fastcc void @bar(%Foo* sret %1), !dbg !60
  %2 = bitcast %Foo* %1 to i8*, !dbg !62
  %3 = bitcast %Foo* %0 to i8*, !dbg !62
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %3, i8* align 4 %2, i64 8, i1 false), !dbg !62
  ret void, !dbg !62
}

after this change

define internal fastcc void @foo(%Foo* nonnull sret) unnamed_addr #2 !dbg !55 {
Entry:
  call fastcc void @bar(%Foo* sret %0), !dbg !60
  ret void, !dbg !62
}

Double Implicit Cast

export fn entry() void {
    var x: error!?Foo = foo();
}

before this change

define void @entry() #2 !dbg !41 {
Entry:
  %0 = alloca %Foo, align 4
  %1 = alloca { %Foo, i1 }, align 4
  %2 = alloca { i16, { %Foo, i1 } }, align 4
  %x = alloca { i16, { %Foo, i1 } }, align 4
  call fastcc void @foo(%Foo* sret %0), !dbg !61
  %3 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %1, i32 0, i32 0, !dbg !61
  %4 = bitcast %Foo* %0 to i8*, !dbg !61
  %5 = bitcast %Foo* %3 to i8*, !dbg !61
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %5, i8* align 4 %4, i64 8, i1 false), !dbg !61
  %6 = getelementptr inbounds { %Foo, i1 }, { %Foo, i1 }* %1, i32 0, i32 1, !dbg !61
  store i1 true, i1* %6, align 1, !dbg !61
  %7 = getelementptr inbounds { i16, { %Foo, i1 } }, { i16, { %Foo, i1 } }* %2, i32 0, i32 0, !dbg !61
  store i16 0, i16* %7, align 2, !dbg !61
  %8 = getelementptr inbounds { i16, { %Foo, i1 } }, { i16, { %Foo, i1 } }* %2, i32 0, i32 1, !dbg !61
  %9 = bitcast { %Foo, i1 }* %1 to i8*, !dbg !61
  %10 = bitcast { %Foo, i1 }* %8 to i8*, !dbg !61
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %10, i8* align 4 %9, i64 12, i1 false), !dbg !61
  %11 = bitcast { i16, { %Foo, i1 } }* %2 to i8*, !dbg !62
  %12 = bitcast { i16, { %Foo, i1 } }* %x to i8*, !dbg !62
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %12, i8* align 4 %11, i64 16, i1 false), !dbg !62
  call void @llvm.dbg.declare(metadata { i16, { %Foo, i1 } }* %x, metadata !45, metadata !DIExpression()), !dbg !62
  ret void, !dbg !63
}

after this change

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 @foo(%Foo* sret %3), !dbg !61
  call void @llvm.dbg.declare(metadata { i16, { %Foo, i1 } }* %x, metadata !45, metadata !DIExpression()), !dbg !62
  ret void, !dbg !63
}

@andrewrk andrewrk added the work in progress This pull request is not ready for review yet. label Oct 11, 2018
@phase
Copy link
Contributor

phase commented Oct 11, 2018

Your “Function Forwarding” example code seems to be incorrect.

@andrewrk
Copy link
Member Author

Thanks, fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
work in progress This pull request is not ready for review yet.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants