Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ PTFF_SAVE_R15 equ 00000080h
PTFF_SAVE_ALL_PRESERVED equ 000000F7h ;; NOTE: RBP is not included in this set!
PTFF_SAVE_RSP equ 00008000h
PTFF_SAVE_RAX equ 00000100h ;; RAX is saved in hijack handler - in case it contains a GC ref
PTFF_SAVE_RCX equ 00000200h ;; RCX is saved in hijack handler - in case it contains a GC ref
PTFF_SAVE_ALL_SCRATCH equ 00007F00h
PTFF_THREAD_HIJACK equ 00100000h ;; indicates that this is a frame for a hijacked call

Expand Down
38 changes: 25 additions & 13 deletions src/coreclr/nativeaot/Runtime/amd64/GcProbe.S
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <unixasmmacros.inc>

//
// See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX/RDX and accepts the register
// See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX/RCX/RDX and accepts the register
// bitmask in RCX
//
// On entry:
Expand All @@ -20,8 +20,9 @@
//
.macro PUSH_PROBE_FRAME threadReg, trashReg, BITMASK
push_register rdx // save RDX, it might contain an objectref
push_register rcx // save RCX, it might contain an objectref (async continuation)
push_register rax // save RAX, it might contain an objectref
lea \trashReg, [rsp + 0x18]
lea \trashReg, [rsp + 0x20]
push_register \trashReg // save caller`s RSP
push_nonvol_reg r15 // save preserved registers
push_nonvol_reg r14 // ..
Expand All @@ -31,12 +32,12 @@
push_register \BITMASK // save the register bitmask passed in by caller
push_register \threadReg // Thread * (unused by stackwalker)
push_nonvol_reg rbp // save caller`s RBP
mov \trashReg, [rsp + 11*8] // Find the return address
mov \trashReg, [rsp + 12*8] // Find the return address
push_register \trashReg // save m_RIP
lea \trashReg, [rsp + 0] // trashReg == address of frame

// allocate space for xmm0, xmm1 and alignment
alloc_stack 0x28
alloc_stack 0x20 + 0

// save xmm0 and xmm1 in case they are used as return values
movdqa [rsp + 0x10], xmm0
Expand All @@ -53,7 +54,7 @@
.macro POP_PROBE_FRAME
movdqa xmm1, [rsp + 0]
movdqa xmm0, [rsp + 0x10]
add rsp, 0x28 + 8 // skip xmm0, xmm1 and discard RIP
add rsp, 0x20 + 8 // skip xmm0, xmm1 and discard RIP
pop rbp
pop rax // discard Thread*
pop rax // discard BITMASK
Expand All @@ -64,6 +65,7 @@
pop r15
pop rax // discard caller RSP
pop rax
pop rcx
pop rdx
.endm

Expand All @@ -76,28 +78,38 @@
//
// Register state on exit:
// R11: thread pointer
// RAX, RDX preserved, other volatile regs trashed
// RAX, RCX, RDX preserved, other volatile regs trashed
//
.macro FixupHijackedCallstack
// preserve RAX, RDX as they may contain return values
push rax
push rdx

// preserve RCX as it may contain async continuation return value
push rcx

// align stack
sub rsp, 0x8

// rax = GetThread(), makes nested calls
INLINE_GETTHREAD
mov r11, rax

add rsp, 0x8

pop rcx

pop rdx
pop rax

// Fix the stack by pushing the original return address
mov rcx, [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress]
push rcx
mov r8, [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress]
push r8

// Clear hijack state
xor ecx, ecx
mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx
mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx
xor r8, r8
mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r8
mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], r8
.endm

//
Expand All @@ -112,12 +124,12 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler
ret

LOCAL_LABEL(WaitForGC):
mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RDX + PTFF_THREAD_HIJACK
mov r8d, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RCX + PTFF_SAVE_RDX + PTFF_THREAD_HIJACK
jmp C_FUNC(RhpWaitForGC)
NESTED_END RhpGcProbeHijack, _TEXT

NESTED_ENTRY RhpWaitForGC, _TEXT, NoHandler
PUSH_PROBE_FRAME r11, rax, rcx
PUSH_PROBE_FRAME r11, rax, r8
END_PROLOGUE

mov rbx, r11
Expand Down
39 changes: 21 additions & 18 deletions src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
include AsmMacros.inc

;;
;; See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX and accepts
;; See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX/RCX and accepts
;; the register bitmask
;;
;; On entry:
;; - BITMASK: bitmask describing pushes, a volatile register
;; - RAX: managed function return value, may be an object or byref
;; - RCX: managed function return value (async continuation), may be an object
;; - preserved regs: need to stay preserved, may contain objects or byrefs
;;
;; INVARIANTS
Expand All @@ -18,8 +19,9 @@ include AsmMacros.inc
;;
PUSH_PROBE_FRAME macro threadReg, trashReg, BITMASK

push_vol_reg rcx ; save RCX, it might contain an objectref (async continuation)
push_vol_reg rax ; save RAX, it might contain an objectref
lea trashReg, [rsp + 10h]
lea trashReg, [rsp + 18h]
push_vol_reg trashReg ; save caller's RSP
push_nonvol_reg r15 ; save preserved registers
push_nonvol_reg r14 ; ..
Expand All @@ -31,12 +33,12 @@ PUSH_PROBE_FRAME macro threadReg, trashReg, BITMASK
push_vol_reg BITMASK ; save the register bitmask passed in by caller
push_vol_reg threadReg ; Thread * (unused by stackwalker)
push_nonvol_reg rbp ; save caller's RBP
mov trashReg, [rsp + 12*8] ; Find the return address
mov trashReg, [rsp + 13*8] ; Find the return address
push_vol_reg trashReg ; save m_RIP
lea trashReg, [rsp + 0] ; trashReg == address of frame

;; allocate scratch space and any required alignment
alloc_stack 20h + 10h
alloc_stack 20h + 10h + 8

;; save xmm0 in case it's being used as a return value
movdqa [rsp + 20h], xmm0
Expand All @@ -52,7 +54,7 @@ endm
;;
POP_PROBE_FRAME macro
movdqa xmm0, [rsp + 20h]
add rsp, 20h + 10h + 8 ; deallocate stack and discard saved m_RIP
add rsp, 20h + 10h + 8 + 8 ; deallocate stack and discard saved m_RIP
pop rbp
pop rax ; discard Thread*
pop rax ; discard BITMASK
Expand All @@ -65,6 +67,7 @@ POP_PROBE_FRAME macro
pop r15
pop rax ; discard caller RSP
pop rax
pop rcx
endm

;;
Expand All @@ -76,20 +79,20 @@ endm
;;
;; Register state on exit:
;; RDX: thread pointer
;; RAX: preserved, other volatile regs trashed
;; RAX/RCX: preserved, other volatile regs trashed
;;
FixupHijackedCallstack macro
;; rdx <- GetThread(), TRASHES rcx
INLINE_GETTHREAD rdx, rcx
;; rdx <- GetThread(), TRASHES r8
INLINE_GETTHREAD rdx, r8

;; Fix the stack by pushing the original return address
mov rcx, [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress]
push rcx
mov r8, [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress]
push r8

;; Clear hijack state
xor ecx, ecx
mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx
mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx
xor r8, r8
mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r8
mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], r8
endm

;;
Expand All @@ -103,12 +106,12 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT
jnz @f
ret
@@:
mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_THREAD_HIJACK
mov r8d, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RCX + PTFF_THREAD_HIJACK
jmp RhpWaitForGC
NESTED_END RhpGcProbeHijack, _TEXT

NESTED_ENTRY RhpWaitForGC, _TEXT
PUSH_PROBE_FRAME rdx, rax, rcx
PUSH_PROBE_FRAME rdx, rax, r8
END_PROLOGUE

mov rbx, rdx
Expand Down Expand Up @@ -144,7 +147,7 @@ ifdef FEATURE_GC_STRESS
;;
LEAF_ENTRY RhpGcStressHijack, _TEXT
FixupHijackedCallstack
or ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX
mov r8d, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RCX
jmp RhpGcStressProbe
LEAF_END RhpGcStressHijack, _TEXT

Expand All @@ -158,11 +161,11 @@ LEAF_END RhpGcStressHijack, _TEXT
;; RCX: register bitmask
;;
;; Register state on exit:
;; Scratch registers, except for RAX, have been trashed
;; Scratch registers, except for RAX/RCX, have been trashed
;; All other registers restored as they were when the hijack was first reached.
;;
NESTED_ENTRY RhpGcStressProbe, _TEXT
PUSH_PROBE_FRAME rdx, rax, rcx
PUSH_PROBE_FRAME rdx, rax, r8
END_PROLOGUE

call RhpStressGc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ C_FUNC(\Name):
#define PTFF_SAVE_ALL_PRESERVED 0x000000F1 // NOTE: RBP is not included in this set!
#define PTFF_SAVE_RSP 0x00008000
#define PTFF_SAVE_RAX 0x00000100 // RAX is saved in hijack handler - in case it contains a GC ref
#define PTFF_SAVE_RCX 0x00000200 // RCX is saved in hijack handler - in case it contains a GC ref
#define PTFF_SAVE_RDX 0x00000400 // RDX is saved in hijack handler - in case it contains a GC ref
#define PTFF_SAVE_ALL_SCRATCH 0x00007F00
#define PTFF_THREAD_HIJACK 0x00100000 // indicates that this is a frame for a hijacked call
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Condition="'$(TargetArchitecture)' != 'arm64'">
Copy link
Member

@am11 am11 Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<PropertyGroup Condition="'$(TargetArchitecture)' != 'arm64'">
<!-- disabling some archs: https://github.com/dotnet/runtime/issues/121871#issuecomment-3628963087 -->
<PropertyGroup Condition="'$(TargetArchitecture)' != 'arm64' and '$(TargetArchitecture)' != 'riscv64' and '$(TargetArchitecture)' != 'loongarch64'">

<Features>$(Features);runtime-async=on</Features>
</PropertyGroup>

<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@ public bool TryGetUserMethodForCompilerGeneratedMember(TypeSystemEntity sourceMe
if (sourceMember == null)
return false;

Debug.Assert(sourceMember is not MethodDesc sourceMethod || sourceMethod.IsTypicalMethodDefinition);
if (sourceMember is AsyncMethodVariant asyncVariant)
sourceMember = asyncVariant.Target;

TypeSystemEntity member = sourceMember;
MethodDesc? userMethodCandidate;
while (TryGetOwningMethodForCompilerGeneratedMember(member, out userMethodCandidate))
Expand Down
5 changes: 5 additions & 0 deletions src/libraries/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,9 @@
<CoverageIncludeDirectory Include="shared\Microsoft.NETCore.App\$(ProductVersion)" />
</ItemGroup>

<PropertyGroup Condition="'$(UseNativeAotCoreLib)' == 'true' and '$(TargetArchitecture)' != 'arm64'">
Copy link
Member

@am11 am11 Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<PropertyGroup Condition="'$(UseNativeAotCoreLib)' == 'true' and '$(TargetArchitecture)' != 'arm64'">
<!-- disabling some archs: https://github.com/dotnet/runtime/issues/121871#issuecomment-3628963087 -->
<PropertyGroup Condition="'$(UseNativeAotCoreLib)' == 'true' and '$(TargetArchitecture)' != 'arm64' and '$(TargetArchitecture)' != 'riscv64' and '$(TargetArchitecture)' != 'loongarch64'">

<Features>$(Features);runtime-async=on</Features>
<NoWarn>$(NoWarn);xUnit1013;CS1998;SYSLIB5007</NoWarn>
</PropertyGroup>

</Project>
Loading