From 608d2513991e8c137e9b116c884e6f631f5d3ca7 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 25 Feb 2022 00:09:46 +0100 Subject: [PATCH] WIP: Work on magic nop instructions --- objc2/benches/autorelease.rs | 1 + objc2/src/rc/id.rs | 74 +++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/objc2/benches/autorelease.rs b/objc2/benches/autorelease.rs index ec6b45a7d..3590dd9f9 100644 --- a/objc2/benches/autorelease.rs +++ b/objc2/benches/autorelease.rs @@ -134,6 +134,7 @@ macro_rules! main_with_warmup { )+ } + // Needed to get DYLD to resolve the stubs fn warmup() { $( warmup_fns::$f(); diff --git a/objc2/src/rc/id.rs b/objc2/src/rc/id.rs index 68a43b988..8b40168bc 100644 --- a/objc2/src/rc/id.rs +++ b/objc2/src/rc/id.rs @@ -274,12 +274,82 @@ impl Id { /// TODO #[doc(alias = "objc_retainAutoreleasedReturnValue")] + // This relies heavily on being inlined right after `objc_msgSend`. #[inline(always)] pub unsafe fn retain_autoreleased(ptr: NonNull) -> Id { - // SAFETY: Same as `retain`, `objc_retainAutoreleasedReturnValue` is - // just an optimization. + // Not supported on TARGET_OS_WIN32 + #[cfg(all(apple, not(target_os = "windows")))] + { + // Add magic nop instruction to participate in the fast + // autorelease scheme. + // + // We will unconditionally emit these instructions, even if they + // end up being unused (for example because we're unlucky with + // inlining, some other work is done between the objc_msgSend and + // this, or the runtime version is too old to support it). + // + // See `callerAcceptsOptimizedReturn` in `objc-object.h`: + // https://github.com/apple-oss-distributions/objc4/blob/objc4-838/runtime/objc-object.h#L1209-L1377 + // and this StackOverflow answer for some background on why the + // design is like it is: https://stackoverflow.com/a/23765612. + // + // It may seem like there should be a better way to do this, but + // emitting raw assembly is exactly what Clang and Swift does: + // swiftc: https://github.com/apple/swift/blob/swift-5.5.3-RELEASE/lib/IRGen/GenObjC.cpp#L148-L173 + // Clang: https://github.com/llvm/llvm-project/blob/889317d47b7f046cf0e68746da8f7f264582fb5b/clang/lib/CodeGen/CGObjC.cpp#L2339-L2373 + // + // SAFETY: + // Based on https://doc.rust-lang.org/stable/reference/inline-assembly.html#rules-for-inline-assembly + // + // We don't care about the value of the register (so it's okay to + // be undefined), and its value is preserved. + // + // nomem: No reads or writes to memory are performed (this `mov` + // operates entirely on registers). + // preserves_flags: `mov` doesn't modify any flags. + // nostack: We don't touch the stack. + + // Supported since macOS 10.7. + #[cfg(target_arch = "x86_64")] + {} // x86_64 looks at the next call instruction + + // Supported since macOS 10.8. + #[cfg(target_arch = "arm")] + unsafe { + core::arch::asm!("mov r7, r7", options(nomem, preserves_flags, nostack)) + }; + + // Supported since macOS 10.10. + #[cfg(target_arch = "aarch64")] + unsafe { + core::arch::asm!("mov fp, fp", options(nomem, preserves_flags, nostack)) + }; + + // Supported since macOS 10.12. + #[cfg(target_arch = "x86")] + unsafe { + core::arch::asm!("mov ebp, ebp", options(nomem, preserves_flags, nostack)) + }; + } let ptr = ptr.as_ptr() as *mut objc_sys::objc_object; + + // objc_autoreleaseReturnValue / objc_retainAutoreleasedReturnValue: + + // #![feature(asm_sym)] + // #[cfg(target_arch = "x86_64")] + // unsafe { + // core::arch::asm!( + // "mov rdi, rax", + // "call {}", + // sym objc2::ffi::objc_retainAutoreleasedReturnValue, + // inout("rax") obj, + // clobber_abi("C"), + // ); + // } + + // SAFETY: Same as `retain`, `objc_retainAutoreleasedReturnValue` is + // just an optimization. let res = unsafe { objc_sys::objc_retainAutoreleasedReturnValue(ptr) }; debug_assert_eq!( res, ptr,