From baef99246b7ac4afdb1f9cdcdbd49c7d4287c486 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 27 Oct 2024 16:06:00 +0900 Subject: [PATCH] xtensa: Support load/store (experimental) --- .github/.cspell/project-dictionary.txt | 4 + .github/dependabot.yml | 2 + .github/workflows/ci.yml | 11 ++ Cargo.toml | 1 + README.md | 3 +- build.rs | 2 +- src/arch/cfgs/xtensa.rs | 52 ++++++ src/arch/mod.rs | 4 + src/arch/xtensa.rs | 90 +++++++++++ src/lib.rs | 3 +- tests/xtensa/.cargo/config.toml | 6 + tests/xtensa/Cargo.toml | 34 ++++ tests/xtensa/src/main.rs | 216 +++++++++++++++++++++++++ tools/build.sh | 13 +- tools/no-std.sh | 29 +++- 15 files changed, 463 insertions(+), 7 deletions(-) create mode 100644 src/arch/cfgs/xtensa.rs create mode 100644 src/arch/xtensa.rs create mode 100644 tests/xtensa/.cargo/config.toml create mode 100644 tests/xtensa/Cargo.toml create mode 100644 tests/xtensa/src/main.rs diff --git a/.github/.cspell/project-dictionary.txt b/.github/.cspell/project-dictionary.txt index 312dbc37..1e61e887 100644 --- a/.github/.cspell/project-dictionary.txt +++ b/.github/.cspell/project-dictionary.txt @@ -20,6 +20,7 @@ cmpxchg cset dbar DWCAS +espup fild fistp Halfword @@ -41,6 +42,7 @@ ldsetp ldxp lgcc libunwind +linkall lmul lqarx lrcpc @@ -94,12 +96,14 @@ stwcx stxp subarch swpp +uart usart uwrite uwriteln uxtb uxth virt +wokwi xchg xmmword xorps diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ea80f263..03351ccd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,9 @@ updates: - / # crates with [workspace] table are not recognized by the above 'directory: /' - /tests/avr + - /tests/msp430 - /tests/no-std-qemu + - /tests/xtensa schedule: interval: daily commit-message: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e71ae573..0e96cc8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -374,6 +374,10 @@ jobs: - uses: taiki-e/github-actions/install-rust@main with: toolchain: ${{ matrix.rust }} + - uses: taiki-e/install-action@cargo-hack + if: startsWith(matrix.rust, 'nightly') + - uses: taiki-e/install-action@espup + if: startsWith(matrix.rust, 'nightly') - run: | retry() { for i in {1..10}; do @@ -411,10 +415,17 @@ jobs: retry curl --proto '=https' --tlsv1.2 -fsSL --retry 10 --retry-connrefused https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-LlCjWuAbzH/9.3.1.2/msp430-gcc-9.3.1.11_linux64.tar.bz2 \ | tar xjf - --strip-components 1 -C "${HOME}"/msp430-gcc printf '%s\n' "${HOME}"/msp430-gcc/bin >>"${GITHUB_PATH}" + retry espup install --targets esp32,esp32s2,esp32s3 fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: rm -- tests/avr/rust-toolchain.toml if: startsWith(matrix.rust, 'nightly-') - run: tools/no-std.sh + - run: tools/build.sh +esp xtensa-esp32-none-elf xtensa-esp32s2-none-elf xtensa-esp32s3-none-elf + if: startsWith(matrix.rust, 'nightly') + - run: tools/no-std.sh +esp xtensa-esp32-none-elf xtensa-esp32s2-none-elf xtensa-esp32s3-none-elf + if: startsWith(matrix.rust, 'nightly') valgrind: needs: tidy diff --git a/Cargo.toml b/Cargo.toml index f99c9f8e..2471bd1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ non_ascii_idents = "warn" rust_2018_idioms = "warn" single_use_lifetimes = "warn" unexpected_cfgs = { level = "warn", check-cfg = [ + 'cfg(target_arch,values("xtensa"))', # 1.81+ https://github.com/rust-lang/rust/pull/125141 'cfg(target_feature,values("lse2","lse128","rcpc3"))', # 1.82+ https://github.com/rust-lang/rust/pull/128192 'cfg(target_feature,values("partword-atomics","quadword-atomics"))', # 1.83+ https://github.com/rust-lang/rust/pull/130873 'cfg(target_feature,values("zaamo","zabha"))', # 1.83+ https://github.com/rust-lang/rust/pull/130877 diff --git a/README.md b/README.md index e4a1958e..98f468e5 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ This crate provides a way to soundly perform such operations. ## Platform Support -Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch64, MIPS32, MIPS64, PowerPC, s390x, MSP430, Arm64EC, AVR, and Hexagon are supported. +Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch64, MIPS32, MIPS64, PowerPC, s390x, MSP430, Arm64EC, AVR, Hexagon, and Xtensa are supported. | target_arch | primitives | load/store | swap/CAS | | -------------------------------- | --------------------------------------------------- |:----------:|:--------:| @@ -41,6 +41,7 @@ Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch64, MIPS32, MIPS64, Power | msp430 \[4] (experimental) | isize,usize,i8,u8,i16,u16 | ✓ | ✓ | | avr \[4] (experimental) | isize,usize,i8,u8,i16,u16 | ✓ | ✓ | | hexagon \[4] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | +| xtensa \[4] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | | \[1] Arm's atomic RMW operations are not available on v6-m (thumbv6m). RISC-V's atomic RMW operations are not available on targets without the A (or G which means IMAFD) extension such as riscv32i, riscv32imc, etc.
\[2] Armv6+ or Linux/Android, except for M-profile architecture such as thumbv6m, thumbv7m, etc.
diff --git a/build.rs b/build.rs index 7d7ea450..875d3e6c 100644 --- a/build.rs +++ b/build.rs @@ -89,7 +89,7 @@ fn main() { } } "arm64ec" | "avr" | "hexagon" | "mips" | "mips32r6" | "mips64" | "mips64r6" | "msp430" - | "powerpc" | "powerpc64" => { + | "powerpc" | "powerpc64" | "xtensa" => { if version.nightly && is_allowed_feature("asm_experimental_arch") { println!("cargo:rustc-cfg=atomic_maybe_uninit_unstable_asm_experimental_arch"); } diff --git a/src/arch/cfgs/xtensa.rs b/src/arch/cfgs/xtensa.rs new file mode 100644 index 00000000..dde8e6d6 --- /dev/null +++ b/src/arch/cfgs/xtensa.rs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![allow(missing_docs)] + +#[macro_export] +macro_rules! cfg_has_atomic_8 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_no_atomic_8 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_has_atomic_16 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_no_atomic_16 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_has_atomic_32 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_no_atomic_32 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_has_atomic_64 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_no_atomic_64 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_has_atomic_128 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_no_atomic_128 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_has_atomic_cas { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_no_atomic_cas { + ($($tt:tt)*) => { $($tt)* }; +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 3ea4981e..c453a0a8 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -38,6 +38,7 @@ target_arch = "powerpc", target_arch = "powerpc64", target_arch = "s390x", + target_arch = "xtensa", ), atomic_maybe_uninit_unstable_asm_experimental_arch, ), @@ -105,3 +106,6 @@ mod riscv; mod s390x; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86; +#[cfg(target_arch = "xtensa")] +#[cfg(atomic_maybe_uninit_unstable_asm_experimental_arch)] +mod xtensa; diff --git a/src/arch/xtensa.rs b/src/arch/xtensa.rs new file mode 100644 index 00000000..ed574871 --- /dev/null +++ b/src/arch/xtensa.rs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/* +Xtensa + +Refs: +- Xtensa Instruction Set Architecture (ISA) Reference Manual https://web.archive.org/web/20241005102231/https://0x04.net/~mwk/doc/xtensa.pdf +- https://github.com/espressif/llvm-project/blob/xtensa_release_18.1.2/llvm/test/CodeGen/Xtensa/atomic-load-store.ll +*/ + +#[path = "cfgs/xtensa.rs"] +mod cfgs; + +use core::{arch::asm, mem::MaybeUninit, sync::atomic::Ordering}; + +use crate::raw::{AtomicLoad, AtomicStore}; + +macro_rules! atomic { + ($int_type:ident, $asm_size:tt, $asm_suffix:tt, $asm_load_ext:tt) => { + impl AtomicLoad for $int_type { + #[inline] + unsafe fn atomic_load( + src: *const MaybeUninit, + order: Ordering, + ) -> MaybeUninit { + let out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + macro_rules! atomic_load { + ($acquire:tt) => { + asm!( + concat!("l", $asm_size, $asm_load_ext, "i", $asm_suffix, " {out}, {src}, 0"), + $acquire, + src = in(reg) ptr_reg!(src), + out = lateout(reg) out, + options(nostack, preserves_flags), + ) + }; + } + match order { + Ordering::Relaxed => atomic_load!(""), + Ordering::Acquire | Ordering::SeqCst => atomic_load!("memw"), + _ => unreachable!(), + } + } + out + } + } + impl AtomicStore for $int_type { + #[inline] + unsafe fn atomic_store( + dst: *mut MaybeUninit, + val: MaybeUninit, + order: Ordering, + ) { + // SAFETY: the caller must uphold the safety contract. + unsafe { + macro_rules! atomic_store { + ($acquire:tt, $release:tt) => { + asm!( + $release, + concat!("s", $asm_size, "i", $asm_suffix, " {val}, {dst}, 0"), + $acquire, + dst = in(reg) ptr_reg!(dst), + val = in(reg) val, + options(nostack, preserves_flags), + ) + }; + } + match order { + Ordering::Relaxed => atomic_store!("", ""), + Ordering::Release => atomic_store!("", "memw"), + Ordering::SeqCst => atomic_store!("memw", "memw"), + _ => unreachable!(), + } + } + } + } + }; +} + +atomic!(i8, "8", "", "u"); +atomic!(u8, "8", "", "u"); +atomic!(i16, "16", "", "u"); +atomic!(u16, "16", "", "u"); +atomic!(i32, "32", ".n", ""); +atomic!(u32, "32", ".n", ""); +atomic!(isize, "32", ".n", ""); +atomic!(usize, "32", ".n", ""); diff --git a/src/lib.rs b/src/lib.rs index 64a795a0..42d0b497 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ This crate provides a way to soundly perform such operations. ## Platform Support -Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch64, MIPS32, MIPS64, PowerPC, s390x, MSP430, Arm64EC, AVR, and Hexagon are supported. +Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch64, MIPS32, MIPS64, PowerPC, s390x, MSP430, Arm64EC, AVR, Hexagon, and Xtensa are supported. | target_arch | primitives | load/store | swap/CAS | | -------------------------------- | --------------------------------------------------- |:----------:|:--------:| @@ -35,6 +35,7 @@ Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch64, MIPS32, MIPS64, Power | msp430 \[4] (experimental) | isize,usize,i8,u8,i16,u16 | ✓ | ✓ | | avr \[4] (experimental) | isize,usize,i8,u8,i16,u16 | ✓ | ✓ | | hexagon \[4] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | +| xtensa \[4] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | | \[1] Arm's atomic RMW operations are not available on v6-m (thumbv6m). RISC-V's atomic RMW operations are not available on targets without the A (or G which means IMAFD) extension such as riscv32i, riscv32imc, etc.
\[2] Armv6+ or Linux/Android, except for M-profile architecture such as thumbv6m, thumbv7m, etc.
diff --git a/tests/xtensa/.cargo/config.toml b/tests/xtensa/.cargo/config.toml new file mode 100644 index 00000000..464a006e --- /dev/null +++ b/tests/xtensa/.cargo/config.toml @@ -0,0 +1,6 @@ +[target.xtensa-esp32-none-elf] +runner = "wokwi-server --chip esp32" +[target.xtensa-esp32s2-none-elf] +runner = "wokwi-server --chip esp32s2" +[target.xtensa-esp32s3-none-elf] +runner = "wokwi-server --chip esp32s3" diff --git a/tests/xtensa/Cargo.toml b/tests/xtensa/Cargo.toml new file mode 100644 index 00000000..56d525d0 --- /dev/null +++ b/tests/xtensa/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "xtensa-test" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies] +atomic-maybe-uninit = { path = "../.." } + +paste = "1" + +[target.xtensa-esp32-none-elf.dependencies] +esp-println = { version = "0.12", default-features = false, features = ["uart", "esp32"] } +esp-hal = { version = "0.21", features = ["esp32"] } +[target.xtensa-esp32s2-none-elf.dependencies] +esp-println = { version = "0.12", default-features = false, features = ["uart", "esp32s2"] } +esp-hal = { version = "0.21", features = ["esp32s2"] } +[target.xtensa-esp32s3-none-elf.dependencies] +esp-println = { version = "0.12", default-features = false, features = ["uart", "esp32s3"] } +esp-hal = { version = "0.21", features = ["esp32s3"] } + +[workspace] +resolver = "2" + +[lints.rust] +rust_2018_idioms = "warn" +single_use_lifetimes = "warn" +# unsafe_op_in_unsafe_fn = "warn" # Set at crate-level instead since https://github.com/rust-lang/rust/pull/100081 is not available on MSRV + +[profile.dev] +opt-level = 'z' + +[profile.release] +opt-level = 'z' diff --git a/tests/xtensa/src/main.rs b/tests/xtensa/src/main.rs new file mode 100644 index 00000000..f9cbe8cc --- /dev/null +++ b/tests/xtensa/src/main.rs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![no_main] +#![no_std] +#![warn(unsafe_op_in_unsafe_fn)] + +use core::{mem::MaybeUninit, sync::atomic::Ordering}; + +use atomic_maybe_uninit::*; +use esp_println::{print, println}; + +macro_rules! __test_atomic { + ($int_type:ident) => { + load_store(); + fn load_store() { + unsafe { + static VAR: AtomicMaybeUninit<$int_type> = + AtomicMaybeUninit::<$int_type>::const_new(MaybeUninit::new(10)); + for (load_order, store_order) in load_orderings().into_iter().zip(store_orderings()) + { + assert_eq!(VAR.load(load_order).assume_init(), 10); + VAR.store(MaybeUninit::new(5), store_order); + assert_eq!(VAR.load(load_order).assume_init(), 5); + VAR.store(MaybeUninit::uninit(), store_order); + let _v = VAR.load(load_order); + VAR.store(MaybeUninit::new(10), store_order); + + let a = AtomicMaybeUninit::<$int_type>::new(MaybeUninit::new(1)); + assert_eq!(a.load(load_order).assume_init(), 1); + a.store(MaybeUninit::new(2), store_order); + assert_eq!(a.load(load_order).assume_init(), 2); + let a = AtomicMaybeUninit::<$int_type>::new(MaybeUninit::uninit()); + let _v = a.load(load_order); + a.store(MaybeUninit::new(2), store_order); + assert_eq!(a.load(load_order).assume_init(), 2); + a.store(MaybeUninit::uninit(), store_order); + let _v = a.load(load_order); + } + } + } + cfg_has_atomic_cas! { + swap(); + fn swap() { + unsafe { + for order in swap_orderings() { + let a = AtomicMaybeUninit::<$int_type>::new(MaybeUninit::new(5)); + assert_eq!(a.swap(MaybeUninit::new(10), order).assume_init(), 5); + assert_eq!(a.swap(MaybeUninit::uninit(), order).assume_init(), 10); + let _v = a.swap(MaybeUninit::new(15), order); + let a = AtomicMaybeUninit::<$int_type>::new(MaybeUninit::uninit()); + let _v = a.swap(MaybeUninit::new(10), order); + assert_eq!(a.swap(MaybeUninit::uninit(), order).assume_init(), 10); + } + } + } + compare_exchange(); + fn compare_exchange() { + unsafe { + for (success, failure) in compare_exchange_orderings() { + let a = AtomicMaybeUninit::<$int_type>::new(MaybeUninit::new(5)); + assert_eq!( + a.compare_exchange( + MaybeUninit::new(5), + MaybeUninit::new(10), + success, + failure + ) + .unwrap() + .assume_init(), + 5 + ); + assert_eq!(a.load(Ordering::Relaxed).assume_init(), 10); + assert_eq!( + a.compare_exchange( + MaybeUninit::new(6), + MaybeUninit::new(12), + success, + failure + ) + .unwrap_err() + .assume_init(), + 10 + ); + assert_eq!(a.load(Ordering::Relaxed).assume_init(), 10); + } + } + } + compare_exchange_weak(); + fn compare_exchange_weak() { + unsafe { + for (success, failure) in compare_exchange_orderings() { + let a = AtomicMaybeUninit::<$int_type>::new(MaybeUninit::new(4)); + assert_eq!( + a.compare_exchange_weak( + MaybeUninit::new(6), + MaybeUninit::new(8), + success, + failure + ) + .unwrap_err() + .assume_init(), + 4 + ); + let mut old = a.load(Ordering::Relaxed); + loop { + let new = MaybeUninit::new(old.assume_init() * 2); + match a.compare_exchange_weak(old, new, success, failure) { + Ok(_) => break, + Err(x) => old = x, + } + } + assert_eq!(a.load(Ordering::Relaxed).assume_init(), 8); + } + } + } + fetch_update(); + fn fetch_update() { + unsafe { + for (success, failure) in compare_exchange_orderings() { + let a = AtomicMaybeUninit::<$int_type>::new(MaybeUninit::new(7)); + assert_eq!( + a.fetch_update(success, failure, |_| None).unwrap_err().assume_init(), + 7 + ); + assert_eq!( + a.fetch_update(success, failure, |x| Some(MaybeUninit::new( + x.assume_init() + 1 + ))) + .unwrap() + .assume_init(), + 7 + ); + assert_eq!( + a.fetch_update(success, failure, |x| Some(MaybeUninit::new( + x.assume_init() + 1 + ))) + .unwrap() + .assume_init(), + 8 + ); + assert_eq!(a.load(Ordering::Relaxed).assume_init(), 9); + } + } + } + } + }; +} + +#[esp_hal::entry] +fn main() -> ! { + macro_rules! test_atomic { + ($int_type:ident) => { + paste::paste! { + fn []() { + __test_atomic!($int_type); + } + print!("{}", concat!("test test_atomic_", stringify!($int_type), " ... ")); + [](); + println!("ok"); + } + }; + } + + test_atomic!(isize); + test_atomic!(usize); + test_atomic!(i8); + test_atomic!(u8); + test_atomic!(i16); + test_atomic!(u16); + test_atomic!(i32); + test_atomic!(u32); + + println!("Tests finished successfully"); + + #[allow(clippy::empty_loop)] // this test crate is #![no_std] + loop {} +} + +fn load_orderings() -> [Ordering; 3] { + [Ordering::Relaxed, Ordering::Acquire, Ordering::SeqCst] +} +fn store_orderings() -> [Ordering; 3] { + [Ordering::Relaxed, Ordering::Release, Ordering::SeqCst] +} +cfg_has_atomic_cas! { +fn swap_orderings() -> [Ordering; 5] { + [Ordering::Relaxed, Ordering::Release, Ordering::Acquire, Ordering::AcqRel, Ordering::SeqCst] +} +fn compare_exchange_orderings() -> [(Ordering, Ordering); 15] { + [ + (Ordering::Relaxed, Ordering::Relaxed), + (Ordering::Relaxed, Ordering::Acquire), + (Ordering::Relaxed, Ordering::SeqCst), + (Ordering::Acquire, Ordering::Relaxed), + (Ordering::Acquire, Ordering::Acquire), + (Ordering::Acquire, Ordering::SeqCst), + (Ordering::Release, Ordering::Relaxed), + (Ordering::Release, Ordering::Acquire), + (Ordering::Release, Ordering::SeqCst), + (Ordering::AcqRel, Ordering::Relaxed), + (Ordering::AcqRel, Ordering::Acquire), + (Ordering::AcqRel, Ordering::SeqCst), + (Ordering::SeqCst, Ordering::Relaxed), + (Ordering::SeqCst, Ordering::Acquire), + (Ordering::SeqCst, Ordering::SeqCst), + ] +} +} + +#[inline(never)] +#[panic_handler] +fn panic(info: &core::panic::PanicInfo<'_>) -> ! { + println!("{info}"); + #[allow(clippy::empty_loop)] // this test crate is #![no_std] + loop {} +} diff --git a/tools/build.sh b/tools/build.sh index f8c66222..094878fa 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -156,7 +156,11 @@ retry() { } pre_args=() +is_custom_toolchain='' if [[ "${1:-}" == "+"* ]]; then + if [[ "$1" == "+esp" ]]; then + is_custom_toolchain=1 + fi pre_args+=("$1") shift fi @@ -166,7 +170,10 @@ else targets=("${default_targets[@]}") fi -rustup_target_list=$(rustup ${pre_args[@]+"${pre_args[@]}"} target list | cut -d' ' -f1) +rustup_target_list='' +if [[ -z "${is_custom_toolchain}" ]]; then + rustup_target_list=$(rustup ${pre_args[@]+"${pre_args[@]}"} target list | cut -d' ' -f1) +fi rustc_target_list=$(rustc ${pre_args[@]+"${pre_args[@]}"} --print target-list) rustc_version=$(rustc ${pre_args[@]+"${pre_args[@]}"} -vV | grep -E '^release:' | cut -d' ' -f2) rustc_minor_version="${rustc_version#*.}" @@ -179,7 +186,9 @@ nightly='' base_rustflags="${RUSTFLAGS:-}" if [[ "${rustc_version}" =~ nightly|dev ]]; then nightly=1 - retry rustup ${pre_args[@]+"${pre_args[@]}"} component add rust-src &>/dev/null + if [[ -z "${is_custom_toolchain}" ]]; then + retry rustup ${pre_args[@]+"${pre_args[@]}"} component add rust-src &>/dev/null + fi # We only run clippy on the recent nightly to avoid old clippy bugs. if [[ "${rustc_minor_version}" -ge 84 ]]; then retry rustup ${pre_args[@]+"${pre_args[@]}"} component add clippy &>/dev/null diff --git a/tools/no-std.sh b/tools/no-std.sh index dcfd0f37..4238bf34 100755 --- a/tools/no-std.sh +++ b/tools/no-std.sh @@ -74,7 +74,13 @@ bail() { } pre_args=() +is_custom_toolchain='' if [[ "${1:-}" == "+"* ]]; then + if [[ "$1" == "+esp" ]]; then + # shellcheck disable=SC1091 + . "${HOME}/export-esp.sh" + is_custom_toolchain=1 + fi pre_args+=("$1") shift fi @@ -84,7 +90,10 @@ else targets=("${default_targets[@]}") fi -rustup_target_list=$(rustup ${pre_args[@]+"${pre_args[@]}"} target list | cut -d' ' -f1) +rustup_target_list='' +if [[ -z "${is_custom_toolchain}" ]]; then + rustup_target_list=$(rustup ${pre_args[@]+"${pre_args[@]}"} target list | cut -d' ' -f1) +fi rustc_target_list=$(rustc ${pre_args[@]+"${pre_args[@]}"} --print target-list) rustc_version=$(rustc ${pre_args[@]+"${pre_args[@]}"} -vV | grep -E '^release:' | cut -d' ' -f2) commit_date=$(rustc ${pre_args[@]+"${pre_args[@]}"} -vV | grep -E '^commit-date:' | cut -d' ' -f2) @@ -92,7 +101,9 @@ target_dir=$(pwd)/target nightly='' if [[ "${rustc_version}" =~ nightly|dev ]]; then nightly=1 - retry rustup ${pre_args[@]+"${pre_args[@]}"} component add rust-src &>/dev/null + if [[ -z "${is_custom_toolchain}" ]]; then + retry rustup ${pre_args[@]+"${pre_args[@]}"} component add rust-src &>/dev/null + fi fi export QEMU_AUDIO_DRV=none export ATOMIC_MAYBE_UNINIT_DENY_WARNINGS=1 @@ -131,6 +142,15 @@ run() { ;; esac fi + case "${target}" in + xtensa*) + # TODO: run test with simulator on CI + if ! type -P wokwi-server >/dev/null; then + printf '%s\n' "no-std test for ${target} requires wokwi-server (switched to build-only)" + subcmd=build + fi + ;; + esac args+=("${subcmd}" "${target_flags[@]}") if grep -Eq "^${target}$" <<<"${rustup_target_list}"; then retry rustup ${pre_args[@]+"${pre_args[@]}"} target add "${target}" &>/dev/null @@ -170,6 +190,11 @@ run() { target_rustflags+=" -C link-arg=-lmul_f5" target_rustflags+=" -C link-arg=-lgcc" ;; + xtensa*) + test_dir=tests/xtensa + linker=linkall.x + target_rustflags+=" -C link-arg=-Wl,-T${linker} -C link-arg=-nostartfiles" + ;; *) bail "unrecognized target '${target}'" ;; esac args+=(--all-features)