Skip to content

Commit 9782d0a

Browse files
Auto merge of #143511 - orlp:tls-cold-init, r=<try>
Improve TLS codegen by marking the panic/init path as cold This is an extension of the performance improvements seen from <#141685>. I noticed that the non-`const` TLS still didn't have the `#[cold]` attribute for the uninit/panic path, and I also realized that neither implementation should have the initialization or panic path inlined, ever. These paths are taken either only once per thread (`init`) or never (`panic`, in a well-behaving Rust program), thus they don't deserve to litter the code generated each time you access a thread-local variable. So in addition to `#[cold]` I added the more aggressive `#[inline(never)]` to both cold paths as well.
2 parents c83e217 + 92fa8e8 commit 9782d0a

File tree

4 files changed

+26
-11
lines changed

4 files changed

+26
-11
lines changed

library/std/src/sys/thread_local/key/racy.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ impl LazyKey {
4242
}
4343
}
4444

45+
#[cold]
4546
fn lazy_init(&self) -> usize {
4647
// POSIX allows the key created here to be KEY_SENTVAL, but the compare_exchange
4748
// below relies on using KEY_SENTVAL as a sentinel value to check who won the

library/std/src/sys/thread_local/key/xous.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ unsafe extern "Rust" {
6767
static DTORS: Atomic<*mut Node>;
6868
}
6969

70+
#[inline]
7071
fn tls_ptr_addr() -> *mut *mut u8 {
7172
let mut tp: usize;
7273
unsafe {
@@ -80,14 +81,19 @@ fn tls_ptr_addr() -> *mut *mut u8 {
8081

8182
/// Creates an area of memory that's unique per thread. This area will
8283
/// contain all thread local pointers.
84+
#[inline]
8385
fn tls_table() -> &'static mut [*mut u8] {
8486
let tp = tls_ptr_addr();
8587

8688
if !tp.is_null() {
87-
return unsafe {
88-
core::slice::from_raw_parts_mut(tp, TLS_MEMORY_SIZE / size_of::<*mut u8>())
89-
};
89+
unsafe { core::slice::from_raw_parts_mut(tp, TLS_MEMORY_SIZE / size_of::<*mut u8>()) }
90+
} else {
91+
tls_table_slow()
9092
}
93+
}
94+
95+
#[cold]
96+
fn tls_table_slow() -> &'static mut [*mut u8] {
9197
// If the TP register is `0`, then this thread hasn't initialized
9298
// its TLS yet. Allocate a new page to store this memory.
9399
let tp = unsafe {

library/std/src/sys/thread_local/native/eager.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::sys::thread_local::{abort_on_dtor_unwind, destructors};
44

55
#[derive(Clone, Copy)]
66
enum State {
7-
Initial,
7+
Uninitialized,
88
Alive,
99
Destroyed,
1010
}
@@ -17,7 +17,7 @@ pub struct Storage<T> {
1717

1818
impl<T> Storage<T> {
1919
pub const fn new(val: T) -> Storage<T> {
20-
Storage { state: Cell::new(State::Initial), val: UnsafeCell::new(val) }
20+
Storage { state: Cell::new(State::Uninitialized), val: UnsafeCell::new(val) }
2121
}
2222

2323
/// Gets a pointer to the TLS value. If the TLS variable has been destroyed,
@@ -30,16 +30,22 @@ impl<T> Storage<T> {
3030
/// The `self` reference must remain valid until the TLS destructor is run.
3131
#[inline]
3232
pub unsafe fn get(&self) -> *const T {
33-
match self.state.get() {
34-
State::Alive => self.val.get(),
35-
State::Destroyed => ptr::null(),
36-
State::Initial => unsafe { self.initialize() },
33+
if let State::Alive = self.state.get() {
34+
self.val.get()
35+
} else {
36+
unsafe { self.get_or_init_slow() }
3737
}
3838
}
3939

4040
#[cold]
41-
unsafe fn initialize(&self) -> *const T {
42-
// Register the destructor
41+
unsafe fn get_or_init_slow(&self) -> *const T {
42+
match self.state.get() {
43+
State::Uninitialized => {}
44+
State::Alive => return self.val.get(),
45+
State::Destroyed => return ptr::null(),
46+
}
47+
48+
// Register the destructor.
4349

4450
// SAFETY:
4551
// The caller guarantees that `self` will be valid until thread destruction.

library/std/src/sys/thread_local/os.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ impl<T: 'static> Storage<T> {
6868
///
6969
/// The resulting pointer may not be used after reentrant inialialization
7070
/// or thread destruction has occurred.
71+
#[inline]
7172
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
7273
let key = self.key.force();
7374
let ptr = unsafe { get(key) as *mut Value<T> };
@@ -84,6 +85,7 @@ impl<T: 'static> Storage<T> {
8485
/// # Safety
8586
/// * `key` must be the result of calling `self.key.force()`
8687
/// * `ptr` must be the current value associated with `key`.
88+
#[cold]
8789
unsafe fn try_initialize(
8890
key: Key,
8991
ptr: *mut Value<T>,

0 commit comments

Comments
 (0)