use std::collections::{BTreeMap, HashMap, VecDeque};
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicUsize, Ordering};
#[derive(Debug, Clone)]
pub struct X86Concurrency {
pub atomic_model: X86AtomicModel,
pub memory_order: X86MemoryOrder,
pub thread_model: X86ThreadModel,
pub futex_support: X86FutexSupport,
pub spin_lock: X86SpinLock,
pub lock_elision: X86LockElision,
pub barrier: X86Barrier,
pub volatile_ops: X86VolatileOps,
pub arch: X86Arch,
pub has_tsx: bool,
pub has_cmpxchg16b: bool,
pub platform: X86Platform,
pub max_atomic_width: u8,
pub fence_strategy: X86FenceStrategy,
pub tls_model_pref: X86TlsModelPreference,
}
impl X86Concurrency {
pub fn new_x86_64_linux() -> Self {
Self {
atomic_model: X86AtomicModel::new_x86_64(),
memory_order: X86MemoryOrder::default(),
thread_model: X86ThreadModel::new_linux_x86_64(),
futex_support: X86FutexSupport::linux(),
spin_lock: X86SpinLock::default(),
lock_elision: X86LockElision::new(true),
barrier: X86Barrier::new_x86_64(),
volatile_ops: X86VolatileOps::new_x86_64(),
arch: X86Arch::X86_64,
has_tsx: true,
has_cmpxchg16b: true,
platform: X86Platform::Linux,
max_atomic_width: 16,
fence_strategy: X86FenceStrategy::default(),
tls_model_pref: X86TlsModelPreference::default(),
}
}
pub fn new_x86_32() -> Self {
Self {
atomic_model: X86AtomicModel::new_x86_32(),
memory_order: X86MemoryOrder::default(),
thread_model: X86ThreadModel::new_linux_x86_32(),
futex_support: X86FutexSupport::linux(),
spin_lock: X86SpinLock::default(),
lock_elision: X86LockElision::new(false),
barrier: X86Barrier::new_x86_32(),
volatile_ops: X86VolatileOps::new_x86_32(),
arch: X86Arch::X86_32,
has_tsx: false,
has_cmpxchg16b: false,
platform: X86Platform::Linux,
max_atomic_width: 8,
fence_strategy: X86FenceStrategy::Minimal,
tls_model_pref: X86TlsModelPreference::GeneralDynamic,
}
}
pub fn new_windows_x86_64() -> Self {
Self {
atomic_model: X86AtomicModel::new_x86_64(),
memory_order: X86MemoryOrder::default(),
thread_model: X86ThreadModel::new_windows_x86_64(),
futex_support: X86FutexSupport::windows(),
spin_lock: X86SpinLock::default(),
lock_elision: X86LockElision::new(true),
barrier: X86Barrier::new_x86_64(),
volatile_ops: X86VolatileOps::new_x86_64(),
arch: X86Arch::X86_64,
has_tsx: true,
has_cmpxchg16b: true,
platform: X86Platform::Windows,
max_atomic_width: 16,
fence_strategy: X86FenceStrategy::default(),
tls_model_pref: X86TlsModelPreference::LocalExec,
}
}
pub fn new_macos_x86_64() -> Self {
Self {
atomic_model: X86AtomicModel::new_x86_64(),
memory_order: X86MemoryOrder::default(),
thread_model: X86ThreadModel::new_macos_x86_64(),
futex_support: X86FutexSupport::macos(),
spin_lock: X86SpinLock::default(),
lock_elision: X86LockElision::new(true),
barrier: X86Barrier::new_x86_64(),
volatile_ops: X86VolatileOps::new_x86_64(),
arch: X86Arch::X86_64,
has_tsx: true,
has_cmpxchg16b: true,
platform: X86Platform::macOS,
max_atomic_width: 16,
fence_strategy: X86FenceStrategy::default(),
tls_model_pref: X86TlsModelPreference::InitialExec,
}
}
pub fn is_lock_free(&self, size_bytes: u8) -> bool {
self.atomic_model.is_lock_free(size_bytes)
}
pub fn emit_store_fence(&self, order: X86AtomicOrdering) -> Option<String> {
self.memory_order.emit_store_fence(order)
}
pub fn emit_load_fence(&self, order: X86AtomicOrdering) -> Option<String> {
self.memory_order.emit_load_fence(order)
}
pub fn emit_full_barrier(&self) -> String {
self.barrier.emit_full_barrier()
}
pub fn emit_read_barrier(&self) -> String {
self.barrier.emit_read_barrier()
}
pub fn emit_write_barrier(&self) -> String {
self.barrier.emit_write_barrier()
}
pub fn emit_tls_access(&self, var_name: &str, offset: u64) -> String {
self.thread_model.emit_tls_access(var_name, offset)
}
pub fn should_use_hle(&self) -> bool {
self.has_tsx && self.lock_elision.enabled
}
pub fn emit_thread_create(&self, func: &str, arg: &str) -> String {
self.thread_model.emit_thread_create(func, arg)
}
pub fn emit_thread_join(&self, thread_id: &str) -> String {
self.thread_model.emit_thread_join(thread_id)
}
pub fn emit_futex_wait(&self, addr: &str, expected: u32) -> String {
self.futex_support.emit_futex_wait(addr, expected)
}
pub fn emit_futex_wake(&self, addr: &str, count: u32) -> String {
self.futex_support.emit_futex_wake(addr, count)
}
pub fn emit_volatile_load(&self, dst: &str, src: &str, ty: &str) -> String {
self.volatile_ops.emit_volatile_load(dst, src, ty)
}
pub fn emit_volatile_store(&self, dst: &str, src: &str, ty: &str) -> String {
self.volatile_ops.emit_volatile_store(dst, src, ty)
}
pub fn describe(&self) -> String {
format!(
"X86Concurrency(arch={:?}, platform={:?}, max_atomic={}B, tsx={}, cmpxchg16b={}, fence={:?}, tls={:?})",
self.arch,
self.platform,
self.max_atomic_width,
self.has_tsx,
self.has_cmpxchg16b,
self.fence_strategy,
self.tls_model_pref,
)
}
}
impl Default for X86Concurrency {
fn default() -> Self {
Self::new_x86_64_linux()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Arch {
X86_32,
X86_64,
X86_16,
}
impl fmt::Display for X86Arch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::X86_32 => write!(f, "x86-32"),
Self::X86_64 => write!(f, "x86-64"),
Self::X86_16 => write!(f, "x86-16"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Platform {
Linux,
Windows,
macOS,
FreeBSD,
NetBSD,
OpenBSD,
Solaris,
Haiku,
Other,
}
impl fmt::Display for X86Platform {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Linux => write!(f, "linux"),
Self::Windows => write!(f, "windows"),
Self::macOS => write!(f, "macos"),
Self::FreeBSD => write!(f, "freebsd"),
Self::NetBSD => write!(f, "netbsd"),
Self::OpenBSD => write!(f, "openbsd"),
Self::Solaris => write!(f, "solaris"),
Self::Haiku => write!(f, "haiku"),
Self::Other => write!(f, "other"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86FenceStrategy {
Conservative,
Minimal,
LockAdd,
Default,
}
impl Default for X86FenceStrategy {
fn default() -> Self {
Self::Default
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TlsModelPreference {
GeneralDynamic,
LocalDynamic,
InitialExec,
LocalExec,
}
impl Default for X86TlsModelPreference {
fn default() -> Self {
Self::InitialExec
}
}
#[derive(Debug, Clone)]
pub struct X86AtomicModel {
pub arch: X86Arch,
pub has_cmpxchg16b: bool,
pub lock_prefix_supported: bool,
pub max_lock_free_width: u8,
pub cache_line_size: u8,
pub lock_implies_mfence: bool,
pub aligned_access_atomic: bool,
pub model: X86MemoryModelKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MemoryModelKind {
TSO,
StrongTSO,
WeakTSO,
}
impl fmt::Display for X86MemoryModelKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TSO => write!(f, "TSO"),
Self::StrongTSO => write!(f, "StrongTSO"),
Self::WeakTSO => write!(f, "WeakTSO"),
}
}
}
impl X86AtomicModel {
pub fn new_x86_64() -> Self {
Self {
arch: X86Arch::X86_64,
has_cmpxchg16b: true,
lock_prefix_supported: true,
max_lock_free_width: 16,
cache_line_size: 64,
lock_implies_mfence: true,
aligned_access_atomic: true,
model: X86MemoryModelKind::TSO,
}
}
pub fn new_x86_32() -> Self {
Self {
arch: X86Arch::X86_32,
has_cmpxchg16b: false,
lock_prefix_supported: true,
max_lock_free_width: 8,
cache_line_size: 64,
lock_implies_mfence: true,
aligned_access_atomic: true,
model: X86MemoryModelKind::TSO,
}
}
pub fn new_x86_16() -> Self {
Self {
arch: X86Arch::X86_16,
has_cmpxchg16b: false,
lock_prefix_supported: true,
max_lock_free_width: 4,
cache_line_size: 16,
lock_implies_mfence: false,
aligned_access_atomic: false,
model: X86MemoryModelKind::WeakTSO,
}
}
pub fn is_lock_free(&self, size_bytes: u8) -> bool {
match size_bytes {
1 | 2 | 4 => true,
8 => self.arch == X86Arch::X86_64 || self.arch == X86Arch::X86_32,
16 => self.arch == X86Arch::X86_64 && self.has_cmpxchg16b,
_ => false,
}
}
pub fn lock_free_sizes(&self) -> Vec<u8> {
let mut sizes = vec![1, 2, 4];
if self.arch == X86Arch::X86_64 || self.arch == X86Arch::X86_32 {
sizes.push(8);
}
if self.arch == X86Arch::X86_64 && self.has_cmpxchg16b {
sizes.push(16);
}
sizes
}
pub fn emit_atomic_load(
&self,
dst_reg: &str,
src_addr: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let mov_suffix = size_to_mov_suffix(size_bytes);
match ordering {
X86AtomicOrdering::Relaxed
| X86AtomicOrdering::Consume
| X86AtomicOrdering::Acquire => {
format!(
"mov{} {}, [{}] ; atomic load ({:?})\n\t# compiler_barrier",
mov_suffix, dst_reg, src_addr, ordering
)
}
X86AtomicOrdering::Release | X86AtomicOrdering::AcqRel => {
format!(
"mov{} {}, [{}] ; atomic load ({:?})\n\t# compiler_barrier",
mov_suffix, dst_reg, src_addr, ordering
)
}
X86AtomicOrdering::SeqCst => {
format!(
"mov{} {}, [{}] ; atomic load ({:?})\n\tmfence",
mov_suffix, dst_reg, src_addr, ordering
)
}
}
}
pub fn emit_atomic_store(
&self,
src_val: &str,
dst_addr: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let mov_suffix = size_to_mov_suffix(size_bytes);
match ordering {
X86AtomicOrdering::Relaxed => {
format!(
"mov{} [{}], {} ; atomic store ({:?})",
mov_suffix, dst_addr, src_val, ordering
)
}
X86AtomicOrdering::Consume | X86AtomicOrdering::Acquire => {
format!(
"# compiler_barrier\n\tmov{} [{}], {} ; atomic store ({:?})",
mov_suffix, dst_addr, src_val, ordering
)
}
X86AtomicOrdering::Release | X86AtomicOrdering::AcqRel => {
format!(
"# compiler_barrier\n\tmov{} [{}], {} ; atomic store ({:?})\n\t# compiler_barrier",
mov_suffix, dst_addr, src_val, ordering
)
}
X86AtomicOrdering::SeqCst => {
if size_bytes <= 8 {
format!(
"xchg{} [{}], {} ; atomic store seq_cst (implicit lock)",
mov_suffix, dst_addr, src_val
)
} else {
format!(
"# compiler_barrier\n\tmovdqa [{}], {} ; atomic store seq_cst (16B)\n\tmfence",
dst_addr, src_val
)
}
}
}
}
pub fn emit_atomic_exchange(
&self,
dst_reg: &str,
src_addr: &str,
val_reg: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"xchg{} {}, [{}] ; atomic xchg ({:?}), dst={}, val={}",
suffix, dst_reg, src_addr, ordering, dst_reg, val_reg
)
}
pub fn emit_atomic_compare_exchange(
&self,
addr: &str,
expected: &str,
desired: &str,
size_bytes: u8,
ordering_success: X86AtomicOrdering,
ordering_failure: X86AtomicOrdering,
is_weak: bool,
) -> String {
if size_bytes == 16 {
return self.emit_cmpxchg16b(addr, expected, desired, ordering_success);
}
let suffix = size_to_mov_suffix(size_bytes);
let lock_prefix = if self.lock_prefix_supported && !is_weak {
"lock "
} else {
""
};
let mut code = String::new();
code.push_str(&format!(
"; atomic cmpxchg{} ({:?}/{:?}) weak={}\n",
suffix, ordering_success, ordering_failure, is_weak
));
code.push_str(&format!(
"{}cmpxchg{} [{}], {}\n",
lock_prefix, suffix, addr, desired
));
if ordering_success == X86AtomicOrdering::SeqCst {
code.push_str("mfence\n");
}
code
}
fn emit_cmpxchg16b(
&self,
addr: &str,
expected: &str,
desired: &str,
ordering: X86AtomicOrdering,
) -> String {
let lock = if ordering == X86AtomicOrdering::SeqCst {
"lock "
} else {
""
};
format!(
"; atomic cmpxchg16b ({:?})\n\
; expected in RDX:RAX = {}, desired in RCX:RBX = {}\n\
{}cmpxchg16b [{}]",
ordering, expected, desired, lock, addr
)
}
pub fn emit_atomic_fetch_add(
&self,
dst_reg: &str,
addr: &str,
val: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"lock xadd{} {}, [{}] ; atomic fetch_add ({:?}), add {}",
suffix, dst_reg, addr, ordering, val
)
}
pub fn emit_atomic_fetch_sub(
&self,
dst_reg: &str,
addr: &str,
val: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"neg {}\n\tlock xadd{} {}, [{}] ; atomic fetch_sub ({:?}), sub {}",
val, suffix, dst_reg, addr, ordering, val
)
}
pub fn emit_atomic_fetch_and(
&self,
dst_reg: &str,
addr: &str,
val: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"; atomic fetch_and ({:?})\n\
.L_fetch_and_retry_{:x}:\n\
\tmov{} {}, [{}]\n\
\tmov{} r_temp, {}\n\
\tand{} r_temp, {}\n\
\tlock cmpxchg{} [{}], r_temp\n\
\tjne .L_fetch_and_retry_{:x}\n\
; result in {}",
ordering,
hash_str(addr),
suffix,
dst_reg,
addr,
suffix,
dst_reg,
suffix,
val,
suffix,
addr,
hash_str(addr),
dst_reg
)
}
pub fn emit_atomic_fetch_or(
&self,
dst_reg: &str,
addr: &str,
val: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"; atomic fetch_or ({:?})\n\
.L_fetch_or_retry_{:x}:\n\
\tmov{} {}, [{}]\n\
\tmov{} r_temp, {}\n\
\tor{} r_temp, {}\n\
\tlock cmpxchg{} [{}], r_temp\n\
\tjne .L_fetch_or_retry_{:x}\n\
; result in {}",
ordering,
hash_str(addr),
suffix,
dst_reg,
addr,
suffix,
dst_reg,
suffix,
val,
suffix,
addr,
hash_str(addr),
dst_reg
)
}
pub fn emit_atomic_fetch_xor(
&self,
dst_reg: &str,
addr: &str,
val: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"; atomic fetch_xor ({:?})\n\
.L_fetch_xor_retry_{:x}:\n\
\tmov{} {}, [{}]\n\
\tmov{} r_temp, {}\n\
\txor{} r_temp, {}\n\
\tlock cmpxchg{} [{}], r_temp\n\
\tjne .L_fetch_xor_retry_{:x}\n\
; result in {}",
ordering,
hash_str(addr),
suffix,
dst_reg,
addr,
suffix,
dst_reg,
suffix,
val,
suffix,
addr,
hash_str(addr),
dst_reg
)
}
pub fn emit_atomic_fetch_umax(
&self,
dst_reg: &str,
addr: &str,
val: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"; atomic fetch_umax ({:?})\n\
.L_fetch_umax_retry_{:x}:\n\
\tmov{} {}, [{}]\n\
\tcmp{} {}, {}\n\
\tjae .L_fetch_umax_done_{:x}\n\
\tmov{} {}, {}\n\
.L_fetch_umax_done_{:x}:\n\
; result in {}",
ordering,
hash_str(addr),
suffix,
dst_reg,
addr,
suffix,
dst_reg,
val,
hash_str(addr),
suffix,
dst_reg,
val,
hash_str(addr),
dst_reg
)
}
pub fn emit_atomic_fetch_umin(
&self,
dst_reg: &str,
addr: &str,
val: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"; atomic fetch_umin ({:?})\n\
.L_fetch_umin_retry_{:x}:\n\
\tmov{} {}, [{}]\n\
\tcmp{} {}, {}\n\
\tjbe .L_fetch_umin_done_{:x}\n\
\tmov{} {}, {}\n\
.L_fetch_umin_done_{:x}:\n\
; result in {}",
ordering,
hash_str(addr),
suffix,
dst_reg,
addr,
suffix,
dst_reg,
val,
hash_str(addr),
suffix,
dst_reg,
val,
hash_str(addr),
dst_reg
)
}
pub fn lock_prefix_semantics(&self) -> &'static str {
"LOCK prefix: asserts LOCK# signal, atomic RMW, full memory barrier (MFENCE equivalent), serializes pending loads/stores"
}
pub fn atomic_is_lock_free_c11(&self, size_bytes: u8) -> bool {
size_bytes.is_power_of_two() && size_bytes <= self.max_lock_free_width && size_bytes >= 1
}
pub fn recommended_alignment(&self, size_bytes: u8) -> u8 {
size_bytes
}
pub fn size_suffix(&self, size_bytes: u8) -> &'static str {
size_to_mov_suffix(size_bytes)
}
pub fn describe(&self) -> String {
format!(
"X86AtomicModel(arch={:?}, model={:?}, max_lock_free={}B, cmpxchg16b={}, lock_barrier={})",
self.arch,
self.model,
self.max_lock_free_width,
self.has_cmpxchg16b,
self.lock_implies_mfence,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86AtomicOrdering {
Relaxed,
Consume,
Acquire,
Release,
AcqRel,
SeqCst,
}
impl X86AtomicOrdering {
pub fn from_c11(mo: i32) -> Self {
match mo {
0 => Self::Relaxed, 1 => Self::Consume, 2 => Self::Acquire, 3 => Self::Release, 4 => Self::AcqRel, 5 => Self::SeqCst, _ => Self::SeqCst, }
}
pub fn to_c11(&self) -> i32 {
match self {
Self::Relaxed => 0,
Self::Consume => 1,
Self::Acquire => 2,
Self::Release => 3,
Self::AcqRel => 4,
Self::SeqCst => 5,
}
}
pub fn to_llvm_str(&self) -> &'static str {
match self {
Self::Relaxed => "monotonic",
Self::Consume => "consume",
Self::Acquire => "acquire",
Self::Release => "release",
Self::AcqRel => "acq_rel",
Self::SeqCst => "seq_cst",
}
}
pub fn requires_fence(&self) -> bool {
matches!(self, Self::SeqCst)
}
pub fn is_store_ordering(&self) -> bool {
matches!(self, Self::Release | Self::AcqRel | Self::SeqCst)
}
pub fn is_load_ordering(&self) -> bool {
matches!(
self,
Self::Acquire | Self::AcqRel | Self::SeqCst | Self::Consume
)
}
pub fn is_valid_cas_failure(&self) -> bool {
!matches!(self, Self::Release | Self::AcqRel)
}
pub fn valid_failure_ordering(success: X86AtomicOrdering) -> X86AtomicOrdering {
match success {
Self::Relaxed | Self::Consume => Self::Relaxed,
Self::Acquire => Self::Acquire,
Self::Release => Self::Relaxed, Self::AcqRel => Self::Acquire,
Self::SeqCst => Self::SeqCst,
}
}
pub fn is_compatible(success: X86AtomicOrdering, failure: X86AtomicOrdering) -> bool {
failure.to_c11() <= success.to_c11() && failure.is_valid_cas_failure()
}
}
impl fmt::Display for X86AtomicOrdering {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Relaxed => write!(f, "relaxed"),
Self::Consume => write!(f, "consume"),
Self::Acquire => write!(f, "acquire"),
Self::Release => write!(f, "release"),
Self::AcqRel => write!(f, "acq_rel"),
Self::SeqCst => write!(f, "seq_cst"),
}
}
}
fn size_to_mov_suffix(size_bytes: u8) -> &'static str {
match size_bytes {
1 => "b", 2 => "w", 4 => "l", 8 => "q", 16 => "dqa", _ => "q", }
}
fn hash_str(s: &str) -> u64 {
let mut h: u64 = 5381;
for b in s.bytes() {
h = h.wrapping_mul(33).wrapping_add(b as u64);
}
h
}
#[derive(Debug, Clone)]
pub struct X86MemoryOrder {
pub use_mfence: bool,
pub spectre_lfence_mitigation: bool,
pub has_sse2: bool,
pub use_lock_add_fence: bool,
pub order_table: X86OrderTable,
}
#[derive(Debug, Clone)]
pub struct X86OrderTable {
pub relaxed: (String, String),
pub consume: (String, String),
pub acquire: (String, String),
pub release: (String, String),
pub acq_rel: (String, String),
pub seq_cst: (String, String),
}
impl Default for X86OrderTable {
fn default() -> Self {
Self {
relaxed: ("mov".into(), "mov".into()),
consume: ("mov ; compiler_barrier".into(), "mov".into()),
acquire: ("mov ; compiler_barrier".into(), "mov".into()),
release: ("mov".into(), "compiler_barrier ; mov".into()),
acq_rel: (
"mov ; compiler_barrier".into(),
"compiler_barrier ; mov".into(),
),
seq_cst: ("mov ; mfence".into(), "xchg".into()),
}
}
}
impl X86MemoryOrder {
pub fn default() -> Self {
Self {
use_mfence: true,
spectre_lfence_mitigation: false,
has_sse2: true,
use_lock_add_fence: false,
order_table: X86OrderTable::default(),
}
}
pub fn new_modern() -> Self {
Self {
use_mfence: true,
spectre_lfence_mitigation: true,
has_sse2: true,
use_lock_add_fence: false,
order_table: X86OrderTable::default(),
}
}
pub fn new_no_sse2() -> Self {
Self {
use_mfence: false,
spectre_lfence_mitigation: false,
has_sse2: false,
use_lock_add_fence: true, order_table: X86OrderTable::default(),
}
}
pub fn emit_load_fence(&self, order: X86AtomicOrdering) -> Option<String> {
match order {
X86AtomicOrdering::Relaxed => None,
X86AtomicOrdering::Consume | X86AtomicOrdering::Acquire => {
if self.spectre_lfence_mitigation {
Some("lfence ; Spectre mitigation for load fence".into())
} else {
Some("# compiler_barrier".into())
}
}
X86AtomicOrdering::Release => None, X86AtomicOrdering::AcqRel => {
if self.spectre_lfence_mitigation {
Some("lfence ; acquire fence (Spectre mitigation)".into())
} else {
Some("# compiler_barrier".into())
}
}
X86AtomicOrdering::SeqCst => {
if self.use_mfence && self.has_sse2 {
Some("mfence ; seq_cst load barrier".into())
} else if self.use_lock_add_fence {
Some("lock addl $0, 0(%rsp) ; seq_cst load barrier (lock add)".into())
} else {
Some("mfence ; seq_cst load barrier".into())
}
}
}
}
pub fn emit_store_fence(&self, order: X86AtomicOrdering) -> Option<String> {
match order {
X86AtomicOrdering::Relaxed => None,
X86AtomicOrdering::Consume | X86AtomicOrdering::Acquire => None,
X86AtomicOrdering::Release => {
Some("# compiler_barrier".into())
}
X86AtomicOrdering::AcqRel => Some("# compiler_barrier".into()),
X86AtomicOrdering::SeqCst => {
if self.use_mfence && self.has_sse2 {
Some("mfence ; seq_cst store barrier".into())
} else if self.use_lock_add_fence {
Some("lock addl $0, 0(%rsp) ; seq_cst store barrier (lock add)".into())
} else {
Some("mfence ; seq_cst store barrier".into())
}
}
}
}
pub fn emit_seq_cst_fence(&self) -> String {
if self.use_mfence && self.has_sse2 {
"mfence ; seq_cst full barrier".into()
} else if self.use_lock_add_fence {
"lock addl $0, 0(%rsp) ; seq_cst full barrier (not recommended for perf)".into()
} else {
"mfence ; seq_cst full barrier".into()
}
}
pub fn describe_mapping(&self, order: X86AtomicOrdering) -> String {
let load_desc = match self.emit_load_fence(order) {
Some(ref f) => format!("load_fence={}", f),
None => "load_fence=none".to_string(),
};
let store_desc = match self.emit_store_fence(order) {
Some(ref f) => format!("store_fence={}", f),
None => "store_fence=none".to_string(),
};
format!("X86({:?}): {} ; {}", order, load_desc, store_desc)
}
pub fn can_reorder(&self, earlier: X86AtomicOrdering, later: X86AtomicOrdering) -> bool {
!matches!(
(earlier, later),
(X86AtomicOrdering::SeqCst, _) | (_, X86AtomicOrdering::SeqCst)
)
}
}
#[derive(Debug, Clone)]
pub struct X86ThreadModel {
pub platform: X86Platform,
pub arch: X86Arch,
pub tls_model: X86TlsModel,
pub tls_format: X86TlsFormat,
pub tls_abi_version: u8,
pub has_tls_get_addr: bool,
pub thread_create_api: X86ThreadCreateApi,
pub thread_join_api: X86ThreadJoinApi,
pub cxx11_thread_local: bool,
pub tls_cache: X86TlsCache,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TlsModel {
GeneralDynamic,
LocalDynamic,
InitialExec,
LocalExec,
}
impl fmt::Display for X86TlsModel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::GeneralDynamic => write!(f, "general-dynamic"),
Self::LocalDynamic => write!(f, "local-dynamic"),
Self::InitialExec => write!(f, "initial-exec"),
Self::LocalExec => write!(f, "local-exec"),
}
}
}
impl X86TlsModel {
pub fn can_relax_to(&self, target: X86TlsModel) -> bool {
let strength = |m: X86TlsModel| -> u8 {
match m {
X86TlsModel::GeneralDynamic => 0,
X86TlsModel::LocalDynamic => 1,
X86TlsModel::InitialExec => 2,
X86TlsModel::LocalExec => 3,
}
};
strength(*self) <= strength(target)
}
pub fn x86_64_access_pattern(&self) -> &'static str {
match self {
Self::GeneralDynamic => "leaq var@TLSGD(%rip), %rdi ; call __tls_get_addr@PLT",
Self::LocalDynamic => {
"leaq var@TLSLD(%rip), %rdi ; call __tls_get_addr@PLT ; leaq var@DTPOFF(%rax), %rax"
}
Self::InitialExec => "movq var@GOTTPOFF(%rip), %rax ; movq (%fs:%rax), %rax",
Self::LocalExec => "movq %fs:var@TPOFF, %rax",
}
}
pub fn x86_32_access_pattern(&self) -> &'static str {
match self {
Self::GeneralDynamic => {
"leal var@TLSGD(,%ebx,1), %eax ; call ___tls_get_addr@PLT"
}
Self::LocalDynamic => {
"leal var@TLSLDM(%ebx), %eax ; call ___tls_get_addr@PLT ; leal var@DTPOFF(%eax), %eax"
}
Self::InitialExec => {
"movl var@GOTNTPOFF(%ebx), %eax ; movl (%gs:%eax), %eax"
}
Self::LocalExec => {
"movl %gs:var@NTPOFF, %eax"
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TlsFormat {
ELF,
PE,
MachO,
}
impl fmt::Display for X86TlsFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ELF => write!(f, "elf"),
Self::PE => write!(f, "pe"),
Self::MachO => write!(f, "macho"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ThreadCreateApi {
PthreadCreate,
CreateThread,
PthreadCreateMacOS,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ThreadJoinApi {
PthreadJoin,
WaitForSingleObject,
PthreadJoinMacOS,
Custom,
}
#[derive(Debug, Clone)]
pub struct X86TlsCache {
pub offset_cache: HashMap<String, u64>,
pub enabled: bool,
pub hits: u64,
pub misses: u64,
}
impl Default for X86TlsCache {
fn default() -> Self {
Self {
offset_cache: HashMap::new(),
enabled: true,
hits: 0,
misses: 0,
}
}
}
impl X86TlsCache {
pub fn lookup(&self, var_name: &str) -> Option<u64> {
self.offset_cache.get(var_name).copied()
}
pub fn insert(&mut self, var_name: String, offset: u64) {
self.offset_cache.insert(var_name, offset);
}
pub fn clear(&mut self) {
self.offset_cache.clear();
self.hits = 0;
self.misses = 0;
}
}
impl X86ThreadModel {
pub fn new_linux_x86_64() -> Self {
Self {
platform: X86Platform::Linux,
arch: X86Arch::X86_64,
tls_model: X86TlsModel::InitialExec,
tls_format: X86TlsFormat::ELF,
tls_abi_version: 1,
has_tls_get_addr: true,
thread_create_api: X86ThreadCreateApi::PthreadCreate,
thread_join_api: X86ThreadJoinApi::PthreadJoin,
cxx11_thread_local: true,
tls_cache: X86TlsCache::default(),
}
}
pub fn new_linux_x86_32() -> Self {
Self {
platform: X86Platform::Linux,
arch: X86Arch::X86_32,
tls_model: X86TlsModel::GeneralDynamic,
tls_format: X86TlsFormat::ELF,
tls_abi_version: 1,
has_tls_get_addr: true,
thread_create_api: X86ThreadCreateApi::PthreadCreate,
thread_join_api: X86ThreadJoinApi::PthreadJoin,
cxx11_thread_local: true,
tls_cache: X86TlsCache::default(),
}
}
pub fn new_windows_x86_64() -> Self {
Self {
platform: X86Platform::Windows,
arch: X86Arch::X86_64,
tls_model: X86TlsModel::LocalExec,
tls_format: X86TlsFormat::PE,
tls_abi_version: 0,
has_tls_get_addr: false,
thread_create_api: X86ThreadCreateApi::CreateThread,
thread_join_api: X86ThreadJoinApi::WaitForSingleObject,
cxx11_thread_local: true,
tls_cache: X86TlsCache::default(),
}
}
pub fn new_macos_x86_64() -> Self {
Self {
platform: X86Platform::macOS,
arch: X86Arch::X86_64,
tls_model: X86TlsModel::InitialExec,
tls_format: X86TlsFormat::MachO,
tls_abi_version: 0,
has_tls_get_addr: false,
thread_create_api: X86ThreadCreateApi::PthreadCreateMacOS,
thread_join_api: X86ThreadJoinApi::PthreadJoinMacOS,
cxx11_thread_local: true,
tls_cache: X86TlsCache::default(),
}
}
pub fn emit_tls_access(&self, var_name: &str, offset: u64) -> String {
match (self.tls_format, self.tls_model) {
(X86TlsFormat::ELF, model) => self.emit_elf_tls_access(var_name, offset, model),
(X86TlsFormat::PE, _) => self.emit_pe_tls_access(var_name, offset),
(X86TlsFormat::MachO, _) => self.emit_macho_tls_access(var_name, offset),
}
}
fn emit_elf_tls_access(&self, var_name: &str, offset: u64, model: X86TlsModel) -> String {
match (self.arch, model) {
(X86Arch::X86_64, X86TlsModel::GeneralDynamic) => format!(
"; ELF TLS General Dynamic access to {}\n\
.byte 0x66\n\
leaq {}@TLSGD(%rip), %rdi\n\
.word 0x6666\n\
rex64\n\
call __tls_get_addr@PLT\n\
; result in %rax (base), add DTPOFF({}) = {}",
var_name, var_name, var_name, offset
),
(X86Arch::X86_64, X86TlsModel::LocalDynamic) => format!(
"; ELF TLS Local Dynamic access to {}\n\
leaq {}@TLSLD(%rip), %rdi\n\
call __tls_get_addr@PLT\n\
; base in %rax, add DTPOFF\n\
leaq {}(%rax), %rax ; DTPOFF({}) = {}",
var_name, var_name, var_name, var_name, offset
),
(X86Arch::X86_64, X86TlsModel::InitialExec) => format!(
"; ELF TLS Initial Exec access to {}\n\
movq {}@GOTTPOFF(%rip), %rax\n\
movq %fs:(%rax), %rax ; offset from thread pointer = {}",
var_name, var_name, offset
),
(X86Arch::X86_64, X86TlsModel::LocalExec) => format!(
"; ELF TLS Local Exec access to {}\n\
movq %fs:{}, %rax ; direct TPOFF = {}",
var_name, offset, offset
),
(X86Arch::X86_32, X86TlsModel::GeneralDynamic) => format!(
"; ELF TLS (32-bit) General Dynamic access to {}\n\
leal {}@TLSGD(,%ebx,1), %eax\n\
call ___tls_get_addr@PLT\n\
; result in %eax",
var_name, var_name
),
(X86Arch::X86_32, X86TlsModel::LocalDynamic) => format!(
"; ELF TLS (32-bit) Local Dynamic access to {}\n\
leal {}@TLSLDM(%ebx), %eax\n\
call ___tls_get_addr@PLT\n\
leal {}@DTPOFF(%eax), %eax",
var_name, var_name, var_name
),
(X86Arch::X86_32, X86TlsModel::InitialExec) => format!(
"; ELF TLS (32-bit) Initial Exec access to {}\n\
movl {}@GOTNTPOFF(%ebx), %eax\n\
movl %gs:(%eax), %eax",
var_name, var_name
),
(X86Arch::X86_32, X86TlsModel::LocalExec) => format!(
"; ELF TLS (32-bit) Local Exec access to {}\n\
movl %gs:{}, %eax ; NTPOFF = {}",
var_name, offset, offset
),
_ => format!(
"; Unsupported TLS access: arch={:?} model={:?}",
self.arch, model
),
}
}
fn emit_pe_tls_access(&self, var_name: &str, offset: u64) -> String {
match self.arch {
X86Arch::X86_64 => format!(
"; Windows PE TLS (64-bit) access to {}\n\
movq %gs:0x58, %rax ; ThreadLocalStoragePointer\n\
movq {}(%rax), %rax ; offset {} in TLS block",
var_name, offset, offset
),
X86Arch::X86_32 => format!(
"; Windows PE TLS (32-bit) access to {}\n\
movl %fs:0x2C, %eax ; ThreadLocalStoragePointer\n\
movl {}(%eax), %eax ; offset {} in TLS block",
var_name, offset, offset
),
_ => format!("; PE TLS access not supported for {:?}", self.arch),
}
}
fn emit_macho_tls_access(&self, var_name: &str, offset: u64) -> String {
format!(
"; macOS Mach-O TLS access to {}\n\
; Uses dyld_stub_binder for TLS initialization\n\
movq {}@TLVP(%rip), %rdi\n\
callq *(%rdi) ; tlv_get_address\n\
movq {}(%rax), %rax",
var_name, var_name, offset
)
}
pub fn emit_thread_create(&self, func: &str, arg: &str) -> String {
match (self.platform, self.arch) {
(X86Platform::Linux, _) => format!(
"; POSIX thread creation: {} ({})\n\
; pthread_create(ptr thread_id, ptr attr, ptr func, ptr arg)\n\
movq ${}, %rdi ; func\n\
movq ${}, %rsi ; arg\n\
call pthread_create@PLT",
func, arg, func, arg
),
(X86Platform::Windows, X86Arch::X86_64) => format!(
"; Windows thread creation: {} ({})\n\
; CreateThread(NULL, 0, func, arg, 0, NULL)\n\
xorl %ecx, %ecx ; lpThreadAttributes = NULL\n\
xorl %edx, %edx ; dwStackSize = 0\n\
leaq {}@GOTPCREL(%rip), %r8 ; lpStartAddress\n\
leaq {}@GOTPCREL(%rip), %r9 ; lpParameter\n\
pushq $0 ; dwCreationFlags = 0\n\
pushq $0 ; lpThreadId = NULL\n\
call CreateThread",
func, arg, func, arg
),
(X86Platform::macOS, _) => format!(
"; macOS thread creation: {} ({})\n\
; pthread_create via libSystem\n\
leaq {}@GOTPCREL(%rip), %rdi\n\
xorl %esi, %esi ; attr = NULL\n\
leaq {}@GOTPCREL(%rip), %rdx\n\
xorl %ecx, %ecx ; arg = NULL? (handled in wrapper)\n\
call _pthread_create",
func, arg, func, arg
),
_ => format!(
"; Thread creation not implemented for {:?}/{:?}\n\
; func: {}, arg: {}",
self.platform, self.arch, func, arg
),
}
}
pub fn emit_thread_join(&self, thread_id: &str) -> String {
match (self.platform, self.arch) {
(X86Platform::Linux, X86Arch::X86_64) => format!(
"; POSIX thread join (X86-64 Linux)\n\
; pthread_join(tid, NULL)\n\
movq {}, %rdi ; thread_id\n\
xorl %esi, %esi ; retval = NULL\n\
call pthread_join@PLT",
thread_id
),
(X86Platform::Linux, X86Arch::X86_32) => format!(
"; POSIX thread join (IA-32 Linux)\n\
; pthread_join(tid, NULL)\n\
pushl $0 ; retval = NULL\n\
pushl {} ; thread_id\n\
call pthread_join@PLT\n\
addl $8, %esp",
thread_id
),
(X86Platform::Windows, _) => format!(
"; Windows thread wait (WaitForSingleObject)\n\
; WaitForSingleObject(thread_handle, INFINITE)\n\
movq {}, %rcx ; hThread\n\
movl $0xFFFFFFFF, %edx ; INFINITE\n\
call WaitForSingleObject",
thread_id
),
(X86Platform::macOS, _) => format!(
"; macOS thread join\n\
movq {}, %rdi ; thread\n\
xorl %esi, %esi ; retval = NULL\n\
call _pthread_join",
thread_id
),
_ => format!(
"; Thread join not implemented for {:?}/{:?}, tid={}",
self.platform, self.arch, thread_id
),
}
}
pub fn emit_thread_local_declaration(&self, var_type: &str, var_name: &str) -> String {
match self.tls_format {
X86TlsFormat::ELF => format!(
"; C++11 thread_local ({:?} TLS)\n\
.section .tdata,\"awT\",@progbits\n\
.globl {}\n\
.type {}, @object\n\
.size {}, {}\n\
{}:\n\
\t.{}\t0 ; initialized to zero",
self.tls_model,
var_name,
var_name,
var_name,
type_size_str(var_type),
var_name,
type_directive(var_type),
),
X86TlsFormat::PE => format!(
"; Windows __declspec(thread) / thread_local ({})\n\
.section .tls$,\"dw\"\n\
.globl {}\n\
{}:\n\
\t.{}\t0",
var_name,
var_name,
var_name,
type_directive(var_type),
),
X86TlsFormat::MachO => format!(
"; macOS __thread / thread_local ({})\n\
.section __DATA,__thread_vars,thread_local_variables\n\
.globl {}\n\
{}:\n\
\t.{}\t0",
var_name,
var_name,
var_name,
type_directive(var_type),
),
}
}
pub fn emit_tls_get_addr_stub(&self) -> String {
match (self.platform, self.arch) {
(X86Platform::Linux, X86Arch::X86_64) => "; __tls_get_addr for X86-64 Linux ELF TLS\n\
; Input: %rdi = pointer to tls_index (module, offset)\n\
; Output: %rax = address of TLS variable\n\
; The actual implementation is in ld-linux.so / libc\n\
; This is just a stub for documentation purposes.\n\
__tls_get_addr:\n\
\tpushq %rbx\n\
\tmovq %fs:0, %rax ; thread pointer\n\
\tmovq (%rdi), %rdx ; module ID\n\
\tmovq 8(%rdi), %rcx ; offset\n\
\t; Look up dtv[module] + offset\n\
\t; This is handled by the dynamic linker\n\
\tpopq %rbx\n\
\tret"
.to_string(),
(X86Platform::Linux, X86Arch::X86_32) => "; ___tls_get_addr for IA-32 Linux ELF TLS\n\
___tls_get_addr:\n\
\tpushl %ebx\n\
\tmovl %gs:0, %eax ; thread pointer\n\
\tmovl (%eax), %edx\n\
\taddl 4(%eax), %edx\n\
\tpopl %ebx\n\
\tret"
.to_string(),
_ => format!(
"; __tls_get_addr not applicable for {:?}/{:?}",
self.platform, self.arch
),
}
}
pub fn tls_relaxation_chain(&self) -> &'static str {
"TLS Relaxation: General Dynamic → Local Dynamic → Initial Exec → Local Exec\n\
GD→LD: same-module optimization\n\
LD→IE: executable (not shared library)\n\
IE→LE: main executable, known TPOFF"
}
pub fn can_relax_tls(&self, from: X86TlsModel, to: X86TlsModel) -> bool {
from.can_relax_to(to)
}
pub fn thread_pointer_register(&self) -> &'static str {
match self.arch {
X86Arch::X86_64 => "%fs", X86Arch::X86_32 => "%gs", X86Arch::X86_16 => "%gs", }
}
pub fn thread_pointer_offset(&self) -> i64 {
match self.arch {
X86Arch::X86_64 => 0, X86Arch::X86_32 => 0, X86Arch::X86_16 => 0,
}
}
}
fn type_size_str(ty: &str) -> &'static str {
match ty {
"i8" | "i8*" | "char" => "1",
"i16" | "short" => "2",
"i32" | "int" | "float" => "4",
"i64" | "long" | "double" | "i64*" | "ptr" => "8",
_ => "8",
}
}
fn type_directive(ty: &str) -> &'static str {
match ty {
"i8" | "i8*" | "char" => "byte",
"i16" | "short" => "word",
"i32" | "int" | "float" => "long",
"i64" | "long" | "double" | "i64*" | "ptr" => "quad",
_ => "quad",
}
}
#[derive(Debug, Clone)]
pub struct X86FutexSupport {
pub platform: X86Platform,
pub has_futex_syscall: bool,
pub has_wait_on_address: bool,
pub has_ulock: bool,
pub bitset_match_any: u32,
pub supports_private_futex: bool,
pub supports_futex_wait_multiple: bool,
pub ops: X86FutexOps,
}
#[derive(Debug, Clone)]
pub struct X86FutexOps {
pub futex_wait: i32,
pub futex_wake: i32,
pub futex_fd: i32,
pub futex_requeue: i32,
pub futex_cmp_requeue: i32,
pub futex_wake_op: i32,
pub futex_lock_pi: i32,
pub futex_unlock_pi: i32,
pub futex_trylock_pi: i32,
pub futex_wait_bitset: i32,
pub futex_wake_bitset: i32,
pub futex_wait_requeue_pi: i32,
pub futex_cmp_requeue_pi: i32,
}
impl Default for X86FutexOps {
fn default() -> Self {
Self {
futex_wait: 0,
futex_wake: 1,
futex_fd: 2,
futex_requeue: 3,
futex_cmp_requeue: 4,
futex_wake_op: 5,
futex_lock_pi: 6,
futex_unlock_pi: 7,
futex_trylock_pi: 8,
futex_wait_bitset: 9,
futex_wake_bitset: 10,
futex_wait_requeue_pi: 11,
futex_cmp_requeue_pi: 12,
}
}
}
impl X86FutexSupport {
pub fn linux() -> Self {
Self {
platform: X86Platform::Linux,
has_futex_syscall: true,
has_wait_on_address: false,
has_ulock: false,
bitset_match_any: 0xFFFFFFFF,
supports_private_futex: true,
supports_futex_wait_multiple: false,
ops: X86FutexOps::default(),
}
}
pub fn windows() -> Self {
Self {
platform: X86Platform::Windows,
has_futex_syscall: false,
has_wait_on_address: true,
has_ulock: false,
bitset_match_any: 0,
supports_private_futex: false,
supports_futex_wait_multiple: false,
ops: X86FutexOps::default(),
}
}
pub fn macos() -> Self {
Self {
platform: X86Platform::macOS,
has_futex_syscall: false,
has_wait_on_address: false,
has_ulock: true,
bitset_match_any: 0,
supports_private_futex: false,
supports_futex_wait_multiple: false,
ops: X86FutexOps::default(),
}
}
pub fn emit_futex_wait(&self, addr: &str, expected: u32) -> String {
match self.platform {
X86Platform::Linux => {
format!(
"; Linux futex_wait(addr={{}}, expected_val={{}})\n\
; syscall futex(FUTEX_WAIT, uaddr, FUTEX_WAIT_PRIVATE, val, NULL)\n\
; X86-64 calling convention: %rdi=uaddr, %rsi=op, %rdx=val, %r10=timeout\n\
movq ${}, %%rdi ; uaddr\n\
movl ${}, %%esi ; FUTEX_WAIT_PRIVATE (128)\n\
movl ${}, %%edx ; expected value\n\
xorl %%r10d, %%r10d ; timeout = NULL\n\
xorl %%r8d, %%r8d ; uaddr2 = NULL\n\
xorl %%r9d, %%r9d ; val3 = 0\n\
movl $202, %%eax ; sys_futex (X86-64)\n\
syscall",
addr,
self.ops.futex_wait + 128,
expected
)
}
X86Platform::Windows => self.emit_wait_on_address(addr, expected),
X86Platform::macOS => self.emit_ulock_wait(addr, expected as u64),
_ => format!("; Futex wait not implemented for {:?}", self.platform),
}
}
pub fn emit_futex_wake(&self, addr: &str, count: u32) -> String {
match self.platform {
X86Platform::Linux => {
let op = if count == 1 {
129 } else {
129 };
format!(
"; Linux futex_wake(addr={{}}, count={{}})\n\
; syscall futex(FUTEX_WAKE_PRIVATE, uaddr, count)\n\
movq ${}, %%rdi ; uaddr\n\
movl ${}, %%esi ; FUTEX_WAKE_PRIVATE\n\
movl ${}, %%edx ; count\n\
xorl %%r10d, %%r10d\n\
xorl %%r8d, %%r8d\n\
movl $202, %%eax\n\
syscall\n\
; return value in %%eax: number of woken threads",
addr, op, count
)
}
X86Platform::Windows => self.emit_wake_by_address(addr, count),
X86Platform::macOS => self.emit_ulock_wake(addr),
_ => format!("; Futex wake not implemented for {:?}", self.platform),
}
}
pub fn emit_futex_requeue(
&self,
src_addr: &str,
dst_addr: &str,
wake_count: u32,
requeue_count: u32,
) -> String {
match self.platform {
X86Platform::Linux => format!(
"; Linux futex_requeue(src={}, dst={}, wake={}, requeue={})\n\
movq ${}, %%rdi ; uaddr (src)\n\
movl ${}, %%esi ; FUTEX_CMP_REQUEUE_PRIVATE\n\
movl ${}, %%edx ; wake_count\n\
movl ${}, %%r10d ; val (unused for non-CMP)\n\
movq ${}, %%r8 ; uaddr2 (dst)\n\
movl ${}, %%r9d ; requeue_count\n\
movl $202, %%eax\n\
syscall",
src_addr,
dst_addr,
wake_count,
requeue_count,
src_addr,
self.ops.futex_cmp_requeue + 128,
wake_count,
requeue_count,
dst_addr,
requeue_count,
),
_ => format!("; Futex requeue not supported on {:?}", self.platform),
}
}
fn emit_wait_on_address(&self, addr: &str, expected: u32) -> String {
format!(
"; Windows WaitOnAddress(addr={}, expected={})\n\
; WaitOnAddress(Address, CompareAddress, AddressSize, dwMilliseconds)\n\
leaq {}, %%rcx\n\
leaq {}, %%rdx ; Can't use immediate; use static storage for expected\n\
movl $4, %%r8d ; AddressSize = sizeof(DWORD)\n\
movl $0xFFFFFFFF, %%r9d ; INFINITE\n\
call WaitOnAddress",
addr, expected, addr, expected
)
}
fn emit_wake_by_address(&self, addr: &str, count: u32) -> String {
if count == 1 {
format!(
"; Windows WakeByAddressSingle(addr={})\n\
leaq {}, %%rcx\n\
call WakeByAddressSingle",
addr, addr
)
} else {
format!(
"; Windows WakeByAddressAll(addr={})\n\
leaq {}, %%rcx\n\
call WakeByAddressAll",
addr, addr
)
}
}
fn emit_ulock_wait(&self, addr: &str, expected: u64) -> String {
format!(
"; macOS __ulock_wait(addr={}, expected={})\n\
; __ulock_wait(operation, addr, value, timeout_microseconds)\n\
movl $0x01000001, %%edi ; ULF_WAIT | ULF_NO_ERRNO\n\
leaq {}, %%rsi\n\
movq ${}, %%rdx\n\
movl $0, %%ecx ; timeout = 0 (wait indefinitely via retry)\n\
; actual syscall number on macOS varies, use wrapper\n\
call ___ulock_wait",
addr, expected, addr, expected
)
}
fn emit_ulock_wake(&self, addr: &str) -> String {
format!(
"; macOS __ulock_wake(addr={})\n\
; __ulock_wake(operation, addr, wake_value)\n\
movl $0x01000002, %%edi ; ULF_WAKE | ULF_NO_ERRNO\n\
leaq {}, %%rsi\n\
movl $0, %%edx ; wake_value = 0\n\
call ___ulock_wake",
addr, addr
)
}
pub fn emit_futex_mutex_lock(&self, mutex_addr: &str) -> String {
format!(
"; Futex-based mutex lock\n\
; Algorithm: try atomic cmpxchg 0→1, if fail, futex_wait\n\
; mutex layout: uint32_t, 0=unlocked, 1=locked(no waiters), 2=locked(waiters)\n\
.L_mutex_lock_{}:\n\
\tmovl $1, %%eax\n\
\tlock cmpxchgl %%eax, [{}] ; try lock (0→1)\n\
\tjz .L_mutex_lock_done_{}\n\
\t; CAS failed — mutex is already locked\n\
\t; Try to transition 1→2 (now with waiters)\n\
\tmovl $2, %%eax\n\
\txchgl %%eax, [{}] ; get current value, set to 2\n\
\ttestl %%eax, %%eax\n\
\tjz .L_mutex_lock_done_{} ; was 0 → we own it now\n\
\t; Wait on the futex\n\
\tmovl $2, %%edx ; expected value = 2 (locked+waiters)\n\
{}\n\
\tjmp .L_mutex_lock_{}\n\
.L_mutex_lock_done_{}:",
hash_fast(mutex_addr),
mutex_addr,
hash_fast(mutex_addr),
mutex_addr,
hash_fast(mutex_addr),
self.emit_futex_wait_simple(mutex_addr, 2),
hash_fast(mutex_addr),
hash_fast(mutex_addr),
)
}
pub fn emit_futex_mutex_unlock(&self, mutex_addr: &str) -> String {
format!(
"; Futex-based mutex unlock\n\
; Decrement mutex (2→0 if no waiters, or 1→0)\n\
lock decl [{}]\n\
jnz .L_mutex_unlock_wake_{}\n\
; If old value was 2, wake one waiter\n\
movl $1, [{}] ; set to unlocked\n\
{}\n\
.L_mutex_unlock_done_{}:",
mutex_addr,
hash_fast(mutex_addr),
mutex_addr,
self.emit_futex_wake_simple(mutex_addr, 1),
hash_fast(mutex_addr),
)
}
pub fn emit_futex_condvar_wait(&self, cv_addr: &str, mutex_addr: &str) -> String {
format!(
"; Futex-based condition variable wait\n\
; The mutex must be held on entry; it is released atomically with the wait.\n\
; condvar layout: uint32_t (sequence number)\n\
movl [{}], %%eax ; read current sequence\n\
; Unlock the mutex\n\
movl $0, [{}]\n\
mfence\n\
; Wait on condvar futex with the read sequence as expected\n\
{}\n\
; Re-acquire the mutex\n\
{}",
cv_addr,
mutex_addr,
self.emit_futex_wait_simple(cv_addr, 0),
self.emit_futex_mutex_lock(mutex_addr),
)
}
pub fn emit_futex_condvar_signal(&self, cv_addr: &str) -> String {
format!(
"; Futex-based condition variable signal\n\
incl [{}] ; increment sequence\n\
{}",
cv_addr,
self.emit_futex_wake_simple(cv_addr, 1),
)
}
pub fn emit_futex_condvar_broadcast(&self, cv_addr: &str) -> String {
format!(
"; Futex-based condition variable broadcast\n\
incl [{}] ; increment sequence\n\
{}",
cv_addr,
self.emit_futex_wake_simple(cv_addr, u32::MAX),
)
}
pub fn emit_futex_semaphore_wait(&self, sem_addr: &str) -> String {
format!(
"; Futex-based semaphore wait (P/down)\n\
; semaphore layout: uint32_t count\n\
; Algorithm: try to decrement if > 0, else futex_wait\n\
.L_sem_wait_{}:\n\
\tmovl [{}], %%eax\n\
\ttestl %%eax, %%eax\n\
\tjz .L_sem_wait_slow_{}\n\
\t; Try to decrement\n\
\tleal -1(%%eax), %%edx\n\
\tlock cmpxchgl %%edx, [{}]\n\
\tjnz .L_sem_wait_{}\n\
\tjmp .L_sem_wait_done_{}\n\
.L_sem_wait_slow_{}:\n\
\t; Count is 0, need to wait\n\
{}\n\
\tjmp .L_sem_wait_{}\n\
.L_sem_wait_done_{}:",
hash_fast(sem_addr),
sem_addr,
hash_fast(sem_addr),
sem_addr,
hash_fast(sem_addr),
hash_fast(sem_addr),
hash_fast(sem_addr),
self.emit_futex_wait_simple(sem_addr, 0),
hash_fast(sem_addr),
hash_fast(sem_addr),
)
}
pub fn emit_futex_semaphore_post(&self, sem_addr: &str) -> String {
format!(
"; Futex-based semaphore post (V/up)\n\
lock incl [{}] ; increment count\n\
{}",
sem_addr,
self.emit_futex_wake_simple(sem_addr, 1),
)
}
fn emit_futex_wait_simple(&self, addr: &str, expected: u32) -> String {
match self.platform {
X86Platform::Linux => format!(
"\tmovq ${}, %%rdi\n\
\tmovl ${}, %%esi\n\
\tmovl $128, %%edx\n\
\txorl %%r10d, %%r10d\n\
\tmovl $202, %%eax\n\
\tsyscall",
addr, expected
),
_ => format!("; Simple futex wait unsupported for {:?}", self.platform),
}
}
fn emit_futex_wake_simple(&self, addr: &str, count: u32) -> String {
match self.platform {
X86Platform::Linux => format!(
"\tmovq ${}, %%rdi\n\
\tmovl $129, %%esi\n\
\tmovl ${}, %%edx\n\
\tmovl $202, %%eax\n\
\tsyscall",
addr, count
),
_ => format!("; Simple futex wake unsupported for {:?}", self.platform),
}
}
}
fn hash_fast(s: &str) -> u64 {
let mut h: u64 = 14695981039346656037; for b in s.bytes() {
h ^= b as u64;
h = h.wrapping_mul(1099511628211); }
h
}
#[derive(Debug, Clone)]
pub struct X86SpinLock {
pub lock_type: X86SpinLockType,
pub use_pause: bool,
pub use_exp_backoff: bool,
pub base_spin_count: u32,
pub max_spin_count: u32,
pub use_mwait: bool,
pub cache_line_pad: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SpinLockType {
TestAndSet,
Ticket,
MCS,
Adaptive,
}
impl fmt::Display for X86SpinLockType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TestAndSet => write!(f, "tas"),
Self::Ticket => write!(f, "ticket"),
Self::MCS => write!(f, "mcs"),
Self::Adaptive => write!(f, "adaptive"),
}
}
}
impl Default for X86SpinLock {
fn default() -> Self {
Self {
lock_type: X86SpinLockType::Adaptive,
use_pause: true,
use_exp_backoff: true,
base_spin_count: 16,
max_spin_count: 4096,
use_mwait: false,
cache_line_pad: true,
}
}
}
impl X86SpinLock {
pub fn emit_tas_lock_acquire(&self, lock_addr: &str) -> String {
let pause_instr = if self.use_pause { "pause\n" } else { "" };
let backoff = if self.use_exp_backoff {
self.emit_exp_backoff_loop(lock_addr, "tas")
} else {
format!(
".L_tas_spin_{}:\n\
{}lock btsl $0, [{}] ; test-and-set\n\
jnc .L_tas_acquired_{}\n\
jmp .L_tas_spin_{}\n\
.L_tas_acquired_{}:",
hash_fast(lock_addr),
pause_instr,
lock_addr,
hash_fast(lock_addr),
hash_fast(lock_addr),
hash_fast(lock_addr),
)
};
format!(
"; Test-and-set spin lock acquire ({})\n{}",
lock_addr, backoff
)
}
fn emit_exp_backoff_loop(&self, lock_addr: &str, label_prefix: &str) -> String {
let h = hash_fast(lock_addr);
format!(
"; Exponential backoff spin loop\n\
movl $1, %%ecx ; initial spin count\n\
.L_{}_spin_{}:\n\
{}lock btsl $0, [{}]\n\
jnc .L_{}_acquired_{}\n\
; Backoff: spin for ecx iterations\n\
.L_{}_backoff_{}:\n\
pause ; PAUSE for ~140 cycles on Skylake\n\
decl %%ecx\n\
jnz .L_{}_backoff_{}\n\
; Double the spin count (exponential backoff)\n\
shll $1, %%ecx\n\
cmpl ${}, %%ecx\n\
jbe .L_{}_spin_{}\n\
movl ${}, %%ecx ; cap at max\n\
jmp .L_{}_spin_{}\n\
.L_{}_acquired_{}:",
label_prefix,
h,
pause_instr(self.use_pause),
lock_addr,
label_prefix,
h,
label_prefix,
h,
label_prefix,
h,
self.max_spin_count,
label_prefix,
h,
self.max_spin_count,
label_prefix,
h,
label_prefix,
h,
)
}
pub fn emit_tas_lock_release(&self, lock_addr: &str) -> String {
format!(
"; Test-and-set spin lock release ({})\n\
movl $0, [{}] ; clear the lock\n\
; No fence needed on X86 — store ordering ensures visibility",
lock_addr, lock_addr
)
}
pub fn emit_ticket_lock_acquire(&self, lock_addr: &str) -> String {
let h = hash_fast(lock_addr);
format!(
"; Ticket spin lock acquire ({})\n\
; Lock layout: uint16_t next_ticket, uint16_t now_serving\n\
; Get a ticket\n\
movw $1, %%ax\n\
lock xaddw %%ax, [{}] ; fetch-and-add next_ticket\n\
; %%ax now holds our ticket\n\
movzwl %%ax, %%ecx\n\
; Spin until now_serving == our ticket\n\
.L_ticket_spin_{}:\n\
movzwl 2+[{}], %%edx ; load now_serving\n\
cmpl %%ecx, %%edx\n\
je .L_ticket_acquired_{}\n\
{}\
jmp .L_ticket_spin_{}\n\
.L_ticket_acquired_{}:",
lock_addr,
lock_addr,
h,
lock_addr,
h,
pause_instr(self.use_pause),
h,
h,
)
}
pub fn emit_ticket_lock_release(&self, lock_addr: &str) -> String {
format!(
"; Ticket spin lock release ({})\n\
incw [{} + 2] ; increment now_serving\n\
; The increment is atomic on X86 for aligned word accesses",
lock_addr, lock_addr
)
}
pub fn emit_mcs_lock_acquire(
&self,
lock_addr: &str,
node_addr: &str, ) -> String {
let h = hash_fast(lock_addr);
format!(
"; MCS lock acquire ({{}})\n\
; Each thread has a local mcs_node (16 bytes):\n\
; uint64_t next (pointer to next node)\n\
; uint32_t locked (1 = spinning, 0 = acquired)\n\
; Lock itself is a pointer to the tail of the queue\n\
; Step 1: Initialize our node\n\
movq $0, 0({}) ; node->next = NULL\n\
movl $1, 8({}) ; node->locked = 1\n\
; Step 2: Atomically swap our node with the tail\n\
movq {}, %%rax ; our node address\n\
xchgq %%rax, [{}] ; swap with lock tail pointer\n\
; Now %%rax = previous tail (NULL if lock was free)\n\
testq %%rax, %%rax\n\
jz .L_mcs_acquired_{}\n\
; Step 3: Link ourselves into the queue\n\
movq {}, 0(%%rax) ; prev->next = our node\n\
; Step 4: Spin on our node->locked\n\
.L_mcs_spin_{}:\n\
movl $1, %%ecx\n\
lock cmpxchgl %%ecx, 8({})\n\
; If cmpxchg succeeded (value was 1), keep spinning\n\
; If failed (value was 0), we are now the lock holder\n\
jz .L_mcs_spin_{}\n\
; value changed to 0: unlocked → proceed\n\
.L_mcs_acquired_{}:",
lock_addr, h, node_addr, node_addr, node_addr, lock_addr, h, node_addr, h, h,
)
}
pub fn emit_mcs_lock_release(&self, lock_addr: &str, node_addr: &str) -> String {
let h = hash_fast(lock_addr);
format!(
"; MCS lock release ({})\n\
; Check if there is a next node\n\
movq 0({}), %%rax\n\
testq %%rax, %%rax\n\
jnz .L_mcs_release_has_next_{}\n\
; No next node: try to CAS the tail from our node to NULL\n\
movq {}, %%rcx ; our node address\n\
movq $0, %%rax\n\
lock cmpxchgq %%rax, [{}] ; if tail == our node, tail = NULL\n\
jz .L_mcs_release_done_{}\n\
; CAS failed: another thread is being added.\n\
; Wait for the new thread to set our node->next\n\
.L_mcs_wait_next_{}:\n\
movq 0({}), %%rax\n\
testq %%rax, %%rax\n\
jz .L_mcs_wait_next_{}\n\
.L_mcs_release_has_next_{}:\n\
; Unlock the next thread\n\
movl $0, 8(%%rax) ; next->locked = 0\n\
.L_mcs_release_done_{}:",
lock_addr, h, node_addr, h, node_addr, lock_addr, h, h, node_addr, h, h,
)
}
pub fn emit_adaptive_lock_acquire(&self, lock_addr: &str) -> String {
format!(
"; Adaptive spin lock acquire ({})\n\
; First attempt: fast TAS path\n\
lock btsl $0, [{}]\n\
jnc .L_adaptive_acquired_{}\n\
; TAS failed — fall back to ticket-based path with backoff\n\
{}",
lock_addr,
lock_addr,
hash_fast(lock_addr),
self.emit_ticket_lock_acquire(lock_addr),
)
}
pub fn emit_adaptive_lock_release(&self, lock_addr: &str) -> String {
self.emit_ticket_lock_release(lock_addr)
}
pub fn emit_try_acquire(&self, lock_addr: &str) -> String {
match self.lock_type {
X86SpinLockType::TestAndSet => format!(
"; Try acquire TAS ({})\n\
lock btsl $0, [{}] ; test-and-set\n\
setnc %%al ; %%al = 1 if acquired, 0 if not\n\
movzbl %%al, %%eax",
lock_addr, lock_addr
),
X86SpinLockType::Ticket => format!(
"; Try acquire ticket ({})\n\
; Load next_ticket and now_serving\n\
movl [{}], %%eax ; both 16-bit fields in one 32-bit load\n\
movzwl %%ax, %%ecx ; next_ticket\n\
shrl $16, %%eax ; now_serving\n\
cmpl %%ecx, %%eax\n\
jne .L_try_acquire_fail_{}\n\
; They're equal — try to advance next_ticket\n\
leal 1(%%ecx), %%edx\n\
movw %%dx, %%ax ; new next_ticket in low 16 bits\n\
shll $16, %%eax ; keep now_serving in high 16 bits\n\
lock cmpxchgl %%eax, [{}]\n\
setne %%al\n\
jmp .L_try_acquire_done_{}\n\
.L_try_acquire_fail_{}:\n\
movb $0, %%al\n\
.L_try_acquire_done_{}:",
lock_addr,
lock_addr,
hash_fast(lock_addr),
lock_addr,
hash_fast(lock_addr),
hash_fast(lock_addr),
hash_fast(lock_addr),
),
_ => format!(
"; Try acquire — using TAS fallback\n\
lock btsl $0, [{}]\n\
setnc %%al\n\
movzbl %%al, %%eax",
lock_addr,
),
}
}
}
fn pause_instr(use_pause: bool) -> &'static str {
if use_pause {
"pause\n\t"
} else {
""
}
}
#[derive(Debug, Clone)]
pub struct X86LockElision {
pub enabled: bool,
pub use_hle: bool,
pub use_rtm: bool,
pub max_retry: u32,
pub adaptive_retry: bool,
pub abort_reasons: X86TsxAbortReasons,
pub status_bits: X86TsxStatusBits,
pub has_xtest: bool,
}
#[derive(Debug, Clone)]
pub struct X86TsxAbortReasons {
pub xabort_explicit: u32,
pub xabort_ret: u32,
pub xabort_retry: u32,
pub xabort_capacity: u32,
pub xabort_conflict: u32,
pub xabort_debug: u32,
pub xabort_nested: u32,
}
impl Default for X86TsxAbortReasons {
fn default() -> Self {
Self {
xabort_explicit: 1 << 0,
xabort_ret: 1 << 1,
xabort_retry: 1 << 2,
xabort_capacity: 1 << 3,
xabort_conflict: 1 << 4,
xabort_debug: 1 << 5,
xabort_nested: 1 << 6,
}
}
}
#[derive(Debug, Clone)]
pub struct X86TsxStatusBits {
pub tsx_hw_status: bool,
pub tsx_async_abort: bool,
}
impl Default for X86TsxStatusBits {
fn default() -> Self {
Self {
tsx_hw_status: true,
tsx_async_abort: true,
}
}
}
impl X86LockElision {
pub fn new(has_tsx: bool) -> Self {
Self {
enabled: has_tsx,
use_hle: true,
use_rtm: true,
max_retry: 5,
adaptive_retry: true,
abort_reasons: X86TsxAbortReasons::default(),
status_bits: X86TsxStatusBits::default(),
has_xtest: has_tsx,
}
}
pub fn emit_hle_acquire(&self, lock_addr: &str) -> String {
if !self.enabled || !self.use_hle {
return format!(
"; HLE disabled — falling back to regular lock\n\
lock btsl $0, [{}]",
lock_addr
);
}
format!(
"; HLE XACQUIRE: speculative lock acquire ({})\n\
xacquire lock btsl $0, [{}] ; begin elision\n\
; If the transaction aborts (cache conflict, etc.),\n\
; the LOCK becomes a regular non-speculative lock.",
lock_addr, lock_addr
)
}
pub fn emit_hle_release(&self, lock_addr: &str) -> String {
if !self.enabled || !self.use_hle {
return format!(
"; HLE disabled\n\
movl $0, [{}]",
lock_addr
);
}
format!(
"; HLE XRELEASE: end speculative lock ({})\n\
xrelease movl $0, [{}] ; commit elision immediately",
lock_addr, lock_addr
)
}
pub fn emit_rtm_spin_lock_acquire(&self, lock_addr: &str) -> String {
if !self.enabled || !self.use_rtm {
return format!(
"; RTM disabled\n\
lock btsl $0, [{}]",
lock_addr
);
}
let h = hash_fast(lock_addr);
format!(
"; RTM-based spin lock acquire ({{}})\n\
; Transaction retry counter\n\
movl $0, %%r15d ; rtx_retry_count = 0\n\
.L_rtm_retry_{}:\n\
; Check if lock is free before entering transaction\n\
movl [{}], %%eax\n\
testl %%eax, %%eax\n\
jnz .L_rtm_fallback_{}\n\
; Begin transaction — abort handler at .L_rtm_abort\n\
xbegin .L_rtm_abort_{}\n\
; Check if lock is still free (transactionally)\n\
movl [{}], %%eax\n\
testl %%eax, %%eax\n\
jnz .L_rtm_abort_contended_{}\n\
; Lock is free — we hold it speculatively\n\
; The lock value is NOT modified (no write to cache line)\n\
; Other threads see the lock as still free\n\
; Set a flag in our local memory that we hold the lock\n\
movl $1, [{}+64] ; local lock flag (cache line 2)\n\
; Transaction committed — we hold the lock\n\
xend\n\
jmp .L_rtm_acquired_{}\n\
.L_rtm_abort_{}:\n\
; Transaction aborted — check EAX for reason\n\
; Common abort reasons:\n\
; bit 0: explicit XABORT\n\
; bit 1: explicit XABORT (argument in ESI)\n\
; bit 2: retry possible\n\
; bit 3: capacity (cache overflow)\n\
; bit 4: conflict (data conflict)\n\
testl $0x18, %%eax ; capacity or conflict\n\
jnz .L_rtm_retry_check_{}\n\
; Unknown abort — fallback to regular lock\n\
.L_rtm_abort_contended_{}:\n\
xend ; try to commit (will succeed on XTEST)\n\
.L_rtm_fallback_{}:\n\
; Fallback: acquire regular lock\n\
lock btsl $0, [{}]\n\
jc .L_rtm_spin_{}\n\
movl $1, [{}+64]\n\
jmp .L_rtm_acquired_{}\n\
.L_rtm_retry_check_{}:\n\
incl %%r15d\n\
cmpl ${}, %%r15d\n\
jae .L_rtm_abort_contended_{}\n\
; Exponential backoff for retry\n\
movl %%r15d, %%ecx\n\
.L_rtm_backoff_{}:\n\
pause\n\
decl %%ecx\n\
jnz .L_rtm_backoff_{}\n\
jmp .L_rtm_retry_{}\n\
.L_rtm_spin_{}:\n\
pause\n\
movl [{}], %%eax\n\
testl %%eax, %%eax\n\
jnz .L_rtm_spin_{}\n\
jmp .L_rtm_fallback_{}\n\
.L_rtm_acquired_{}:",
lock_addr,
h,
h,
lock_addr,
h,
h,
lock_addr,
h,
lock_addr,
h,
h,
lock_addr,
h,
h,
h,
h,
h,
lock_addr,
h,
lock_addr,
h,
h,
self.max_retry,
h,
h,
h,
h,
)
}
pub fn emit_rtm_spin_lock_release(&self, lock_addr: &str) -> String {
let h = hash_fast(lock_addr);
format!(
"; RTM spin lock release ({})\n\
; Check if we hold the lock speculatively (via RTM)\n\
xtest ; ZF = 0 if in transaction\n\
jz .L_rtm_release_transactional_{}\n\
; Non-transactional (fallback path): release normally\n\
movl $0, [{}+64] ; clear local flag\n\
movl $0, [{}] ; clear the lock\n\
jmp .L_rtm_release_done_{}\n\
.L_rtm_release_transactional_{}:\n\
; Transactional: just commit\n\
xend ; commit transaction — lock becomes visible\n\
.L_rtm_release_done_{}:",
lock_addr, h, h, lock_addr, lock_addr, h, h,
)
}
pub fn emit_xtest_check(&self, dst_reg: &str) -> String {
format!(
"; XTEST — check if in transactional region\n\
xtest\n\
setnz {} ; 1 if in transaction, 0 if not",
dst_reg
)
}
pub fn emit_xabort(&self, reason: u8) -> String {
format!(
"xabort ${} ; abort transaction with reason code {}",
reason, reason
)
}
pub fn hle_semantics(&self) -> &'static str {
"HLE (Hardware Lock Elision):\n\
- XACQUIRE prefix (F2): begins speculative lock acquisition\n\
- XRELEASE prefix (F3): ends speculative lock, commits elision\n\
- On abort: falls back to non-speculative lock acquire\n\
- Compatible with legacy processors (prefixes ignored as NOP)\n\
- Best for short critical sections with low contention"
}
pub fn rtm_semantics(&self) -> &'static str {
"RTM (Restricted Transactional Memory):\n\
- XBEGIN rel32: start transaction; EIP on abort = rel32\n\
- XEND: commit transaction successfully\n\
- XABORT imm8: abort transaction with 8-bit reason code\n\
- XTEST: check if in transaction (ZF=0 means in transaction)\n\
- EAX on abort: reason bitmap\n\
- Nested XBEGIN allowed (up to implementation limit)\n\
- Transactions abort on: cache overflow, memory conflicts,\n\
interrupts, system calls, unsupported instructions"
}
pub fn emit_fallback_acquire(&self, lock_addr: &str) -> String {
format!(
"; Transaction abort fallback — regular spin lock ({})\n\
; Wait for lock to be free, then acquire\n\
.L_fallback_spin_{}:\n\
pause\n\
movl [{}], %%eax\n\
testl %%eax, %%eax\n\
jnz .L_fallback_spin_{}\n\
lock btsl $0, [{}]\n\
jc .L_fallback_spin_{}",
lock_addr,
hash_fast(lock_addr),
lock_addr,
hash_fast(lock_addr),
lock_addr,
hash_fast(lock_addr),
)
}
}
#[derive(Debug, Clone)]
pub struct X86Barrier {
pub arch: X86Arch,
pub has_sse2: bool,
pub use_lock_add_barrier: bool,
pub spectre_v1_mitigation: bool,
pub spectre_v2_mitigation: bool,
pub style: X86BarrierStyle,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86BarrierStyle {
Fence,
LockAdd,
CpuId,
Hybrid,
}
impl Default for X86BarrierStyle {
fn default() -> Self {
Self::Hybrid
}
}
impl X86Barrier {
pub fn new_x86_64() -> Self {
Self {
arch: X86Arch::X86_64,
has_sse2: true,
use_lock_add_barrier: false,
spectre_v1_mitigation: false,
spectre_v2_mitigation: false,
style: X86BarrierStyle::Hybrid,
}
}
pub fn new_x86_32() -> Self {
Self {
arch: X86Arch::X86_32,
has_sse2: true,
use_lock_add_barrier: false,
spectre_v1_mitigation: false,
spectre_v2_mitigation: false,
style: X86BarrierStyle::Fence,
}
}
pub fn emit_compiler_barrier(&self) -> &'static str {
"# compiler_barrier (asm volatile(\"\" ::: \"memory\"))"
}
pub fn emit_full_barrier(&self) -> String {
match self.style {
X86BarrierStyle::Fence => {
if self.has_sse2 {
"mfence ; full memory barrier".into()
} else {
self.emit_lock_add_barrier()
}
}
X86BarrierStyle::LockAdd => self.emit_lock_add_barrier(),
X86BarrierStyle::CpuId => self.emit_cpuid_barrier(),
X86BarrierStyle::Hybrid => {
if self.has_sse2 {
"mfence ; full memory barrier".into()
} else {
self.emit_lock_add_barrier()
}
}
}
}
pub fn emit_read_barrier(&self) -> String {
let mut result = String::new();
if self.spectre_v1_mitigation {
result.push_str("lfence ; read barrier + speculation barrier (Spectre v1)\n");
}
if self.has_sse2 {
result.push_str("lfence ; read barrier\n");
} else {
result.push_str("; LFENCE not available — using compiler barrier only\n");
result.push_str("# compiler_barrier\n");
}
result
}
pub fn emit_write_barrier(&self) -> String {
if self.has_sse2 {
"sfence ; write barrier (store ordering)".into()
} else {
"; SFENCE not available — using compiler barrier\n# compiler_barrier".into()
}
}
pub fn emit_store_load_barrier(&self) -> String {
self.emit_full_barrier()
}
pub fn emit_data_dependency_barrier(&self) -> &'static str {
"; Data dependency barrier — compiler barrier only on X86\n# compiler_barrier"
}
fn emit_lock_add_barrier(&self) -> String {
match self.arch {
X86Arch::X86_64 => "lock addq $0, 0(%rsp) ; full barrier (lock add)".into(),
X86Arch::X86_32 => "lock addl $0, 0(%esp) ; full barrier (lock add)".into(),
X86Arch::X86_16 => "lock addw $0, 0(%sp) ; full barrier (lock add)".into(),
}
}
fn emit_cpuid_barrier(&self) -> String {
match self.arch {
X86Arch::X86_64 => "pushq %rbx\n\
\txorl %eax, %eax\n\
\tcpuid ; serializing barrier\n\
\tpopq %rbx"
.into(),
X86Arch::X86_32 => "pushl %ebx\n\
\txorl %eax, %eax\n\
\tcpuid ; serializing barrier\n\
\tpopl %ebx"
.into(),
X86Arch::X86_16 => "pushw %bx\n\
\txorw %ax, %ax\n\
\tcpuid ; serializing barrier\n\
\tpopw %bx"
.into(),
}
}
pub fn emit_lock_or_barrier(&self, scratch_addr: &str) -> String {
match self.arch {
X86Arch::X86_64 => format!("lock orq $0, [{}] ; full barrier (lock or)", scratch_addr),
X86Arch::X86_32 => format!("lock orl $0, [{}] ; full barrier (lock or)", scratch_addr),
X86Arch::X86_16 => format!("lock orw $0, [{}] ; full barrier (lock or)", scratch_addr),
}
}
pub fn emit_non_temporal_barrier(&self, addr: &str) -> String {
format!(
"; Non-temporal store barrier\n\
clflushopt [{}] ; flush cache line (optimized)\n\
sfence ; order the flush",
addr
)
}
pub fn emit_nt_store_barrier(&self) -> String {
if self.has_sse2 {
"sfence ; non-temporal store barrier".into()
} else {
"# compiler_barrier ; non-temporal store barrier (no SFENCE)".into()
}
}
pub fn fence_comparison(&self) -> &'static str {
"X86 Fence Comparison:\n\
- MFENCE: full barrier — serializes ALL loads and stores.\n\
Prevents Store→Load, Store→Store, Load→Load, Load→Store reordering.\n\
Used for seq_cst atomics.\n\
- LFENCE: load barrier — serializes LOAD operations only.\n\
Used for acquire semantics + Spectre mitigation.\n\
- SFENCE: store barrier — serializes STORE operations only.\n\
Used for non-temporal stores, write-combining memory.\n\
- LOCK prefix: implies full barrier on most CPUs.\n\
Faster than MFENCE on some uarchs."
}
}
#[derive(Debug, Clone)]
pub struct X86VolatileOps {
pub arch: X86Arch,
pub volatile_loads_acquire: bool,
pub volatile_stores_release: bool,
pub asm_style: X86VolatileAsmStyle,
pub use_memory_clobber: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86VolatileAsmStyle {
GCC,
MSVC,
LLVM,
}
impl Default for X86VolatileAsmStyle {
fn default() -> Self {
Self::GCC
}
}
impl X86VolatileOps {
pub fn new_x86_64() -> Self {
Self {
arch: X86Arch::X86_64,
volatile_loads_acquire: true,
volatile_stores_release: true,
asm_style: X86VolatileAsmStyle::GCC,
use_memory_clobber: true,
}
}
pub fn new_x86_32() -> Self {
Self {
arch: X86Arch::X86_32,
volatile_loads_acquire: true,
volatile_stores_release: true,
asm_style: X86VolatileAsmStyle::GCC,
use_memory_clobber: true,
}
}
pub fn emit_volatile_load(&self, dst: &str, src: &str, ty: &str) -> String {
let mov_suffix = match ty {
"i8" | "i8*" | "char" => "b",
"i16" | "short" => "w",
"i32" | "int" | "float" => "l",
"i64" | "long" | "double" | "ptr" | "i64*" => "q",
_ => "q",
};
let acquire = if self.volatile_loads_acquire {
" ; acquire semantics via compiler barrier"
} else {
""
};
format!(
"; volatile load {} = *{}{}\n\
# compiler_barrier\n\
mov{} {}, [{}]",
dst, src, acquire, mov_suffix, dst, src
)
}
pub fn emit_volatile_store(&self, dst: &str, src: &str, ty: &str) -> String {
let mov_suffix = match ty {
"i8" | "i8*" | "char" => "b",
"i16" | "short" => "w",
"i32" | "int" | "float" => "l",
"i64" | "long" | "double" | "ptr" | "i64*" => "q",
_ => "q",
};
let release = if self.volatile_stores_release {
" ; release semantics via compiler barrier"
} else {
""
};
format!(
"; volatile store *{} = {}{}\n\
mov{} [{}], {}\n\
# compiler_barrier",
dst, src, release, mov_suffix, dst, src
)
}
pub fn emit_volatile_rmw(&self, addr: &str, operation: &str, ty: &str) -> String {
format!(
"; volatile RMW at {}\n\
# compiler_barrier\n\
lock {} [{}] ; locked volatile RMW\n\
# compiler_barrier",
addr, operation, addr
)
}
pub fn emit_gcc_asm_volatile(
&self,
asm_template: &str,
outputs: &[(String, String)],
inputs: &[(String, String)],
) -> String {
let mut code = String::new();
code.push_str("asm volatile(\n");
code.push_str(&format!(" \"{}\"\n", asm_template));
if !outputs.is_empty() {
code.push_str(" : ");
let out_strs: Vec<String> = outputs
.iter()
.map(|(constraint, var)| format!("\"{}\" ({})", constraint, var))
.collect();
code.push_str(&out_strs.join(", "));
}
if !inputs.is_empty() {
code.push_str("\n : ");
let in_strs: Vec<String> = inputs
.iter()
.map(|(constraint, var)| format!("\"{}\" ({})", constraint, var))
.collect();
code.push_str(&in_strs.join(", "));
}
if self.use_memory_clobber {
code.push_str("\n : \"memory\"");
}
code.push_str("\n);");
code
}
pub fn emit_msvc_asm_volatile(&self, instructions: &str) -> String {
format!(
"; MSVC volatile __asm\n\
__asm {{\n\
{}\n\
}}",
instructions
)
}
pub fn emit_llvm_asm_volatile(&self, asm_str: &str, constraints: &str) -> String {
format!(
"call void asm sideeffect \"{}\", \"{}\"()",
asm_str, constraints
)
}
pub fn emit_mmio_read(&self, dst: &str, addr: &str, size_bytes: u8) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"; MMIO volatile read from {}\n\
# compiler_barrier\n\
mov{} {}, [{}] ; read from MMIO\n\
; Additional ordering: LFENCE may be needed for some devices\n\
# compiler_barrier",
addr, suffix, dst, addr
)
}
pub fn emit_mmio_write(&self, addr: &str, src: &str, size_bytes: u8) -> String {
let suffix = size_to_mov_suffix(size_bytes);
format!(
"; MMIO volatile write to {}\n\
# compiler_barrier\n\
mov{} [{}], {} ; write to MMIO\n\
sfence ; ensure write is flushed to device\n\
# compiler_barrier",
addr, suffix, addr, src
)
}
pub fn emit_inline_asm_volatile_load(
&self,
dst: &str,
src_ptr: &str,
size_bytes: u8,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
let constraint = match size_bytes {
1 => "=r",
2 => "=r",
4 => "=r",
8 => "=r",
_ => "=r",
};
match self.asm_style {
X86VolatileAsmStyle::GCC => self.emit_gcc_asm_volatile(
&format!("mov{} %1, %0", suffix),
&[(constraint.to_string(), dst.to_string())],
&[("m".to_string(), format!("*({})", src_ptr))],
),
X86VolatileAsmStyle::MSVC => {
self.emit_msvc_asm_volatile(&format!("mov {}, dword ptr [{}]", dst, src_ptr))
}
X86VolatileAsmStyle::LLVM => self.emit_llvm_asm_volatile(
&format!("mov{} $$1, $0", suffix),
&format!("=*m,*m,~{{memory}}"),
),
}
}
pub fn emit_inline_asm_volatile_store(
&self,
dst_ptr: &str,
src: &str,
size_bytes: u8,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
match self.asm_style {
X86VolatileAsmStyle::GCC => self.emit_gcc_asm_volatile(
&format!("mov{} %0, %1", suffix),
&[],
&[
("r".to_string(), src.to_string()),
("=m".to_string(), format!("*({})", dst_ptr)),
],
),
X86VolatileAsmStyle::MSVC => {
self.emit_msvc_asm_volatile(&format!("mov dword ptr [{}], {}", dst_ptr, src))
}
X86VolatileAsmStyle::LLVM => self.emit_llvm_asm_volatile(
&format!("mov{} $1, $$0", suffix),
&format!("=*m,*m,~{{memory}}"),
),
}
}
pub fn constraint_reference(&self) -> &'static str {
"X86 Inline Asm Constraints:\n\
- \"r\": any general-purpose register\n\
- \"a\": RAX/EAX/AX/AL\n\
- \"b\": RBX/EBX/BX/BL\n\
- \"c\": RCX/ECX/CX/CL\n\
- \"d\": RDX/EDX/DX/DL\n\
- \"S\": RSI/ESI/SI\n\
- \"D\": RDI/EDI/DI\n\
- \"m\": memory operand (any addressing mode)\n\
- \"=m\": output memory operand\n\
- \"+m\": read-write memory operand\n\
- \"i\": immediate integer operand\n\
- \"n\": immediate integer with known value\n\
- \"memory\": clobbers memory (compiler barrier)"
}
}
#[derive(Debug, Clone)]
pub struct X86AtomicOperand {
pub size_bytes: u8,
pub is_aligned: bool,
pub alignment: u8,
pub is_lock_free: bool,
pub ordering: X86AtomicOrdering,
pub op_kind: X86AtomicOpKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86AtomicOpKind {
Load,
Store,
Exchange,
CompareExchange,
FetchAdd,
FetchSub,
FetchAnd,
FetchOr,
FetchXor,
FetchNand,
FetchMax,
FetchMin,
FetchUMax,
FetchUMin,
TestAndSet,
Clear,
}
impl fmt::Display for X86AtomicOpKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Load => write!(f, "load"),
Self::Store => write!(f, "store"),
Self::Exchange => write!(f, "exchange"),
Self::CompareExchange => write!(f, "compare_exchange"),
Self::FetchAdd => write!(f, "fetch_add"),
Self::FetchSub => write!(f, "fetch_sub"),
Self::FetchAnd => write!(f, "fetch_and"),
Self::FetchOr => write!(f, "fetch_or"),
Self::FetchXor => write!(f, "fetch_xor"),
Self::FetchNand => write!(f, "fetch_nand"),
Self::FetchMax => write!(f, "fetch_max"),
Self::FetchMin => write!(f, "fetch_min"),
Self::FetchUMax => write!(f, "fetch_umax"),
Self::FetchUMin => write!(f, "fetch_umin"),
Self::TestAndSet => write!(f, "test_and_set"),
Self::Clear => write!(f, "clear"),
}
}
}
impl X86AtomicOperand {
pub fn new(
size_bytes: u8,
alignment: u8,
ordering: X86AtomicOrdering,
op_kind: X86AtomicOpKind,
model: &X86AtomicModel,
) -> Self {
let is_lock_free = model.is_lock_free(size_bytes);
let is_aligned = alignment >= size_bytes;
Self {
size_bytes,
is_aligned,
alignment,
is_lock_free,
ordering,
op_kind,
}
}
pub fn suffix(&self) -> &'static str {
size_to_mov_suffix(self.size_bytes)
}
pub fn needs_lock_prefix(&self) -> bool {
matches!(
self.op_kind,
X86AtomicOpKind::Exchange
| X86AtomicOpKind::CompareExchange
| X86AtomicOpKind::FetchAdd
| X86AtomicOpKind::FetchSub
| X86AtomicOpKind::FetchAnd
| X86AtomicOpKind::FetchOr
| X86AtomicOpKind::FetchXor
| X86AtomicOpKind::FetchNand
| X86AtomicOpKind::FetchMax
| X86AtomicOpKind::FetchMin
| X86AtomicOpKind::FetchUMax
| X86AtomicOpKind::FetchUMin
| X86AtomicOpKind::TestAndSet
)
}
pub fn needs_cas_loop(&self) -> bool {
matches!(
self.op_kind,
X86AtomicOpKind::FetchAnd
| X86AtomicOpKind::FetchOr
| X86AtomicOpKind::FetchXor
| X86AtomicOpKind::FetchNand
| X86AtomicOpKind::FetchMax
| X86AtomicOpKind::FetchMin
| X86AtomicOpKind::FetchUMax
| X86AtomicOpKind::FetchUMin
)
}
}
#[derive(Debug, Clone)]
pub struct X86C11AtomicMapping {
pub model: X86AtomicModel,
pub memory_order: X86MemoryOrder,
pub sequence_cache: HashMap<String, String>,
}
impl X86C11AtomicMapping {
pub fn new(model: X86AtomicModel, memory_order: X86MemoryOrder) -> Self {
Self {
model,
memory_order,
sequence_cache: HashMap::new(),
}
}
pub fn c11_atomic_load(
&self,
dst: &str,
obj: &str,
size_bytes: u8,
order: X86AtomicOrdering,
) -> String {
self.model.emit_atomic_load(dst, obj, size_bytes, order)
}
pub fn c11_atomic_store(
&self,
obj: &str,
desired: &str,
size_bytes: u8,
order: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_store(desired, obj, size_bytes, order)
}
pub fn c11_atomic_exchange(
&self,
dst: &str,
obj: &str,
desired: &str,
size_bytes: u8,
order: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_exchange(dst, obj, desired, size_bytes, order)
}
pub fn c11_atomic_compare_exchange_strong(
&self,
obj: &str,
expected: &str,
desired: &str,
size_bytes: u8,
success: X86AtomicOrdering,
failure: X86AtomicOrdering,
) -> String {
self.model.emit_atomic_compare_exchange(
obj, expected, desired, size_bytes, success, failure, false, )
}
pub fn c11_atomic_compare_exchange_weak(
&self,
obj: &str,
expected: &str,
desired: &str,
size_bytes: u8,
success: X86AtomicOrdering,
failure: X86AtomicOrdering,
) -> String {
self.model.emit_atomic_compare_exchange(
obj, expected, desired, size_bytes, success, failure,
true, )
}
pub fn c11_atomic_fetch_add(
&self,
dst: &str,
obj: &str,
val: &str,
size_bytes: u8,
order: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_fetch_add(dst, obj, val, size_bytes, order)
}
pub fn c11_atomic_fetch_sub(
&self,
dst: &str,
obj: &str,
val: &str,
size_bytes: u8,
order: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_fetch_sub(dst, obj, val, size_bytes, order)
}
pub fn c11_atomic_fetch_and(
&self,
dst: &str,
obj: &str,
val: &str,
size_bytes: u8,
order: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_fetch_and(dst, obj, val, size_bytes, order)
}
pub fn c11_atomic_fetch_or(
&self,
dst: &str,
obj: &str,
val: &str,
size_bytes: u8,
order: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_fetch_or(dst, obj, val, size_bytes, order)
}
pub fn c11_atomic_fetch_xor(
&self,
dst: &str,
obj: &str,
val: &str,
size_bytes: u8,
order: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_fetch_xor(dst, obj, val, size_bytes, order)
}
pub fn c11_atomic_thread_fence(&self, order: X86AtomicOrdering) -> String {
match order {
X86AtomicOrdering::Relaxed => "; atomic_thread_fence(relaxed) — nothing on X86".into(),
X86AtomicOrdering::Acquire | X86AtomicOrdering::Consume => {
if self.memory_order.spectre_lfence_mitigation {
"lfence ; atomic_thread_fence(acquire)".into()
} else {
"# compiler_barrier ; atomic_thread_fence(acquire)".into()
}
}
X86AtomicOrdering::Release => {
"# compiler_barrier ; atomic_thread_fence(release)".into()
}
X86AtomicOrdering::AcqRel => {
if self.memory_order.spectre_lfence_mitigation {
"lfence ; atomic_thread_fence(acq_rel)".into()
} else {
"# compiler_barrier ; atomic_thread_fence(acq_rel)".into()
}
}
X86AtomicOrdering::SeqCst => self.memory_order.emit_seq_cst_fence(),
}
}
pub fn c11_atomic_signal_fence(&self, _order: X86AtomicOrdering) -> &'static str {
"# compiler_barrier ; atomic_signal_fence — compiler barrier only on X86"
}
pub fn c11_atomic_is_lock_free(&self, size_bytes: u8) -> bool {
self.model.atomic_is_lock_free_c11(size_bytes)
}
pub fn c11_atomic_flag_size(&self) -> (u8, u8) {
(1, 1) }
}
#[derive(Debug, Clone)]
pub struct X86AtomicSizeClass {
pub size_class: u8,
pub size_bytes: u8,
pub is_lock_free: bool,
pub has_xadd: bool,
pub has_cmpxchg: bool,
pub needs_cmpxchg16b: bool,
pub reg_class: &'static str,
pub suffix: &'static str,
}
impl X86AtomicSizeClass {
pub fn classify(size_bytes: u8, model: &X86AtomicModel) -> Self {
let (size_class, has_xadd, has_cmpxchg, needs_cmpxchg16b) = match size_bytes {
1 => (0, true, true, false),
2 => (1, true, true, false),
4 => (2, true, true, false),
8 => (3, true, true, false),
16 => (4, false, model.has_cmpxchg16b, true),
_ => (3, false, false, false),
};
let reg_class = match size_class {
0 => "r8",
1 => "r16",
2 => "r32",
3 => "r64",
_ => "vr128",
};
Self {
size_class,
size_bytes,
is_lock_free: model.is_lock_free(size_bytes),
has_xadd,
has_cmpxchg,
needs_cmpxchg16b,
reg_class,
suffix: size_to_mov_suffix(size_bytes),
}
}
pub fn xadd_suffix(&self) -> &'static str {
match self.size_bytes {
1 => "b",
2 => "w",
4 => "l",
8 => "q",
_ => "q",
}
}
pub fn cmpxchg_suffix(&self) -> &'static str {
match self.size_bytes {
1 => "b",
2 => "w",
4 => "l",
8 => "q",
16 => "16b",
_ => "q",
}
}
}
#[derive(Debug, Clone)]
pub struct X86AtomicFlag {
pub address: String,
pub is_set: bool,
pub ordering: X86AtomicOrdering,
}
impl X86AtomicFlag {
pub fn new(address: &str) -> Self {
Self {
address: address.to_string(),
is_set: false,
ordering: X86AtomicOrdering::SeqCst,
}
}
pub fn with_ordering(mut self, order: X86AtomicOrdering) -> Self {
self.ordering = order;
self
}
pub fn emit_test_and_set(&self) -> String {
match self.ordering {
X86AtomicOrdering::Relaxed => format!(
"; atomic_flag_test_and_set_explicit({}, relaxed)\n\
lock btsl $0, [{}] ; test-and-set\n\
setc %%al ; previous value in %%al\n\
movzbl %%al, %%eax",
self.address, self.address
),
X86AtomicOrdering::Acquire | X86AtomicOrdering::Consume => format!(
"; atomic_flag_test_and_set_explicit({}, acquire)\n\
lock btsl $0, [{}] ; test-and-set (LOCK = full barrier)\n\
setc %%al\n\
movzbl %%al, %%eax",
self.address, self.address
),
X86AtomicOrdering::Release => format!(
"; atomic_flag_test_and_set_explicit({}, release)\n\
; Use regular store (no barrier needed for release)\n\
lock btsl $0, [{}]\n\
setc %%al\n\
movzbl %%al, %%eax",
self.address, self.address
),
X86AtomicOrdering::SeqCst => format!(
"; atomic_flag_test_and_set_explicit({}, seq_cst)\n\
lock btsl $0, [{}] ; LOCK provides full barrier\n\
setc %%al\n\
movzbl %%al, %%eax\n\
mfence",
self.address, self.address
),
_ => format!(
"; atomic_flag_test_and_set({})\n\
lock btsl $0, [{}]\n\
setc %%al\n\
movzbl %%al, %%eax",
self.address, self.address
),
}
}
pub fn emit_clear(&self) -> String {
match self.ordering {
X86AtomicOrdering::Relaxed => format!(
"; atomic_flag_clear_explicit({}, relaxed)\n\
movb $0, [{}] ; clear flag (plain store)\n\
# compiler_barrier",
self.address, self.address
),
X86AtomicOrdering::Release | X86AtomicOrdering::AcqRel => format!(
"; atomic_flag_clear_explicit({}, release)\n\
movb $0, [{}] ; clear flag (store + compiler barrier for release)\n\
# compiler_barrier",
self.address, self.address
),
X86AtomicOrdering::SeqCst => format!(
"; atomic_flag_clear_explicit({}, seq_cst)\n\
movb $0, [{}] ; clear flag\n\
mfence ; seq_cst barrier",
self.address, self.address
),
_ => format!(
"; atomic_flag_clear({})\n\
lock andb $0, [{}] ; clear flag with LOCK\n\
mfence",
self.address, self.address
),
}
}
}
#[derive(Debug, Clone)]
pub struct X86ConcurrencyOracle {
pub concurrency: X86Concurrency,
pub opt_level: X86ConcurrencyOptLevel,
pub pgo_data: Option<X86ConcurrencyPGO>,
pub features: X86ConcurrencyFeatures,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86ConcurrencyOptLevel {
O0,
O1,
O2,
O3,
}
impl Default for X86ConcurrencyOptLevel {
fn default() -> Self {
Self::O2
}
}
#[derive(Debug, Clone)]
pub struct X86ConcurrencyPGO {
pub avg_contention: f64,
pub tsx_abort_rate: f64,
pub tsx_beneficial: bool,
pub preferred_lock_type: X86SpinLockType,
pub adaptive_spinning_beneficial: bool,
}
impl Default for X86ConcurrencyPGO {
fn default() -> Self {
Self {
avg_contention: 0.05,
tsx_abort_rate: 0.1,
tsx_beneficial: false,
preferred_lock_type: X86SpinLockType::Adaptive,
adaptive_spinning_beneficial: true,
}
}
}
#[derive(Debug, Clone)]
pub struct X86ConcurrencyFeatures {
pub use_fast_uncontended_path: bool,
pub use_native_atomic_fetch_op: bool,
pub prefer_xchg_over_mov_mfence: bool,
pub allow_tsx: bool,
pub allow_hle: bool,
pub allow_rtm: bool,
pub use_compiler_barriers: bool,
pub disable_mfence_in_opt: bool,
pub use_lightweight_barriers: bool,
}
impl Default for X86ConcurrencyFeatures {
fn default() -> Self {
Self {
use_fast_uncontended_path: true,
use_native_atomic_fetch_op: true,
prefer_xchg_over_mov_mfence: true,
allow_tsx: true,
allow_hle: true,
allow_rtm: true,
use_compiler_barriers: true,
disable_mfence_in_opt: false,
use_lightweight_barriers: true,
}
}
}
impl X86ConcurrencyOracle {
pub fn new(concurrency: X86Concurrency) -> Self {
Self {
concurrency,
opt_level: X86ConcurrencyOptLevel::O2,
pgo_data: None,
features: X86ConcurrencyFeatures::default(),
}
}
pub fn with_opt_level(mut self, level: X86ConcurrencyOptLevel) -> Self {
self.opt_level = level;
self
}
pub fn with_pgo(mut self, pgo: X86ConcurrencyPGO) -> Self {
self.pgo_data = Some(pgo);
self
}
pub fn decide_seq_cst_store_strategy(&self) -> &'static str {
if self.features.prefer_xchg_over_mov_mfence {
"xchg ; implicit LOCK, full barrier, saves code size"
} else {
"mov + mfence ; may have better throughput on some uarchs"
}
}
pub fn decide_seq_cst_load_strategy(&self) -> &'static str {
match self.opt_level {
X86ConcurrencyOptLevel::O0 => "mov + mfence ; conservative",
X86ConcurrencyOptLevel::O1 | X86ConcurrencyOptLevel::O2 => {
if self.features.use_lightweight_barriers {
"mov ; TSO provides load ordering; no fence unless next op is a store"
} else {
"mov + mfence"
}
}
X86ConcurrencyOptLevel::O3 => {
"mov ; PGO-optimized: skip fence when next op is not a store"
}
}
}
pub fn should_use_hle(&self) -> bool {
self.concurrency.has_tsx
&& self.features.allow_hle
&& self.features.allow_tsx
&& self.opt_level >= X86ConcurrencyOptLevel::O2
}
pub fn should_use_rtm(&self) -> bool {
if !self.concurrency.has_tsx || !self.features.allow_rtm || !self.features.allow_tsx {
return false;
}
match &self.pgo_data {
Some(pgo) => pgo.tsx_beneficial && pgo.tsx_abort_rate < 0.3,
None => self.opt_level >= X86ConcurrencyOptLevel::O2,
}
}
pub fn optimal_spin_lock_type(&self) -> X86SpinLockType {
match &self.pgo_data {
Some(pgo) if pgo.avg_contention > 0.3 => X86SpinLockType::Ticket,
Some(pgo) => pgo.preferred_lock_type,
None => match self.opt_level {
X86ConcurrencyOptLevel::O0 => X86SpinLockType::TestAndSet,
X86ConcurrencyOptLevel::O1 => X86SpinLockType::TestAndSet,
X86ConcurrencyOptLevel::O2 => X86SpinLockType::Adaptive,
X86ConcurrencyOptLevel::O3 => X86SpinLockType::Adaptive,
},
}
}
pub fn should_emit_mfence(&self, ordering: X86AtomicOrdering) -> bool {
if self.opt_level >= X86ConcurrencyOptLevel::O2 && self.features.disable_mfence_in_opt {
return false;
}
ordering.requires_fence()
}
pub fn relaxed_store_sequence(&self) -> &'static str {
match self.opt_level {
X86ConcurrencyOptLevel::O0 => "mov [mem], reg ; mfence",
_ => "mov [mem], reg ; compiler_barrier (or nothing for pure relaxed)",
}
}
pub fn describe_decisions(&self) -> String {
format!(
"Oracle(opt={:?}, tsx={}, hle={}, rtm={}, lock_type={:?}, mfence={}, relaxed_store={})",
self.opt_level,
self.should_use_rtm(),
self.should_use_hle(),
self.should_use_rtm(),
self.optimal_spin_lock_type(),
self.should_emit_mfence(X86AtomicOrdering::SeqCst),
self.relaxed_store_sequence(),
)
}
}
#[derive(Debug, Clone)]
pub struct X86ConcurrencyCodeGen {
pub oracle: X86ConcurrencyOracle,
pub scratch_regs: Vec<String>,
label_counter: u64,
code: Vec<String>,
}
impl X86ConcurrencyCodeGen {
pub fn new(oracle: X86ConcurrencyOracle) -> Self {
Self {
oracle,
scratch_regs: vec!["%rax".into(), "%rcx".into(), "%rdx".into()],
label_counter: 0,
code: Vec::new(),
}
}
fn next_label(&mut self, prefix: &str) -> String {
let label = format!(".L{}_{}", prefix, self.label_counter);
self.label_counter += 1;
label
}
pub fn emit_critical_section(&mut self, lock_addr: &str, body: &str) -> String {
let lock_type = self.oracle.optimal_spin_lock_type();
let spin_lock = X86SpinLock {
lock_type,
..X86SpinLock::default()
};
let acquire = match lock_type {
X86SpinLockType::TestAndSet => spin_lock.emit_tas_lock_acquire(lock_addr),
X86SpinLockType::Ticket => spin_lock.emit_ticket_lock_acquire(lock_addr),
X86SpinLockType::Adaptive => spin_lock.emit_adaptive_lock_acquire(lock_addr),
X86SpinLockType::MCS => {
let node = format!("[%rsp + {}]", 8);
spin_lock.emit_mcs_lock_acquire(lock_addr, &node)
}
};
let release = match lock_type {
X86SpinLockType::TestAndSet => spin_lock.emit_tas_lock_release(lock_addr),
X86SpinLockType::Ticket => spin_lock.emit_ticket_lock_release(lock_addr),
X86SpinLockType::Adaptive => spin_lock.emit_adaptive_lock_release(lock_addr),
X86SpinLockType::MCS => {
let node = format!("[%rsp + {}]", 8);
spin_lock.emit_mcs_lock_release(lock_addr, &node)
}
};
format!(
"; Critical section (lock: {}, type: {:?})\n\
{}\n\
; --- Critical section body ---\n\
{}\n\
; --- End critical section ---\n\
{}",
lock_addr, lock_type, acquire, body, release
)
}
pub fn emit_atomic_inc(
&mut self,
addr: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
let mut code = format!(
"; Atomic increment (size={}, order={:?})\n",
size_bytes, ordering
);
code.push_str(&format!("lock inc{} [{}]\n", suffix, addr));
if self.oracle.should_emit_mfence(ordering) {
code.push_str("mfence\n");
}
code
}
pub fn emit_atomic_dec(
&mut self,
addr: &str,
size_bytes: u8,
ordering: X86AtomicOrdering,
) -> String {
let suffix = size_to_mov_suffix(size_bytes);
let mut code = format!(
"; Atomic decrement (size={}, order={:?})\n",
size_bytes, ordering
);
code.push_str(&format!("lock dec{} [{}]\n", suffix, addr));
if self.oracle.should_emit_mfence(ordering) {
code.push_str("mfence\n");
}
code
}
pub fn emit_seqlock_read_begin(&mut self, lock_addr: &str, seq_addr: &str) -> String {
format!(
"; Seqlock read begin ({})\n\
.L_seqlock_read_{}:\n\
movl [{}], %%eax ; read sequence number\n\
testb $1, %%al ; odd = writer active\n\
jnz .L_seqlock_read_{} ; spin until even\n\
; Now read the protected data...\n\
; After reading, check sequence again\n\
mfence ; full barrier before reading data\n\
; Read the protected data here...\n\
; Then check sequence again:\n\
mfence ; full barrier after reading data\n\
movl [{}], %%edx ; re-read sequence\n\
cmpl %%eax, %%edx\n\
jne .L_seqlock_read_{} ; retry if changed\n\
; Read successful",
lock_addr,
hash_fast(seq_addr),
seq_addr,
hash_fast(seq_addr),
seq_addr,
hash_fast(seq_addr),
)
}
pub fn emit_seqlock_write_begin(&mut self, lock_addr: &str, seq_addr: &str) -> String {
format!(
"; Seqlock write begin ({})\n\
; Acquire the write lock (test-and-set)\n\
.L_seqlock_write_{}:\n\
lock btsl $0, [{}] ; try acquire write lock\n\
jnc .L_seqlock_write_acquired_{}\n\
pause\n\
jmp .L_seqlock_write_{}\n\
.L_seqlock_write_acquired_{}:\n\
; Increment sequence number (odd = writer active)\n\
incl [{}] ; now odd — readers spin\n\
mfence ; full barrier",
lock_addr,
hash_fast(seq_addr),
lock_addr,
hash_fast(seq_addr),
hash_fast(seq_addr),
hash_fast(seq_addr),
seq_addr,
)
}
pub fn emit_seqlock_write_end(&mut self, lock_addr: &str, seq_addr: &str) -> String {
format!(
"; Seqlock write end ({})\n\
; Done writing — increment sequence to even\n\
mfence ; full barrier before releasing\n\
incl [{}] ; now even — readers proceed\n\
; Release the write lock\n\
movl $0, [{}] ; clear write lock",
lock_addr, seq_addr, lock_addr
)
}
pub fn emit_thread_lifecycle(&mut self, func: &str, arg: &str) -> String {
let create = self.oracle.concurrency.emit_thread_create(func, arg);
let join = self.oracle.concurrency.emit_thread_join("%rax");
format!(
"; Thread lifecycle: create → run → join\n\
; Step 1: Create the thread\n\
{}\n\
; Thread ID returned in %%rax — save it\n\
movq %%rax, %%rbx\n\
; Step 2: Thread runs concurrently...\n\
; Step 3: Join the thread\n\
movq %%rbx, %%rdi\n\
{}",
create, join
)
}
pub fn emit_mutex_critical_section(&mut self, mutex_addr: &str, body: &str) -> String {
let futex = &self.oracle.concurrency.futex_support;
let lock = futex.emit_futex_mutex_lock(mutex_addr);
let unlock = futex.emit_futex_mutex_unlock(mutex_addr);
format!(
"; Mutex-based critical section\n\
{}\n\
; --- Critical section ---\n\
{}\n\
; --- End critical section ---\n\
{}",
lock, body, unlock
)
}
}
#[derive(Debug, Clone)]
pub struct X86MemoryFenceModel {
pub store_load_ordering: bool,
pub store_store_ordering: bool,
pub load_load_ordering: bool,
pub load_store_ordering: bool,
pub full_fence_instr: String,
pub load_fence_instr: String,
pub store_fence_instr: String,
}
impl X86MemoryFenceModel {
pub fn tso() -> Self {
Self {
store_load_ordering: false, store_store_ordering: true,
load_load_ordering: true,
load_store_ordering: true,
full_fence_instr: "mfence".into(),
load_fence_instr: "lfence".into(),
store_fence_instr: "sfence".into(),
}
}
pub fn strong_tso() -> Self {
Self {
store_load_ordering: true,
store_store_ordering: true,
load_load_ordering: true,
load_store_ordering: true,
full_fence_instr: "mfence".into(),
load_fence_instr: "lfence".into(),
store_fence_instr: "sfence".into(),
}
}
pub fn required_fence(&self, earlier_is_store: bool, later_is_load: bool) -> Option<&str> {
if earlier_is_store && later_is_load && !self.store_load_ordering {
Some(&self.full_fence_instr)
} else if earlier_is_store && !self.store_store_ordering {
Some(&self.store_fence_instr)
} else if later_is_load && !self.load_load_ordering {
Some(&self.load_fence_instr)
} else {
None }
}
pub fn describe(&self) -> String {
format!(
"X86 Fence Model:\n\
- Store→Load ordered: {}\n\
- Store→Store ordered: {}\n\
- Load→Load ordered: {}\n\
- Load→Store ordered: {}\n\
- Full fence: {}\n\
- Load fence: {}\n\
- Store fence: {}",
self.store_load_ordering,
self.store_store_ordering,
self.load_load_ordering,
self.load_store_ordering,
self.full_fence_instr,
self.load_fence_instr,
self.store_fence_instr,
)
}
}
impl Default for X86MemoryFenceModel {
fn default() -> Self {
Self::tso()
}
}
#[derive(Debug, Clone)]
pub struct X86AtomicBuiltins {
pub model: X86AtomicModel,
pub use_c11_builtins: bool,
pub builtin_cache: HashMap<String, String>,
}
impl X86AtomicBuiltins {
pub fn new(model: X86AtomicModel) -> Self {
Self {
model,
use_c11_builtins: true,
builtin_cache: HashMap::new(),
}
}
pub fn sync_fetch_and_add(&self, ptr: &str, val: i64, size: u8) -> String {
format!(
"lock xadd{} ${}, [{}] ; __sync_fetch_and_add",
size_to_mov_suffix(size),
val,
ptr
)
}
pub fn sync_fetch_and_sub(&self, ptr: &str, val: i64, size: u8) -> String {
format!(
"neg ${}\n\tlock xadd{} ${}, [{}] ; __sync_fetch_and_sub",
val,
size_to_mov_suffix(size),
val,
ptr
)
}
pub fn sync_lock_test_and_set(&self, ptr: &str, val: i64, size: u8) -> String {
format!(
"xchg{} ${}, [{}] ; __sync_lock_test_and_set (implicit LOCK)",
size_to_mov_suffix(size),
val,
ptr
)
}
pub fn sync_lock_release(&self, ptr: &str, size: u8) -> String {
format!(
"mov{} $0, [{}] ; __sync_lock_release\n\t# compiler_barrier",
size_to_mov_suffix(size),
ptr
)
}
pub fn sync_bool_compare_and_swap(&self, ptr: &str, old: i64, new: i64, size: u8) -> String {
format!(
"mov{} ${}, %%eax\n\tlock cmpxchg{} ${}, [{}] ; __sync_bool_compare_and_swap\n\tsete %%al\n\tmovzbl %%al, %%eax",
size_to_mov_suffix(size), old, size_to_mov_suffix(size), new, ptr
)
}
pub fn sync_val_compare_and_swap(&self, ptr: &str, old: i64, new: i64, size: u8) -> String {
format!(
"mov{} ${}, %%eax\n\tlock cmpxchg{} ${}, [{}] ; __sync_val_compare_and_swap (old value returned in %%eax)",
size_to_mov_suffix(size), old, size_to_mov_suffix(size), new, ptr
)
}
pub fn sync_synchronize(&self) -> String {
if self.model.lock_implies_mfence {
"mfence ; __sync_synchronize".into()
} else {
"lock addl $0, 0(%esp) ; __sync_synchronize (no SSE2)".into()
}
}
pub fn atomic_load_n(&self, ptr: &str, size: u8, order: X86AtomicOrdering) -> String {
self.model.emit_atomic_load("%eax", ptr, size, order)
}
pub fn atomic_store_n(
&self,
ptr: &str,
val: &str,
size: u8,
order: X86AtomicOrdering,
) -> String {
self.model.emit_atomic_store(val, ptr, size, order)
}
pub fn atomic_exchange_n(
&self,
ptr: &str,
val: &str,
size: u8,
order: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_exchange("%eax", ptr, val, size, order)
}
pub fn atomic_compare_exchange_n(
&self,
ptr: &str,
expected: &str,
desired: &str,
weak: bool,
size: u8,
success: X86AtomicOrdering,
failure: X86AtomicOrdering,
) -> String {
self.model
.emit_atomic_compare_exchange(ptr, expected, desired, size, success, failure, weak)
}
pub fn atomic_thread_fence(&self, order: X86AtomicOrdering) -> String {
match order {
X86AtomicOrdering::Relaxed => "; __atomic_thread_fence(relaxed) — nop".into(),
X86AtomicOrdering::Consume | X86AtomicOrdering::Acquire => {
"# compiler_barrier ; __atomic_thread_fence(acquire/consume)".into()
}
X86AtomicOrdering::Release => {
"# compiler_barrier ; __atomic_thread_fence(release)".into()
}
X86AtomicOrdering::AcqRel => {
"# compiler_barrier ; __atomic_thread_fence(acq_rel)".into()
}
X86AtomicOrdering::SeqCst => "mfence ; __atomic_thread_fence(seq_cst)".into(),
}
}
pub fn atomic_signal_fence(&self, _order: X86AtomicOrdering) -> &'static str {
"# compiler_barrier ; __atomic_signal_fence"
}
pub fn atomic_is_lock_free(&self, size: u8) -> bool {
self.model.atomic_is_lock_free_c11(size)
}
}
#[derive(Debug, Clone)]
pub struct X86ReadWriteLock {
pub lock_addr: String,
pub max_readers: u32,
pub writer_preference: bool,
pub spin_lock: X86SpinLock,
}
impl X86ReadWriteLock {
pub fn new(lock_addr: &str) -> Self {
Self {
lock_addr: lock_addr.to_string(),
max_readers: 0x7FFFFFFF,
writer_preference: true,
spin_lock: X86SpinLock::default(),
}
}
pub fn emit_read_lock(&self) -> String {
let h = hash_fast(&self.lock_addr);
format!(
"; RWLock read acquire ({})\n\
.L_rwlock_read_{}:\n\
; Atomically increment the reader count\n\
lock incl [{}]\n\
; Check if write-locked (bit 31 set after increment)\n\
js .L_rwlock_read_contended_{} ; SF=1 if bit 31 set\n\
; Successfully acquired read lock\n\
jmp .L_rwlock_read_done_{}\n\
.L_rwlock_read_contended_{}:\n\
; Write-locked — undo the increment and spin\n\
lock decl [{}]\n\
; Spin until write lock is released\n\
.L_rwlock_read_spin_{}:\n\
movl [{}], %%eax\n\
testl %%eax, %%eax\n\
js .L_rwlock_read_spin_{} ; negative = write-locked\n\
jmp .L_rwlock_read_{}\n\
.L_rwlock_read_done_{}:",
self.lock_addr, h, self.lock_addr, h, h, h, self.lock_addr, h, self.lock_addr, h, h, h,
)
}
pub fn emit_read_unlock(&self) -> String {
format!(
"; RWLock read unlock ({})\n\
lock decl [{}] ; decrement reader count",
self.lock_addr, self.lock_addr
)
}
pub fn emit_write_lock(&self) -> String {
let h = hash_fast(&self.lock_addr);
format!(
"; RWLock write acquire ({})\n\
.L_rwlock_write_{}:\n\
; Try to acquire the write lock: CAS 0 → 0x80000000\n\
movl $0, %%eax\n\
movl $0x80000000, %%edx\n\
lock cmpxchgl %%edx, [{}]\n\
jz .L_rwlock_write_done_{} ; acquired\n\
; Spin until lock is free\n\
pause\n\
movl [{}], %%eax\n\
testl %%eax, %%eax\n\
jnz .L_rwlock_write_{} ; still locked\n\
jmp .L_rwlock_write_{}\n\
.L_rwlock_write_done_{}:",
self.lock_addr, h, h, self.lock_addr, h, self.lock_addr, h, h,
)
}
pub fn emit_write_unlock(&self) -> String {
format!(
"; RWLock write unlock ({})\n\
movl $0, [{}] ; clear the lock\n\
# compiler_barrier",
self.lock_addr, self.lock_addr
)
}
pub fn emit_read_to_write_upgrade(&self) -> String {
let h = hash_fast(&self.lock_addr);
format!(
"; RWLock read-to-write upgrade ({})\n\
; We hold a read lock (count ≥ 1).\n\
; To upgrade: we need the writer bit AND the reader count to be 1 (just us).\n\
; This is unlikely to succeed but can be tried.\n\
movl $1, %%eax ; expected: count = 1, write-locked = 0\n\
movl $0x80000001, %%edx ; desired: write-locked + count = 1\n\
lock cmpxchgl %%edx, [{}]\n\
sete %%al ; 1 if upgraded, 0 if failed\n\
movzbl %%al, %%eax\n\
; If failed, we still hold the read lock",
self.lock_addr, self.lock_addr,
)
}
}
#[derive(Debug, Clone)]
pub struct X86LockFreeStack {
pub top_addr: String,
pub model: X86AtomicModel,
pub ordering: X86AtomicOrdering,
}
impl X86LockFreeStack {
pub fn new(top_addr: &str, model: &X86AtomicModel) -> Self {
Self {
top_addr: top_addr.to_string(),
model: model.clone(),
ordering: X86AtomicOrdering::Release,
}
}
pub fn emit_push(&self, new_node: &str) -> String {
let h = hash_fast(&self.top_addr);
format!(
"; Lock-free stack push ({})\n\
; new_node in {}\n\
.L_lfs_push_{}:\n\
movq [{}], %%rax ; load current top\n\
movq %%rax, 0({}) ; new_node->next = top\n\
lock cmpxchgq {}, [{}] ; if top == rax, top = new_node\n\
jne .L_lfs_push_{}",
self.top_addr, new_node, h, self.top_addr, new_node, new_node, self.top_addr, h,
)
}
pub fn emit_pop(&self, result_reg: &str) -> String {
let h = hash_fast(&self.top_addr);
format!(
"; Lock-free stack pop ({})\n\
.L_lfs_pop_{}:\n\
movq [{}], %%rax ; load current top\n\
testq %%rax, %%rax\n\
jz .L_lfs_pop_empty_{} ; stack empty\n\
movq 0(%%rax), %%rcx ; next = top->next\n\
lock cmpxchgq %%rcx, [{}] ; CAS(top, rax, next)\n\
jne .L_lfs_pop_{}\n\
; Pop successful — old top in %%rax\n\
movq %%rax, {}\n\
jmp .L_lfs_pop_done_{}\n\
.L_lfs_pop_empty_{}:\n\
xorl %{}, %{}\n\
.L_lfs_pop_done_{}:",
self.top_addr,
h,
self.top_addr,
h,
self.top_addr,
h,
result_reg,
h,
h,
result_reg,
result_reg,
h,
)
}
}
#[derive(Debug, Clone)]
pub struct X86LockFreeQueue {
pub head_addr: String,
pub tail_addr: String,
pub model: X86AtomicModel,
}
impl X86LockFreeQueue {
pub fn new(head_addr: &str, tail_addr: &str, model: &X86AtomicModel) -> Self {
Self {
head_addr: head_addr.to_string(),
tail_addr: tail_addr.to_string(),
model: model.clone(),
}
}
pub fn emit_enqueue(&self, new_node: &str) -> String {
let h = hash_fast(&self.tail_addr);
format!(
"; Lock-free queue enqueue ({})\n\
; new_node in {}\n\
; Initialize new_node\n\
movq $0, 0({}) ; new_node->next = NULL\n\
.L_lfq_enq_{}:\n\
movq [{}], %%rax ; load tail\n\
movq 0(%%rax), %%rcx ; next = tail->next\n\
; Check if tail is still consistent\n\
movq [{}], %%rdx ; reload tail\n\
cmpq %%rax, %%rdx\n\
jne .L_lfq_enq_{} ; tail changed — retry\n\
; If next is NULL, try to link\n\
testq %%rcx, %%rcx\n\
jnz .L_lfq_enq_advance_{} ; tail is behind — advance it\n\
; Try to CAS tail->next from NULL to new_node\n\
movq $0, %%rax\n\
movq {}, %%rdx ; our new node\n\
lock cmpxchgq %%rdx, 0(%%rcx) ; ??? Actually we need the right operand\n\
; (Simplified — actual MS queue is more complex)\n\
mfence\n\
jmp .L_lfq_enq_done_{}\n\
.L_lfq_enq_advance_{}:\n\
; Help advance tail\n\
movq %%rcx, %%rax\n\
lock cmpxchgq %%rcx, [{}] ; CAS(tail, rax, next)\n\
jmp .L_lfq_enq_{}\n\
.L_lfq_enq_done_{}:",
self.tail_addr,
new_node,
new_node,
h,
self.tail_addr,
self.tail_addr,
h,
h,
new_node,
h,
h,
self.tail_addr,
h,
h,
)
}
pub fn emit_dequeue(&self, result_reg: &str) -> String {
let h = hash_fast(&self.head_addr);
format!(
"; Lock-free queue dequeue ({{}})\n\
.L_lfq_deq_{}:\n\
movq [{}], %%rax ; load head\n\
movq [{}], %%rcx ; load tail\n\
movq 0(%%rax), %%rdx ; next = head->next\n\
; Check if queue is empty\n\
cmpq %%rax, %%rcx\n\
jne .L_lfq_deq_not_empty_{}\n\
testq %%rdx, %%rdx\n\
jz .L_lfq_deq_empty_{} ; empty\n\
; Help advance tail\n\
movq %%rax, %%rcx ; old head value\n\
lock cmpxchgq %%rdx, [{}] ; CAS(tail, head, next)\n\
jmp .L_lfq_deq_{}\n\
.L_lfq_deq_not_empty_{}:\n\
; Try to advance head\n\
lock cmpxchgq %%rdx, [{}] ; CAS(head, rax, next)\n\
jne .L_lfq_deq_{}\n\
; Dequeue successful — old head in %%rax\n\
movq %%rax, {}\n\
jmp .L_lfq_deq_done_{}\n\
.L_lfq_deq_empty_{}:\n\
xorl %{}, %{}\n\
.L_lfq_deq_done_{}:",
self.head_addr,
h,
self.head_addr,
self.tail_addr,
h,
h,
self.tail_addr,
h,
self.head_addr,
h,
result_reg,
h,
h,
result_reg,
result_reg,
h,
)
}
}
#[derive(Debug, Clone)]
pub struct X86HazardPointer {
pub num_per_thread: usize,
pub retire_threshold: usize,
pub ordering: X86AtomicOrdering,
}
impl X86HazardPointer {
pub fn new(num_per_thread: usize, retire_threshold: usize) -> Self {
Self {
num_per_thread,
retire_threshold,
ordering: X86AtomicOrdering::Release,
}
}
pub fn emit_protect(&self, hp_slot: &str, ptr: &str) -> String {
format!(
"; Hazard pointer: protect {}\n\
; Store pointer to our hazard pointer slot\n\
movq {}, [{}] ; hp[i] = ptr\n\
# compiler_barrier ; ensure store is visible\n\
; Re-read the original pointer to verify it hasn't been freed\n\
; (ABA prevention — caller must validate)",
ptr, ptr, hp_slot
)
}
pub fn emit_clear(&self, hp_slot: &str) -> String {
format!(
"; Hazard pointer: clear {}\n\
movq $0, [{}] ; hp[i] = NULL\n\
# compiler_barrier",
hp_slot, hp_slot
)
}
pub fn emit_scan_for(&self, ptr: &str, hp_array: &str, num_slots: usize) -> String {
let h = hash_fast(ptr);
format!(
"; Hazard pointer scan for {{}}\n\
; Check if any hazard pointer references this pointer\n\
movq {}, %%rsi ; ptr to check\n\
movq {}, %%rdi ; hp array base\n\
movl ${}, %%ecx ; number of slots\n\
.L_hp_scan_{}:\n\
movq (%%rdi), %%rax ; hp[i]\n\
cmpq %%rsi, %%rax\n\
je .L_hp_scan_found_{}\n\
addq $8, %%rdi\n\
decl %%ecx\n\
jnz .L_hp_scan_{}\n\
; Not found — safe to reclaim\n\
xorl %%eax, %%eax ; return 0 (not found)\n\
jmp .L_hp_scan_done_{}\n\
.L_hp_scan_found_{}:\n\
movl $1, %%eax ; return 1 (found, do NOT reclaim)\n\
.L_hp_scan_done_{}:",
ptr, ptr, hp_array, num_slots, h, h, h, h, h,
)
}
}
#[derive(Debug, Clone)]
pub struct X86RcuSimulator {
pub grace_period: X86RcuGracePeriod,
pub ordering: X86AtomicOrdering,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RcuGracePeriod {
QSBR,
EpochBased,
SignalBased,
}
impl X86RcuSimulator {
pub fn new() -> Self {
Self {
grace_period: X86RcuGracePeriod::EpochBased,
ordering: X86AtomicOrdering::Acquire,
}
}
pub fn emit_read_lock(&self) -> &'static str {
"# compiler_barrier ; rcu_read_lock() — compiler barrier only on X86"
}
pub fn emit_read_unlock(&self) -> &'static str {
"# compiler_barrier ; rcu_read_unlock() — compiler barrier only on X86"
}
pub fn emit_synchronize_rcu(&self) -> String {
"mfence ; synchronize_rcu() — full memory barrier\n\
; In real RCU: wait for grace period (all readers done)\n\
; This simplified version just uses a full barrier"
.into()
}
pub fn emit_call_rcu(&self, callback: &str, arg: &str) -> String {
format!(
"; call_rcu({}, {}) — deferred reclamation\n\
; In real RCU: schedule callback for grace period end\n\
; This simplified version: just call immediately (unsafe!)\n\
call {}",
callback, arg, callback
)
}
}
impl Default for X86RcuSimulator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86TransactionalMemoryStats {
pub attempts: u64,
pub commits: u64,
pub capacity_aborts: u64,
pub conflict_aborts: u64,
pub explicit_aborts: u64,
pub other_aborts: u64,
pub total_aborts: u64,
pub tsx_recommended: bool,
}
impl X86TransactionalMemoryStats {
pub fn new() -> Self {
Self {
attempts: 0,
commits: 0,
capacity_aborts: 0,
conflict_aborts: 0,
explicit_aborts: 0,
other_aborts: 0,
total_aborts: 0,
tsx_recommended: true,
}
}
pub fn record_attempt(&mut self) {
self.attempts += 1;
}
pub fn record_commit(&mut self) {
self.commits += 1;
self.attempts += 1;
}
pub fn record_abort(&mut self, eax_reason: u32) {
self.total_aborts += 1;
if eax_reason & (1 << 3) != 0 {
self.capacity_aborts += 1;
} else if eax_reason & (1 << 4) != 0 {
self.conflict_aborts += 1;
} else if eax_reason & (1 << 0) != 0 {
self.explicit_aborts += 1;
} else {
self.other_aborts += 1;
}
self.attempts += 1;
}
pub fn abort_rate(&self) -> f64 {
if self.attempts == 0 {
return 0.0;
}
self.total_aborts as f64 / self.attempts as f64
}
pub fn commit_rate(&self) -> f64 {
if self.attempts == 0 {
return 0.0;
}
self.commits as f64 / self.attempts as f64
}
pub fn update_recommendation(&mut self) {
self.tsx_recommended = self.abort_rate() < 0.5;
}
pub fn reset(&mut self) {
*self = Self::new();
}
pub fn summarize(&self) -> String {
format!(
"TSX Stats: attempts={}, commits={}, aborts={} (capacity={}, conflict={}, explicit={}, other={}), abort_rate={:.2}%, tsx_recommended={}",
self.attempts,
self.commits,
self.total_aborts,
self.capacity_aborts,
self.conflict_aborts,
self.explicit_aborts,
self.other_aborts,
self.abort_rate() * 100.0,
self.tsx_recommended,
)
}
}
impl Default for X86TransactionalMemoryStats {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ThreadSanitizerHooks {
pub enabled: bool,
pub func_prefix: String,
pub func_entry_hook: String,
pub func_exit_hook: String,
pub access_hooks: X86TsanAccessHooks,
}
#[derive(Debug, Clone)]
pub struct X86TsanAccessHooks {
pub read1: String,
pub read2: String,
pub read4: String,
pub read8: String,
pub read16: String,
pub write1: String,
pub write2: String,
pub write4: String,
pub write8: String,
pub write16: String,
}
impl Default for X86TsanAccessHooks {
fn default() -> Self {
Self {
read1: "__tsan_read1".into(),
read2: "__tsan_read2".into(),
read4: "__tsan_read4".into(),
read8: "__tsan_read8".into(),
read16: "__tsan_read16".into(),
write1: "__tsan_write1".into(),
write2: "__tsan_write2".into(),
write4: "__tsan_write4".into(),
write8: "__tsan_write8".into(),
write16: "__tsan_write16".into(),
}
}
}
impl X86ThreadSanitizerHooks {
pub fn new() -> Self {
Self {
enabled: false,
func_prefix: "__tsan_".into(),
func_entry_hook: "__tsan_func_entry".into(),
func_exit_hook: "__tsan_func_exit".into(),
access_hooks: X86TsanAccessHooks::default(),
}
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn emit_read_hook(&self, addr: &str, size_bytes: u8) -> String {
if !self.enabled {
return String::new();
}
let hook = match size_bytes {
1 => &self.access_hooks.read1,
2 => &self.access_hooks.read2,
4 => &self.access_hooks.read4,
8 => &self.access_hooks.read8,
16 => &self.access_hooks.read16,
_ => &self.access_hooks.read8,
};
format!(
"; TSan: read hook for size={}\n\
leaq {}, %%rdi ; address\n\
call {}",
size_bytes, addr, hook
)
}
pub fn emit_write_hook(&self, addr: &str, size_bytes: u8) -> String {
if !self.enabled {
return String::new();
}
let hook = match size_bytes {
1 => &self.access_hooks.write1,
2 => &self.access_hooks.write2,
4 => &self.access_hooks.write4,
8 => &self.access_hooks.write8,
16 => &self.access_hooks.write16,
_ => &self.access_hooks.write8,
};
format!(
"; TSan: write hook for size={}\n\
leaq {}, %%rdi ; address\n\
call {}",
size_bytes, addr, hook
)
}
pub fn emit_func_entry(&self) -> String {
if !self.enabled {
return String::new();
}
format!("call {}", self.func_entry_hook)
}
pub fn emit_func_exit(&self) -> String {
if !self.enabled {
return String::new();
}
format!("call {}", self.func_exit_hook)
}
pub fn emit_atomic_hook(&self, addr: &str, size_bytes: u8, is_store: bool) -> String {
if !self.enabled {
return String::new();
}
let kind = if is_store {
"atomic_store"
} else {
"atomic_load"
};
let size = match size_bytes {
1 => "1",
2 => "2",
4 => "4",
8 => "8",
16 => "16",
_ => "8",
};
format!(
"; TSan: {} hook for size={}\n\
leaq {}, %%rdi\n\
call __tsan_{}_{}",
kind, size_bytes, addr, kind, size
)
}
}
impl Default for X86ThreadSanitizerHooks {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ConcurrencyValidator {
pub warnings: Vec<String>,
pub errors: Vec<String>,
pub info: Vec<String>,
}
impl X86ConcurrencyValidator {
pub fn new() -> Self {
Self {
warnings: Vec::new(),
errors: Vec::new(),
info: Vec::new(),
}
}
pub fn validate(&mut self, concurrency: &X86Concurrency) -> bool {
self.warnings.clear();
self.errors.clear();
self.info.clear();
if concurrency.atomic_model.max_lock_free_width == 0 {
self.errors.push(
"Atomic model has max_lock_free_width=0 — no lock-free atomics available".into(),
);
}
if concurrency.has_cmpxchg16b && !matches!(concurrency.arch, X86Arch::X86_64) {
self.warnings
.push("CMPXCHG16B reported as available on non-X86-64 target".into());
}
if !concurrency.atomic_model.is_lock_free(4) {
self.errors
.push("32-bit atomics must be lock-free on X86".into());
}
if concurrency.memory_order.spectre_lfence_mitigation && !concurrency.memory_order.has_sse2
{
self.warnings
.push("Spectre LFENCE mitigation enabled but SSE2 not available".into());
}
if concurrency.has_tsx && !concurrency.lock_elision.enabled {
self.info
.push("TSX is available but lock elision is disabled".into());
}
if concurrency.lock_elision.enabled && !concurrency.has_tsx {
self.warnings
.push("Lock elision enabled but TSX is not available".into());
}
match (
concurrency.tls_model_pref,
&concurrency.thread_model.tls_format,
) {
(X86TlsModelPreference::LocalExec, X86TlsFormat::ELF) => {
self.info.push(
"LocalExec TLS selected — fastest but only works in main executable".into(),
);
}
_ => {}
}
match concurrency.platform {
X86Platform::Linux if !concurrency.futex_support.has_futex_syscall => {
self.errors
.push("Linux target without futex syscall — this is invalid".into());
}
X86Platform::Windows if !concurrency.futex_support.has_wait_on_address => {
self.warnings.push(
"Windows target without WaitOnAddress — falling back to older APIs".into(),
);
}
_ => {}
}
if !concurrency.barrier.has_sse2 {
self.warnings
.push("SSE2 not available — MFENCE/LFENCE/SFENCE unavailable".into());
}
if concurrency.lock_elision.use_hle && concurrency.lock_elision.use_rtm {
self.info
.push("Both HLE and RTM enabled — RTM preferred for new code".into());
}
self.errors.is_empty()
}
pub fn messages(&self) -> &Vec<String> {
&self.errors
}
pub fn report(&self) -> String {
let mut report = String::from("X86 Concurrency Validation Report\n");
report.push_str("=================================\n");
if !self.errors.is_empty() {
report.push_str("\nErrors:\n");
for e in &self.errors {
report.push_str(&format!(" ERROR: {}\n", e));
}
}
if !self.warnings.is_empty() {
report.push_str("\nWarnings:\n");
for w in &self.warnings {
report.push_str(&format!(" WARN: {}\n", w));
}
}
if !self.info.is_empty() {
report.push_str("\nInfo:\n");
for i in &self.info {
report.push_str(&format!(" INFO: {}\n", i));
}
}
if self.errors.is_empty() && self.warnings.is_empty() {
report.push_str("\nAll validations passed.\n");
}
report
}
}
impl Default for X86ConcurrencyValidator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ConcurrencyDriver {
pub config: X86Concurrency,
pub codegen: X86ConcurrencyCodeGen,
pub oracle: X86ConcurrencyOracle,
pub validator: X86ConcurrencyValidator,
pub c11_mapping: X86C11AtomicMapping,
pub builtins: X86AtomicBuiltins,
pub tsx_stats: X86TransactionalMemoryStats,
}
impl X86ConcurrencyDriver {
pub fn new_x86_64_linux() -> Self {
let config = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(config.clone());
let codegen = X86ConcurrencyCodeGen::new(oracle.clone());
let c11_mapping =
X86C11AtomicMapping::new(config.atomic_model.clone(), config.memory_order.clone());
let builtins = X86AtomicBuiltins::new(config.atomic_model.clone());
Self {
config,
codegen,
oracle,
validator: X86ConcurrencyValidator::new(),
c11_mapping,
builtins,
tsx_stats: X86TransactionalMemoryStats::new(),
}
}
pub fn new_windows_x86_64() -> Self {
let config = X86Concurrency::new_windows_x86_64();
let oracle = X86ConcurrencyOracle::new(config.clone());
let codegen = X86ConcurrencyCodeGen::new(oracle.clone());
let c11_mapping =
X86C11AtomicMapping::new(config.atomic_model.clone(), config.memory_order.clone());
let builtins = X86AtomicBuiltins::new(config.atomic_model.clone());
Self {
config,
codegen,
oracle,
validator: X86ConcurrencyValidator::new(),
c11_mapping,
builtins,
tsx_stats: X86TransactionalMemoryStats::new(),
}
}
pub fn validate(&mut self) -> bool {
self.validator.validate(&self.config)
}
pub fn validation_report(&self) -> String {
self.validator.report()
}
pub fn emit_optimal_fetch_add(
&self,
dst: &str,
addr: &str,
val: &str,
size: u8,
order: X86AtomicOrdering,
) -> String {
self.c11_mapping
.c11_atomic_fetch_add(dst, addr, val, size, order)
}
pub fn emit_tls_access(&self, var: &str, offset: u64) -> String {
self.config.thread_model.emit_tls_access(var, offset)
}
pub fn emit_spin_lock_critical_section(&self, lock_addr: &str, body: &str) -> String {
let mut codegen = self.codegen.clone();
codegen.emit_critical_section(lock_addr, body)
}
pub fn emit_futex_mutex_lock(&self, addr: &str) -> String {
self.config.futex_support.emit_futex_mutex_lock(addr)
}
pub fn emit_futex_mutex_unlock(&self, addr: &str) -> String {
self.config.futex_support.emit_futex_mutex_unlock(addr)
}
pub fn describe(&self) -> String {
format!(
"X86ConcurrencyDriver {{\n\
\tconfig: {}\n\
\toracle: {}\n\
\ttsx_stats: {}\n\
\tvalidation: {} errors, {} warnings\n\
}}",
self.config.describe(),
self.oracle.describe_decisions(),
self.tsx_stats.summarize(),
self.validator.errors.len(),
self.validator.warnings.len(),
)
}
}
impl Default for X86ConcurrencyDriver {
fn default() -> Self {
Self::new_x86_64_linux()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atomic_model_x86_64_lock_free_sizes() {
let model = X86AtomicModel::new_x86_64();
assert!(model.is_lock_free(1));
assert!(model.is_lock_free(2));
assert!(model.is_lock_free(4));
assert!(model.is_lock_free(8));
assert!(model.is_lock_free(16));
assert!(!model.is_lock_free(32));
assert!(!model.is_lock_free(3));
assert!(!model.is_lock_free(0));
}
#[test]
fn test_atomic_model_x86_32_lock_free_sizes() {
let model = X86AtomicModel::new_x86_32();
assert!(model.is_lock_free(1));
assert!(model.is_lock_free(2));
assert!(model.is_lock_free(4));
assert!(model.is_lock_free(8));
assert!(!model.is_lock_free(16)); }
#[test]
fn test_atomic_model_x86_16_lock_free_sizes() {
let model = X86AtomicModel::new_x86_16();
assert!(model.is_lock_free(1));
assert!(model.is_lock_free(2));
assert!(model.is_lock_free(4));
assert!(!model.is_lock_free(8));
}
#[test]
fn test_lock_free_sizes_list() {
let model = X86AtomicModel::new_x86_64();
let sizes = model.lock_free_sizes();
assert_eq!(sizes, vec![1, 2, 4, 8, 16]);
}
#[test]
fn test_lock_free_sizes_list_32() {
let model = X86AtomicModel::new_x86_32();
let sizes = model.lock_free_sizes();
assert_eq!(sizes, vec![1, 2, 4, 8]);
}
#[test]
fn test_atomic_is_lock_free_c11() {
let model = X86AtomicModel::new_x86_64();
assert!(model.atomic_is_lock_free_c11(1));
assert!(model.atomic_is_lock_free_c11(2));
assert!(model.atomic_is_lock_free_c11(4));
assert!(model.atomic_is_lock_free_c11(8));
assert!(model.atomic_is_lock_free_c11(16));
assert!(!model.atomic_is_lock_free_c11(3)); assert!(!model.atomic_is_lock_free_c11(32)); }
#[test]
fn test_recommended_alignment() {
let model = X86AtomicModel::new_x86_64();
assert_eq!(model.recommended_alignment(1), 1);
assert_eq!(model.recommended_alignment(2), 2);
assert_eq!(model.recommended_alignment(4), 4);
assert_eq!(model.recommended_alignment(8), 8);
assert_eq!(model.recommended_alignment(16), 16);
}
#[test]
fn test_atomic_load_emission() {
let model = X86AtomicModel::new_x86_64();
let code = model.emit_atomic_load("%rax", "(%rbx)", 8, X86AtomicOrdering::Relaxed);
assert!(code.contains("movq"));
assert!(code.contains("relaxed"));
assert!(!code.contains("mfence"));
}
#[test]
fn test_atomic_load_seq_cst_emission() {
let model = X86AtomicModel::new_x86_64();
let code = model.emit_atomic_load("%rax", "(%rbx)", 8, X86AtomicOrdering::SeqCst);
assert!(code.contains("movq"));
assert!(code.contains("mfence"));
}
#[test]
fn test_atomic_store_seq_cst_xchg() {
let model = X86AtomicModel::new_x86_64();
let code = model.emit_atomic_store("%rax", "(%rbx)", 8, X86AtomicOrdering::SeqCst);
assert!(code.contains("xchg"));
}
#[test]
fn test_atomic_store_relaxed() {
let model = X86AtomicModel::new_x86_64();
let code = model.emit_atomic_store("%rax", "(%rbx)", 4, X86AtomicOrdering::Relaxed);
assert!(code.contains("movl"));
assert!(!code.contains("mfence"));
assert!(!code.contains("xchg"));
}
#[test]
fn test_atomic_store_release() {
let model = X86AtomicModel::new_x86_64();
let code = model.emit_atomic_store("%rax", "(%rbx)", 4, X86AtomicOrdering::Release);
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_atomic_exchange() {
let model = X86AtomicModel::new_x86_64();
let code =
model.emit_atomic_exchange("%rax", "(%rbx)", "%rcx", 8, X86AtomicOrdering::SeqCst);
assert!(code.contains("xchg"));
assert!(code.contains("%rax"));
}
#[test]
fn test_atomic_cmpxchg() {
let model = X86AtomicModel::new_x86_64();
let code = model.emit_atomic_compare_exchange(
"(%rbx)",
"%rax",
"%rcx",
8,
X86AtomicOrdering::SeqCst,
X86AtomicOrdering::Acquire,
false,
);
assert!(code.contains("cmpxchg"));
}
#[test]
fn test_atomic_cmpxchg_weak() {
let model = X86AtomicModel::new_x86_64();
let code = model.emit_atomic_compare_exchange(
"(%rbx)",
"%rax",
"%rcx",
4,
X86AtomicOrdering::AcqRel,
X86AtomicOrdering::Acquire,
true,
);
assert!(code.contains("cmpxchg"));
assert!(code.contains("weak=true"));
}
#[test]
fn test_cmpxchg16b_emission() {
let model = X86AtomicModel::new_x86_64();
let code = model.emit_atomic_compare_exchange(
"(%rbx)",
"%rdx:%rax",
"%rcx:%rbx",
16,
X86AtomicOrdering::SeqCst,
X86AtomicOrdering::SeqCst,
false,
);
assert!(code.contains("cmpxchg16b"));
}
#[test]
fn test_fetch_add_emission() {
let model = X86AtomicModel::new_x86_64();
let code =
model.emit_atomic_fetch_add("%rax", "(%rbx)", "%rcx", 8, X86AtomicOrdering::SeqCst);
assert!(code.contains("lock xaddq"));
assert!(code.contains("fetch_add"));
}
#[test]
fn test_fetch_sub_emission() {
let model = X86AtomicModel::new_x86_64();
let code =
model.emit_atomic_fetch_sub("%rax", "(%rbx)", "%rcx", 4, X86AtomicOrdering::AcqRel);
assert!(code.contains("neg"));
assert!(code.contains("lock xaddl"));
}
#[test]
fn test_fetch_and_cas_loop() {
let model = X86AtomicModel::new_x86_64();
let code =
model.emit_atomic_fetch_and("%rax", "(%rbx)", "%rcx", 8, X86AtomicOrdering::SeqCst);
assert!(code.contains("fetch_and"));
assert!(code.contains("cmpxchg"));
}
#[test]
fn test_fetch_or_cas_loop() {
let model = X86AtomicModel::new_x86_64();
let code =
model.emit_atomic_fetch_or("%rax", "(%rbx)", "%rcx", 8, X86AtomicOrdering::Release);
assert!(code.contains("fetch_or"));
}
#[test]
fn test_fetch_xor_cas_loop() {
let model = X86AtomicModel::new_x86_64();
let code =
model.emit_atomic_fetch_xor("%rax", "(%rbx)", "%rcx", 4, X86AtomicOrdering::AcqRel);
assert!(code.contains("fetch_xor"));
}
#[test]
fn test_lock_prefix_semantics() {
let model = X86AtomicModel::new_x86_64();
let semantics = model.lock_prefix_semantics();
assert!(semantics.contains("LOCK"));
assert!(semantics.contains("MFENCE"));
}
#[test]
fn test_describe_atomic_model() {
let model = X86AtomicModel::new_x86_64();
let desc = model.describe();
assert!(desc.contains("TSO"));
assert!(desc.contains("16"));
}
#[test]
fn test_ordering_from_c11() {
assert_eq!(X86AtomicOrdering::from_c11(0), X86AtomicOrdering::Relaxed);
assert_eq!(X86AtomicOrdering::from_c11(2), X86AtomicOrdering::Acquire);
assert_eq!(X86AtomicOrdering::from_c11(3), X86AtomicOrdering::Release);
assert_eq!(X86AtomicOrdering::from_c11(5), X86AtomicOrdering::SeqCst);
assert_eq!(X86AtomicOrdering::from_c11(99), X86AtomicOrdering::SeqCst); }
#[test]
fn test_ordering_to_c11() {
assert_eq!(X86AtomicOrdering::Relaxed.to_c11(), 0);
assert_eq!(X86AtomicOrdering::Acquire.to_c11(), 2);
assert_eq!(X86AtomicOrdering::Release.to_c11(), 3);
assert_eq!(X86AtomicOrdering::SeqCst.to_c11(), 5);
}
#[test]
fn test_ordering_to_llvm_str() {
assert_eq!(X86AtomicOrdering::Relaxed.to_llvm_str(), "monotonic");
assert_eq!(X86AtomicOrdering::Acquire.to_llvm_str(), "acquire");
assert_eq!(X86AtomicOrdering::Release.to_llvm_str(), "release");
assert_eq!(X86AtomicOrdering::SeqCst.to_llvm_str(), "seq_cst");
}
#[test]
fn test_requires_fence() {
assert!(!X86AtomicOrdering::Relaxed.requires_fence());
assert!(!X86AtomicOrdering::Acquire.requires_fence());
assert!(!X86AtomicOrdering::Release.requires_fence());
assert!(X86AtomicOrdering::SeqCst.requires_fence());
}
#[test]
fn test_valid_cas_failure() {
assert!(X86AtomicOrdering::Relaxed.is_valid_cas_failure());
assert!(X86AtomicOrdering::Acquire.is_valid_cas_failure());
assert!(!X86AtomicOrdering::Release.is_valid_cas_failure());
assert!(!X86AtomicOrdering::AcqRel.is_valid_cas_failure());
assert!(X86AtomicOrdering::SeqCst.is_valid_cas_failure());
}
#[test]
fn test_valid_failure_ordering() {
assert_eq!(
X86AtomicOrdering::valid_failure_ordering(X86AtomicOrdering::SeqCst),
X86AtomicOrdering::SeqCst
);
assert_eq!(
X86AtomicOrdering::valid_failure_ordering(X86AtomicOrdering::AcqRel),
X86AtomicOrdering::Acquire
);
assert_eq!(
X86AtomicOrdering::valid_failure_ordering(X86AtomicOrdering::Release),
X86AtomicOrdering::Relaxed
);
assert_eq!(
X86AtomicOrdering::valid_failure_ordering(X86AtomicOrdering::Relaxed),
X86AtomicOrdering::Relaxed
);
}
#[test]
fn test_ordering_compatibility() {
assert!(X86AtomicOrdering::is_compatible(
X86AtomicOrdering::SeqCst,
X86AtomicOrdering::SeqCst,
));
assert!(X86AtomicOrdering::is_compatible(
X86AtomicOrdering::SeqCst,
X86AtomicOrdering::Acquire,
));
assert!(!X86AtomicOrdering::is_compatible(
X86AtomicOrdering::Acquire,
X86AtomicOrdering::SeqCst,
));
}
#[test]
fn test_ordering_display() {
assert_eq!(format!("{}", X86AtomicOrdering::Relaxed), "relaxed");
assert_eq!(format!("{}", X86AtomicOrdering::SeqCst), "seq_cst");
}
#[test]
fn test_memory_order_relaxed_no_fence() {
let mo = X86MemoryOrder::default();
assert_eq!(mo.emit_load_fence(X86AtomicOrdering::Relaxed), None);
assert_eq!(mo.emit_store_fence(X86AtomicOrdering::Relaxed), None);
}
#[test]
fn test_memory_order_acquire_compiler_barrier() {
let mo = X86MemoryOrder::default();
let fence = mo.emit_load_fence(X86AtomicOrdering::Acquire);
assert!(fence.is_some());
assert!(fence.unwrap().contains("compiler_barrier"));
}
#[test]
fn test_memory_order_release_compiler_barrier() {
let mo = X86MemoryOrder::default();
let fence = mo.emit_store_fence(X86AtomicOrdering::Release);
assert!(fence.is_some());
assert!(fence.unwrap().contains("compiler_barrier"));
}
#[test]
fn test_memory_order_seq_cst_mfence() {
let mo = X86MemoryOrder::default();
let load_fence = mo.emit_load_fence(X86AtomicOrdering::SeqCst);
let store_fence = mo.emit_store_fence(X86AtomicOrdering::SeqCst);
assert!(load_fence.is_some());
assert!(store_fence.is_some());
assert!(load_fence.unwrap().contains("mfence"));
assert!(store_fence.unwrap().contains("mfence"));
}
#[test]
fn test_memory_order_seq_cst_fence_emission() {
let mo = X86MemoryOrder::default();
let fence = mo.emit_seq_cst_fence();
assert!(fence.contains("mfence"));
}
#[test]
fn test_memory_order_sse2_absence_lock_add() {
let mo = X86MemoryOrder::new_no_sse2();
let fence = mo.emit_seq_cst_fence();
assert!(fence.contains("lock addl"));
assert!(!fence.contains("mfence"));
}
#[test]
fn test_describe_mapping() {
let mo = X86MemoryOrder::default();
let desc = mo.describe_mapping(X86AtomicOrdering::SeqCst);
assert!(desc.contains("seq_cst"));
assert!(desc.contains("mfence"));
}
#[test]
fn test_spectre_mitigation() {
let mo = X86MemoryOrder::new_modern();
let fence = mo.emit_load_fence(X86AtomicOrdering::Acquire);
assert!(fence.is_some());
assert!(fence.unwrap().contains("lfence"));
}
#[test]
fn test_thread_model_linux_x86_64() {
let tm = X86ThreadModel::new_linux_x86_64();
assert_eq!(tm.platform, X86Platform::Linux);
assert_eq!(tm.arch, X86Arch::X86_64);
assert_eq!(tm.tls_format, X86TlsFormat::ELF);
assert!(tm.cxx11_thread_local);
}
#[test]
fn test_thread_model_windows_x86_64() {
let tm = X86ThreadModel::new_windows_x86_64();
assert_eq!(tm.platform, X86Platform::Windows);
assert_eq!(tm.tls_format, X86TlsFormat::PE);
}
#[test]
fn test_tls_access_local_exec_x86_64() {
let tm = X86ThreadModel::new_linux_x86_64();
let code = tm.emit_tls_access("my_var", 8);
assert!(code.contains("%fs"));
assert!(code.contains("my_var"));
}
#[test]
fn test_tls_access_general_dynamic() {
let mut tm = X86ThreadModel::new_linux_x86_64();
tm.tls_model = X86TlsModel::GeneralDynamic;
let code = tm.emit_tls_access("my_var", 8);
assert!(code.contains("TLSGD"));
assert!(code.contains("__tls_get_addr"));
}
#[test]
fn test_tls_access_initial_exec() {
let mut tm = X86ThreadModel::new_linux_x86_64();
tm.tls_model = X86TlsModel::InitialExec;
let code = tm.emit_tls_access("my_var", 8);
assert!(code.contains("GOTTPOFF"));
assert!(code.contains("%fs"));
}
#[test]
fn test_tls_model_can_relax() {
assert!(X86TlsModel::GeneralDynamic.can_relax_to(X86TlsModel::LocalExec));
assert!(X86TlsModel::LocalDynamic.can_relax_to(X86TlsModel::InitialExec));
assert!(!X86TlsModel::LocalExec.can_relax_to(X86TlsModel::GeneralDynamic));
}
#[test]
fn test_tls_model_display() {
assert_eq!(
format!("{}", X86TlsModel::GeneralDynamic),
"general-dynamic"
);
assert_eq!(format!("{}", X86TlsModel::LocalExec), "local-exec");
}
#[test]
fn test_tls_model_access_patterns() {
assert!(X86TlsModel::LocalExec
.x86_64_access_pattern()
.contains("%fs"));
assert!(X86TlsModel::GeneralDynamic
.x86_64_access_pattern()
.contains("TLSGD"));
}
#[test]
fn test_tls_32_access_patterns() {
assert!(X86TlsModel::LocalExec
.x86_32_access_pattern()
.contains("%gs"));
assert!(X86TlsModel::InitialExec
.x86_32_access_pattern()
.contains("GOTNTPOFF"));
}
#[test]
fn test_thread_create_linux() {
let tm = X86ThreadModel::new_linux_x86_64();
let code = tm.emit_thread_create("my_thread_func", "arg");
assert!(code.contains("pthread_create"));
}
#[test]
fn test_thread_join_linux() {
let tm = X86ThreadModel::new_linux_x86_64();
let code = tm.emit_thread_join("%rax");
assert!(code.contains("pthread_join"));
}
#[test]
fn test_thread_create_windows() {
let tm = X86ThreadModel::new_windows_x86_64();
let code = tm.emit_thread_create("my_thread_func", "arg");
assert!(code.contains("CreateThread"));
}
#[test]
fn test_thread_pointer_register() {
let tm64 = X86ThreadModel::new_linux_x86_64();
let tm32 = X86ThreadModel::new_linux_x86_32();
assert_eq!(tm64.thread_pointer_register(), "%fs");
assert_eq!(tm32.thread_pointer_register(), "%gs");
}
#[test]
fn test_thread_local_declaration_elf() {
let tm = X86ThreadModel::new_linux_x86_64();
let code = tm.emit_thread_local_declaration("i32", "my_tls_var");
assert!(code.contains(".tdata"));
assert!(code.contains("my_tls_var"));
}
#[test]
fn test_thread_local_declaration_pe() {
let tm = X86ThreadModel::new_windows_x86_64();
let code = tm.emit_thread_local_declaration("i32", "my_tls_var");
assert!(code.contains(".tls$"));
}
#[test]
fn test_tls_get_addr_stub() {
let tm = X86ThreadModel::new_linux_x86_64();
let stub = tm.emit_tls_get_addr_stub();
assert!(stub.contains("__tls_get_addr"));
assert!(stub.contains("%fs"));
}
#[test]
fn test_tls_relaxation_chain() {
let tm = X86ThreadModel::new_linux_x86_64();
let chain = tm.tls_relaxation_chain();
assert!(chain.contains("General Dynamic"));
assert!(chain.contains("Local Exec"));
}
#[test]
fn test_tls_cache() {
let mut cache = X86TlsCache::default();
assert!(cache.lookup("var1").is_none());
cache.insert("var1".into(), 42);
assert_eq!(cache.lookup("var1"), Some(42));
cache.clear();
assert!(cache.lookup("var1").is_none());
}
#[test]
fn test_tls_format_display() {
assert_eq!(format!("{}", X86TlsFormat::ELF), "elf");
assert_eq!(format!("{}", X86TlsFormat::PE), "pe");
assert_eq!(format!("{}", X86TlsFormat::MachO), "macho");
}
#[test]
fn test_futex_linux_available() {
let futex = X86FutexSupport::linux();
assert!(futex.has_futex_syscall);
assert!(!futex.has_wait_on_address);
assert!(futex.supports_private_futex);
}
#[test]
fn test_futex_windows_available() {
let futex = X86FutexSupport::windows();
assert!(!futex.has_futex_syscall);
assert!(futex.has_wait_on_address);
}
#[test]
fn test_futex_macos_available() {
let futex = X86FutexSupport::macos();
assert!(!futex.has_futex_syscall);
assert!(futex.has_ulock);
}
#[test]
fn test_futex_wait_emission_linux() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_wait("my_futex", 42);
assert!(code.contains("sys_futex"));
assert!(code.contains("syscall"));
}
#[test]
fn test_futex_wake_emission_linux() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_wake("my_futex", 1);
assert!(code.contains("syscall"));
assert!(code.contains("202"));
}
#[test]
fn test_futex_wait_emission_windows() {
let futex = X86FutexSupport::windows();
let code = futex.emit_futex_wait("my_futex", 42);
assert!(code.contains("WaitOnAddress"));
}
#[test]
fn test_futex_wake_emission_windows() {
let futex = X86FutexSupport::windows();
let code = futex.emit_futex_wake("my_futex", 1);
assert!(code.contains("WakeByAddressSingle"));
}
#[test]
fn test_futex_wait_emission_macos() {
let futex = X86FutexSupport::macos();
let code = futex.emit_futex_wait("my_futex", 42);
assert!(code.contains("__ulock_wait"));
}
#[test]
fn test_futex_wake_emission_macos() {
let futex = X86FutexSupport::macos();
let code = futex.emit_futex_wake("my_futex", 1);
assert!(code.contains("__ulock_wake"));
}
#[test]
fn test_futex_requeue() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_requeue("src", "dst", 1, 5);
assert!(code.contains("FUTEX_CMP_REQUEUE"));
}
#[test]
fn test_futex_mutex_lock() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_mutex_lock("mutex");
assert!(code.contains("cmpxchg"));
assert!(code.contains("futex"));
}
#[test]
fn test_futex_mutex_unlock() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_mutex_unlock("mutex");
assert!(code.contains("lock decl"));
}
#[test]
fn test_futex_condvar_wait() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_condvar_wait("cv", "mutex");
assert!(code.contains("futex"));
assert!(code.contains("mutex"));
}
#[test]
fn test_futex_condvar_signal() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_condvar_signal("cv");
assert!(code.contains("incl"));
}
#[test]
fn test_futex_condvar_broadcast() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_condvar_broadcast("cv");
assert!(code.contains("incl"));
}
#[test]
fn test_futex_semaphore_wait() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_semaphore_wait("sem");
assert!(code.contains("cmpxchg"));
}
#[test]
fn test_futex_semaphore_post() {
let futex = X86FutexSupport::linux();
let code = futex.emit_futex_semaphore_post("sem");
assert!(code.contains("lock incl"));
}
#[test]
fn test_spin_lock_tas_acquire() {
let sl = X86SpinLock::default();
let code = sl.emit_tas_lock_acquire("lock_addr");
assert!(code.contains("btsl"));
}
#[test]
fn test_spin_lock_tas_release() {
let sl = X86SpinLock::default();
let code = sl.emit_tas_lock_release("lock_addr");
assert!(code.contains("movl $0"));
}
#[test]
fn test_spin_lock_ticket_acquire() {
let sl = X86SpinLock::default();
let code = sl.emit_ticket_lock_acquire("lock_addr");
assert!(code.contains("xaddw"));
}
#[test]
fn test_spin_lock_ticket_release() {
let sl = X86SpinLock::default();
let code = sl.emit_ticket_lock_release("lock_addr");
assert!(code.contains("incw"));
}
#[test]
fn test_spin_lock_mcs_acquire() {
let sl = X86SpinLock::default();
let code = sl.emit_mcs_lock_acquire("lock_addr", "node_addr");
assert!(code.contains("xchgq"));
assert!(code.contains("node"));
}
#[test]
fn test_spin_lock_mcs_release() {
let sl = X86SpinLock::default();
let code = sl.emit_mcs_lock_release("lock_addr", "node_addr");
assert!(code.contains("cmpxchgq"));
}
#[test]
fn test_spin_lock_adaptive_acquire() {
let sl = X86SpinLock::default();
let code = sl.emit_adaptive_lock_acquire("lock_addr");
assert!(code.contains("btsl"));
}
#[test]
fn test_spin_lock_try_acquire() {
let sl = X86SpinLock::default();
let code = sl.emit_try_acquire("lock_addr");
assert!(code.contains("btsl"));
assert!(code.contains("setnc"));
}
#[test]
fn test_spin_lock_try_acquire_ticket() {
let sl = X86SpinLock {
lock_type: X86SpinLockType::Ticket,
..X86SpinLock::default()
};
let code = sl.emit_try_acquire("lock_addr");
assert!(code.contains("cmpxchg"));
}
#[test]
fn test_spin_lock_type_display() {
assert_eq!(format!("{}", X86SpinLockType::TestAndSet), "tas");
assert_eq!(format!("{}", X86SpinLockType::Ticket), "ticket");
assert_eq!(format!("{}", X86SpinLockType::MCS), "mcs");
assert_eq!(format!("{}", X86SpinLockType::Adaptive), "adaptive");
}
#[test]
fn test_hle_acquire_enabled() {
let hle = X86LockElision::new(true);
let code = hle.emit_hle_acquire("lock_addr");
assert!(code.contains("xacquire"));
}
#[test]
fn test_hle_acquire_disabled() {
let hle = X86LockElision::new(false);
let code = hle.emit_hle_acquire("lock_addr");
assert!(!code.contains("xacquire"));
}
#[test]
fn test_hle_release_enabled() {
let hle = X86LockElision::new(true);
let code = hle.emit_hle_release("lock_addr");
assert!(code.contains("xrelease"));
}
#[test]
fn test_rtm_spin_lock_acquire() {
let hle = X86LockElision::new(true);
let code = hle.emit_rtm_spin_lock_acquire("lock_addr");
assert!(code.contains("xbegin"));
assert!(code.contains("xend"));
}
#[test]
fn test_rtm_spin_lock_release() {
let hle = X86LockElision::new(true);
let code = hle.emit_rtm_spin_lock_release("lock_addr");
assert!(code.contains("xtest"));
}
#[test]
fn test_xtest_check() {
let hle = X86LockElision::new(true);
let code = hle.emit_xtest_check("%al");
assert!(code.contains("xtest"));
assert!(code.contains("setnz"));
}
#[test]
fn test_xabort() {
let hle = X86LockElision::new(true);
let code = hle.emit_xabort(0x42);
assert!(code.contains("xabort"));
assert!(code.contains("0x42"));
}
#[test]
fn test_hle_semantics() {
let hle = X86LockElision::new(true);
let semantics = hle.hle_semantics();
assert!(semantics.contains("XACQUIRE"));
assert!(semantics.contains("XRELEASE"));
}
#[test]
fn test_rtm_semantics() {
let hle = X86LockElision::new(true);
let semantics = hle.rtm_semantics();
assert!(semantics.contains("XBEGIN"));
assert!(semantics.contains("XEND"));
assert!(semantics.contains("XABORT"));
}
#[test]
fn test_fallback_acquire() {
let hle = X86LockElision::new(true);
let code = hle.emit_fallback_acquire("lock_addr");
assert!(code.contains("btsl"));
assert!(code.contains("pause"));
}
#[test]
fn test_full_barrier_mfence() {
let barrier = X86Barrier::new_x86_64();
let code = barrier.emit_full_barrier();
assert!(code.contains("mfence"));
}
#[test]
fn test_read_barrier_lfence() {
let barrier = X86Barrier::new_x86_64();
let code = barrier.emit_read_barrier();
assert!(code.contains("lfence"));
}
#[test]
fn test_write_barrier_sfence() {
let barrier = X86Barrier::new_x86_64();
let code = barrier.emit_write_barrier();
assert!(code.contains("sfence"));
}
#[test]
fn test_compiler_barrier() {
let barrier = X86Barrier::new_x86_64();
let code = barrier.emit_compiler_barrier();
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_store_load_barrier() {
let barrier = X86Barrier::new_x86_64();
let code = barrier.emit_store_load_barrier();
assert!(code.contains("mfence"));
}
#[test]
fn test_lock_add_barrier_x86_64() {
let barrier = X86Barrier {
style: X86BarrierStyle::LockAdd,
..X86Barrier::new_x86_64()
};
let code = barrier.emit_full_barrier();
assert!(code.contains("lock addq"));
}
#[test]
fn test_lock_add_barrier_x86_32() {
let barrier = X86Barrier {
style: X86BarrierStyle::LockAdd,
arch: X86Arch::X86_32,
..X86Barrier::new_x86_64()
};
let code = barrier.emit_full_barrier();
assert!(code.contains("lock addl"));
assert!(code.contains("%esp"));
}
#[test]
fn test_cpuid_barrier() {
let barrier = X86Barrier {
style: X86BarrierStyle::CpuId,
..X86Barrier::new_x86_64()
};
let code = barrier.emit_full_barrier();
assert!(code.contains("cpuid"));
}
#[test]
fn test_no_sse2_fallback() {
let barrier = X86Barrier {
has_sse2: false,
..X86Barrier::new_x86_64()
};
let code = barrier.emit_full_barrier();
assert!(!code.contains("mfence"));
}
#[test]
fn test_spectre_mitigation_barrier() {
let barrier = X86Barrier {
spectre_v1_mitigation: true,
..X86Barrier::new_x86_64()
};
let code = barrier.emit_read_barrier();
assert!(code.contains("Spectre"));
}
#[test]
fn test_non_temporal_barrier() {
let barrier = X86Barrier::new_x86_64();
let code = barrier.emit_nt_store_barrier();
assert!(code.contains("sfence"));
}
#[test]
fn test_fence_comparison() {
let barrier = X86Barrier::new_x86_64();
let comp = barrier.fence_comparison();
assert!(comp.contains("MFENCE"));
assert!(comp.contains("LFENCE"));
assert!(comp.contains("SFENCE"));
}
#[test]
fn test_volatile_load() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_volatile_load("%rax", "(%rbx)", "i64");
assert!(code.contains("volatile load"));
assert!(code.contains("compiler_barrier"));
assert!(code.contains("movq"));
}
#[test]
fn test_volatile_store() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_volatile_store("(%rbx)", "%rax", "i64");
assert!(code.contains("volatile store"));
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_volatile_rmw() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_volatile_rmw("(%rbx)", "addl $5", "i32");
assert!(code.contains("volatile RMW"));
assert!(code.contains("lock"));
}
#[test]
fn test_gcc_asm_volatile() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_gcc_asm_volatile(
"movl %1, %0",
&[("=r".into(), "out".into())],
&[("r".into(), "in".into())],
);
assert!(code.contains("asm volatile"));
assert!(code.contains("memory"));
}
#[test]
fn test_msvc_asm_volatile() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_msvc_asm_volatile("mov eax, ebx");
assert!(code.contains("__asm"));
}
#[test]
fn test_llvm_asm_volatile() {
let vo = X86VolatileOps {
asm_style: X86VolatileAsmStyle::LLVM,
..X86VolatileOps::new_x86_64()
};
let code = vo.emit_llvm_asm_volatile("movq $1, $0", "=*m,*m,~{memory}");
assert!(code.contains("asm sideeffect"));
}
#[test]
fn test_mmio_read() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_mmio_read("%rax", "0xFEED0000", 4);
assert!(code.contains("MMIO"));
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_mmio_write() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_mmio_write("0xFEED0000", "%rax", 4);
assert!(code.contains("MMIO"));
assert!(code.contains("sfence"));
}
#[test]
fn test_inline_asm_volatile_load() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_inline_asm_volatile_load("%rax", "ptr", 8);
assert!(code.contains("asm volatile"));
}
#[test]
fn test_inline_asm_volatile_store() {
let vo = X86VolatileOps::new_x86_64();
let code = vo.emit_inline_asm_volatile_store("ptr", "%rax", 8);
assert!(code.contains("asm volatile"));
}
#[test]
fn test_constraint_reference() {
let vo = X86VolatileOps::new_x86_64();
let ref_text = vo.constraint_reference();
assert!(ref_text.contains("\"r\""));
assert!(ref_text.contains("\"m\""));
assert!(ref_text.contains("memory"));
}
#[test]
fn test_concurrency_default() {
let cc = X86Concurrency::default();
assert_eq!(cc.arch, X86Arch::X86_64);
assert_eq!(cc.platform, X86Platform::Linux);
assert!(cc.has_tsx);
assert_eq!(cc.max_atomic_width, 16);
}
#[test]
fn test_concurrency_x86_32() {
let cc = X86Concurrency::new_x86_32();
assert_eq!(cc.arch, X86Arch::X86_32);
assert!(!cc.has_tsx);
assert_eq!(cc.max_atomic_width, 8);
}
#[test]
fn test_concurrency_windows() {
let cc = X86Concurrency::new_windows_x86_64();
assert_eq!(cc.platform, X86Platform::Windows);
}
#[test]
fn test_concurrency_macos() {
let cc = X86Concurrency::new_macos_x86_64();
assert_eq!(cc.platform, X86Platform::macOS);
}
#[test]
fn test_is_lock_free_delegation() {
let cc = X86Concurrency::new_x86_64_linux();
assert!(cc.is_lock_free(4));
assert!(!cc.is_lock_free(32));
}
#[test]
fn test_emit_store_fence() {
let cc = X86Concurrency::new_x86_64_linux();
let fence = cc.emit_store_fence(X86AtomicOrdering::Release);
assert!(fence.is_some());
}
#[test]
fn test_emit_load_fence() {
let cc = X86Concurrency::new_x86_64_linux();
let fence = cc.emit_load_fence(X86AtomicOrdering::SeqCst);
assert!(fence.is_some());
}
#[test]
fn test_emit_full_barrier() {
let cc = X86Concurrency::new_x86_64_linux();
let code = cc.emit_full_barrier();
assert!(code.contains("mfence"));
}
#[test]
fn test_should_use_hle() {
let cc = X86Concurrency::new_x86_64_linux();
assert!(cc.should_use_hle());
let cc_no_tsx = X86Concurrency::new_x86_32();
assert!(!cc_no_tsx.should_use_hle());
}
#[test]
fn test_emit_tls_access() {
let cc = X86Concurrency::new_x86_64_linux();
let code = cc.emit_tls_access("my_tls", 16);
assert!(code.contains("my_tls"));
}
#[test]
fn test_emit_thread_create() {
let cc = X86Concurrency::new_x86_64_linux();
let code = cc.emit_thread_create("func", "arg");
assert!(code.contains("pthread_create"));
}
#[test]
fn test_emit_thread_join() {
let cc = X86Concurrency::new_x86_64_linux();
let code = cc.emit_thread_join("%rax");
assert!(code.contains("pthread_join"));
}
#[test]
fn test_emit_futex_wait() {
let cc = X86Concurrency::new_x86_64_linux();
let code = cc.emit_futex_wait("futex_addr", 42);
assert!(code.contains("syscall"));
}
#[test]
fn test_emit_futex_wake() {
let cc = X86Concurrency::new_x86_64_linux();
let code = cc.emit_futex_wake("futex_addr", 1);
assert!(code.contains("syscall"));
}
#[test]
fn test_emit_volatile_load() {
let cc = X86Concurrency::new_x86_64_linux();
let code = cc.emit_volatile_load("%rax", "(%rbx)", "i64");
assert!(code.contains("volatile load"));
}
#[test]
fn test_emit_volatile_store() {
let cc = X86Concurrency::new_x86_64_linux();
let code = cc.emit_volatile_store("(%rbx)", "%rax", "i64");
assert!(code.contains("volatile store"));
}
#[test]
fn test_concurrency_describe() {
let cc = X86Concurrency::new_x86_64_linux();
let desc = cc.describe();
assert!(desc.contains("X86Concurrency"));
assert!(desc.contains("x86-64"));
assert!(desc.contains("linux"));
}
#[test]
fn test_x86_arch_display() {
assert_eq!(format!("{}", X86Arch::X86_64), "x86-64");
assert_eq!(format!("{}", X86Arch::X86_32), "x86-32");
assert_eq!(format!("{}", X86Arch::X86_16), "x86-16");
}
#[test]
fn test_x86_platform_display() {
assert_eq!(format!("{}", X86Platform::Linux), "linux");
assert_eq!(format!("{}", X86Platform::Windows), "windows");
assert_eq!(format!("{}", X86Platform::macOS), "macos");
}
#[test]
fn test_atomic_operand_new_lock_free() {
let model = X86AtomicModel::new_x86_64();
let op = X86AtomicOperand::new(
8,
8,
X86AtomicOrdering::SeqCst,
X86AtomicOpKind::FetchAdd,
&model,
);
assert!(op.is_lock_free);
assert!(op.is_aligned);
assert_eq!(op.suffix(), "q");
assert!(op.needs_lock_prefix());
assert!(!op.needs_cas_loop());
}
#[test]
fn test_atomic_operand_misaligned() {
let model = X86AtomicModel::new_x86_64();
let op = X86AtomicOperand::new(
8,
4,
X86AtomicOrdering::SeqCst,
X86AtomicOpKind::Load,
&model,
);
assert!(!op.is_aligned);
}
#[test]
fn test_atomic_operand_cas_loop() {
let model = X86AtomicModel::new_x86_64();
let op = X86AtomicOperand::new(
4,
4,
X86AtomicOrdering::SeqCst,
X86AtomicOpKind::FetchAnd,
&model,
);
assert!(op.needs_cas_loop());
}
#[test]
fn test_atomic_op_kind_display() {
assert_eq!(format!("{}", X86AtomicOpKind::FetchAdd), "fetch_add");
assert_eq!(
format!("{}", X86AtomicOpKind::CompareExchange),
"compare_exchange"
);
assert_eq!(format!("{}", X86AtomicOpKind::FetchUMax), "fetch_umax");
}
#[test]
fn test_c11_atomic_is_lock_free() {
let model = X86AtomicModel::new_x86_64();
let mapping = X86C11AtomicMapping::new(model, X86MemoryOrder::default());
assert!(mapping.c11_atomic_is_lock_free(4));
assert!(!mapping.c11_atomic_is_lock_free(3));
}
#[test]
fn test_c11_atomic_flag_size() {
let model = X86AtomicModel::new_x86_64();
let mapping = X86C11AtomicMapping::new(model, X86MemoryOrder::default());
let (size, align) = mapping.c11_atomic_flag_size();
assert_eq!(size, 1);
assert_eq!(align, 1);
}
#[test]
fn test_c11_atomic_thread_fence_relaxed() {
let model = X86AtomicModel::new_x86_64();
let mapping = X86C11AtomicMapping::new(model, X86MemoryOrder::default());
let code = mapping.c11_atomic_thread_fence(X86AtomicOrdering::Relaxed);
assert!(code.contains("nothing"));
}
#[test]
fn test_c11_atomic_thread_fence_seq_cst() {
let model = X86AtomicModel::new_x86_64();
let mapping = X86C11AtomicMapping::new(model, X86MemoryOrder::default());
let code = mapping.c11_atomic_thread_fence(X86AtomicOrdering::SeqCst);
assert!(code.contains("mfence"));
}
#[test]
fn test_c11_atomic_signal_fence() {
let model = X86AtomicModel::new_x86_64();
let mapping = X86C11AtomicMapping::new(model, X86MemoryOrder::default());
let code = mapping.c11_atomic_signal_fence(X86AtomicOrdering::SeqCst);
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_c11_atomic_load() {
let model = X86AtomicModel::new_x86_64();
let mapping = X86C11AtomicMapping::new(model, X86MemoryOrder::default());
let code = mapping.c11_atomic_load("%rax", "(%rbx)", 8, X86AtomicOrdering::Acquire);
assert!(code.contains("movq"));
}
#[test]
fn test_c11_atomic_store_seq_cst() {
let model = X86AtomicModel::new_x86_64();
let mapping = X86C11AtomicMapping::new(model, X86MemoryOrder::default());
let code = mapping.c11_atomic_store("(%rbx)", "%rax", 8, X86AtomicOrdering::SeqCst);
assert!(code.contains("xchg"));
}
#[test]
fn test_size_class_byte() {
let model = X86AtomicModel::new_x86_64();
let sc = X86AtomicSizeClass::classify(1, &model);
assert_eq!(sc.size_class, 0);
assert!(sc.is_lock_free);
assert_eq!(sc.suffix, "b");
}
#[test]
fn test_size_class_qword() {
let model = X86AtomicModel::new_x86_64();
let sc = X86AtomicSizeClass::classify(8, &model);
assert_eq!(sc.size_class, 3);
assert!(sc.is_lock_free);
assert_eq!(sc.suffix, "q");
}
#[test]
fn test_size_class_dqword() {
let model = X86AtomicModel::new_x86_64();
let sc = X86AtomicSizeClass::classify(16, &model);
assert_eq!(sc.size_class, 4);
assert!(sc.is_lock_free);
assert!(sc.needs_cmpxchg16b);
}
#[test]
fn test_size_class_xadd_suffix() {
let model = X86AtomicModel::new_x86_64();
let sc1 = X86AtomicSizeClass::classify(1, &model);
assert_eq!(sc1.xadd_suffix(), "b");
let sc8 = X86AtomicSizeClass::classify(8, &model);
assert_eq!(sc8.xadd_suffix(), "q");
}
#[test]
fn test_atomic_flag_new() {
let flag = X86AtomicFlag::new("flag_addr");
assert_eq!(flag.address, "flag_addr");
assert!(!flag.is_set);
assert_eq!(flag.ordering, X86AtomicOrdering::SeqCst);
}
#[test]
fn test_atomic_flag_test_and_set() {
let flag = X86AtomicFlag::new("flag_addr");
let code = flag.emit_test_and_set();
assert!(code.contains("btsl"));
assert!(code.contains("setc"));
}
#[test]
fn test_atomic_flag_clear() {
let flag = X86AtomicFlag::new("flag_addr");
let code = flag.emit_clear();
assert!(code.contains("clear flag"));
}
#[test]
fn test_atomic_flag_with_ordering() {
let flag = X86AtomicFlag::new("flag_addr").with_ordering(X86AtomicOrdering::Relaxed);
assert_eq!(flag.ordering, X86AtomicOrdering::Relaxed);
}
#[test]
fn test_oracle_decide_seq_cst_store() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc);
let strategy = oracle.decide_seq_cst_store_strategy();
assert!(strategy.contains("xchg"));
}
#[test]
fn test_oracle_should_use_hle() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc);
assert!(oracle.should_use_hle());
}
#[test]
fn test_oracle_optimal_lock_type() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc).with_opt_level(X86ConcurrencyOptLevel::O0);
assert_eq!(oracle.optimal_spin_lock_type(), X86SpinLockType::TestAndSet);
}
#[test]
fn test_oracle_with_pgo() {
let cc = X86Concurrency::new_x86_64_linux();
let pgo = X86ConcurrencyPGO {
avg_contention: 0.5,
preferred_lock_type: X86SpinLockType::Ticket,
..Default::default()
};
let oracle = X86ConcurrencyOracle::new(cc).with_pgo(pgo);
assert_eq!(oracle.optimal_spin_lock_type(), X86SpinLockType::Ticket);
}
#[test]
fn test_oracle_describe() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc);
let desc = oracle.describe_decisions();
assert!(desc.contains("Oracle"));
}
#[test]
fn test_codegen_critical_section() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc);
let mut codegen = X86ConcurrencyCodeGen::new(oracle);
let code = codegen.emit_critical_section("lock", "movl $42, %eax");
assert!(code.contains("Critical section"));
assert!(code.contains("movl $42"));
}
#[test]
fn test_codegen_atomic_inc() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc);
let mut codegen = X86ConcurrencyCodeGen::new(oracle);
let code = codegen.emit_atomic_inc("(%rbx)", 4, X86AtomicOrdering::SeqCst);
assert!(code.contains("lock incl"));
assert!(code.contains("mfence"));
}
#[test]
fn test_codegen_atomic_dec() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc);
let mut codegen = X86ConcurrencyCodeGen::new(oracle);
let code = codegen.emit_atomic_dec("(%rbx)", 4, X86AtomicOrdering::Relaxed);
assert!(code.contains("lock decl"));
assert!(!code.contains("mfence"));
}
#[test]
fn test_codegen_seqlock_read_begin() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc);
let mut codegen = X86ConcurrencyCodeGen::new(oracle);
let code = codegen.emit_seqlock_read_begin("lock", "seq");
assert!(code.contains("Seqlock"));
}
#[test]
fn test_codegen_seqlock_write_begin() {
let cc = X86Concurrency::new_x86_64_linux();
let oracle = X86ConcurrencyOracle::new(cc);
let mut codegen = X86ConcurrencyCodeGen::new(oracle);
let code = codegen.emit_seqlock_write_begin("lock", "seq");
assert!(code.contains("Seqlock"));
assert!(code.contains("btsl"));
}
#[test]
fn test_fence_model_tso() {
let model = X86MemoryFenceModel::tso();
assert!(!model.store_load_ordering);
assert!(model.store_store_ordering);
assert!(model.load_load_ordering);
assert!(model.load_store_ordering);
}
#[test]
fn test_fence_model_strong_tso() {
let model = X86MemoryFenceModel::strong_tso();
assert!(model.store_load_ordering);
}
#[test]
fn test_required_fence_store_load() {
let model = X86MemoryFenceModel::tso();
let fence = model.required_fence(true, true); assert!(fence.is_some());
assert_eq!(fence.unwrap(), "mfence");
}
#[test]
fn test_required_fence_load_load() {
let model = X86MemoryFenceModel::tso();
let fence = model.required_fence(false, true); assert!(fence.is_none()); }
#[test]
fn test_fence_model_describe() {
let model = X86MemoryFenceModel::tso();
let desc = model.describe();
assert!(desc.contains("Store→Load"));
}
#[test]
fn test_sync_fetch_and_add() {
let model = X86AtomicModel::new_x86_64();
let builtins = X86AtomicBuiltins::new(model);
let code = builtins.sync_fetch_and_add("(%rbx)", 1, 4);
assert!(code.contains("lock xaddl"));
assert!(code.contains("__sync_fetch_and_add"));
}
#[test]
fn test_sync_synchronize() {
let model = X86AtomicModel::new_x86_64();
let builtins = X86AtomicBuiltins::new(model);
let code = builtins.sync_synchronize();
assert!(code.contains("mfence"));
}
#[test]
fn test_sync_lock_test_and_set() {
let model = X86AtomicModel::new_x86_64();
let builtins = X86AtomicBuiltins::new(model);
let code = builtins.sync_lock_test_and_set("(%rbx)", 1, 4);
assert!(code.contains("xchg"));
}
#[test]
fn test_sync_bool_compare_and_swap() {
let model = X86AtomicModel::new_x86_64();
let builtins = X86AtomicBuiltins::new(model);
let code = builtins.sync_bool_compare_and_swap("(%rbx)", 0, 1, 4);
assert!(code.contains("cmpxchg"));
assert!(code.contains("sete"));
}
#[test]
fn test_atomic_thread_fence_builtin() {
let model = X86AtomicModel::new_x86_64();
let builtins = X86AtomicBuiltins::new(model);
let code = builtins.atomic_thread_fence(X86AtomicOrdering::SeqCst);
assert!(code.contains("mfence"));
}
#[test]
fn test_atomic_signal_fence_builtin() {
let model = X86AtomicModel::new_x86_64();
let builtins = X86AtomicBuiltins::new(model);
let code = builtins.atomic_signal_fence(X86AtomicOrdering::SeqCst);
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_rwlock_read_lock() {
let rwl = X86ReadWriteLock::new("rwlock");
let code = rwl.emit_read_lock();
assert!(code.contains("RWLock"));
assert!(code.contains("lock incl"));
}
#[test]
fn test_rwlock_read_unlock() {
let rwl = X86ReadWriteLock::new("rwlock");
let code = rwl.emit_read_unlock();
assert!(code.contains("lock decl"));
}
#[test]
fn test_rwlock_write_lock() {
let rwl = X86ReadWriteLock::new("rwlock");
let code = rwl.emit_write_lock();
assert!(code.contains("cmpxchg"));
assert!(code.contains("0x80000000"));
}
#[test]
fn test_rwlock_write_unlock() {
let rwl = X86ReadWriteLock::new("rwlock");
let code = rwl.emit_write_unlock();
assert!(code.contains("movl $0"));
}
#[test]
fn test_rwlock_read_to_write_upgrade() {
let rwl = X86ReadWriteLock::new("rwlock");
let code = rwl.emit_read_to_write_upgrade();
assert!(code.contains("cmpxchg"));
assert!(code.contains("sete"));
}
#[test]
fn test_lock_free_stack_push() {
let model = X86AtomicModel::new_x86_64();
let stack = X86LockFreeStack::new("top", &model);
let code = stack.emit_push("%rax");
assert!(code.contains("Lock-free stack push"));
assert!(code.contains("cmpxchgq"));
}
#[test]
fn test_lock_free_stack_pop() {
let model = X86AtomicModel::new_x86_64();
let stack = X86LockFreeStack::new("top", &model);
let code = stack.emit_pop("%rax");
assert!(code.contains("Lock-free stack pop"));
assert!(code.contains("cmpxchgq"));
}
#[test]
fn test_lock_free_queue_enqueue() {
let model = X86AtomicModel::new_x86_64();
let queue = X86LockFreeQueue::new("head", "tail", &model);
let code = queue.emit_enqueue("%rax");
assert!(code.contains("Lock-free queue enqueue"));
}
#[test]
fn test_lock_free_queue_dequeue() {
let model = X86AtomicModel::new_x86_64();
let queue = X86LockFreeQueue::new("head", "tail", &model);
let code = queue.emit_dequeue("%rax");
assert!(code.contains("Lock-free queue dequeue"));
}
#[test]
fn test_hazard_pointer_protect() {
let hp = X86HazardPointer::new(2, 10);
let code = hp.emit_protect("hp_slot", "%rax");
assert!(code.contains("Hazard pointer"));
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_hazard_pointer_clear() {
let hp = X86HazardPointer::new(2, 10);
let code = hp.emit_clear("hp_slot");
assert!(code.contains("movq $0"));
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_hazard_pointer_scan() {
let hp = X86HazardPointer::new(2, 10);
let code = hp.emit_scan_for("ptr_value", "hp_array", 4);
assert!(code.contains("Hazard pointer scan"));
}
#[test]
fn test_rcu_read_lock() {
let rcu = X86RcuSimulator::new();
let code = rcu.emit_read_lock();
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_rcu_read_unlock() {
let rcu = X86RcuSimulator::new();
let code = rcu.emit_read_unlock();
assert!(code.contains("compiler_barrier"));
}
#[test]
fn test_rcu_synchronize() {
let rcu = X86RcuSimulator::new();
let code = rcu.emit_synchronize_rcu();
assert!(code.contains("mfence"));
}
#[test]
fn test_rcu_call_rcu() {
let rcu = X86RcuSimulator::new();
let code = rcu.emit_call_rcu("callback_fn", "%rax");
assert!(code.contains("call_rcu"));
}
#[test]
fn test_tsx_stats_initial() {
let stats = X86TransactionalMemoryStats::new();
assert_eq!(stats.attempts, 0);
assert_eq!(stats.commits, 0);
assert!(stats.tsx_recommended);
assert_eq!(stats.abort_rate(), 0.0);
}
#[test]
fn test_tsx_stats_record_commit() {
let mut stats = X86TransactionalMemoryStats::new();
stats.record_commit();
assert_eq!(stats.commits, 1);
assert_eq!(stats.attempts, 1);
}
#[test]
fn test_tsx_stats_record_abort() {
let mut stats = X86TransactionalMemoryStats::new();
stats.record_abort(0x08); assert_eq!(stats.capacity_aborts, 1);
stats.record_abort(0x10); assert_eq!(stats.conflict_aborts, 1);
}
#[test]
fn test_tsx_stats_abort_rate() {
let mut stats = X86TransactionalMemoryStats::new();
stats.record_commit();
stats.record_commit();
stats.record_abort(0x10);
assert!((stats.abort_rate() - 1.0 / 3.0).abs() < 0.001);
}
#[test]
fn test_tsx_stats_update_recommendation() {
let mut stats = X86TransactionalMemoryStats::new();
for _ in 0..10 {
stats.record_abort(0x10);
}
stats.record_commit();
stats.update_recommendation();
assert!(!stats.tsx_recommended); }
#[test]
fn test_tsx_stats_reset() {
let mut stats = X86TransactionalMemoryStats::new();
stats.record_commit();
stats.record_abort(0x10);
stats.reset();
assert_eq!(stats.attempts, 0);
assert_eq!(stats.commits, 0);
assert!(stats.tsx_recommended);
}
#[test]
fn test_tsx_stats_summarize() {
let stats = X86TransactionalMemoryStats::new();
let summary = stats.summarize();
assert!(summary.contains("TSX Stats"));
}
#[test]
fn test_tsan_hooks_disabled() {
let tsan = X86ThreadSanitizerHooks::new();
let code = tsan.emit_read_hook("(%rbx)", 4);
assert!(code.is_empty());
}
#[test]
fn test_tsan_hooks_enabled() {
let mut tsan = X86ThreadSanitizerHooks::new();
tsan.enable();
let code = tsan.emit_read_hook("(%rbx)", 4);
assert!(code.contains("__tsan_read4"));
}
#[test]
fn test_tsan_write_hook() {
let mut tsan = X86ThreadSanitizerHooks::new();
tsan.enable();
let code = tsan.emit_write_hook("(%rbx)", 8);
assert!(code.contains("__tsan_write8"));
}
#[test]
fn test_tsan_func_entry() {
let mut tsan = X86ThreadSanitizerHooks::new();
tsan.enable();
let code = tsan.emit_func_entry();
assert!(code.contains("__tsan_func_entry"));
}
#[test]
fn test_tsan_func_exit() {
let mut tsan = X86ThreadSanitizerHooks::new();
tsan.enable();
let code = tsan.emit_func_exit();
assert!(code.contains("__tsan_func_exit"));
}
#[test]
fn test_tsan_atomic_hook() {
let mut tsan = X86ThreadSanitizerHooks::new();
tsan.enable();
let code = tsan.emit_atomic_hook("(%rbx)", 8, true);
assert!(code.contains("__tsan_atomic_store_8"));
}
#[test]
fn test_validator_valid_config() {
let cc = X86Concurrency::new_x86_64_linux();
let mut validator = X86ConcurrencyValidator::new();
assert!(validator.validate(&cc));
assert!(validator.errors.is_empty());
}
#[test]
fn test_validator_invalid_atomic_width() {
let mut cc = X86Concurrency::new_x86_64_linux();
cc.atomic_model.max_lock_free_width = 0;
let mut validator = X86ConcurrencyValidator::new();
assert!(!validator.validate(&cc));
assert!(!validator.errors.is_empty());
}
#[test]
fn test_validator_hle_without_tsx() {
let mut cc = X86Concurrency::new_x86_64_linux();
cc.has_tsx = false;
cc.lock_elision.enabled = true;
let mut validator = X86ConcurrencyValidator::new();
validator.validate(&cc);
assert!(validator.warnings.iter().any(|w| w.contains("TSX")));
}
#[test]
fn test_validator_report() {
let cc = X86Concurrency::new_x86_64_linux();
let mut validator = X86ConcurrencyValidator::new();
validator.validate(&cc);
let report = validator.report();
assert!(report.contains("Validation Report"));
}
#[test]
fn test_validator_report_with_errors() {
let mut cc = X86Concurrency::new_x86_64_linux();
cc.atomic_model.max_lock_free_width = 0;
let mut validator = X86ConcurrencyValidator::new();
validator.validate(&cc);
let report = validator.report();
assert!(report.contains("ERROR"));
}
#[test]
fn test_driver_default() {
let driver = X86ConcurrencyDriver::default();
assert_eq!(driver.config.arch, X86Arch::X86_64);
assert_eq!(driver.config.platform, X86Platform::Linux);
}
#[test]
fn test_driver_new_windows() {
let driver = X86ConcurrencyDriver::new_windows_x86_64();
assert_eq!(driver.config.platform, X86Platform::Windows);
}
#[test]
fn test_driver_validate() {
let mut driver = X86ConcurrencyDriver::default();
assert!(driver.validate());
}
#[test]
fn test_driver_validation_report() {
let mut driver = X86ConcurrencyDriver::default();
driver.validate();
let report = driver.validation_report();
assert!(!report.is_empty());
}
#[test]
fn test_driver_emit_optimal_fetch_add() {
let driver = X86ConcurrencyDriver::default();
let code =
driver.emit_optimal_fetch_add("%rax", "(%rbx)", "%rcx", 8, X86AtomicOrdering::SeqCst);
assert!(code.contains("xadd"));
}
#[test]
fn test_driver_emit_tls_access() {
let driver = X86ConcurrencyDriver::default();
let code = driver.emit_tls_access("my_var", 16);
assert!(code.contains("my_var"));
}
#[test]
fn test_driver_emit_futex_mutex_lock() {
let driver = X86ConcurrencyDriver::default();
let code = driver.emit_futex_mutex_lock("mutex");
assert!(code.contains("Futex-based mutex lock"));
}
#[test]
fn test_driver_emit_futex_mutex_unlock() {
let driver = X86ConcurrencyDriver::default();
let code = driver.emit_futex_mutex_unlock("mutex");
assert!(code.contains("Futex-based mutex unlock"));
}
#[test]
fn test_driver_describe() {
let driver = X86ConcurrencyDriver::default();
let desc = driver.describe();
assert!(desc.contains("X86ConcurrencyDriver"));
assert!(desc.contains("TSO"));
}
#[test]
fn test_size_to_mov_suffix() {
assert_eq!(size_to_mov_suffix(1), "b");
assert_eq!(size_to_mov_suffix(2), "w");
assert_eq!(size_to_mov_suffix(4), "l");
assert_eq!(size_to_mov_suffix(8), "q");
assert_eq!(size_to_mov_suffix(16), "dqa");
assert_eq!(size_to_mov_suffix(32), "q"); }
#[test]
fn test_hash_fast_consistent() {
let h1 = hash_fast("test_string");
let h2 = hash_fast("test_string");
assert_eq!(h1, h2);
}
#[test]
fn test_hash_str_consistent() {
let h1 = hash_str("test_string");
let h2 = hash_str("test_string");
assert_eq!(h1, h2);
}
#[test]
fn test_pause_instr() {
assert_eq!(pause_instr(true), "pause\n\t");
assert_eq!(pause_instr(false), "");
}
#[test]
fn test_type_size_str() {
assert_eq!(type_size_str("char"), "1");
assert_eq!(type_size_str("int"), "4");
assert_eq!(type_size_str("long"), "8");
assert_eq!(type_size_str("unknown"), "8");
}
#[test]
fn test_type_directive() {
assert_eq!(type_directive("char"), "byte");
assert_eq!(type_directive("int"), "long");
assert_eq!(type_directive("long"), "quad");
assert_eq!(type_directive("unknown"), "quad");
}
#[test]
fn test_end_to_end_concurrent_workflow() {
let driver = X86ConcurrencyDriver::default();
let sl = X86SpinLock::default();
let acquire = sl.emit_tas_lock_acquire("my_lock");
assert!(acquire.contains("btsl"));
let model = X86AtomicModel::new_x86_64();
let fetch_add =
model.emit_atomic_fetch_add("%rax", "(%rbx)", "%rcx", 8, X86AtomicOrdering::AcqRel);
assert!(fetch_add.contains("lock xaddq"));
let cas = model.emit_atomic_compare_exchange(
"(%rbx)",
"%rax",
"%rcx",
8,
X86AtomicOrdering::SeqCst,
X86AtomicOrdering::Acquire,
false,
);
assert!(cas.contains("cmpxchg"));
let release = sl.emit_tas_lock_release("my_lock");
assert!(release.contains("movl $0"));
let tls = driver.emit_tls_access("counter", 0);
assert!(tls.contains("counter"));
let create = driver.config.emit_thread_create("worker", "arg");
assert!(create.contains("pthread_create"));
let join = driver.config.emit_thread_join("%rax");
assert!(join.contains("pthread_join"));
}
#[test]
fn test_all_architectures_have_concurrency() {
let drivers = vec![
X86ConcurrencyDriver::new_x86_64_linux(),
X86ConcurrencyDriver::new_windows_x86_64(),
];
for driver in &drivers {
let desc = driver.describe();
assert!(desc.contains("X86ConcurrencyDriver"));
}
}
#[test]
fn test_all_fence_types() {
let barrier = X86Barrier::new_x86_64();
assert!(!barrier.emit_full_barrier().is_empty());
assert!(!barrier.emit_read_barrier().is_empty());
assert!(!barrier.emit_write_barrier().is_empty());
assert!(!barrier.emit_store_load_barrier().is_empty());
assert!(!barrier.emit_data_dependency_barrier().is_empty());
}
#[test]
fn test_all_atomic_operations_compile() {
let model = X86AtomicModel::new_x86_64();
for size in &[1u8, 2, 4, 8] {
let _ = model.emit_atomic_load("%rax", "(%rbx)", *size, X86AtomicOrdering::SeqCst);
let _ = model.emit_atomic_store("%rax", "(%rbx)", *size, X86AtomicOrdering::SeqCst);
let _ = model.emit_atomic_exchange(
"%rax",
"(%rbx)",
"%rcx",
*size,
X86AtomicOrdering::SeqCst,
);
let _ = model.emit_atomic_fetch_add(
"%rax",
"(%rbx)",
"%rcx",
*size,
X86AtomicOrdering::SeqCst,
);
let _ = model.emit_atomic_fetch_sub(
"%rax",
"(%rbx)",
"%rcx",
*size,
X86AtomicOrdering::SeqCst,
);
let _ = model.emit_atomic_fetch_and(
"%rax",
"(%rbx)",
"%rcx",
*size,
X86AtomicOrdering::SeqCst,
);
let _ = model.emit_atomic_fetch_or(
"%rax",
"(%rbx)",
"%rcx",
*size,
X86AtomicOrdering::SeqCst,
);
let _ = model.emit_atomic_fetch_xor(
"%rax",
"(%rbx)",
"%rcx",
*size,
X86AtomicOrdering::SeqCst,
);
let _ = model.emit_atomic_compare_exchange(
"(%rbx)",
"%rax",
"%rcx",
*size,
X86AtomicOrdering::SeqCst,
X86AtomicOrdering::Acquire,
false,
);
}
}
#[test]
fn test_all_ordering_cross_product() {
let orders = [
X86AtomicOrdering::Relaxed,
X86AtomicOrdering::Consume,
X86AtomicOrdering::Acquire,
X86AtomicOrdering::Release,
X86AtomicOrdering::AcqRel,
X86AtomicOrdering::SeqCst,
];
for &o in &orders {
assert_eq!(o.to_c11(), X86AtomicOrdering::from_c11(o.to_c11()).to_c11());
assert!(!o.to_llvm_str().is_empty());
let _ = format!("{}", o);
}
}
#[test]
fn test_platform_specific_features() {
let linux = X86FutexSupport::linux();
assert!(linux.has_futex_syscall);
let windows = X86FutexSupport::windows();
assert!(windows.has_wait_on_address);
let macos = X86FutexSupport::macos();
assert!(macos.has_ulock);
}
#[test]
fn test_mutex_lock_unlock_pair() {
let futex = X86FutexSupport::linux();
let lock = futex.emit_futex_mutex_lock("m");
let unlock = futex.emit_futex_mutex_unlock("m");
assert!(lock.contains("Futex-based mutex lock"));
assert!(unlock.contains("Futex-based mutex unlock"));
}
#[test]
fn test_condvar_wait_signal() {
let futex = X86FutexSupport::linux();
let wait = futex.emit_futex_condvar_wait("cv", "m");
let signal = futex.emit_futex_condvar_signal("cv");
let broadcast = futex.emit_futex_condvar_broadcast("cv");
assert!(!wait.is_empty());
assert!(!signal.is_empty());
assert!(!broadcast.is_empty());
}
#[test]
fn test_semaphore_wait_post() {
let futex = X86FutexSupport::linux();
let wait = futex.emit_futex_semaphore_wait("sem");
let post = futex.emit_futex_semaphore_post("sem");
assert!(!wait.is_empty());
assert!(!post.is_empty());
}
#[test]
fn test_memory_model_kind_display() {
assert_eq!(format!("{}", X86MemoryModelKind::TSO), "TSO");
assert_eq!(format!("{}", X86MemoryModelKind::StrongTSO), "StrongTSO");
assert_eq!(format!("{}", X86MemoryModelKind::WeakTSO), "WeakTSO");
}
#[test]
fn test_fence_strategy_default() {
let strategy = X86FenceStrategy::default();
assert_eq!(strategy, X86FenceStrategy::Default);
}
#[test]
fn test_tls_model_pref_default() {
let pref = X86TlsModelPreference::default();
assert_eq!(pref, X86TlsModelPreference::InitialExec);
}
#[test]
fn test_concurrency_pgo_default() {
let pgo = X86ConcurrencyPGO::default();
assert!((pgo.avg_contention - 0.05).abs() < 0.001);
assert_eq!(pgo.preferred_lock_type, X86SpinLockType::Adaptive);
assert!(pgo.adaptive_spinning_beneficial);
}
}