-
Notifications
You must be signed in to change notification settings - Fork 170
Implement List return in LLVM #955
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
Conversation
This needs to wait till #952 is merged. |
@Smit-create Update your branch from main and then try to implement the feature. |
integration_tests/test_list_03.py
Outdated
@@ -49,9 +49,29 @@ def test_list_02_string(): | |||
for i in range(50): | |||
assert x[i] == y[i] | |||
|
|||
|
|||
def foo(x: i32) -> list[i32]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a trivial list. Try returning list[list[f64]]
, list[tuple[f64, i32, str]]
, tuple[list[f64], list[str]]
and list[tuple[list[f64], list[c64]]]
. Its very easy to make mistakes and hard code for intrinsic data types like i32
, f64
. Things change when we return nested data types. Take your time, test for above combinations I suggested. If it works then great otherwise I will help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can take inspiration from integration_tests/test_list_06.py
, integration_tests/test_list_05.py
and integration_tests/test_list_07.py
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I slept on the idea of implementing this and found out that we need to make a design choice. The choice will be applicable to any data structure defined by a struct (i.e., lists, tuples, class, excluding arrays though because they are already handled in array_op
ASR pass). First let's note a couple of points,
- Data structures defined by struct should be returned by value - We cannot return pointers to struct especially for those which are created inside the function body. The reason is simple. For example in LLVM backend, we will call
struct_ptr = builder->CreateAlloca(some_struct_type, nullptr)
inside the function body and as soon as the function call will complete and we will return to the caller scope,struct_ptr
will become invalid (because call stack will not be active anymore). - To access any element of a struct we need to use pointer to it in LLVM backend - LLVM only allows accessing elements of a struct if we use a pointer to it. For example if
list
is a struct type, then you cannot callbuilder->CreateGEP(list, idx)
orbuilder->CreateInBoundsGEP(list, idx)
. You must do,builder->CreateGEP(list*, idx)
orbuilder->CreateInBoundsGEP(list*, idx)
.
So, as you can see returning pointers to struct is not possible and structs are only usable if we have pointers to them. So, to satisfy both the constraints there are two options which I can think of,
- Create a temporary variable and store the returned struct into it - Let's see the code directly,
llvm::Value* CreateCallUtil(llvm::Function* fn, std::vector<llvm::Value*>& args, ASR::ttype_t* asr_return_type) {
llvm::Value* return_value = builder->CreateCall(fn, args);
if( !LLVM::is_llvm_struct(asr_return_type) ) {
return return_value;
}
llvm::Value* pointer_to_struct = builder->CreateAlloca(fn->getFunctionType()->getReturnType(), nullptr); // Call to LLVM APIs not needed to fetch the return type of the function. We can use asr_return_type as well but anyways for compactness I did it here.
LLVM::CreateStore(*builder, return_value, pointer_to_struct);
return pointer_to_struct;
}
So you can see how we create a pointer to struct and then store the struct returned by value into it. We can replace all, builder->CreateCall(fn, args)
with the call to above function everywhere. This idea already works in main
(see https://github.com/lcompilers/lpython/blob/main/integration_tests/test_tuple_02.py) but is not generalised to all types yet.
- Create an ASR to ASR pass to convert all functions returning list, tuple, class or anything defined by a struct in the backend to a function not returning anything and accepting the return value as the last argument with
intent(out)
. We do this for arrays already (they are defined by a struct in C/C++ and LLVM backend). We can extend the same to list, tuple, class types as well. We already have the infrastructure to do this.
So what should we do? IMO, choice 1 is quick and easy, and it already works we just have to generalise it. Its very small code to write as well. Choice 2 that is ASR to ASR pass requires visiting the whole ASR again so higher overhead as compared to Choice 1 but it is sharable across all backends. I think we should go for choice 1 for now and then in future if we see it working then we can add an ASR to ASR pass to do the same job. @certik What do you say?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, Choise 1 seems like the way to go for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs some involved effort. @Smit-create Will you mind if I push the remaining commits in your PR sometime this week?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, no problem!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Benchmark updated in #857 (comment) for my MBP. No slowdowns observed.
@certik This is ready for review. |
Resolved and review requested from @certik
|
||
tensor = (deepcopy(mat), deepcopy(vec)) | ||
|
||
return tensors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I personally do not like using the term tensor to mean multidimensional array, but we can change that later.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me.
@czgdp1807 or @Smit-create go ahead and merge this if it is ready. |
I will confirm once that this works with changes from main. Once that happens I will merge. |
1. Generate correct LLVM type for ListConstant 2. Handle lists as return type Co-authored-by: Smit Lunagariya <[email protected]>
No description provided.