Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

[Arm64] For reflection invoke, use ArgLocDesc#10257

Merged
jashook merged 1 commit intodotnet:masterfrom
jashook:reflection_invoke_arm64
Apr 19, 2017
Merged

[Arm64] For reflection invoke, use ArgLocDesc#10257
jashook merged 1 commit intodotnet:masterfrom
jashook:reflection_invoke_arm64

Conversation

@jashook
Copy link

@jashook jashook commented Mar 17, 2017

For arm64 if the struct needs to be passed in registers, then pass
that information along when we do the reflection invoke.

Note this adds more test cases for passing different struct types via reflection.
I am guessing similar work would need to be done for Arm32.

@jashook jashook force-pushed the reflection_invoke_arm64 branch 2 times, most recently from c74ab3e to 870d324 Compare March 17, 2017 17:25
@jashook
Copy link
Author

jashook commented Mar 17, 2017

Fixes #10107

@jashook jashook force-pushed the reflection_invoke_arm64 branch from 870d324 to 9788f94 Compare March 17, 2017 17:29
@jashook
Copy link
Author

jashook commented Mar 17, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@jashook
Copy link
Author

jashook commented Mar 17, 2017

@rahku @janvorli @jkotas ptal

/cc @dotnet/arm64-contrib @RussKeldorph

@jashook
Copy link
Author

jashook commented Mar 17, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@jashook jashook force-pushed the reflection_invoke_arm64 branch from 9788f94 to 906d03e Compare March 17, 2017 17:41
@jashook
Copy link
Author

jashook commented Mar 17, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@sdmaclea
Copy link

@jashook #10107 looks like it is a pri1 test to me. I have not been running these yet. Are there a significant number of pri1 fails? Should I be looking at these for Arm64/Unix? The default unix test documentation does not really recommend them on unix. Please advise.

m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
m_argLocDescForStructInRegs.m_isSinglePrecision = isSinglePrecision;
Copy link

Choose a reason for hiding this comment

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

I understand that you have modelled your changes as done for unix_amd64. I don't really like the way this has been implemented for amd64_unix. I dont think above needs to be done here in GetNextOffset(). GetNextOffset() only returns the offset in TransitionBlock. In order to get ArgLocDesc we have another function GetArgLoc(). So first you get the offset and then use that to get ArgLocDesc. This would also eliminate the need for fields m_hasArgLocDescForStructInRegs & m_argLocDescForStructInRegs in ArgIterator which I think is not required.

Copy link
Member

Choose a reason for hiding this comment

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

@rahku the reason why I have implemented it this way for unix amd64 is that there is no single offset in the TransitionBlock for structs passed in registers on Unix amd64. A struct can be split between upto two floating point and two general purpose registers. In this case, we return a special offset value TransitionBlock::StructInRegsOffset. I have originally refactored the code to let the GetNextOffset return a bitfield that would contain both the floating point registers and general purpose registers offsets, but it turned out to complicate code in many callers of the function and so it was decided during the code review to revert that change and rather use this side storage for these details.

But unless ARM64 can also split a single argument between multiple general purpose and floating point registers, I agree with you that we should use a single offset like we do e.g. for AMD64 on Windows.

}

#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) || defined(_TARGET_ARM64_)
Copy link

Choose a reason for hiding this comment

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

changing above to
#if (defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)) || defined(TARGET_ARM64)
will make it clear what the real conditions are

#if defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
#if defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) || defined(_TARGET_ARM64_)
ArgLocDesc m_argLocDescForStructInRegs;
bool m_hasArgLocDescForStructInRegs;
Copy link

Choose a reason for hiding this comment

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

I don't think this is a good design. I would not do it for arm64. I have explained details below.

@rahku
Copy link

rahku commented Mar 17, 2017

Thanks for debugging and fixing this.

@jashook jashook force-pushed the reflection_invoke_arm64 branch from 906d03e to c75bfa3 Compare March 20, 2017 16:26
@jashook
Copy link
Author

jashook commented Mar 20, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@jashook
Copy link
Author

jashook commented Mar 20, 2017

@sdmaclea for correctness testing we should be trying to run as many tests as we can, both Pri1 and Pri0.

#endif // (UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING) || _TARGET_ARM64_

#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)

Copy link

Choose a reason for hiding this comment

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

shouldn't function "void ZeroStructInRegisters(int fieldBytes)" also be moved out of ifdef arm64

Copy link
Author

Choose a reason for hiding this comment

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

Addressed

@rahku
Copy link

rahku commented Mar 21, 2017

LGTM otherwise

}
#endif

#ifdef _TARGET_ARM64_
Copy link
Member

Choose a reason for hiding this comment

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

Why is this different for ARM64? It seems you can make it the same and then you would not need the change in the argdestination.h in IsStructPassedInRegs.

Copy link

Choose a reason for hiding this comment

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

Are you suggesting to add fields m_hasArgLocDescForStructInRegs & m_argLocDescForStructInRegs in ArgIterator for arm64 as well? These are not required for any other architecture except unix amd64. It is possible on arm64 to get ArgLocDesc with just the offset address. This is how it is for other architectures as well you first get the offset and then get ArgLocDesc. As you said that on amd64 unix that is not possible so I would let the special case be just for amd64 unix and let the logic for arm64 be same as other arch.

Copy link
Member

Choose a reason for hiding this comment

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

No, I was suggesting that to keep the code uniform for all platforms. All platforms now have ArgIterator::GetArgLocDescForStructInRegs and it just always returns NULL on non-amd64. So it seems strange to deviate from that for ARM64.

{
LIMITED_METHOD_CONTRACT;
#if defined(_TARGET_ARM64_)
return m_argLocDescForStructInRegs != NULL;
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand why we need to handle this differently on ARM64 and why NULL m_argLocDescForStructInRegs cannot indicate that there is no struct passed in regs.

Copy link

Choose a reason for hiding this comment

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

for amd64 we seem to always be setting m_argLocDescForStructInRegs for all arguments irrespective of whether the offset corresponds to struct_in_regs....which i don't think is right. You could refactor amd64 unix code to not do that and then this can be made same for both arch.

Copy link
Member

Choose a reason for hiding this comment

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

That's not the case. It is set only for structs in registers, for others, it is NULL. The ArgDestination::m_argLocDescForStructInRegs is set in the constructor and we pass ArgIterator::GetArgLocDescForStructInRegs result to the constructor. And the ArgIterator::GetArgLocDescForStructInRegs looks like this:

    ArgLocDesc* GetArgLocDescForStructInRegs()
    {
#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
        return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL;
#else
        return NULL;
#endif
    }

So it only returns non-NULL if the argument is a struct passed in registers.

Copy link

Choose a reason for hiding this comment

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

by "you could" i meant "one would need to "

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand your comment. I've just shown that we are not setting m_argLocDescForStructInRegs for all arguments.

Copy link

Choose a reason for hiding this comment

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

never mind regarding the above comment. Yes I now see that you re-initialize m_hasArgLocDescForStructInRegs to be false at the start of every call to ArgIterator::NextOffset(). So yes this function can be made to
return m_argLocDescForStructInRegs != NULL;
for all applicable arch (amd64 & arm64)

if (typeFloat)
{
// Zero out memory before copying.
memset(dest, 0, floatRegCount * sizeof(double));
Copy link
Member

Choose a reason for hiding this comment

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

Why do we zero the memory when we use 64 bit stores in the loop below that overwrite all the zeroed bytes anyways?

Copy link
Author

Choose a reason for hiding this comment

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

This was mostly used when debugging, thank you for catching it. Will remove.

@jashook jashook force-pushed the reflection_invoke_arm64 branch 2 times, most recently from 872d225 to efeb173 Compare April 6, 2017 17:44
@jashook
Copy link
Author

jashook commented Apr 6, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@rahku
Copy link

rahku commented Apr 6, 2017

@jashook as discussed can you decouple this from FEATURE_UNIX_AMD64_STRUCT_PASSING so that it is evident that this only for HFA's. We can look at merging with FEATURE_UNIX_AMD64_STRUCT_PASSING later.

@jashook jashook force-pushed the reflection_invoke_arm64 branch from efeb173 to d4245d1 Compare April 11, 2017 16:34
@jashook
Copy link
Author

jashook commented Apr 11, 2017

@rahku ptal the change has been done as discussed.

@jashook
Copy link
Author

jashook commented Apr 11, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@jashook jashook force-pushed the reflection_invoke_arm64 branch 2 times, most recently from 8e2af8e to 06e2dcb Compare April 11, 2017 17:08
#endif

#ifdef _TARGET_ARM64_
ArgLocDesc argLocDescForStructInRegs;
Copy link
Member

Choose a reason for hiding this comment

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

I still wonder why we have to differ for ARM64 here. Based on the IsHFA function above, the ArgDestination should have the m_argLocDescForStructInRegs set to NULL for non-HFA args. So if you used the original code at this place and just made sure that m_hasArgLocDescForStructInRegs in the ArgIterator is set only for HFA args, it seems it should work.

Copy link

Choose a reason for hiding this comment

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

Yes and that seems like unification with unix amd64 which we decided to do later and not as part of this work.

Copy link
Member

Choose a reason for hiding this comment

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

@jashook I am sorry for the delay, I was OOF for the last four days. Looking at the change again, I am not sure I understand one thing related to my last comment here. It seems that IsHFA now always returns true, since you always pass the ArgDestination constructor a non-null argLocDescForStructInRegs argument. Is that correct?

@jashook
Copy link
Author

jashook commented Apr 11, 2017

@dotnet-bot test Windows_NT Arm64 Checked

1 similar comment
@jashook
Copy link
Author

jashook commented Apr 12, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@jashook
Copy link
Author

jashook commented Apr 12, 2017

@dotnet-bot test Ubuntu16.04 arm Cross Debug Build

@jashook
Copy link
Author

jashook commented Apr 12, 2017

@dotnet-bot test Ubuntu arm Cross Release Build

@jashook jashook force-pushed the reflection_invoke_arm64 branch from 06e2dcb to 3674e1a Compare April 12, 2017 19:50
@jashook
Copy link
Author

jashook commented Apr 12, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@jashook
Copy link
Author

jashook commented Apr 12, 2017

@rahku @janvorli ping

@rahku
Copy link

rahku commented Apr 12, 2017

Looks good

@jashook
Copy link
Author

jashook commented Apr 13, 2017

ping @janvorli

1 similar comment
@jashook
Copy link
Author

jashook commented Apr 14, 2017

ping @janvorli

@RussKeldorph RussKeldorph changed the title [Arm64] For relection invoke, use ArgLocDesc [Arm64] For reflection invoke, use ArgLocDesc Apr 18, 2017
@jashook jashook force-pushed the reflection_invoke_arm64 branch from 3674e1a to a7e677d Compare April 18, 2017 20:30
@jashook
Copy link
Author

jashook commented Apr 18, 2017

@janvorli @rahku @gkhanna79 ptal. I have modified arm64 to use m_argLocDescForStructInRegs based on @janvorli and @gkhanna79's suggestions.

/cc @RussKeldorph

@jashook
Copy link
Author

jashook commented Apr 18, 2017

@dotnet-bot test Windows_NT Arm64 Checked

int cbArg = StackElemSize(argSize);
int cArgSlots = cbArg / STACK_ELEM_SIZE;

auto setupArgLocDesc = [argType, &thValueType, this](int argOfs)
Copy link
Member

Choose a reason for hiding this comment

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

It seems that you can do even this the way it is done for amd64. Instead of having this lambda and calling it from three code paths below, it seems you can just add code to initialize the m_argLocDescForStructInRegs inside the if (thValueType.IsHFA()) under the case ELEMENT_TYPE_VALUETYPE above.
At that place, instead of translating the just generated offset back to the specific m_idxXXXReg, you would just call m_argLocDescForStructInRegs.Init() and set the m_argLocDescForStructInRegs.m_idxFloatReg to m_idxFPReg and m_argLocDescForStructInRegs.m_cFloatReg to cFPRegs. Since the lambda below sets the m_argLocDescForStructInRegs for HFA only, it should get the equivalent result.
It looks the change would also be simpler. I hope I have not missed something.

@jashook jashook force-pushed the reflection_invoke_arm64 branch from a7e677d to 9ccbaf7 Compare April 18, 2017 23:04
@jashook
Copy link
Author

jashook commented Apr 18, 2017

@dotnet-bot test Windows_NT Arm64 Checked

@jashook
Copy link
Author

jashook commented Apr 18, 2017

Thank you for the review @janvorli

Copy link
Member

@janvorli janvorli left a comment

Choose a reason for hiding this comment

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

LGTM, thank you!

@rahku
Copy link

rahku commented Apr 18, 2017

Thanks for getting this through

@janvorli
Copy link
Member

@jashook the Windows ARM64 build has failed in the passing struct with floats. Looking at the change again, it seems that you are missing settings m_argLocDescForStructInRegs.m_isSinglePrecision at the place where you set the m_argLocDescForStructInRegs.m_cFloatReg and m_argLocDescForStructInRegs.m_idxFloatReg.

17:28:30 Error: passing struct with floats via reflection. Callee received [1.2, 5.6, 0, 0] instead of [1.2, 3.4, 5.6, 7.8]
17:28:30 Expected: 100
17:28:30 Actual: 0
17:28:30 END EXECUTION - FAILED
17:28:30 FAILED

For arm64 if the struct needs to be passed in registers, then pass
that information along when we do the reflection invoke.

Note this adds more test cases for passing different struct types via reflection.
I am guessing similar work would need to be done for Arm32.
@jashook jashook force-pushed the reflection_invoke_arm64 branch from 9ccbaf7 to aa0eee4 Compare April 19, 2017 19:29
m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;

m_argLocDescForStructInRegs.m_isSinglePrecision = isFloatType;
Copy link
Author

Choose a reason for hiding this comment

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

The type information was missing.

@jashook
Copy link
Author

jashook commented Apr 19, 2017

@dotnet-bot test Windows_NT Arm64 Checked.

I have already run testing on these changes here; however, to be pedantic I will run them one last time before checking in.

@jashook
Copy link
Author

jashook commented Apr 19, 2017

Test failure is unrelated and requires a test drop update.

@jashook jashook merged commit 9b9989d into dotnet:master Apr 19, 2017
@jashook jashook deleted the reflection_invoke_arm64 branch April 19, 2017 23:04
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants