Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add static selectors #104

Merged
merged 30 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7eb623f
Add initial static selectors
madsmtm Mar 3, 2022
81e8faf
Add static selector assembly tests
madsmtm Jun 16, 2022
4c5cbcc
Use a volatile read
madsmtm Jun 16, 2022
e650f6d
Remove a few hacks around static selectors
madsmtm Mar 3, 2022
ca065ab
Allow dead_stripping
madsmtm Mar 3, 2022
a8803e3
Use a hack to make the compiler group internal selector functions int…
madsmtm Mar 3, 2022
5ef826c
Add static selector export names with generated hashes
madsmtm Jun 16, 2022
7c34341
Add hack to set up the __objc_imageinfo section
madsmtm Mar 3, 2022
3b33d8b
Add always present image tag
madsmtm Mar 3, 2022
70fdff9
Test static selectors in CI
madsmtm Jan 2, 2022
279d458
Comment and clean up static-sel
madsmtm Mar 29, 2022
959c991
Rename feature to `unstable-static-sel`
madsmtm Jun 17, 2022
69cca77
Show docs on sel! macro with the `unstable-static-sel` feature
madsmtm Jun 17, 2022
65f0bd3
Test __hash_idents macro
madsmtm Jun 17, 2022
832d6a6
Test getting sel!(alloc) twice
madsmtm Jun 17, 2022
8c07b3a
Use v0 name mangling in assembly tests
madsmtm Jun 17, 2022
e1735b0
Better document sel! macro
madsmtm Jun 21, 2022
3122c6c
Move the statics out of the function
madsmtm Jun 21, 2022
53611a1
Add "unstable-static-sel-inlined" feature flag
madsmtm Jun 21, 2022
c338496
Refactor image info creation
madsmtm Jun 21, 2022
9f7185b
Fix image info on simulator targets
madsmtm Jun 21, 2022
8decad4
Add support for static selectors on 32bit macOS
madsmtm Jun 21, 2022
61213ad
Emit `no_dead_strip` the same places that clang does
madsmtm Jun 21, 2022
14300c3
Use UnsafeCell instead of read_volatile
madsmtm Jun 21, 2022
1142392
Fix assembly tests in CI
madsmtm Jun 22, 2022
8b53cd1
Only test static selectors in CI on the Apple runtime
madsmtm Jun 22, 2022
f9cce82
Fix assembly tests for simulator targets
madsmtm Jun 22, 2022
a3c9bcd
Emit different assembly on x86 based on if using the old or new runtime
madsmtm Jun 22, 2022
776e6c9
More documentation
madsmtm Jun 23, 2022
783a742
Add changelog entry
madsmtm Jun 23, 2022
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
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ jobs:
# Not using --all-features because that would enable e.g. gnustep
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features ${{ env.FEATURES }},${{ env.UNSTABLE_FEATURES }}

- name: Test static selectors
if: ${{ !matrix.dinghy && (matrix.runtime || 'apple') == 'apple' }}
uses: actions-rs/cargo@v1
with:
command: test
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel

- name: Run assembly tests
# Not run on GNUStep yet since a lot of function labels are mangled and
# not inlined (and hence quite hard to match on, at some point we'll
Expand Down
18 changes: 17 additions & 1 deletion objc-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,27 @@ fn main() {
// The script doesn't depend on our code
println!("cargo:rerun-if-changed=build.rs");

let target = env::var("TARGET").unwrap();

// Used to figure out when BOOL should be i8 vs. bool
if env::var("TARGET").unwrap().ends_with("macabi") {
// Matches:
// aarch64-apple-ios-macabi
// x86_64-apple-ios-macabi
if target.ends_with("macabi") {
println!("cargo:rustc-cfg=target_abi_macabi");
}

// Used to set correct image info in `objc2`
// Matches:
// aarch64-apple-ios-sim
// aarch64-apple-watchos-sim
// x86_64-apple-watchos-sim
// i386-apple-ios
// x86_64-apple-ios
if target.ends_with("sim") || target == "i386-apple-ios" || target == "x86_64-apple-ios" {
println!("cargo:rustc-cfg=target_simulator");
}

// TODO: Figure out when to enable this
// println!("cargo:rustc-cfg=libobjc2_strict_apple_compat");

Expand Down
45 changes: 45 additions & 0 deletions objc-sys/src/image_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#[repr(C)]
#[doc(hidden)] // Private for now
pub struct __ImageInfo {
// These are not actually `unsigned int`, even though the docs say so
/// The version of the image info struct.
version: u32,
flags: u32,
}

#[allow(unused)]
impl __ImageInfo {
/// Unused
const FIX_AND_CONTINUE: u32 = 1 << 0;
const SUPPORTS_GARBAGE_COLLECTED: u32 = 1 << 1;
const REQUIRES_GARBAGE_COLLECTION: u32 = 1 << 2;
const OPTIMIZED_BY_DYLD: u32 = 1 << 3; // TODO
/// Unused
const CORRECTED_SYNTHESIZE: u32 = 1 << 4;
/// Whether we're compiling this to run on a simulator.
const IMAGE_IS_SIMULATED: u32 = 1 << 5;
/// Whether we are generating class properties.
const CLASS_PROPERTIES: u32 = 1 << 6;
const DYLD_PREOPTIMIZED: u32 = 1 << 7;

const SWIFT_ABI_VERSION_SHIFT: u32 = 8;
const SWIFT_ABI_VERSION_MASK: u32 = 0xff << Self::SWIFT_ABI_VERSION_SHIFT;
const SWIFT_MINOR_VERSION_SHIFT: u32 = 16;
const SWIFT_MINOR_VERSION_MASK: u32 = 0xff << Self::SWIFT_MINOR_VERSION_SHIFT;
const SWIFT_MAJOR_VERSION_SHIFT: u32 = 24;
const SWIFT_MAJOR_VERSION_MASK: u32 = 0xff << Self::SWIFT_MAJOR_VERSION_SHIFT;

/// Fetches the image info for the current runtime + target combination
#[inline]
pub const fn system() -> Self {
// We don't currently do anything relating to class properties, but
// let's just mimic what Clang does!
let mut flags = Self::CLASS_PROPERTIES;

if cfg!(target_simulator) {
flags |= Self::IMAGE_IS_SIMULATED;
}

Self { version: 0, flags }
}
}
2 changes: 2 additions & 0 deletions objc-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ mod class;
mod constants;

mod exception;
mod image_info;
mod message;
mod method;
mod object;
Expand All @@ -113,6 +114,7 @@ mod various;
pub use class::*;
pub use constants::*;
pub use exception::*;
pub use image_info::*;
pub use message::*;
pub use method::*;
pub use object::*;
Expand Down
2 changes: 1 addition & 1 deletion objc2-proc-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "objc2-proc-macros"
# Remember to update html_root_url in lib.rs
version = "0.0.0"
authors = ["Mads Marquart <mads@marquart.dk>"]
authors = ["Mads Marquart <mads@marquart.dk>", "Calvin Watford"]
edition = "2021"

description = "Procedural macros for the objc2 project"
Expand Down
64 changes: 64 additions & 0 deletions objc2-proc-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,67 @@
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
extern "C" {}

use core::hash::{Hash, Hasher};

use proc_macro::Ident;
use proc_macro::Literal;
use proc_macro::TokenStream;
use proc_macro::TokenTree;

/// Quick n' dirty way to extract the idents given by the `sel!` macro.
fn get_idents(input: TokenStream) -> impl Iterator<Item = Ident> {
input.into_iter().flat_map(|token| {
if let TokenTree::Group(group) = token {
group
.stream()
.into_iter()
.map(|token| {
if let TokenTree::Ident(ident) = token {
ident
} else {
panic!("Expected ident, got {:?}", token)
}
})
.collect::<Vec<_>>()
.into_iter()
} else if let TokenTree::Ident(ident) = token {
vec![ident].into_iter()
} else {
panic!("Expected group or ident, got {:?}", token)
}
})
}

/// Creates a hash from the input and source code locations in the provided
/// idents.
///
/// This hash is not guaranteed to be stable across compiler versions.
///
/// Tests are in [`objc2::__macro_helpers`].
#[proc_macro]
#[doc(hidden)]
pub fn __hash_idents(input: TokenStream) -> TokenStream {
// Create the hasher
let mut hasher = std::collections::hash_map::DefaultHasher::new();

// Hash each ident
for ident in get_idents(input) {
ident.to_string().hash(&mut hasher);

// Hash the source code location of the ident
//
// HACK: the only somewhat-reasonable way to get "unique" data in a
// proc macro right now is from the `Debug` formatter for spans which
// includes the source code location... so just hash the whole `Debug`
// format output of the span
//
// Prior art in the `defmt` crate, see here:
// https://github.com/knurling-rs/defmt/blob/defmt-v0.3.1/macros/src/construct.rs
format!("{:?}", ident.span()).hash(&mut hasher);
}

// Get the hash from the hasher and return it as 16 hexadecimal characters
let s = format!("{:016x}", hasher.finish());
TokenTree::Literal(Literal::string(&s)).into()
}
3 changes: 3 additions & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
msg_send_id![msg_send_id![Self::class(), alloc], new].unwrap()
};
```
* Added the `"unstable-static-sel"` and `"unstable-static-sel-inlined"`
feature flags to make the `sel!` macro (and by extension, the `msg_send!`
macros) faster.


## 0.3.0-beta.0 - 2022-06-13
Expand Down
12 changes: 12 additions & 0 deletions objc2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ verify_message = ["malloc"] # TODO: Remove malloc feature here
# increases compilation time.
malloc = ["malloc_buf"]

# Make the `sel!` macro look up the selector statically.
#
# The plan is to enable this by default, but right now we are uncertain of
# it's stability, and it might need significant changes before being fully
# ready!
#
# Please test it, and report any issues you may find:
# https://github.com/madsmtm/objc2/issues/new
unstable-static-sel = ["objc2-proc-macros"]
unstable-static-sel-inlined = ["unstable-static-sel"]

# Uses nightly features to make AutoreleasePool zero-cost even in debug mode
unstable-autoreleasesafe = []

Expand All @@ -52,6 +63,7 @@ gnustep-2-1 = ["gnustep-2-0", "objc-sys/gnustep-2-1"]
malloc_buf = { version = "1.0", optional = true }
objc-sys = { path = "../objc-sys", version = "=0.2.0-beta.0", default-features = false }
objc2-encode = { path = "../objc2-encode", version = "=2.0.0-pre.0", default-features = false }
objc2-proc-macros = { path = "../objc2-proc-macros", optional = true }

[dev-dependencies]
iai = { version = "0.1", git = "https://github.com/madsmtm/iai", branch = "callgrind" }
Expand Down
34 changes: 30 additions & 4 deletions objc2/src/__macro_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use crate::rc::{Id, Ownership};
use crate::runtime::{Class, Sel};
use crate::{Message, MessageArguments, MessageError, MessageReceiver};

#[doc(hidden)]
pub use core::cell::UnsafeCell;
pub use core::compile_error;
#[cfg(feature = "unstable-static-sel")]
pub use objc2_proc_macros::__hash_idents;

/// Helper for specifying the retain semantics for a given selector family.
///
Expand All @@ -28,7 +30,6 @@ pub use core::compile_error;
/// ARC though!
///
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments>
#[doc(hidden)]
pub struct RetainSemantics<
// `new` family
const NEW: bool,
Expand All @@ -40,7 +41,6 @@ pub struct RetainSemantics<
const COPY_OR_MUT_COPY: bool,
> {}

#[doc(hidden)]
pub trait MsgSendId<T, U> {
unsafe fn send_message_id<A: MessageArguments>(
obj: T,
Expand Down Expand Up @@ -131,7 +131,6 @@ impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, Id<U, O>>
/// Checks whether a given selector is said to be in a given selector family.
///
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families>
#[doc(hidden)]
pub const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool {
// Skip leading underscores from selector
loop {
Expand Down Expand Up @@ -373,4 +372,31 @@ mod tests {
let _obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new].unwrap() };
}
}

#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_different() {
assert_ne!(__hash_idents!(abc), __hash_idents!(def));
}

#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_same_no_equal() {
assert_ne!(__hash_idents!(abc), __hash_idents!(abc));
assert_ne!(__hash_idents!(abc def ghi), __hash_idents!(abc def ghi));
}

#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_exact_same_ident() {
macro_rules! x {
($x:ident) => {
(__hash_idents!($x), __hash_idents!($x))
};
}
let (ident1, ident2) = x!(abc);
// This is a limitation of `__hash_idents`, ideally we'd like these
// to be different!
assert_eq!(ident1, ident2);
}
}
Loading