Sync aug24 release/8.0-tizen (#595) accepted/tizen_unified_x tizen_dev accepted/tizen/unified/x/20240913.014835
authorTimur Mustafin/Advanced System SW Lab /SRR/Staff Engineer/Samsung Electronics <t.mustafin@partner.samsung.com>
Thu, 12 Sep 2024 06:56:46 +0000 (09:56 +0300)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Thu, 12 Sep 2024 06:56:46 +0000 (09:56 +0300)
* [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>
119 files changed:
eng/targetingpacks.targets
src/coreclr/crossgen-corelib.proj
src/coreclr/debug/daccess/daccess.cpp
src/coreclr/debug/daccess/stack.cpp
src/coreclr/debug/di/rsthread.cpp
src/coreclr/gc/gc.cpp
src/coreclr/gc/gc.h
src/coreclr/inc/clrconfigvalues.h
src/coreclr/inc/log.h
src/coreclr/jit/emitriscv64.cpp
src/coreclr/jit/lsra.h
src/coreclr/jit/targetriscv64.h
src/coreclr/pal/src/arch/arm/context2.S
src/coreclr/pal/src/arch/i386/context2.S
src/coreclr/pal/src/arch/riscv64/context2.S
src/coreclr/tools/Common/CommandLineHelpers.cs
src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs
src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/AddrMode.cs [new file with mode: 0644]
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/Register.cs [new file with mode: 0644]
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs [new file with mode: 0644]
src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/TargetRegisterMap.cs [new file with mode: 0644]
src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs
src/coreclr/tools/Common/Compiler/VectorFieldLayoutAlgorithm.cs
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs
src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs
src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs
src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs
src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64JumpStubNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64TentativeMethodNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64UnboxingStubNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj
src/coreclr/tools/aot/ILCompiler.Diagnostics/PerfMapWriter.cs
src/coreclr/tools/aot/ILCompiler.Diagnostics/ReadyToRunDiagnosticsConstants.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodGCInfoNode.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TargetExtensions.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcInfo.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/RiscV64/Registers.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/RiscV64/UnwindInfo.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs
src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs
src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs
src/coreclr/tools/aot/crossgen2/crossgen2.props
src/coreclr/tools/r2rdump/CoreDisTools.cs
src/coreclr/tools/r2rdump/Program.cs
src/coreclr/tools/r2rdump/R2RDump.csproj
src/coreclr/vm/amd64/Context.asm
src/coreclr/vm/argdestination.h
src/coreclr/vm/callhelpers.cpp
src/coreclr/vm/callingconvention.h
src/coreclr/vm/invokeutil.cpp
src/coreclr/vm/loongarch64/profiler.cpp
src/coreclr/vm/loongarch64/stubs.cpp
src/coreclr/vm/methodtable.cpp
src/coreclr/vm/riscv64/cgencpu.h
src/coreclr/vm/riscv64/stubs.cpp
src/coreclr/vm/riscv64/virtualcallstubcpu.hpp
src/coreclr/vm/stubmgr.cpp
src/coreclr/vm/stubmgr.h
src/coreclr/vm/util.cpp
src/installer/pkg/projects/netcoreappRIDs.props
src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj
src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props
src/libraries/Common/src/Interop/Unix/System.Native/Interop.IOVector.cs
src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReceiveSocketError.cs [new file with mode: 0644]
src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs
src/libraries/System.Console/tests/CancelKeyPress.Unix.cs
src/libraries/System.Console/tests/CancelKeyPress.cs
src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs
src/libraries/System.Net.Ping/src/System.Net.Ping.csproj
src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/KeepAliveTest.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.cs
src/native/libs/Common/pal_config.h.in
src/native/libs/System.Native/entrypoints.c
src/native/libs/System.Native/pal_networking.c
src/native/libs/System.Native/pal_networking.h
src/native/libs/configure.cmake
src/tasks/Crossgen2Tasks/ResolveReadyToRunCompilers.cs
src/tests/JIT/Directed/PrimitiveABI/CMakeLists.txt [new file with mode: 0644]
src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.c [new file with mode: 0644]
src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.cs [new file with mode: 0644]
src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.csproj [new file with mode: 0644]
src/tests/JIT/Directed/StructABI/CMakeLists.txt
src/tests/JIT/Directed/StructABI/EmptyStructs.cpp [new file with mode: 0644]
src/tests/JIT/Directed/StructABI/EmptyStructs.cs [new file with mode: 0644]
src/tests/JIT/Directed/StructABI/EmptyStructs.csproj [new file with mode: 0644]
src/tests/profiler/native/eltprofiler/slowpatheltprofiler.cpp
src/tests/profiler/native/gcprofiler/gcprofiler.cpp
src/tests/profiler/native/getappdomainstaticaddress/getappdomainstaticaddress.cpp
src/tests/profiler/native/handlesprofiler/handlesprofiler.cpp
src/tests/profiler/native/handlesprofiler/handlesprofiler.h
src/tests/profiler/native/profiler.cpp
src/tests/profiler/native/rejitprofiler/rejitprofiler.cpp
src/tests/readytorun/coreroot_determinism/Program.cs
src/tools/illink/src/linker/Linker.Steps/OutputStep.cs

index d0c39eb..d18ed32 100644 (file)
@@ -35,7 +35,7 @@
                              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"
index 2dc62b8..c24592e 100644 (file)
@@ -24,7 +24,7 @@
   <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>
index 2ff45fd..515cf62 100644 (file)
@@ -5970,7 +5970,7 @@ ClrDataAccess::GetMethodVarInfo(MethodDesc* methodDesc,
 {
     SUPPORTS_DAC;
     COUNT_T countNativeVarInfo;
-    NewHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL);
+    NewArrayHolder<ICorDebugInfo::NativeVarInfo> nativeVars(NULL);
     TADDR nativeCodeStartAddr;
     if (address != NULL)
     {
index 6b9f1a4..a0dc074 100644 (file)
@@ -1309,7 +1309,7 @@ ClrDataFrame::ValueFromDebugInfo(MetaSig* sig,
 {
     HRESULT status;
     ULONG32 numVarInfo;
-    NewHolder<ICorDebugInfo::NativeVarInfo> varInfo(NULL);
+    NewArrayHolder<ICorDebugInfo::NativeVarInfo> varInfo(NULL);
     ULONG32 codeOffset;
     ULONG32 valueFlags;
     ULONG32 i;
index 385addc..92b7f33 100644 (file)
@@ -7158,6 +7158,11 @@ HRESULT CordbNativeFrame::GetLocalFloatingPointValue(DWORD index,
         (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)))
index 7351954..5061b40 100644 (file)
@@ -3616,10 +3616,9 @@ sorted_table::delete_sorted_table()
 {
     if (slots != (bk*)(this+1))
     {
-        delete slots;
+        delete[] slots;
     }
     delete_old_slots();
-    delete this;
 }
 void
 sorted_table::delete_old_slots()
@@ -3629,7 +3628,7 @@ sorted_table::delete_old_slots()
     {
         uint8_t* dsl = sl;
         sl = last_slot ((bk*)sl);
-        delete dsl;
+        delete[] dsl;
     }
     old_slots = 0;
 }
@@ -15313,14 +15312,12 @@ gc_heap::destroy_semi_shared()
 //#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
 }
 
@@ -15377,7 +15374,7 @@ void gc_heap::shutdown_gc()
 
 #ifdef MULTIPLE_HEAPS
     //delete the heaps array
-    delete g_heaps;
+    delete[] g_heaps;
     destroy_thread_support();
     n_heaps = 0;
 #endif //MULTIPLE_HEAPS
@@ -28641,7 +28638,7 @@ recheck:
             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;
             }
@@ -33493,7 +33490,7 @@ void gc_heap::plan_phase (int condemned_gen_number)
 
                 if (!loh_pinned_queue_decay)
                 {
-                    delete loh_pinned_queue;
+                    delete[] loh_pinned_queue;
                     loh_pinned_queue = 0;
                 }
             }
@@ -51333,7 +51330,7 @@ bool CFinalize::Initialize()
 
 CFinalize::~CFinalize()
 {
-    delete m_Array;
+    delete[] m_Array;
 }
 
 size_t CFinalize::GetPromotedCount ()
index e9c739e..85655fc 100644 (file)
@@ -136,10 +136,6 @@ extern size_t gc_global_mechanisms[MAX_GLOBAL_GC_MECHANISMS_COUNT];
 class DacHeapWalker;
 #endif
 
-#ifdef _DEBUG
-#define  _LOGALLOC
-#endif
-
 #define MP_LOCKS
 
 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
index 80b1c4e..6a87f18 100644 (file)
@@ -720,7 +720,12 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_LTTng, W("LTTng"), 1, "If DOTNET_LTTng is s
 //
 // 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
 ///
index 3191921..3f608ce 100644 (file)
@@ -47,6 +47,10 @@ enum {
 #define ERROR       0
 #define FATALERROR  0
 
+#ifdef _DEBUG
+#define _LOGALLOC
+#endif
+
 #ifndef LOGGING
 
 #define LOG(x)
index eb60190..21ae5fc 100644 (file)
@@ -3309,6 +3309,13 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
             assert(!"JitBreakEmitOutputInstr reached");
         }
     }
+
+    // Output any delta in GC info.
+    if (EMIT_GC_VERBOSE || emitComp->opts.disasmWithGC)
+    {
+        emitDispGCInfoDelta();
+    }
+
 #else  // !DEBUG
     if (emitComp->opts.disAsm)
     {
index 20941e4..637095b 100644 (file)
@@ -476,6 +476,7 @@ public:
         regNum           = REG_NA;
         isCalleeSave     = false;
         registerType     = IntRegisterType;
+        regOrder         = UCHAR_MAX;
     }
 
     void init(regNumber reg)
index 5ac82fa..c732ad8 100644 (file)
                                    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
index 32983c1..73ffd5f 100644 (file)
@@ -161,25 +161,28 @@ LOCAL_LABEL(Restore_CONTEXT_FLOATING_POINT):
     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)]
index cf5b464..35768ea 100644 (file)
@@ -115,11 +115,12 @@ LOCAL_LABEL(Done_Restore_CONTEXT_FLOATING_POINT):
     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]
@@ -128,7 +129,13 @@ LOCAL_LABEL(Done_Restore_CONTEXT_EXTENDED_REGISTERS):
     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
index 20273eb..725e4ec 100644 (file)
@@ -105,10 +105,15 @@ LOCAL_LABEL(No_Restore_CONTEXT_INTEGER):
     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):
index 3fb977a..0fa595d 100644 (file)
@@ -96,6 +96,7 @@ namespace System.CommandLine
                     Architecture.Arm => TargetArchitecture.ARM,
                     Architecture.Arm64 => TargetArchitecture.ARM64,
                     Architecture.LoongArch64 => TargetArchitecture.LoongArch64,
+                    (Architecture)9 => TargetArchitecture.RiscV64, /* TODO: update with Architecture.RiscV64 */
                     _ => throw new NotImplementedException()
                 };
             }
@@ -108,6 +109,7 @@ namespace System.CommandLine
                     "arm" or "armel" => TargetArchitecture.ARM,
                     "arm64" => TargetArchitecture.ARM64,
                     "loongarch64" => TargetArchitecture.LoongArch64,
+                    "riscv64" => TargetArchitecture.RiscV64,
                     _ => throw new CommandLineException($"Target architecture '{token}' is not supported")
                 };
             }
index 6b51f87..cc1e389 100644 (file)
@@ -75,6 +75,13 @@ namespace ILCompiler.DependencyAnalysis
                     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();
             }
@@ -85,5 +92,6 @@ namespace ILCompiler.DependencyAnalysis
         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);
     }
 }
index d9d56e3..4043dad 100644 (file)
@@ -319,6 +319,8 @@ namespace ILCompiler.DependencyAnalysis
 
                 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.
index b833c90..4756ae2 100644 (file)
@@ -19,6 +19,7 @@ namespace ILCompiler.DependencyAnalysis
         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
@@ -411,6 +412,55 @@ namespace ILCompiler.DependencyAnalysis
             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;
@@ -455,6 +505,9 @@ namespace ILCompiler.DependencyAnalysis
                 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;
@@ -517,6 +570,8 @@ namespace ILCompiler.DependencyAnalysis
                     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;
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/AddrMode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/AddrMode.cs
new file mode 100644 (file)
index 0000000..03ed433
--- /dev/null
@@ -0,0 +1,32 @@
+// 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;
+        }
+    }
+}
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/Register.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/Register.cs
new file mode 100644 (file)
index 0000000..2d3a13f
--- /dev/null
@@ -0,0 +1,44 @@
+// 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
+    }
+}
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs
new file mode 100644 (file)
index 0000000..7bec609
--- /dev/null
@@ -0,0 +1,135 @@
+// 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);
+        }
+    }
+}
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/TargetRegisterMap.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/TargetRegisterMap.cs
new file mode 100644 (file)
index 0000000..493fc44
--- /dev/null
@@ -0,0 +1,38 @@
+// 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;
+        }
+    }
+}
index d66eee9..22e733c 100644 (file)
@@ -89,9 +89,13 @@ namespace ILCompiler
                 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;
@@ -141,6 +145,10 @@ namespace ILCompiler
             {
                 return SimdVectorLength.None;
             }
+            else if (_targetArchitecture == TargetArchitecture.RiscV64)
+            {
+                return SimdVectorLength.None;
+            }
             else
             {
                 Debug.Assert(false); // Unknown architecture
index ac52471..bb0a5bc 100644 (file)
@@ -58,6 +58,13 @@ namespace ILCompiler
                     // 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);
@@ -79,6 +86,13 @@ namespace ILCompiler
                     // 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);
index 9d98650..0ac8713 100644 (file)
@@ -56,6 +56,7 @@ namespace Internal.JitInterface
             ARM = 0x01c4,
             ARM64 = 0xaa64,
             LoongArch64 = 0x6264,
+            RiscV64 = 0x5064,
         }
 
         internal const string JitLibrary = "clrjitilc";
@@ -411,9 +412,10 @@ namespace Internal.JitInterface
 
             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
@@ -3861,6 +3863,19 @@ namespace Internal.JitInterface
                             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;
             }
@@ -3962,6 +3977,8 @@ namespace Internal.JitInterface
                     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");
             }
@@ -4048,6 +4065,9 @@ namespace Internal.JitInterface
                 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
index 47e7e6b..13906f8 100644 (file)
@@ -1222,6 +1222,7 @@ namespace Internal.JitInterface
     //   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,
index 2605d54..eb7b71e 100644 (file)
@@ -138,6 +138,7 @@ namespace Internal.JitInterface
                 TargetArchitecture.ARM => "arm",
                 TargetArchitecture.ARM64 => "arm64",
                 TargetArchitecture.LoongArch64 => "loongarch64",
+                TargetArchitecture.RiscV64 => "riscv64",
                 _ => throw new NotImplementedException(target.Architecture.ToString())
             };
 
index 19ffe40..fae694f 100644 (file)
 // 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;
         }
     }
 }
index bfb88cf..5aa1265 100644 (file)
@@ -5,6 +5,7 @@ using System.Collections.Generic;
 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;
 
@@ -207,7 +208,10 @@ namespace Internal.JitInterface
 
             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
@@ -375,8 +379,12 @@ namespace Internal.JitInterface
                 // 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;
@@ -403,6 +411,8 @@ namespace Internal.JitInterface
                         // 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
                     {
@@ -455,13 +465,16 @@ namespace Internal.JitInterface
                         }
                     }
 
-                    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;
index 928e301..2d188fb 100644 (file)
@@ -15,5 +15,6 @@ namespace Internal.TypeSystem
         X86,
         Wasm32,
         LoongArch64,
+        RiscV64,
     }
 }
index 38eb48b..8dcc727 100644 (file)
@@ -86,6 +86,7 @@ namespace Internal.TypeSystem
                     case TargetArchitecture.ARM64:
                     case TargetArchitecture.X64:
                     case TargetArchitecture.LoongArch64:
+                    case TargetArchitecture.RiscV64:
                         return 8;
                     case TargetArchitecture.ARM:
                     case TargetArchitecture.X86:
@@ -126,6 +127,10 @@ namespace Internal.TypeSystem
                 {
                     return 16;
                 }
+                else if (Architecture == TargetArchitecture.RiscV64)
+                {
+                    return 16;
+                }
 
                 // 512-bit vector is the type with the highest alignment we support
                 return 64;
@@ -183,6 +188,7 @@ namespace Internal.TypeSystem
                         return 2;
                     case TargetArchitecture.ARM64:
                     case TargetArchitecture.LoongArch64:
+                    case TargetArchitecture.RiscV64:
                         return 4;
                     default:
                         return 1;
@@ -288,6 +294,7 @@ namespace Internal.TypeSystem
                 case TargetArchitecture.X64:
                 case TargetArchitecture.ARM64:
                 case TargetArchitecture.LoongArch64:
+                case TargetArchitecture.RiscV64:
                     return new LayoutInt(8);
                 case TargetArchitecture.X86:
                     return new LayoutInt(4);
@@ -337,6 +344,7 @@ namespace Internal.TypeSystem
                 Debug.Assert(Architecture == TargetArchitecture.ARM ||
                     Architecture == TargetArchitecture.ARM64 ||
                     Architecture == TargetArchitecture.LoongArch64 ||
+                    Architecture == TargetArchitecture.RiscV64 ||
                     Architecture == TargetArchitecture.X64 ||
                     Architecture == TargetArchitecture.X86);
 
index 13c7335..7b7c6ba 100644 (file)
@@ -9,6 +9,7 @@ using ILCompiler.DependencyAnalysis.X64;
 using ILCompiler.DependencyAnalysis.X86;
 using ILCompiler.DependencyAnalysis.ARM64;
 using ILCompiler.DependencyAnalysis.LoongArch64;
+using ILCompiler.DependencyAnalysis.RiscV64;
 
 namespace ILCompiler.DependencyAnalysis
 {
@@ -56,6 +57,10 @@ 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;
     }
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64JumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64JumpStubNode.cs
new file mode 100644 (file)
index 0000000..784c856
--- /dev/null
@@ -0,0 +1,15 @@
+// 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);
+        }
+    }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs
new file mode 100644 (file)
index 0000000..a382331
--- /dev/null
@@ -0,0 +1,224 @@
+// 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);
+        }
+    }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs
new file mode 100644 (file)
index 0000000..e4288ac
--- /dev/null
@@ -0,0 +1,199 @@
+// 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();
+            }
+        }
+    }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64TentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64TentativeMethodNode.cs
new file mode 100644 (file)
index 0000000..1521b85
--- /dev/null
@@ -0,0 +1,15 @@
+ï»ż// 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));
+        }
+    }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64UnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64UnboxingStubNode.cs
new file mode 100644 (file)
index 0000000..1852a09
--- /dev/null
@@ -0,0 +1,17 @@
+// 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));
+        }
+    }
+}
index 67b81c5..cd09e16 100644 (file)
     <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" />
index 508c33c..9856c38 100644 (file)
@@ -6,7 +6,7 @@
     <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>
 
index 6f1d311..98941b3 100644 (file)
@@ -135,6 +135,7 @@ namespace ILCompiler.Diagnostics
                 TargetArchitecture.ARM64 => PerfMapArchitectureToken.ARM64,
                 TargetArchitecture.X64 => PerfMapArchitectureToken.X64,
                 TargetArchitecture.X86 => PerfMapArchitectureToken.X86,
+                TargetArchitecture.RiscV64 => PerfMapArchitectureToken.RiscV64,
                 _ => throw new NotImplementedException(details.Architecture.ToString())
             };
 
index fea26f9..395b26f 100644 (file)
@@ -19,6 +19,7 @@ public enum PerfMapArchitectureToken : uint
     ARM64 = 2,
     X64 = 3,
     X86 = 4,
+    RiscV64 = 5,
 }
 
 public enum PerfMapOSToken : uint
index 78dfc95..16736ae 100644 (file)
@@ -624,6 +624,13 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                             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();
                 }
@@ -827,6 +834,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         _loongarch64IdxFPReg = 0;
                         break;
 
+                    case TargetArchitecture.RiscV64:
+                        _riscv64IdxGenReg = numRegistersUsed;
+                        _riscv64OfsStack = 0;
+
+                        _riscv64IdxFPReg = 0;
+                        break;
                     default:
                         throw new NotImplementedException();
                 }
@@ -1377,7 +1390,16 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                                     _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;
@@ -1434,6 +1456,132 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         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();
             }
@@ -1766,6 +1914,56 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         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)
                     {
@@ -1855,6 +2053,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         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;
index ccb91bb..095387b 100644 (file)
@@ -197,7 +197,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         // 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;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs
new file mode 100644 (file)
index 0000000..9db82c3
--- /dev/null
@@ -0,0 +1,68 @@
+// 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);
+        }
+    }
+}
index 09372f1..7f2b1fd 100644 (file)
@@ -46,6 +46,9 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                 case TargetArchitecture.LoongArch64:
                     return LoongArch64TransitionBlock.Instance;
 
+                case TargetArchitecture.RiscV64:
+                    return RiscV64TransitionBlock.Instance;
+
                 default:
                     throw new NotImplementedException(target.Architecture.ToString());
             }
@@ -64,6 +67,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         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.
@@ -386,7 +390,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                             {
                                 if (IsLoongArch64)
                                     fpReturnSize = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff;
+                                else if (IsRiscV64)
+                                    fpReturnSize = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff;
                                 break;
+
                             }
 
                         }
@@ -683,5 +690,43 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                 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);
+            }
+            
+        }
     }
 }
index de15cbc..4cf6d65 100644 (file)
@@ -121,6 +121,11 @@ namespace ILCompiler
             /// </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;
@@ -423,6 +428,10 @@ namespace ILCompiler
                             nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64;
                             break;
 
+                        case TargetArchitecture.RiscV64:
+                            nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobRISCV64;
+                            break;
+
                         default:
                             throw new NotImplementedException();
                     }
index 7631bd9..9db169f 100644 (file)
     <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" />
index d0d9947..e682145 100644 (file)
@@ -228,6 +228,13 @@ namespace ILCompiler.PEWriter
                         break;
                     }
 
+                case RelocType.IMAGE_REL_BASED_RISCV64_PC:
+                    {
+                        relocationLength = 8;
+                        delta = targetRVA - sourceRVA;
+                        break;
+                    }
+
                 default:
                     throw new NotSupportedException();
             }
@@ -244,7 +251,8 @@ namespace ILCompiler.PEWriter
                         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();
index 7689691..e10348e 100644 (file)
@@ -317,6 +317,10 @@ namespace ILCompiler.PEWriter
                     _codePadding = 0x002A0005u;
                     break;
 
+                case TargetArchitecture.RiscV64:
+                    _codePadding = 0x00100073u;
+                    break;
+
                 default:
                     throw new NotImplementedException();
             }
index 8b58919..c48c705 100644 (file)
@@ -90,6 +90,9 @@ namespace ILCompiler.PEWriter
                 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());
             }
index cd57833..e0634c8 100644 (file)
@@ -267,6 +267,14 @@ namespace ILCompiler.Reflection.ReadyToRun.Amd64
                 }
                 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)
index 8355a1a..2f3e89f 100644 (file)
@@ -71,6 +71,9 @@ namespace ILCompiler.Reflection.ReadyToRun.Amd64
                     case Machine.LoongArch64:
                         return ((LoongArch64.Registers)registerNumber).ToString();
 
+                    case (Machine)0x5064:
+                        return ((RiscV64.Registers)registerNumber).ToString();
+
                     default:
                         throw new NotImplementedException(machine.ToString());
                 }
index ecea8cc..a0b3683 100644 (file)
@@ -69,6 +69,10 @@ namespace ILCompiler.Reflection.ReadyToRun.Amd64
                         regType = typeof(LoongArch64.Registers);
                         break;
 
+                    case (Machine)0x5064:
+                        regType = typeof(RiscV64.Registers);
+                        break;
+
                     default:
                         throw new NotImplementedException();
                 }
index 0311019..304a9e9 100644 (file)
@@ -74,6 +74,8 @@ namespace ILCompiler.Reflection.ReadyToRun
                     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}.");
             }
index 9356bce..b6117dd 100644 (file)
@@ -154,6 +154,11 @@ namespace ILCompiler.Reflection.ReadyToRun
                     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;
             }
         }
 
@@ -165,6 +170,7 @@ namespace ILCompiler.Reflection.ReadyToRun
                     return (x << 1);
                 case Machine.Arm64:
                 case Machine.LoongArch64:
+                case (Machine)0x5064: /* TODO: update with RiscV64 */
                     return (x << 2);
             }
             return x;
@@ -180,6 +186,7 @@ namespace ILCompiler.Reflection.ReadyToRun
                     return (x << 2);
                 case Machine.Arm64:
                 case Machine.LoongArch64:
+                case (Machine)0x5064: /* TODO: update with RiscV64 */
                     return (x << 3);
             }
             return x;
@@ -197,6 +204,8 @@ namespace ILCompiler.Reflection.ReadyToRun
                     return (x ^ 29);
                 case Machine.LoongArch64:
                     return ((x ^ 22) & 0x3);
+                case (Machine)0x5064: /* TODO: update with RiscV64 */
+                    return (x ^ 8);
             }
             return x;
         }
@@ -211,6 +220,7 @@ namespace ILCompiler.Reflection.ReadyToRun
                     return (x << 2);
                 case Machine.Arm64:
                 case Machine.LoongArch64:
+                case (Machine)0x5064: /* TODO: update with RiscV64 */
                     return (x << 3);
             }
             return x;
index dee60ea..c365ea5 100644 (file)
@@ -229,6 +229,10 @@ namespace ILCompiler.Reflection.ReadyToRun
             {
                 return (int)loongarch64Info.FunctionLength;
             }
+            else if (UnwindInfo is RiscV64.UnwindInfo riscv64Info)
+            {
+                return (int)riscv64Info.FunctionLength;
+            }
             else if (Method.GcInfo != null)
             {
                 return Method.GcInfo.CodeLength;
@@ -492,7 +496,7 @@ namespace ILCompiler.Reflection.ReadyToRun
                     }
                     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);
                     }
                 }
@@ -612,6 +616,10 @@ namespace ILCompiler.Reflection.ReadyToRun
                 {
                     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)
                 {
index c46bcc6..927c975 100644 (file)
@@ -633,6 +633,7 @@ namespace ILCompiler.Reflection.ReadyToRun
                 case Machine.Amd64:
                 case Machine.Arm64:
                 case Machine.LoongArch64:
+                case (Machine)0x5064: /* TODO: update with RiscV64 */
                     _pointerSize = 8;
                     break;
 
@@ -1416,6 +1417,7 @@ namespace ILCompiler.Reflection.ReadyToRun
                         case Machine.Amd64:
                         case Machine.Arm64:
                         case Machine.LoongArch64:
+                        case (Machine)0x5064: /* TODO: update with RiscV64 */
                             entrySize = 8;
                             break;
 
diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/RiscV64/Registers.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/RiscV64/Registers.cs
new file mode 100644 (file)
index 0000000..d88ca03
--- /dev/null
@@ -0,0 +1,41 @@
+// 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,
+    }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/RiscV64/UnwindInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/RiscV64/UnwindInfo.cs
new file mode 100644 (file)
index 0000000..34411ef
--- /dev/null
@@ -0,0 +1,161 @@
+// 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));
+        }
+    }
+}
index dcd1f53..4687f63 100644 (file)
@@ -34,6 +34,9 @@ namespace ILCompiler.Reflection.ReadyToRun
                 case Machine.LoongArch64:
                     return LoongArch64TransitionBlock.Instance;
 
+                case (Machine)0x5064: /* TODO: update with RiscV64 */
+                    return RiscV64TransitionBlock.Instance;
+
                 default:
                     throw new NotImplementedException();
             }
@@ -169,5 +172,21 @@ namespace ILCompiler.Reflection.ReadyToRun
             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;
+        }
+        
     }
 }
index 1470888..57450c0 100644 (file)
@@ -313,7 +313,7 @@ namespace ILCompiler
                 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());
index 4e97b67..5752221 100644 (file)
@@ -294,7 +294,7 @@ namespace ILCompiler
                 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()));
index 0f2f954..b44b607 100644 (file)
@@ -5,7 +5,7 @@
     <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>
index eda9433..2e44e36 100644 (file)
@@ -22,16 +22,14 @@ namespace R2RDump
             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)]
@@ -77,6 +75,9 @@ namespace R2RDump
                 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;
@@ -191,6 +192,10 @@ namespace R2RDump
                     // 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()
                 };
 
@@ -228,6 +233,11 @@ namespace R2RDump
             }
 
             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:
             //
@@ -260,7 +270,8 @@ namespace R2RDump
                     }
                     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.
@@ -348,6 +359,10 @@ namespace R2RDump
                     case Machine.ArmThumb2:
                         break;
 
+                    case (Machine)0x5064:
+                        ProbeRiscV64Quirks(rtf, imageOffset, rtfOffset, ref fixedTranslatedLine);
+                        break;
+
                     default:
                         break;
                 }
@@ -1199,6 +1214,185 @@ namespace R2RDump
         }
 
         /// <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');
index 2b299ea..0fd30e3 100644 (file)
@@ -210,6 +210,7 @@ namespace R2RDump
                     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
index f0a60be..103d816 100644 (file)
@@ -4,7 +4,7 @@
     <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>
index 71bc256..e84baf1 100644 (file)
@@ -56,8 +56,9 @@ NESTED_ENTRY ClrRestoreNonvolatileContextWorker, _TEXT
         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
index 57df032..15f3e12 100644 (file)
@@ -173,6 +173,26 @@ public:
             _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()
index 0d7e5e8..7b32074 100644 (file)
@@ -485,10 +485,15 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
                             *((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:
index 0f503c3..66440f8 100644 (file)
@@ -308,6 +308,11 @@ struct TransitionBlock
         {
             return argLocDescForStructInRegs->m_cFloatReg > 0;
         }
+    #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
+        if (argLocDescForStructInRegs != NULL)
+        {
+            return argLocDescForStructInRegs->m_cFloatReg > 0;
+        }
     #endif
         return offset < 0;
     }
@@ -1719,16 +1724,24 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
 
             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;
@@ -1836,7 +1849,9 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
                 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;
index eb8462e..d99a227 100644 (file)
@@ -139,7 +139,9 @@ void InvokeUtil::CopyArg(TypeHandle th, PVOID argRef, ArgDestination *argDest) {
 
     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);
@@ -164,18 +166,13 @@ void InvokeUtil::CopyArg(TypeHandle th, PVOID argRef, ArgDestination *argDest) {
 
     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
index 2afa6bf..0d056d5 100644 (file)
@@ -157,6 +157,27 @@ LPVOID ProfileArgIterator::GetNextArgAddr()
     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
     {
index d0ab7f8..138b05e 100644 (file)
@@ -810,6 +810,8 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
     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;
index 7014bc0..ff8c7aa 100644 (file)
@@ -2319,11 +2319,14 @@ bool MethodTable::ClassifyEightBytesWithManagedLayout(SystemVStructRegisterPassi
 
     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
@@ -2539,7 +2542,12 @@ bool MethodTable::ClassifyEightBytesWithNativeLayout(SystemVStructRegisterPassin
     // 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);
@@ -2791,8 +2799,12 @@ void  MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe
         // 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;
@@ -2819,6 +2831,8 @@ void  MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe
                 // 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
             {
@@ -2871,13 +2885,16 @@ void  MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe
                 }
             }
 
-            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;
@@ -2910,9 +2927,9 @@ void  MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe
         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
     }
index 19fb205..8ef532c 100644 (file)
@@ -234,7 +234,7 @@ inline TADDR GetMem(PCODE address, SIZE_T size, bool signExtend)
     }
     EX_CATCH
     {
-        mem = NULL;
+        mem = (TADDR)NULL;
         _ASSERTE(!"Memory read within jitted Code Failed, this should not happen!!!!");
     }
     EX_END_CATCH(SwallowAllExceptions);
index f9f72e0..4cc4454 100644 (file)
@@ -202,7 +202,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState,
     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;
@@ -226,7 +226,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState,
     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
 
@@ -390,7 +390,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
         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];
@@ -434,7 +434,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
     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];
@@ -450,7 +450,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
     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)
@@ -730,6 +730,8 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
     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;
index 2f39e4f..f77147d 100644 (file)
@@ -193,7 +193,7 @@ struct ResolveHolder
         ;;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
@@ -263,14 +263,14 @@ struct ResolveHolder
         _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;
 
@@ -289,7 +289,7 @@ struct ResolveHolder
 
         //     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);
@@ -347,7 +347,7 @@ struct ResolveHolder
         _stub._resolveWorkerTarget = resolveWorkerTarget;
 
         _ASSERTE(resolveWorkerTarget == (PCODE)ResolveWorkerChainLookupAsmStub);
-        _ASSERTE(patcherTarget == NULL);
+        _ASSERTE(patcherTarget == (PCODE)NULL);
 
 #undef DATA_OFFSET
 #undef PC_REL_OFFSET
index d6258ac..1e246b2 100644 (file)
@@ -1438,7 +1438,7 @@ static BOOL TraceManagedThunk(
 
 #else
     PORTABILITY_ASSERT("TraceManagedThunk");
-    destAddr = NULL;
+    destAddr = (PCODE)NULL;
 #endif
 
     LOG((LF_CORDB,LL_INFO10000, "TraceManagedThunk: ppbDest: %p\n", destAddr));
index 6064939..1db0edd 100644 (file)
@@ -798,7 +798,7 @@ public:
         return pContext->Lr;
 #else
         PORTABILITY_ASSERT("StubManagerHelpers::GetReturnAddress");
-        return NULL;
+        return (TADDR)NULL;
 #endif
     }
 
@@ -834,7 +834,7 @@ public:
         return pContext->X12;
 #else
         PORTABILITY_ASSERT("StubManagerHelpers::GetTailCallTarget");
-        return NULL;
+        return (TADDR)NULL;
 #endif
     }
 
@@ -850,7 +850,7 @@ public:
         return pContext->X12;
 #else
         PORTABILITY_ASSERT("StubManagerHelpers::GetHiddenArg");
-        return NULL;
+        return (TADDR)NULL;
 #endif
     }
 
@@ -880,7 +880,7 @@ public:
         return *((PCODE *)pContext->Fp + 1);
 #else
         PORTABILITY_ASSERT("StubManagerHelpers::GetRetAddrFromMulticastILStubFrame");
-        return NULL;
+        return (TADDR)NULL;
 #endif
     }
 
@@ -900,7 +900,7 @@ public:
         return pContext->X1;
 #else
         PORTABILITY_ASSERT("StubManagerHelpers::GetSecondArg");
-        return NULL;
+        return (TADDR)NULL;
 #endif
     }
 
index 03c9568..3550275 100644 (file)
@@ -374,6 +374,46 @@ SIZE_T GetRegOffsInCONTEXT(ICorDebugInfo::RegNum regNum)
     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;
index 4c73b3d..0a0234d 100644 (file)
     <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>
index e8e8591..752890b 100644 (file)
@@ -13,6 +13,9 @@
     <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>
index 6907e82..acd9792 100644 (file)
@@ -11,8 +11,6 @@
     <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>
index 9cbf1ee..a55e2ca 100644 (file)
@@ -2,11 +2,13 @@
 // 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;
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReceiveSocketError.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReceiveSocketError.cs
new file mode 100644 (file)
index 0000000..be4888b
--- /dev/null
@@ -0,0 +1,15 @@
+// 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);
+    }
+}
index 48b8ba8..985b453 100644 (file)
@@ -1,6 +1,7 @@
 // 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;
 
@@ -30,6 +31,8 @@ namespace System
 
         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;
index 3211c14..02760d2 100644 (file)
@@ -42,6 +42,8 @@ public partial class CancelKeyPressTests
         HandlerInvokedForSignal(SIGQUIT, redirectStandardInput);
     }
 
+    private static readonly int WaitFailTestTimeoutSeconds = 30 * PlatformDetection.SlowRuntimeTimeoutModifier;
+
     [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
     public void ExitDetectionNotBlockedByHandler()
     {
@@ -79,7 +81,10 @@ public partial class CancelKeyPressTests
             // 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)
index d638596..b2deeaf 100644 (file)
@@ -10,8 +10,6 @@ using Xunit;
 
 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()
index 7b64250..9f70af5 100644 (file)
@@ -563,6 +563,7 @@ namespace System.Diagnostics.Tests
         }
 
         [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/105686", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
         public void TestMaxWorkingSet()
         {
             CreateDefaultProcess();
@@ -618,6 +619,7 @@ namespace System.Diagnostics.Tests
         }
 
         [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/105686", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
         public void TestMinWorkingSet()
         {
             CreateDefaultProcess();
index b96a6f1..231a785 100644 (file)
              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" />
index d535734..7c2dd58 100644 (file)
@@ -98,10 +98,25 @@ namespace System.Net.NetworkInformation
 #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
 
@@ -232,7 +247,7 @@ namespace System.Net.NetworkInformation
             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))
@@ -270,12 +285,40 @@ namespace System.Net.NetworkInformation
                 {
                     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);
@@ -330,6 +373,12 @@ namespace System.Net.NetworkInformation
             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);
index f0f7ed6..5f49071 100644 (file)
@@ -6,7 +6,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
-
+using Microsoft.DotNet.XUnitExtensions;
 using Xunit;
 
 namespace System.Net.Sockets.Tests
@@ -792,7 +792,7 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [Theory]
+        [ConditionalTheory]
         [PlatformSpecific(TestPlatforms.AnyUnix)]  // API throws PNSE on Unix
         [InlineData(0)]
         [InlineData(1)]
@@ -800,6 +800,11 @@ namespace System.Net.Sockets.Tests
         {
             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:
@@ -823,7 +828,7 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [Theory]
+        [ConditionalTheory]
         [PlatformSpecific(TestPlatforms.AnyUnix)]  // API throws PNSE on Unix
         [InlineData(0)]
         [InlineData(1)]
@@ -833,6 +838,11 @@ namespace System.Net.Sockets.Tests
 
             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:
index c7d119d..572bd77 100644 (file)
@@ -2,6 +2,7 @@
 // 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
@@ -122,6 +123,7 @@ 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))
@@ -138,12 +140,17 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [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)
index 2d87c58..d5f40c3 100644 (file)
@@ -51,6 +51,7 @@ namespace System.Net.Sockets.Tests
         }
 
         [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/104547", typeof(PlatformDetection), nameof(PlatformDetection.IsQemuLinux))]
         public void MulticastOption_CreateSocketSetGetOption_GroupAndInterfaceIndex_SetSucceeds_GetThrows()
         {
             int interfaceIndex = 0;
@@ -65,6 +66,7 @@ namespace System.Net.Sockets.Tests
         }
 
         [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"
@@ -123,6 +125,7 @@ namespace System.Net.Sockets.Tests
         [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)
index bd7f249..b32a926 100644 (file)
@@ -105,7 +105,7 @@ namespace System.Reflection.PortableExecutable
             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 +
index 42837a9..4108cfd 100644 (file)
@@ -98,6 +98,7 @@
 #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
index 8491ab8..b23c88d 100644 (file)
@@ -159,6 +159,7 @@ static const Entry s_sysNative[] =
     DllImportEntry(SystemNative_SetSendTimeout)
     DllImportEntry(SystemNative_Receive)
     DllImportEntry(SystemNative_ReceiveMessage)
+    DllImportEntry(SystemNative_ReceiveSocketError)
     DllImportEntry(SystemNative_Send)
     DllImportEntry(SystemNative_SendMessage)
     DllImportEntry(SystemNative_Accept)
index 8dfc133..4d3e94e 100644 (file)
 #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
@@ -1325,7 +1330,11 @@ int32_t SystemNative_SetSendTimeout(intptr_t socket, int32_t millisecondsTimeout
 
 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)
     {
@@ -1335,9 +1344,15 @@ static int8_t ConvertSocketFlagsPalToPlatform(int32_t palFlags, int* platformFla
     *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;
 }
 
@@ -1381,6 +1396,60 @@ int32_t SystemNative_Receive(intptr_t socket, void* buffer, int32_t bufferLen, i
     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 ||
index 0a46f14..5dfe1c1 100644 (file)
@@ -206,6 +206,8 @@ typedef enum
     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;
 
 /*
@@ -356,6 +358,8 @@ PALEXPORT int32_t SystemNative_Receive(intptr_t socket, void* buffer, int32_t bu
 
 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);
index a1e151d..8190ff6 100644 (file)
@@ -493,6 +493,10 @@ check_include_files(
      "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
index ced9bec..f51cf45 100644 (file)
@@ -230,6 +230,9 @@ namespace Microsoft.NET.Build.Tasks
                 case "x86":
                     architecture = Architecture.X86;
                     break;
+                case "riscv64":
+                    architecture = (Architecture)9;
+                    break;
                 default:
                     return false;
             }
@@ -387,6 +390,7 @@ namespace Microsoft.NET.Build.Tasks
                 Architecture.X64 => "x64",
                 Architecture.Arm => "arm",
                 Architecture.Arm64 => "arm64",
+                (Architecture)9 => "riscv64",
                 _ => null
             };
         }
diff --git a/src/tests/JIT/Directed/PrimitiveABI/CMakeLists.txt b/src/tests/JIT/Directed/PrimitiveABI/CMakeLists.txt
new file mode 100644 (file)
index 0000000..20cb799
--- /dev/null
@@ -0,0 +1,12 @@
+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)
diff --git a/src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.c b/src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.c
new file mode 100644 (file)
index 0000000..0bfc2a3
--- /dev/null
@@ -0,0 +1,41 @@
+// 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;
+}
diff --git a/src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.cs b/src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.cs
new file mode 100644 (file)
index 0000000..8bcc349
--- /dev/null
@@ -0,0 +1,182 @@
+// 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
diff --git a/src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.csproj b/src/tests/JIT/Directed/PrimitiveABI/PrimitiveABI.csproj
new file mode 100644 (file)
index 0000000..a38b585
--- /dev/null
@@ -0,0 +1,16 @@
+<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>
index 1c7c868..cf028a3 100644 (file)
@@ -2,13 +2,15 @@ project (StructABILib)
 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)
diff --git a/src/tests/JIT/Directed/StructABI/EmptyStructs.cpp b/src/tests/JIT/Directed/StructABI/EmptyStructs.cpp
new file mode 100644 (file)
index 0000000..8e21d26
--- /dev/null
@@ -0,0 +1,68 @@
+// 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;
+}
+
diff --git a/src/tests/JIT/Directed/StructABI/EmptyStructs.cs b/src/tests/JIT/Directed/StructABI/EmptyStructs.cs
new file mode 100644 (file)
index 0000000..d6b3334
--- /dev/null
@@ -0,0 +1,150 @@
+// 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
diff --git a/src/tests/JIT/Directed/StructABI/EmptyStructs.csproj b/src/tests/JIT/Directed/StructABI/EmptyStructs.csproj
new file mode 100644 (file)
index 0000000..372d011
--- /dev/null
@@ -0,0 +1,17 @@
+<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>
index 513195a..ed2f2c6 100644 (file)
@@ -615,7 +615,7 @@ void SlowPathELTProfiler::PrintBytes(const BYTE *bytes, size_t length)
 
 bool SlowPathELTProfiler::ValidateInt(UINT_PTR ptr, int expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -625,7 +625,7 @@ bool SlowPathELTProfiler::ValidateInt(UINT_PTR ptr, int expected)
 
 bool SlowPathELTProfiler::ValidateFloat(UINT_PTR ptr, float expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -635,7 +635,7 @@ bool SlowPathELTProfiler::ValidateFloat(UINT_PTR ptr, float expected)
 
 bool SlowPathELTProfiler::ValidateDouble(UINT_PTR ptr, double expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -645,7 +645,7 @@ bool SlowPathELTProfiler::ValidateDouble(UINT_PTR ptr, double expected)
 
 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;
     }
@@ -673,7 +673,7 @@ bool SlowPathELTProfiler::ValidateString(UINT_PTR ptr, const WCHAR *expected)
 
 bool SlowPathELTProfiler::ValidateMixedStruct(UINT_PTR ptr, MixedStruct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -684,7 +684,7 @@ bool SlowPathELTProfiler::ValidateMixedStruct(UINT_PTR ptr, MixedStruct expected
 
 bool SlowPathELTProfiler::ValidateLargeStruct(UINT_PTR ptr, LargeStruct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -702,7 +702,7 @@ bool SlowPathELTProfiler::ValidateLargeStruct(UINT_PTR ptr, LargeStruct expected
 
 bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x2Struct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -713,7 +713,7 @@ bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x2Struct
 
 bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x3Struct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -724,7 +724,7 @@ bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x3Struct
 
 bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x4Struct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -738,7 +738,7 @@ bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp32x4Struct
 
 bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x2Struct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -749,7 +749,7 @@ bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x2Struct
 
 bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x3Struct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -760,7 +760,7 @@ bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x3Struct
 
 bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x4Struct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -774,7 +774,7 @@ bool SlowPathELTProfiler::ValidateFloatingPointStruct(UINT_PTR ptr, Fp64x4Struct
 
 bool SlowPathELTProfiler::ValidateIntegerStruct(UINT_PTR ptr, IntegerStruct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -785,7 +785,7 @@ bool SlowPathELTProfiler::ValidateIntegerStruct(UINT_PTR ptr, IntegerStruct expe
 
 bool SlowPathELTProfiler::ValidateIntegerSseStruct(UINT_PTR ptr, IntegerSseStruct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -798,7 +798,7 @@ bool SlowPathELTProfiler::ValidateIntegerSseStruct(UINT_PTR ptr, IntegerSseStruc
 
 bool SlowPathELTProfiler::ValidateSseIntegerStruct(UINT_PTR ptr, SseIntegerStruct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -811,7 +811,7 @@ bool SlowPathELTProfiler::ValidateSseIntegerStruct(UINT_PTR ptr, SseIntegerStruc
 
 bool SlowPathELTProfiler::ValidateMixedSseStruct(UINT_PTR ptr, MixedSseStruct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -825,7 +825,7 @@ bool SlowPathELTProfiler::ValidateMixedSseStruct(UINT_PTR ptr, MixedSseStruct ex
 
 bool SlowPathELTProfiler::ValidateSseMixedStruct(UINT_PTR ptr, SseMixedStruct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
@@ -839,7 +839,7 @@ bool SlowPathELTProfiler::ValidateSseMixedStruct(UINT_PTR ptr, SseMixedStruct ex
 
 bool SlowPathELTProfiler::ValidateMixedMixedStruct(UINT_PTR ptr, MixedMixedStruct expected)
 {
-    if (ptr == NULL)
+    if (ptr == (UINT_PTR)NULL)
     {
         return false;
     }
index ac94f78..45d810b 100644 (file)
@@ -115,7 +115,7 @@ HRESULT GCProfiler::ObjectReferences(ObjectID objectId, ClassID classId, ULONG c
     for (ULONG i = 0; i < cObjectRefs; ++i)
     {
         ObjectID obj = objectRefIds[i];
-        if (obj != NULL)
+        if (obj != 0)
         {
             _objectReferencesSeen.insert(obj);
         }
@@ -131,7 +131,7 @@ HRESULT GCProfiler::RootReferences(ULONG cRootRefs, ObjectID rootRefIds[])
     for (ULONG i = 0; i < cRootRefs; ++i)
     {
         ObjectID obj = rootRefIds[i];
-        if (obj != NULL)
+        if (obj != 0)
         {
             _rootReferencesSeen.insert(obj);
         }
index c7c00ad..94ba4ae 100644 (file)
@@ -255,8 +255,8 @@ HRESULT GetAppDomainStaticAddress::ClassLoadFinished(ClassID classId, HRESULT hr
 
     HRESULT hr = S_OK;
 
-    ThreadID threadId = NULL;
-    AppDomainID appDomainId = NULL;
+    ThreadID threadId = 0;
+    AppDomainID appDomainId = 0;
     CorElementType baseElemType;
     ClassID        baseClassId;
     ULONG          cRank;
@@ -298,7 +298,7 @@ HRESULT GetAppDomainStaticAddress::ClassLoadFinished(ClassID classId, HRESULT hr
                                           &modId,
                                           NULL,
                                           NULL,
-                                          NULL,
+                                          0,
                                           NULL,
                                           NULL);
     if (FAILED(hr))
@@ -390,12 +390,12 @@ HRESULT GetAppDomainStaticAddress::GarbageCollectionFinished()
             fflush(stdout);
         }
 
-        ModuleID classModuleId = NULL;
+        ModuleID classModuleId = 0;
         hr = pCorProfilerInfo->GetClassIDInfo2(classId,
                                     &classModuleId,
                                     NULL,
                                     NULL,
-                                    NULL,
+                                    0,
                                     NULL,
                                     NULL);
         if (FAILED(hr))
@@ -424,9 +424,9 @@ HRESULT GetAppDomainStaticAddress::GarbageCollectionFinished()
         }
 
         HCORENUM hEnum = NULL;
-        mdTypeDef token = NULL;
+        mdTypeDef token = 0;
         mdFieldDef fieldTokens[SHORT_LENGTH];
-        ULONG cTokens = NULL;
+        ULONG cTokens = 0;
 
         if (DEBUG_OUT)
         {
@@ -439,7 +439,7 @@ HRESULT GetAppDomainStaticAddress::GarbageCollectionFinished()
                                             NULL,
                                             &token,
                                             NULL,
-                                            NULL,
+                                            0,
                                             NULL,
                                             NULL);
         if (hr == CORPROF_E_DATAINCOMPLETE)
@@ -469,13 +469,13 @@ HRESULT GetAppDomainStaticAddress::GarbageCollectionFinished()
 
         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,
index 78ad8e4..43c55ef 100644 (file)
@@ -139,7 +139,7 @@ ObjectID HandlesProfiler::CheckIfAlive(const char* name, ObjectHandleID handle,
     {
         _failures++;
         printf("HandlesProfiler::CheckIfAlive(%s): FAIL: null handle.\n", name);
-        return NULL;
+        return 0;
     }
 
     ObjectID objectId{0};
@@ -148,12 +148,12 @@ ObjectID HandlesProfiler::CheckIfAlive(const char* name, ObjectHandleID handle,
     {
         _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);
@@ -179,7 +179,7 @@ ObjectID HandlesProfiler::CheckIfAlive(const char* name, ObjectHandleID handle,
     }
     else
     {
-        if (objectId != NULL)
+        if (objectId != 0)
         {
             _failures++;
             printf("HandlesProfiler::CheckIfAlive(%s): FAIL: the object should not be alive anymore.\n", name);
@@ -190,7 +190,7 @@ ObjectID HandlesProfiler::CheckIfAlive(const char* name, ObjectHandleID handle,
         }
     }
 
-    return NULL;
+    return 0;
 }
 
 HRESULT HandlesProfiler::GarbageCollectionFinished()
index 4365352..8c94462 100644 (file)
@@ -15,7 +15,7 @@ public:
         _weakHandle(NULL),
         _strongHandle(NULL),
         _pinnedHandle(NULL),
-        _pinnedObject(NULL)
+        _pinnedObject(0)
     {}
 
        static GUID GetClsid();
index 4eff242..fe30e3a 100644 (file)
@@ -592,12 +592,12 @@ String Profiler::GetFunctionIDName(FunctionID funcId)
 
     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,
@@ -671,7 +671,7 @@ String Profiler::GetClassIDName(ClassID classId)
     ClassID typeArgs[SHORT_LENGTH];
     HRESULT hr = S_OK;
 
-    if (classId == NULL)
+    if (classId == 0)
     {
         printf("FAIL: Null ClassID passed in\n");
         return WCHAR("");
@@ -757,7 +757,7 @@ String Profiler::GetModuleIDName(ModuleID modId)
     ULONG nameLength = 0;
     AssemblyID assemID;
 
-    if (modId == NULL)
+    if (modId == 0)
     {
         printf("FAIL: Null ModuleID\n");
         return WCHAR("NullModuleIDPassedIn");
index ab824f4..b93d3bc 100644 (file)
@@ -194,7 +194,7 @@ bool ReJITProfiler::FunctionSeen(FunctionID functionId)
 
             for (auto &&address : codeStartAddresses)
             {
-                if (address == NULL)
+                if (address == (UINT_PTR)NULL)
                 {
                     printf("Found NULL start address from GetNativeCodeStartAddresses.\n");
                     _failures++;
@@ -477,12 +477,12 @@ FunctionID ReJITProfiler::GetFunctionIDFromToken(ModuleID module, mdMethodDef to
 
 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,
@@ -505,12 +505,12 @@ mdMethodDef ReJITProfiler::GetMethodDefForFunction(FunctionID 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,
index 78aebca..de9f7f7 100644 (file)
@@ -96,7 +96,7 @@ internal class Program
 
         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)
index 5cf6f0e..2fa3c03 100644 (file)
@@ -42,17 +42,6 @@ namespace Mono.Linker.Steps
 
        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 ()
@@ -60,25 +49,6 @@ namespace Mono.Linker.Steps
                        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;
@@ -125,7 +95,7 @@ namespace Mono.Linker.Steps
                                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
                                }
                        }