Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/.cspell/project-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ devtmpfs
dlfcn
dlsym
ecall
Espressif
espup
exynos
fild
Expand Down Expand Up @@ -155,6 +156,7 @@ poweroff
prctl
prefetcher
PRIMASK
PSRAM
pstq
quadword
rcpc
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,7 @@ jobs:
# env:
# TESTS: 1
# if: matrix.rust == 'stable'
- run: tools/no-std.sh +esp xtensa-esp32s2-none-elf
- run: tools/no-std.sh +esp xtensa-esp32s2-none-elf xtensa-esp32s3-none-elf
Comment thread
bugadani marked this conversation as resolved.
if: matrix.rust == 'stable'

miri:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com

## [Unreleased]

- Xtensa: route atomic operations on ESP32 / ESP32-S3 PSRAM addresses through a critical section.

## [1.15.0] - 2026-08-09

- Work around [`core::sync::atomic` bug](https://github.com/rust-lang/compiler-builtins/pull/1234) which may cause data races with non-relaxed load/CAS on pre-v6 ARM Linux/Android due to missing memory barrier. ([#112](https://github.com/taiki-e/portable-atomic/pull/112))
Expand Down
37 changes: 36 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,16 @@ fn main() {
// Custom cfgs set by build script. Not public API.
// grep -F 'cargo:rustc-cfg=' build.rs | grep -Ev '^ *//' | sed -E 's/^.*cargo:rustc-cfg=//; s/(=\\)?".*$//' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/'
println!(
"cargo:rustc-check-cfg=cfg(portable_atomic_atomic_intrinsics,portable_atomic_disable_fiq,portable_atomic_force_amo,portable_atomic_ll_sc_rmw,portable_atomic_no_asm,portable_atomic_no_asm_maybe_uninit,portable_atomic_no_asm_syscall,portable_atomic_no_atomic_64,portable_atomic_no_atomic_cas,portable_atomic_no_atomic_load_store,portable_atomic_no_atomic_min_max,portable_atomic_no_cfg_target_has_atomic,portable_atomic_no_cmpxchg16b_intrinsic,portable_atomic_no_cmpxchg16b_target_feature,portable_atomic_no_const_mut_refs,portable_atomic_no_const_raw_ptr_deref,portable_atomic_no_const_transmute,portable_atomic_no_core_unwind_safe,portable_atomic_no_diagnostic_namespace,portable_atomic_no_strict_provenance,portable_atomic_no_strict_provenance_atomic_ptr,portable_atomic_no_stronger_failure_ordering,portable_atomic_no_track_caller,portable_atomic_no_unsafe_op_in_unsafe_fn,portable_atomic_pre_llvm_16,portable_atomic_pre_llvm_18,portable_atomic_pre_llvm_20,portable_atomic_s_mode,portable_atomic_sanitize_thread,portable_atomic_target_feature,portable_atomic_unsafe_assume_privileged,portable_atomic_unsafe_assume_single_core,portable_atomic_unstable_asm,portable_atomic_unstable_asm_experimental_arch,portable_atomic_unstable_cfg_target_has_atomic,portable_atomic_unstable_isa_attribute)"
"cargo:rustc-check-cfg=cfg(portable_atomic_atomic_intrinsics,portable_atomic_disable_fiq,portable_atomic_force_amo,portable_atomic_ll_sc_rmw,portable_atomic_no_asm,portable_atomic_no_asm_maybe_uninit,portable_atomic_no_asm_syscall,portable_atomic_no_atomic_64,portable_atomic_no_atomic_cas,portable_atomic_no_atomic_load_store,portable_atomic_no_atomic_min_max,portable_atomic_no_cfg_target_has_atomic,portable_atomic_no_cmpxchg16b_intrinsic,portable_atomic_no_cmpxchg16b_target_feature,portable_atomic_no_const_mut_refs,portable_atomic_no_const_raw_ptr_deref,portable_atomic_no_const_transmute,portable_atomic_no_core_unwind_safe,portable_atomic_no_diagnostic_namespace,portable_atomic_no_strict_provenance,portable_atomic_no_strict_provenance_atomic_ptr,portable_atomic_no_stronger_failure_ordering,portable_atomic_no_track_caller,portable_atomic_no_unsafe_op_in_unsafe_fn,portable_atomic_pre_llvm_16,portable_atomic_pre_llvm_18,portable_atomic_pre_llvm_20,portable_atomic_s_mode,portable_atomic_sanitize_thread,portable_atomic_target_cpu,portable_atomic_target_feature,portable_atomic_unsafe_assume_privileged,portable_atomic_unsafe_assume_single_core,portable_atomic_unstable_asm,portable_atomic_unstable_asm_experimental_arch,portable_atomic_unstable_cfg_target_has_atomic,portable_atomic_unstable_isa_attribute)"
);
// TODO: handle multi-line target_feature_fallback
// grep -F 'target_feature_fallback("' build.rs | grep -Ev '^ *//' | sed -E 's/^.*target_feature_fallback\(//; s/",.*$/"/' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/'
println!(
r#"cargo:rustc-check-cfg=cfg(portable_atomic_target_feature,values("cmpxchg16b","distinct-ops","fast-serialization","load-store-on-cond","lse","lse128","lse2","lsfe","mclass","miscellaneous-extensions-3","quadword-atomics","rcpc3","rmw","v6","v7","zaamo","zabha","zacas"))"#
);
println!(
r#"cargo:rustc-check-cfg=cfg(portable_atomic_target_cpu,values("esp32","esp32s3"))"#
);
}

// https://github.com/rust-lang/rust/pull/123745 (includes https://github.com/rust-lang/cargo/pull/13560) merged in Rust 1.79 (nightly-2024-04-11).
Expand Down Expand Up @@ -527,6 +530,33 @@ fn main() {
target_feature_fallback("rmw", xmegau);
}
}
"xtensa" => {
// ESP32 / ESP32-S3 advertise atomic CAS in core atomics, but LLVM's
// atomic instructions do not behave atomically on addresses backed by
// external (PSRAM / data bus) memory. The `src/imp/xtensa.rs` wrapper
// dispatches each operation based on the address at runtime; to
// select it we need to know which of the two affected CPUs we are
// targeting. Infer from `target-cpu`, or from the known Espressif
// target triples when no target-cpu is specified.
// ESP32-S2 has no atomic CAS at all and is handled by the generic
// no-CAS path, so it is intentionally absent here.
if let Some(cpu) = target_cpu() {
if cpu == "esp32" || cpu == "esp32s3" {
target_cpu_fallback(&cpu);
}
} else {
let esp_cpu = match target {
"xtensa-esp32-none-elf" | "xtensa-esp32-espidf" => Some("esp32"),
"xtensa-esp32s3-none-elf" | "xtensa-esp32s3-espidf" => Some("esp32s3"),
// ESP32-S2 does not have atomic CAS, so it is not affected by the issue the same way.
// For other Xtensa CPUs, assume they are not affected.
_ => None,
};
if let Some(cpu) = esp_cpu {
target_cpu_fallback(cpu);
}
}
}
_ => {}
}
}
Expand Down Expand Up @@ -585,6 +615,11 @@ fn target_cpu() -> Option<String> {
cpu.map(str::to_owned)
}

// `target_cpu` is not a valid cfg option. Where there is absolutely no other option, inject a cfg fallback.
fn target_cpu_fallback(cpu: &str) {
println!("cargo:rustc-cfg=portable_atomic_target_cpu=\"{}\"", cpu);
}

fn is_allowed_feature(name: &str) -> bool {
// https://github.com/dtolnay/thiserror/pull/248
if env::var_os("RUSTC_STAGE").is_some() {
Expand Down
7 changes: 7 additions & 0 deletions src/imp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
// Lock-free implementations

cfg_core_atomic!({
// ESP32 and ESP32-S3 have native atomic CAS on internal memory but not on
// PSRAM. Redirect this module to a wrapper, which exposes the same public API
// and dispatches each operation based on the address at runtime.
#[cfg_attr(
any(portable_atomic_target_cpu = "esp32", portable_atomic_target_cpu = "esp32s3"),
path = "xtensa.rs"
)]
mod core_atomic;
});

Expand Down
Loading