problem with moveSpillUsesAfterCoroBegin

Here’s what this function is supposed to do:

// Move early uses of spilled variable after CoroBegin.
// For example, if a parameter had address taken, we may end up with the code
// like:
// define @f(i32 %n) {
// %n.addr = alloca i32
// store %n, %n.addr
// …
// call @coro.begin
// we need to move the store after coro.begin

in the implementation it has:

// TODO: Make this more robust. Currently if we run into a situation
// where simple instruction move won’t work we panic and
// report_fatal_error.
for (User *UI : I->users()) {
if (!DT.dominates(CoroBegin, cast(UI)))
report_fatal_error(“cannot move instruction since its users are not”
" dominated by CoroBegin");
}

here is what my frontend is currently generating (I’ll paste the frontend code for clarity and then the LLVM IR):

const std = @import(“std”);

export fn entry() void {
const p = (async(std.debug.global_allocator) amain()) catch unreachable;
cancel p;
}

async fn amain() error {
return error.Failure;
}

; Function Attrs: nobuiltin nounwind
define void @entry() #0 !dbg !220 {
Entry:
%0 = alloca { i16, i8* }, align 8
%1 = alloca { i16, i8* }, align 8
%p = alloca i8*, align 8
%2 = getelementptr inbounds { i16, i8* }, { i16, i8* }* %0, i32 0, i32 0, !dbg !226
store i16 0, i16* %2, !dbg !226
%3 = call fastcc i8* @amain(%Allocator* getelementptr inbounds (%FixedBufferAllocator, %FixedBufferAllocator* @global_fixed_allocator, i32 0, i32 0), i16* %2), !dbg !226
%4 = getelementptr inbounds { i16, i8* }, { i16, i8* }* %0, i32 0, i32 1, !dbg !226
store i8* %3, i8** %4, !dbg !226
%5 = getelementptr inbounds { i16, i8* }, { i16, i8* }* %0, i32 0, i32 0, !dbg !227
%6 = load i16, i16* %5, align 2, !dbg !227
%7 = icmp eq i16 %6, 0, !dbg !227
br i1 %7, label %UnwrapErrOk, label %UnwrapErrError, !dbg !227

UnwrapErrError: ; preds = %Entry
tail call fastcc void @__zig_fail_unwrap(%StackTrace* null, i16 %6), !dbg !227
unreachable, !dbg !227

UnwrapErrOk: ; preds = %Entry
%8 = getelementptr inbounds { i16, i8* }, { i16, i8* }* %0, i32 0, i32 1, !dbg !227
%9 = load i8*, i8** %8, align 8, !dbg !227
store i8* %9, i8** %p, align 8, !dbg !228
call void @llvm.dbg.declare(metadata i8** %p, metadata !224, metadata !DIExpression()), !dbg !228
%10 = load i8*, i8** %p, align 8, !dbg !229
call void @llvm.coro.destroy(i8* %10), !dbg !231
ret void, !dbg !232
}

; Function Attrs: nobuiltin nounwind
define internal fastcc i8* @amain(%StackTrace* nonnull, %Allocator*, i16*) unnamed_addr #0 !dbg !234 {
Entry:
%3 = alloca { i16, %“u8” }, align 8
%4 = alloca { i16, %“u8” }, align 8
%_anon = alloca %AsyncFramePromise, align 8
%5 = bitcast %AsyncFramePromise* %_anon to i8*, !dbg !245
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %5, i8* bitcast (%AsyncFramePromise* @16 to i8*), i64 24, i32 8, i1 false), !dbg !245
call void @llvm.dbg.declare(metadata %AsyncFramePromise* %_anon, metadata !239, metadata !DIExpression()), !dbg !245
%6 = bitcast %AsyncFramePromise* %_anon to i8*, !dbg !245
%7 = call token @llvm.coro.id(i32 16, i8* %6, i8* null, i8* null), !dbg !245
%8 = call i1 @llvm.coro.alloc(token %7), !dbg !245
br i1 %8, label %DynAlloc, label %CoroBegin, !dbg !245

DynAlloc: ; preds = %Entry
%9 = call i64 @llvm.coro.size.i64(), !dbg !245
%10 = getelementptr inbounds %Allocator, %Allocator* %1, i32 0, i32 0, !dbg !245
%11 = load void ({ i16, %“u8” }, %StackTrace, %Allocator*, i64, i29), void ({ i16, %“[]u8” }, %StackTrace*, %Allocator*, i64, i29)** %10, align 8, !dbg !245
call fastcc void %11({ i16, %“u8” }* %3, %StackTrace* %0, %Allocator* %1, i64 %9, i29 16), !dbg !245
%12 = getelementptr inbounds { i16, %“u8” }, { i16, %“u8” }* %3, i32 0, i32 0, !dbg !245
%13 = load i16, i16* %12, align 2, !dbg !245
%14 = icmp ne i16 %13, 0, !dbg !245
br i1 %14, label %AllocError, label %AllocOk, !dbg !245

AllocError: ; preds = %DynAlloc
%15 = getelementptr inbounds { i16, %“u8” }, { i16, %“u8” }* %3, i32 0, i32 0, !dbg !245
%16 = load i16, i16* %15, align 2, !dbg !245
store i16 %16, i16* %2, !dbg !245
ret i8* null, !dbg !245

AllocOk: ; preds = %DynAlloc
%17 = getelementptr inbounds { i16, %“u8” }, { i16, %“u8” }* %3, i32 0, i32 1, !dbg !245
%18 = getelementptr inbounds %“u8”, %“u8”* %17, i32 0, i32 0, !dbg !245
%19 = load i8*, i8** %18, align 8, !dbg !245
br label %CoroBegin, !dbg !245

CoroBegin: ; preds = %AllocOk, %Entry
%20 = phi i8* [ null, %Entry ], [ %19, %AllocOk ], !dbg !245
%21 = phi %“u8”* [ undef, %Entry ], [ %17, %AllocOk ], !dbg !245
%22 = call i8* @llvm.coro.begin(token %7, i8* %20), !dbg !245
%23 = getelementptr inbounds %AsyncFramePromise, %AsyncFramePromise* %_anon, i32 0, i32 0, !dbg !245
%24 = getelementptr inbounds %AsyncFramePromise, %AsyncFramePromise* %_anon, i32 0, i32 1, !dbg !245
%25 = getelementptr inbounds %AsyncFramePromise, %AsyncFramePromise* %_anon, i32 0, i32 2, !dbg !245
store i16* %24, i16** %25, align 8, !dbg !245
%26 = load i16*, i16** %25, align 8, !dbg !246
store i16 2, i16* %26, align 2, !dbg !246
%27 = load i8*, i8** %23, align 8, !dbg !246
%28 = icmp ne i8* %27, null, !dbg !246
br i1 %28, label %CoroNormalFinal, label %CoroEarlyFinal, !dbg !246

CoroEarlyFinal: ; preds = %CoroBegin
%29 = call i8 @llvm.coro.suspend(token none, i1 true), !dbg !245
switch i8 %29, label %Suspend [
i8 0, label %InvalidResume
i8 1, label %FinalCleanup
], !dbg !245

Suspend: ; preds = %CoroEarlyFinal
%30 = call i1 @llvm.coro.end(i8* null, i1 false), !dbg !245
ret i8* %22, !dbg !245

InvalidResume: ; preds = %CoroEarlyFinal
tail call fastcc void @panic(%“u8”* @20, %StackTrace* null), !dbg !245
unreachable, !dbg !245

CoroNormalFinal: ; preds = %CoroBegin
br label %CheckFree, !dbg !245

FinalCleanup: ; preds = %CoroEarlyFinal
%31 = load i16*, i16** %25, align 8, !dbg !245
%32 = bitcast i16* %31 to i8*, !dbg !245
%33 = bitcast i16* %24 to i8*, !dbg !245
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %32, i8* %33, i64 2, i32 2, i1 false), !dbg !245
br label %CheckFree, !dbg !245

CheckFree: ; preds = %FinalCleanup, %CoroNormalFinal
%34 = phi i1 [ false, %FinalCleanup ], [ true, %CoroNormalFinal ], !dbg !245
br i1 %8, label %DynFree, label %EndFree, !dbg !245

DynFree: ; preds = %CheckFree
%35 = getelementptr inbounds %Allocator, %Allocator* %1, i32 0, i32 2, !dbg !245
%36 = load void (%Allocator*, %“u8”), void (%Allocator*, %“u8”)** %35, align 8, !dbg !245
call fastcc void %36(%Allocator
%1, %“u8”* byval %21), !dbg !245
br label %EndFree, !dbg !245

EndFree: ; preds = %DynFree, %CheckFree
br i1 %34, label %Resume, label %Return, !dbg !245

Resume: ; preds = %EndFree
%37 = load i8*, i8** %23, align 8, !dbg !245
%38 = load i8*, i8** %23, align 8, !dbg !245
call void @llvm.coro.resume(i8* %38), !dbg !245
br label %Return, !dbg !245

Return: ; preds = %Resume, %EndFree
ret i8* undef, !dbg !245
}

This triggers:

“cannot move instruction since its users are not dominated by CoroBegin”

When I use gdb to print I->dump():

%8 = getelementptr inbounds %Allocator, %Allocator* %1, i32 0, i32 0, !dbg !71

Much to my surprise, this is pointing to an instruction in the entry function, not the amain coroutine function. Is this a bug?

Thanks for the help,

Andrew