* [RISC-V] Add crossgen2 for riscv64 (#95188)
* Add crossgen2 for riscv64
* Fix review comments
* Fix review
* [RISC-V] Enable R2RDump (#97156)
* [RISC-V] Fix errors in crosgen2 for risc-v (#97368)
* [RISC-V] Fix crossgen2
* [RISC-V] Fix typo in codegen
* [RISC-V] Update assert to support all rel insts
* [RISC-V] Fix helper function call
* [RISC-V] Update GetRISCV64PassStructInRegisterFlags
* [RISC-V] Fix int arg reg passing for float type
A failure during crossgen2 SPC.dll
`System.Diagnostics.Tracing.NativeRuntimeEventSource:LogThreadPoolWorkerThreadAdjustmentStats(double,double,double,double,double,double,double,double,double,ushort,ushort)`
* [RISC-V] Update EPILOG_WITH_TRANSITION_BLOCK_RETURN
* [RISC-V] Update indentation in dump
* [RISC-V] Fix stubs
* [RISC-V] Fix virtualcallstubcpu
* [RISC-V] Update a comment
* [RISC-V] Update RO data block
* [RISC-V] Fix data overwrites
* [RISC-V] Fix typo
* [RISC-V] Fix register number of A3
* [RISC-V] Code Formatting
* [RISC-V] Fix format and typos
* Revert "[RISC-V] Fix int arg reg passing for float type"
This reverts commit
381858cb2335782d66e8a6439db9c6f053adff21.
* [RISC-V] Fix a typo in a comment
* [RISC-V] Fix error when arg type and arg reg mismatch
* [RISC-V] Rename according to review
* [LoongArch64] Remove LA64 specific handling for unspilling
* Enable riscv64 R2R in installer (#96941)
* [RISC-V] R2RDump to handle System.Private.CoreLib (#98849)
* Add missing codes for RISC-V to enable R2RDump'ing unwind and gcInfo
* Handle decode failures from coredistools in R2RDump instead of getting stuck in an infinite loop
* Remove unused dll import DumpCodeBlock
* [RISC-V] Enable crossgen for corelib (#99436)
* [RISC-V] Fix alignment for vector types (#99589)
For vector256 and vector512, set possible max alignment value.
Reference
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc
* Update Architecture to unknown machine (#101038)
* Update Architecture to unknown machine
* Update src/tools/illink/src/linker/Linker.Steps/OutputStep.cs
Suggested by @am11. Thank you.
Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com>
---------
Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* [RISC-V] Simplify flags for passing struct in registers (#100080)
* [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>
* [RISC-V] Add quirks for riscv to R2RDump (#101683)
* [RISC-V] Add quirks for riscv
* [RISC-V] minimize code
* [LoongArch64] Fix the `ArgIteratorTemplate::GetNextOffset()` return argOfs for 'ELEMENT_TYPE_VALUETYPE' which is flattened liking struct{Arr[], float}. (#103108)
* [LoongArch64] Fix the `ArgIteratorTemplate::GetNextOffset()` return argOfs for 'ELEMENT_TYPE_VALUETYPE' which is flattened liking struct{Arr[], float}.
* The 'ELEMENT_TYPE_VALUETYPE' is marked to 'TYPE_GC_OTHER' and the first flattened element Arr[] should GC.
* This also fixed the assert failure "!CREATE_CHECK_STRING(pMT && pMT->Validate())" under GC=8 of MarshalStructAsLayoutSeq.sh.
* Update callingconvention.h
delete redundancy condition.
* Fix ArgIteratorTemplate::GetNextOffset() for struct{Arr[], float} (#103125)
* fix ping with TTL on Linux (#99875)
* fix ping with TTL on Linux
* feedback
* feedback
* [RISC-V] Set proper options for connected IPv6 raw socket (#103127)
Before this change we get following failure from PingTest.SendPingWithIPAddres:
System.Net.NetworkInformation.PingException : An exception occurred during a Ping request.\n---- System.Net.Sockets.SocketException : Protocol not available
at System.Net.NetworkInformation.Ping.Send(IPAddress address, Int32 timeout, Byte[] buffer, PingOptions options) in /runtime/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs:line 411
at System.Net.NetworkInformation.Ping.Send(IPAddress address, Int32 timeout, Byte[] buffer) in /runtime/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs:line 319
at System.Net.NetworkInformation.Ping.Send(IPAddress address, Int32 timeout) in /runtime/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs:line 258
at System.Net.NetworkInformation.Tests.PingTest.<>c__DisplayClass12_0.<SendPingWithIPAddress>b__0(Ping ping) in /runtime/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs:line 149
at System.Net.NetworkInformation.Tests.PingTest.SendPing(Func`2 sendPing, Action`1 pingResultValidator) in /runtime/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs:line 628
at System.Net.NetworkInformation.Tests.PingTest.SendBatchPing(Func`2 sendPing, Action`1 pingResultValidator) in /runtime/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs:line 611
at System.Net.NetworkInformation.Tests.PingTest.SendPingWithIPAddress(AddressFamily addressFamily) in /runtime/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs:line 148
at InvokeStub_PingTest.SendPingWithIPAddress(Object, Span`1)
In this patch we fix setsockopt error by adding extra handling of IPv6 case.
* Fix sending ICMP echo request when host is unreachable (#103158)
Before this change we get following failure from PingTest.SendPingToExternalHostWithLowTtlTest:
System.Net.NetworkInformation.PingException : An exception occurred during a Ping request.
---- System.Net.Sockets.SocketException : No route to host
Stack Trace:
/runtime/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs(729,0): at System.Net.NetworkInformation.Ping.SendPingAsyncInternal[TArg](TArg getAddressArg, Func`3 getAddress, Int32 timeout, Byte[] buffer, PingOptions options, CancellationToken cancellationToken)
/home/d.jurczak2/runtime/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs(743,0): at System.Net.NetworkInformation.Tests.PingTest.SendPingToExternalHostWithLowTtlTest()
--- End of stack trace from previous location ---
----- Inner Stack Trace -----
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs(1395,0): at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.CreateException(SocketError error, Boolean forAsyncThrow)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs(1097,0): at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ReceiveFromAsync(Socket socket, CancellationToken cancellationToken)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs(423,0): at System.Net.Sockets.Socket.ReceiveFromAsync(Memory`1 buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken)
/runtime/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs(334,0): at System.Net.NetworkInformation.Ping.SendIcmpEchoRequestOverRawSocketAsync(IPAddress address, Byte[] buffer, Int32 timeout, PingOptions options)
/runtime/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderCore.cs(38,0): at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at System.Net.NetworkInformation.Ping.SendIcmpEchoRequestOverRawSocketAsync(IPAddress address, Byte[] buffer, Int32 timeout, PingOptions options)
/runtime/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs(32,0): at System.Net.NetworkInformation.Ping.SendPingAsyncCore(IPAddress address, Byte[] buffer, Int32 timeout, PingOptions options)
/runtime/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs(721,0): at System.Net.NetworkInformation.Ping.SendPingAsyncInternal[TArg](TArg getAddressArg, Func`3 getAddress, Int32 timeout, Byte[] buffer, PingOptions options, CancellationToken cancellationToken)
/runtime/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncTaskMethodBuilderT.cs(292,0): at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncS
In this patch we add HostUnreachable handling to SendIcmpEchoRequestOverRawSocketAsync
and fix getting empty message from error queue.
* Fix alloc-dealloc mismatch in GC (#102929)
* Fix alloc-dealloc mismatch
* Fix few more alloc-dealloc mismatch in GC
* [RISC-V] Disable EnableWriteXorExecute by default on riscv64 architectue (#103408)
* Disable EnableWriteXorExecute by default on riscv64 architectue
On riscv64 architectue any dotnet process that loads corossgened assembly
without DOTNET_EnableWriteXorExecute=0 environment variable set
crashes with "Segmentation fault" error.
For example System.Private.CoreLib.dll is compiled with crossgen2 by default
during build phase so this crash applies to any processes that loads it.
Setting EnableWriteXorExecute=0 disables this options on riscv64 architecure
and prevents these crashes.
Co-authored-by: Dong-Heon Jung <clamp03@gmail.com>
* Add a link to an issue about re-enabling WriteXorExecute on riscv64 arch.
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
---------
Co-authored-by: Dong-Heon Jung <clamp03@gmail.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* Fix nonvolatile context restoration (#101709)
* Fix nonvolatile context restoration
There is a possibility of a race between the
ClrRestoreNonVolatileContext and an async signal handling (like
the one we use for runtime suspension). If the signal kicks in after
we've loaded Rsp, but before we jumped to the target address, the
context we are loading the registers from could get overwritten by the
signal handler stack. So the ClrRestoreNonVolatileContext would end up
jumping into a wrong target address.
The fix is to load the target address into a register before loading the
Rsp and then jumping using the register.
* Fix arm and x86
* [RISC-V] Fix context restoration as #101709 describes (#101865)
* [RISCV-V] Fix context restoration as 101709
* Feedback
* [x64][SysV] Classify empty structs for passing like padding (#103799)
The current implementation barred a struct containing empty struct fields from enregistration. This did not match the [System V ABI](https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf) which says "NO_CLASS This class is used as initializer in the algorithms. It will be used for padding and **empty structures** and unions". It also does not match the behavior of GCC & Clang on Linux.
* [RISC-V] Fix coreclr test readytorun/coreroot_determinism/coreroot_determinism/coreroot_determinism.sh (#104140)
To make test passing we increase TimeoutMilliseconds to 30 min.
* Fix alpine-riscv64 build (#104920)
* [RISC-V] Fix System.Net.Sockets.Tests on Qemu (#104094)
* [RISC-V] Fix System.Net.Sockets.Tests on Qemu
Before this change there are 8 failures from System.Net.Sockets.Tests with following reports:
root@
69fa7050f168:/runtime/artifacts/bin/System.Net.Sockets.Tests/Release/net9.0-unix# /runtime/artifacts/bin/testhost/net9.0-linux-Release-riscv64/dotnet exec --runtimeconfig System.Net.Sockets.Tests.runtimeconfig.json --depsfile System.Net.Sockets.Tests.deps.json xunit.console.dll System.Net.Sockets.Tests.dll -xml testResults.xml -nologo -notrait category=nonnetcoreapptests -notrait category=nonlinuxtests -notrait category=failing -maxthreads 32
Discovering: System.Net.Sockets.Tests (method display = ClassAndMethod, method display options = None)
Discovered: System.Net.Sockets.Tests (found 1672 of 1820 test cases)
Starting: System.Net.Sockets.Tests (parallel test collections = on [32 threads], stop on fail = off)
System.Net.Sockets.Tests.CreateSocket.Ctor_Raw_NotSupported_ExpectedError [SKIP]
Condition(s) not met: "NotSupportsRawSockets"
System.Net.Sockets.Tests.KeepAliveTest.Socket_Get_KeepAlive_Time_AsByteArray_OptionLengthZero_Failure [FAIL]
System.Net.Sockets.SocketException : Bad address
Stack Trace:
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3737,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3728,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(2145,0): at System.Net.Sockets.Socket.GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionLength)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/KeepAliveTest.cs(136,0): at System.Net.Sockets.Tests.KeepAliveTest.Socket_Get_KeepAlive_Time_AsByteArray_OptionLengthZero_Failure()
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
/runtime/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs(57,0): at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
System.Net.Sockets.Tests.ArgumentValidation.Connect_ConnectTwice_NotSupported(invalidatingAction: 1) [FAIL]
System.Net.Sockets.SocketException : Protocol not available
Stack Trace:
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3737,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3728,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3466,0): at System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionValue, Boolean silent)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(1966,0): at System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionValue)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs(809,0): at System.Net.Sockets.Tests.ArgumentValidation.Connect_ConnectTwice_NotSupported(Int32 invalidatingAction)
at InvokeStub_ArgumentValidation.Connect_ConnectTwice_NotSupported(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Net.Sockets.Tests.SocketOptionNameTest.MulticastInterface_Set_AnyInterface_Succeeds [FAIL]
System.Net.Sockets.SocketException : Unknown socket error
Stack Trace:
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3737,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3728,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3479,0): at System.Net.Sockets.Socket.SetMulticastOption(SocketOptionName optionName, MulticastOption MR)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(2021,0): at System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Object optionValue)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs(96,0): at System.Net.Sockets.Tests.SocketOptionNameTest.MulticastInterface_Set_Helper(Int32 interfaceIndex)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs(71,0): at System.Net.Sockets.Tests.SocketOptionNameTest.MulticastInterface_Set_AnyInterface_Succeeds()
--- End of stack trace from previous location ---
System.Net.Sockets.Tests.KeepAliveTest.Socket_Get_KeepAlive_Time_AsByteArray_BufferNullOrTooSmall_Failure(buffer: null) [FAIL]
System.Net.Sockets.SocketException : Bad address
Stack Trace:
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3737,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3728,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(2121,0): at System.Net.Sockets.Socket.GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Byte[] optionValue)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/KeepAliveTest.cs(156,0): at System.Net.Sockets.Tests.KeepAliveTest.Socket_Get_KeepAlive_Time_AsByteArray_BufferNullOrTooSmall_Failure(Byte[] buffer)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
/runtime/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs(178,0): at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
System.Net.Sockets.Tests.KeepAliveTest.Socket_Get_KeepAlive_Time_AsByteArray_BufferNullOrTooSmall_Failure(buffer: []) [FAIL]
System.Net.Sockets.SocketException : Bad address
Stack Trace:
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3737,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3728,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(2121,0): at System.Net.Sockets.Socket.GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Byte[] optionValue)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/KeepAliveTest.cs(156,0): at System.Net.Sockets.Tests.KeepAliveTest.Socket_Get_KeepAlive_Time_AsByteArray_BufferNullOrTooSmall_Failure(Byte[] buffer)
at InvokeStub_KeepAliveTest.Socket_Get_KeepAlive_Time_AsByteArray_BufferNullOrTooSmall_Failure(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Net.Sockets.Tests.SocketOptionNameTest.MulticastOption_CreateSocketSetGetOption_GroupAndInterfaceIndex_SetSucceeds_GetThrows [FAIL]
System.Net.Sockets.SocketException : Unknown socket error
Stack Trace:
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3737,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3728,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3479,0): at System.Net.Sockets.Socket.SetMulticastOption(SocketOptionName optionName, MulticastOption MR)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(2021,0): at System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Object optionValue)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs(61,0): at System.Net.Sockets.Tests.SocketOptionNameTest.MulticastOption_CreateSocketSetGetOption_GroupAndInterfaceIndex_SetSucceeds_GetThrows()
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
/runtime/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs(57,0): at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
System.Net.Sockets.Tests.SocketOptionNameTest.MulticastInterface_Set_IPv6_AnyInterface_Succeeds [FAIL]
System.Net.Sockets.SocketException : Protocol not available
Stack Trace:
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3737,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3728,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3466,0): at System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionValue, Boolean silent)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(1966,0): at System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionValue)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs(199,0): at System.Net.Sockets.Tests.SocketOptionNameTest.MulticastInterface_Set_IPv6_Helper(Int32 interfaceIndex)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs(129,0): at System.Net.Sockets.Tests.SocketOptionNameTest.MulticastInterface_Set_IPv6_AnyInterface_Succeeds()
--- End of stack trace from previous location ---
System.Net.Sockets.Tests.ArgumentValidation.ConnectAsync_ConnectTwice_NotSupported(invalidatingAction: 1) [FAIL]
System.Net.Sockets.SocketException : Protocol not available
Stack Trace:
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3737,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3728,0): at System.Net.Sockets.Socket.UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError error, String callerName)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(3466,0): at System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionValue, Boolean silent)
/runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs(1966,0): at System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionValue)
/home/d.jurczak2/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs(842,0): at System.Net.Sockets.Tests.ArgumentValidation.ConnectAsync_ConnectTwice_NotSupported(Int32 invalidatingAction)
at InvokeStub_ArgumentValidation.ConnectAsync_ConnectTwice_NotSupported(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
Finished: System.Net.Sockets.Tests
=== TEST EXECUTION SUMMARY ===
System.Net.Sockets.Tests Total: 2874, Errors: 0, Failed: 8, Skipped: 1, Time: 139.280s
Those failures are caused by Qemu's buggy and/or implementation defined behavior like:
https://gitlab.com/qemu-project/qemu/-/issues/2410
https://gitlab.com/qemu-project/qemu/-/issues/2390
https://gitlab.com/qemu-project/qemu/-/issues/1837
In this patch we add couple of workarounds to make all System.Net.Sockets.Tests passing.
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* [RISC-V] Fix passing float and uint arguments in VM (#105021)
* Add tests
* Fix passing float and uint arguments in VM
* Change test lib name so it doesn't clash with managed DLL on Windows
* Disable execution of two WorkingSet tests on QEMU (#105689)
System.Diagnostics.Tests.ProcessTests.TestMaxWorkingSet()
System.Diagnostics.Tests.ProcessTests.TestMinWorkingSet()
These two corefx tests fail because on QEMU the file /proc/pid/stat
contains incorrect values.
See issue #105686 for details.
* Fix _LOGALLOC define (#106023)
* [RISC-V][LoongArch64] HijackFrame::UpdateRegDisplay restore A0 (#106018)
* [RISC-V] Fix Reg Order for Float Registers (#105930)
* [RISC-V] Fix coreroot_determinism in release build
Initialize regOrder and put all floating registers in REG_VAR_ORDER_FLT
* [RISC-V] Update GetLocalFloatingPointValue for RISC-V
* [RISC-V] System.Console CancelKeyPressTests.ExitDetectionNotBlockedByHandler increase timeout (#106119)
* Increase timeout to prevent failure of CancelKeyPressTests.ExitDetectionNotBlockedByHandler on Debug RISC-V
* Make WaitFailTestTimeoutSeconds static, move it closer to where it's used
* [RISC-V] JitDisasmWithGC: Output any delta in GC info (#106196)
* [RISC-V] Add GetRegOffsInCONTEXT() implementation (fix for `clrstack -p` SOS command). (#106277)
* [RISC-V] Fix GenerateResolveStub (#106562)
Fix to allow use of fast cached way.
* Fix alloc-dealloc mismatch (#106957)
* [Tizen] Fix build cause SDK does not know Riscv64
On reverting this #97021 should be picked.
---------
Co-authored-by: Aleksandr Shaurtaev <38426614+ashaurtaev@users.noreply.github.com>
Co-authored-by: Tomasz SowiĆski <tomeksowi@gmail.com>
Co-authored-by: Dong-Heon Jung <clamp03@gmail.com>
Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Co-authored-by: SzpejnaDawid <166010737+SzpejnaDawid@users.noreply.github.com>
Co-authored-by: Xu Liangyu <xuliangyu@loongson.cn>
Co-authored-by: Tomas Weinfurt <tweinfurt@yahoo.com>
Co-authored-by: yurai007 <dawid_jurek@vp.pl>
Co-authored-by: Gleb Balykov <g.balykov@samsung.com>
Co-authored-by: rzsc <160726116+rzsc@users.noreply.github.com>
Co-authored-by: Jan Vorlicek <janvorli@microsoft.com>
Co-authored-by: t-mustafin <66252296+t-mustafin@users.noreply.github.com>
Co-authored-by: Mikhail Kurinnoi <m.kurinnoi@samsung.com>
LatestRuntimeFrameworkVersion="$(ProductVersion)"
RuntimeFrameworkName="$(LocalFrameworkOverrideName)"
RuntimePackNamePatterns="$(LocalFrameworkOverrideName).Runtime.**RID**"
- RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;maccatalyst-x64;maccatalyst-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86"
+ RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;maccatalyst-x64;maccatalyst-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86,linux-riscv64;linux-musl-riscv64"
TargetFramework="$(NetCoreAppCurrent)"
TargetingPackName="$(LocalFrameworkOverrideName).Ref"
TargetingPackVersion="$(ProductVersion)"
RuntimeFrameworkName="$(LocalFrameworkOverrideName)"
LatestRuntimeFrameworkVersion="$(ProductVersion)"
RuntimePackNamePatterns="$(LocalFrameworkOverrideName).Runtime.Mono.**RID**"
- RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;maccatalyst-x64;maccatalyst-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86;browser-wasm;wasi-wasm;ios-arm64;iossimulator-arm64;iossimulator-x64;tvos-arm64;tvossimulator-arm64;tvossimulator-x64;android-arm64;android-arm;android-x64;android-x86"
+ RuntimePackRuntimeIdentifiers="linux-arm;linux-arm63;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;linux-riscv64;linux-musl-riscv64;rhel.6-x64;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;maccatalyst-x64;maccatalyst-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86;browser-wasm;wasi-wasm;ios-arm64;iossimulator-arm64;iossimulator-x64;tvos-arm64;tvossimulator-arm64;tvossimulator-x64;android-arm64;android-arm;android-x64;android-x86"
RuntimePackLabels="Mono"
Condition="'@(KnownRuntimePack)' == '' or !@(KnownRuntimePack->AnyHaveMetadataValue('TargetFramework', '$(NetCoreAppCurrent)'))"/>
<KnownCrossgen2Pack Include="$(LocalFrameworkOverrideName).Crossgen2"
TargetFramework="$(NetCoreAppCurrent)"
Crossgen2PackNamePattern="$(LocalFrameworkOverrideName).Crossgen2.**RID**"
Crossgen2PackVersion="$(ProductVersion)"
- Crossgen2RuntimeIdentifiers="linux-musl-x64;linux-x64;win-x64;linux-arm;linux-arm64;linux-musl-arm;linux-musl-arm64;osx-arm64;osx-x64;win-arm64;win-x86"
+ Crossgen2RuntimeIdentifiers="linux-musl-x64;linux-x64;win-x64;linux-arm;linux-arm64;linux-musl-arm;linux-musl-arm64;osx-arm64;osx-x64;win-arm64;win-x86;linux-riscv64;linux-musl-riscv64"
Condition="'@(KnownCrossgen2Pack)' == '' or !@(KnownCrossgen2Pack->AnyHaveMetadataValue('TargetFramework', '$(NetCoreAppCurrent)'))" />
<KnownILCompilerPack Include="Microsoft.DotNet.ILCompiler"
ILCompilerPackNamePattern="runtime.**RID**.Microsoft.DotNet.ILCompiler"
<PropertyGroup>
<BuildDll>true</BuildDll>
<BuildDll Condition="'$(TargetOS)' == 'netbsd' or '$(TargetOS)' == 'illumos' or '$(TargetOS)' == 'solaris'">false</BuildDll>
- <BuildDll Condition="'$(TargetArchitecture)' == 'riscv64'">false</BuildDll>
+ <BuildDll Condition="'$(TargetOS)' == 'netbsd' or '$(TargetOS)' == 'illumos' or '$(TargetOS)' == 'solaris' or '$(TargetOS)' == 'haiku'">false</BuildDll>
<BuildPdb>false</BuildPdb>
<BuildPdb Condition="$(BuildDll) and '$(OS)' == 'Windows_NT' and '$(TargetOS)' == 'windows'">true</BuildPdb>
{
SUPPORTS_DAC;
COUNT_T countNativeVarInfo;
- NewHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL);
+ NewArrayHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL);
TADDR nativeCodeStartAddr;
if (address != NULL)
{
{
HRESULT status;
ULONG32 numVarInfo;
- NewHolder<ICorDebugInfo::NativeVarInfo> varInfo(NULL);
+ NewArrayHolder<ICorDebugInfo::NativeVarInfo> varInfo(NULL);
ULONG32 codeOffset;
ULONG32 valueFlags;
ULONG32 i;
(index <= REGISTER_ARM_D31)))
return E_INVALIDARG;
index -= REGISTER_ARM_D0;
+#elif defined(TARGET_RISCV64)
+ if (!((index >= REGISTER_RISCV64_F0) &&
+ (index <= REGISTER_RISCV64_F31)))
+ return E_INVALIDARG;
+ index -= REGISTER_RISCV64_F0;
#else
if (!((index >= REGISTER_X86_FPSTACK_0) &&
(index <= REGISTER_X86_FPSTACK_7)))
{
if (slots != (bk*)(this+1))
{
- delete slots;
+ delete[] slots;
}
delete_old_slots();
- delete this;
}
void
sorted_table::delete_old_slots()
{
uint8_t* dsl = sl;
sl = last_slot ((bk*)sl);
- delete dsl;
+ delete[] dsl;
}
old_slots = 0;
}
//#endif //BACKGROUND_GC
if (g_mark_list)
- delete g_mark_list;
-
- if (seg_mapping_table)
- delete seg_mapping_table;
+ delete[] g_mark_list;
#ifdef FEATURE_BASICFREEZE
//destroy the segment map
seg_table->delete_sorted_table();
+ delete[] (char*)seg_table;
#endif //FEATURE_BASICFREEZE
}
#ifdef MULTIPLE_HEAPS
//delete the heaps array
- delete g_heaps;
+ delete[] g_heaps;
destroy_thread_support();
n_heaps = 0;
#endif //MULTIPLE_HEAPS
mark* tmp = new (nothrow) mark [new_size];
if (tmp)
{
- delete mark_stack_array;
+ delete[] mark_stack_array;
mark_stack_array = tmp;
mark_stack_array_length = new_size;
}
if (!loh_pinned_queue_decay)
{
- delete loh_pinned_queue;
+ delete[] loh_pinned_queue;
loh_pinned_queue = 0;
}
}
CFinalize::~CFinalize()
{
- delete m_Array;
+ delete[] m_Array;
}
size_t CFinalize::GetPromotedCount ()
class DacHeapWalker;
#endif
-#ifdef _DEBUG
-#define _LOGALLOC
-#endif
-
#define MP_LOCKS
#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
//
// Executable code
//
+// TODO: https://github.com/dotnet/runtime/issues/103465
+#ifdef TARGET_RISCV64
+RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableWriteXorExecute, W("EnableWriteXorExecute"), 0, "Enable W^X for executable memory.");
+#else
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableWriteXorExecute, W("EnableWriteXorExecute"), 1, "Enable W^X for executable memory.");
+#endif // TARGET_RISCV64
#ifdef FEATURE_GDBJIT
///
#define ERROR 0
#define FATALERROR 0
+#ifdef _DEBUG
+#define _LOGALLOC
+#endif
+
#ifndef LOGGING
#define LOG(x)
assert(!"JitBreakEmitOutputInstr reached");
}
}
+
+ // Output any delta in GC info.
+ if (EMIT_GC_VERBOSE || emitComp->opts.disasmWithGC)
+ {
+ emitDispGCInfoDelta();
+ }
+
#else // !DEBUG
if (emitComp->opts.disAsm)
{
regNum = REG_NA;
isCalleeSave = false;
registerType = IntRegisterType;
+ regOrder = UCHAR_MAX;
}
void init(regNumber reg)
REG_T0,REG_T1,REG_T2,REG_T3,REG_T4,REG_T5,REG_T6, \
REG_CALLEE_SAVED_ORDER
- #define REG_VAR_ORDER_FLT REG_F12,REG_F13,REG_F14,REG_F15,REG_F16,REG_F17,REG_F18,REG_F19, \
- REG_F2,REG_F3,REG_F4,REG_F5,REG_F6,REG_F7,REG_F8,REG_F9,REG_F10, \
- REG_F20,REG_F21,REG_F22,REG_F23, \
- REG_F24,REG_F25,REG_F26,REG_F27,REG_F28,REG_F29,REG_F30,REG_F31, \
- REG_F1,REG_F0
+ #define REG_VAR_ORDER_FLT REG_F4, REG_F5, REG_F6, REG_F7, REG_F28, REG_F29, REG_F30, REG_F31, \
+ REG_F12, REG_F13, REG_F14, REG_F15, REG_F16, REG_F17, \
+ REG_F0, REG_F1, REG_F2, REG_F3, \
+ REG_F22, REG_F23, REG_F24, REG_F25, REG_F26, REG_F27, REG_F18, REG_F19, REG_F20, REG_F21, REG_F8, REG_F9, \
+ REG_F11, REG_F10
#define REG_CALLEE_SAVED_ORDER REG_S1,REG_S2,REG_S3,REG_S4,REG_S5,REG_S6,REG_S7,REG_S8,REG_S9,REG_S10,REG_S11
#define RBM_CALLEE_SAVED_ORDER RBM_S1,RBM_S2,RBM_S3,RBM_S4,RBM_S5,RBM_S6,RBM_S7,RBM_S8,RBM_S9,RBM_S10,RBM_S11
ldr R2, [r0, #(CONTEXT_Cpsr)]
msr APSR, r2
- // Ideally, we would like to use `ldmia r0, {r0-r12, sp, lr, pc}` here,
- // but clang 3.6 and later, as per ARM recommendation, disallows using
- // Sp in the register list, and Pc and Lr simultaneously.
- // So we are going to use the IPC register r12 to copy Sp, Lr and Pc
- // which should be ok -- TODO: Is this really ok?
+ ldr r1, [r0, #(CONTEXT_Sp)]
+ ldr r2, [r0, #(CONTEXT_Pc)]
+ str r2, [r1, #-4]
+ ldr r2, [r0, #(CONTEXT_R12)]
+ str r2, [r1, #-8]
add r12, r0, CONTEXT_R0
ldm r12, {r0-r11}
- ldr sp, [r12, #(CONTEXT_Sp - (CONTEXT_R0))]
ldr lr, [r12, #(CONTEXT_Lr - (CONTEXT_R0))]
- ldr pc, [r12, #(CONTEXT_Pc - (CONTEXT_R0))]
+ ldr r12, [r12, #(CONTEXT_Sp - (CONTEXT_R0))]
+ sub r12, r12, #8
+ mov sp, r12
+ pop {r12, pc}
LOCAL_LABEL(No_Restore_CONTEXT_INTEGER):
ldr r2, [r0, #(CONTEXT_Cpsr)]
msr APSR, r2
- ldr sp, [r0, #(CONTEXT_Sp)]
ldr lr, [r0, #(CONTEXT_Lr)]
- ldr pc, [r0, #(CONTEXT_Pc)]
+ ldr r2, [r0, #(CONTEXT_Pc)]
+ ldr sp, [r0, #(CONTEXT_Sp)]
+ bx r2
LOCAL_LABEL(No_Restore_CONTEXT_CONTROL):
ldr r2, [r0, #(CONTEXT_ContextFlags)]
movdqu xmm7, [eax + CONTEXT_Xmm7]
LOCAL_LABEL(Done_Restore_CONTEXT_EXTENDED_REGISTERS):
- // Restore Stack
- mov esp, [eax + CONTEXT_Esp]
-
// Create a minimal frame
- push DWORD PTR [eax + CONTEXT_Eip]
+ mov ebx, [eax + CONTEXT_Esp]
+ mov ecx, [eax + CONTEXT_Eip]
+ mov edx, [eax + CONTEXT_Eax]
+ mov [ebx - 4], ecx
+ mov [ebx - 8], edx
// Restore register(s)
mov ebp, [eax + CONTEXT_Ebp]
mov edx, [eax + CONTEXT_Edx]
mov ecx, [eax + CONTEXT_Ecx]
mov ebx, [eax + CONTEXT_Ebx]
- mov eax, [eax + CONTEXT_Eax]
+
+ // Restore Stack
+ mov eax, [eax + CONTEXT_Esp]
+ sub eax, 8
+ mov esp, eax
+
+ pop eax
// Resume
ret
beqz t1, LOCAL_LABEL(No_Restore_CONTEXT_CONTROL)
ld ra, (CONTEXT_Ra)(t4)
+ ld t1, (CONTEXT_T4)(t4)
+ ld fp, (CONTEXT_Sp)(t4)
+ sd t1, -8(fp)
ld fp, (CONTEXT_Fp)(t4)
- ld sp, (CONTEXT_Sp)(t4)
ld t1, (CONTEXT_Pc)(t4) // Since we cannot control $pc directly, we're going to corrupt t1
- ld t4, (CONTEXT_T4)(t4)
+ ld t4, (CONTEXT_Sp)(t4)
+ addi sp, t4, -8
+ ld t4, (sp)
+ addi sp, sp, 8
jr t1
LOCAL_LABEL(No_Restore_CONTEXT_CONTROL):
Architecture.Arm => TargetArchitecture.ARM,
Architecture.Arm64 => TargetArchitecture.ARM64,
Architecture.LoongArch64 => TargetArchitecture.LoongArch64,
+ (Architecture)9 => TargetArchitecture.RiscV64, /* TODO: update with Architecture.RiscV64 */
_ => throw new NotImplementedException()
};
}
"arm" or "armel" => TargetArchitecture.ARM,
"arm64" => TargetArchitecture.ARM64,
"loongarch64" => TargetArchitecture.LoongArch64,
+ "riscv64" => TargetArchitecture.RiscV64,
_ => throw new CommandLineException($"Target architecture '{token}' is not supported")
};
}
loongarch64Emitter.Builder.AddSymbol(this);
return loongarch64Emitter.Builder.ToObjectData();
+ case TargetArchitecture.RiscV64:
+ RiscV64.RiscV64Emitter riscv64Emitter = new RiscV64.RiscV64Emitter(factory, relocsOnly);
+ EmitCode(factory, ref riscv64Emitter, relocsOnly);
+ riscv64Emitter.Builder.RequireInitialAlignment(alignment);
+ riscv64Emitter.Builder.AddSymbol(this);
+ return riscv64Emitter.Builder.ToObjectData();
+
default:
throw new NotImplementedException();
}
protected abstract void EmitCode(NodeFactory factory, ref ARM.ARMEmitter instructionEncoder, bool relocsOnly);
protected abstract void EmitCode(NodeFactory factory, ref ARM64.ARM64Emitter instructionEncoder, bool relocsOnly);
protected abstract void EmitCode(NodeFactory factory, ref LoongArch64.LoongArch64Emitter instructionEncoder, bool relocsOnly);
+ protected abstract void EmitCode(NodeFactory factory, ref RiscV64.RiscV64Emitter instructionEncoder, bool relocsOnly);
}
}
case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC:
case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR:
+
+ case RelocType.IMAGE_REL_BASED_RISCV64_PC:
Debug.Assert(delta == 0);
// Do not vacate space for this kind of relocation, because
// the space is embedded in the instruction.
IMAGE_REL_BASED_ARM64_BRANCH26 = 0x15, // Arm64: B, BL
IMAGE_REL_BASED_LOONGARCH64_PC = 0x16, // LoongArch64: pcaddu12i+imm12
IMAGE_REL_BASED_LOONGARCH64_JIR = 0x17, // LoongArch64: pcaddu18i+jirl
+ IMAGE_REL_BASED_RISCV64_PC = 0x18, // RiscV64: auipc
IMAGE_REL_BASED_RELPTR32 = 0x7C, // 32-bit relative address from byte starting reloc
// This is a special NGEN-specific relocation type
// for relative pointer (used to make NGen relocation
Debug.Assert(GetLoongArch64JIR(pCode) == imm38);
}
+ private static unsafe int GetRiscV64PC(uint* pCode)
+ {
+ uint auipcInstr = *pCode;
+ Debug.Assert((auipcInstr & 0x7f) == 0x00000017);
+ // first get the high 20 bits,
+ int imm = (int)((auipcInstr & 0xfffff000));
+ // then get the low 12 bits,
+ uint nextInstr = *(pCode + 1);
+ Debug.Assert((nextInstr & 0x707f) == 0x00000013 ||
+ (nextInstr & 0x707f) == 0x00000067 ||
+ (nextInstr & 0x707f) == 0x00003003);
+ imm += ((int)(nextInstr)) >> 20;
+
+ return imm;
+ }
+
+ // INS_OPTS_RELOC: placeholders. 2-ins:
+ // case:EA_HANDLE_CNS_RELOC
+ // auipc reg, off-hi-20bits
+ // addi reg, reg, off-lo-12bits
+ // case:EA_PTR_DSP_RELOC
+ // auipc reg, off-hi-20bits
+ // ld reg, reg, off-lo-12bits
+ // case:
+ // INS_OPTS_C
+ // auipc reg, off-hi-20bits
+ // jalr reg, reg, off-lo-12bits
+ private static unsafe void PutRiscV64PC(uint* pCode, long imm32)
+ {
+ // Verify that we got a valid offset
+ Debug.Assert((int)imm32 == imm32);
+
+ int doff = (int)(imm32 & 0xfff);
+ uint auipcInstr = *pCode;
+ Debug.Assert((auipcInstr & 0x7f) == 0x00000017);
+
+ auipcInstr |= (uint)((imm32 + 0x800) & 0xfffff000);
+ *pCode = auipcInstr;
+
+ uint nextInstr = *(pCode + 1);
+ Debug.Assert((nextInstr & 0x707f) == 0x00000013 ||
+ (nextInstr & 0x707f) == 0x00000067 ||
+ (nextInstr & 0x707f) == 0x00003003);
+ nextInstr |= (uint)((doff & 0xfff) << 20);
+ *(pCode + 1) = nextInstr;
+
+ Debug.Assert(GetRiscV64PC(pCode) == imm32);
+ }
+
public Relocation(RelocType relocType, int offset, ISymbolNode target)
{
RelocType = relocType;
case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR:
PutLoongArch64JIR((uint*)location, value);
break;
+ case RelocType.IMAGE_REL_BASED_RISCV64_PC:
+ PutRiscV64PC((uint*)location, value);
+ break;
default:
Debug.Fail("Invalid RelocType: " + relocType);
break;
return (long)GetLoongArch64PC12((uint*)location);
case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR:
return (long)GetLoongArch64JIR((uint*)location);
+ case RelocType.IMAGE_REL_BASED_RISCV64_PC:
+ return (long)GetRiscV64PC((uint*)location);
default:
Debug.Fail("Invalid RelocType: " + relocType);
return 0;
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace ILCompiler.DependencyAnalysis.RiscV64
+{
+ public enum AddrModeSize
+ {
+ Int8 = 1,
+ Int16 = 2,
+ Int32 = 4,
+ Int64 = 8,
+ Int128 = 16
+ }
+
+ public struct AddrMode
+ {
+ public readonly Register BaseReg;
+ public readonly Register? IndexReg;
+ public readonly int Offset;
+ public readonly byte Scale;
+ public readonly AddrModeSize Size;
+
+ public AddrMode(Register baseRegister, Register? indexRegister, int offset, byte scale, AddrModeSize size)
+ {
+ BaseReg = baseRegister;
+ IndexReg = indexRegister;
+ Offset = offset;
+ Scale = scale;
+ Size = size;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace ILCompiler.DependencyAnalysis.RiscV64
+{
+ public enum Register
+ {
+ X0,
+ X1,
+ X2,
+ X3,
+ X4,
+ X5,
+ X6,
+ X7,
+ X8,
+ X9,
+ X10,
+ X11,
+ X12,
+ X13,
+ X14,
+ X15,
+ X16,
+ X17,
+ X18,
+ X19,
+ X20,
+ X21,
+ X22,
+ X23,
+ X24,
+ X25,
+ X26,
+ X27,
+ X28,
+ X29,
+ X30,
+ X31,
+
+ None,
+ NoIndex = 128
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+
+namespace ILCompiler.DependencyAnalysis.RiscV64
+{
+ public struct RiscV64Emitter
+ {
+ public RiscV64Emitter(NodeFactory factory, bool relocsOnly)
+ {
+ Builder = new ObjectDataBuilder(factory, relocsOnly);
+ TargetRegister = new TargetRegisterMap(factory.Target.OperatingSystem);
+ }
+
+ public ObjectDataBuilder Builder;
+ public TargetRegisterMap TargetRegister;
+
+ // Assembly stub creation api. TBD, actually make this general purpose
+
+ //ebreak
+ public void EmitBreak()
+ {
+ Builder.EmitUInt(0x00100073);
+ }
+
+ public void EmitLI(Register regDst, int offset)
+ {
+ Debug.Assert((offset >= -2048) && (offset <= 2047));
+ EmitADDI(regDst, Register.X0, offset);
+ }
+
+ public void EmitMOV(Register regDst, Register regSrc)
+ {
+ EmitADDI(regDst, regSrc, 0);
+ }
+
+ public void EmitMOV(Register regDst, ISymbolNode symbol)
+ {
+ Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_PC);
+ //auipc reg, off-hi-20bits
+ EmitPC(regDst);
+ //addi reg, reg, off-lo-12bits
+ EmitADDI(regDst, regDst, 0);
+ }
+
+ // auipc regDst, 0
+ public void EmitPC(Register regDst)
+ {
+ Debug.Assert((uint)regDst > 0 && (uint)regDst < 32);
+ Builder.EmitUInt(0x00000017u | (uint)regDst << 7);
+ }
+
+ // addi regDst, regSrc, offset
+ public void EmitADDI(Register regDst, Register regSrc, int offset)
+ {
+ Debug.Assert((uint)regDst <= 0x1f);
+ Debug.Assert((uint)regSrc <= 0x1f);
+ Debug.Assert((offset >= -2048) && (offset <= 2047));
+ Builder.EmitUInt((uint)(0x00000013u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20)));
+ }
+
+ // xori regDst, regSrc, offset
+ public void EmitXORI(Register regDst, Register regSrc, int offset)
+ {
+ Debug.Assert((offset >= -2048) && (offset <= 2047));
+ Builder.EmitUInt((uint)(0x00004013u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20)));
+ }
+
+ // ld regDst, offset(regSrc)
+ public void EmitLD(Register regDst, Register regSrc, int offset)
+ {
+ Debug.Assert((offset >= -2048) && (offset <= 2047));
+ Builder.EmitUInt((uint)(0x00003003u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20)));
+ }
+
+ // jalr regDst, offset(regSrc)
+ public void EmitJALR(Register regDst, Register regSrc, int offset)
+ {
+ Debug.Assert((offset >= -2048) && (offset <= 2047));
+ Builder.EmitUInt((uint)(0x00000067u | ((uint)regSrc << 15) | ((uint)regDst << 7) | (uint)((offset & 0xfff) << 20)));
+ }
+
+ public void EmitRET()
+ {
+ // jalr x0,0(x1)
+ EmitJALR(Register.X0, Register.X1, 0);
+ }
+
+ public void EmitJMP(Register reg)
+ {
+ //jalr x0, 0(reg)
+ EmitJALR(Register.X0, reg, 0);
+ }
+
+ public void EmitJMP(ISymbolNode symbol)
+ {
+ if (symbol.RepresentsIndirectionCell)
+ {
+ //auipc x29, 0
+ EmitPC(Register.X29);
+ //ld x29,16(x29)
+ EmitLD(Register.X29, Register.X29, 16);
+ //ld x29,0(x29)
+ EmitLD(Register.X29, Register.X29, 0);
+ //jalr x0,0(x29)
+ EmitJALR(Register.X0, Register.X29, 0);
+
+ Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64);
+ }
+ else
+ {
+ Builder.EmitUInt(0x00000000); // bad code.
+ throw new NotImplementedException();
+ }
+ }
+
+ public void EmitRETIfZero(Register regSrc)
+ {
+ // bne regSrc, x0, 8
+ Builder.EmitUInt((uint)(0x00001463 | ((uint)regSrc << 15)));
+ EmitRET();
+ }
+
+ public void EmitJMPIfZero(Register regSrc, ISymbolNode symbol)
+ {
+ uint offset = symbol.RepresentsIndirectionCell ? 28u : 8u;
+ uint encodedOffset = ((offset & 0x1e) << 7) | ((offset & 0x7e0) << 20) | ((offset & 0x800) >> 4) | ((offset & 0x1000) << 19);
+ // bne regSrc, x0, offset
+ Builder.EmitUInt((uint)(0x00001063 | ((uint)regSrc << 15) | encodedOffset));
+ EmitJMP(symbol);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Internal.TypeSystem;
+
+namespace ILCompiler.DependencyAnalysis.RiscV64
+{
+ /// <summary>
+ /// Maps logical registers to physical registers on a specified OS.
+ /// </summary>
+ public struct TargetRegisterMap
+ {
+ public readonly Register Arg0;
+ public readonly Register Arg1;
+ public readonly Register Arg2;
+ public readonly Register Arg3;
+ public readonly Register Arg4;
+ public readonly Register Arg5;
+ public readonly Register Arg6;
+ public readonly Register Arg7;
+ public readonly Register IntraProcedureCallScratch1;
+ public readonly Register Result;
+
+ public TargetRegisterMap(TargetOS os)
+ {
+ Arg0 = Register.X10;
+ Arg1 = Register.X11;
+ Arg2 = Register.X12;
+ Arg3 = Register.X13;
+ Arg4 = Register.X14;
+ Arg5 = Register.X15;
+ Arg6 = Register.X16;
+ Arg7 = Register.X17;
+ IntraProcedureCallScratch1 = Register.X28;
+ Result = Register.X10;
+ }
+ }
+}
if (potentialType.Namespace != "System.Runtime.Intrinsics.Arm")
return "";
}
+ else if (architecture == TargetArchitecture.RiscV64)
+ {
+ return "";
+ }
else
{
- throw new InternalCompilerErrorException("Unknown architecture");
+ throw new InternalCompilerErrorException($"Unknown architecture '{architecture}'");
}
return potentialType.Name;
{
return SimdVectorLength.None;
}
+ else if (_targetArchitecture == TargetArchitecture.RiscV64)
+ {
+ return SimdVectorLength.None;
+ }
else
{
Debug.Assert(false); // Unknown architecture
// 16-byte alignment for __m256.
alignment = new LayoutInt(16);
}
+ else if (defType.Context.Target.Architecture == TargetArchitecture.RiscV64)
+ {
+ // TODO-RISCV64: Update alignment to proper value when we implement RISC-V intrinsic.
+ // RISC-V Vector Extenstion Intrinsic Document
+ // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/vector_type_infos.adoc
+ alignment = new LayoutInt(16);
+ }
else
{
alignment = new LayoutInt(32);
// 16-byte alignment for __m256.
alignment = new LayoutInt(16);
}
+ else if (defType.Context.Target.Architecture == TargetArchitecture.RiscV64)
+ {
+ // TODO-RISCV64: Update alignment to proper value when we implement RISC-V intrinsic.
+ // RISC-V Vector Extenstion Intrinsic Document
+ // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/vector_type_infos.adoc
+ alignment = new LayoutInt(16);
+ }
else
{
alignment = new LayoutInt(64);
ARM = 0x01c4,
ARM64 = 0xaa64,
LoongArch64 = 0x6264,
+ RiscV64 = 0x5064,
}
internal const string JitLibrary = "clrjitilc";
if (codeSize < _code.Length)
{
- if (_compilation.TypeSystemContext.Target.Architecture != TargetArchitecture.ARM64)
+ if (_compilation.TypeSystemContext.Target.Architecture != TargetArchitecture.ARM64
+ && _compilation.TypeSystemContext.Target.Architecture != TargetArchitecture.RiscV64)
{
- // For xarch/arm32, the generated code is sometimes smaller than the memory allocated.
+ // For xarch/arm32/RiscV64, the generated code is sometimes smaller than the memory allocated.
// In that case, trim the codeBlock to the actual value.
//
// For arm64, the allocation request of `hotCodeSize` also includes the roData size
return 0;
}
}
+ case TargetArchitecture.RiscV64:
+ {
+ const ushort IMAGE_REL_RISCV64_PC = 3;
+
+ switch (fRelocType)
+ {
+ case IMAGE_REL_RISCV64_PC:
+ return RelocType.IMAGE_REL_BASED_RISCV64_PC;
+ default:
+ Debug.Fail("Invalid RelocType: " + fRelocType);
+ return 0;
+ }
+ }
default:
return (RelocType)fRelocType;
}
return (uint)ImageFileMachine.ARM64;
case TargetArchitecture.LoongArch64:
return (uint)ImageFileMachine.LoongArch64;
+ case TargetArchitecture.RiscV64:
+ return (uint)ImageFileMachine.RiscV64;
default:
throw new NotImplementedException("Expected target architecture is not supported");
}
flags.Set(CorJitFlag.CORJIT_FLAG_RELATIVE_CODE_RELOCS);
#endif
+ if (targetArchitecture == TargetArchitecture.RiscV64)
+ flags.Set(CorJitFlag.CORJIT_FLAG_FRAMED);
+
if (this.MethodBeingCompiled.IsUnmanagedCallersOnly)
{
// Validate UnmanagedCallersOnlyAttribute usage
// bit 5: `1` means the second field's size is 8.
//
// Note that bit 0 and 3 cannot both be set.
+ [Flags]
public enum StructFloatFieldInfoFlags
{
STRUCT_NO_FLOAT_FIELD = 0x0,
TargetArchitecture.ARM => "arm",
TargetArchitecture.ARM64 => "arm64",
TargetArchitecture.LoongArch64 => "loongarch64",
+ TargetArchitecture.RiscV64 => "riscv64",
_ => throw new NotImplementedException(target.Architecture.ToString())
};
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using ILCompiler;
using Internal.TypeSystem;
+using static Internal.JitInterface.StructFloatFieldInfoFlags;
namespace Internal.JitInterface
{
-
internal static class RISCV64PassStructInRegister
{
- public static uint GetRISCV64PassStructInRegisterFlags(TypeDesc typeDesc)
+ private const int
+ ENREGISTERED_PARAMTYPE_MAXSIZE = 16,
+ TARGET_POINTER_SIZE = 8;
+
+ private static bool HandleInlineArray(int elementTypeIndex, int nElements, Span<StructFloatFieldInfoFlags> types, ref int typeIndex)
{
- FieldDesc firstField = null;
- uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
- int numIntroducedFields = 0;
- foreach (FieldDesc field in typeDesc.GetFields())
- {
- if (!field.IsStatic)
- {
- firstField ??= field;
- numIntroducedFields++;
- }
- }
+ int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex;
+ if (nFlattenedFieldsPerElement == 0)
+ return true;
+
+ Debug.Assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2);
- if ((numIntroducedFields == 0) || (numIntroducedFields > 2) || (typeDesc.GetElementSize().AsInt > 16))
+ if (nElements > 2)
+ return false;
+
+ if (nElements == 2)
{
- return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+ if (typeIndex + nFlattenedFieldsPerElement > 2)
+ return false;
+
+ Debug.Assert(elementTypeIndex == 0);
+ Debug.Assert(typeIndex == 1);
+ types[typeIndex++] = types[elementTypeIndex]; // duplicate the array element type
}
+ return true;
+ }
- //// The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers
- if (typeDesc.IsIntrinsic)
+ private static bool FlattenFieldTypes(TypeDesc td, Span<StructFloatFieldInfoFlags> types, ref int typeIndex)
+ {
+ IEnumerable<FieldDesc> fields = td.GetFields();
+ int nFields = 0;
+ int elementTypeIndex = typeIndex;
+ FieldDesc prevField = null;
+ foreach (FieldDesc field in fields)
{
- throw new NotImplementedException("For RISCV64, SIMD would be implemented later");
- }
+ if (field.IsStatic)
+ continue;
+ nFields++;
- MetadataType mdType = typeDesc as MetadataType;
- Debug.Assert(mdType != null);
+ if (prevField != null && prevField.Offset.AsInt + prevField.FieldType.GetElementSize().AsInt > field.Offset.AsInt)
+ return false; // overlapping fields
- TypeDesc firstFieldElementType = firstField.FieldType;
- int firstFieldSize = firstFieldElementType.GetElementSize().AsInt;
- bool hasImpliedRepeatedFields = mdType.HasImpliedRepeatedFields();
+ prevField = field;
- if (hasImpliedRepeatedFields)
- {
- numIntroducedFields = typeDesc.GetElementSize().AsInt / firstFieldSize;
- if (numIntroducedFields > 2)
+ TypeFlags category = field.FieldType.Category;
+ if (category == TypeFlags.ValueType)
{
- return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+ TypeDesc nested = field.FieldType;
+ if (!FlattenFieldTypes(nested, types, ref typeIndex))
+ return false;
}
- }
-
- int fieldIndex = 0;
- foreach (FieldDesc field in typeDesc.GetFields())
- {
- if (fieldIndex > 1)
+ else if (field.FieldType.GetElementSize().AsInt <= TARGET_POINTER_SIZE)
{
- return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+ if (typeIndex >= 2)
+ return false;
+
+ StructFloatFieldInfoFlags type =
+ (category is TypeFlags.Single or TypeFlags.Double ? STRUCT_FLOAT_FIELD_FIRST : (StructFloatFieldInfoFlags)0) |
+ (field.FieldType.GetElementSize().AsInt == TARGET_POINTER_SIZE ? STRUCT_FIRST_FIELD_SIZE_IS8 : (StructFloatFieldInfoFlags)0);
+ types[typeIndex++] = type;
}
- else if (field.IsStatic)
+ else
{
- continue;
+ return false;
}
+ }
- Debug.Assert(fieldIndex < numIntroducedFields);
+ if ((td as MetadataType).HasImpliedRepeatedFields())
+ {
+ Debug.Assert(nFields == 1);
+ int nElements = td.GetElementSize().AsInt / prevField.FieldType.GetElementSize().AsInt;
+ if (!HandleInlineArray(elementTypeIndex, nElements, types, ref typeIndex))
+ return false;
+ }
+ return true;
+ }
- switch (field.FieldType.Category)
- {
- case TypeFlags.Double:
- {
- if (numIntroducedFields == 1)
- {
- floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE;
- }
- else if (fieldIndex == 0)
- {
- floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_DOUBLE;
- }
- else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0)
- {
- floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND_8;
- }
- else
- {
- floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_DOUBLE;
- }
- }
- break;
-
- case TypeFlags.Single:
- {
- if (numIntroducedFields == 1)
- {
- floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE;
- }
- else if (fieldIndex == 0)
- {
- floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST;
- }
- else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0)
- {
- floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND;
- }
- else
- {
- floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND;
- }
- }
- break;
-
- case TypeFlags.ValueType:
- //case TypeFlags.Class:
- //case TypeFlags.Array:
- //case TypeFlags.SzArray:
- {
- uint floatFieldFlags2 = GetRISCV64PassStructInRegisterFlags(field.FieldType);
- if (numIntroducedFields == 1)
- {
- floatFieldFlags = floatFieldFlags2;
- }
- else if (field.FieldType.GetElementSize().AsInt > 8)
- {
- return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
- }
- else if (fieldIndex == 0)
- {
- if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
- {
- floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST;
- }
- if (field.FieldType.GetElementSize().AsInt == 8)
- {
- floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8;
- }
- }
- else
- {
- Debug.Assert(fieldIndex == 1);
- if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
- {
- floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND;
- }
- if (field.FieldType.GetElementSize().AsInt == 8)
- {
- floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8;
- }
-
- floatFieldFlags2 = floatFieldFlags & ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND);
- if (floatFieldFlags2 == 0)
- {
- floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
- }
- else if (floatFieldFlags2 == ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND))
- {
- floatFieldFlags ^= ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND);
- }
- }
- }
- break;
-
- default:
- {
- if (field.FieldType.GetElementSize().AsInt == 8)
- {
- if (numIntroducedFields > 1)
- {
- if (fieldIndex == 0)
- {
- floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8;
- }
- else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0)
- {
- floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8;
- }
- else
- {
- floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
- }
- }
- }
- else if (fieldIndex == 1)
- {
- floatFieldFlags = (floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) > 0 ? floatFieldFlags : (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
- }
- break;
- }
- }
+ public static uint GetRISCV64PassStructInRegisterFlags(TypeDesc td)
+ {
+ if (td.GetElementSize().AsInt > ENREGISTERED_PARAMTYPE_MAXSIZE)
+ return (uint)STRUCT_NO_FLOAT_FIELD;
- fieldIndex++;
- }
+ Span<StructFloatFieldInfoFlags> types = stackalloc StructFloatFieldInfoFlags[] {
+ STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD
+ };
+ int nFields = 0;
+ if (!FlattenFieldTypes(td, types, ref nFields) || nFields == 0)
+ return (uint)STRUCT_NO_FLOAT_FIELD;
+
+ Debug.Assert(nFields == 1 || nFields == 2);
+
+ Debug.Assert((uint)(STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8)
+ == (uint)(STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8) << 1,
+ "SECOND flags need to be FIRST shifted by 1");
+ StructFloatFieldInfoFlags flags = types[0] | (StructFloatFieldInfoFlags)((uint)types[1] << 1);
+
+ const StructFloatFieldInfoFlags bothFloat = STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND;
+ if ((flags & bothFloat) == 0)
+ return (uint)STRUCT_NO_FLOAT_FIELD;
- return floatFieldFlags;
+ if ((flags & bothFloat) == bothFloat)
+ {
+ Debug.Assert(nFields == 2);
+ flags ^= (bothFloat | STRUCT_FLOAT_FIELD_ONLY_TWO); // replace bothFloat with ONLY_TWO
+ }
+ else if (nFields == 1)
+ {
+ Debug.Assert((flags & STRUCT_FLOAT_FIELD_FIRST) != 0);
+ flags ^= (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_ONE); // replace FIRST with ONLY_ONE
+ }
+ return (uint)flags;
}
}
}
using System.Diagnostics;
using ILCompiler;
using Internal.TypeSystem;
+using System.Runtime.CompilerServices;
using static Internal.JitInterface.SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR;
using static Internal.JitInterface.SystemVClassificationType;
if (numIntroducedFields == 0)
{
- return false;
+ // Classify empty struct like padding
+ helper.LargestFieldOffset = startOffsetOfStruct;
+ AssignClassifiedEightByteTypes(ref helper);
+ return true;
}
// The SIMD and Int128 Intrinsic types are meant to be handled specially and should not be passed as struct registers
// Calculate the eightbytes and their types.
int lastFieldOrdinal = sortedFieldOrder[largestFieldOffset];
- int offsetAfterLastFieldByte = largestFieldOffset + helper.FieldSizes[lastFieldOrdinal];
- SystemVClassificationType lastFieldClassification = helper.FieldClassifications[lastFieldOrdinal];
+ int lastFieldSize = (lastFieldOrdinal >= 0) ? helper.FieldSizes[lastFieldOrdinal] : 0;
+ int offsetAfterLastFieldByte = largestFieldOffset + lastFieldSize;
+ Debug.Assert(offsetAfterLastFieldByte <= helper.StructSize);
+ SystemVClassificationType lastFieldClassification = (lastFieldOrdinal >= 0)
+ ? helper.FieldClassifications[lastFieldOrdinal]
+ : SystemVClassificationTypeNoClass;
int usedEightBytes = 0;
int accumulatedSizeForEightBytes = 0;
// the SysV ABI spec.
fieldSize = 1;
fieldClassificationType = offset < offsetAfterLastFieldByte ? SystemVClassificationTypeNoClass : lastFieldClassification;
+ if (offset % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0) // new eightbyte
+ foundFieldInEightByte = false;
}
else
{
}
}
- if ((offset + 1) % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0) // If we just finished checking the last byte of an eightbyte
+ // If we just finished checking the last byte of an eightbyte or the entire struct
+ if ((offset + 1) % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0 || (offset + 1) == helper.StructSize)
{
if (!foundFieldInEightByte)
{
- // If we didn't find a field in an eight-byte (i.e. there are no explicit offsets that start a field in this eightbyte)
+ // If we didn't find a field in an eightbyte (i.e. there are no explicit offsets that start a field in this eightbyte)
// then the classification of this eightbyte might be NoClass. We can't hand a classification of NoClass to the JIT
// so set the class to Integer (as though the struct has a char[8] padding) if the class is NoClass.
+ //
+ // TODO: Fix JIT, NoClass eightbytes are valid and passing them is broken because of this.
if (helper.EightByteClassifications[offset / SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES] == SystemVClassificationTypeNoClass)
{
helper.EightByteClassifications[offset / SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES] = SystemVClassificationTypeInteger;
X86,
Wasm32,
LoongArch64,
+ RiscV64,
}
}
case TargetArchitecture.ARM64:
case TargetArchitecture.X64:
case TargetArchitecture.LoongArch64:
+ case TargetArchitecture.RiscV64:
return 8;
case TargetArchitecture.ARM:
case TargetArchitecture.X86:
{
return 16;
}
+ else if (Architecture == TargetArchitecture.RiscV64)
+ {
+ return 16;
+ }
// 512-bit vector is the type with the highest alignment we support
return 64;
return 2;
case TargetArchitecture.ARM64:
case TargetArchitecture.LoongArch64:
+ case TargetArchitecture.RiscV64:
return 4;
default:
return 1;
case TargetArchitecture.X64:
case TargetArchitecture.ARM64:
case TargetArchitecture.LoongArch64:
+ case TargetArchitecture.RiscV64:
return new LayoutInt(8);
case TargetArchitecture.X86:
return new LayoutInt(4);
Debug.Assert(Architecture == TargetArchitecture.ARM ||
Architecture == TargetArchitecture.ARM64 ||
Architecture == TargetArchitecture.LoongArch64 ||
+ Architecture == TargetArchitecture.RiscV64 ||
Architecture == TargetArchitecture.X64 ||
Architecture == TargetArchitecture.X86);
using ILCompiler.DependencyAnalysis.X86;
using ILCompiler.DependencyAnalysis.ARM64;
using ILCompiler.DependencyAnalysis.LoongArch64;
+using ILCompiler.DependencyAnalysis.RiscV64;
namespace ILCompiler.DependencyAnalysis
{
{
throw new NotImplementedException();
}
+ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instructionEncoder, bool relocsOnly)
+ {
+ throw new NotImplementedException();
+ }
public override int ClassCode => 588185132;
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using ILCompiler.DependencyAnalysis.RiscV64;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ public partial class JumpStubNode
+ {
+ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly)
+ {
+ encoder.EmitJMP(_target);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+using ILCompiler.DependencyAnalysis.RiscV64;
+
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ public partial class ReadyToRunGenericHelperNode
+ {
+ protected Register GetContextRegister(ref /* readonly */ RiscV64Emitter encoder)
+ {
+ if (_id == ReadyToRunHelperId.DelegateCtor)
+ return encoder.TargetRegister.Arg2;
+ else
+ return encoder.TargetRegister.Arg0;
+ }
+
+ protected void EmitDictionaryLookup(NodeFactory factory, ref RiscV64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly)
+ {
+ // INVARIANT: must not trash context register
+
+ // Find the generic dictionary slot
+ int dictionarySlot = 0;
+ if (!relocsOnly)
+ {
+ // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly.
+ if (!factory.GenericDictionaryLayout(_dictionaryOwner).TryGetSlotForEntry(lookup, out dictionarySlot))
+ {
+ encoder.EmitLI(result, 0);
+ return;
+ }
+ }
+
+ // Load the generic dictionary cell
+ encoder.EmitLD(result, context, dictionarySlot * factory.Target.PointerSize);
+ }
+
+ protected sealed override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly)
+ {
+ // First load the generic context into the context register.
+ EmitLoadGenericContext(factory, ref encoder, relocsOnly);
+
+ Register contextRegister = GetContextRegister(ref encoder);
+
+ switch (_id)
+ {
+ case ReadyToRunHelperId.GetNonGCStaticBase:
+ {
+ Debug.Assert(contextRegister == encoder.TargetRegister.Arg0);
+
+ EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
+
+ if (!TriggersLazyStaticConstructor(factory))
+ {
+ encoder.EmitRET();
+ }
+ else
+ {
+ // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
+ encoder.EmitADDI(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg0, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target));
+ encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, 0);
+ encoder.EmitRETIfZero(encoder.TargetRegister.Arg2);
+
+ encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
+ encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg3);
+
+ encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
+ }
+ }
+ break;
+
+ case ReadyToRunHelperId.GetGCStaticBase:
+ {
+ Debug.Assert(contextRegister == encoder.TargetRegister.Arg0);
+
+ encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0);
+ EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
+ encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, 0);
+
+ MetadataType target = (MetadataType)_target;
+ if (!TriggersLazyStaticConstructor(factory))
+ {
+ encoder.EmitRET();
+ }
+ else
+ {
+ // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
+ GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
+ EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly);
+
+ encoder.EmitADDI(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target));
+ encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, 0);
+ encoder.EmitRETIfZero(encoder.TargetRegister.Arg3);
+
+ encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
+ encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2);
+
+ encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
+ }
+ }
+ break;
+
+ case ReadyToRunHelperId.GetThreadStaticBase:
+ {
+ Debug.Assert(contextRegister == encoder.TargetRegister.Arg0);
+
+ MetadataType target = (MetadataType)_target;
+
+ // Look up the index cell
+ EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly);
+
+ ISymbolNode helperEntrypoint;
+ if (TriggersLazyStaticConstructor(factory))
+ {
+ // There is a lazy class constructor. We need the non-GC static base because that's where the
+ // class constructor context lives.
+ GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
+ EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly);
+ int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextSize(factory.Target);
+ encoder.EmitADDI(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -cctorContextSize);
+
+ helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase);
+ }
+ else
+ {
+ helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType);
+ }
+
+ // First arg: address of the TypeManager slot that provides the helper with
+ // information about module index and the type manager instance (which is used
+ // for initialization on first access).
+ encoder.EmitLD(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, 0);
+
+ // Second arg: index of the type in the ThreadStatic section of the modules
+ encoder.EmitLD(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg1, factory.Target.PointerSize);
+
+ encoder.EmitJMP(helperEntrypoint);
+ }
+ break;
+
+ case ReadyToRunHelperId.DelegateCtor:
+ {
+ // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor
+ // method expects. Codegen also passed us the generic context in Arg2.
+ // We now need to load the delegate target method into Arg2 (using a dictionary lookup)
+ // and the optional 4th parameter, and call the ctor.
+
+ Debug.Assert(contextRegister == encoder.TargetRegister.Arg2);
+
+ var target = (DelegateCreationInfo)_target;
+
+ EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly);
+
+ if (target.Thunk != null)
+ {
+ Debug.Assert(target.Constructor.Method.Signature.Length == 3);
+ encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk);
+ }
+ else
+ {
+ Debug.Assert(target.Constructor.Method.Signature.Length == 2);
+ }
+
+ encoder.EmitJMP(target.Constructor);
+ }
+ break;
+
+ // These are all simple: just get the thing from the dictionary and we're done
+ case ReadyToRunHelperId.TypeHandle:
+ case ReadyToRunHelperId.MethodHandle:
+ case ReadyToRunHelperId.FieldHandle:
+ case ReadyToRunHelperId.MethodDictionary:
+ case ReadyToRunHelperId.MethodEntry:
+ case ReadyToRunHelperId.VirtualDispatchCell:
+ case ReadyToRunHelperId.DefaultConstructor:
+ case ReadyToRunHelperId.ObjectAllocator:
+ case ReadyToRunHelperId.TypeHandleForCasting:
+ case ReadyToRunHelperId.ConstrainedDirectCall:
+ {
+ EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
+ encoder.EmitRET();
+ }
+ break;
+
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ protected virtual void EmitLoadGenericContext(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly)
+ {
+ // Assume generic context is already loaded in the context register.
+ }
+ }
+
+ public partial class ReadyToRunGenericLookupFromTypeNode
+ {
+ protected override void EmitLoadGenericContext(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly)
+ {
+ // We start with context register pointing to the MethodTable
+ Register contextRegister = GetContextRegister(ref encoder);
+
+ // Locate the VTable slot that points to the dictionary
+ int vtableSlot = 0;
+ if (!relocsOnly)
+ {
+ // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly.
+ vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner);
+ }
+
+ int pointerSize = factory.Target.PointerSize;
+ int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize);
+
+ // Load the dictionary pointer from the VTable
+ encoder.EmitLD(contextRegister, contextRegister, slotOffset);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+
+using ILCompiler.DependencyAnalysis.RiscV64;
+using Internal.TypeSystem;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ /// <summary>
+ /// RiscV64 specific portions of ReadyToRunHelperNode
+ /// </summary>
+ public partial class ReadyToRunHelperNode
+ {
+ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly)
+ {
+ switch (Id)
+ {
+ case ReadyToRunHelperId.VirtualCall:
+ {
+ MethodDesc targetMethod = (MethodDesc)Target;
+
+ Debug.Assert(!targetMethod.OwningType.IsInterface);
+ Debug.Assert(!targetMethod.CanMethodBeInSealedVTable());
+
+ int pointerSize = factory.Target.PointerSize;
+
+ int slot = 0;
+ if (!relocsOnly)
+ {
+ slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
+ Debug.Assert(slot != -1);
+ }
+
+ encoder.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg0, 0);
+ encoder.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.IntraProcedureCallScratch1,
+ EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize));
+ encoder.EmitJMP(encoder.TargetRegister.IntraProcedureCallScratch1);
+ }
+ break;
+
+ case ReadyToRunHelperId.GetNonGCStaticBase:
+ {
+ MetadataType target = (MetadataType)Target;
+
+ bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target);
+ encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target));
+
+ if (!hasLazyStaticConstructor)
+ {
+ encoder.EmitRET();
+ }
+ else
+ {
+ // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
+ encoder.EmitADDI(encoder.TargetRegister.Arg3, encoder.TargetRegister.Result, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target));
+ encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg3, 0);
+ encoder.EmitRETIfZero(encoder.TargetRegister.Arg2);
+
+ encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
+ encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg3);
+
+ encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
+ }
+ }
+ break;
+
+ case ReadyToRunHelperId.GetThreadStaticBase:
+ {
+ MetadataType target = (MetadataType)Target;
+ encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target));
+
+ // First arg: address of the TypeManager slot that provides the helper with
+ // information about module index and the type manager instance (which is used
+ // for initialization on first access).
+ encoder.EmitLD(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, 0);
+
+ // Second arg: index of the type in the ThreadStatic section of the modules
+ encoder.EmitLD(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg2, factory.Target.PointerSize);
+ ISymbolNode helper = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType);
+ if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
+ {
+ encoder.EmitJMP(helper);
+ }
+ else
+ {
+ encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target));
+ encoder.EmitADDI(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target));
+
+ encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, 0);
+ encoder.EmitJMPIfZero(encoder.TargetRegister.Arg3, helper);
+
+ encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase));
+ }
+ }
+ break;
+
+ case ReadyToRunHelperId.GetGCStaticBase:
+ {
+ MetadataType target = (MetadataType)Target;
+
+ encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target));
+ encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, 0);
+
+ if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
+ {
+ encoder.EmitRET();
+ }
+ else
+ {
+ // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
+ encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target));
+ encoder.EmitADDI(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target));
+ encoder.EmitLD(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2, 0);
+ encoder.EmitRETIfZero(encoder.TargetRegister.Arg3);
+
+ encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
+ encoder.EmitMOV(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2);
+
+ encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
+ }
+ }
+ break;
+
+ case ReadyToRunHelperId.DelegateCtor:
+ {
+ DelegateCreationInfo target = (DelegateCreationInfo)Target;
+
+ if (target.TargetNeedsVTableLookup)
+ {
+ Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable());
+
+ encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1, 0);
+
+ int slot = 0;
+ if (!relocsOnly)
+ slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType);
+
+ Debug.Assert(slot != -1);
+ encoder.EmitLD(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2,
+ EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize));
+ }
+ else
+ {
+ encoder.EmitMOV(encoder.TargetRegister.Arg2, target.GetTargetNode(factory));
+ }
+
+ if (target.Thunk != null)
+ {
+ Debug.Assert(target.Constructor.Method.Signature.Length == 3);
+ encoder.EmitMOV(encoder.TargetRegister.Arg3, target.Thunk);
+ }
+ else
+ {
+ Debug.Assert(target.Constructor.Method.Signature.Length == 2);
+ }
+
+ encoder.EmitJMP(target.Constructor);
+ }
+ break;
+
+ case ReadyToRunHelperId.ResolveVirtualFunction:
+ {
+ // Not tested
+ encoder.EmitBreak();
+
+ MethodDesc targetMethod = (MethodDesc)Target;
+ if (targetMethod.OwningType.IsInterface)
+ {
+ encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod));
+ encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod"));
+ }
+ else
+ {
+ if (relocsOnly)
+ break;
+
+ encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0, 0);
+
+ Debug.Assert(!targetMethod.CanMethodBeInSealedVTable());
+
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
+ Debug.Assert(slot != -1);
+ encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result,
+ EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize));
+ encoder.EmitRET();
+ }
+ }
+ break;
+
+
+ default:
+ throw new NotImplementedException();
+ }
+ }
+ }
+}
--- /dev/null
+ï»ż// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using ILCompiler.DependencyAnalysis.RiscV64;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ public partial class TentativeMethodNode
+ {
+ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly)
+ {
+ encoder.EmitJMP(GetTarget(factory));
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using ILCompiler.DependencyAnalysis.RiscV64;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ public partial class UnboxingStubNode
+ {
+ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder, bool relocsOnly)
+ {
+ // addi a0, a0, sizeof(void*)
+ encoder.EmitADDI(encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, factory.Target.PointerSize);
+ encoder.EmitJMP(GetUnderlyingMethodEntrypoint(factory));
+ }
+ }
+}
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64Emitter.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64Emitter.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\Register.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\Register.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\TargetRegisterMap.cs" />
+ <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_RiscV64\AddrMode.cs" Link="Compiler\DependencyAnalysis\Target_RiscV64\AddrMode.cs" />
+ <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_RiscV64\RiscV64Emitter.cs" Link="Compiler\DependencyAnalysis\Target_RiscV64\RiscV64Emitter.cs" />
+ <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_RiscV64\Register.cs" Link="Compiler\DependencyAnalysis\Target_RiscV64\Register.cs" />
+ <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_RiscV64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_RiscV64\TargetRegisterMap.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\AddrMode.cs" Link="Compiler\DependencyAnalysis\Target_X64\AddrMode.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\Register.cs" Link="Compiler\DependencyAnalysis\Target_X64\Register.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_X64\TargetRegisterMap.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_ARM64\ARM64TentativeMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_ARM\ARMTentativeMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64TentativeMethodNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\Target_RiscV64\RiscV64TentativeMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_X64\X64TentativeMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_X86\X86TentativeMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\TentativeInstanceMethodNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64ReadyToRunHelperNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64ReadyToRunGenericHelperNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64UnboxingStubNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\Target_RiscV64\RiscV64JumpStubNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\Target_RiscV64\RiscV64ReadyToRunHelperNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\Target_RiscV64\RiscV64ReadyToRunGenericHelperNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\Target_RiscV64\RiscV64UnboxingStubNode.cs" />
<Compile Include="Compiler\UnmanagedEntryPointsRootProvider.cs" />
<Compile Include="Compiler\GenericDictionaryLookup.cs" />
<Compile Include="Compiler\IRootingServiceProvider.cs" />
<IsDotNetFrameworkProductAssembly>true</IsDotNetFrameworkProductAssembly>
<TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
<DefineConstants>READYTORUN;$(DefineConstants)</DefineConstants>
- <Platforms>x64;x86;arm;arm64</Platforms>
+ <Platforms>x64;x86;arm;arm64;</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
TargetArchitecture.ARM64 => PerfMapArchitectureToken.ARM64,
TargetArchitecture.X64 => PerfMapArchitectureToken.X64,
TargetArchitecture.X86 => PerfMapArchitectureToken.X86,
+ TargetArchitecture.RiscV64 => PerfMapArchitectureToken.RiscV64,
_ => throw new NotImplementedException(details.Architecture.ToString())
};
ARM64 = 2,
X64 = 3,
X86 = 4,
+ RiscV64 = 5,
}
public enum PerfMapOSToken : uint
return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) || _transitionBlock.IsArgPassedByRef(_argTypeHandle));
}
return false;
+ case TargetArchitecture.RiscV64:
+ if (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE)
+ {
+ Debug.Assert(!_argTypeHandle.IsNull());
+ return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) || _transitionBlock.IsArgPassedByRef(_argTypeHandle));
+ }
+ return false;
default:
throw new NotImplementedException();
}
_loongarch64IdxFPReg = 0;
break;
+ case TargetArchitecture.RiscV64:
+ _riscv64IdxGenReg = numRegistersUsed;
+ _riscv64OfsStack = 0;
+
+ _riscv64IdxFPReg = 0;
+ break;
default:
throw new NotImplementedException();
}
_hasArgLocDescForStructInRegs = true;
_argLocDescForStructInRegs.m_floatFlags = floatFieldFlags;
- int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * 8;
+ int argOfsInner = 0;
+ if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) != 0)
+ {
+ argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _loongarch64IdxGenReg * 8;
+ }
+ else
+ {
+ argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * 8;
+ }
+
_loongarch64IdxFPReg++;
_loongarch64IdxGenReg++;
return argOfsInner;
return argOfs;
}
+ case TargetArchitecture.RiscV64:
+ {
+ int cFPRegs = 0;
+ uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
+ _hasArgLocDescForStructInRegs = false;
+
+ switch (argType)
+ {
+ case CorElementType.ELEMENT_TYPE_R4:
+ // 32-bit floating point argument.
+ cFPRegs = 1;
+ break;
+
+ case CorElementType.ELEMENT_TYPE_R8:
+ // 64-bit floating point argument.
+ cFPRegs = 1;
+ break;
+
+ case CorElementType.ELEMENT_TYPE_VALUETYPE:
+ {
+ // Composite greater than 16 bytes should be passed by reference
+ if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize)
+ {
+ argSize = _transitionBlock.PointerSize;
+ }
+ else
+ {
+ floatFieldFlags = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle());
+ if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0)
+ {
+ cFPRegs = 2;
+ }
+ else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0)
+ {
+ cFPRegs = 1;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE);
+ int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, false);
+
+ if (cFPRegs > 0 && !IsVarArg)
+ {
+ if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0))
+ {
+ Debug.Assert(cFPRegs == 1);
+ if ((_riscv64IdxFPReg < 8) && (_riscv64IdxGenReg < 8))
+ {
+ _argLocDescForStructInRegs = new ArgLocDesc();
+ _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg;
+ _argLocDescForStructInRegs.m_cFloatReg = 1;
+
+ _argLocDescForStructInRegs.m_idxGenReg = _riscv64IdxGenReg;
+ _argLocDescForStructInRegs.m_cGenReg = 1;
+
+ _hasArgLocDescForStructInRegs = true;
+ _argLocDescForStructInRegs.m_floatFlags = floatFieldFlags;
+
+ int argOfsInner =
+ ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) != 0)
+ ? _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8
+ : _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8;
+
+ _riscv64IdxFPReg++;
+ _riscv64IdxGenReg++;
+ return argOfsInner;
+ }
+ }
+ else if (cFPRegs + _riscv64IdxFPReg <= 8)
+ {
+ // Each floating point register in the argument area is 8 bytes.
+ int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8;
+ if (floatFieldFlags == (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO)
+ {
+ // struct with two single-float fields.
+ _argLocDescForStructInRegs = new ArgLocDesc();
+ _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg;
+ _argLocDescForStructInRegs.m_cFloatReg = 2;
+ Debug.Assert(cFPRegs == 2);
+ Debug.Assert(argSize == 8);
+
+ _hasArgLocDescForStructInRegs = true;
+ _argLocDescForStructInRegs.m_floatFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO;
+ }
+ _riscv64IdxFPReg += cFPRegs;
+ return argOfsInner;
+ }
+ else
+ {
+ _riscv64IdxFPReg = 8;
+ }
+ }
+
+ {
+ Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0);
+
+ int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize;
+ // Only a0-a7 are valid argument registers.
+ if (_riscv64IdxGenReg + regSlots <= 8)
+ {
+ // The entirety of the arg fits in the register slots.
+ int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8;
+ _riscv64IdxGenReg += regSlots;
+ return argOfsInner;
+ }
+ else if (_riscv64IdxGenReg < 8)
+ {
+ int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8;
+ _riscv64IdxGenReg = 8;
+ _riscv64OfsStack += 8;
+ return argOfsInner;
+ }
+ }
+
+ argOfs = _transitionBlock.OffsetOfArgs + _riscv64OfsStack;
+ _riscv64OfsStack += cbArg;
+ return argOfs;
+ }
+
default:
throw new NotImplementedException();
}
return pLoc;
}
+ case TargetArchitecture.RiscV64:
+ {
+ if (_hasArgLocDescForStructInRegs)
+ {
+ return _argLocDescForStructInRegs;
+ }
+
+ // LIMITED_METHOD_CONTRACT;
+
+ ArgLocDesc pLoc = new ArgLocDesc();
+
+ if (_transitionBlock.IsFloatArgumentRegisterOffset(argOffset))
+ {
+ int floatRegOfsInBytes = argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters;
+ Debug.Assert((floatRegOfsInBytes % _transitionBlock.FloatRegisterSize) == 0);
+ pLoc.m_idxFloatReg = floatRegOfsInBytes / _transitionBlock.FloatRegisterSize;
+ pLoc.m_cFloatReg = 1;
+
+ return pLoc;
+ }
+
+ int byteArgSize = GetArgSize();
+
+ // Composites greater than 16bytes are passed by reference
+ TypeHandle dummy;
+ if (GetArgType(out dummy) == CorElementType.ELEMENT_TYPE_VALUETYPE && GetArgSize() > _transitionBlock.EnregisteredParamTypeMaxSize)
+ {
+ byteArgSize = _transitionBlock.PointerSize;
+ }
+
+ if (!_transitionBlock.IsStackArgumentOffset(argOffset))
+ {
+ pLoc.m_idxGenReg = _transitionBlock.GetArgumentIndexFromOffset(argOffset);
+ if ((pLoc.m_idxGenReg == 7) && (byteArgSize > _transitionBlock.PointerSize))
+ {
+ pLoc.m_cGenReg = 1;
+ pLoc.m_byteStackIndex = 0;
+ pLoc.m_byteStackSize = 8;
+ }
+ else
+ pLoc.m_cGenReg = (short)(ALIGN_UP(byteArgSize, _transitionBlock.PointerSize) / _transitionBlock.PointerSize);
+ }
+ else
+ {
+ pLoc.m_byteStackIndex = _transitionBlock.GetStackArgumentByteIndexFromOffset(argOffset);
+ pLoc.m_byteStackSize = _transitionBlock.StackElemSize(byteArgSize, IsValueType(), IsFloatHfa());
+ }
+ return pLoc;
+ }
+
case TargetArchitecture.X64:
if (_transitionBlock.IsX64UnixABI)
{
private int _loongarch64OfsStack; // Offset of next stack location to be assigned a value
private int _loongarch64IdxFPReg; // Next FP register to be assigned a value
+ private int _riscv64IdxGenReg; // Next general register to be assigned a value
+ private int _riscv64OfsStack; // Offset of next stack location to be assigned a value
+ private int _riscv64IdxFPReg; // Next FP register to be assigned a value
+
// These are enum flags in CallingConventions.h, but that's really ugly in C#, so I've changed them to bools.
private bool _ITERATION_STARTED; // Started iterating over arguments
private bool _SIZE_OF_ARG_STACK_COMPUTED;
// as that's what CoreCLR does (zapcode.cpp, ZapUnwindData::Save).
unwindInfo[0] |= (byte)((UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER) << FlagsShift);
}
- else if ((targetArch == TargetArchitecture.ARM) || (targetArch == TargetArchitecture.ARM64) || (targetArch == TargetArchitecture.LoongArch64))
+ else if ((targetArch == TargetArchitecture.ARM) || (targetArch == TargetArchitecture.ARM64) || (targetArch == TargetArchitecture.LoongArch64) || (targetArch == TargetArchitecture.RiscV64))
{
// Set the 'X' bit to indicate that there is a personality routine associated with this method
unwindInfo[2] |= 1 << 4;
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+using ILCompiler.DependencyAnalysis.RiscV64;
+
+namespace ILCompiler.DependencyAnalysis.ReadyToRun
+{
+ /// <summary>
+ /// This node emits a thunk calling DelayLoad_Helper with a given instance signature
+ /// to populate its indirection cell.
+ /// </summary>
+ public partial class ImportThunk
+ {
+ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instructionEncoder, bool relocsOnly)
+ {
+
+ switch (_thunkKind)
+ {
+ case Kind.Eager:
+ break;
+
+ case Kind.DelayLoadHelper:
+ case Kind.VirtualStubDispatch:
+ // t5 contains indirection cell
+ // Do nothing t5 contains our first param
+ if (!relocsOnly)
+ {
+ // li t0, #index
+ int index = _containingImportSection.IndexFromBeginningOfArray;
+ instructionEncoder.EmitLI(Register.X5, index);
+ }
+ // get pc
+ // auipc t1, 0
+ instructionEncoder.EmitPC(Register.X6);
+
+ // load Module* -> t1
+ instructionEncoder.EmitLD(Register.X6, Register.X6, 0x24);
+
+ // ld t1, t1, 0
+ instructionEncoder.EmitLD(Register.X6, Register.X6, 0);
+ break;
+
+ case Kind.Lazy:
+ // get pc
+ instructionEncoder.EmitPC(Register.X11);
+
+ // load Module* -> a1
+ instructionEncoder.EmitLD(Register.X11, Register.X11, 0x24);
+
+ // ld a1, a1, 0
+ instructionEncoder.EmitLD(Register.X11, Register.X11, 0);
+ break;
+
+ default:
+ throw new NotImplementedException();
+ }
+
+ // branch to helper
+ instructionEncoder.EmitJMP(_helperCell);
+
+ // Emit relocation for the Module* load above
+ if (_thunkKind != Kind.Eager)
+ instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);
+ }
+ }
+}
case TargetArchitecture.LoongArch64:
return LoongArch64TransitionBlock.Instance;
+ case TargetArchitecture.RiscV64:
+ return RiscV64TransitionBlock.Instance;
+
default:
throw new NotImplementedException(target.Architecture.ToString());
}
public bool IsARM => Architecture == TargetArchitecture.ARM;
public bool IsARM64 => Architecture == TargetArchitecture.ARM64;
public bool IsLoongArch64 => Architecture == TargetArchitecture.LoongArch64;
+ public bool IsRiscV64 => Architecture == TargetArchitecture.RiscV64;
/// <summary>
/// This property is only overridden in AMD64 Unix variant of the transition block.
{
if (IsLoongArch64)
fpReturnSize = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff;
+ else if (IsRiscV64)
+ fpReturnSize = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff;
break;
+
}
}
return ALIGN_UP(parmSize, stackSlotSize);
}
}
+
+ private class RiscV64TransitionBlock : TransitionBlock
+ {
+ public static TransitionBlock Instance = new RiscV64TransitionBlock();
+ public override TargetArchitecture Architecture => TargetArchitecture.RiscV64;
+ public override int PointerSize => 8;
+ public override int FloatRegisterSize => 8;
+ // a0 .. a7
+ public override int NumArgumentRegisters => 8;
+ // fp=x8, ra=x1, s1-s11(R9,R18-R27), tp=x3, gp=x4
+ public override int NumCalleeSavedRegisters => 15;
+ // Callee-saves, argument registers
+ public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters;
+ public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters;
+ public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot;
+
+ public override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double);
+ public override int EnregisteredParamTypeMaxSize => 16;
+ public override int EnregisteredReturnTypeIntegerMaxSize => 16;
+
+ public override bool IsArgPassedByRef(TypeHandle th)
+ {
+ Debug.Assert(!th.IsNull());
+ Debug.Assert(th.IsValueType());
+
+ // Composites greater than 16 bytes are passed by reference
+ return th.GetSize() > EnregisteredParamTypeMaxSize;
+ }
+
+ public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfFirstGCRefMapSlot + (hasThis ? 8 : 0);
+
+ public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false)
+ {
+ int stackSlotSize = 8;
+ return ALIGN_UP(parmSize, stackSlotSize);
+ }
+
+ }
}
}
/// </summary>
private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64 = 8;
+ /// <summary>
+ /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for RISCV64
+ /// </summary>
+ private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobRISCV64 = 8;
+
protected override bool CompareKeyToValue(EcmaModule key, ModuleFieldLayout value)
{
return key == value.Module;
nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64;
break;
+ case TargetArchitecture.RiscV64:
+ nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobRISCV64;
+ break;
+
default:
throw new NotImplementedException();
}
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64Emitter.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\LoongArch64Emitter.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\Register.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\Register.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_LoongArch64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_LoongArch64\TargetRegisterMap.cs" />
+ <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_RiscV64\AddrMode.cs" Link="Compiler\DependencyAnalysis\Target_RiscV64\AddrMode.cs" />
+ <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_RiscV64\RiscV64Emitter.cs" Link="Compiler\DependencyAnalysis\Target_RiscV64\RiscV64Emitter.cs" />
+ <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_RiscV64\Register.cs" Link="Compiler\DependencyAnalysis\Target_RiscV64\Register.cs" />
+ <Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_RiscV64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_RiscV64\TargetRegisterMap.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\AddrMode.cs" Link="Compiler\DependencyAnalysis\Target_X64\AddrMode.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\Register.cs" Link="Compiler\DependencyAnalysis\Target_X64\Register.cs" />
<Compile Include="..\..\Common\Compiler\DependencyAnalysis\Target_X64\TargetRegisterMap.cs" Link="Compiler\DependencyAnalysis\Target_X64\TargetRegisterMap.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_ARM64\ImportThunk.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_ARM\ImportThunk.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_LoongArch64\ImportThunk.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_RiscV64\ImportThunk.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_X64\ImportThunk.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Target_X86\ImportThunk.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\TransitionBlock.cs" />
break;
}
+ case RelocType.IMAGE_REL_BASED_RISCV64_PC:
+ {
+ relocationLength = 8;
+ delta = targetRVA - sourceRVA;
+ break;
+ }
+
default:
throw new NotSupportedException();
}
if (((relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21) ||
(relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A) ||
(relocationType == RelocType.IMAGE_REL_BASED_LOONGARCH64_PC) ||
- (relocationType == RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR)
+ (relocationType == RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR) ||
+ (relocationType == RelocType.IMAGE_REL_BASED_RISCV64_PC)
) && (value != 0))
{
throw new NotSupportedException();
_codePadding = 0x002A0005u;
break;
+ case TargetArchitecture.RiscV64:
+ _codePadding = 0x00100073u;
+ break;
+
default:
throw new NotImplementedException();
}
case Internal.TypeSystem.TargetArchitecture.LoongArch64:
return Machine.LoongArch64;
+ case Internal.TypeSystem.TargetArchitecture.RiscV64:
+ return (Machine)0x5064; /* TODO: update with RiscV64 */
+
default:
throw new NotImplementedException(target.Architecture.ToString());
}
}
sb.AppendLine($" Has Tailcalls: {_wantsReportOnlyLeaf}");
}
+ else if (_machine == (Machine)0x5064)
+ {
+ if (StackBaseRegister != 0xffffffff)
+ {
+ sb.AppendLine($" StackBaseRegister: {(RiscV64.Registers)StackBaseRegister}");
+ }
+ sb.AppendLine($" Has Tailcalls: {_wantsReportOnlyLeaf}");
+ }
sb.AppendLine($" Size of parameter area: 0x{SizeOfStackOutgoingAndScratchArea:X}");
if (SizeOfEditAndContinuePreservedArea != 0xffffffff)
case Machine.LoongArch64:
return ((LoongArch64.Registers)registerNumber).ToString();
+ case (Machine)0x5064:
+ return ((RiscV64.Registers)registerNumber).ToString();
+
default:
throw new NotImplementedException(machine.ToString());
}
regType = typeof(LoongArch64.Registers);
break;
+ case (Machine)0x5064:
+ regType = typeof(RiscV64.Registers);
+ break;
+
default:
throw new NotImplementedException();
}
return ((Arm64.Registers)regnum).ToString();
case Machine.LoongArch64:
return ((LoongArch64.Registers)regnum).ToString();
+ case (Machine)0x5064:
+ return ((RiscV64.Registers)regnum).ToString();
default:
throw new NotImplementedException($"No implementation for machine type {machine}.");
}
STACK_BASE_REGISTER_ENCBASE = 2;
NUM_REGISTERS_ENCBASE = 3;
break;
+ case (Machine)0x5064: /* TODO: update with RiscV64 */
+ SIZE_OF_RETURN_KIND_FAT = 4;
+ STACK_BASE_REGISTER_ENCBASE = 2;
+ NUM_REGISTERS_ENCBASE = 3;
+ break;
}
}
return (x << 1);
case Machine.Arm64:
case Machine.LoongArch64:
+ case (Machine)0x5064: /* TODO: update with RiscV64 */
return (x << 2);
}
return x;
return (x << 2);
case Machine.Arm64:
case Machine.LoongArch64:
+ case (Machine)0x5064: /* TODO: update with RiscV64 */
return (x << 3);
}
return x;
return (x ^ 29);
case Machine.LoongArch64:
return ((x ^ 22) & 0x3);
+ case (Machine)0x5064: /* TODO: update with RiscV64 */
+ return (x ^ 8);
}
return x;
}
return (x << 2);
case Machine.Arm64:
case Machine.LoongArch64:
+ case (Machine)0x5064: /* TODO: update with RiscV64 */
return (x << 3);
}
return x;
{
return (int)loongarch64Info.FunctionLength;
}
+ else if (UnwindInfo is RiscV64.UnwindInfo riscv64Info)
+ {
+ return (int)riscv64Info.FunctionLength;
+ }
else if (Method.GcInfo != null)
{
return Method.GcInfo.CodeLength;
}
else
{
- // Arm, Arm64 and LoongArch64 use the same GcInfo format as Amd64
+ // Arm, Arm64, LoongArch64 and RISCV64 use the same GcInfo format as Amd64
_gcInfo = new Amd64.GcInfo(_readyToRunReader.Image, gcInfoOffset, _readyToRunReader.Machine, _readyToRunReader.ReadyToRunHeader.MajorVersion);
}
}
{
unwindInfo = new LoongArch64.UnwindInfo(_readyToRunReader.Image, unwindOffset);
}
+ else if (_readyToRunReader.Machine == (Machine)0x5064)
+ {
+ unwindInfo = new RiscV64.UnwindInfo(_readyToRunReader.Image, unwindOffset);
+ }
if (i == 0 && unwindInfo != null)
{
case Machine.Amd64:
case Machine.Arm64:
case Machine.LoongArch64:
+ case (Machine)0x5064: /* TODO: update with RiscV64 */
_pointerSize = 8;
break;
case Machine.Amd64:
case Machine.Arm64:
case Machine.LoongArch64:
+ case (Machine)0x5064: /* TODO: update with RiscV64 */
entrySize = 8;
break;
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace ILCompiler.Reflection.ReadyToRun.RiscV64
+{
+ public enum Registers
+ {
+ Zero,
+ Ra,
+ Sp,
+ Gp,
+ Tp,
+ T0,
+ T1,
+ T2,
+ Fp,
+ S1,
+ A0,
+ A1,
+ A2,
+ A3,
+ A4,
+ A5,
+ A6,
+ A7,
+ S2,
+ S3,
+ S4,
+ S5,
+ S6,
+ S7,
+ S8,
+ S9,
+ S10,
+ S11,
+ T3,
+ T4,
+ T5,
+ T6,
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace ILCompiler.Reflection.ReadyToRun.RiscV64
+{
+ public class Epilog
+ {
+ public int Index { get; set; }
+
+ public uint EpilogStartOffset { get; set; }
+ public uint Res { get; set; }
+ public uint Condition { get; set; }
+ public uint EpilogStartIndex { get; set; }
+ public uint EpilogStartOffsetFromMainFunctionBegin { get; set; }
+
+ public Epilog() { }
+
+ public Epilog(int index, int dw, uint startOffset)
+ {
+ Index = index;
+
+ EpilogStartOffset = UnwindInfo.ExtractBits(dw, 0, 18);
+ Res = UnwindInfo.ExtractBits(dw, 18, 4);
+ Condition = UnwindInfo.ExtractBits(dw, 20, 4);
+ EpilogStartIndex = UnwindInfo.ExtractBits(dw, 22, 10);
+
+ // Note that epilogStartOffset for a funclet is the offset from the beginning
+ // of the current funclet, not the offset from the beginning of the main function.
+ // To help find it when looking through JitDump output, also show the offset from
+ // the beginning of the main function.
+ EpilogStartOffsetFromMainFunctionBegin = EpilogStartOffset * 4 + startOffset;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine($" Epilog Start Offset: 0x{EpilogStartOffset:X5} Actual offset = 0x{EpilogStartOffset * 4:X5} Offset from main function begin = 0x{EpilogStartOffsetFromMainFunctionBegin:X6}");
+ sb.AppendLine($" Condition: {Condition} (0x{Condition:X})" + ((Condition == 0xE) ? " (always)" : ""));
+ sb.Append($" Epilog Start Index: {EpilogStartIndex} (0x{EpilogStartIndex:X})");
+ return sb.ToString();
+ }
+ }
+
+ public class UnwindCode
+ {
+ public int Index { get; set; }
+
+ public UnwindCode() { }
+
+ public UnwindCode(int index)
+ {
+ Index = index;
+
+ }
+ }
+
+ /// <summary>
+ /// based on <a href="https://github.com/dotnet/runtime/src/coreclr/jit/unwindriscv64.cpp">src/jit/unwindriscv64.cpp</a> DumpUnwindInfo
+ /// </summary>
+ public class UnwindInfo : BaseUnwindInfo
+ {
+ public uint CodeWords { get; set; }
+ public uint EpilogCount { get; set; }
+ public uint EBit { get; set; }
+ public uint XBit { get; set; }
+ public uint Vers { get; set; }
+ public uint FunctionLength { get; set; }
+
+ public uint ExtendedCodeWords { get; set; }
+ public uint ExtendedEpilogCount { get; set; }
+
+ public Epilog[] Epilogs { get; set; }
+
+ public UnwindInfo() { }
+
+ public UnwindInfo(byte[] image, int offset)
+ {
+ uint startOffset = (uint)offset;
+
+ int dw = NativeReader.ReadInt32(image, ref offset);
+ CodeWords = ExtractBits(dw, 27, 5);
+ EpilogCount = ExtractBits(dw, 22, 5);
+ EBit = ExtractBits(dw, 21, 1);
+ XBit = ExtractBits(dw, 20, 1);
+ Vers = ExtractBits(dw, 18, 2);
+ FunctionLength = ExtractBits(dw, 0, 18) * 4;
+
+ if (CodeWords == 0 && EpilogCount == 0)
+ {
+ // We have an extension word specifying a larger number of Code Words or Epilog Counts
+ // than can be specified in the header word.
+ dw = NativeReader.ReadInt32(image, ref offset);
+ ExtendedCodeWords = ExtractBits(dw, 16, 8);
+ ExtendedEpilogCount = ExtractBits(dw, 0, 16);
+ }
+
+ bool[] epilogStartAt = new bool[1024]; // One byte per possible epilog start index; initialized to false
+
+ if (EBit == 0)
+ {
+ Epilogs = new Epilog[EpilogCount];
+ if (EpilogCount != 0)
+ {
+ for (int scope = 0; scope < EpilogCount; scope++)
+ {
+ dw = NativeReader.ReadInt32(image, ref offset);
+ Epilogs[scope] = new Epilog(scope, dw, startOffset);
+ epilogStartAt[Epilogs[scope].EpilogStartIndex] = true; // an epilog starts at this offset in the unwind codes
+ }
+ }
+ }
+ else
+ {
+ Epilogs = new Epilog[0];
+ epilogStartAt[EpilogCount] = true; // the one and only epilog starts its unwind codes at this offset
+ }
+
+ Size = offset - (int)startOffset + (int)CodeWords * 4;
+ int alignmentPad = ((Size + sizeof(int) - 1) & ~(sizeof(int) - 1)) - Size;
+ Size += (alignmentPad + sizeof(uint));
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine($" CodeWords: {CodeWords}");
+ sb.AppendLine($" EpilogCount: {EpilogCount}");
+ sb.AppendLine($" EBit: {EBit}");
+ sb.AppendLine($" XBit: {XBit}");
+ sb.AppendLine($" Vers: {Vers}");
+ sb.AppendLine($" FunctionLength: {FunctionLength}");
+ if (CodeWords == 0 && EpilogCount == 0)
+ {
+ sb.AppendLine(" ---- Extension word ----");
+ sb.AppendLine($" Extended Code Words: {CodeWords}");
+ sb.AppendLine($" Extended Epilog Count: {EpilogCount}");
+ }
+ if (EpilogCount == 0)
+ {
+ sb.AppendLine(" No epilogs");
+ }
+ else
+ {
+ for (int i = 0; i < Epilogs.Length; i++)
+ {
+ sb.AppendLine(" -------------------------");
+ sb.AppendLine(Epilogs[i].ToString());
+ sb.AppendLine(" -------------------------");
+ }
+ }
+ return sb.ToString();
+ }
+
+ internal static uint ExtractBits(int dw, int start, int length)
+ {
+ return (uint)((dw >> start) & ((1 << length) - 1));
+ }
+ }
+}
case Machine.LoongArch64:
return LoongArch64TransitionBlock.Instance;
+ case (Machine)0x5064: /* TODO: update with RiscV64 */
+ return RiscV64TransitionBlock.Instance;
+
default:
throw new NotImplementedException();
}
public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters;
public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot;
}
+
+ private sealed class RiscV64TransitionBlock : TransitionBlock
+ {
+ public static readonly TransitionBlock Instance = new RiscV64TransitionBlock();
+
+ public override int PointerSize => 8;
+ // a0 .. a7
+ public override int NumArgumentRegisters => 8;
+ // fp=x8, ra=x1, s1-s11(R9,R18-R27), tp=x3, gp=x4
+ public override int NumCalleeSavedRegisters => 15;
+ // Callee-saves, argument registers
+ public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters;
+ public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters;
+ public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot;
+ }
+
}
}
Console.WriteLine("Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are " +
"considered to be input files. If no input files begin with '--' then this option is not necessary.\n");
- string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64" };
+ string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64", "riscv64" };
string[] ValidOS = new string[] { "windows", "linux", "freebsd", "osx", "maccatalyst", "ios", "iossimulator", "tvos", "tvossimulator" };
Console.WriteLine("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetos", string.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant());
Console.WriteLine(SR.DashDashHelp);
Console.WriteLine();
- string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"};
+ string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64", "riscv64"};
string[] ValidOS = new string[] {"windows", "linux", "osx"};
Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant()));
<OutputType>Exe</OutputType>
<TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
<NoWarn>8002,NU1701</NoWarn>
- <Platforms>x64;x86;arm64;arm;loongarch64</Platforms>
+ <Platforms>x64;x86;arm64;arm;loongarch64;riscv64</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
Target_X64,
Target_Thumb,
Target_Arm64,
- Target_LoongArch64
+ Target_LoongArch64,
+ Target_RiscV64,
};
[DllImport(_dll, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr InitBufferedDisasm(TargetArch Target);
[DllImport(_dll, CallingConvention = CallingConvention.Cdecl)]
- public static extern void DumpCodeBlock(IntPtr Disasm, IntPtr Address, IntPtr Bytes, IntPtr Size);
-
- [DllImport(_dll, CallingConvention = CallingConvention.Cdecl)]
public static extern int DumpInstruction(IntPtr Disasm, IntPtr Address, IntPtr Bytes, IntPtr Size);
[DllImport(_dll, CallingConvention = CallingConvention.Cdecl)]
case Machine.LoongArch64:
target = TargetArch.Target_LoongArch64;
break;
+ case (Machine)0x5064:
+ target = TargetArch.Target_RiscV64;
+ break;
default:
Program.WriteWarning($"{machine} not supported on CoreDisTools");
return IntPtr.Zero;
// Instructions are dumped as 4-byte hexadecimal integers
Machine.LoongArch64 => 4 * 2 + 1,
+ // Instructions are dumped as 4-byte hexadecimal integers
+ // TODO: update once RISC-V runtime supports "C" extension (compressed instructions)
+ (Machine)0x5064 => 4 * 2 + 1,
+
_ => throw new NotImplementedException()
};
}
int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _reader.Image, out instruction);
+ if (instrSize == 0)
+ {
+ instruction = "Decode failure, aborting disassembly" + Environment.NewLine;
+ return rtf.Size - rtfOffset;
+ }
// CoreDisTools dumps instructions in the following format:
//
}
else
{
- if ((_reader.Machine == Machine.Arm64) || (_reader.Machine == Machine.LoongArch64))
+ // TODO: update once RISC-V runtime supports "C" extension (compressed instructions)
+ if (_reader.Machine is Machine.Arm64 or Machine.LoongArch64 or (Machine)0x5064)
{
// Replace " hh hh hh hh " byte dump with " hhhhhhhh ".
// CoreDisTools should be fixed to dump bytes this way for ARM64.
case Machine.ArmThumb2:
break;
+ case (Machine)0x5064:
+ ProbeRiscV64Quirks(rtf, imageOffset, rtfOffset, ref fixedTranslatedLine);
+ break;
+
default:
break;
}
}
/// <summary>
+ /// Improves disassembler output for RiscV64 by adding comments at the end of instructions.
+ /// </summary>
+ /// <param name="rtf">Runtime function</param>
+ /// <param name="imageOffset">Offset within the image byte array</param>
+ /// <param name="rtfOffset">Offset within the runtime function</param>
+ /// <param name="instruction">Textual representation of the instruction</param>
+ private void ProbeRiscV64Quirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, ref string instruction)
+ {
+ const int InstructionSize = 4;
+ uint instr = BitConverter.ToUInt32(_reader.Image, imageOffset + rtfOffset);
+
+ if (IsRiscV64JalrInstruction(instr))
+ {
+ /*
+ Supported patterns:
+ auipc
+ addi
+ ld
+ jalr
+
+ auipc
+ ld
+ jalr
+
+ auipc
+ addi
+ ld
+ ld
+ jalr
+
+ Irrelevant instructions for calle address calculations are skiped.
+ */
+
+ AnalyzeRiscV64Itype(instr, out uint rd, out uint rs1, out int imm);
+ uint register = rs1;
+ int target = imm;
+
+ bool isFound = false;
+ int currentInstrOffset = rtfOffset - InstructionSize;
+ int currentPC = rtf.StartAddress + currentInstrOffset;
+ do
+ {
+ instr = BitConverter.ToUInt32(_reader.Image, imageOffset + currentInstrOffset);
+
+ if (IsRiscV64LdInstruction(instr))
+ {
+ AnalyzeRiscV64Itype(instr, out rd, out rs1, out imm);
+ if (rd == register)
+ {
+ target = imm;
+ register = rs1;
+ }
+ }
+ else if (IsRiscV64AddiInstruction(instr))
+ {
+ AnalyzeRiscV64Itype(instr, out rd, out rs1, out imm);
+ if (rd == register)
+ {
+ target =+ imm;
+ register = rs1;
+ }
+ }
+ else if (IsRiscV64AuipcInstruction(instr))
+ {
+ AnalyzeRiscV64Utype(instr, out rd, out imm);
+ if (rd == register)
+ {
+ target += currentPC + imm;
+ isFound = true;
+ break;
+ }
+ }
+ else
+ {
+ // check if callee address is calculated using an unsupported instruction
+ rd = (instr >> 7) & 0b_11111U;
+ if (rd == register)
+ {
+ break;
+ }
+ }
+
+ currentInstrOffset -= InstructionSize;
+ currentPC -= InstructionSize;
+ } while (currentInstrOffset > 0);
+
+ if (isFound)
+ {
+ if (!TryGetImportCellName(target, out string targetName) || string.IsNullOrWhiteSpace(targetName))
+ {
+ return;
+ }
+
+ instruction = $"{instruction} // {targetName}";
+ }
+ }
+ }
+
+ /// <summary>
+ /// Checks if instruction is auipc.
+ /// </summary>
+ /// <param name="instruction">Assembly code of instruction</param>
+ /// <returns>It returns true if instruction is auipc. Otherwise false</returns>
+ private bool IsRiscV64AuipcInstruction(uint instruction)
+ {
+ const uint OpcodeAuipc = 0b_0010111;
+ return (instruction & 0x7f) == OpcodeAuipc;
+ }
+
+ /// <summary>
+ /// Checks if instruction is jalr.
+ /// </summary>
+ /// <param name="instruction">Assembly code of instruction</param>
+ /// <returns>It returns true if instruction is jalr. Otherwise false</returns>
+ private bool IsRiscV64JalrInstruction(uint instruction)
+ {
+ const uint OpcodeJalr = 0b_1100111;
+ const uint Funct3Jalr = 0b_000;
+ return (instruction & 0x7f) == OpcodeJalr &&
+ ((instruction >> 12) & 0b_111) == Funct3Jalr;
+ }
+
+ /// <summary>
+ /// Checks if instruction is addi.
+ /// </summary>
+ /// <param name="instruction">Assembly code of instruction</param>
+ /// <returns>It returns true if instruction is addi. Otherwise false</returns>
+ private bool IsRiscV64AddiInstruction(uint instruction)
+ {
+ const uint OpcodeAddi = 0b_0010011;
+ const uint Funct3Addi = 0b_000;
+ return (instruction & 0x7f) == OpcodeAddi &&
+ ((instruction >> 12) & 0b_111) == Funct3Addi;
+ }
+
+ /// <summary>
+ /// Checks if instruction is ld.
+ /// </summary>
+ /// <param name="instruction">Assembly code of instruction</param>
+ /// <returns>It returns true if instruction is ld. Otherwise false</returns>
+ private bool IsRiscV64LdInstruction(uint instruction)
+ {
+ const uint OpcodeLd = 0b_0000011;
+ const uint Funct3Ld = 0b_011;
+ return (instruction & 0x7f) == OpcodeLd &&
+ ((instruction >> 12) & 0b_111) == Funct3Ld;
+ }
+
+ /// <summary>
+ /// Retrieves output register and immediate value from U-type instruction.
+ /// </summary>
+ /// <param name="instruction">Assembly code of instruction</param>
+ /// <param name="rd">Output register</param>
+ /// <param name="imm">Immediate value</param>
+ private void AnalyzeRiscV64Utype(uint instruction, out uint rd, out int imm)
+ {
+ // U-type 31 12 11 7 6 0
+ // [ imm ] [ rd ] [ opcode ]
+ rd = (instruction >> 7) & 0b_11111U;
+ imm = unchecked((int)(instruction & (0xfffff << 12)));
+ }
+
+ /// <summary>
+ /// Retrieves output register, resource register and immediate value from U-type instruction.
+ /// </summary>
+ /// <param name="instruction">Assembly code of instruction</param>
+ /// <param name="rd">Output register</param>
+ /// <param name="rs1">Resource register</param>
+ /// <param name="imm">Immediate value</param>
+ private void AnalyzeRiscV64Itype(uint instruction, out uint rd, out uint rs1, out int imm)
+ {
+ // I-type 31 20 19 15 14 12 11 7 6 0
+ // [ imm ] [ rs1 ] [ funct3 ] [ rd ] [ opcode ]
+ rd = (instruction >> 7) & 0b_11111U;
+ rs1 = (instruction >> 15) & 0b_11111U;
+ imm = unchecked((int)instruction) >> 20;
+ }
+
+ /// <summary>
/// Determine whether a given character is an ASCII digit.
/// </summary>
private static bool IsDigit(char c) => (uint)(c - '0') <= (uint)('9' - '0');
Machine.ArmThumb2 => TargetArchitecture.ARM,
Machine.Arm64 => TargetArchitecture.ARM64,
Machine.LoongArch64 => TargetArchitecture.LoongArch64,
+ (Machine)0x5064 => TargetArchitecture.RiscV64,
_ => throw new NotImplementedException(r2r.Machine.ToString()),
};
TargetOS os = r2r.OperatingSystem switch
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Exe</OutputType>
- <Platforms>x64;x86;arm64;arm;loongarch64</Platforms>
+ <Platforms>x64;x86;arm64;arm;loongarch64;riscv64</Platforms>
<AssemblyKey>Open</AssemblyKey>
<IsDotNetFrameworkProductAssembly>true</IsDotNetFrameworkProductAssembly>
<TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
mov eax, [rcx + OFFSETOF__CONTEXT__EFlags]
push rax
popfq
+ mov rax, [rcx + OFFSETOF__CONTEXT__Rip]
mov rsp, [rcx + OFFSETOF__CONTEXT__Rsp]
- jmp qword ptr [rcx + OFFSETOF__CONTEXT__Rip]
+ jmp rax
Done_Restore_CONTEXT_CONTROL:
; The function was not asked to restore the control registers so we return back to the caller
_ASSERTE(!"---------UNReachable-------LoongArch64/RISC-V64!!!");
}
}
+
+#ifdef TARGET_RISCV64
+ void CopySingleFloatToRegister(void* src)
+ {
+ void* dest = GetDestinationAddress();
+ UINT32 value = *(UINT32*)src;
+ if (TransitionBlock::IsFloatArgumentRegisterOffset(m_offset))
+ {
+ // NaN-box the floating register value or single-float instructions will treat it as NaN
+ *(UINT64*)dest = 0xffffffff00000000L | value;
+ }
+ else
+ {
+ // When a single float is passed according to integer calling convention
+ // (in integer register or on stack), the upper bits are not specified.
+ *(UINT32*)dest = value;
+ }
+ }
+#endif // TARGET_RISCV64
+
#endif // !DACCESS_COMPILE
PTR_VOID GetStructGenRegDestinationAddress()
*((INT64*)pDest) = (INT16)pArguments[arg];
break;
case 4:
+#ifdef TARGET_RISCV64
+ // RISC-V integer calling convention requires to sign-extend `uint` arguments as well
+ *((INT64*)pDest) = (INT32)pArguments[arg];
+#else // TARGET_LOONGARCH64
if (m_argIt.GetArgType() == ELEMENT_TYPE_U4)
*((INT64*)pDest) = (UINT32)pArguments[arg];
else
*((INT64*)pDest) = (INT32)pArguments[arg];
+#endif // TARGET_RISCV64
break;
#else
case 1:
{
return argLocDescForStructInRegs->m_cFloatReg > 0;
}
+ #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
+ if (argLocDescForStructInRegs != NULL)
+ {
+ return argLocDescForStructInRegs->m_cFloatReg > 0;
+ }
#endif
return offset < 0;
}
if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
{
+ int argOfs = 0;
m_argLocDescForStructInRegs.Init();
m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
m_argLocDescForStructInRegs.m_cFloatReg = 1;
- int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
- m_idxFPReg += 1;
-
- m_argLocDescForStructInRegs.m_structFields = flags;
-
m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
m_argLocDescForStructInRegs.m_cGenReg = 1;
+ m_argLocDescForStructInRegs.m_structFields = flags;
+
+ if (flags & STRUCT_FLOAT_FIELD_SECOND)
+ {
+ argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
+ }
+ else
+ {
+ argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
+ }
+
+ m_idxFPReg += 1;
m_idxGenReg += 1;
m_hasArgLocDescForStructInRegs = true;
m_argLocDescForStructInRegs.Init();
m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
m_argLocDescForStructInRegs.m_cFloatReg = 1;
- int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
+ int argOfs = (flags & STRUCT_FLOAT_FIELD_SECOND)
+ ? TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8
+ : TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
m_idxFPReg += 1;
m_argLocDescForStructInRegs.m_structFields = flags;
switch (type) {
#ifdef TARGET_RISCV64
- // RISC-V call convention requires signed ints sign-extended (unsigned -- zero-extended) to register width
+ // RISC-V call convention requires integer scalars narrower than XLEN bits to be widened according to the sign
+ // of their type up to 32 bits, then sign-extended to XLEN bits. In practice it means type-extending all ints
+ // except `uint` which is sign-extended regardless.
case ELEMENT_TYPE_BOOLEAN:
case ELEMENT_TYPE_U1:
_ASSERTE(argRef != NULL);
case ELEMENT_TYPE_R4:
_ASSERTE(argRef != NULL);
- // NaN-box the register value or single-float instructions will treat it as NaN
- *(UINT64 *)pArgDst = 0xffffffff00000000L | *(UINT32 *)argRef;
+ argDest->CopySingleFloatToRegister(argRef);
break;
case ELEMENT_TYPE_I4:
- _ASSERTE(argRef != NULL);
- *(INT64 *)pArgDst = *(INT32 *)argRef;
- break;
-
case ELEMENT_TYPE_U4:
_ASSERTE(argRef != NULL);
- *(UINT64 *)pArgDst = *(UINT32 *)argRef;
+ *(INT64 *)pArgDst = *(INT32 *)argRef;
break;
#else // !TARGET_RISCV64
if (TransitionBlock::IsArgumentRegisterOffset(argOffset))
{
pArg = (LPBYTE)&pData->argumentRegisters + (argOffset - TransitionBlock::GetOffsetOfArgumentRegisters());
+ ArgLocDesc* pArgLocDesc = m_argIterator.GetArgLocDescForStructInRegs();
+
+ if (pArgLocDesc)
+ {
+ if (pArgLocDesc->m_cFloatReg == 1)
+ {
+ _ASSERTE(!(pArgLocDesc->m_structFields & STRUCT_FLOAT_FIELD_FIRST));
+ _ASSERTE(pArgLocDesc->m_structFields & STRUCT_FLOAT_FIELD_SECOND);
+
+ UINT32 bufferPos = m_bufferPos;
+ UINT64* dst = (UINT64*)&pData->buffer[bufferPos];
+ m_bufferPos += 16;
+
+ *dst = *(UINT64*)pArg;
+ *(double*)(dst + 1) = pData->floatArgumentRegisters.f[pArgLocDesc->m_idxFloatReg];
+
+ return (LPBYTE)&pData->buffer[bufferPos];
+ }
+
+ _ASSERTE(pArgLocDesc->m_cFloatReg == 0);
+ }
}
else
{
s = s + s%16;
pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ;
+ pRD->pCurrentContext->A0 = m_Args->A0;
+
pRD->pCurrentContext->S0 = m_Args->S0;
pRD->pCurrentContext->S1 = m_Args->S1;
pRD->pCurrentContext->S2 = m_Args->S2;
DWORD numIntroducedFields = GetNumIntroducedInstanceFields();
- // It appears the VM gives a struct with no fields of size 1.
- // Don't pass in register such structure.
if (numIntroducedFields == 0)
{
- return false;
+ helperPtr->largestFieldOffset = startOffsetOfStruct;
+ LOG((LF_JIT, LL_EVERYTHING, "%*s**** Classify empty struct %s (%p) like padding, startOffset %d, total struct size %d\n",
+ nestingLevel * 5, "", this->GetDebugClassName(), this, startOffsetOfStruct, helperPtr->structSize));
+
+ AssignClassifiedEightByteTypes(helperPtr, nestingLevel);
+ return true;
}
// The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers
// No fields.
if (numIntroducedFields == 0)
{
- return false;
+ helperPtr->largestFieldOffset = startOffsetOfStruct;
+ LOG((LF_JIT, LL_EVERYTHING, "%*s**** Classify empty struct %s (%p) like padding, startOffset %d, total struct size %d\n",
+ nestingLevel * 5, "", this->GetDebugClassName(), this, startOffsetOfStruct, helperPtr->structSize));
+
+ AssignClassifiedEightByteTypes(helperPtr, nestingLevel);
+ return true;
}
bool hasImpliedRepeatedFields = HasImpliedRepeatedFields(this);
// Calculate the eightbytes and their types.
int lastFieldOrdinal = sortedFieldOrder[largestFieldOffset];
- unsigned int offsetAfterLastFieldByte = largestFieldOffset + helperPtr->fieldSizes[lastFieldOrdinal];
- SystemVClassificationType lastFieldClassification = helperPtr->fieldClassifications[lastFieldOrdinal];
+ unsigned int lastFieldSize = (lastFieldOrdinal >= 0) ? helperPtr->fieldSizes[lastFieldOrdinal] : 0;
+ unsigned int offsetAfterLastFieldByte = largestFieldOffset + lastFieldSize;
+ _ASSERTE(offsetAfterLastFieldByte <= helperPtr->structSize);
+ SystemVClassificationType lastFieldClassification = (lastFieldOrdinal >= 0)
+ ? helperPtr->fieldClassifications[lastFieldOrdinal]
+ : SystemVClassificationTypeNoClass;
unsigned int usedEightBytes = 0;
unsigned int accumulatedSizeForEightBytes = 0;
// the SysV ABI spec.
fieldSize = 1;
fieldClassificationType = offset < offsetAfterLastFieldByte ? SystemVClassificationTypeNoClass : lastFieldClassification;
+ if (offset % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0) // new eightbyte
+ foundFieldInEightByte = false;
}
else
{
}
}
- if ((offset + 1) % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0) // If we just finished checking the last byte of an eightbyte
+ // If we just finished checking the last byte of an eightbyte or the entire struct
+ if ((offset + 1) % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0 || (offset + 1) == helperPtr->structSize)
{
if (!foundFieldInEightByte)
{
- // If we didn't find a field in an eight-byte (i.e. there are no explicit offsets that start a field in this eightbyte)
+ // If we didn't find a field in an eightbyte (i.e. there are no explicit offsets that start a field in this eightbyte)
// then the classification of this eightbyte might be NoClass. We can't hand a classification of NoClass to the JIT
// so set the class to Integer (as though the struct has a char[8] padding) if the class is NoClass.
+ //
+ // TODO: Fix JIT, NoClass eightbytes are valid and passing them is broken because of this.
if (helperPtr->eightByteClassifications[offset / SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES] == SystemVClassificationTypeNoClass)
{
helperPtr->eightByteClassifications[offset / SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES] = SystemVClassificationTypeInteger;
LOG((LF_JIT, LL_EVERYTHING, " **** Number EightBytes: %d\n", helperPtr->eightByteCount));
for (unsigned i = 0; i < helperPtr->eightByteCount; i++)
{
- _ASSERTE(helperPtr->eightByteClassifications[i] != SystemVClassificationTypeNoClass);
LOG((LF_JIT, LL_EVERYTHING, " **** eightByte %d -- classType: %s, eightByteOffset: %d, eightByteSize: %d\n",
i, GetSystemVClassificationTypeName(helperPtr->eightByteClassifications[i]), helperPtr->eightByteOffsets[i], helperPtr->eightByteSizes[i]));
+ _ASSERTE(helperPtr->eightByteClassifications[i] != SystemVClassificationTypeNoClass);
}
#endif // _DEBUG
}
}
EX_CATCH
{
- mem = NULL;
+ mem = (TADDR)NULL;
_ASSERTE(!"Memory read within jitted Code Failed, this should not happen!!!!");
}
EX_END_CATCH(SwallowAllExceptions);
context.S11 = unwoundstate->captureCalleeSavedRegisters[11] = baseState->captureCalleeSavedRegisters[11];
context.Gp = unwoundstate->captureCalleeSavedRegisters[12] = baseState->captureCalleeSavedRegisters[12];
context.Tp = unwoundstate->captureCalleeSavedRegisters[13] = baseState->captureCalleeSavedRegisters[13];
- context.Ra = NULL; // Filled by the unwinder
+ context.Ra = 0; // Filled by the unwinder
context.Sp = baseState->captureSp;
context.Pc = baseState->captureIp;
nonVolContextPtrs.S11 = &unwoundstate->captureCalleeSavedRegisters[11];
nonVolContextPtrs.Gp = &unwoundstate->captureCalleeSavedRegisters[12];
nonVolContextPtrs.Tp = &unwoundstate->captureCalleeSavedRegisters[13];
- nonVolContextPtrs.Ra = NULL; // Filled by the unwinder
+ nonVolContextPtrs.Ra = 0; // Filled by the unwinder
#endif // DACCESS_COMPILE
pRD->pCurrentContext->S11 = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[11]);
pRD->pCurrentContext->Gp = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[12]);
pRD->pCurrentContext->Tp = (DWORD64)(pUnwoundState->captureCalleeSavedRegisters[13]);
- pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC
+ pRD->pCurrentContext->Ra = 0; // Unwind again to get Caller's PC
pRD->pCurrentContextPointers->Fp = pUnwoundState->ptrCalleeSavedRegisters[0];
pRD->pCurrentContextPointers->S1 = pUnwoundState->ptrCalleeSavedRegisters[1];
pRD->pCurrentContext->S11 = m_MachState.ptrCalleeSavedRegisters[11] ? *m_MachState.ptrCalleeSavedRegisters[11] : m_MachState.captureCalleeSavedRegisters[11];
pRD->pCurrentContext->Gp = m_MachState.ptrCalleeSavedRegisters[12] ? *m_MachState.ptrCalleeSavedRegisters[12] : m_MachState.captureCalleeSavedRegisters[12];
pRD->pCurrentContext->Tp = m_MachState.ptrCalleeSavedRegisters[13] ? *m_MachState.ptrCalleeSavedRegisters[13] : m_MachState.captureCalleeSavedRegisters[13];
- pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC
+ pRD->pCurrentContext->Ra = 0; // Unwind again to get Caller's PC
#else // TARGET_UNIX
pRD->pCurrentContext->Fp = *m_MachState.ptrCalleeSavedRegisters[0];
pRD->pCurrentContext->S1 = *m_MachState.ptrCalleeSavedRegisters[1];
pRD->pCurrentContext->S11 = *m_MachState.ptrCalleeSavedRegisters[11];
pRD->pCurrentContext->Gp = *m_MachState.ptrCalleeSavedRegisters[12];
pRD->pCurrentContext->Tp = *m_MachState.ptrCalleeSavedRegisters[13];
- pRD->pCurrentContext->Ra = NULL; // Unwind again to get Caller's PC
+ pRD->pCurrentContext->Ra = 0; // Unwind again to get Caller's PC
#endif
#if !defined(DACCESS_COMPILE)
s = s + s%16;
pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ;
+ pRD->pCurrentContext->A0 = m_Args->A0;
+
pRD->pCurrentContext->S1 = m_Args->S1;
pRD->pCurrentContext->S2 = m_Args->S2;
pRD->pCurrentContext->S3 = m_Args->S3;
;;cachemask => [CALL_STUB_CACHE_MASK * sizeof(void*)]
// Called directly by JITTED code
- // ResolveStub._resolveEntryPoint(a0:Object*, a1 ...,a7, t8:IndirectionCellAndFlags)
+ // ResolveStub._resolveEntryPoint(a0:Object*, a1 ...,a7, t5:IndirectionCellAndFlags)
// {
// MethodTable mt = a0.m_pMethTab;
// int i = ((mt + mt >> 12) ^ this._hashedToken) & _cacheMask
_stub._resolveEntryPoint[n++] = 0x01cf9a63;// | PC_REL_OFFSET(_slowEntryPoint[0], n);
// ld t6, 0(t1) # t6 = e.token;
- _stub._resolveEntryPoint[n++] = 0x00033f83 | ((offsetof(ResolveCacheElem, token) & 0xfff)<<10);
+ _stub._resolveEntryPoint[n++] = 0x00033f83 | ((offsetof(ResolveCacheElem, token) & 0xfff)<<20);
// bne t6, t2, next
_stub._resolveEntryPoint[n++] = 0x007f9663;// | PC_REL_OFFSET(_slowEntryPoint[0], n);
pc_offset = offsetof(ResolveCacheElem, target) & 0xffffffff;
_ASSERTE(pc_offset >=0 && pc_offset%8 == 0);
// ld t3, 0(t1) # t3 = e.target;
- _stub._resolveEntryPoint[n++] = 0x00033e03 | ((offsetof(ResolveCacheElem, target) & 0xfff)<<10);
+ _stub._resolveEntryPoint[n++] = 0x00033e03 | ((offsetof(ResolveCacheElem, target) & 0xfff)<<20);
// jalr x0, t3, 0
_stub._resolveEntryPoint[n++] = 0x000e0067;
// auipc t0, 0
_stub._slowEntryPoint[0] = 0x00000297;
- // ld t6, 0(t0) # r21 = _resolveWorkerTarget;
+ // ld t6, 0(t0) # t6 = _resolveWorkerTarget;
static_assert_no_msg((0x14*4) == ((INT32)(offsetof(ResolveStub, _resolveWorkerTarget) - (offsetof(ResolveStub, _slowEntryPoint[0])))));
static_assert_no_msg((ResolveStub::slowEntryPointLen + ResolveStub::failEntryPointLen+1+3*2) == 0x14);
_stub._slowEntryPoint[1] = 0x0002bf83 | ((0x14 * 4) << 20);
_stub._resolveWorkerTarget = resolveWorkerTarget;
_ASSERTE(resolveWorkerTarget == (PCODE)ResolveWorkerChainLookupAsmStub);
- _ASSERTE(patcherTarget == NULL);
+ _ASSERTE(patcherTarget == (PCODE)NULL);
#undef DATA_OFFSET
#undef PC_REL_OFFSET
#else
PORTABILITY_ASSERT("TraceManagedThunk");
- destAddr = NULL;
+ destAddr = (PCODE)NULL;
#endif
LOG((LF_CORDB,LL_INFO10000, "TraceManagedThunk: ppbDest: %p\n", destAddr));
return pContext->Lr;
#else
PORTABILITY_ASSERT("StubManagerHelpers::GetReturnAddress");
- return NULL;
+ return (TADDR)NULL;
#endif
}
return pContext->X12;
#else
PORTABILITY_ASSERT("StubManagerHelpers::GetTailCallTarget");
- return NULL;
+ return (TADDR)NULL;
#endif
}
return pContext->X12;
#else
PORTABILITY_ASSERT("StubManagerHelpers::GetHiddenArg");
- return NULL;
+ return (TADDR)NULL;
#endif
}
return *((PCODE *)pContext->Fp + 1);
#else
PORTABILITY_ASSERT("StubManagerHelpers::GetRetAddrFromMulticastILStubFrame");
- return NULL;
+ return (TADDR)NULL;
#endif
}
return pContext->X1;
#else
PORTABILITY_ASSERT("StubManagerHelpers::GetSecondArg");
- return NULL;
+ return (TADDR)NULL;
#endif
}
case ICorDebugInfo::REGNUM_AMBIENT_SP: return offsetof(T_CONTEXT, Sp);
default: _ASSERTE(!"Bad regNum"); return (SIZE_T)(-1);
}
+#elif defined(TARGET_RISCV64)
+
+ switch(regNum)
+ {
+ case ICorDebugInfo::REGNUM_R0: return offsetof(T_CONTEXT, R0);
+ case ICorDebugInfo::REGNUM_RA: return offsetof(T_CONTEXT, Ra);
+ case ICorDebugInfo::REGNUM_SP: return offsetof(T_CONTEXT, Sp);
+ case ICorDebugInfo::REGNUM_GP: return offsetof(T_CONTEXT, Gp);
+ case ICorDebugInfo::REGNUM_TP: return offsetof(T_CONTEXT, Tp);
+ case ICorDebugInfo::REGNUM_T0: return offsetof(T_CONTEXT, T0);
+ case ICorDebugInfo::REGNUM_T1: return offsetof(T_CONTEXT, T1);
+ case ICorDebugInfo::REGNUM_T2: return offsetof(T_CONTEXT, T2);
+ case ICorDebugInfo::REGNUM_FP: return offsetof(T_CONTEXT, Fp);
+ case ICorDebugInfo::REGNUM_S1: return offsetof(T_CONTEXT, S1);
+ case ICorDebugInfo::REGNUM_A0: return offsetof(T_CONTEXT, A0);
+ case ICorDebugInfo::REGNUM_A1: return offsetof(T_CONTEXT, A1);
+ case ICorDebugInfo::REGNUM_A2: return offsetof(T_CONTEXT, A2);
+ case ICorDebugInfo::REGNUM_A3: return offsetof(T_CONTEXT, A3);
+ case ICorDebugInfo::REGNUM_A4: return offsetof(T_CONTEXT, A4);
+ case ICorDebugInfo::REGNUM_A5: return offsetof(T_CONTEXT, A5);
+ case ICorDebugInfo::REGNUM_A6: return offsetof(T_CONTEXT, A6);
+ case ICorDebugInfo::REGNUM_A7: return offsetof(T_CONTEXT, A7);
+ case ICorDebugInfo::REGNUM_S2: return offsetof(T_CONTEXT, S2);
+ case ICorDebugInfo::REGNUM_S3: return offsetof(T_CONTEXT, S3);
+ case ICorDebugInfo::REGNUM_S4: return offsetof(T_CONTEXT, S4);
+ case ICorDebugInfo::REGNUM_S5: return offsetof(T_CONTEXT, S5);
+ case ICorDebugInfo::REGNUM_S6: return offsetof(T_CONTEXT, S6);
+ case ICorDebugInfo::REGNUM_S7: return offsetof(T_CONTEXT, S7);
+ case ICorDebugInfo::REGNUM_S8: return offsetof(T_CONTEXT, S8);
+ case ICorDebugInfo::REGNUM_S9: return offsetof(T_CONTEXT, S9);
+ case ICorDebugInfo::REGNUM_S10: return offsetof(T_CONTEXT, S10);
+ case ICorDebugInfo::REGNUM_S11: return offsetof(T_CONTEXT, S11);
+ case ICorDebugInfo::REGNUM_T3: return offsetof(T_CONTEXT, T3);
+ case ICorDebugInfo::REGNUM_T4: return offsetof(T_CONTEXT, T4);
+ case ICorDebugInfo::REGNUM_T5: return offsetof(T_CONTEXT, T5);
+ case ICorDebugInfo::REGNUM_T6: return offsetof(T_CONTEXT, T6);
+ case ICorDebugInfo::REGNUM_PC: return offsetof(T_CONTEXT, Pc);
+ case ICorDebugInfo::REGNUM_AMBIENT_SP: return offsetof(T_CONTEXT, Sp);
+ default: _ASSERTE(!"Bad regNum"); return (SIZE_T)(-1);
+ }
#else
PORTABILITY_ASSERT("GetRegOffsInCONTEXT is not implemented on this platform.");
return (SIZE_T) -1;
<UnofficialBuildRID Include="linux-musl-ppc64le">
<Platform>ppc64le</Platform>
</UnofficialBuildRID>
+ <UnofficialBuildRID Include="linux-riscv64">
+ <Platform>riscv64</Platform>
+ </UnofficialBuildRID>
+ <UnofficialBuildRID Include="linux-musl-riscv64">
+ <Platform>riscv64</Platform>
+ </UnofficialBuildRID>
</ItemGroup>
</Project>
<SharedFrameworkHostFileNameOverride>crossgen2</SharedFrameworkHostFileNameOverride>
<!-- Build this pack for any RID if building from source. Otherwise, only build select RIDs. -->
<RuntimeIdentifiers Condition="'$(DotNetBuildFromSource)' != 'true'">linux-x64;linux-musl-x64;linux-arm;linux-musl-arm;linux-arm64;linux-musl-arm64;freebsd-x64;freebsd-arm64;osx-x64;osx-arm64;win-x64;win-x86;win-arm64</RuntimeIdentifiers>
+ <!-- runtime/apphost packs of these platforms aren't built in the official build, so only reference the RIDs when we are targetting the community-supported platforms. -->
+ <RuntimeIdentifiers Condition="'$(DotNetBuildSourceOnly)' != 'true' and '$(TargetsLinuxMusl)' == 'true' and '$(TargetArchitecture)' == 'riscv64'">$(RuntimeIdentifiers);linux-musl-riscv64</RuntimeIdentifiers>
+ <RuntimeIdentifiers Condition="'$(DotNetBuildSourceOnly)' != 'true' and '$(TargetsLinux)' == 'true' and '$(TargetsLinuxMusl)' != 'true' and '$(TargetArchitecture)' == 'riscv64'">$(RuntimeIdentifiers);linux-riscv64</RuntimeIdentifiers>
<GenerateInstallers>false</GenerateInstallers>
<HostJsonTargetPath>tools/</HostJsonTargetPath>
<PermitDllAndExeFilesLackingFileVersion>true</PermitDllAndExeFilesLackingFileVersion>
<PublishReadyToRun Condition="'$(TargetOS)' == 'netbsd' or '$(TargetOS)' == 'illumos' or '$(TargetOS)' == 'solaris'">false</PublishReadyToRun>
<!-- Disable crossgen on FreeBSD when cross building from Linux. -->
<PublishReadyToRun Condition="'$(TargetOS)'=='freebsd' and '$(CrossBuild)'=='true'">false</PublishReadyToRun>
- <!-- Disable crossgen on riscv64. -->
- <PublishReadyToRun Condition="'$(TargetArchitecture)'=='riscv64'">false</PublishReadyToRun>
<!-- These components are installed by the root shared framework, but not others. -->
<IncludeWerRelatedKeys>true</IncludeWerRelatedKeys>
<IncludeBreadcrumbStoreFolder>true</IncludeBreadcrumbStoreFolder>
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Runtime.InteropServices;
internal static partial class Interop
{
internal static partial class Sys
{
+ [StructLayout(LayoutKind.Sequential)]
internal unsafe struct IOVector
{
public byte* Base;
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReceiveSocketError")]
+ internal static unsafe partial SocketError ReceiveSocketError(SafeHandle socket, MessageHeader* messageHeader);
+ }
+}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.IO;
using System.Runtime.InteropServices;
public static bool IsMonoLinuxArm64 => IsMonoRuntime && IsLinux && IsArm64Process;
public static bool IsNotMonoLinuxArm64 => !IsMonoLinuxArm64;
+ public static bool IsQemuLinux => IsLinux && Environment.GetEnvironmentVariable("DOTNET_RUNNING_UNDER_QEMU") != null;
+ public static bool IsNotQemuLinux => !IsQemuLinux;
// OSX family
public static bool IsOSXLike => IsOSX || IsiOS || IstvOS || IsMacCatalyst;
HandlerInvokedForSignal(SIGQUIT, redirectStandardInput);
}
+ private static readonly int WaitFailTestTimeoutSeconds = 30 * PlatformDetection.SlowRuntimeTimeoutModifier;
+
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void ExitDetectionNotBlockedByHandler()
{
// Release CancelKeyPress, and give it time to return and tear down the app
mre.Set();
Thread.Sleep(WaitFailTestTimeoutSeconds * 1000);
- }, new RemoteInvokeOptions() { ExpectedExitCode = 130 }).Dispose();
+ }, new RemoteInvokeOptions() {
+ ExpectedExitCode = 130,
+ TimeOut = RemoteExecutor.FailWaitTimeoutMilliseconds * PlatformDetection.SlowRuntimeTimeoutModifier
+ }).Dispose();
}
private void HandlerInvokedForSignal(int signalOuter, bool redirectStandardInput)
public partial class CancelKeyPressTests
{
- private const int WaitFailTestTimeoutSeconds = 30;
-
[Fact]
[SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")]
public static void CanAddAndRemoveHandler()
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/105686", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
public void TestMaxWorkingSet()
{
CreateDefaultProcess();
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/105686", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
public void TestMinWorkingSet()
{
CreateDefaultProcess();
Link="Common\System\Net\SocketProtocolSupportPal.Unix.cs" />
<Compile Include="$(CommonPath)System\Net\NetworkInformation\UnixCommandLinePing.cs"
Link="Common\System\Net\NetworkInformation\UnixCommandLinePing.cs" />
+ <Compile Include="$(CommonPath)System\Net\IPEndPointExtensions.cs"
+ Link="Common\System\Net\IPEndPointExtensions.cs" />
+ <Compile Include="$(CommonPath)System\Net\SocketAddressPal.Unix.cs"
+ Link="Common\System\Net\SocketAddressPal.Unix.cs" />
+ <Compile Include="$(CommonPath)System\Net\IPAddressParserStatics.cs"
+ Link="Common\System\Net\IPAddressParserStatics.cs" />
+ <Compile Include="$(CommonPath)System\Net\Sockets\SocketErrorPal.Unix.cs"
+ Link="Common\System\Net\Sockets\SocketErrorPal.Unix" />
<!-- Interop -->
<Compile Include="$(CommonPath)Interop\Unix\Interop.DefaultPathBufferSize.cs"
Link="Common\Interop\Unix\Interop.DefaultPathBufferSize.cs" />
Link="Common\Interop\Unix\Interop.Errors.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
Link="Common\Interop\Unix\Interop.Libraries.cs" />
+ <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.IOVector.cs"
+ Link="Common\Interop\Unix\System.Native\Interop.IOVector.cs" />
+ <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ReceiveSocketError.cs"
+ Link="Common\Interop\Unix\System.Native\Interop.ReceiveSocketError.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Close.cs"
Link="Common\Interop\Unix\System.Native\Interop.Close.cs" />
+ <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MessageHeader.cs"
+ Link="Common\Interop\Unix\System.Native\Interop.MessageHeader.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Socket.cs"
Link="Common\Interop\Unix\System.Native\Interop.Socket.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SocketAddress.cs"
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Win32.Primitives" />
+ <Reference Include="System.Collections" />
<Reference Include="System.ComponentModel.EventBasedAsync" />
<Reference Include="System.ComponentModel.Primitives" />
<Reference Include="System.Diagnostics.Tracing" />
#pragma warning disable 618
// Disable warning about obsolete property. We could use GetAddressBytes but that allocates.
// IPv4 multicast address starts with 1110 bits so mask rest and test if we get correct value e.g. 0xe0.
- if (NeedsConnect && !ep.Address.IsIPv6Multicast && !(addrFamily == AddressFamily.InterNetwork && (ep.Address.Address & 0xf0) == 0xe0))
+ bool ipv4 = addrFamily == AddressFamily.InterNetwork;
+ if (NeedsConnect && !ep.Address.IsIPv6Multicast && !(ipv4 && (ep.Address.Address & 0xf0) == 0xe0))
{
// If it is not multicast, use Connect to scope responses only to the target address.
socket.Connect(socketConfig.EndPoint);
+ unsafe
+ {
+ int opt = 1;
+ if (ipv4)
+ {
+ // setsockopt(fd, IPPROTO_IP, IP_RECVERR, &value, sizeof(int))
+ socket.SetRawSocketOption(0, 11, new ReadOnlySpan<byte>(&opt, sizeof(int)));
+ }
+ else
+ {
+ // setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &value, sizeof(int))
+ socket.SetRawSocketOption(41, 25, new ReadOnlySpan<byte>(&opt, sizeof(int)));
+ }
+ }
}
#pragma warning restore 618
return true;
}
- private static PingReply SendIcmpEchoRequestOverRawSocket(IPAddress address, byte[] buffer, int timeout, PingOptions? options)
+ private static unsafe PingReply SendIcmpEchoRequestOverRawSocket(IPAddress address, byte[] buffer, int timeout, PingOptions? options)
{
SocketConfig socketConfig = GetSocketConfig(address, buffer, timeout, options);
using (Socket socket = GetRawSocket(socketConfig))
{
return CreatePingReply(IPStatus.PacketTooBig);
}
+ catch (SocketException ex) when (ex.SocketErrorCode == SocketError.HostUnreachable)
+ {
+ // This happens on Linux where we explicitly subscribed to error messages
+ // We should be able to get more info by getting extended socket error from error queue.
+ return CreatePingReplyForUnreachableHost(address, socket);
+ }
// We have exceeded our timeout duration, and no reply has been received.
return CreatePingReply(IPStatus.TimedOut);
}
}
+ private static PingReply CreatePingReplyForUnreachableHost(IPAddress address, Socket socket)
+ {
+ Span<byte> socketAddress = stackalloc byte[SocketAddress.GetMaximumAddressSize(address.AddressFamily)];
+ unsafe
+ {
+ Interop.Sys.MessageHeader header = default;
+
+ SocketError result;
+ fixed (byte* sockAddr = &MemoryMarshal.GetReference(socketAddress))
+ {
+ header.SocketAddress = sockAddr;
+ header.SocketAddressLen = socketAddress.Length;
+ result = Interop.Sys.ReceiveSocketError(socket.SafeHandle, &header);
+ }
+ if (result == SocketError.Success && header.SocketAddressLen > 0)
+ {
+ return CreatePingReply(IPStatus.TtlExpired, IPEndPointExtensions.GetIPAddress(socketAddress.Slice(0, header.SocketAddressLen)));
+ }
+ }
+ return CreatePingReply(IPStatus.TimedOut);
+ }
+
private async Task<PingReply> SendIcmpEchoRequestOverRawSocketAsync(IPAddress address, byte[] buffer, int timeout, PingOptions? options)
{
SocketConfig socketConfig = GetSocketConfig(address, buffer, timeout, options);
catch (OperationCanceledException) when (!_canceled)
{
}
+ catch (SocketException ex) when (ex.SocketErrorCode == SocketError.HostUnreachable)
+ {
+ // This happens on Linux where we explicitly subscribed to error messages
+ // We should be able to get more info by getting extended socket error from error queue.
+ return CreatePingReplyForUnreachableHost(address, socket);
+ }
// We have exceeded our timeout duration, and no reply has been received.
return CreatePingReply(IPStatus.TimedOut);
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-
+using Microsoft.DotNet.XUnitExtensions;
using Xunit;
namespace System.Net.Sockets.Tests
}
}
- [Theory]
+ [ConditionalTheory]
[PlatformSpecific(TestPlatforms.AnyUnix)] // API throws PNSE on Unix
[InlineData(0)]
[InlineData(1)]
{
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
+ if (PlatformDetection.IsQemuLinux && invalidatingAction == 1)
+ {
+ throw new SkipTestException("Skip on Qemu due to [ActiveIssue(https://github.com/dotnet/runtime/issues/104542)]");
+ }
+
switch (invalidatingAction)
{
case 0:
}
}
- [Theory]
+ [ConditionalTheory]
[PlatformSpecific(TestPlatforms.AnyUnix)] // API throws PNSE on Unix
[InlineData(0)]
[InlineData(1)]
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
+ if (PlatformDetection.IsQemuLinux && invalidatingAction == 1)
+ {
+ throw new SkipTestException("Skip on Qemu due to [ActiveIssue(https://github.com/dotnet/runtime/issues/104542)]");
+ }
+
switch (invalidatingAction)
{
case 0:
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.InteropServices;
+using Microsoft.DotNet.XUnitExtensions;
using Xunit;
namespace System.Net.Sockets.Tests
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/104545", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
public void Socket_Get_KeepAlive_Time_AsByteArray_OptionLengthZero_Failure()
{
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
}
}
- [Theory]
+ [ConditionalTheory]
[InlineData(null)]
[InlineData(new byte[0])]
[InlineData(new byte[3] { 0, 0, 0 })]
public void Socket_Get_KeepAlive_Time_AsByteArray_BufferNullOrTooSmall_Failure(byte[] buffer)
{
+ if (PlatformDetection.IsQemuLinux && (buffer == null || buffer.Length == 0))
+ {
+ throw new SkipTestException("Skip on Qemu due to [ActiveIssue(https://github.com/dotnet/runtime/issues/104545)]");
+ }
+
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
if (PlatformDetection.IsWindows)
}
[Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/104547", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
public void MulticastOption_CreateSocketSetGetOption_GroupAndInterfaceIndex_SetSucceeds_GetThrows()
{
int interfaceIndex = 0;
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoNorServerCore))] // Skip on Nano: https://github.com/dotnet/runtime/issues/26286
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/104547", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
public async Task MulticastInterface_Set_AnyInterface_Succeeds()
{
// On all platforms, index 0 means "any interface"
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoNorServerCore))] // Skip on Nano: https://github.com/dotnet/runtime/issues/26286
[SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "Expected behavior is different on OSX or FreeBSD")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/52124", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/104547", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
public async Task MulticastInterface_Set_IPv6_AnyInterface_Succeeds()
{
if (PlatformDetection.IsRedHatFamily7)
return new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll);
}
- internal bool Is32Bit => Machine != Machine.Amd64 && Machine != Machine.IA64 && Machine != Machine.Arm64;
+ internal bool Is32Bit => Machine != Machine.Amd64 && Machine != Machine.IA64 && Machine != Machine.Arm64 && Machine != (Machine)0x5064; /* TODO: update with RiscV64 */
internal int ComputeSizeOfPEHeaders(int sectionCount) =>
PEBuilder.DosHeaderSize +
#cmakedefine01 HAVE_IOS_NET_IFMEDIA_H
#cmakedefine01 HAVE_LINUX_RTNETLINK_H
#cmakedefine01 HAVE_LINUX_CAN_H
+#cmakedefine01 HAVE_LINUX_ERRQUEUE_H
#cmakedefine01 HAVE_GETDOMAINNAME_SIZET
#cmakedefine01 HAVE_INOTIFY
#cmakedefine01 HAVE_CLOCK_MONOTONIC
DllImportEntry(SystemNative_SetSendTimeout)
DllImportEntry(SystemNative_Receive)
DllImportEntry(SystemNative_ReceiveMessage)
+ DllImportEntry(SystemNative_ReceiveSocketError)
DllImportEntry(SystemNative_Send)
DllImportEntry(SystemNative_SendMessage)
DllImportEntry(SystemNative_Accept)
#if HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
+#if HAVE_LINUX_ERRQUEUE_H
+#include <linux/errqueue.h>
+#include <linux/icmp.h>
+#endif
+
#if HAVE_KQUEUE
#if KEVENT_HAS_VOID_UDATA
static int8_t ConvertSocketFlagsPalToPlatform(int32_t palFlags, int* platformFlags)
{
- const int32_t SupportedFlagsMask = SocketFlags_MSG_OOB | SocketFlags_MSG_PEEK | SocketFlags_MSG_DONTROUTE | SocketFlags_MSG_TRUNC | SocketFlags_MSG_CTRUNC;
+ const int32_t SupportedFlagsMask =
+#ifdef MSG_ERRQUEUE
+ SocketFlags_MSG_ERRQUEUE |
+#endif
+ SocketFlags_MSG_OOB | SocketFlags_MSG_PEEK | SocketFlags_MSG_DONTROUTE | SocketFlags_MSG_TRUNC | SocketFlags_MSG_CTRUNC | SocketFlags_MSG_DONTWAIT;
if ((palFlags & ~SupportedFlagsMask) != 0)
{
*platformFlags = ((palFlags & SocketFlags_MSG_OOB) == 0 ? 0 : MSG_OOB) |
((palFlags & SocketFlags_MSG_PEEK) == 0 ? 0 : MSG_PEEK) |
((palFlags & SocketFlags_MSG_DONTROUTE) == 0 ? 0 : MSG_DONTROUTE) |
+ ((palFlags & SocketFlags_MSG_DONTWAIT) == 0 ? 0 : MSG_DONTWAIT) |
((palFlags & SocketFlags_MSG_TRUNC) == 0 ? 0 : MSG_TRUNC) |
((palFlags & SocketFlags_MSG_CTRUNC) == 0 ? 0 : MSG_CTRUNC);
-
+#ifdef MSG_ERRQUEUE
+ if ((palFlags & SocketFlags_MSG_ERRQUEUE) != 0)
+ {
+ *platformFlags |= MSG_ERRQUEUE;
+ }
+#endif
return true;
}
return SystemNative_ConvertErrorPlatformToPal(errno);
}
+int32_t SystemNative_ReceiveSocketError(intptr_t socket, MessageHeader* messageHeader)
+{
+ int fd = ToFileDescriptor(socket);
+ ssize_t res;
+
+#if HAVE_LINUX_ERRQUEUE_H
+ char buffer[sizeof(struct sock_extended_err) + sizeof(struct sockaddr_storage)];
+ messageHeader->ControlBufferLen = sizeof(buffer);
+ messageHeader->ControlBuffer = (void*)buffer;
+
+ struct msghdr header;
+ struct icmphdr icmph;
+ struct iovec iov;
+ ConvertMessageHeaderToMsghdr(&header, messageHeader, fd);
+
+ if (header.msg_iovlen == 0 || !header.msg_iov)
+ {
+ iov.iov_base = &icmph;
+ iov.iov_len = sizeof(icmph);
+ header.msg_iov = &iov;
+ header.msg_iovlen = 1;
+ }
+ while ((res = recvmsg(fd, &header, SocketFlags_MSG_DONTWAIT | SocketFlags_MSG_ERRQUEUE)) < 0 && errno == EINTR);
+
+ struct cmsghdr *cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&header); cmsg; cmsg = GET_CMSG_NXTHDR(&header, cmsg))
+ {
+ if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
+ {
+ struct sock_extended_err *e = (struct sock_extended_err *)CMSG_DATA(cmsg);
+ if (e->ee_origin == SO_EE_ORIGIN_ICMP)
+ {
+ int size = (int)(cmsg->cmsg_len - sizeof(struct sock_extended_err));
+ messageHeader->SocketAddressLen = size < messageHeader->SocketAddressLen ? size : messageHeader->SocketAddressLen;
+ memcpy(messageHeader->SocketAddress, (struct sockaddr_in*)(e+1), (size_t)messageHeader->SocketAddressLen);
+ return Error_SUCCESS;
+ }
+ }
+ }
+#else
+ res = -1;
+ errno = ENOTSUP;
+#endif
+
+ messageHeader->SocketAddressLen = 0;
+
+ if (res != -1)
+ {
+ return Error_SUCCESS;
+ }
+
+ return SystemNative_ConvertErrorPlatformToPal(errno);
+}
+
int32_t SystemNative_ReceiveMessage(intptr_t socket, MessageHeader* messageHeader, int32_t flags, int64_t* received)
{
if (messageHeader == NULL || received == NULL || messageHeader->SocketAddressLen < 0 ||
SocketFlags_MSG_DONTROUTE = 0x0004, // SocketFlags.DontRoute
SocketFlags_MSG_TRUNC = 0x0100, // SocketFlags.Truncated
SocketFlags_MSG_CTRUNC = 0x0200, // SocketFlags.ControlDataTruncated
+ SocketFlags_MSG_DONTWAIT = 0x1000, // used privately by Ping
+ SocketFlags_MSG_ERRQUEUE = 0x2000, // used privately by Ping
} SocketFlags;
/*
PALEXPORT int32_t SystemNative_ReceiveMessage(intptr_t socket, MessageHeader* messageHeader, int32_t flags, int64_t* received);
+PALEXPORT int32_t SystemNative_ReceiveSocketError(intptr_t socket, MessageHeader* messageHeader);
+
PALEXPORT int32_t SystemNative_Send(intptr_t socket, void* buffer, int32_t bufferLen, int32_t flags, int32_t* sent);
PALEXPORT int32_t SystemNative_SendMessage(intptr_t socket, MessageHeader* messageHeader, int32_t flags, int64_t* sent);
"sys/proc_info.h"
HAVE_SYS_PROCINFO_H)
+check_include_files(
+ "time.h;linux/errqueue.h"
+ HAVE_LINUX_ERRQUEUE_H)
+
check_symbol_exists(
epoll_create1
sys/epoll.h
case "x86":
architecture = Architecture.X86;
break;
+ case "riscv64":
+ architecture = (Architecture)9;
+ break;
default:
return false;
}
Architecture.X64 => "x64",
Architecture.Arm => "arm",
Architecture.Arm64 => "arm64",
+ (Architecture)9 => "riscv64",
_ => null
};
}
--- /dev/null
+project (PrimitiveABINative)
+include_directories(${INC_PLATFORM_DIR})
+
+if(CLR_CMAKE_HOST_WIN32)
+ set_source_files_properties(PrimitiveABI.c PROPERTIES COMPILE_OPTIONS /TC) # compile as C
+else()
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -Oz")
+endif()
+
+add_library (PrimitiveABINative SHARED PrimitiveABI.c)
+
+install (TARGETS PrimitiveABINative DESTINATION bin)
--- /dev/null
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT __attribute__((visibility("default")))
+#endif // _MSC_VER
+
+DLLEXPORT int64_t Echo_ExtendedUint_RiscV(int a0, uint32_t a1)
+{
+ return (int32_t)a1;
+}
+
+DLLEXPORT int64_t Echo_ExtendedUint_OnStack_RiscV(
+ int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint32_t stack0)
+{
+ return (int32_t)stack0;
+}
+
+DLLEXPORT double Echo_Float_RiscV(float fa0, float fa1)
+{
+ return fa1 + fa0;
+}
+
+DLLEXPORT double Echo_Float_InIntegerReg_RiscV(
+ float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
+ float a0)
+{
+ return a0 + fa7;
+}
+
+DLLEXPORT double Echo_Float_OnStack_RiscV(
+ float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
+ int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0)
+{
+ return stack0 + fa7;
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using Xunit;
+
+public static class Program
+{
+#region ExtendedUint_RiscVTests
+ [DllImport("PrimitiveABINative")]
+ public static extern long Echo_ExtendedUint_RiscV(int a0, uint a1);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static long Echo_ExtendedUint_RiscV_Managed(int a0, uint a1) => unchecked((int)a1);
+
+ [Fact]
+ public static void Test_ExtendedUint_RiscV()
+ {
+ const uint arg = 0xB1ED0C1Eu;
+ const long ret = unchecked((int)arg);
+ long managed = Echo_ExtendedUint_RiscV_Managed(0, arg);
+ long native = Echo_ExtendedUint_RiscV(0, arg);
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+
+ [Fact]
+ public static void Test_ExtendedUint_ByReflection_RiscV()
+ {
+ const uint arg = 0xB1ED0C1Eu;
+ const long ret = unchecked((int)arg);
+ long managed = (long)typeof(Program).GetMethod("Echo_ExtendedUint_RiscV_Managed").Invoke(
+ null, new object[] {0, arg});
+ long native = (long)typeof(Program).GetMethod("Echo_ExtendedUint_RiscV").Invoke(
+ null, new object[] {0, arg});
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+
+ [DllImport("PrimitiveABINative")]
+ public static extern long Echo_ExtendedUint_OnStack_RiscV(
+ int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint stack0);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static long Echo_ExtendedUint_OnStack_RiscV_Managed(
+ int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, uint stack0) => unchecked((int)stack0);
+
+ [Fact]
+ public static void Test_ExtendedUint_OnStack_RiscV()
+ {
+ const uint arg = 0xB1ED0C1Eu;
+ const long ret = unchecked((int)arg);
+ long managed = Echo_ExtendedUint_OnStack_RiscV_Managed(0, 0, 0, 0, 0, 0, 0, 0, arg);
+ long native = Echo_ExtendedUint_OnStack_RiscV(0, 0, 0, 0, 0, 0, 0, 0, arg);
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+
+ [Fact]
+ public static void Test_ExtendedUint_OnStack_ByReflection_RiscV()
+ {
+ const uint arg = 0xB1ED0C1Eu;
+ const long ret = unchecked((int)arg);
+ long managed = (long)typeof(Program).GetMethod("Echo_ExtendedUint_OnStack_RiscV_Managed").Invoke(
+ null, new object[] {0, 0, 0, 0, 0, 0, 0, 0, arg});
+ long native = (long)typeof(Program).GetMethod("Echo_ExtendedUint_OnStack_RiscV").Invoke(
+ null, new object[] {0, 0, 0, 0, 0, 0, 0, 0, arg});
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+#endregion
+
+#region Float_RiscVTests
+ [DllImport("PrimitiveABINative")]
+ public static extern double Echo_Float_RiscV(float fa0, float fa1);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static double Echo_Float_RiscV_Managed(float fa0, float fa1) => fa1;
+
+ [Fact]
+ public static void Test_Float_RiscV()
+ {
+ const float arg = 3.14159f;
+ const double ret = 3.14159f;
+ double managed = Echo_Float_RiscV_Managed(0f, arg);
+ double native = Echo_Float_RiscV(0f, arg);
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+
+ [Fact]
+ public static void Test_Float_ByReflection_RiscV()
+ {
+ const float arg = 3.14159f;
+ const double ret = 3.14159f;
+ double managed = (double)typeof(Program).GetMethod("Echo_Float_RiscV_Managed").Invoke(
+ null, new object[] {0f, arg});
+ double native = (double)typeof(Program).GetMethod("Echo_Float_RiscV").Invoke(
+ null, new object[] {0f, arg});
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+
+ [DllImport("PrimitiveABINative")]
+ public static extern double Echo_Float_InIntegerReg_RiscV(
+ float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, float a0);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static double Echo_Float_InIntegerReg_RiscV_Managed(
+ float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, float a0) => a0;
+
+ [Fact]
+ public static void Test_Float_InIntegerReg_RiscV()
+ {
+ const float arg = 3.14159f;
+ const double ret = 3.14159f;
+ double managed = Echo_Float_InIntegerReg_RiscV_Managed(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg);
+ double native = Echo_Float_InIntegerReg_RiscV(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg);
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+
+ [Fact]
+ public static void Test_Float_InIntegerReg_ByReflection_RiscV()
+ {
+ const float arg = 3.14159f;
+ const double ret = 3.14159f;
+ double managed = (double)typeof(Program).GetMethod("Echo_Float_InIntegerReg_RiscV_Managed").Invoke(
+ null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg});
+ double native = (double)typeof(Program).GetMethod("Echo_Float_InIntegerReg_RiscV").Invoke(
+ null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, arg});
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+
+ [DllImport("PrimitiveABINative")]
+ public static extern double Echo_Float_OnStack_RiscV(
+ float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
+ int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static double Echo_Float_OnStack_RiscV_Managed(
+ float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7,
+ int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float stack0) => stack0;
+
+ [Fact]
+ public static void Test_Float_OnStack_RiscV()
+ {
+ const float arg = 3.14159f;
+ const double ret = 3.14159f;
+ double managed = Echo_Float_OnStack_RiscV_Managed(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg);
+ double native = Echo_Float_OnStack_RiscV(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg);
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+
+ [Fact]
+ public static void Test_Float_OnStack_ByReflection_RiscV()
+ {
+ const float arg = 3.14159f;
+ const double ret = 3.14159f;
+ double managed = (double)typeof(Program).GetMethod("Echo_Float_OnStack_RiscV_Managed").Invoke(
+ null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg});
+ double native = (double)typeof(Program).GetMethod("Echo_Float_OnStack_RiscV").Invoke(
+ null, new object[] {0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0, 0, 0, 0, 0, 0, 0, 0, arg});
+
+ Assert.Equal(ret, managed);
+ Assert.Equal(ret, native);
+ }
+#endregion
+}
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <!-- Needed for CMakeProjectReference -->
+ <RequiresProcessIsolation>true</RequiresProcessIsolation>
+ </PropertyGroup>
+ <PropertyGroup>
+ <DebugType>PdbOnly</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="PrimitiveABI.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <CMakeProjectReference Include="CMakeLists.txt" />
+ </ItemGroup>
+</Project>
include_directories(${INC_PLATFORM_DIR})
if(CLR_CMAKE_HOST_WIN32)
- add_compile_options(/TC) # compile all files as C
+ set_source_files_properties(StructABI.c PROPERTIES COMPILE_OPTIONS /TC) # compile as C
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
+ set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} -fvisibility=hidden -Wno-return-type-c-linkage")
endif()
# add the executable
add_library (StructABILib SHARED StructABI.c)
+add_library (EmptyStructsLib SHARED EmptyStructs.cpp)
# add the install targets
-install (TARGETS StructABILib DESTINATION bin)
+install (TARGETS StructABILib EmptyStructsLib DESTINATION bin)
--- /dev/null
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef _MSC_VER
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT __attribute__((visibility("default")))
+#endif // _MSC_VER
+
+struct Empty
+{
+};
+static_assert(sizeof(Empty) == 1, "Empty struct must be sized like in .NET");
+
+
+struct IntEmpty
+{
+ int32_t Int0;
+ Empty Empty0;
+};
+
+extern "C" DLLEXPORT IntEmpty EchoIntEmptySysV(int i0, IntEmpty val)
+{
+ return val;
+}
+
+
+struct IntEmptyPair
+{
+ IntEmpty IntEmpty0;
+ IntEmpty IntEmpty1;
+};
+
+extern "C" DLLEXPORT IntEmptyPair EchoIntEmptyPairSysV(int i0, IntEmptyPair val)
+{
+ return val;
+}
+
+
+struct EmptyFloatIntInt
+{
+ Empty Empty0;
+ float Float0;
+ int32_t Int0;
+ int32_t Int1;
+};
+
+extern "C" DLLEXPORT EmptyFloatIntInt EchoEmptyFloatIntIntSysV(int i0, float f0, EmptyFloatIntInt val)
+{
+ return val;
+}
+
+
+struct FloatFloatEmptyFloat
+{
+ float Float0;
+ float Float1;
+ Empty Empty0;
+ float Float2;
+};
+
+extern "C" DLLEXPORT FloatFloatEmptyFloat EchoFloatFloatEmptyFloatSysV(float f0, FloatFloatEmptyFloat val)
+{
+ return val;
+}
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using Xunit;
+
+public static class Program
+{
+ public struct Empty
+ {
+ }
+
+
+ public struct IntEmpty
+ {
+ public int Int0;
+ public Empty Empty0;
+
+ public static IntEmpty Get()
+ => new IntEmpty { Int0 = 0xBabc1a };
+
+ public bool Equals(IntEmpty other)
+ => Int0 == other.Int0;
+
+ public override string ToString()
+ => $"{{Int0:{Int0:x}}}";
+ }
+
+ [DllImport("EmptyStructsLib")]
+ public static extern IntEmpty EchoIntEmptySysV(int i0, IntEmpty val);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static IntEmpty EchoIntEmptySysVManaged(int i0, IntEmpty val) => val;
+
+ [Fact]
+ public static void TestIntEmptySysV()
+ {
+ IntEmpty expected = IntEmpty.Get();
+ IntEmpty native = EchoIntEmptySysV(0, expected);
+ IntEmpty managed = EchoIntEmptySysVManaged(0, expected);
+
+ Assert.Equal(expected, native);
+ Assert.Equal(expected, managed);
+ }
+
+
+ public struct IntEmptyPair
+ {
+ public IntEmpty IntEmpty0;
+ public IntEmpty IntEmpty1;
+
+ public static IntEmptyPair Get()
+ => new IntEmptyPair { IntEmpty0 = IntEmpty.Get(), IntEmpty1 = IntEmpty.Get() };
+
+ public bool Equals(IntEmptyPair other)
+ => IntEmpty0.Equals(other.IntEmpty0) && IntEmpty1.Equals(other.IntEmpty1);
+
+ public override string ToString()
+ => $"{{IntEmpty0:{IntEmpty0}, IntEmpty1:{IntEmpty1}}}";
+ }
+
+ [DllImport("EmptyStructsLib")]
+ public static extern IntEmptyPair EchoIntEmptyPairSysV(int i0, IntEmptyPair val);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static IntEmptyPair EchoIntEmptyPairSysVManaged(int i0, IntEmptyPair val) => val;
+
+ [Fact]
+ public static void TestIntEmptyPairSysV()
+ {
+ IntEmptyPair expected = IntEmptyPair.Get();
+ IntEmptyPair native = EchoIntEmptyPairSysV(0, expected);
+ IntEmptyPair managed = EchoIntEmptyPairSysVManaged(0, expected);
+
+ Assert.Equal(expected, native);
+ Assert.Equal(expected, managed);
+ }
+
+
+ public struct EmptyFloatIntInt
+ {
+ public Empty Empty0;
+ public float Float0;
+ public int Int0;
+ public int Int1;
+
+ public static EmptyFloatIntInt Get()
+ => new EmptyFloatIntInt { Float0 = 2.71828f, Int0 = 0xBabc1a, Int1 = 0xC10c1a };
+
+ public bool Equals(EmptyFloatIntInt other)
+ => Float0 == other.Float0 && Int0 == other.Int0 && Int1 == other.Int1;
+
+ public override string ToString()
+ => $"{{Float0:{Float0}, Int0:{Int0:x}, Int1:{Int1:x}}}";
+ }
+
+ [DllImport("EmptyStructsLib")]
+ public static extern EmptyFloatIntInt EchoEmptyFloatIntIntSysV(int i0, float f0, EmptyFloatIntInt val);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static EmptyFloatIntInt EchoEmptyFloatIntIntSysVManaged(int i0, float f0, EmptyFloatIntInt val) => val;
+
+ [Fact]
+ public static void TestEmptyFloatIntIntSysV()
+ {
+ EmptyFloatIntInt expected = EmptyFloatIntInt.Get();
+ EmptyFloatIntInt native = EchoEmptyFloatIntIntSysV(0, 0f, expected);
+ EmptyFloatIntInt managed = EchoEmptyFloatIntIntSysVManaged(0, 0f, expected);
+
+ Assert.Equal(expected, native);
+ Assert.Equal(expected, managed);
+ }
+
+
+ public struct FloatFloatEmptyFloat
+ {
+ public float Float0;
+ public float Float1;
+ public Empty Empty0;
+ public float Float2;
+
+ public static FloatFloatEmptyFloat Get()
+ => new FloatFloatEmptyFloat { Float0 = 2.71828f, Float1 = 3.14159f, Float2 = 1.61803f };
+
+ public bool Equals(FloatFloatEmptyFloat other)
+ => Float0 == other.Float0 && Float1 == other.Float1 && Float2 == other.Float2;
+
+ public override string ToString()
+ => $"{{Float0:{Float0}, Float1:{Float1}, Float2:{Float2}}}";
+ }
+
+ [DllImport("EmptyStructsLib")]
+ public static extern FloatFloatEmptyFloat EchoFloatFloatEmptyFloatSysV(float f0, FloatFloatEmptyFloat val);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static FloatFloatEmptyFloat EchoFloatFloatEmptyFloatSysVManaged(float f0, FloatFloatEmptyFloat val) => val;
+
+ [Fact]
+ public static void TestFloatFloatEmptyFloatSysV()
+ {
+ FloatFloatEmptyFloat expected = FloatFloatEmptyFloat.Get();
+ FloatFloatEmptyFloat native = EchoFloatFloatEmptyFloatSysV(0f, expected);
+ FloatFloatEmptyFloat managed = EchoFloatFloatEmptyFloatSysVManaged(0f, expected);
+
+ Assert.Equal(expected, native);
+ Assert.Equal(expected, managed);
+ }
+}
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <!-- Needed for CMakeProjectReference -->
+ <RequiresProcessIsolation>true</RequiresProcessIsolation>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <PropertyGroup>
+ <DebugType>PdbOnly</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="EmptyStructs.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <CMakeProjectReference Include="CMakeLists.txt" />
+ </ItemGroup>
+</Project>
bool SlowPathELTProfiler::ValidateInt(UINT_PTR ptr, int expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateFloat(UINT_PTR ptr, float expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateDouble(UINT_PTR ptr, double expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateString(UINT_PTR ptr, const WCHAR *expected)
{
- if (ptr == NULL || *(void **)ptr == NULL)
+ if (ptr == (UINT_PTR)NULL || *(void **)ptr == NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateMixedStruct(UINT_PTR ptr, MixedStruct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateLargeStruct(UINT_PTR ptr, LargeStruct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x2Struct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x3Struct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x4Struct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x2Struct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x3Struct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x4Struct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateIntegerStruct(UINT_PTR ptr, IntegerStruct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateIntegerSseStruct(UINT_PTR ptr, IntegerSseStruct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateSseIntegerStruct(UINT_PTR ptr, SseIntegerStruct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateMixedSseStruct(UINT_PTR ptr, MixedSseStruct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateSseMixedStruct(UINT_PTR ptr, SseMixedStruct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
bool SlowPathELTProfiler::ValidateMixedMixedStruct(UINT_PTR ptr, MixedMixedStruct expected)
{
- if (ptr == NULL)
+ if (ptr == (UINT_PTR)NULL)
{
return false;
}
for (ULONG i = 0; i < cObjectRefs; ++i)
{
ObjectID obj = objectRefIds[i];
- if (obj != NULL)
+ if (obj != 0)
{
_objectReferencesSeen.insert(obj);
}
for (ULONG i = 0; i < cRootRefs; ++i)
{
ObjectID obj = rootRefIds[i];
- if (obj != NULL)
+ if (obj != 0)
{
_rootReferencesSeen.insert(obj);
}
HRESULT hr = S_OK;
- ThreadID threadId = NULL;
- AppDomainID appDomainId = NULL;
+ ThreadID threadId = 0;
+ AppDomainID appDomainId = 0;
CorElementType baseElemType;
ClassID baseClassId;
ULONG cRank;
&modId,
NULL,
NULL,
- NULL,
+ 0,
NULL,
NULL);
if (FAILED(hr))
fflush(stdout);
}
- ModuleID classModuleId = NULL;
+ ModuleID classModuleId = 0;
hr = pCorProfilerInfo->GetClassIDInfo2(classId,
&classModuleId,
NULL,
NULL,
- NULL,
+ 0,
NULL,
NULL);
if (FAILED(hr))
}
HCORENUM hEnum = NULL;
- mdTypeDef token = NULL;
+ mdTypeDef token = 0;
mdFieldDef fieldTokens[SHORT_LENGTH];
- ULONG cTokens = NULL;
+ ULONG cTokens = 0;
if (DEBUG_OUT)
{
NULL,
&token,
NULL,
- NULL,
+ 0,
NULL,
NULL);
if (hr == CORPROF_E_DATAINCOMPLETE)
for (ULONG i = 0; i < cTokens; i++)
{
- mdTypeDef fieldClassToken = NULL;
+ mdTypeDef fieldClassToken = 0;
WCHAR tokenName[256];
- ULONG nameLength = NULL;
- DWORD fieldAttributes = NULL;
- PCCOR_SIGNATURE pvSig = NULL;
- ULONG cbSig = NULL;
- DWORD corElementType = NULL;
+ ULONG nameLength = 0;
+ DWORD fieldAttributes = 0;
+ PCCOR_SIGNATURE pvSig = 0;
+ ULONG cbSig = 0;
+ DWORD corElementType = 0;
hr = pIMDImport->GetFieldProps(fieldTokens[i],
&fieldClassToken,
{
_failures++;
printf("HandlesProfiler::CheckIfAlive(%s): FAIL: null handle.\n", name);
- return NULL;
+ return 0;
}
ObjectID objectId{0};
{
_failures++;
printf("HandlesProfiler::CheckIfAlive(%s): FAIL: GetObjectIDFromHandle failed.\n", name);
- return NULL;
+ return 0;
}
if (shouldBeAlive)
{
- if (objectId == NULL)
+ if (objectId == 0)
{
_failures++;
printf("HandlesProfiler::CheckIfAlive(%s): FAIL: the object should be alive.\n", name);
}
else
{
- if (objectId != NULL)
+ if (objectId != 0)
{
_failures++;
printf("HandlesProfiler::CheckIfAlive(%s): FAIL: the object should not be alive anymore.\n", name);
}
}
- return NULL;
+ return 0;
}
HRESULT HandlesProfiler::GarbageCollectionFinished()
_weakHandle(NULL),
_strongHandle(NULL),
_pinnedHandle(NULL),
- _pinnedObject(NULL)
+ _pinnedObject(0)
{}
static GUID GetClsid();
String name;
- ClassID classId = NULL;
- ModuleID moduleId = NULL;
- mdToken token = NULL;
- ULONG32 nTypeArgs = NULL;
+ ClassID classId = 0;
+ ModuleID moduleId = 0;
+ mdToken token = 0;
+ ULONG32 nTypeArgs = 0;
ClassID typeArgs[SHORT_LENGTH];
- COR_PRF_FRAME_INFO frameInfo = NULL;
+ COR_PRF_FRAME_INFO frameInfo = 0;
HRESULT hr = S_OK;
hr = pCorProfilerInfo->GetFunctionInfo2(funcId,
ClassID typeArgs[SHORT_LENGTH];
HRESULT hr = S_OK;
- if (classId == NULL)
+ if (classId == 0)
{
printf("FAIL: Null ClassID passed in\n");
return WCHAR("");
ULONG nameLength = 0;
AssemblyID assemID;
- if (modId == NULL)
+ if (modId == 0)
{
printf("FAIL: Null ModuleID\n");
return WCHAR("NullModuleIDPassedIn");
for (auto &&address : codeStartAddresses)
{
- if (address == NULL)
+ if (address == (UINT_PTR)NULL)
{
printf("Found NULL start address from GetNativeCodeStartAddresses.\n");
_failures++;
mdMethodDef ReJITProfiler::GetMethodDefForFunction(FunctionID functionId)
{
- ClassID classId = NULL;
- ModuleID moduleId = NULL;
- mdToken token = NULL;
- ULONG32 nTypeArgs = NULL;
+ ClassID classId = 0;
+ ModuleID moduleId = 0;
+ mdToken token = 0;
+ ULONG32 nTypeArgs = 0;
ClassID typeArgs[SHORT_LENGTH];
- COR_PRF_FRAME_INFO frameInfo = NULL;
+ COR_PRF_FRAME_INFO frameInfo = 0;
HRESULT hr = S_OK;
hr = pCorProfilerInfo->GetFunctionInfo2(functionId,
ModuleID ReJITProfiler::GetModuleIDForFunction(FunctionID functionId)
{
- ClassID classId = NULL;
- ModuleID moduleId = NULL;
- mdToken token = NULL;
- ULONG32 nTypeArgs = NULL;
+ ClassID classId = 0;
+ ModuleID moduleId = 0;
+ mdToken token = 0;
+ ULONG32 nTypeArgs = 0;
ClassID typeArgs[SHORT_LENGTH];
- COR_PRF_FRAME_INFO frameInfo = NULL;
+ COR_PRF_FRAME_INFO frameInfo = 0;
HRESULT hr = S_OK;
hr = pCorProfilerInfo->GetFunctionInfo2(functionId,
string crossgen2OptsR2RTest = Environment.GetEnvironmentVariable("CrossGen2OptionsR2RTest");
- ProcessStartInfo processStartInfo = new ProcessStartInfo(coreRunPath, $"{superIlcPath} compile-directory -cr {coreRootPath} -in {compilationInputFolder} {crossgen2OptsR2RTest} --nojit --noexe --large-bubble --release --nocleanup -out {outDir}");
+ ProcessStartInfo processStartInfo = new ProcessStartInfo(coreRunPath, $"{superIlcPath} compile-directory -cr {coreRootPath} -in {compilationInputFolder} {crossgen2OptsR2RTest} --nojit --noexe --large-bubble --release --nocleanup -ct 30 -out {outDir}");
var process = Process.Start(processStartInfo);
process.WaitForExit();
if (process.ExitCode != 0)
public class OutputStep : BaseStep
{
- private Dictionary<ushort, TargetArchitecture>? architectureMap;
-
- private enum NativeOSOverride
- {
- Apple = 0x4644,
- FreeBSD = 0xadc4,
- Linux = 0x7b79,
- NetBSD = 0x1993,
- Default = 0
- }
-
readonly List<string> assembliesWritten;
public OutputStep ()
assembliesWritten = new List<string> ();
}
- TargetArchitecture CalculateArchitecture (TargetArchitecture readyToRunArch)
- {
- if (architectureMap == null) {
- architectureMap = new Dictionary<ushort, TargetArchitecture> ();
- foreach (var os in Enum.GetValues (typeof (NativeOSOverride))) {
- ushort osVal = (ushort) (NativeOSOverride) os;
- foreach (var arch in Enum.GetValues (typeof (TargetArchitecture))) {
- ushort archVal = (ushort) (TargetArchitecture) arch;
- architectureMap.Add ((ushort) (archVal ^ osVal), (TargetArchitecture) arch);
- }
- }
- }
-
- if (architectureMap.TryGetValue ((ushort) readyToRunArch, out TargetArchitecture pureILArch)) {
- return pureILArch;
- }
- throw new BadImageFormatException ("unrecognized module attributes");
- }
-
protected override bool ConditionToProcess ()
{
return Context.ErrorsCount == 0;
if (module.IsCrossgened ()) {
module.Attributes |= ModuleAttributes.ILOnly;
module.Attributes ^= ModuleAttributes.ILLibrary;
- module.Architecture = CalculateArchitecture (module.Architecture);
+ module.Architecture = TargetArchitecture.I386; // I386+ILOnly which ultimately translates to AnyCPU
}
}