www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.ldc - Need help about LLVM build coroutine

reply Dakota <dakota gmail.com> writes:
I am trying to build a llvm coroutine with clang without libcxx.  
  ( ref docs
https://github.com/llvm/llvm-project/blob/main/libcxx/include/__coroutine/noo
_coroutine_handle.h )



Here is my code:

```c
#include <stdio.h>

typedef struct coro {
     void *handle;
     int done;
} coro;

void coro_func(coro *c) {
     printf("Coroutine started\n");
     __builtin_coro_resume(c->handle);
     printf("Coroutine resumed\n");
     __builtin_coro_resume(c->handle);
     printf("Coroutine finished\n");
     c->done = 1;
     __builtin_coro_destroy(c->handle);
}

int main() {
     coro c;
     c.done = 0;
     c.handle = __builtin_coro_noop();

     coro_func(&c);

     printf("in main is_done=%d\n", __builtin_coro_done(c.handle));

     if (!__builtin_coro_done(c.handle)) {
         __builtin_coro_resume(c.handle);
     }

     if (c.done) {
         printf("Coroutine has finished\n");
     } else {
         printf("Coroutine has not finished\n");
         __builtin_coro_destroy(c.handle);
     }

     return 0;
}

```

to build it with `clang++ -std=c++20 ./coro.cpp`


I am doing this because I try create a `extern(C)` stackless 
coroutine solution for D.


My example is so wrong, I am not sure how to fix it.


I need a example from main coroutine switch into new create 
coroutine, and from new create coroutine switch into main 
coroutine.


Any tips how to fix this simple code ?
Mar 02
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
 From what I'm reading of the documentation, it looks like you have to 
work at the IR level to declare a coroutine.

This isn't something you can do with only intrinsics in library code.

https://llvm.org/docs/Coroutines.html#introduction

Take note of the function attribute ``presplitcoroutine`` under 
coroutine representation.

https://llvm.org/docs/Coroutines.html#coroutine-representation
Mar 02
parent reply Dakota <dakota gmail.com> writes:
On Saturday, 2 March 2024 at 12:32:32 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 From what I'm reading of the documentation, it looks like you 
 have to work at the IR level to declare a coroutine.

 This isn't something you can do with only intrinsics in library 
 code.

 https://llvm.org/docs/Coroutines.html#introduction

 Take note of the function attribute ``presplitcoroutine`` under 
 coroutine representation.

 https://llvm.org/docs/Coroutines.html#coroutine-representation
Thanks for the tips. I dont understand LLVM IR. I want to create a LLIR function which accept 2 parameters, first one is a pointer of coroutine function entry, the other is a pointer to a struct look like this: ```d struct coroutine_object { void* coro_handler; void* core_argv; size_t core_argc; } ``` this llvm IR should create one new coroutine, and save the handler into arg2.coro_handler, then start enter the coroutine to execute. I use chatGPT get this code. any one can help me fix it and export to `extern(C)` ```ir define void start_coroutine(i8* %func_ptr, i8** %coro_handle_ptr) { entry: %func = bitcast i8* %func_ptr to void (i8*)* %coro_begin = call i8* llvm.coro.begin(token none, i8* null, i8* null) store i8* %coro_begin, i8** %coro_handle_ptr call void llvm.coro.alloc(i8* %coro_begin, i8* bitcast (void (i8*)* coro_func to i8*)) call void llvm.coro.init(token none, i8* %coro_begin, i8* %coro_begin) call void llvm.coro.save(i8* %coro_begin, i8** null) call void llvm.coro.resume(i8* %coro_begin) ret void } declare token llvm.coro.begin(token, i8*, i8*) declare void llvm.coro.alloc(i8*, i8*) declare void llvm.coro.init(token, i8*, i8*) declare void llvm.coro.save(i8*, i8**) declare void llvm.coro.resume(i8*) ``` to build it use : ```sh llvm-as -o coro.bc coro.ll llvm-ar rcs libcoro.a coro.bc ```
Mar 02
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
Even if I was to help you get that IR working, it won't do anything 
useful for you.

A coroutine in LLVM IR is the top of the call stack.

It must be able to see all functions it calls.

You can't pass a function in by pointer, it has to be hard coded.

This needs compiler support, you can't write code outside of the 
compiler for this.
Mar 02
parent reply Dakota <dakota gmail.com> writes:
On Saturday, 2 March 2024 at 13:08:48 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 Even if I was to help you get that IR working, it won't do 
 anything useful for you.

 A coroutine in LLVM IR is the top of the call stack.

 It must be able to see all functions it calls.

 You can't pass a function in by pointer, it has to be hard 
 coded.

 This needs compiler support, you can't write code outside of 
 the compiler for this.
I see tinygo get this support by mix cpp https://github.com/tinygo-org/llvm-project/blob/master/libcxx/include/experimental/coroutine Is the same thing can be done for D ?
Mar 02
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 03/03/2024 2:14 AM, Dakota wrote:
 On Saturday, 2 March 2024 at 13:08:48 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 Even if I was to help you get that IR working, it won't do anything 
 useful for you.

 A coroutine in LLVM IR is the top of the call stack.

 It must be able to see all functions it calls.

 You can't pass a function in by pointer, it has to be hard coded.

 This needs compiler support, you can't write code outside of the 
 compiler for this.
I see tinygo get this support by mix cpp https://github.com/tinygo-org/llvm-project/blob/master/libcxx/include/experimental/coroutine Is the same thing can be done for D ?
That c++ file belongs to LLVM, it is an older intrinsics file that I believe from my quick searches has been replaced. We can define it ourselves as part of ldc, that isn't the issue. The compiler itself needs to be modified to output the appropriate IR for the coroutine function. There are no free lunches here unfortunately. Unless you want to learn LLVM IR and to modify ldc to support it, there is nothing you can do here beyond talk about how you want it in D.General sorry.
Mar 02
parent Dakota <dakota gmail.com> writes:
On Saturday, 2 March 2024 at 13:18:47 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 03/03/2024 2:14 AM, Dakota wrote:

 That c++ file belongs to LLVM, it is an older intrinsics file 
 that I believe from my quick searches has been replaced.

 We can define it ourselves as part of ldc, that isn't the issue.

 The compiler itself needs to be modified to output the 
 appropriate IR for the coroutine function.

 There are no free lunches here unfortunately.

 Unless you want to learn LLVM IR and to modify ldc to support 
 it, there is nothing you can do here beyond talk about how you 
 want it in D.General sorry.
Thanks.
Mar 03
prev sibling parent Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
On Saturday, 2 March 2024 at 12:56:27 UTC, Dakota wrote:
 On Saturday, 2 March 2024 at 12:32:32 UTC, Richard (Rikki) 
 Andrew Cattermole wrote:
 From what I'm reading of the documentation, it looks like you 
 have to work at the IR level to declare a coroutine.

 This isn't something you can do with only intrinsics in 
 library code.

 https://llvm.org/docs/Coroutines.html#introduction

 Take note of the function attribute ``presplitcoroutine`` 
 under coroutine representation.

 https://llvm.org/docs/Coroutines.html#coroutine-representation
Thanks for the tips. I dont understand LLVM IR. I want to create a LLIR function which accept 2 parameters, first one is a pointer of coroutine function entry, the other is a pointer to a struct look like this: ```d struct coroutine_object { void* coro_handler; void* core_argv; size_t core_argc; } ``` this llvm IR should create one new coroutine, and save the handler into arg2.coro_handler, then start enter the coroutine to execute. I use chatGPT get this code. any one can help me fix it and export to `extern(C)` ```ir define void start_coroutine(i8* %func_ptr, i8** %coro_handle_ptr) { entry: %func = bitcast i8* %func_ptr to void (i8*)* %coro_begin = call i8* llvm.coro.begin(token none, i8* null, i8* null) store i8* %coro_begin, i8** %coro_handle_ptr call void llvm.coro.alloc(i8* %coro_begin, i8* bitcast (void (i8*)* coro_func to i8*)) call void llvm.coro.init(token none, i8* %coro_begin, i8* %coro_begin) call void llvm.coro.save(i8* %coro_begin, i8** null) call void llvm.coro.resume(i8* %coro_begin) ret void } declare token llvm.coro.begin(token, i8*, i8*) declare void llvm.coro.alloc(i8*, i8*) declare void llvm.coro.init(token, i8*, i8*) declare void llvm.coro.save(i8*, i8**) declare void llvm.coro.resume(i8*) ``` to build it use : ```sh llvm-as -o coro.bc coro.ll llvm-ar rcs libcoro.a coro.bc ```
I cannot help at any level close to or further than Rikki, but I want to state that in d with LDC you can embed ir code directly into the code like the below code (it just prints a value using a print function of Gou intrinsic. Not sure it's been long time). void printInt(uint val){ __irEx!(` str = private addrspace(4) constant [4 x i8] c"%d\0A\00" declare i8* llvm.nvvm.ptr.constant.to.gen.p0i8.p4i8(i8 addrspace(4)*) nounwind readnone declare i32 vprintf(i8* nocapture, i8*) nounwind declare i32 addrspace(5)* llvm.nvvm.ptr.gen.to.local.p5i32.p0i32(i32*) nounwind readnone `, ` %tmp = alloca [12 x i32], align 8 %tmp2 = getelementptr inbounds [12 x i32], [12 x i32]* %tmp, i64 0, i64 0 %gen2local = call i32 addrspace(5)* llvm.nvvm.ptr.gen.to.local.p5i32.p0i32(i32* %tmp2) %getElem12 = getelementptr i32, i32 addrspace(5)* %gen2local, i64 0 store i32 %0, i32 addrspace(5)* %getElem12, align 8 %fmt = call i8* llvm.nvvm.ptr.constant.to.gen.p0i8.p4i8(i8 addrspace(4)* getelementptr inbounds ([4 x i8], [4 x i8] addrspace(4)* str, i64 0, i64 0)) %val = bitcast [12 x i32]* %tmp to i8* %call = call i32 vprintf(i8* %fmt, i8* %val) ret void `, ``, void, uint)(val); }
Mar 05