Skip to content

Commit 5a7ac76

Browse files
authored
Check HFA alignment for valuetype fields (#106099)
Fixes Issue 80393
1 parent 0b0b4b2 commit 5a7ac76

File tree

4 files changed

+180
-44
lines changed

4 files changed

+180
-44
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Running ARM32 tests on modern hardware
2+
3+
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).
4+
5+
## Configuring your ARM64 environment to run ARM32 binaries
6+
7+
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, which on Debian or Ubuntu will look like:
8+
9+
```bash
10+
$ sudo dpkg --add-architecture armhf
11+
12+
$ sudo apt-get update
13+
Reading package lists... Done
14+
15+
$ sudo apt-get install libc6:armhf libstdc++6:armhf libicu74:armhf
16+
The following additional packages will be installed:
17+
```
18+
19+
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.
20+
21+
## Cross-building for ARM32 on ARM64
22+
23+
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.
24+
25+
## Running an ARM32 test in your ARM64 environment
26+
27+
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.:
28+
29+
```bash
30+
$ export CORE_ROOT=/home/kg/runtime/artifacts/tests/coreclr/linux.arm.Release/Tests/Core_Root/
31+
32+
$ bash artifacts/tests/coreclr/linux.arm.Release/JIT/Directed/StructABI/StructABI/StructABI.sh
33+
BEGIN EXECUTION
34+
/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 ''
35+
Issue80393_HFA failed. Retval: f1=1 f3=3
36+
```

src/coreclr/vm/class.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,29 @@ EEClass::CheckForHFA()
18631863
fieldHFAType = pFD->LookupApproxFieldTypeHandle().AsMethodTable()->GetHFAType();
18641864
#endif
18651865
}
1866+
1867+
int requiredAlignment;
1868+
switch (fieldHFAType)
1869+
{
1870+
case CORINFO_HFA_ELEM_FLOAT:
1871+
requiredAlignment = 4;
1872+
break;
1873+
case CORINFO_HFA_ELEM_VECTOR64:
1874+
case CORINFO_HFA_ELEM_DOUBLE:
1875+
requiredAlignment = 8;
1876+
break;
1877+
case CORINFO_HFA_ELEM_VECTOR128:
1878+
requiredAlignment = 16;
1879+
break;
1880+
default:
1881+
// VT without a valid HFA type inside of this struct means this struct is not an HFA
1882+
return false;
1883+
}
1884+
1885+
if (requiredAlignment && (pFD->GetOffset() % requiredAlignment != 0)) // HFAs don't have unaligned fields.
1886+
{
1887+
return false;
1888+
}
18661889
}
18671890
break;
18681891

src/tests/JIT/Directed/StructABI/StructABI.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,49 @@ struct Nested9
221221
struct InlineArray4 Field2;
222222
};
223223

224+
struct Issue80393_S_Doubles
225+
{
226+
double f1;
227+
double f3;
228+
};
229+
230+
// We need to apply 1-byte packing to these structs to get the exact alignment we want, but we
231+
// don't want to apply packing to the union or 2-doubles struct because it will change the natural
232+
// alignment of the union and as a result alter which registers it's assigned to by clang, which
233+
// won't match what CoreCLR does.
234+
#pragma pack(push, 1)
235+
struct Issue80393_F2
236+
{
237+
double value;
238+
};
239+
240+
struct Issue80393_F2_Offset {
241+
// 3 padding bytes to approximate C# FieldOffset of 3.
242+
// This padding prevents the outer union from being treated as an HVA/HFA by clang for either arm32 or arm64.
243+
char padding[3];
244+
struct Issue80393_F2 F2;
245+
};
246+
#pragma pack(pop)
247+
248+
union Issue80393_S {
249+
struct Issue80393_S_Doubles f1_f3;
250+
struct Issue80393_F2_Offset f2;
251+
};
252+
253+
// NOTE: If investigating this in isolation, make sure you set -mfloat-abi=hard -mfpu=neon when building for arm32
254+
DLLEXPORT union Issue80393_S Issue80393_HFA(union Issue80393_S value)
255+
{
256+
// Simply doing 'return value' like most of these other functions isn't enough to exercise everything, because
257+
// depending on the calling convention it can turn the whole function into a no-op, where 'value' flows in
258+
// via the same registers that the result flows out through.
259+
union Issue80393_S result;
260+
// Use the value argument as part of the result so we can tell whether it was passed in correctly, in addition
261+
// to checking whether the return value was passed correctly back to C#.
262+
result.f1_f3.f1 = 1.0 + value.f1_f3.f1;
263+
result.f1_f3.f3 = 3.0 + value.f1_f3.f3;
264+
return result;
265+
}
266+
224267
DLLEXPORT struct SingleByte EchoSingleByte(struct SingleByte value)
225268
{
226269
return value;

0 commit comments

Comments
 (0)