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

Let real function/kernel support returning a struct/multiple values #6590

Open
lin-hitonami opened this issue Nov 14, 2022 · 0 comments
Open
Assignees
Labels
feature request Suggest an idea on this project

Comments

@lin-hitonami
Copy link
Contributor

lin-hitonami commented Nov 14, 2022

Concisely describe the proposed feature
Currently, the Taichi function supports returning a arbitrary number of return values, and the return values can be scalars, matrices and structs, and the Taichi function is inlined into the Taichi kernel at compile time.

However, the Taichi kernel only supports returning a single return value, and the return value can only be a scalar or a matrix.

The WIP real function (#602) is a Taichi function that does not inline into the kernel. The kernel calls it instead of inlining it. In LLVM-based backends, the real function receives an argument with type RuntimeContext where the arguments and the address of return value buffer are stored, same as the Taichi kernel. Currently, the real function only supports a single scalar return value.

I would like to let the real function (and maybe the Taichi kernel) support returning a struct, and then let them support multiple return values (we can construct a struct containing the return values).

Describe the solution you'd like (if any)
A struct can be seen as a tree. A scalar element is a leaf, and a struct element is a subtree. A matrix can be seen as a struct when real_matrix=False, and maybe a special scalar when real_matrix=True. For simplicity, I will not consider matrices (will consider it in the implementation) and I only consider the return value of the real function in the explanation below. The code below are simplified pseudo-code.

For this struct:

from ti.types import struct
s0 = struct(c=ti.f32, d=ti.i64)
s1 = struct(a=ti.i32, b=s0)

There are 3 leaf elements for s1: a, b.c, and b.d.

Basic Implementation

  1. We allocate a u64 array which size equals to the number of the leaf elements in the struct as the return buffer in the caller.
u64 ret_buf[3];
  1. We pass the pointer to the callee
RuntimeContext *rc;
rc->result_buffer=ret_buf
callee(rc);
  1. We cast the elements to the respective types.
i32 a = bitcast_from_u64(ret_buf[0], i32)
f32 c = bitcast_from_u64(ret_buf[1], f32)
i64 d = bitcast_from_u64(ret_buf[2], i64)
  1. We compose a struct type in Python
  • In C++:
Expr return_struct_element(int index){
    return element[index];
}
  • In Python:
ret = s1(a=return_struct_element(0) b=s0(c=return_struct_element(1), d=return_struct_emement(2)))

Possible improvements

We can construct the return values by sequence to a llvm::StructType, and get the return value via the GEP instructions. By doing this, we can save the memory if not all the elements are 64-bit wide.

Where should we store the return value?

There are two places we can store it: the stack or the heap. I suggest we store it on the stack if the size of the return value is very small, otherwise we should store it on the heap, because the stack of CUDA is small.

If we store it on the heap, we may want an allocator using preallocated memory because malloc on CUDA is slow. We may use the NodeManager to allocate and recycle small items which have the same size.

@lin-hitonami lin-hitonami added the feature request Suggest an idea on this project label Nov 14, 2022
@lin-hitonami lin-hitonami self-assigned this Nov 14, 2022
@taichi-gardener taichi-gardener moved this to Untriaged in Taichi Lang Nov 14, 2022
@PENGUINLIONG PENGUINLIONG moved this from Untriaged to In Progress in Taichi Lang Nov 18, 2022
lin-hitonami added a commit that referenced this issue Nov 24, 2022
Issue: #602 #6590

### Brief Summary
Only supports scalar struct (every element in the struct is a scalar)
for now.

This PR does the following things:
1. Let `FuncCallStmt` return the `real_func_ret_struct *` result buffer
instead of returning the return value directly.
2. Add `GetElementStmt` and `GetElementExpression` to get the i-th
return value in a result buffer
3. Add `StructType.from_real_func_ret` to construct the returned struct
to the `StructType` in Python

Will add support for nested struct and matrix in struct in the following
PRs.

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
lin-hitonami added a commit that referenced this issue Nov 28, 2022
#6734)

Issue: #602 #6590
Also fixed the bug that scalarize pass is not run on real functions
thanks to @jim19930609.
### Brief Summary

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
quadpixels pushed a commit to quadpixels/taichi that referenced this issue May 13, 2023
Issue: taichi-dev#602 taichi-dev#6590

### Brief Summary
Only supports scalar struct (every element in the struct is a scalar)
for now.

This PR does the following things:
1. Let `FuncCallStmt` return the `real_func_ret_struct *` result buffer
instead of returning the return value directly.
2. Add `GetElementStmt` and `GetElementExpression` to get the i-th
return value in a result buffer
3. Add `StructType.from_real_func_ret` to construct the returned struct
to the `StructType` in Python

Will add support for nested struct and matrix in struct in the following
PRs.

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
quadpixels pushed a commit to quadpixels/taichi that referenced this issue May 13, 2023
taichi-dev#6734)

Issue: taichi-dev#602 taichi-dev#6590
Also fixed the bug that scalarize pass is not run on real functions
thanks to @jim19930609.
### Brief Summary

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Suggest an idea on this project
Projects
Status: In Progress
Development

No branches or pull requests

1 participant