-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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][LoongArch64] Pass structs containing empty struct arrays according to integer calling convention #106266
Conversation
…re floating-point calling convention
…o_DoubleFloatNestedEmpty_InIntegerRegs_RiscV
… Equals from object
RISC-V Release-CLR-QEMU: 9389 / 9390 (99.99%)
Release-CLR-QEMU.md, Release-CLR-QEMU.xml, testclr_output.tar.gz RISC-V Release-FX-QEMU: 727185 / 732507 (99.27%)
Release-FX-QEMU.md, Release-FX-QEMU.xml, testfx_output.tar.gz Build information and linksGIT: # CORE_LIBS_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -s libs /p:EnableSourceLink=false
# CORE_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -s clr+libs+host /p:EnableSourceLink=false
# TESTCLR_BUILD_CMD
runtime/src/tests/build.sh -riscv64 -cross -Release -priority1 -p:UseLocalAppHostPack=true
# TESTCLR_CMD
python3 riscv-CI/goci/agent/TestRunner/run.py --core_root ./coreclr.Release/Tests/Core_Root --testhost ./testhost.Release --atest ./coreclr.Release --test ./ --log_dir ./logs --timeout 2700 --log_level DEBUG
# TESTCLR_RUN
/godata/pipelines/Release-CLR-QEMU/logs/run_tests.log
cd "/_PATH_/_WITH_/_TEST_" && ROOTFS_DIR=/crossrootfs/riscv64 QEMU_LD_PREFIX=/crossrootfs/riscv64 __TestDotNetCmd=/godata/pipelines/Release-CLR-QEMU/testhost.Release/dotnet CORE_ROOT=/godata/pipelines/Release-CLR-QEMU/coreclr.Release/Tests/Core_Root /usr/bin/time -f "exec_time: %e" ./_TEST_BINARY_
# TESTFX_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -rc Release -hc Release -lc Release -s libs.tests --testscope innerloop /p:EnableSourceLink=false /p:UseLocalAppHostPack=true
# TESTFX_CMD
python3 riscv-CI/goci/agent/TestRunner/run.py --corefx --testhost ./testhost.Release --atest ./corefx.Release --log_dir ./logs --timeout 6000 --memlimit 4096 --jobs 16 --log_level DEBUG
# TESTFX_RUN
/go-agent/pipelines/Release-FX-QEMU/logs/run_tests.log
cd "/_PATH_/_WITH_/_TEST_" && ROOTFS_DIR=/crossrootfs/riscv64 QEMU_LD_PREFIX=/crossrootfs/riscv64 __TestDotNetCmd=/go-agent/pipelines/Release-FX-QEMU/testhost.Release/dotnet /usr/bin/time -f "exec_time: %e" /go-agent/pipelines/Release-FX-QEMU/testhost.Release/dotnet exec xunit.console.dll _TEST_BINARY_ -nologo -nocolor -notrait category=failing
|
@MichalStrehovsky @jkotas Can anyone review please? |
@shushanhf could you have a look please? Cc @dotnet/jit-contrib |
Are you implementing the C treatment or C++ treatment in this PR? |
C++. That matches empty structs in .NET which are also sized 1 byte. |
The managed/unmanaged interop should follow C ABI. There are number of differences between C and C++ ABIs, for example #106471. The managed/managed calling convention does not need to follow C ABI. Managed/unmanaged should either follow the C ABI or we should throw PlatformNotSupportedException if it is hard to implement. |
So it's ok for managed/managed calling convention to follow C++ ABI wrt empty structs?
Given empty structs are undefined in C (it's a GCC extension to define them as 0 bytes) and they are defined in .NET as 1 byte like in C++, shouldn't .NET throw PlatformNotSupportedException for anything with an empty struct field in a managed/unmanaged signature? (not just on RISC-V but on any architecture) I'm ok with that, but in that case how to check that managed/managed follows C++ ABI wrt empty structs? Currently I'm using the EmptyStructs test for confronting it against native compilers. |
cc @dotnet/interop-contrib for opinions about interop for empty structs.
It would be best, but it would be a breaking change. I am not sure whether we would want to make this breaking change.
The managed/managed calling convention has several differences from the managed/unmanaged calling convention. We do not have an explicit for the specific managed/managed calling convention details. We just have a tests that validate that all parts of the system agree on the details. I guess it may be ok to keep the test that you have added, but it should have a comment that it is testing undefined behavior. |
RISC-V Release-CLR-QEMU: 9399 / 9400 (99.99%)
Release-CLR-QEMU.md, Release-CLR-QEMU.xml, testclr_output.tar.gz RISC-V Release-FX-QEMU: 670605 / 688189 (97.44%)
Release-FX-QEMU.md, Release-FX-QEMU.xml, testclr_output.tar.gz Build information and linksGIT: # CORE_LIBS_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -s libs /p:EnableSourceLink=false
# CORE_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -s clr+libs+host /p:EnableSourceLink=false
# TESTCLR_BUILD_CMD
runtime/src/tests/build.sh -riscv64 -cross -Release -priority1 -p:UseLocalAppHostPack=true
# TESTCLR_CMD
python3 riscv-CI/goci/agent/TestRunner/run.py --core_root ./coreclr.Release/Tests/Core_Root --testhost ./testhost.Release --atest ./coreclr.Release --test ./ --log_dir ./logs --timeout 2700 --log_level DEBUG
# TESTCLR_RUN
/godata/pipelines/Release-CLR-QEMU/logs/run_tests.log
cd "/_PATH_/_WITH_/_TEST_" && ROOTFS_DIR=/crossrootfs/riscv64 QEMU_LD_PREFIX=/crossrootfs/riscv64 __TestDotNetCmd=/godata/pipelines/Release-CLR-QEMU/testhost.Release/dotnet CORE_ROOT=/godata/pipelines/Release-CLR-QEMU/coreclr.Release/Tests/Core_Root /usr/bin/time -f "exec_time: %e" ./_TEST_BINARY_
# TESTFX_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -rc Release -hc Release -lc Release -s libs.tests --testscope innerloop /p:EnableSourceLink=false /p:UseLocalAppHostPack=true
# TESTFX_CMD
python3 riscv-CI/goci/agent/TestRunner/run.py --corefx --testhost ./testhost.Release --atest ./corefx.Release --log_dir ./logs --timeout 6000 --memlimit 4096 --jobs 16 --log_level DEBUG
# TESTFX_RUN
/go-agent/pipelines/Release-FX-QEMU/logs/run_tests.log
cd "/_PATH_/_WITH_/_TEST_" && ROOTFS_DIR=/crossrootfs/riscv64 QEMU_LD_PREFIX=/crossrootfs/riscv64 __TestDotNetCmd=/go-agent/pipelines/Release-FX-QEMU/testhost.Release/dotnet /usr/bin/time -f "exec_time: %e" /go-agent/pipelines/Release-FX-QEMU/testhost.Release/dotnet exec xunit.console.dll _TEST_BINARY_ -nologo -nocolor -notrait category=failing
RISC-V Release-CLR-QEMU: 9399 / 9400 (99.99%)
Release-CLR-QEMU.md, Release-CLR-QEMU.xml, testclr_output.tar.gz RISC-V Release-FX-QEMU: 670605 / 688189 (97.44%)
Release-FX-QEMU.md, Release-FX-QEMU.xml, testclr_output.tar.gz Build information and linksGIT: # CORE_LIBS_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -s libs /p:EnableSourceLink=false
# CORE_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -s clr+libs+host /p:EnableSourceLink=false
# TESTCLR_BUILD_CMD
runtime/src/tests/build.sh -riscv64 -cross -Release -priority1 -p:UseLocalAppHostPack=true
# TESTCLR_CMD
python3 riscv-CI/goci/agent/TestRunner/run.py --core_root ./coreclr.Release/Tests/Core_Root --testhost ./testhost.Release --atest ./coreclr.Release --test ./ --log_dir ./logs --timeout 2700 --log_level DEBUG
# TESTCLR_RUN
/godata/pipelines/Release-CLR-QEMU/logs/run_tests.log
cd "/_PATH_/_WITH_/_TEST_" && ROOTFS_DIR=/crossrootfs/riscv64 QEMU_LD_PREFIX=/crossrootfs/riscv64 __TestDotNetCmd=/godata/pipelines/Release-CLR-QEMU/testhost.Release/dotnet CORE_ROOT=/godata/pipelines/Release-CLR-QEMU/coreclr.Release/Tests/Core_Root /usr/bin/time -f "exec_time: %e" ./_TEST_BINARY_
# TESTFX_BUILD_CMD
runtime/build.sh --arch riscv64 --cross -c Release -rc Release -hc Release -lc Release -s libs.tests --testscope innerloop /p:EnableSourceLink=false /p:UseLocalAppHostPack=true
# TESTFX_CMD
python3 riscv-CI/goci/agent/TestRunner/run.py --corefx --testhost ./testhost.Release --atest ./corefx.Release --log_dir ./logs --timeout 6000 --memlimit 4096 --jobs 16 --log_level DEBUG
# TESTFX_RUN
/go-agent/pipelines/Release-FX-QEMU/logs/run_tests.log
cd "/_PATH_/_WITH_/_TEST_" && ROOTFS_DIR=/crossrootfs/riscv64 QEMU_LD_PREFIX=/crossrootfs/riscv64 __TestDotNetCmd=/go-agent/pipelines/Release-FX-QEMU/testhost.Release/dotnet /usr/bin/time -f "exec_time: %e" /go-agent/pipelines/Release-FX-QEMU/testhost.Release/dotnet exec xunit.console.dll _TEST_BINARY_ -nologo -nocolor -notrait category=failing
|
What are the reasons for these differences? I'm wondering if they are avoidable as long as the arguments can be represented the same way in .NET and on the native side. One of the reasons I've been pushing in recent PRs for closer compliance with the RISC-V calling convention also for managed/managed is to avoid maintaining separate code paths for the managed/unmanaged calling convention, which we have to support anyway and is well documented. Another reason (for supporting for custom field offsets in FP structs) is of course that custom field padding may also appear without empty structs, e.g. when the struct argument is packed, so it would be valid for interop calls. Empty structs just allow for different (oversized) padding cases that couldn't be achieved with StructLayoutAttribute.Pack, whose effects cannot exceed the default alignment for a type.
Added comment. |
|
I think it'd be best/easiest to treat empty structs as 1-byte. This ensures that there isn't any "weirdness" when doing I would expect we are already treating it as 1-byte on Windows x86/x64/Arm64 due to the need for COM interop (which is typically C++ oriented) and that it likely required less work to make function. If we do have some differing back-compat requirement that treats it differently, however, then I'd expect we just preserve that everywhere for consistency. |
Agree. I know that @jkoritzinsky did some work in this area and it caused a great deal of grief trying to get it right. I'll defer to him, but if I recall correctly, we came down on empty structs were 1-byte. |
Yes, today we treat empty structs as 1-byte. I'd recommend we continue doing so. The only alternative I'd be comfortable with at this time would be erroring out for zero-sized structs in interop calls on platforms where there is a special ABI for zero-sized structs (like the Int128 and VectorX cases) and treating them as 1-byte in managed code. |
A note from RISC-V Hardware Floating-point Calling Convention:
@LuckyXu-HF @shushanhf LoongArch ABI doesn't seem to mention it but from what I could discern GCC behaves the same way so I left it in for LA as well.
Plus, some fixes to the EmptyStructs test code.
Stems from #101796, part of #84834, cc @dotnet/samsung