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

[RISC-V] Simplify flags for passing struct in registers #100080

Merged
merged 18 commits into from
Apr 18, 2024

Conversation

tomeksowi
Copy link
Contributor

@tomeksowi tomeksowi commented Mar 21, 2024

Discussion in #99602 pointed out that MethodTable::GetRiscV64PassStructInRegisterFlags grew too complicated to properly compare against its crossgen2 version in RISCV64PassStructInRegister.cs (and understand in general). This PR simplifies it (LOC count reduced by ~70%) by following the wording of RISC-V ABI more closely, in particular by introducing an explicit step for struct field hierarchy flattening which the ABI enregistration rules are based on.

As a result of the re-write, MethodTable::GetRiscV64PassStructInRegisterFlags started returning correct flags (pass in float+integer registers) for the two cases:

  • struct { float; struct { string; }; } - the wrapper struct for string (one pointer) shouldn't matter
  • struct { float; struct {/*empty*/}; int; } - empty struct fields should be ignored

RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags was also re-written to match MethodTable::GetRiscV64PassStructInRegisterFlags.

This PR also fixes assertions in fgMorphMultiregStructArg when crossgen2 compiles in Debug build with --verify-type-and-field-layout option. Error messages:

/runtime/src/coreclr/jit/morph.cpp:3766
Assertion failed 'roundUp(structSize, TARGET_POINTER_SIZE) == roundUp(loadExtent, TARGET_POINTER_SIZE)' in ...
  • System.Private.CoreLib.dll: ValueType which contains two double fields
  • ./Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp/MarshalStructAsLayoutExp: ValueType which contains FieldOffset.

Part of #84834, cc @dotnet/samsung

clamp03 and others added 4 commits March 20, 2024 11:02
Asserts in `./Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp/MarshalStructAsLayoutExp.sh`
Error message is
`Assertion failed 'roundUp(structSize, TARGET_POINTER_SIZE) == roundUp(loadExtent, TARGET_POINTER_SIZE)' in 'Managed:MarshalStructAsParam_AsExpByVal(int)' during 'Morph - Global' (IL size 2208; hash 0x9fd9734a; MinOpts)`
Copied missed codes of GetRiscV64PassStructInRegiste in vm to crossgen2
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Mar 21, 2024
@tomeksowi tomeksowi force-pushed the fixstructinfo branch 2 times, most recently from c5b27c4 to 1d6f53a Compare March 26, 2024 09:06
@tomeksowi
Copy link
Contributor Author

tomeksowi commented Mar 28, 2024

Alright, I think the bulk of work is done. CLR tests are looking ok. I also ran some tests with RunCrossGen2=1, interop- and ABI-related tests are passing and a full CLR test run with RunCrossGen2=1 is under way which will take some time.

@tomeksowi
Copy link
Contributor Author

a full CLR test run with RunCrossGen2=1 is under way

Too many crashes. The discrepancies in GetRISCV64PassStructInRegisterFlags I seen so far seem not to be the cause (new version returns better flags IMHO):

[S.P.CoreLib]System.Collections.Generic.KeyValuePair`2<System.__Canon,float64>: old(STRUCT_NO_FLOAT_FIELD), new(STRUCT_FLOAT_FIELD_SECOND, STRUCT_HAS_8BYTES_FIELDS_MASK)
[S.P.CoreLib]System.Runtime.InteropServices.NFloat: old(STRUCT_FLOAT_FIELD_ONLY_ONE), new(STRUCT_FLOAT_FIELD_ONLY_ONE, STRUCT_FIRST_FIELD_SIZE_IS8)

I'll solve the crashes when I return from vacation.

@tomeksowi tomeksowi changed the title [RISC-V] WiP: Simplify flags for passing struct in registers [RISC-V] Simplify flags for passing struct in registers Apr 9, 2024
@tomeksowi
Copy link
Contributor Author

I'll solve the crashes when I return from vacation.

CLR tests with RunCrossGen2=1 look good now. PR ready for review.

@tomeksowi tomeksowi marked this pull request as ready for review April 9, 2024 13:02
@tomeksowi
Copy link
Contributor Author

I played around more with empty structs and they need some more work. Namely, we assume in quite a few places that only structs no bigger than 16 bytes can be passed in registers, which is not true for e.g. struct { double; struct {}; long; }. However, empty structs are rather niche so I'll go down that rabbit hole in another PR to make review of this one easier and sooner.

@jkotas jkotas requested a review from a team April 10, 2024 09:52
Copy link
Member

@sirntar sirntar left a comment

Choose a reason for hiding this comment

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

LGTM

@tomeksowi
Copy link
Contributor Author

@MichalStrehovsky @jkotas could you review please?

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

LGTM. Nice simplification!


if ((numIntroducedFields == 0) || (numIntroducedFields > 2) || (typeDesc.GetElementSize().AsInt > 16))
private static bool FlattenFieldTypes(TypeDesc td, StructFloatFieldInfoFlags[] types, ref int typeIndex)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
private static bool FlattenFieldTypes(TypeDesc td, StructFloatFieldInfoFlags[] types, ref int typeIndex)
private static bool FlattenFieldTypes(TypeDesc td, Span<StructFloatFieldInfoFlags> types, ref int typeIndex)

Can this use span and stackallocated space in the caller to avoid GC allocations?

Copy link
Contributor Author

@tomeksowi tomeksowi Apr 18, 2024

Choose a reason for hiding this comment

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

Sure.

There's still more opportunities, like use a single ref to struct { StructFloatFieldInfoFlags[2] types; int index; } (or even pass & return by value, it's so small). Since I'll be revisiting this in another PR to handle corner-cases with empty fields, I'll probably do sth like this in both C# and C++ while at it.

@jkotas jkotas merged commit 691e31e into dotnet:main Apr 18, 2024
92 checks passed
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
* [RISC-V] Fix struct info value in crossgen2

* [RISC-V] Fix assertion in crossgen

Asserts in `./Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp/MarshalStructAsLayoutExp.sh`
Error message is
`Assertion failed 'roundUp(structSize, TARGET_POINTER_SIZE) == roundUp(loadExtent, TARGET_POINTER_SIZE)' in 'Managed:MarshalStructAsParam_AsExpByVal(int)' during 'Morph - Global' (IL size 2208; hash 0x9fd9734a; MinOpts)`
Copied missed codes of GetRiscV64PassStructInRegiste in vm to crossgen2

* Check size in GetRiscV64PassStructInRegisterFlags early, use named constant

* Simplify managed branch of GetRiscV64PassStructInRegisterFlags

* Fix assert IsPrimitiveType for 2nd field

* Handle empty structs

* Apply FIELD_SIZE_IS8 flags only when there's at least one float

* Handle empty array struct elements

* Enregister any field type <= 8 bytes, not just primitives; i.e. pointers and refs are also OK

* Simplify native layout branch of GetRiscV64PassStructInRegisterFlags

* Rewrite native branch to look at only at native layout info

* Calculate flags already in GetFlattenedFieldTypes to avoid returning fake CorElementTypes from native branch

* Ignore empty structs during field flattenting because RISC-V calling convention tells us to

* Simplify crossgen2 GetRISCV64PassStructInRegisterFlags, make C++ and C# versions of this method look more alike

* Remove early exit if nFields == 0 because it wasn't doing much, the loop won't do any work if there's no fields

* Return early from HasImpliedRepeatedFields. GetApproxFieldDescListRaw() is null on empty structs, which crashes pFieldStart->GetFieldType()

* Cleanup GetRiscV64PassStructInRegisterFlags call sites

* Stackalloc field types to avoid GC allocations

---------

Co-authored-by: Dong-Heon Jung <clamp03@gmail.com>
@github-actions github-actions bot locked and limited conversation to collaborators May 19, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
arch-riscv Related to the RISC-V architecture area-crossgen2-coreclr community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants