Skip to content

Commit 608d251

Browse files
committed
WIP: Work on magic nop instructions
1 parent 8a3542f commit 608d251

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

objc2/benches/autorelease.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ macro_rules! main_with_warmup {
134134
)+
135135
}
136136

137+
// Needed to get DYLD to resolve the stubs
137138
fn warmup() {
138139
$(
139140
warmup_fns::$f();

objc2/src/rc/id.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,82 @@ impl<T: Message, O: Ownership> Id<T, O> {
274274

275275
/// TODO
276276
#[doc(alias = "objc_retainAutoreleasedReturnValue")]
277+
// This relies heavily on being inlined right after `objc_msgSend`.
277278
#[inline(always)]
278279
pub unsafe fn retain_autoreleased(ptr: NonNull<T>) -> Id<T, O> {
279-
// SAFETY: Same as `retain`, `objc_retainAutoreleasedReturnValue` is
280-
// just an optimization.
280+
// Not supported on TARGET_OS_WIN32
281+
#[cfg(all(apple, not(target_os = "windows")))]
282+
{
283+
// Add magic nop instruction to participate in the fast
284+
// autorelease scheme.
285+
//
286+
// We will unconditionally emit these instructions, even if they
287+
// end up being unused (for example because we're unlucky with
288+
// inlining, some other work is done between the objc_msgSend and
289+
// this, or the runtime version is too old to support it).
290+
//
291+
// See `callerAcceptsOptimizedReturn` in `objc-object.h`:
292+
// https://github.com/apple-oss-distributions/objc4/blob/objc4-838/runtime/objc-object.h#L1209-L1377
293+
// and this StackOverflow answer for some background on why the
294+
// design is like it is: https://stackoverflow.com/a/23765612.
295+
//
296+
// It may seem like there should be a better way to do this, but
297+
// emitting raw assembly is exactly what Clang and Swift does:
298+
// swiftc: https://github.com/apple/swift/blob/swift-5.5.3-RELEASE/lib/IRGen/GenObjC.cpp#L148-L173
299+
// Clang: https://github.com/llvm/llvm-project/blob/889317d47b7f046cf0e68746da8f7f264582fb5b/clang/lib/CodeGen/CGObjC.cpp#L2339-L2373
300+
//
301+
// SAFETY:
302+
// Based on https://doc.rust-lang.org/stable/reference/inline-assembly.html#rules-for-inline-assembly
303+
//
304+
// We don't care about the value of the register (so it's okay to
305+
// be undefined), and its value is preserved.
306+
//
307+
// nomem: No reads or writes to memory are performed (this `mov`
308+
// operates entirely on registers).
309+
// preserves_flags: `mov` doesn't modify any flags.
310+
// nostack: We don't touch the stack.
311+
312+
// Supported since macOS 10.7.
313+
#[cfg(target_arch = "x86_64")]
314+
{} // x86_64 looks at the next call instruction
315+
316+
// Supported since macOS 10.8.
317+
#[cfg(target_arch = "arm")]
318+
unsafe {
319+
core::arch::asm!("mov r7, r7", options(nomem, preserves_flags, nostack))
320+
};
321+
322+
// Supported since macOS 10.10.
323+
#[cfg(target_arch = "aarch64")]
324+
unsafe {
325+
core::arch::asm!("mov fp, fp", options(nomem, preserves_flags, nostack))
326+
};
327+
328+
// Supported since macOS 10.12.
329+
#[cfg(target_arch = "x86")]
330+
unsafe {
331+
core::arch::asm!("mov ebp, ebp", options(nomem, preserves_flags, nostack))
332+
};
333+
}
281334

282335
let ptr = ptr.as_ptr() as *mut objc_sys::objc_object;
336+
337+
// objc_autoreleaseReturnValue / objc_retainAutoreleasedReturnValue:
338+
339+
// #![feature(asm_sym)]
340+
// #[cfg(target_arch = "x86_64")]
341+
// unsafe {
342+
// core::arch::asm!(
343+
// "mov rdi, rax",
344+
// "call {}",
345+
// sym objc2::ffi::objc_retainAutoreleasedReturnValue,
346+
// inout("rax") obj,
347+
// clobber_abi("C"),
348+
// );
349+
// }
350+
351+
// SAFETY: Same as `retain`, `objc_retainAutoreleasedReturnValue` is
352+
// just an optimization.
283353
let res = unsafe { objc_sys::objc_retainAutoreleasedReturnValue(ptr) };
284354
debug_assert_eq!(
285355
res, ptr,

0 commit comments

Comments
 (0)