@@ -274,12 +274,82 @@ impl<T: Message, O: Ownership> Id<T, O> {
274
274
275
275
/// TODO
276
276
#[ doc( alias = "objc_retainAutoreleasedReturnValue" ) ]
277
+ // This relies heavily on being inlined right after `objc_msgSend`.
277
278
#[ inline( always) ]
278
279
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
+ }
281
334
282
335
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.
283
353
let res = unsafe { objc_sys:: objc_retainAutoreleasedReturnValue ( ptr) } ;
284
354
debug_assert_eq ! (
285
355
res, ptr,
0 commit comments