Skip to content

Add thunk/adapter for FFI Upcall on Z - #15596

Merged
joransiu merged 1 commit into
eclipse-openj9:masterfrom
dchopra001:ffiZ_final
Sep 7, 2022
Merged

Add thunk/adapter for FFI Upcall on Z#15596
joransiu merged 1 commit into
eclipse-openj9:masterfrom
dchopra001:ffiZ_final

Conversation

@dchopra001

Copy link
Copy Markdown
Contributor

Signed-off-by: Dhruv Chopra Dhruv.C.Chopra@ibm.com

@dchopra001

Copy link
Copy Markdown
Contributor Author

Marked as a draft because I need to confirm that a failure in the builds is not related to my changes, and some formatting changes need to be made.

@dchopra001

Copy link
Copy Markdown
Contributor Author

@ChengJin01

Copy link
Copy Markdown

FYI: @tajila, @pshipton, @DanHeidinga

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General comment: make the indentation uniform. Maybe your editor setting.

Specific comment to this code: avoid duplicate code in if and else.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of these should be fixed now.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks wrong: since single-precision FP is represented in the left part of the register, you cannot jump over structOffset to return the address later on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The structOffset will be zero when the struct size is 1, 2, 4 or 8 bytes.

On zLinux only structs of size 1, 2, 4, and 8 can be passed by value in a register. This is usually done in a GPR.

The only scenario where this is done in an FPR is if we have a pure float/double struct with a single element (i.e. structs of size 4 and 8 bytes).

So I don't think we will end up in a problematic situation. Unless I'm missing sometime else here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. the point is you cannot handle struct-of-1/2/4 bytes the same way with FPR and GPR arguments. the reason is: FPR is only populated in the left and GPR is only populated on the right. Imagine a single pure float struct to be passed ... it is passed by value in FPR with left half register populated ... you store it back with STEY at offset 0 in the stack slot ... later on, you have a structOffset value of 4 ... the return address actually points at the right half of the slot.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been fixed now. structOffset will be zero for FPR argument types (including structs passed by value in FPR).

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mentioned above: you cannot handle FPs similarly as to integer arguments.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for ALL_SP with a single float, this calculation is obviously having structOffset set to 4.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see what you mean. It does look like I missed that corner case. I'll rework this when I push new changes with the formatting fixed.

I'll also be adding to the upcall tests to capture all the zLinux corner cases (either in this PR or I'll pass them on to Cheng) so hopefully we won't have any more of these.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. the point is you cannot handle struct-of-1/2/4 bytes the same way with FPR and GPR arguments. the reason is: FPR is only populated in the left and GPR is only populated on the right. Imagine a single pure float struct to be passed ... it is passed by value in FPR with left half register populated ... you store it back with STEY at offset 0 in the stack slot ... later on, you have a structOffset value of 4 ... the return address actually points at the right half of the slot.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for ALL_SP with a single float, this calculation is obviously having structOffset set to 4.

@dchopra001
dchopra001 marked this pull request as ready for review July 26, 2022 22:45
@dchopra001
dchopra001 force-pushed the ffiZ_final branch 2 times, most recently from 404b686 to c3e744e Compare August 4, 2022 01:51
@dchopra001

Copy link
Copy Markdown
Contributor Author

This commit should pass all up call tests found in @ChengJin01's branch: https://github.com/ChengJin01/openj9/find/jep389_419_ffi_upcall_v4_struct_v2_outofline_mh_v9_fin_v8
under the test/functional/Java18andUp/src/org/openj9/test/jep419/upcall/ directory.

Note however that the Memory Padding in UpcallMHWithStructTests.java is not compatible on zLinux. Removing the padding on zLinux should resolve the problem. Just leaving this as a note here.

@dchopra001

dchopra001 commented Aug 4, 2022

Copy link
Copy Markdown
Contributor Author

I also did my own unit testing as I was building the solution to make sure I'm covering all cases. On zLinux the parameter can be stored in one of two call frames depending on the situation. So there are some unique tests with 10+ arguments to test these scenarios. I've put those tests here. The readme describes the test cases. @ChengJin01 if you think any of these tests would be worth adding to your existing tests then let me know and I can provide you with the necessary files.

@r30shah r30shah left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dchopra001 Flushing out the first round of review. I have so far looked at the code of functions to generate the instructions in detail, most of the things looks OK, some things I observe while using MVC instructions that I think needs to be checked out.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using #define macro for some of the constants that represents the value of register for instructions, to improve readability and avoid using local variable for them? It would be useful for other functions in the code as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added these here: 39be597c2b57137681a8c4d40d31df5fc384b62e

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This increment represents the size of LG instruction right? In that case, if call to LG function returns the instruction size, shouldn't it use that instead of this hardcoded 6?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed now as I mentioned on the comment on line 172

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to previous comment. As the caller of this function consumes the instruction size returned by it to update instruction pointer, for this particular function passing it instructionPtr+totalInstructionSize may also work and you can avoid incrementing the instruction by hardcoded constants.

Also is it possible to simplify argument so that it is clear, what is source memory reference, what is destination memory reference, and how many bytes we are copying?

@dchopra001 dchopra001 Aug 15, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming here should be improved now to make the function call more readable. I've also improved the variable names in the helper routines and updated how instructionPtr as per your suggestion above.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this commented part?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added these as it makes diagnosing problems easier. Right now I've commented them out for the PR but I'm enabling these while testing locally. I would vote to keep some diagnostic prints in some fashion (maybe we can enable them conditionally based on environment variables). If you think we don't need them then I'll remove them just before we merge.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking the code down below that is used to emit MVC instruction, I believe you are using wrong value here as maxBytes. MVC can handle 256 (1 byte used for L field, so 0-255 bytes) bytes at a time. So your calculation for loop and residue also needed to be changed.

@dchopra001 dchopra001 Aug 9, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in revisiting this area I have a couple of observations:

  • I don't think the issue mentioned above created any functional issues because the displacement integer calculation accounted for this discrepancy. This probably explains why I didn't see any failures in my test case because I do have edge cases for this scenario. Anyway, you're right that we should set maxBytes to 256. I'll have this fixed in the next commit.

  • There is however a bug if we try to copy a structure larger than 4096 bytes. This is because the displacement integer in the instruction is only 12 bits in length. So in scenarios where we have to copy more than 4096 bytes of data, I propose the following solution:

    • Copy the structure using consecutive MVC instruction for the first 4096 bytes
    • If there's more than 4096 bytes, then copy the data over using an MVCLE instruction (edited)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch about the 4096 bytes,
Your proposed solution means we will use 16 MVCs and for the remaining bytes use MCVL/MVCLE . Means we will use 100 bytes of instructions with that. Apart from that, you have to emit instruction to increase both source and destination by 4096 and also load the remaining bytes in the register for MVCLE.
Another caveat with using MVCLE looking at the PoPs, you would have to also insert branch instruction to account for interruption in copying from source to destination to account for operation completion because of CPU determined number of bytes have been moved without reaching the end of the first operand in which case condition code would be set.
We should gauge if this is beneficial or simple loop of MVC (MVC, update src, update dst, check residue is less than 256 and branch back).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I need to confirm this from the instruction documents but the comment in [1] suggests a very high threshold should be used for deciding MVCL.

[1]. https://github.com/eclipse/omr/blob/e26f652a53fef350a52dd999f2b1ce9a422ab2f0/compiler/z/codegen/OMRTreeEvaluator.cpp#L12813-L12818

@dchopra001 dchopra001 Aug 15, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This routine is now split into two here. One copies small structs using upto 16 MVC instructions, and the other one copies larger structs in a loop with 16 MVC instructions at a time. I believe I'm missing one more corner case here. I'll update this comment once I've addressed that.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment needs some clarification on why 23 or why size/256 + 2

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comments around how this is calculated and updated this area as well: 193521ffdae93a7ae820818ab09473e47b0b2a9b

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are not using roundedCodeSize later on this code, we can get rid of using a new variable and combine two lines here to set thunkSize.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fixed now.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use LAY to update the C Stack pointer instead of AGFI?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LAY instructions being used here: 68ef0623c789bfab3d1dcff2dc46fee881686cbf

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this constant initialization for stack frame needs a comment. Perhaps a general comment laying out stack frame would increase readability of the code.

@dchopra001 dchopra001 Aug 26, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments around magic constants here: 6e50a19f91bf9b540b7fd894d891a7b8a9f08aa3

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not return after this point right? In this case, we should use BCR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fixed in: c54d5bd7f1575e9af8aa7f5c2fd37c396ce8be05

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment or description on how the MVC loop works and residue is handled should be added here. Also is it possible to refactor the small struct copy and large struct copy and let single function handle both scenarios?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments Should be added in aed112117b9b089ce249283522afb353c7ac1c6e.

I would propose to keep the routines separated. There isn't as much duplicate code between the two and combining them together will require a bunch of if guards that will add additional complexity.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means the function is fixed to use R2 as source register and R4 as destination register deeming it to be used in specific locations only. Should this be parameterized to allow it to be used if we ever have to copy with different source and destination register?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the source and destination register operands are parametrized for the struct copy routines: 109cbe6452e154afe9686653d3693605391b8423

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be overflowed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fixed now.

@joransiu

Copy link
Copy Markdown
Member

jenkins test sanity zlinux jdk8,jdk19

@r30shah

r30shah commented Aug 24, 2022

Copy link
Copy Markdown
Contributor

@joransiu any concerns/review with this change? Since @dchopra001 have tested the changes with different test case, and the sanity test passes, should we attempt merging this one?

@joransiu

Copy link
Copy Markdown
Member

@joransiu any concerns/review with this change? Since @dchopra001 have tested the changes with different test case, and the sanity test passes, should we attempt merging this one?

I would like to see if @dchopra001 will get a chance to address the few minor outstanding comments, and squash the commits.

@zl-wang / @ChengJin01 Do we have any specific tests we should trigger to exercises these paths?

@ChengJin01

ChengJin01 commented Aug 24, 2022

Copy link
Copy Markdown

@joransiu, @dchopra001 should be able to verify this PR locally with his own tests specific to Z at https://github.com/dchopra001/ffiUpCallTests (he already verified with my test suites locally with my branch). In terms of generic tests suites for upcall, our test cases at #15310 have not yet merged to the repo as we have not yet finished the code review for all PRs listed at #15068. So there is no way to directly verify with these test suites for the moment due to the incomplete code in the repo.

@ChengJin01

Copy link
Copy Markdown

@dchopra001 & @joransiu, could this PR be merged if there is no more concern to be addressed?

@r30shah r30shah left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor mixed up in the license header text, apart from that, I am OK with most of the changes made by @dchopra001. @ChengJin01 What is the timeline within which we need to get this change in?

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated
@ChengJin01

Copy link
Copy Markdown

@ChengJin01 What is the timeline within which we need to get this change in?

@r30shah, we should have sufficient time for this PR but I'd like to have it merged as soon as possible if we could. So I will need to double-check with jtreg tests to see whether there is any platform specific issue to be addressed on zLinux.

@dchopra001

Copy link
Copy Markdown
Contributor Author

I did test the last time I pushed code changes to this branch a week or two ago. Just to be sure I'm doing a clean build and will test again. I'll update here once the build is done and the tests are clear.

@joransiu joransiu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. One minor comment nit.

Comment thread runtime/vm/xz64/UpcallThunkGen.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop concludes where... may be better worded as The loop concludes when...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should now be fixed in the final squashed commit.

@joransiu

joransiu commented Sep 6, 2022

Copy link
Copy Markdown
Member

Not sure why the Copyright check and Line Ending checks suddenly flagged issues in other files, and shows conflicts on merge. @dchopra001 , can you squash and rebase?

@zl-wang / @r30shah : Any other comments/feedback?

@ChengJin01

Copy link
Copy Markdown

@AdamBrousseau, is there any update in terms of the Copyright & Line Endings Check ? It seems the failing lines listed in there totally have nothing to do with this PR.

@AdamBrousseau

Copy link
Copy Markdown
Contributor

@ChengJin01 the checks don't work properly when there are merge conflicts in the PR. Once they are resolved, the check should return proper results.

@ChengJin01

Copy link
Copy Markdown

@dchopra001, please double-check whether there is any conflict to be resolved in this PR.

@r30shah

r30shah commented Sep 6, 2022

Copy link
Copy Markdown
Contributor

@zl-wang / @r30shah : Any other comments/feedback?

Waiting for @dchopra001 's comment for verifying testing. Overall, changes looks good to me.

@dchopra001
dchopra001 force-pushed the ffiZ_final branch 2 times, most recently from c555c03 to 6fd85fe Compare September 6, 2022 20:58
Signed-off-by: Dhruv Chopra <Dhruv.C.Chopra@ibm.com>
@dchopra001

Copy link
Copy Markdown
Contributor Author

Merge conflicts resolved and all my local tests have passed as well.

@joransiu

joransiu commented Sep 6, 2022

Copy link
Copy Markdown
Member

jenkins test sanity zlinux jdk8,jdk19

@zl-wang zl-wang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

@joransiu
joransiu merged commit 8186943 into eclipse-openj9:master Sep 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants