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

error in coroutine intrinsic lowering #60713

Closed
alperenunal opened this issue Feb 13, 2023 · 15 comments
Closed

error in coroutine intrinsic lowering #60713

alperenunal opened this issue Feb 13, 2023 · 15 comments
Assignees
Labels
coroutines C++20 coroutines question A question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead!

Comments

@alperenunal
Copy link

alperenunal commented Feb 13, 2023

Hey, I was trying to compile a module similar to one in the coroutine documentation ^.

define ptr @f(i32 %0) {
entry:
  %1 = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
  %2 = call i32 @llvm.coro.size.i32()
  %3 = call ptr @malloc(i32 %2)
  %4 = call ptr @llvm.coro.begin(token %1, ptr %3)
  br label %loop

loop:                                             ; preds = %loop, %entry
  %5 = phi i32 [ %0, %entry ], [ %6, %loop ]
  %6 = add nsw i32 %5, 1
  %7 = call i32 (ptr, ...) @printf(ptr @0, i32 %5)
  %8 = call i8 @llvm.coro.suspend(token %1, i1 false)
  switch i8 %8, label %suspend [
    i8 0, label %loop
    i8 1, label %cleanup
  ]

cleanup:                                          ; preds = %loop
  %9 = call ptr @llvm.coro.free(token none, ptr %4)
  call void @free(ptr %9)
  br label %suspend

suspend:                                          ; preds = %cleanup, %loop
  %10 = call i1 @llvm.coro.end(ptr %4, i1 false)
  ret ptr %4
}

define i32 @main() {
entry:
  %0 = call ptr @f(i32 4)
  call void @llvm.coro.resume(ptr %0)
  call void @llvm.coro.resume(ptr %0)
  call void @llvm.coro.destroy(ptr %0)
  ret i32 0
}

When I try to compile this with (apt.llvm distribution of Clang) clang-15 coro.ll. I got the following error.

fatal error: error in backend: Cannot select: intrinsic %llvm.coro.size
clang: error: clang frontend command failed with exit code 70 (use -v to see invocation)
Ubuntu clang version 15.0.7
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
clang: note: diagnostic msg: Error generating preprocessed source(s) - no preprocessable inputs.

Also with llc-15 coro.ll I get:

LLVM ERROR: Cannot select: intrinsic %llvm.coro.destroy
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.	Program arguments: llc-15 coro.ll
1.	Running pass 'Function Pass Manager' on module 'coro.ll'.
2.	Running pass 'X86 DAG->DAG Instruction Selection' on function '@main'
#0 0x00007fc925b0ae91 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0xf01e91)
#1 0x00007fc925b08bde llvm::sys::RunSignalHandlers() (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0xeffbde)
#2 0x00007fc925b0b3bb (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0xf023bb)
#3 0x00007fc9246d8520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
#4 0x00007fc92472ca7c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
#5 0x00007fc92472ca7c __pthread_kill_internal ./nptl/pthread_kill.c:78:10
#6 0x00007fc92472ca7c pthread_kill ./nptl/pthread_kill.c:89:10
#7 0x00007fc9246d8476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
#8 0x00007fc9246be7f3 abort ./stdlib/abort.c:81:7
#9 0x00007fc925a408d2 llvm::report_fatal_error(llvm::Twine const&, bool) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0xe378d2)
#10 0x00007fc92634d3c4 (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x17443c4)
#11 0x00007fc92634c898 llvm::SelectionDAGISel::SelectCodeCommon(llvm::SDNode*, unsigned char const*, unsigned int) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x1743898)
#12 0x00007fc9284b3a38 (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x38aaa38)
#13 0x00007fc92634432f llvm::SelectionDAGISel::DoInstructionSelection() (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x173b32f)
#14 0x00007fc926343a04 llvm::SelectionDAGISel::CodeGenAndEmitDAG() (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x173aa04)
#15 0x00007fc926342df1 llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x1739df1)
#16 0x00007fc92634095b llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x173795b)
#17 0x00007fc9284aadbd (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x38a1dbd)
#18 0x00007fc925e9651c llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x128d51c)
#19 0x00007fc925c46fa2 llvm::FPPassManager::runOnFunction(llvm::Function&) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x103dfa2)
#20 0x00007fc925c4e563 llvm::FPPassManager::runOnModule(llvm::Module&) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x1045563)
#21 0x00007fc925c47b46 llvm::legacy::PassManagerImpl::run(llvm::Module&) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x103eb46)
#22 0x00005576383a9dff main (/usr/lib/llvm-15/bin/llc+0x10dff)
#23 0x00007fc9246bfd90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#24 0x00007fc9246bfe40 call_init ./csu/../csu/libc-start.c:128:20
#25 0x00007fc9246bfe40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#26 0x00005576383a4dce _start (/usr/lib/llvm-15/bin/llc+0xbdce)
Aborted (core dumped)

So I tried to run coroutine lowering passes manually with

opt-15 --passes=coro-early,coro-split,coro-elide,coro-cleanup coro.ll -S -o coro.ll

Resulting module looks like this:

define ptr @f(i32 %0) {
entry:
  %1 = call i32 @llvm.coro.size.i32()
  %2 = call ptr @malloc(i32 %1)
  br label %loop

loop:                                             ; preds = %loop, %entry
  %3 = phi i32 [ %0, %entry ], [ %4, %loop ]
  %4 = add nsw i32 %3, 1
  %5 = call i32 (ptr, ...) @printf(ptr @0, i32 %3)
  %6 = call i8 @llvm.coro.suspend(token none, i1 false)
  switch i8 %6, label %suspend [
    i8 0, label %loop
    i8 1, label %cleanup
  ]

cleanup:                                          ; preds = %loop
  call void @free(ptr %2)
  br label %suspend

suspend:                                          ; preds = %cleanup, %loop
  %7 = call i1 @llvm.coro.end(ptr %2, i1 false) #5
  ret ptr %2
}

Documentation says llvm.coro.size.i32 should be lowered to a constant, so I guess call to that should not be here.

@EugeneZelenko EugeneZelenko added coroutines C++20 coroutines and removed new issue labels Feb 13, 2023
@alperenunal
Copy link
Author

alperenunal commented Feb 13, 2023

Also when I try to compile the code in #50004, even when I changed file to a .cpp file, and used Clang++ to compile, I get error:

fatal error: error in backend: Cannot select: intrinsic %llvm.coro.size
clang: error: clang frontend command failed with exit code 70 (use -v to see invocation)
Ubuntu clang version 15.0.7
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
clang: note: diagnostic msg:

@llvmbot
Copy link
Collaborator

llvmbot commented Feb 13, 2023

@llvm/issue-subscribers-coroutines

@WojciechMazur
Copy link

WojciechMazur commented Feb 13, 2023

I had similar issues recently. From what I've observed there seems to be a regression started in llvm-14
When trying with opt-<11..13> --enable-coroutines -O0 it correctly lowers the intrinsic: is able to calculate frame size, and split method.

Without -O# flag the intrinsic are not lowered, when passing passes explicitly the coro-split is not found unless we add include default passes as well.

In version 14 --enable-coroutines flag seems to have no effect. In versions <15..17> this flag is missing, and support should be enabled by default, however it does not work.

Output after lowering with opt-13 (click arrow to expand)

source_filename = "test.ll"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

%f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i32, i1 }

@.str = private unnamed_addr constant [7 x i8] c"n: %d\0A\00", align 1
@f.resumers = private constant [3 x void (%f.Frame*)*] [void (%f.Frame*)* @f.resume, void (%f.Frame*)* @f.destroy, void (%f.Frame*)* @f.cleanup]

define i8* @f(i32 %n) {
entry:
  %alloc = call i8* @malloc(i32 24)
  %FramePtr = bitcast i8* %alloc to %f.Frame*
  %resume.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 0
  store void (%f.Frame*)* @f.resume, void (%f.Frame*)** %resume.addr, align 8
  %destroy.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 1
  store void (%f.Frame*)* @f.destroy, void (%f.Frame*)** %destroy.addr, align 8
  %val = alloca i32, align 4
  store i32 0, i32* %val, align 4
  %n.val.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
  store i32 %n, i32* %n.val.spill.addr, align 4
  %inc = add nsw i32 %n, 1
  call void @print(i32 %n)
  %index.addr2 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3
  store i1 false, i1* %index.addr2, align 1
  ret i8* %alloc
}

; Function Attrs: argmemonly nounwind readonly
declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*) #0

; Function Attrs: nounwind readnone
declare i32 @llvm.coro.size.i32() #1

declare noalias i8* @malloc(i32)

declare void @free(i8*)

; Function Attrs: nounwind
declare i8* @llvm.coro.begin(token, i8* writeonly) #2

declare void @print(i32)

; Function Attrs: nounwind
declare i8 @llvm.coro.suspend(token, i1) #2

; Function Attrs: argmemonly nounwind readonly
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #0

; Function Attrs: nounwind
declare i1 @llvm.coro.end(i8*, i1) #2

define i32 @main() {
entry:
  %hdl = call i8* @f(i32 4)
  %0 = bitcast i8* %hdl to { i8*, i8* }*
  %1 = getelementptr inbounds { i8*, i8* }, { i8*, i8* }* %0, i32 0, i32 0
  %2 = load i8*, i8** %1, align 8
  %3 = bitcast i8* %2 to void (i8*)*
  call fastcc void %3(i8* %hdl)
  %4 = bitcast i8* %hdl to { i8*, i8* }*
  %5 = getelementptr inbounds { i8*, i8* }, { i8*, i8* }* %4, i32 0, i32 0
  %6 = load i8*, i8** %5, align 8
  %7 = bitcast i8* %6 to void (i8*)*
  call fastcc void %7(i8* %hdl)
  %8 = bitcast i8* %hdl to { i8*, i8* }*
  %9 = getelementptr inbounds { i8*, i8* }, { i8*, i8* }* %8, i32 0, i32 1
  %10 = load i8*, i8** %9, align 8
  %11 = bitcast i8* %10 to void (i8*)*
  call fastcc void %11(i8* %hdl)
  ret i32 0
}

declare void @llvm.coro.resume(i8*)

declare void @llvm.coro.destroy(i8*)

; Function Attrs: argmemonly nounwind readonly
declare i8* @llvm.coro.subfn.addr(i8* nocapture readonly, i8) #0

declare void @i64_print(i64)

; Function Attrs: cold noreturn nounwind
declare void @llvm.trap() #3

; Function Attrs: nomerge nounwind
declare token @llvm.coro.save(i8*) #4

define internal fastcc void @f.resume(%f.Frame* noalias nonnull align 8 dereferenceable(24) %FramePtr) {
entry.resume:
  %vFrame = bitcast %f.Frame* %FramePtr to i8*
  %val = alloca i32, align 4
  br label %resume.entry

loop:                                             ; preds = %loop.from.AfterCoroSuspend
  %n.val.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
  store i32 %inc1, i32* %n.val.spill.addr, align 4
  %inc = add nsw i32 %inc1, 1
  call void @print(i32 %inc1)
  br label %CoroSave

CoroSave:                                         ; preds = %loop
  %index.addr2 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3
  store i1 false, i1* %index.addr2, align 1
  br label %CoroSuspend

CoroSuspend:                                      ; preds = %CoroSave
  br label %resume.0.landing

resume.0:                                         ; preds = %resume.entry
  br label %resume.0.landing

resume.0.landing:                                 ; preds = %resume.0, %CoroSuspend
  %0 = phi i8 [ -1, %CoroSuspend ], [ 0, %resume.0 ]
  br label %AfterCoroSuspend

AfterCoroSuspend:                                 ; preds = %resume.0.landing
  switch i8 %0, label %suspend [
    i8 0, label %loop.from.AfterCoroSuspend
    i8 1, label %cleanup
  ]

loop.from.AfterCoroSuspend:                       ; preds = %AfterCoroSuspend
  %n.val.reload.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
  %n.val.reload = load i32, i32* %n.val.reload.addr, align 4
  %inc1 = add nsw i32 %n.val.reload, 1
  br label %loop

cleanup:                                          ; preds = %AfterCoroSuspend
  call void @free(i8* %vFrame)
  br label %suspend

suspend:                                          ; preds = %cleanup, %AfterCoroSuspend
  br label %CoroEnd

CoroEnd:                                          ; preds = %suspend
  ret void

resume.entry:                                     ; preds = %entry.resume
  br label %resume.0
}

define internal fastcc void @f.destroy(%f.Frame* noalias nonnull align 8 dereferenceable(24) %FramePtr) {
entry.destroy:
  %vFrame = bitcast %f.Frame* %FramePtr to i8*
  %val = alloca i32, align 4
  br label %resume.entry

loop:                                             ; preds = %loop.from.AfterCoroSuspend
  %n.val.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
  store i32 %inc1, i32* %n.val.spill.addr, align 4
  %inc = add nsw i32 %inc1, 1
  call void @print(i32 %inc1)
  br label %CoroSave

CoroSave:                                         ; preds = %loop
  %index.addr2 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3
  store i1 false, i1* %index.addr2, align 1
  br label %CoroSuspend

CoroSuspend:                                      ; preds = %CoroSave
  br label %resume.0.landing

resume.0:                                         ; preds = %resume.entry
  br label %resume.0.landing

resume.0.landing:                                 ; preds = %resume.0, %CoroSuspend
  %0 = phi i8 [ -1, %CoroSuspend ], [ 1, %resume.0 ]
  br label %AfterCoroSuspend

AfterCoroSuspend:                                 ; preds = %resume.0.landing
  switch i8 %0, label %suspend [
    i8 0, label %loop.from.AfterCoroSuspend
    i8 1, label %cleanup
  ]

loop.from.AfterCoroSuspend:                       ; preds = %AfterCoroSuspend
  %n.val.reload.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
  %n.val.reload = load i32, i32* %n.val.reload.addr, align 4
  %inc1 = add nsw i32 %n.val.reload, 1
  br label %loop

cleanup:                                          ; preds = %AfterCoroSuspend
  call void @free(i8* %vFrame)
  br label %suspend

suspend:                                          ; preds = %cleanup, %AfterCoroSuspend
  br label %CoroEnd

CoroEnd:                                          ; preds = %suspend
  ret void

resume.entry:                                     ; preds = %entry.destroy
  br label %resume.0
}

define internal fastcc void @f.cleanup(%f.Frame* noalias nonnull align 8 dereferenceable(24) %FramePtr) {
entry.cleanup:
  %vFrame = bitcast %f.Frame* %FramePtr to i8*
  %val = alloca i32, align 4
  br label %resume.entry

loop:                                             ; preds = %loop.from.AfterCoroSuspend
  %n.val.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
  store i32 %inc1, i32* %n.val.spill.addr, align 4
  %inc = add nsw i32 %inc1, 1
  call void @print(i32 %inc1)
  br label %CoroSave

CoroSave:                                         ; preds = %loop
  %index.addr2 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3
  store i1 false, i1* %index.addr2, align 1
  br label %CoroSuspend

CoroSuspend:                                      ; preds = %CoroSave
  br label %resume.0.landing

resume.0:                                         ; preds = %resume.entry
  br label %resume.0.landing

resume.0.landing:                                 ; preds = %resume.0, %CoroSuspend
  %0 = phi i8 [ -1, %CoroSuspend ], [ 1, %resume.0 ]
  br label %AfterCoroSuspend

AfterCoroSuspend:                                 ; preds = %resume.0.landing
  switch i8 %0, label %suspend [
    i8 0, label %loop.from.AfterCoroSuspend
    i8 1, label %cleanup
  ]

loop.from.AfterCoroSuspend:                       ; preds = %AfterCoroSuspend
  %n.val.reload.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
  %n.val.reload = load i32, i32* %n.val.reload.addr, align 4
  %inc1 = add nsw i32 %n.val.reload, 1
  br label %loop

cleanup:                                          ; preds = %AfterCoroSuspend
  call void @free(i8* null)
  br label %suspend

suspend:                                          ; preds = %cleanup, %AfterCoroSuspend
  br label %CoroEnd

CoroEnd:                                          ; preds = %suspend
  ret void

resume.entry:                                     ; preds = %entry.cleanup
  br label %resume.0
}

attributes #0 = { argmemonly nounwind readonly }
attributes #1 = { nounwind readnone }
attributes #2 = { nounwind }
attributes #3 = { cold noreturn nounwind }
attributes #4 = { nomerge nounwind }

!llvm.linker.options = !{}
!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 1}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"Ubuntu clang version 14.0.6"}

@alperenunal
Copy link
Author

Looks like you are right. I am able to compile the module with clang-13, and opt-13 with -O# (--enable-coroutines flag seems not necessary) produces properly lowered IR. Also llvm-14 has the issues same as 15.

So, this is a bug needs to be fixed, right?

@ChuanqiXu9 ChuanqiXu9 self-assigned this Feb 14, 2023
@ChuanqiXu9
Copy link
Member

Yeah, this one should be a bug to be addressed.

@ChuanqiXu9
Copy link
Member

This looks related to pipeline. @aeubanks would you like to take a look?

@aeubanks
Copy link
Contributor

unless I'm missing something, all the modules and C++ code linked in this bug aren't complete or don't compile. is there some complete C++ code or LLVM IR plus a clang/opt invocation to look at

@WojciechMazur
Copy link

@aeubanks Here's the full LLVM IR used to generate output using opt-13 few posts above:

; ModuleID = 'test.ll'
source_filename = "test.ll"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@.str = private unnamed_addr constant [7 x i8] c"n: %d\0A\00", align 1

define i8* @f(i32 %n) {
entry:
  %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
  %size = call i32 @llvm.coro.size.i32()
  %alloc = call i8* @malloc(i32 %size)
  %hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
    %val = alloca i32
    store i32 0, i32* %val
  br label %loop
loop:
  %n.val = phi i32 [ %n, %entry ], [ %inc, %loop ]
  %inc = add nsw i32 %n.val, 1
  call void @print(i32 %n.val)
  %0 = call i8 @llvm.coro.suspend(token none, i1 false)
  switch i8 %0, label %suspend [i8 0, label %loop
                                i8 1, label %cleanup]
cleanup:
  %mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
  call void @free(i8* %mem)
  br label %suspend
suspend:
  %unused = call i1 @llvm.coro.end(i8* %hdl, i1 false)
  ret i8* %hdl
}


declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare i32 @llvm.coro.size.i32()
declare noalias i8* @malloc(i32)
declare void @free(i8*)
declare i8* @llvm.coro.begin(token, i8*)
declare void @print(i32)
declare i8 @llvm.coro.suspend(token, i1)
declare i8* @llvm.coro.free(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)

define i32 @main() {
entry:
  %hdl = call i8* @f(i32 4)
  call void @llvm.coro.resume(i8* %hdl)
  call void @llvm.coro.resume(i8* %hdl)
  call void @llvm.coro.destroy(i8* %hdl)
  ret i32 0
}

It's basically the example from the docs
It's being processed using opt test.ll --passes='default<O0>' -S -o test-opt.ll

The expected output is in the post above (using opt-13), below there's the output when using opt-14 or newer

; ModuleID = 'test.ll'
source_filename = "test.ll"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

define i8* @f(i32 %n) {
entry:
  %size = call i32 @llvm.coro.size.i32()
  %alloc = call i8* @malloc(i32 %size)
  %val = alloca i32, align 4
  store i32 0, i32* %val, align 4
  br label %loop

loop:                                             ; preds = %loop, %entry
  %n.val = phi i32 [ %n, %entry ], [ %inc, %loop ]
  %inc = add nsw i32 %n.val, 1
  call void @print(i32 %n.val)
  %0 = call i8 @llvm.coro.suspend(token none, i1 false)
  switch i8 %0, label %suspend [
    i8 0, label %loop
    i8 1, label %cleanup
  ]

cleanup:                                          ; preds = %loop
  call void @free(i8* %alloc)
  br label %suspend

suspend:                                          ; preds = %cleanup, %loop
  %unused = call i1 @llvm.coro.end(i8* %alloc, i1 false) #2
  ret i8* %alloc
}

; Function Attrs: nounwind readnone
declare i32 @llvm.coro.size.i32() #0

declare noalias i8* @malloc(i32)

declare void @free(i8*)

declare void @print(i32)

; Function Attrs: nounwind
declare i8 @llvm.coro.suspend(token, i1) #1

; Function Attrs: nounwind
declare i1 @llvm.coro.end(i8*, i1) #1

define i32 @main() {
entry:
  %hdl = call i8* @f(i32 4)
  %0 = bitcast i8* %hdl to { i8*, i8* }*
  %1 = getelementptr inbounds { i8*, i8* }, { i8*, i8* }* %0, i32 0, i32 0
  %2 = load i8*, i8** %1, align 8
  %3 = bitcast i8* %2 to void (i8*)*
  call fastcc void %3(i8* %hdl)
  %4 = bitcast i8* %hdl to { i8*, i8* }*
  %5 = getelementptr inbounds { i8*, i8* }, { i8*, i8* }* %4, i32 0, i32 0
  %6 = load i8*, i8** %5, align 8
  %7 = bitcast i8* %6 to void (i8*)*
  call fastcc void %7(i8* %hdl)
  %8 = bitcast i8* %hdl to { i8*, i8* }*
  %9 = getelementptr inbounds { i8*, i8* }, { i8*, i8* }* %8, i32 0, i32 1
  %10 = load i8*, i8** %9, align 8
  %11 = bitcast i8* %10 to void (i8*)*
  call fastcc void %11(i8* %hdl)
  ret i32 0
}

attributes #0 = { nounwind readnone }
attributes #1 = { nounwind }
attributes #2 = { noduplicate }

@aeubanks
Copy link
Contributor

I'm not too familiar with coroutines, which pass is supposed to lower the coroutine size intrinsic? (I'm not figuring it out from a quick glance at the various coroutine passes)

@alperenunal
Copy link
Author

@aeubanks CoroSplit pass.

@aeubanks
Copy link
Contributor

./build/rel/bin/opt -passes=coro-split -S /tmp/a.ll -disable-output --print-changed claims CoroSplit isn't changing anything, so seems like an issue with the pass itself rather than the pipeline?

(running opt -O0/opt -O3 complains Assertion F.isPresplitCoroutine() && "The frontend uses Swtich-Resumed ABI should emit " "\"coroutine.presplit\" attribute for the coroutine. saying the IR has already been lowered, so I'm assuming I haven't forgotten to run another coroutine pass first)

@alperenunal
Copy link
Author

alperenunal commented Feb 16, 2023

Well, this is not a bug, but my fault. I forgot to add presplitcoroutine attribute to the coroutine. Documentation says frontend should emit it ^. opt-13 emits coroutine.presplit attribute for the coroutine in CoroEarly pass, so that's the difference. One thing confused me is that your output says coroutine.presplit attribute is missing. Which version is that? Looks like coroutine.presplit attribute does not work anymore. Instead we should use presplitcoroutine.

define ptr @f(i32 %0) presplitcoroutine {
entry:
  %1 = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
  %2 = call i32 @llvm.coro.size.i32()
  %3 = call ptr @malloc(i32 %2)
  %4 = call ptr @llvm.coro.begin(token %1, ptr %3)
  br label %loop

loop:                                             ; preds = %loop, %entry
  %5 = phi i32 [ %0, %entry ], [ %6, %loop ]
  %6 = add nsw i32 %5, 1
  %7 = call i32 (ptr, ...) @printf(ptr @0, i32 %5)
  %8 = call i8 @llvm.coro.suspend(token %1, i1 false)
  switch i8 %8, label %suspend [
    i8 0, label %loop
    i8 1, label %cleanup
  ]

cleanup:                                          ; preds = %loop
  %9 = call ptr @llvm.coro.free(token none, ptr %4)
  call void @free(ptr %9)
  br label %suspend

suspend:                                          ; preds = %cleanup, %loop
  %10 = call i1 @llvm.coro.end(ptr %4, i1 false)
  ret ptr %4
}

define i32 @main() {
entry:
  %0 = call ptr @f(i32 4)
  call void @llvm.coro.resume(ptr %0)
  call void @llvm.coro.resume(ptr %0)
  call void @llvm.coro.destroy(ptr %0)
  ret i32 0
}

This compiles fine with clang-15

@EugeneZelenko EugeneZelenko added the question A question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead! label Feb 16, 2023
@aeubanks
Copy link
Contributor

ToT LLVM

That should probably be a verifier check instead of a random assert?

@aeubanks
Copy link
Contributor

going to reopen this as the current error reporting seems confusing, this seems very easy to get wrong and there should be some sort of error reporting, whether in the lowering pass itself or in the verifier

@ChuanqiXu9
Copy link
Member

Yeah, a verifier looks better. Let's track the issue in #60863.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
coroutines C++20 coroutines question A question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead!
Projects
Status: No status
Development

No branches or pull requests

6 participants