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

Check HFA alignment for valuetype fields #106099

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions docs/workflow/testing/coreclr/running-arm32-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Running ARM32 tests on modern hardware

One of our supported targets is 32-bit ARM. It can be quite challenging to construct a realistic ARM32 environment where you can build or run tests in a reasonable amount of time. Thankfully, it's possible to configure an ARM64 linux environment so that you can cross-build from ARM64 to ARM32, and run tests there using native hardware support instead of software emulation. This is not possible on ARM64-based Windows (this functionality is not offered by the OS).

## Configuring your ARM64 environment to run ARM32 binaries

By default your ARM64 Linux install probably doesn't have support for ARM32 binaries enabled, which will cause running the binaries to fail with a cryptic error. So you'll need to add the architecture to dpkg and install some core userspace libraries that CoreCLR will need to actually run your tests, i.e.:

```bash
$ sudo dpkg --add-architecture armhf

$ sudo apt-get update
Reading package lists... Done

$ sudo apt-get install libc6:armhf libstdc++6:armhf libicu74:armhf
The following additional packages will be installed:
```

Note that when installing a package for another architecture, you need to suffix the package name with the architecture name. For me, the three packages above were sufficient to run an ARM32 JIT test.

## Cross-building for ARM32 on ARM64

Follow the steps from https://github.com/dotnet/runtime/blob/main/docs/workflow/building/coreclr/cross-building.md#linux-cross-building as-is. You should end up with a `linux.arm` `Core_Root` and test artifacts.

## Running an ARM32 test in your ARM64 environment

We're finally ready to go, probably. Export an environment variable to point to your ARM32 core root, and then run your test, i.e.:

```bash
$ export CORE_ROOT=/home/kg/runtime/artifacts/tests/coreclr/linux.arm.Release/Tests/Core_Root/

$ bash artifacts/tests/coreclr/linux.arm.Release/JIT/Directed/StructABI/StructABI/StructABI.sh
BEGIN EXECUTION
/home/kg/runtime/artifacts/tests/coreclr/linux.arm.Release/Tests/Core_Root//corerun -p System.Reflection.Metadata.MetadataUpdater.IsSupported=false -p System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization=true StructABI.dll ''
Issue80393_HFA failed. Retval: f1=1 f3=3
```
23 changes: 23 additions & 0 deletions src/coreclr/vm/class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1896,6 +1896,29 @@ EEClass::CheckForHFA()
fieldHFAType = pFD->LookupApproxFieldTypeHandle().AsMethodTable()->GetHFAType();
#endif
}

int requiredAlignment;
switch (fieldHFAType)
{
case CORINFO_HFA_ELEM_FLOAT:
requiredAlignment = 4;
break;
case CORINFO_HFA_ELEM_VECTOR64:
case CORINFO_HFA_ELEM_DOUBLE:
requiredAlignment = 8;
break;
case CORINFO_HFA_ELEM_VECTOR128:
requiredAlignment = 16;
break;
default:
// VT without a valid HFA type inside of this struct means this struct is not an HFA
return false;
}

if (requiredAlignment && (pFD->GetOffset() % requiredAlignment != 0)) // HFAs don't have unaligned fields.
{
return false;
}
}
break;

Expand Down
43 changes: 43 additions & 0 deletions src/tests/JIT/Directed/StructABI/StructABI.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,49 @@ struct Nested9
struct InlineArray4 Field2;
};

struct Issue80393_S_Doubles
{
double f1;
double f3;
};

// We need to apply 1-byte packing to these structs to get the exact alignment we want, but we
// don't want to apply packing to the union or 2-doubles struct because it will change the natural
// alignment of the union and as a result alter which registers it's assigned to by clang, which
// won't match what CoreCLR does.
#pragma pack(push, 1)
struct Issue80393_F2
{
double value;
};

struct Issue80393_F2_Offset {
// 3 padding bytes to approximate C# FieldOffset of 3.
// This padding prevents the outer union from being treated as an HVA/HFA by clang for either arm32 or arm64.
char padding[3];
struct Issue80393_F2 F2;
};
#pragma pack(pop)

union Issue80393_S {
struct Issue80393_S_Doubles f1_f3;
struct Issue80393_F2_Offset f2;
};

// NOTE: If investigating this in isolation, make sure you set -mfloat-abi=hard -mfpu=neon when building for arm32
DLLEXPORT union Issue80393_S Issue80393_HFA(union Issue80393_S value)
{
// Simply doing 'return value' like most of these other functions isn't enough to exercise everything, because
// depending on the calling convention it can turn the whole function into a no-op, where 'value' flows in
// via the same registers that the result flows out through.
union Issue80393_S result;
// Use the value argument as part of the result so we can tell whether it was passed in correctly, in addition
// to checking whether the return value was passed correctly back to C#.
result.f1_f3.f1 = 1.0 + value.f1_f3.f1;
result.f1_f3.f3 = 3.0 + value.f1_f3.f3;
return result;
}

DLLEXPORT struct SingleByte EchoSingleByte(struct SingleByte value)
{
return value;
Expand Down
Loading
Loading