Skip to content

Commit 6849481

Browse files
authored
Merge pull request #469 from madsmtm/assembly-improvements
Assembly improvements
2 parents 55703bc + 0405a38 commit 6849481

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+10002
-592
lines changed

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use core::ptr;
2+
use core::str;
23
use core::sync::atomic::{AtomicPtr, Ordering};
4+
use std::ffi::CStr;
5+
use std::os::raw::c_char;
36

47
use crate::ffi;
58
use crate::runtime::{AnyClass, Sel};
69

710
/// Allows storing a [`Sel`] in a static and lazily loading it.
8-
#[doc(hidden)]
911
pub struct CachedSel {
1012
ptr: AtomicPtr<ffi::objc_selector>,
1113
}
@@ -18,30 +20,38 @@ impl CachedSel {
1820
}
1921
}
2022

23+
// Mark as cold since this should only ever be called once (or maybe twice
24+
// if running on multiple threads).
25+
#[cold]
26+
unsafe fn fetch(&self, name: *const c_char) -> Sel {
27+
// The panic inside `Sel::register_unchecked` is unfortunate, but
28+
// strict correctness is more important than speed
29+
30+
// SAFETY: Input is a non-null, NUL-terminated C-string pointer.
31+
//
32+
// We know this, because we construct it in `sel!` ourselves
33+
let sel = unsafe { Sel::register_unchecked(name) };
34+
self.ptr
35+
.store(sel.as_ptr() as *mut ffi::objc_selector, Ordering::Relaxed);
36+
sel
37+
}
38+
2139
/// Returns the cached selector. If no selector is yet cached, registers
2240
/// one with the given name and stores it.
2341
#[inline]
24-
#[doc(hidden)]
2542
pub unsafe fn get(&self, name: &str) -> Sel {
2643
// `Relaxed` should be fine since `sel_registerName` is thread-safe.
2744
let ptr = self.ptr.load(Ordering::Relaxed);
28-
unsafe { Sel::from_ptr(ptr) }.unwrap_or_else(|| {
29-
// The panic inside `Sel::register_unchecked` is unfortunate, but
30-
// strict correctness is more important than speed
31-
32-
// SAFETY: Input is a non-null, NUL-terminated C-string pointer.
33-
//
34-
// We know this, because we construct it in `sel!` ourselves
35-
let sel = unsafe { Sel::register_unchecked(name.as_ptr().cast()) };
36-
self.ptr
37-
.store(sel.as_ptr() as *mut ffi::objc_selector, Ordering::Relaxed);
45+
if let Some(sel) = unsafe { Sel::from_ptr(ptr) } {
3846
sel
39-
})
47+
} else {
48+
// SAFETY: Checked by caller
49+
unsafe { self.fetch(name.as_ptr().cast()) }
50+
}
4051
}
4152
}
4253

4354
/// Allows storing a [`AnyClass`] reference in a static and lazily loading it.
44-
#[doc(hidden)]
4555
pub struct CachedClass {
4656
ptr: AtomicPtr<AnyClass>,
4757
}
@@ -54,19 +64,47 @@ impl CachedClass {
5464
}
5565
}
5666

67+
// Mark as cold since this should only ever be called once (or maybe twice
68+
// if running on multiple threads).
69+
#[cold]
70+
#[track_caller]
71+
unsafe fn fetch(&self, name: *const c_char) -> &'static AnyClass {
72+
let ptr: *const AnyClass = unsafe { ffi::objc_getClass(name) }.cast();
73+
self.ptr.store(ptr as *mut AnyClass, Ordering::Relaxed);
74+
if let Some(cls) = unsafe { ptr.as_ref() } {
75+
cls
76+
} else {
77+
// Recover the name from the pointer. We do it like this so that
78+
// we don't have to pass the length of the class to this method,
79+
// improving binary size.
80+
let name = unsafe { CStr::from_ptr(name) };
81+
let name = str::from_utf8(name.to_bytes()).unwrap();
82+
panic!("class {name} could not be found")
83+
}
84+
}
85+
5786
/// Returns the cached class. If no class is yet cached, gets one with
5887
/// the given name and stores it.
5988
#[inline]
60-
#[doc(hidden)]
61-
pub unsafe fn get(&self, name: &str) -> Option<&'static AnyClass> {
89+
#[track_caller]
90+
pub unsafe fn get(&self, name: &str) -> &'static AnyClass {
6291
// `Relaxed` should be fine since `objc_getClass` is thread-safe.
6392
let ptr = self.ptr.load(Ordering::Relaxed);
6493
if let Some(cls) = unsafe { ptr.as_ref() } {
65-
Some(cls)
94+
cls
6695
} else {
67-
let ptr: *const AnyClass = unsafe { ffi::objc_getClass(name.as_ptr().cast()) }.cast();
68-
self.ptr.store(ptr as *mut AnyClass, Ordering::Relaxed);
69-
unsafe { ptr.as_ref() }
96+
// SAFETY: Checked by caller
97+
unsafe { self.fetch(name.as_ptr().cast()) }
7098
}
7199
}
72100
}
101+
102+
#[cfg(test)]
103+
mod tests {
104+
#[test]
105+
#[should_panic = "class NonExistantClass could not be found"]
106+
#[cfg(not(feature = "unstable-static-class"))]
107+
fn test_not_found() {
108+
let _ = crate::class!(NonExistantClass);
109+
}
110+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//! Common selectors.
2+
//!
3+
//! These are put here to deduplicate the cached selector, and when using
4+
//! `unstable-static-sel`, the statics.
5+
//!
6+
//! Note that our assembly tests of `unstable-static-sel-inlined` output a GOT
7+
//! entry for such accesses, but that is just a limitation of our tests - the
8+
//! actual assembly is as one would expect.
9+
use crate::runtime::Sel;
10+
use crate::{__sel_data, __sel_inner};
11+
12+
#[inline]
13+
pub fn alloc_sel() -> Sel {
14+
__sel_inner!(__sel_data!(alloc), "alloc")
15+
}
16+
17+
#[inline]
18+
pub fn init_sel() -> Sel {
19+
__sel_inner!(__sel_data!(init), "init")
20+
}
21+
22+
#[inline]
23+
pub fn new_sel() -> Sel {
24+
__sel_inner!(__sel_data!(new), "new")
25+
}
26+
27+
#[inline]
28+
pub fn dealloc_sel() -> Sel {
29+
__sel_inner!(__sel_data!(dealloc), "dealloc")
30+
}

crates/objc2/src/__macro_helpers/mod.rs

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use crate::rc::{Allocated, Id};
1313
use crate::runtime::MethodDescription;
1414
use crate::runtime::{AnyClass, AnyObject, AnyProtocol, Sel};
1515
use crate::{Message, MessageArguments, MessageReceiver};
16-
use crate::{__sel_data, __sel_inner};
1716

1817
pub use core::borrow::{Borrow, BorrowMut};
1918
pub use core::cell::UnsafeCell;
@@ -29,36 +28,16 @@ pub use core::{compile_error, concat, panic, stringify};
2928
pub use std::sync::Once;
3029

3130
mod cache;
31+
mod common_selectors;
3232
mod declare_class;
3333

3434
pub use self::cache::{CachedClass, CachedSel};
35+
pub use self::common_selectors::{alloc_sel, dealloc_sel, init_sel, new_sel};
3536
pub use self::declare_class::{
3637
assert_mutability_matches_superclass_mutability, MaybeOptionId, MessageRecieveId,
3738
ValidSubclassMutability,
3839
};
3940

40-
// Common selectors.
41-
//
42-
// These are put here to deduplicate the cached selector, and when using
43-
// `unstable-static-sel`, the statics.
44-
//
45-
// Note that our assembly tests of `unstable-static-sel-inlined` output a GOT
46-
// entry for such accesses, but that is just a limitation of our tests - the
47-
// actual assembly is as one would expect.
48-
49-
#[inline]
50-
pub fn alloc_sel() -> Sel {
51-
__sel_inner!(__sel_data!(alloc), "alloc")
52-
}
53-
#[inline]
54-
pub fn init_sel() -> Sel {
55-
__sel_inner!(__sel_data!(init), "init")
56-
}
57-
#[inline]
58-
pub fn new_sel() -> Sel {
59-
__sel_inner!(__sel_data!(new), "new")
60-
}
61-
6241
/// Helper for specifying the retain semantics for a given selector family.
6342
///
6443
/// Note that we can't actually check if a method is in a method family; only
@@ -595,6 +574,7 @@ impl ClassProtocolMethodsBuilder<'_, '_> {
595574
}
596575
}
597576

577+
#[inline]
598578
pub fn __finish(self) {
599579
#[cfg(all(debug_assertions, feature = "verify"))]
600580
if let Some(protocol) = self.protocol {

crates/objc2/src/declare/declare_class_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ fn test_unreachable() {
368368
}
369369

370370
#[test]
371-
#[should_panic = "Failed to add ivar _ivar"]
371+
#[should_panic = "failed to add ivar _ivar"]
372372
fn test_duplicate_ivar() {
373373
declare_class!(
374374
struct DeclareClassDuplicateIvar {

0 commit comments

Comments
 (0)