├── tools ├── valgrind │ └── .gitkeep ├── .tidy-check-license-headers ├── gen.sh ├── ci │ └── gen.sh ├── runner.sh ├── target_spec.sh ├── test.sh └── no-std.sh ├── tests ├── .gitignore ├── sparc │ ├── tsim-commands.txt │ ├── .cargo │ │ └── config.toml │ ├── rust-toolchain.toml │ ├── src │ │ └── main.rs │ └── Cargo.toml ├── no-std-linux │ ├── .cargo │ │ └── config.toml │ ├── src │ │ └── main.rs │ └── Cargo.toml ├── msp430 │ ├── build.rs │ ├── memory.x │ ├── src │ │ └── main.rs │ └── Cargo.toml ├── avr │ ├── rust-toolchain.toml │ ├── src │ │ └── main.rs │ └── Cargo.toml ├── no-std-qemu │ ├── .cargo │ │ └── config.toml │ ├── src │ │ └── main.rs │ └── Cargo.toml ├── xtensa │ ├── src │ │ └── main.rs │ └── Cargo.toml └── include │ └── basic.rs ├── .git-blame-ignore-revs ├── .gitattributes ├── .github ├── .cspell │ ├── rust-dependencies.txt │ └── project-dictionary.txt ├── zizmor.yml ├── dependabot.yml └── workflows │ └── release.yml ├── .taplo.toml ├── .gitignore ├── src ├── gen │ ├── build.rs │ └── utils.rs ├── tests │ └── mod.rs ├── arch │ ├── mod.rs │ ├── msp430.rs │ ├── avr.rs │ ├── csky.rs │ ├── xtensa.rs │ └── mips.rs └── raw.rs ├── .clippy.toml ├── .markdownlint-cli2.yaml ├── target-specs ├── riscv32gc-unknown-none-elf.json ├── riscv64i-unknown-none-elf.json ├── armebv8r-none-eabihf.json ├── armv8a-none-eabi.json ├── avr-unknown-gnu-atmega2560.json └── armebv7-unknown-linux-gnueabi.json ├── .editorconfig ├── .deny.toml ├── LICENSE-MIT ├── .shellcheckrc ├── .rustfmt.toml ├── .cspell.json ├── README.md ├── Cargo.toml └── LICENSE-APACHE /tools/valgrind/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | !rust-toolchain.toml 2 | -------------------------------------------------------------------------------- /tests/sparc/tsim-commands.txt: -------------------------------------------------------------------------------- 1 | run 2 | quit 3 | -------------------------------------------------------------------------------- /tools/.tidy-check-license-headers: -------------------------------------------------------------------------------- 1 | git ls-files 2 | -------------------------------------------------------------------------------- /tests/sparc/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.sparc-unknown-none-elf] 2 | linker = "sparc-gaisler-elf-gcc" 3 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Change indent size of shell script files to match scripts in CI config 2 | bae276a945a0d58d779bf1e7d1b3903c568f9a20 3 | -------------------------------------------------------------------------------- /tests/no-std-linux/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.m68k-unknown-linux-gnu] 2 | linker = "m68k-linux-gnu-gcc" 3 | runner = "qemu-m68k -L /usr/m68k-linux-gnu" 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | tools/tidy.sh linguist-detectable=false 3 | .github/.cspell/rust-dependencies.txt linguist-generated 4 | src/gen/** linguist-generated 5 | -------------------------------------------------------------------------------- /.github/.cspell/rust-dependencies.txt: -------------------------------------------------------------------------------- 1 | // This file is @generated by tidy.sh. 2 | // It is not intended for manual editing. 3 | 4 | crabgrind 5 | fastrand 6 | quickcheck 7 | ufmt 8 | -------------------------------------------------------------------------------- /tests/msp430/build.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | fn main() { 4 | println!("cargo:rerun-if-changed=build.rs"); 5 | println!("cargo:rerun-if-changed=memory.x"); 6 | } 7 | -------------------------------------------------------------------------------- /tests/sparc/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-02-17" # TODO: "rustc-LLVM ERROR: Not supported instr: >" with LLVM 20-21 3 | components = ["rust-src"] 4 | profile = "minimal" 5 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | # Taplo configuration 2 | # https://taplo.tamasfe.dev/configuration/formatter-options.html 3 | 4 | [formatting] 5 | align_comments = false 6 | allowed_blank_lines = 1 7 | array_auto_collapse = false 8 | array_auto_expand = false 9 | indent_string = " " 10 | -------------------------------------------------------------------------------- /tests/avr/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # channel = "nightly-2023-08-09" # LLVM 17 3 | # channel = "nightly-2024-04-14" # LLVM 18 4 | # channel = "nightly-2025-01-01" # LLVM 19 5 | channel = "nightly-2025-02-18" # LLVM 20 6 | components = ["rust-src"] 7 | profile = "minimal" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | tmp 4 | 5 | # For platform and editor specific settings, it is recommended to add to 6 | # a global .gitignore file. 7 | # Refs: https://docs.github.com/en/github/using-git/ignoring-files#configuring-ignored-files-for-all-repositories-on-your-computer 8 | -------------------------------------------------------------------------------- /src/gen/build.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | // This file is @generated by target_spec.sh. 3 | // It is not intended for manual editing. 4 | 5 | #[rustfmt::skip] 6 | pub(crate) static ARM_BUT_THUMB_MODE: &[&str] = &[ 7 | "armv7-linux-androideabi", 8 | "armv7-sony-vita-newlibeabihf", 9 | ]; 10 | -------------------------------------------------------------------------------- /tools/gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-License-Identifier: Apache-2.0 OR MIT 3 | set -CeEuo pipefail 4 | IFS=$'\n\t' 5 | trap -- 's=$?; printf >&2 "%s\n" "${0##*/}:${LINENO}: \`${BASH_COMMAND}\` exit with ${s}"; exit ${s}' ERR 6 | cd -- "$(dirname -- "$0")"/.. 7 | 8 | # Run code generators. 9 | # 10 | # USAGE: 11 | # ./tools/gen.sh 12 | 13 | set -x 14 | 15 | ./tools/target_spec.sh 16 | -------------------------------------------------------------------------------- /.clippy.toml: -------------------------------------------------------------------------------- 1 | # Clippy configuration 2 | # https://doc.rust-lang.org/nightly/clippy/lint_configuration.html 3 | 4 | allow-private-module-inception = true 5 | avoid-breaking-exported-api = false 6 | disallowed-names = [] 7 | disallowed-macros = [ 8 | { path = "std::dbg", reason = "it is okay to use during development, but please do not include it in main branch" }, 9 | ] 10 | disallowed-methods = [ 11 | ] 12 | disallowed-types = [ 13 | ] 14 | -------------------------------------------------------------------------------- /.markdownlint-cli2.yaml: -------------------------------------------------------------------------------- 1 | # https://github.com/DavidAnson/markdownlint/blob/HEAD/doc/Rules.md 2 | config: 3 | line-length: false # MD013 4 | no-duplicate-heading: false # MD024 5 | no-blanks-blockquote: false # MD028 (this warns valid GFM alerts usage) 6 | no-inline-html: false # MD033 7 | no-emphasis-as-heading: false # MD036 8 | 9 | # https://github.com/DavidAnson/markdownlint-cli2#markdownlint-cli2jsonc 10 | noBanner: true 11 | noProgress: true 12 | -------------------------------------------------------------------------------- /.github/zizmor.yml: -------------------------------------------------------------------------------- 1 | # zizmor configuration 2 | # https://docs.zizmor.sh/configuration/ 3 | 4 | rules: 5 | dependabot-cooldown: { disable: true } # Useless unless unpinned-uses is enabled. 6 | ref-confusion: { disable: true } # TODO: Old GHA didn't work without this pattern in some cases, but does it seem to be fixed? 7 | secrets-inherit: { disable: true } 8 | unpinned-uses: 9 | config: 10 | policies: 11 | taiki-e/*: any 12 | '*': ref-pin 13 | -------------------------------------------------------------------------------- /target-specs/riscv32gc-unknown-none-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "riscv32", 3 | "cpu": "generic-rv32", 4 | "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", 5 | "eh-frame-header": false, 6 | "emit-debug-gdb-scripts": false, 7 | "features": "+m,+a,+f,+d,+c,+zicsr,+zifencei", 8 | "linker": "rust-lld", 9 | "linker-flavor": "ld.lld", 10 | "llvm-abiname": "ilp32d", 11 | "llvm-target": "riscv32", 12 | "max-atomic-width": 32, 13 | "panic-strategy": "abort", 14 | "relocation-model": "static", 15 | "target-pointer-width": 32 16 | } 17 | -------------------------------------------------------------------------------- /target-specs/riscv64i-unknown-none-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "riscv64", 3 | "atomic-cas": false, 4 | "code-model": "medium", 5 | "cpu": "generic-rv64", 6 | "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", 7 | "eh-frame-header": false, 8 | "emit-debug-gdb-scripts": false, 9 | "linker": "rust-lld", 10 | "linker-flavor": "ld.lld", 11 | "llvm-abiname": "lp64", 12 | "llvm-target": "riscv64", 13 | "max-atomic-width": 0, 14 | "panic-strategy": "abort", 15 | "relocation-model": "static", 16 | "target-pointer-width": 64 17 | } 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig configuration 2 | # https://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_size = 4 10 | indent_style = space 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.{css,html,json,md,rb,sh,yml,yaml}] 15 | indent_size = 2 16 | 17 | [*.{js,yml,yaml}] 18 | quote_type = single 19 | 20 | [*.sh] 21 | # https://google.github.io/styleguide/shellguide.html#s5.3-pipelines 22 | binary_next_line = true 23 | # https://google.github.io/styleguide/shellguide.html#s5.5-case-statement 24 | switch_case_indent = true 25 | -------------------------------------------------------------------------------- /target-specs/armebv8r-none-eabihf.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabihf", 3 | "arch": "arm", 4 | "c-enum-min-bits": 8, 5 | "crt-objects-fallback": "false", 6 | "data-layout": "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 7 | "emit-debug-gdb-scripts": false, 8 | "features": "+fp-armv8,-fp64,-d32", 9 | "linker": "rust-lld", 10 | "linker-flavor": "gnu-lld", 11 | "llvm-floatabi": "hard", 12 | "llvm-target": "armebv8r-none-eabihf", 13 | "max-atomic-width": 64, 14 | "panic-strategy": "abort", 15 | "relocation-model": "static", 16 | "target-endian": "big", 17 | "target-pointer-width": 32 18 | } 19 | -------------------------------------------------------------------------------- /target-specs/armv8a-none-eabi.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabi", 3 | "arch": "arm", 4 | "cpu": "cortex-a32", 5 | "c-enum-min-bits": 8, 6 | "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 7 | "disable-redzone": true, 8 | "emit-debug-gdb-scripts": false, 9 | "features": "+thumb2,+soft-float,-neon,+strict-align", 10 | "linker": "rust-lld", 11 | "linker-flavor": "ld.lld", 12 | "llvm-floatabi": "soft", 13 | "llvm-target": "armv8a-none-eabi", 14 | "max-atomic-width": 64, 15 | "panic-strategy": "abort", 16 | "relocation-model": "static", 17 | "target-pointer-width": 32 18 | } 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directories: 5 | - / 6 | # crates with [workspace] table are not recognized by the above 'directory: /' 7 | - /tests/avr 8 | - /tests/msp430 9 | - /tests/no-std-linux 10 | - /tests/no-std-qemu 11 | - /tests/sparc 12 | - /tests/xtensa 13 | schedule: 14 | interval: daily 15 | commit-message: 16 | prefix: '' 17 | labels: [] 18 | - package-ecosystem: github-actions 19 | directory: / 20 | schedule: 21 | interval: daily 22 | commit-message: 23 | prefix: '' 24 | labels: [] 25 | -------------------------------------------------------------------------------- /target-specs/avr-unknown-gnu-atmega2560.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "avr", 3 | "atomic-cas": false, 4 | "cpu": "atmega2560", 5 | "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8", 6 | "eh-frame-header": false, 7 | "exe-suffix": ".elf", 8 | "executables": true, 9 | "late-link-args": { 10 | "gcc": ["-lgcc"] 11 | }, 12 | "linker": "avr-gcc", 13 | "llvm-target": "avr-unknown-unknown", 14 | "max-atomic-width": 16, 15 | "no-default-libraries": false, 16 | "pre-link-args": { 17 | "gcc": ["-mmcu=atmega2560"] 18 | }, 19 | "relocation-model": "static", 20 | "target-c-int-width": "16", 21 | "target-pointer-width": "16" 22 | } 23 | -------------------------------------------------------------------------------- /target-specs/armebv7-unknown-linux-gnueabi.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabi", 3 | "arch": "arm", 4 | "crt-static-respected": true, 5 | "data-layout": "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 6 | "dynamic-linking": true, 7 | "env": "gnu", 8 | "features": "+v7,+thumb2,+soft-float,-neon", 9 | "has-rpath": true, 10 | "has-thread-local": true, 11 | "llvm-floatabi": "soft", 12 | "llvm-target": "armebv7-unknown-linux-gnueabi", 13 | "max-atomic-width": 64, 14 | "os": "linux", 15 | "position-independent-executables": true, 16 | "relro-level": "full", 17 | "supported-split-debuginfo": ["packed", "unpacked", "off"], 18 | "target-endian": "big", 19 | "target-family": ["unix"], 20 | "target-mcount": "\u0001__gnu_mcount_nc", 21 | "target-pointer-width": 32 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | description: Version to be increased 11 | required: true 12 | type: choice 13 | options: 14 | - patch 15 | - minor 16 | - major 17 | 18 | defaults: 19 | run: 20 | shell: bash --noprofile --norc -CeEuxo pipefail {0} 21 | 22 | jobs: 23 | release: 24 | if: github.repository_owner == 'taiki-e' 25 | uses: taiki-e/github-actions/.github/workflows/rust-release.yml@main 26 | permissions: 27 | contents: write # for taiki-e/create-gh-release-action 28 | id-token: write # for rust-lang/crates-io-auth-action 29 | secrets: inherit 30 | with: 31 | version: ${{ inputs.version }} 32 | -------------------------------------------------------------------------------- /tests/msp430/memory.x: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Apache-2.0 OR MIT */ 2 | /* Adapted from https://github.com/cr1901/msp430f5529-quickstart/blob/7de729ece0aeb37d9462ac832d3aa5f6686bbbc5/memory.x. */ 3 | 4 | MEMORY 5 | { 6 | /* These values are correct for the msp430f5529 device. You will need to 7 | modify these values if using a different device. Room must be reserved 8 | for interrupt vectors plus reset vector and the end of the first 64kB 9 | of address space. */ 10 | RAM : ORIGIN = 0x2200, LENGTH = 0x2000 11 | ROM : ORIGIN = 0x4400, LENGTH = 0xBB80 12 | VECTORS : ORIGIN = 0xFF80, LENGTH = 0x80 13 | } 14 | 15 | /* Stack begins at the end of RAM: 16 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ 17 | 18 | /* TODO: Code (and data?) above 64kB mark, which is supported even without 19 | using MSP430X mode. */ 20 | -------------------------------------------------------------------------------- /tools/ci/gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-License-Identifier: Apache-2.0 OR MIT 3 | set -CeEuo pipefail 4 | IFS=$'\n\t' 5 | trap -- 's=$?; printf >&2 "%s\n" "${0##*/}:${LINENO}: \`${BASH_COMMAND}\` exit with ${s}"; exit ${s}' ERR 6 | cd -- "$(dirname -- "$0")"/../.. 7 | 8 | bail() { 9 | printf >&2 'error: %s\n' "$*" 10 | exit 1 11 | } 12 | 13 | if [[ -z "${CI:-}" ]]; then 14 | bail "this script is intended to call from release workflow on CI" 15 | fi 16 | 17 | git config user.name 'Taiki Endo' 18 | git config user.email 'te316e89@gmail.com' 19 | 20 | has_update='' 21 | for path in src/gen/*; do 22 | git add -N "${path}" 23 | if ! git diff --exit-code -- "${path}" &>/dev/null; then 24 | git add "${path}" 25 | git commit -m "codegen: Update ${path}" 26 | has_update=1 27 | fi 28 | done 29 | 30 | if [[ -n "${has_update}" ]] && [[ -n "${GITHUB_OUTPUT:-}" ]]; then 31 | printf 'success=false\n' >>"${GITHUB_OUTPUT}" 32 | fi 33 | -------------------------------------------------------------------------------- /.deny.toml: -------------------------------------------------------------------------------- 1 | # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html 2 | [advisories] 3 | yanked = "deny" 4 | git-fetch-with-cli = true 5 | ignore = [ 6 | ] 7 | 8 | # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html 9 | [bans] 10 | multiple-versions = "warn" 11 | wildcards = "deny" 12 | allow-wildcard-paths = true 13 | build.executables = "deny" 14 | build.interpreted = "deny" 15 | build.include-dependencies = true 16 | build.include-workspace = false # covered by tools/tidy.sh 17 | build.include-archives = true 18 | build.allow-build-scripts = [ 19 | { name = "atomic-maybe-uninit" }, 20 | ] 21 | build.bypass = [ 22 | ] 23 | 24 | # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html 25 | [licenses] 26 | unused-allowed-license = "deny" 27 | private.ignore = true 28 | allow = [ 29 | "Apache-2.0", 30 | "MIT", 31 | ] 32 | 33 | # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html 34 | [sources] 35 | unknown-registry = "deny" 36 | unknown-git = "deny" 37 | allow-git = [ 38 | ] 39 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | # ShellCheck configuration 2 | # https://github.com/koalaman/shellcheck/blob/HEAD/shellcheck.1.md#rc-files 3 | 4 | # See also: 5 | # https://github.com/koalaman/shellcheck/wiki/Optional 6 | # https://google.github.io/styleguide/shellguide.html 7 | 8 | # https://github.com/koalaman/shellcheck/wiki/Directive#external-sources 9 | external-sources=true 10 | 11 | # https://github.com/koalaman/shellcheck/wiki/SC2249 12 | # enable=add-default-case 13 | 14 | # https://github.com/koalaman/shellcheck/wiki/SC2244 15 | enable=avoid-nullary-conditions 16 | 17 | # https://github.com/koalaman/shellcheck/wiki/SC2312 18 | # enable=check-extra-masked-returns 19 | 20 | # https://github.com/koalaman/shellcheck/wiki/SC2310 21 | # https://github.com/koalaman/shellcheck/wiki/SC2311 22 | # enable=check-set-e-suppressed 23 | 24 | # enable=check-unassigned-uppercase 25 | 26 | # https://github.com/koalaman/shellcheck/wiki/SC2230 27 | enable=deprecate-which 28 | 29 | # https://github.com/koalaman/shellcheck/wiki/SC2248 30 | enable=quote-safe-variables 31 | 32 | # https://github.com/koalaman/shellcheck/wiki/SC2292 33 | # https://google.github.io/styleguide/shellguide.html#s6.3-tests 34 | enable=require-double-brackets 35 | 36 | # https://github.com/koalaman/shellcheck/wiki/SC2250 37 | # https://google.github.io/styleguide/shellguide.html#s5.6-variable-expansion 38 | enable=require-variable-braces 39 | -------------------------------------------------------------------------------- /tests/no-std-qemu/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # See https://github.com/taiki-e/semihosting/blob/HEAD/tools/qemu-system-runner.sh 2 | 3 | # Cortex-M 4 | [target.thumbv6m-none-eabi] 5 | runner = "qemu-system-arm -M lm3s6965evb -cpu cortex-m0 -display none -semihosting -kernel" 6 | [target.thumbv7m-none-eabi] 7 | runner = "qemu-system-arm -M lm3s6965evb -cpu cortex-m3 -display none -semihosting -kernel" 8 | [target.thumbv7em-none-eabi] 9 | runner = "qemu-system-arm -M lm3s6965evb -cpu cortex-m4 -display none -semihosting -kernel" 10 | [target.thumbv7em-none-eabihf] 11 | runner = "qemu-system-arm -M lm3s6965evb -cpu cortex-m4 -display none -semihosting -kernel" 12 | [target.thumbv8m.base-none-eabi] 13 | # TODO: As of QEMU 10.1, QEMU doesn't support -cpu cortex-m23 14 | runner = "qemu-system-arm -M lm3s6965evb -cpu cortex-m33 -display none -semihosting -kernel" 15 | [target.thumbv8m.main-none-eabi] 16 | runner = "qemu-system-arm -M lm3s6965evb -cpu cortex-m33 -display none -semihosting -kernel" 17 | [target.thumbv8m.main-none-eabihf] 18 | runner = "qemu-system-arm -M lm3s6965evb -cpu cortex-m33 -display none -semihosting -kernel" 19 | 20 | # RISC-V 21 | [target.'cfg(all(target_arch = "riscv32", target_os = "none"))'] 22 | runner = "qemu-system-riscv32 -M virt -display none -semihosting -kernel" 23 | [target.'cfg(all(target_arch = "riscv64", target_os = "none"))'] 24 | runner = "qemu-system-riscv64 -M virt -display none -semihosting -kernel" 25 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Rustfmt configuration 2 | # https://github.com/rust-lang/rustfmt/blob/HEAD/Configurations.md 3 | 4 | # Rustfmt cannot format long lines inside macros, but this option detects this. 5 | # This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/3391) 6 | error_on_line_overflow = true 7 | 8 | # Override the default formatting style. 9 | # See https://internals.rust-lang.org/t/running-rustfmt-on-rust-lang-rust-and-other-rust-lang-repositories/8732/81. 10 | use_small_heuristics = "Max" 11 | # This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/3370) 12 | overflow_delimited_expr = true 13 | # This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/4991). 14 | imports_granularity = "Crate" 15 | # This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/5083). 16 | group_imports = "StdExternalCrate" 17 | 18 | # Apply rustfmt to more places. 19 | # This is unstable (tracking issue: https://github.com/rust-lang/rustfmt/issues/3348). 20 | format_code_in_doc_comments = true 21 | 22 | # Automatically fix deprecated style. 23 | use_field_init_shorthand = true 24 | use_try_shorthand = true 25 | 26 | # Set the default settings again to always apply the proper formatting without 27 | # being affected by the editor settings. 28 | edition = "2021" 29 | style_edition = "2024" 30 | hard_tabs = false 31 | newline_style = "Unix" 32 | tab_spaces = 4 33 | -------------------------------------------------------------------------------- /tests/no-std-qemu/src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | #![no_main] 4 | #![no_std] 5 | #![allow(clippy::undocumented_unsafe_blocks, clippy::wildcard_imports)] 6 | 7 | include!("../../include/basic.rs"); 8 | 9 | use core::{mem::MaybeUninit, sync::atomic::Ordering}; 10 | 11 | use atomic_maybe_uninit::*; 12 | use semihosting::{print, println}; 13 | 14 | semihosting_no_std_test_rt::entry!(run); 15 | fn run() { 16 | macro_rules! test_atomic { 17 | ($ty:ident) => { 18 | paste::paste! { 19 | fn []() { 20 | __test_atomic!($ty); 21 | } 22 | print!("test test_atomic_{} ... ", stringify!($ty)); 23 | [](); 24 | println!("ok"); 25 | } 26 | }; 27 | } 28 | 29 | cfg_has_atomic_cas! { 30 | println!("target_has_cas: true"); 31 | } 32 | cfg_no_atomic_cas! { 33 | println!("target_has_cas: false"); 34 | } 35 | test_atomic!(isize); 36 | test_atomic!(usize); 37 | test_atomic!(i8); 38 | test_atomic!(u8); 39 | test_atomic!(i16); 40 | test_atomic!(u16); 41 | test_atomic!(i32); 42 | test_atomic!(u32); 43 | cfg_has_atomic_64! { 44 | test_atomic!(i64); 45 | test_atomic!(u64); 46 | } 47 | cfg_has_atomic_128! { 48 | test_atomic!(i128); 49 | test_atomic!(u128); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | #![allow( 4 | clippy::alloc_instead_of_core, 5 | clippy::arithmetic_side_effects, 6 | clippy::std_instead_of_alloc, 7 | clippy::std_instead_of_core, 8 | clippy::undocumented_unsafe_blocks, 9 | clippy::wildcard_imports 10 | )] 11 | 12 | #[macro_use] 13 | pub(crate) mod helper; 14 | 15 | use crate::*; 16 | 17 | test_common!(isize); 18 | test_common!(usize); 19 | cfg_has_atomic_ptr! { 20 | test_atomic!(isize); 21 | test_atomic!(usize); 22 | } 23 | test_common!(i8); 24 | test_common!(u8); 25 | cfg_has_atomic_8! { 26 | test_atomic!(i8); 27 | test_atomic!(u8); 28 | // load/store/swap implementation is not affected by signedness, so it is 29 | // enough to test only unsigned types. 30 | stress_test!(u8); 31 | } 32 | test_common!(i16); 33 | test_common!(u16); 34 | cfg_has_atomic_16! { 35 | test_atomic!(i16); 36 | test_atomic!(u16); 37 | stress_test!(u16); 38 | } 39 | test_common!(i32); 40 | test_common!(u32); 41 | cfg_has_atomic_32! { 42 | test_atomic!(i32); 43 | test_atomic!(u32); 44 | stress_test!(u32); 45 | } 46 | test_common!(i64); 47 | test_common!(u64); 48 | cfg_has_atomic_64! { 49 | test_atomic!(i64); 50 | test_atomic!(u64); 51 | stress_test!(u64); 52 | } 53 | test_common!(i128); 54 | test_common!(u128); 55 | #[cfg(not(all(valgrind, target_arch = "powerpc64")))] // TODO(powerpc64): Hang (as of Valgrind 3.25) 56 | cfg_has_atomic_128! { 57 | test_atomic!(i128); 58 | test_atomic!(u128); 59 | stress_test!(u128); 60 | } 61 | -------------------------------------------------------------------------------- /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "gitignoreRoot": ".", 4 | "useGitignore": true, 5 | "dictionaryDefinitions": [ 6 | { 7 | "name": "organization-dictionary", 8 | "path": "https://raw.githubusercontent.com/taiki-e/github-actions/HEAD/.github/.cspell/organization-dictionary.txt", 9 | "addWords": true 10 | }, 11 | { 12 | "name": "project-dictionary", 13 | "path": "./.github/.cspell/project-dictionary.txt", 14 | "addWords": true 15 | }, 16 | { 17 | "name": "rust-dependencies", 18 | "path": "./.github/.cspell/rust-dependencies.txt", 19 | "addWords": true 20 | } 21 | ], 22 | "dictionaries": [ 23 | "organization-dictionary", 24 | "project-dictionary", 25 | "rust-dependencies" 26 | ], 27 | "ignoreRegExpList": [ 28 | // Copyright notice 29 | "Copyright .*", 30 | "SPDX-(File|Snippet)CopyrightText: .*", 31 | // GHA actions/workflows 32 | "uses: .+@[\\w_.-]+", 33 | // GHA context (repo name, owner name, etc.) 34 | "github.[\\w_.-]+ (=|!)= '[^']+'", 35 | // GH username 36 | "( |\\[)@[\\w_-]+", 37 | // Git config username 38 | "git config( --[^ ]+)? user.name .*", 39 | // Username in TODO|FIXME comment 40 | "(TODO|FIXME)\\([\\w_., -]+\\)", 41 | // Cargo.toml authors 42 | "authors *= *\\[[^\\]]*\\]", 43 | "\"[^\"]* <[\\w_.+-]+@[\\w.-]+>\"" 44 | ], 45 | "languageSettings": [ 46 | { 47 | "languageId": ["*"], 48 | "dictionaries": ["bash", "cpp-refined", "rust"] 49 | } 50 | ], 51 | "ignorePaths": ["target-specs/**", "tools/valgrind/**"] 52 | } 53 | -------------------------------------------------------------------------------- /tests/xtensa/src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | #![no_main] 4 | #![no_std] 5 | #![allow(clippy::undocumented_unsafe_blocks, clippy::wildcard_imports)] 6 | 7 | include!("../../include/basic.rs"); 8 | 9 | use core::{mem::MaybeUninit, sync::atomic::Ordering}; 10 | 11 | use atomic_maybe_uninit::*; 12 | use esp_println::{print, println}; 13 | 14 | #[esp_hal::main] 15 | fn main() -> ! { 16 | macro_rules! test_atomic { 17 | ($ty:ident) => { 18 | paste::paste! { 19 | fn []() { 20 | __test_atomic!($ty); 21 | } 22 | print!("{}", concat!("test test_atomic_", stringify!($ty), " ... ")); 23 | [](); 24 | println!("ok"); 25 | } 26 | }; 27 | } 28 | 29 | cfg_has_atomic_cas! { 30 | println!("target_has_cas: true"); 31 | } 32 | cfg_no_atomic_cas! { 33 | println!("target_has_cas: false"); 34 | } 35 | test_atomic!(isize); 36 | test_atomic!(usize); 37 | test_atomic!(i8); 38 | test_atomic!(u8); 39 | test_atomic!(i16); 40 | test_atomic!(u16); 41 | test_atomic!(i32); 42 | test_atomic!(u32); 43 | 44 | println!("Tests finished successfully"); 45 | 46 | #[allow(clippy::empty_loop)] // this test crate is #![no_std] 47 | loop {} 48 | } 49 | 50 | #[inline(never)] 51 | #[panic_handler] 52 | fn panic(info: &core::panic::PanicInfo<'_>) -> ! { 53 | println!("{info}"); 54 | #[allow(clippy::empty_loop)] // this test crate is #![no_std] 55 | loop {} 56 | } 57 | -------------------------------------------------------------------------------- /.github/.cspell/project-dictionary.txt: -------------------------------------------------------------------------------- 1 | abiv 2 | amocas 3 | amoswap 4 | amswap 5 | andc 6 | andn 7 | aqrl 8 | armasm 9 | atxmega 10 | beqz 11 | Bicc 12 | bnez 13 | boundedly 14 | brne 15 | bswap 16 | casp 17 | cbnz 18 | ccmp 19 | cdsg 20 | CDSY 21 | clrex 22 | cmpne 23 | cmpw 24 | cmpxchg 25 | csel 26 | cset 27 | cskyv 28 | dbar 29 | DWCAS 30 | espup 31 | fild 32 | fistp 33 | gaisler 34 | getex 35 | GRLIB 36 | Halfword 37 | hwsync 38 | IMAFD 39 | inequal 40 | ishld 41 | isync 42 | kuser 43 | LAAL 44 | lclang 45 | ldar 46 | ldarx 47 | ldaxp 48 | ldclrp 49 | ldex 50 | ldiapp 51 | ldrex 52 | ldrexd 53 | ldsetp 54 | ldstub 55 | ldxp 56 | leoncasa 57 | lgcc 58 | libatomic 59 | libnspr 60 | linkall 61 | LLDP 62 | LLWP 63 | LLWPE 64 | lmul 65 | lqarx 66 | lrcpc 67 | lwarx 68 | lwsync 69 | mcpu 70 | membar 71 | memcheck 72 | memd 73 | memw 74 | mfcr 75 | mfence 76 | mgba 77 | movi 78 | movlps 79 | movq 80 | mspdebug 81 | msync 82 | muldi 83 | noat 84 | nostartfiles 85 | opensbi 86 | orrs 87 | partword 88 | pstq 89 | pthread 90 | qbsp 91 | quadword 92 | rcpc 93 | risbg 94 | rsbegin 95 | rsend 96 | RVWMO 97 | SCDP 98 | scompare 99 | SCWP 100 | SCWPE 101 | seqz 102 | sete 103 | signedness 104 | simavr 105 | simio 106 | slli 107 | sllv 108 | srai 109 | sreg 110 | srlv 111 | stbar 112 | stdcx 113 | stex 114 | stilp 115 | stlxp 116 | stpq 117 | stqcx 118 | strex 119 | strexd 120 | stwcx 121 | stxp 122 | subarch 123 | swpp 124 | systeminfo 125 | tsim 126 | uart 127 | ultrasparc 128 | usart 129 | uwrite 130 | uwriteln 131 | uxtb 132 | uxth 133 | virt 134 | vmovdqa 135 | wokwi 136 | xadd 137 | xchg 138 | xmegau 139 | xmmword 140 | zaamo 141 | zabha 142 | zacas 143 | zalasr 144 | zalrsc 145 | -------------------------------------------------------------------------------- /src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | // Note: This file is enabled when the current target is not supported. 4 | 5 | // Refs: 6 | // - inline assembly reference: https://doc.rust-lang.org/nightly/reference/inline-assembly.html 7 | // - inline assembly rust by example: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html 8 | // - feature(asm_experimental_arch): https://doc.rust-lang.org/nightly/unstable-book/language-features/asm-experimental-arch.html 9 | // - asm module in rustc_target: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc_target/src/asm 10 | // - asm module in rustc_codegen_llvm: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_codegen_llvm/src/asm.rs 11 | // - LLVM LangRef: https://llvm.org/docs/LangRef.html#inline-assembler-expressions 12 | // - inline assembly related issues in rust-lang/rust: https://github.com/rust-lang/rust/labels/A-inline-assembly 13 | 14 | #[cfg(test)] 15 | compile_error!("testing unsupported target is not supported"); 16 | 17 | #[macro_export] 18 | macro_rules! cfg_has_atomic_8 { 19 | ($($tt:tt)*) => {}; 20 | } 21 | #[macro_export] 22 | macro_rules! cfg_no_atomic_8 { 23 | ($($tt:tt)*) => { $($tt)* }; 24 | } 25 | #[macro_export] 26 | macro_rules! cfg_has_atomic_16 { 27 | ($($tt:tt)*) => {}; 28 | } 29 | #[macro_export] 30 | macro_rules! cfg_no_atomic_16 { 31 | ($($tt:tt)*) => { $($tt)* }; 32 | } 33 | #[macro_export] 34 | macro_rules! cfg_has_atomic_32 { 35 | ($($tt:tt)*) => {}; 36 | } 37 | #[macro_export] 38 | macro_rules! cfg_no_atomic_32 { 39 | ($($tt:tt)*) => { $($tt)* }; 40 | } 41 | #[macro_export] 42 | macro_rules! cfg_has_atomic_64 { 43 | ($($tt:tt)*) => {}; 44 | } 45 | #[macro_export] 46 | macro_rules! cfg_no_atomic_64 { 47 | ($($tt:tt)*) => { $($tt)* }; 48 | } 49 | #[macro_export] 50 | macro_rules! cfg_has_atomic_128 { 51 | ($($tt:tt)*) => {}; 52 | } 53 | #[macro_export] 54 | macro_rules! cfg_no_atomic_128 { 55 | ($($tt:tt)*) => { $($tt)* }; 56 | } 57 | #[macro_export] 58 | macro_rules! cfg_has_atomic_cas { 59 | ($($tt:tt)*) => {}; 60 | } 61 | #[macro_export] 62 | macro_rules! cfg_no_atomic_cas { 63 | ($($tt:tt)*) => { $($tt)* }; 64 | } 65 | -------------------------------------------------------------------------------- /tests/sparc/src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | #![no_main] 4 | #![no_std] 5 | #![allow(clippy::undocumented_unsafe_blocks, clippy::wildcard_imports)] 6 | 7 | include!("../../include/basic.rs"); 8 | 9 | use core::{mem::MaybeUninit, sync::atomic::Ordering}; 10 | 11 | use atomic_maybe_uninit::*; 12 | 13 | macro_rules! print { 14 | ($($tt:tt)*) => {{ 15 | use core::fmt::Write as _; 16 | let _ = write!(sim::Console, $($tt)*); 17 | }}; 18 | } 19 | macro_rules! println { 20 | ($($tt:tt)*) => {{ 21 | use core::fmt::Write as _; 22 | let _ = writeln!(sim::Console, $($tt)*); 23 | }}; 24 | } 25 | 26 | #[no_mangle] 27 | extern "C" fn main() -> i32 { 28 | macro_rules! test_atomic { 29 | ($ty:ident) => { 30 | paste::paste! { 31 | fn []() { 32 | __test_atomic!($ty); 33 | } 34 | print!("test test_atomic_{} ... ", stringify!($ty)); 35 | [](); 36 | println!("ok"); 37 | } 38 | }; 39 | } 40 | 41 | cfg_has_atomic_cas! { 42 | println!("target_has_cas: true"); 43 | } 44 | cfg_no_atomic_cas! { 45 | println!("target_has_cas: false"); 46 | } 47 | test_atomic!(isize); 48 | test_atomic!(usize); 49 | test_atomic!(i8); 50 | test_atomic!(u8); 51 | test_atomic!(i16); 52 | test_atomic!(u16); 53 | test_atomic!(i32); 54 | test_atomic!(u32); 55 | 56 | println!("Tests finished successfully"); 57 | 58 | 0 59 | } 60 | 61 | #[inline(never)] 62 | #[panic_handler] 63 | fn panic(info: &core::panic::PanicInfo<'_>) -> ! { 64 | println!("{info}"); 65 | // Note that this doesn't make tsim-leon3 to be exit with non-zero exit code. 66 | unsafe { sim::_exit(1) } 67 | } 68 | 69 | mod sim { 70 | // Refs: https://github.com/rust-lang/rust/blob/1.90.0/src/doc/rustc/src/platform-support/sparc-unknown-none-elf.md 71 | 72 | use core::fmt; 73 | 74 | extern "C" { 75 | fn putchar(ch: i32); 76 | pub fn _exit(code: i32) -> !; 77 | } 78 | 79 | pub struct Console; 80 | impl fmt::Write for Console { 81 | fn write_str(&mut self, s: &str) -> fmt::Result { 82 | for &b in s.as_bytes() { 83 | unsafe { putchar(b as i32) } 84 | } 85 | Ok(()) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/no-std-linux/src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | #![no_main] 4 | #![no_std] 5 | #![allow(clippy::undocumented_unsafe_blocks, clippy::wildcard_imports)] 6 | 7 | include!("../../include/basic.rs"); 8 | 9 | use core::{mem::MaybeUninit, sync::atomic::Ordering}; 10 | 11 | use atomic_maybe_uninit::*; 12 | 13 | #[no_mangle] 14 | extern "C" fn main() { 15 | macro_rules! test_atomic { 16 | ($ty:ident) => { 17 | paste::paste! { 18 | fn []() { 19 | __test_atomic!($ty); 20 | } 21 | unsafe { 22 | libc::puts(concat!("test test_atomic_", stringify!($ty), " ... \0") as *const str as *const i8); 23 | } 24 | [](); 25 | unsafe { 26 | libc::puts("ok\0" as *const str as *const i8); 27 | } 28 | } 29 | }; 30 | } 31 | 32 | cfg_has_atomic_cas! { 33 | unsafe { 34 | libc::puts("target_has_cas: true\0" as *const str as *const i8); 35 | } 36 | } 37 | cfg_no_atomic_cas! { 38 | unsafe { 39 | libc::puts("target_has_cas: false\0" as *const str as *const i8); 40 | } 41 | } 42 | #[cfg(feature = "isize")] 43 | test_atomic!(isize); 44 | #[cfg(feature = "usize")] 45 | test_atomic!(usize); 46 | #[cfg(feature = "i8")] 47 | test_atomic!(i8); 48 | #[cfg(feature = "u8")] 49 | test_atomic!(u8); 50 | #[cfg(feature = "i16")] 51 | test_atomic!(i16); 52 | #[cfg(feature = "u16")] 53 | test_atomic!(u16); 54 | #[cfg(feature = "i32")] 55 | test_atomic!(i32); 56 | #[cfg(feature = "u32")] 57 | test_atomic!(u32); 58 | cfg_has_atomic_64! { 59 | #[cfg(feature = "i64")] 60 | test_atomic!(i64); 61 | #[cfg(feature = "u64")] 62 | test_atomic!(u64); 63 | } 64 | 65 | unsafe { libc::exit(0) } 66 | } 67 | 68 | #[inline(never)] 69 | #[panic_handler] 70 | fn panic(_info: &core::panic::PanicInfo<'_>) -> ! { 71 | // macro_rules! println { 72 | // ($($tt:tt)*) => { 73 | // use core::fmt::Write as _; 74 | // let _ = writeln!(simio::Console, $($tt)*); 75 | // }; 76 | // } 77 | 78 | // println!("{info}"); 79 | unsafe { 80 | libc::puts("panicked\0" as *const str as *const i8); 81 | libc::abort() 82 | } 83 | } 84 | 85 | mod libc { 86 | #[link(name = "c")] 87 | extern "C" { 88 | pub fn abort() -> !; 89 | pub fn exit(status: i32) -> !; 90 | pub fn puts(s: *const i8) -> i32; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/msp430/src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | #![no_main] 4 | #![no_std] 5 | #![allow(clippy::undocumented_unsafe_blocks, clippy::wildcard_imports)] 6 | 7 | include!("../../include/basic.rs"); 8 | 9 | use core::{mem::MaybeUninit, sync::atomic::Ordering}; 10 | 11 | use atomic_maybe_uninit::*; 12 | use msp430f5529 as _; 13 | 14 | macro_rules! print { 15 | ($($tt:tt)*) => { 16 | let _ = ufmt::uwrite!(simio::Console, $($tt)*); 17 | }; 18 | } 19 | macro_rules! println { 20 | ($($tt:tt)*) => { 21 | let _ = ufmt::uwriteln!(simio::Console, $($tt)*); 22 | }; 23 | } 24 | 25 | #[msp430_rt::entry] 26 | fn main() -> ! { 27 | macro_rules! test_atomic { 28 | ($ty:ident) => { 29 | paste::paste! { 30 | fn []() { 31 | __test_atomic!($ty); 32 | } 33 | print!("{}", concat!("test test_atomic_", stringify!($ty), " ... ")); 34 | [](); 35 | println!("ok"); 36 | } 37 | }; 38 | } 39 | 40 | test_atomic!(isize); 41 | test_atomic!(usize); 42 | test_atomic!(i8); 43 | test_atomic!(u8); 44 | test_atomic!(i16); 45 | test_atomic!(u16); 46 | 47 | println!("Tests finished successfully"); 48 | 49 | #[allow(clippy::empty_loop)] // this test crate is #![no_std] 50 | loop {} 51 | } 52 | 53 | #[inline(never)] 54 | #[panic_handler] 55 | fn panic(info: &core::panic::PanicInfo<'_>) -> ! { 56 | macro_rules! println { 57 | ($($tt:tt)*) => { 58 | use core::fmt::Write as _; 59 | let _ = writeln!(simio::Console, $($tt)*); 60 | }; 61 | } 62 | 63 | println!("{info}"); // this println takes a lot of spaces but better panic message is useful... 64 | #[allow(clippy::empty_loop)] // this test crate is #![no_std] 65 | loop {} 66 | } 67 | 68 | #[no_mangle] 69 | extern "C" fn abort() -> ! { 70 | panic!() 71 | } 72 | 73 | mod simio { 74 | use core::{convert::Infallible, fmt}; 75 | 76 | pub struct Console; 77 | fn write_str(s: &str) { 78 | // https://github.com/dlbeer/mspdebug/blob/v0.25/simio/simio_console.c#L130 79 | let addr = 0x00FF_usize as *mut u8; 80 | for &b in s.as_bytes() { 81 | unsafe { addr.write_volatile(b) } 82 | } 83 | } 84 | impl ufmt::uWrite for Console { 85 | type Error = Infallible; 86 | fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { 87 | write_str(s); 88 | Ok(()) 89 | } 90 | } 91 | impl fmt::Write for Console { 92 | fn write_str(&mut self, s: &str) -> fmt::Result { 93 | write_str(s); 94 | Ok(()) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/avr/src/main.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | #![no_main] 4 | #![no_std] 5 | #![allow(clippy::undocumented_unsafe_blocks, clippy::wildcard_imports)] 6 | 7 | include!("../../include/basic.rs"); 8 | 9 | use core::{mem::MaybeUninit, sync::atomic::Ordering}; 10 | 11 | use arduino_hal::avr_device; 12 | use atomic_maybe_uninit::*; 13 | 14 | #[avr_device::entry] 15 | fn main() -> ! { 16 | let dp = arduino_hal::Peripherals::take().unwrap(); 17 | let pins = arduino_hal::pins!(dp); 18 | let mut serial = arduino_hal::default_serial!(dp, pins, 57600); 19 | 20 | macro_rules! print { 21 | ($($tt:tt)*) => {{ 22 | let _ = ufmt::uwrite!(serial, $($tt)*); 23 | }}; 24 | } 25 | macro_rules! println { 26 | ($($tt:tt)*) => {{ 27 | let _ = ufmt::uwriteln!(serial, $($tt)*); 28 | }}; 29 | } 30 | 31 | macro_rules! test_atomic { 32 | ($ty:ident) => { 33 | paste::paste! { 34 | fn []() { 35 | __test_atomic!($ty); 36 | } 37 | print!("test test_atomic_{} ... ", stringify!($ty)); 38 | [](); 39 | println!("ok"); 40 | } 41 | }; 42 | } 43 | 44 | test_atomic!(isize); 45 | test_atomic!(usize); 46 | test_atomic!(i8); 47 | test_atomic!(u8); 48 | test_atomic!(i16); 49 | test_atomic!(u16); 50 | 51 | println!("Tests finished successfully"); 52 | 53 | sim::exit(0) 54 | } 55 | 56 | #[inline(never)] 57 | #[panic_handler] 58 | fn panic(info: &core::panic::PanicInfo<'_>) -> ! { 59 | avr_device::interrupt::disable(); 60 | 61 | let dp = unsafe { arduino_hal::Peripherals::steal() }; 62 | let pins = arduino_hal::pins!(dp); 63 | let mut serial = sim::Usart(arduino_hal::default_serial!(dp, pins, 57600)); 64 | 65 | macro_rules! println { 66 | ($($tt:tt)*) => {{ 67 | use core::fmt::Write as _; 68 | let _ = writeln!(serial, $($tt)*); 69 | }}; 70 | } 71 | 72 | println!("{info}"); 73 | sim::exit(1) 74 | } 75 | 76 | mod sim { 77 | use core::fmt; 78 | 79 | use arduino_hal::avr_device; 80 | 81 | pub fn exit(_code: u32) -> ! { 82 | // Note that there is no way to exit simavr with a non-zero exit code. 83 | // https://github.com/buserror/simavr/issues/362 84 | avr_device::interrupt::disable(); 85 | avr_device::asm::sleep(); 86 | #[allow(clippy::empty_loop)] // this test crate is #![no_std] 87 | loop {} 88 | } 89 | 90 | pub struct Usart(pub arduino_hal::usart::Usart) 91 | where 92 | USART: arduino_hal::usart::UsartOps; 93 | impl fmt::Write for Usart 94 | where 95 | USART: arduino_hal::usart::UsartOps, 96 | { 97 | fn write_str(&mut self, s: &str) -> fmt::Result { 98 | for b in s.bytes() { 99 | self.0.write_byte(b); 100 | } 101 | Ok(()) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /tests/sparc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sparc-test" 3 | version = "0.0.0" 4 | edition = "2021" 5 | rust-version = "1.74" # Prevent clippy from suggesting a code that requires a new version. 6 | publish = false 7 | 8 | [dependencies] 9 | atomic-maybe-uninit = { path = "../.." } 10 | 11 | paste = "1" 12 | 13 | [lints] 14 | workspace = true 15 | 16 | [workspace] 17 | resolver = "2" 18 | 19 | # This table is shared by projects under github.com/taiki-e. 20 | # Expect for unexpected_cfgs.check-cfg, it is not intended for manual editing. 21 | [workspace.lints.rust] 22 | deprecated_safe = "warn" 23 | improper_ctypes = "warn" 24 | improper_ctypes_definitions = "warn" 25 | non_ascii_idents = "warn" 26 | rust_2018_idioms = "warn" 27 | single_use_lifetimes = "warn" 28 | unexpected_cfgs = { level = "warn", check-cfg = [ 29 | ] } 30 | # unnameable_types = "warn" 31 | # unreachable_pub = "warn" 32 | unsafe_op_in_unsafe_fn = "warn" 33 | [workspace.lints.clippy] 34 | all = "warn" # Downgrade deny-by-default lints 35 | pedantic = "warn" 36 | as_ptr_cast_mut = "warn" 37 | as_underscore = "warn" 38 | default_union_representation = "warn" 39 | inline_asm_x86_att_syntax = "warn" 40 | trailing_empty_array = "warn" 41 | transmute_undefined_repr = "warn" 42 | undocumented_unsafe_blocks = "warn" 43 | unused_trait_names = "warn" 44 | # Suppress buggy or noisy clippy lints 45 | bool_assert_comparison = { level = "allow", priority = 1 } 46 | borrow_as_ptr = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/8286 47 | cast_lossless = { level = "allow", priority = 1 } # https://godbolt.org/z/Pv6vbGG6E 48 | declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665 49 | doc_markdown = { level = "allow", priority = 1 } 50 | float_cmp = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7725 51 | incompatible_msrv = { level = "allow", priority = 1 } # buggy: doesn't consider cfg, https://github.com/rust-lang/rust-clippy/issues/12280, https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 52 | lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920 53 | manual_assert = { level = "allow", priority = 1 } 54 | manual_range_contains = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/6455#issuecomment-1225966395 55 | missing_errors_doc = { level = "allow", priority = 1 } 56 | module_name_repetitions = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+module_name_repetitions 57 | naive_bytecount = { level = "allow", priority = 1 } 58 | nonminimal_bool = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+nonminimal_bool 59 | range_plus_one = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+range_plus_one 60 | similar_names = { level = "allow", priority = 1 } 61 | single_match = { level = "allow", priority = 1 } 62 | single_match_else = { level = "allow", priority = 1 } 63 | struct_excessive_bools = { level = "allow", priority = 1 } 64 | struct_field_names = { level = "allow", priority = 1 } 65 | too_many_arguments = { level = "allow", priority = 1 } 66 | too_many_lines = { level = "allow", priority = 1 } 67 | type_complexity = { level = "allow", priority = 1 } 68 | unreadable_literal = { level = "allow", priority = 1 } 69 | -------------------------------------------------------------------------------- /tests/xtensa/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtensa-test" 3 | version = "0.0.0" 4 | edition = "2021" 5 | rust-version = "1.74" # Prevent clippy from suggesting a code that requires a new version. 6 | publish = false 7 | 8 | [dependencies] 9 | atomic-maybe-uninit = { path = "../.." } 10 | 11 | esp-hal = "0.23" 12 | esp-println = { version = "0.16", default-features = false, features = ["uart"] } 13 | paste = "1" 14 | 15 | [lints] 16 | workspace = true 17 | 18 | [workspace] 19 | resolver = "2" 20 | 21 | # This table is shared by projects under github.com/taiki-e. 22 | # Expect for unexpected_cfgs.check-cfg, it is not intended for manual editing. 23 | [workspace.lints.rust] 24 | deprecated_safe = "warn" 25 | improper_ctypes = "warn" 26 | improper_ctypes_definitions = "warn" 27 | non_ascii_idents = "warn" 28 | rust_2018_idioms = "warn" 29 | single_use_lifetimes = "warn" 30 | unexpected_cfgs = { level = "warn", check-cfg = [ 31 | ] } 32 | # unnameable_types = "warn" 33 | # unreachable_pub = "warn" 34 | unsafe_op_in_unsafe_fn = "warn" 35 | [workspace.lints.clippy] 36 | all = "warn" # Downgrade deny-by-default lints 37 | pedantic = "warn" 38 | as_ptr_cast_mut = "warn" 39 | as_underscore = "warn" 40 | default_union_representation = "warn" 41 | inline_asm_x86_att_syntax = "warn" 42 | trailing_empty_array = "warn" 43 | transmute_undefined_repr = "warn" 44 | undocumented_unsafe_blocks = "warn" 45 | unused_trait_names = "warn" 46 | # Suppress buggy or noisy clippy lints 47 | bool_assert_comparison = { level = "allow", priority = 1 } 48 | borrow_as_ptr = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/8286 49 | cast_lossless = { level = "allow", priority = 1 } # https://godbolt.org/z/Pv6vbGG6E 50 | declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665 51 | doc_markdown = { level = "allow", priority = 1 } 52 | float_cmp = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7725 53 | incompatible_msrv = { level = "allow", priority = 1 } # buggy: doesn't consider cfg, https://github.com/rust-lang/rust-clippy/issues/12280, https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 54 | lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920 55 | manual_assert = { level = "allow", priority = 1 } 56 | manual_range_contains = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/6455#issuecomment-1225966395 57 | missing_errors_doc = { level = "allow", priority = 1 } 58 | module_name_repetitions = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+module_name_repetitions 59 | naive_bytecount = { level = "allow", priority = 1 } 60 | nonminimal_bool = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+nonminimal_bool 61 | range_plus_one = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+range_plus_one 62 | similar_names = { level = "allow", priority = 1 } 63 | single_match = { level = "allow", priority = 1 } 64 | single_match_else = { level = "allow", priority = 1 } 65 | struct_excessive_bools = { level = "allow", priority = 1 } 66 | struct_field_names = { level = "allow", priority = 1 } 67 | too_many_arguments = { level = "allow", priority = 1 } 68 | too_many_lines = { level = "allow", priority = 1 } 69 | type_complexity = { level = "allow", priority = 1 } 70 | unreadable_literal = { level = "allow", priority = 1 } 71 | 72 | [profile.dev] 73 | opt-level = 'z' 74 | 75 | [profile.release] 76 | opt-level = 'z' 77 | -------------------------------------------------------------------------------- /tests/no-std-qemu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "no-std-qemu-test" 3 | version = "0.0.0" 4 | edition = "2021" 5 | rust-version = "1.74" # Prevent clippy from suggesting a code that requires a new version. 6 | publish = false 7 | 8 | [dependencies] 9 | atomic-maybe-uninit = { path = "../.." } 10 | 11 | paste = "1" 12 | semihosting = { version = "0.1", features = ["stdio", "panic-handler"] } 13 | semihosting-no-std-test-rt = { features = ["qemu-system"], git = "https://github.com/taiki-e/semihosting.git", rev = "f22ca36" } 14 | 15 | [lints] 16 | workspace = true 17 | 18 | [workspace] 19 | resolver = "2" 20 | 21 | # This table is shared by projects under github.com/taiki-e. 22 | # Expect for unexpected_cfgs.check-cfg, it is not intended for manual editing. 23 | [workspace.lints.rust] 24 | deprecated_safe = "warn" 25 | improper_ctypes = "warn" 26 | improper_ctypes_definitions = "warn" 27 | non_ascii_idents = "warn" 28 | rust_2018_idioms = "warn" 29 | single_use_lifetimes = "warn" 30 | unexpected_cfgs = { level = "warn", check-cfg = [ 31 | ] } 32 | # unnameable_types = "warn" 33 | # unreachable_pub = "warn" 34 | unsafe_op_in_unsafe_fn = "warn" 35 | [workspace.lints.clippy] 36 | all = "warn" # Downgrade deny-by-default lints 37 | pedantic = "warn" 38 | as_ptr_cast_mut = "warn" 39 | as_underscore = "warn" 40 | default_union_representation = "warn" 41 | inline_asm_x86_att_syntax = "warn" 42 | trailing_empty_array = "warn" 43 | transmute_undefined_repr = "warn" 44 | undocumented_unsafe_blocks = "warn" 45 | unused_trait_names = "warn" 46 | # Suppress buggy or noisy clippy lints 47 | bool_assert_comparison = { level = "allow", priority = 1 } 48 | borrow_as_ptr = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/8286 49 | cast_lossless = { level = "allow", priority = 1 } # https://godbolt.org/z/Pv6vbGG6E 50 | declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665 51 | doc_markdown = { level = "allow", priority = 1 } 52 | float_cmp = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7725 53 | incompatible_msrv = { level = "allow", priority = 1 } # buggy: doesn't consider cfg, https://github.com/rust-lang/rust-clippy/issues/12280, https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 54 | lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920 55 | manual_assert = { level = "allow", priority = 1 } 56 | manual_range_contains = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/6455#issuecomment-1225966395 57 | missing_errors_doc = { level = "allow", priority = 1 } 58 | module_name_repetitions = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+module_name_repetitions 59 | naive_bytecount = { level = "allow", priority = 1 } 60 | nonminimal_bool = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+nonminimal_bool 61 | range_plus_one = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+range_plus_one 62 | similar_names = { level = "allow", priority = 1 } 63 | single_match = { level = "allow", priority = 1 } 64 | single_match_else = { level = "allow", priority = 1 } 65 | struct_excessive_bools = { level = "allow", priority = 1 } 66 | struct_field_names = { level = "allow", priority = 1 } 67 | too_many_arguments = { level = "allow", priority = 1 } 68 | too_many_lines = { level = "allow", priority = 1 } 69 | type_complexity = { level = "allow", priority = 1 } 70 | unreadable_literal = { level = "allow", priority = 1 } 71 | -------------------------------------------------------------------------------- /tests/avr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "avr-test" 3 | version = "0.0.0" 4 | edition = "2021" 5 | rust-version = "1.74" # Prevent clippy from suggesting a code that requires a new version. 6 | publish = false 7 | 8 | [dependencies] 9 | atomic-maybe-uninit = { path = "../.." } 10 | 11 | arduino-hal = { features = ["arduino-mega2560"], git = "https://github.com/taiki-e/avr-hal.git", rev = "3e35ecf" } 12 | paste = "1" 13 | ufmt = "0.2" 14 | 15 | [lints] 16 | workspace = true 17 | 18 | [workspace] 19 | resolver = "2" 20 | 21 | # This table is shared by projects under github.com/taiki-e. 22 | # Expect for unexpected_cfgs.check-cfg, it is not intended for manual editing. 23 | [workspace.lints.rust] 24 | deprecated_safe = "warn" 25 | improper_ctypes = "warn" 26 | improper_ctypes_definitions = "warn" 27 | non_ascii_idents = "warn" 28 | rust_2018_idioms = "warn" 29 | single_use_lifetimes = "warn" 30 | unexpected_cfgs = { level = "warn", check-cfg = [ 31 | ] } 32 | # unnameable_types = "warn" 33 | # unreachable_pub = "warn" 34 | unsafe_op_in_unsafe_fn = "warn" 35 | [workspace.lints.clippy] 36 | all = "warn" # Downgrade deny-by-default lints 37 | pedantic = "warn" 38 | as_ptr_cast_mut = "warn" 39 | as_underscore = "warn" 40 | default_union_representation = "warn" 41 | inline_asm_x86_att_syntax = "warn" 42 | trailing_empty_array = "warn" 43 | transmute_undefined_repr = "warn" 44 | undocumented_unsafe_blocks = "warn" 45 | unused_trait_names = "warn" 46 | # Suppress buggy or noisy clippy lints 47 | bool_assert_comparison = { level = "allow", priority = 1 } 48 | borrow_as_ptr = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/8286 49 | cast_lossless = { level = "allow", priority = 1 } # https://godbolt.org/z/Pv6vbGG6E 50 | declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665 51 | doc_markdown = { level = "allow", priority = 1 } 52 | float_cmp = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7725 53 | incompatible_msrv = { level = "allow", priority = 1 } # buggy: doesn't consider cfg, https://github.com/rust-lang/rust-clippy/issues/12280, https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 54 | lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920 55 | manual_assert = { level = "allow", priority = 1 } 56 | manual_range_contains = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/6455#issuecomment-1225966395 57 | missing_errors_doc = { level = "allow", priority = 1 } 58 | module_name_repetitions = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+module_name_repetitions 59 | naive_bytecount = { level = "allow", priority = 1 } 60 | nonminimal_bool = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+nonminimal_bool 61 | range_plus_one = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+range_plus_one 62 | similar_names = { level = "allow", priority = 1 } 63 | single_match = { level = "allow", priority = 1 } 64 | single_match_else = { level = "allow", priority = 1 } 65 | struct_excessive_bools = { level = "allow", priority = 1 } 66 | struct_field_names = { level = "allow", priority = 1 } 67 | too_many_arguments = { level = "allow", priority = 1 } 68 | too_many_lines = { level = "allow", priority = 1 } 69 | type_complexity = { level = "allow", priority = 1 } 70 | unreadable_literal = { level = "allow", priority = 1 } 71 | 72 | [profile.dev] 73 | codegen-units = 1 74 | lto = true 75 | opt-level = "s" 76 | panic = "abort" 77 | 78 | [profile.release] 79 | codegen-units = 1 80 | lto = true 81 | opt-level = "s" 82 | panic = "abort" 83 | -------------------------------------------------------------------------------- /tests/msp430/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "msp430-test" 3 | version = "0.0.0" 4 | edition = "2021" 5 | rust-version = "1.74" # Prevent clippy from suggesting a code that requires a new version. 6 | publish = false 7 | 8 | [dependencies] 9 | atomic-maybe-uninit = { path = "../.." } 10 | 11 | msp430-rt = "0.4" 12 | msp430f5529 = { features = ["rt"], git = "https://github.com/cr1901/msp430f5529.git", rev = "23946ef" } 13 | paste = "1" 14 | ufmt = "0.2" 15 | 16 | [lints] 17 | workspace = true 18 | 19 | [workspace] 20 | resolver = "2" 21 | 22 | # This table is shared by projects under github.com/taiki-e. 23 | # Expect for unexpected_cfgs.check-cfg, it is not intended for manual editing. 24 | [workspace.lints.rust] 25 | deprecated_safe = "warn" 26 | improper_ctypes = "warn" 27 | improper_ctypes_definitions = "warn" 28 | non_ascii_idents = "warn" 29 | rust_2018_idioms = "warn" 30 | single_use_lifetimes = "warn" 31 | unexpected_cfgs = { level = "warn", check-cfg = [ 32 | ] } 33 | # unnameable_types = "warn" 34 | # unreachable_pub = "warn" 35 | unsafe_op_in_unsafe_fn = "warn" 36 | [workspace.lints.clippy] 37 | all = "warn" # Downgrade deny-by-default lints 38 | pedantic = "warn" 39 | as_ptr_cast_mut = "warn" 40 | as_underscore = "warn" 41 | default_union_representation = "warn" 42 | inline_asm_x86_att_syntax = "warn" 43 | trailing_empty_array = "warn" 44 | transmute_undefined_repr = "warn" 45 | undocumented_unsafe_blocks = "warn" 46 | unused_trait_names = "warn" 47 | # Suppress buggy or noisy clippy lints 48 | bool_assert_comparison = { level = "allow", priority = 1 } 49 | borrow_as_ptr = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/8286 50 | cast_lossless = { level = "allow", priority = 1 } # https://godbolt.org/z/Pv6vbGG6E 51 | declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665 52 | doc_markdown = { level = "allow", priority = 1 } 53 | float_cmp = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7725 54 | incompatible_msrv = { level = "allow", priority = 1 } # buggy: doesn't consider cfg, https://github.com/rust-lang/rust-clippy/issues/12280, https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 55 | lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920 56 | manual_assert = { level = "allow", priority = 1 } 57 | manual_range_contains = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/6455#issuecomment-1225966395 58 | missing_errors_doc = { level = "allow", priority = 1 } 59 | module_name_repetitions = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+module_name_repetitions 60 | naive_bytecount = { level = "allow", priority = 1 } 61 | nonminimal_bool = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+nonminimal_bool 62 | range_plus_one = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+range_plus_one 63 | similar_names = { level = "allow", priority = 1 } 64 | single_match = { level = "allow", priority = 1 } 65 | single_match_else = { level = "allow", priority = 1 } 66 | struct_excessive_bools = { level = "allow", priority = 1 } 67 | struct_field_names = { level = "allow", priority = 1 } 68 | too_many_arguments = { level = "allow", priority = 1 } 69 | too_many_lines = { level = "allow", priority = 1 } 70 | type_complexity = { level = "allow", priority = 1 } 71 | unreadable_literal = { level = "allow", priority = 1 } 72 | 73 | [profile.dev] 74 | codegen-units = 1 75 | lto = true 76 | opt-level = "s" 77 | panic = "abort" 78 | 79 | [profile.release] 80 | codegen-units = 1 81 | lto = true 82 | opt-level = "s" 83 | panic = "abort" 84 | -------------------------------------------------------------------------------- /tests/no-std-linux/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "no-std-linux-test" 3 | version = "0.0.0" 4 | edition = "2021" 5 | rust-version = "1.74" # Prevent clippy from suggesting a code that requires a new version. 6 | publish = false 7 | 8 | [features] 9 | default = ["isize", "usize", "i8", "u8"] 10 | # NB: Sync feature list with tools/no-std.sh 11 | isize = [] 12 | usize = [] 13 | i8 = [] 14 | u8 = [] 15 | i16 = [] 16 | u16 = [] 17 | i32 = [] 18 | u32 = [] 19 | i64 = [] 20 | u64 = [] 21 | 22 | [dependencies] 23 | atomic-maybe-uninit = { path = "../.." } 24 | 25 | paste = "1" 26 | 27 | [lints] 28 | workspace = true 29 | 30 | [workspace] 31 | resolver = "2" 32 | 33 | # This table is shared by projects under github.com/taiki-e. 34 | # Expect for unexpected_cfgs.check-cfg, it is not intended for manual editing. 35 | [workspace.lints.rust] 36 | deprecated_safe = "warn" 37 | improper_ctypes = "warn" 38 | improper_ctypes_definitions = "warn" 39 | non_ascii_idents = "warn" 40 | rust_2018_idioms = "warn" 41 | single_use_lifetimes = "warn" 42 | unexpected_cfgs = { level = "warn", check-cfg = [ 43 | ] } 44 | # unnameable_types = "warn" 45 | # unreachable_pub = "warn" 46 | unsafe_op_in_unsafe_fn = "warn" 47 | [workspace.lints.clippy] 48 | all = "warn" # Downgrade deny-by-default lints 49 | pedantic = "warn" 50 | as_ptr_cast_mut = "warn" 51 | as_underscore = "warn" 52 | default_union_representation = "warn" 53 | inline_asm_x86_att_syntax = "warn" 54 | trailing_empty_array = "warn" 55 | transmute_undefined_repr = "warn" 56 | undocumented_unsafe_blocks = "warn" 57 | unused_trait_names = "warn" 58 | # Suppress buggy or noisy clippy lints 59 | bool_assert_comparison = { level = "allow", priority = 1 } 60 | borrow_as_ptr = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/8286 61 | cast_lossless = { level = "allow", priority = 1 } # https://godbolt.org/z/Pv6vbGG6E 62 | declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665 63 | doc_markdown = { level = "allow", priority = 1 } 64 | float_cmp = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7725 65 | incompatible_msrv = { level = "allow", priority = 1 } # buggy: doesn't consider cfg, https://github.com/rust-lang/rust-clippy/issues/12280, https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 66 | lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920 67 | manual_assert = { level = "allow", priority = 1 } 68 | manual_range_contains = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/6455#issuecomment-1225966395 69 | missing_errors_doc = { level = "allow", priority = 1 } 70 | module_name_repetitions = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+module_name_repetitions 71 | naive_bytecount = { level = "allow", priority = 1 } 72 | nonminimal_bool = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+nonminimal_bool 73 | range_plus_one = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+range_plus_one 74 | similar_names = { level = "allow", priority = 1 } 75 | single_match = { level = "allow", priority = 1 } 76 | single_match_else = { level = "allow", priority = 1 } 77 | struct_excessive_bools = { level = "allow", priority = 1 } 78 | struct_field_names = { level = "allow", priority = 1 } 79 | too_many_arguments = { level = "allow", priority = 1 } 80 | too_many_lines = { level = "allow", priority = 1 } 81 | type_complexity = { level = "allow", priority = 1 } 82 | unreadable_literal = { level = "allow", priority = 1 } 83 | 84 | [profile.dev] 85 | codegen-units = 1 86 | lto = true 87 | opt-level = "s" 88 | panic = "abort" 89 | 90 | [profile.release] 91 | codegen-units = 1 92 | lto = true 93 | # TODO: Hang with -C opt-level=3 as of LLVM 21 94 | opt-level = "s" 95 | panic = "abort" 96 | -------------------------------------------------------------------------------- /src/gen/utils.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | // This file is @generated by target_spec.sh. 3 | // It is not intended for manual editing. 4 | 5 | #![allow(dead_code, unused_macros)] 6 | 7 | // On AArch64, the base register of memory-related instructions must be 64-bit. 8 | // Passing a 32-bit value to `in(reg)` on AArch64 results in the upper bits 9 | // having an undefined value, but to work correctly with ILP32 ABI, the upper 10 | // bits must be zero, which is handled here by casting to u64. Another way to 11 | // handle this is to pass it as a pointer and clear the upper bits inside asm, 12 | // but it is easier to overlook than cast, which can catch overlooks by 13 | // asm_sub_register lint. 14 | // See also https://github.com/ARM-software/abi-aa/blob/2025Q1/aapcs64/aapcs64.rst#pointers 15 | // 16 | // Except for x86_64, which can use 32-bit registers in the destination operand 17 | // (on x86_64, we use the ptr_modifier macro to handle this), we need to do the 18 | // same for ILP32 ABI on other 64-bit architectures. (At least, as far as I can 19 | // see from the assembly generated by LLVM, this is also required for MIPS64 N32 20 | // ABI. I don't know about the RISC-V RV64ILP32* ABI, but in any case, this 21 | // should be a safe default for such ABIs). 22 | // 23 | // Known architectures that have such ABI are x86_64 (X32), AArch64 (ILP32), 24 | // mips64 (N32), and riscv64 (RV64ILP32*). (As of 2025-01-23, only the former 25 | // two are supported by rustc.) However, we list all known 64-bit architectures 26 | // because similar ABIs may exist or future added for other architectures. 27 | #[cfg(all( 28 | target_pointer_width = "32", 29 | any( 30 | target_arch = "aarch64", 31 | target_arch = "amdgpu", 32 | target_arch = "arm64ec", 33 | target_arch = "bpf", 34 | target_arch = "loongarch64", 35 | target_arch = "mips64", 36 | target_arch = "mips64r6", 37 | target_arch = "nvptx64", 38 | target_arch = "powerpc64", 39 | target_arch = "riscv64", 40 | target_arch = "s390x", 41 | target_arch = "sparc64", 42 | target_arch = "wasm64", 43 | target_arch = "x86_64", 44 | ), 45 | ))] 46 | #[macro_use] 47 | mod imp { 48 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 49 | macro_rules! ptr_reg { 50 | ($ptr:ident) => {{ 51 | let _: *const _ = $ptr; // ensure $ptr is a pointer (*mut _ or *const _) 52 | #[allow(clippy::ptr_as_ptr)] 53 | { 54 | // If we cast to u64 here, the provenance will be lost, 55 | // so we convert to MaybeUninit via zero extend helper. 56 | crate::utils::zero_extend64::ptr($ptr as *mut ()) 57 | } 58 | }}; 59 | } 60 | pub(crate) type RegSize = u64; 61 | } 62 | #[cfg(not(all( 63 | target_pointer_width = "32", 64 | any( 65 | target_arch = "aarch64", 66 | target_arch = "amdgpu", 67 | target_arch = "arm64ec", 68 | target_arch = "bpf", 69 | target_arch = "loongarch64", 70 | target_arch = "mips64", 71 | target_arch = "mips64r6", 72 | target_arch = "nvptx64", 73 | target_arch = "powerpc64", 74 | target_arch = "riscv64", 75 | target_arch = "s390x", 76 | target_arch = "sparc64", 77 | target_arch = "wasm64", 78 | target_arch = "x86_64", 79 | ), 80 | )))] 81 | #[macro_use] 82 | mod imp { 83 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 84 | macro_rules! ptr_reg { 85 | ($ptr:ident) => {{ 86 | let _: *const _ = $ptr; // ensure $ptr is a pointer (*mut _ or *const _) 87 | $ptr // cast is unnecessary here. 88 | }}; 89 | } 90 | #[cfg(target_pointer_width = "16")] 91 | pub(crate) type RegSize = u16; 92 | #[cfg(target_pointer_width = "32")] 93 | pub(crate) type RegSize = u32; 94 | #[cfg(target_pointer_width = "64")] 95 | pub(crate) type RegSize = u64; 96 | #[cfg(target_pointer_width = "128")] 97 | pub(crate) type RegSize = u128; 98 | } 99 | pub(crate) use self::imp::RegSize; 100 | -------------------------------------------------------------------------------- /tools/runner.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-License-Identifier: Apache-2.0 OR MIT 3 | set -CeEuo pipefail 4 | IFS=$'\n\t' 5 | 6 | bail() { 7 | printf >&2 'error: %s\n' "$*" 8 | exit 1 9 | } 10 | 11 | if [[ $# -ne 3 ]]; then 12 | cat < 15 | EOF 16 | exit 1 17 | fi 18 | runner="$1" 19 | target="$2" 20 | bin="$3" 21 | stdout=".test-${target}.stdout" 22 | 23 | no_exit=1 24 | case "${target}" in 25 | avr*) 26 | case "${runner}" in 27 | simavr) 28 | no_exit='' 29 | case "${target}" in 30 | avr-unknown-gnu-atmega2560) mcu="atmega2560" ;; 31 | *) bail "unrecognized target '${target}'" ;; 32 | esac 33 | start_simulator() { 34 | "${SIMAVR:-simavr}" --mcu "${mcu}" "${bin}" &>>"${stdout}" & 35 | } 36 | ;; 37 | qemu-system) 38 | case "${target}" in 39 | avr-unknown-gnu-atmega2560) machine=mega2560 ;; 40 | *) bail "unrecognized target '${target}'" ;; 41 | esac 42 | start_simulator() { 43 | "${QEMU_SYSTEM_AVR:-qemu-system-avr}" -M "${machine}" -display none -serial stdio -bios "${bin}" &>>"${stdout}" & 44 | } 45 | ;; 46 | *) bail "unrecognized runner '${runner}'" ;; 47 | esac 48 | ;; 49 | msp430*) 50 | case "${runner}" in 51 | mspdebug) 52 | start_simulator() { 53 | "${MSPDEBUG:-mspdebug}" -n sim &>>"${stdout}" <>"${stdout}" & 69 | } 70 | ;; 71 | qemu-system) 72 | case "${target}" in 73 | sparc-unknown-none-elf) machine=leon3_generic ;; 74 | *) bail "unrecognized target '${target}'" ;; 75 | esac 76 | start_simulator() { 77 | "${QEMU_SYSTEM_SPARC:-qemu-system-sparc}" -M "${machine}" -display none -serial stdio -kernel "${bin}" &>>"${stdout}" & 78 | } 79 | ;; 80 | *) bail "unrecognized runner '${runner}'" ;; 81 | esac 82 | ;; 83 | xtensa*) 84 | case "${runner}" in 85 | wokwi-server) 86 | case "${target}" in 87 | xtensa-esp32-none-elf) chip=esp32 ;; 88 | xtensa-esp32s2-none-elf) chip=esp32s2 ;; 89 | # Note: https://github.com/MabezDev/wokwi-server/pull/16 is not yet released 90 | xtensa-esp32s3-none-elf) chip=esp32s3 ;; 91 | *) bail "unrecognized target '${target}'" ;; 92 | esac 93 | start_simulator() { 94 | "${WOKWI_SERVER:-wokwi-server}" --chip "${chip}" "${bin}" &>>"${stdout}" & 95 | } 96 | ;; 97 | *) bail "unrecognized runner '${runner}'" ;; 98 | esac 99 | ;; 100 | *) bail "unrecognized target '${target}'" ;; 101 | esac 102 | 103 | simulator_pid='' 104 | tail_pid='' 105 | code=1 106 | cleanup() { 107 | [[ -z "${simulator_pid}" ]] || kill -- "${simulator_pid}" || true 108 | [[ -z "${tail_pid}" ]] || kill -- "${tail_pid}" || true 109 | rm -f -- ./"${stdout}" 110 | exit "${code}" 111 | } 112 | trap -- 'printf >&2 "%s\n" "${0##*/}: trapped SIGINT"; cleanup' SIGINT 113 | 114 | rm -f -- ./"${stdout}" 115 | touch -- ./"${stdout}" 116 | tail -s0 -f "${stdout}" & 117 | tail_pid=$! 118 | 119 | start_simulator 120 | simulator_pid=$! 121 | 122 | if [[ -n "${no_exit}" ]]; then 123 | # Inspired by mgba-test-runner. 124 | # If there is no way to exit the simulator from the program, 125 | # check output of the simulator on a regular basis. 126 | while true; do 127 | if grep -Fq 'panicked' "${stdout}"; then 128 | code=101 129 | break 130 | elif grep -Fq 'Tests finished successfully' "${stdout}"; then 131 | code=0 132 | break 133 | fi 134 | sleep 0.1 135 | done 136 | else 137 | # If there is no way to exit the simulator with non-zero from the program, 138 | # check output of the simulator after the simulator finished. 139 | wait "${simulator_pid}" 140 | simulator_pid='' # simulator finished 141 | if grep -Fq 'panicked' "${stdout}"; then 142 | code=101 143 | elif grep -Fq 'Tests finished successfully' "${stdout}"; then 144 | code=0 145 | fi 146 | fi 147 | 148 | cleanup 149 | -------------------------------------------------------------------------------- /tools/target_spec.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-License-Identifier: Apache-2.0 OR MIT 3 | # shellcheck disable=SC2207 4 | set -CeEuo pipefail 5 | IFS=$'\n\t' 6 | trap -- 's=$?; printf >&2 "%s\n" "${0##*/}:${LINENO}: \`${BASH_COMMAND}\` exit with ${s}"; exit ${s}' ERR 7 | cd -- "$(dirname -- "$0")"/.. 8 | 9 | # Generates code based on target-spec. 10 | # 11 | # USAGE: 12 | # ./tools/target_spec.sh 13 | # 14 | # This script is intended to be called by gen.sh, but can be called separately. 15 | 16 | bail() { 17 | printf >&2 'error: %s\n' "$*" 18 | exit 1 19 | } 20 | 21 | utils_file=src/gen/utils.rs 22 | build_file=src/gen/build.rs 23 | mkdir -p -- "$(dirname -- "${utils_file}")" 24 | mkdir -p -- "$(dirname -- "${build_file}")" 25 | 26 | known_64_bit_arch=($(rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[].value | if ."target-pointer-width" == 64 then .arch else empty end' | LC_ALL=C sort -u)) 27 | 28 | cat >|"${utils_file}" < {{ 66 | let _: *const _ = \$ptr; // ensure \$ptr is a pointer (*mut _ or *const _) 67 | #[allow(clippy::ptr_as_ptr)] 68 | { 69 | // If we cast to u64 here, the provenance will be lost, 70 | // so we convert to MaybeUninit via zero extend helper. 71 | crate::utils::zero_extend64::ptr(\$ptr as *mut ()) 72 | } 73 | }}; 74 | } 75 | pub(crate) type RegSize = u64; 76 | } 77 | #[cfg(not(all( 78 | target_pointer_width = "32", 79 | any( 80 | $(sed -E 's/^/ target_arch = "/g; s/$/",/g' <<<"${known_64_bit_arch[*]}") 81 | ), 82 | )))] 83 | #[macro_use] 84 | mod imp { 85 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 86 | macro_rules! ptr_reg { 87 | (\$ptr:ident) => {{ 88 | let _: *const _ = \$ptr; // ensure \$ptr is a pointer (*mut _ or *const _) 89 | \$ptr // cast is unnecessary here. 90 | }}; 91 | } 92 | EOF 93 | for size in '16' '32' '64' '128'; do 94 | cat >>"${utils_file}" <>"${utils_file}" <|"${build_file}" < 10 | 11 | Atomic operations on potentially uninitialized integers. 12 | 13 | ## Motivation 14 | 15 | Copying types containing uninitialized bytes (e.g., padding), via the standard library's atomic types 16 | is [undefined behavior because the copy goes through integers][undefined-behavior]. 17 | 18 | This crate provides a way to soundly perform such operations. 19 | 20 | ## Platform Support 21 | 22 | Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch, Arm64EC, s390x, MIPS, PowerPC, MSP430, AVR, SPARC, Hexagon, M68k, C-SKY, and Xtensa are supported. 23 | (You can use `cfg_{has,no}_*` macros to write code based on whether or not which size of primitives is available.) 24 | 25 | | target_arch | primitives | load/store | swap/CAS | 26 | | ------------------------------------------- | --------------------------------------------------- |:----------:|:--------:| 27 | | x86 | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | 28 | | x86_64 | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | 29 | | x86_64 (+cmpxchg16b) \[2] | i128,u128 | ✓ | ✓ | 30 | | arm (v6+ or Linux/Android) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | 31 | | arm (except for M-profile) \[3] | i64,u64 | ✓ | ✓ | 32 | | aarch64 | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64,i128,u128 | ✓ | ✓ | 33 | | riscv32 | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | 34 | | riscv32 (+zacas) \[4] | i64,u64 | ✓ | ✓ | 35 | | riscv64 | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓\[1] | 36 | | riscv64 (+zacas) \[4] | i128,u128 | ✓ | ✓ | 37 | | loongarch64 | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | 38 | | loongarch32 \[8] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓ | 39 | | arm64ec \[7] | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64,i128,u128 | ✓ | ✓ | 40 | | s390x \[7] | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64,i128,u128 | ✓ | ✓ | 41 | | mips / mips32r6 \[9] | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓ | 42 | | mips64 / mips64r6 \[9] | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | 43 | | powerpc \[9] | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓ | 44 | | powerpc64 \[9] | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | 45 | | powerpc64 (+quadword-atomics) \[5] \[9] | i128,u128 | ✓ | ✓ | 46 | | msp430 \[9] (experimental) | isize,usize,i8,u8,i16,u16 | ✓ | ✓ | 47 | | avr \[9] (experimental) | isize,usize,i8,u8,i16,u16 | ✓ | ✓ | 48 | | sparc \[6] \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓ | 49 | | sparc64 \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | 50 | | hexagon \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | 51 | | m68k \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | 52 | | m68k (+isa-68020) \[9] \[10] (experimental) | i64,u64 | ✓ | ✓ | 53 | | csky \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | 54 | | xtensa \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | 55 | 56 | \[1] Arm's atomic RMW operations are not available on Armv6-M (thumbv6m). RISC-V's atomic RMW operations are not available on targets without the A (or G which means IMAFD) or Zalrsc or Zacas extension, such as riscv32i, riscv32imc, etc. M68k's atomic RMW operations requires target-cpu M68020+ (enabled by default on Linux). C-SKY's atomic RMW operations requires target-cpu ck860\* or c860\* (enabled by default on the hard-float target). Xtensa's atomic RMW operations are not available on esp32s2.
57 | \[2] Requires `cmpxchg16b` target feature (enabled by default on Apple, Windows (except Windows 7), and Fuchsia targets).
58 | \[3] Armv6+ or Linux/Android, except for M-profile architecture such as thumbv6m, thumbv7m, etc.
59 | \[4] Requires `zacas` target feature.
60 | \[5] Requires `quadword-atomics` target feature (enabled by default on powerpc64le).
61 | \[6] Requires `v9` or `leoncasa` target feature (enabled by default on Linux).
62 | \[7] Requires Rust 1.84+.
63 | \[8] Requires Rust 1.91+.
64 | \[9] Requires nightly due to `#![feature(asm_experimental_arch)]`.
65 | \[10] Requires target-cpu M68020+ (enabled by default on Linux).
66 | 67 | See also [Atomic operation overview by architecture](https://github.com/taiki-e/atomic-maybe-uninit/blob/HEAD/src/arch/README.md) 68 | for more information about atomic operations in these architectures. 69 | 70 | Feel free to submit an issue if your target is not supported yet. 71 | 72 | ## Related Projects 73 | 74 | - [portable-atomic]: Portable atomic types including support for 128-bit atomics, atomic float, etc. 75 | - [atomic-memcpy]: Byte-wise atomic memcpy. 76 | 77 | [atomic-memcpy]: https://github.com/taiki-e/atomic-memcpy 78 | [portable-atomic]: https://github.com/taiki-e/portable-atomic 79 | [undefined-behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html 80 | 81 | 82 | 83 | ## License 84 | 85 | Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or 86 | [MIT license](LICENSE-MIT) at your option. 87 | 88 | Unless you explicitly state otherwise, any contribution intentionally submitted 89 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 90 | be dual licensed as above, without any additional terms or conditions. 91 | -------------------------------------------------------------------------------- /src/arch/msp430.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | /* 4 | MSP430 5 | 6 | See "Atomic operation overview by architecture" for atomic operations in this architecture: 7 | https://github.com/taiki-e/atomic-maybe-uninit/blob/HEAD/src/arch/README.md#msp430 8 | 9 | Refs: 10 | - MSP430x5xx and MSP430x6xx Family User's Guide, Rev. Q 11 | https://www.ti.com/lit/ug/slau208q/slau208q.pdf 12 | - portable-atomic 13 | https://github.com/taiki-e/portable-atomic 14 | 15 | Generated asm: 16 | - msp430 https://godbolt.org/z/vsrq45h5q 17 | */ 18 | 19 | delegate_size!(delegate_all); 20 | 21 | use core::{ 22 | arch::asm, 23 | mem::{self, MaybeUninit}, 24 | sync::atomic::Ordering, 25 | }; 26 | 27 | use crate::raw::{AtomicCompareExchange, AtomicLoad, AtomicStore, AtomicSwap}; 28 | 29 | macro_rules! atomic { 30 | ($ty:ident, $suffix:tt) => { 31 | delegate_signed!(delegate_all, $ty); 32 | impl AtomicLoad for $ty { 33 | #[inline] 34 | unsafe fn atomic_load( 35 | src: *const MaybeUninit, 36 | _order: Ordering, 37 | ) -> MaybeUninit { 38 | let out: MaybeUninit; 39 | 40 | // SAFETY: the caller must uphold the safety contract. 41 | unsafe { 42 | asm!( 43 | concat!("mov.", $suffix, " @{src}, {out}"), // atomic { out = *src } 44 | src = in(reg) src, 45 | out = lateout(reg) out, 46 | options(nostack, preserves_flags), 47 | ); 48 | } 49 | out 50 | } 51 | } 52 | impl AtomicStore for $ty { 53 | #[inline] 54 | unsafe fn atomic_store( 55 | dst: *mut MaybeUninit, 56 | val: MaybeUninit, 57 | _order: Ordering, 58 | ) { 59 | // SAFETY: the caller must uphold the safety contract. 60 | unsafe { 61 | asm!( 62 | concat!("mov.", $suffix, " {val}, 0({dst})"), // atomic { *dst = val } 63 | dst = in(reg) dst, 64 | val = in(reg) val, 65 | options(nostack, preserves_flags), 66 | ); 67 | } 68 | } 69 | } 70 | impl AtomicSwap for $ty { 71 | #[inline] 72 | unsafe fn atomic_swap( 73 | dst: *mut MaybeUninit, 74 | val: MaybeUninit, 75 | _order: Ordering, 76 | ) -> MaybeUninit { 77 | let out: MaybeUninit; 78 | 79 | // SAFETY: the caller must guarantee that pointer is valid and properly aligned. 80 | // On single-core systems, disabling interrupts is enough to prevent data race. 81 | // See "NOTE: Enable and Disable Interrupt" of User's Guide for NOP: https://www.ti.com/lit/ug/slau208q/slau208q.pdf#page=60 82 | unsafe { 83 | asm!( 84 | "mov r2, {sr}", // sr = SR 85 | "dint {{ nop", // atomic { SR.GIE = 0 86 | concat!("mov.", $suffix, " @{dst}, {out}"), // out = *dst 87 | concat!("mov.", $suffix, " {val}, 0({dst})"), // *dst = val 88 | "nop {{ mov {sr}, r2 {{ nop", // SR = sr } 89 | dst = in(reg) dst, 90 | val = in(reg) val, 91 | out = out(reg) out, 92 | sr = out(reg) _, 93 | options(nostack, preserves_flags), 94 | ); 95 | } 96 | out 97 | } 98 | } 99 | impl AtomicCompareExchange for $ty { 100 | #[inline] 101 | unsafe fn atomic_compare_exchange( 102 | dst: *mut MaybeUninit, 103 | old: MaybeUninit, 104 | new: MaybeUninit, 105 | _success: Ordering, 106 | _failure: Ordering, 107 | ) -> (MaybeUninit, bool) { 108 | let out: MaybeUninit; 109 | let mut r: $ty; 110 | 111 | // SAFETY: the caller must guarantee that pointer is valid and properly aligned. 112 | // On single-core systems, disabling interrupts is enough to prevent data race. 113 | // See "NOTE: Enable and Disable Interrupt" of User's Guide for NOP: https://www.ti.com/lit/ug/slau208q/slau208q.pdf#page=60 114 | unsafe { 115 | asm!( 116 | "mov r2, {sr}", // sr = SR 117 | "dint {{ nop", // atomic { SR.GIE = 0 118 | concat!("mov.", $suffix, " @{dst}, {out}"), // out = *dst 119 | concat!("xor.", $suffix, " {out}, {old}"), // old ^= out; if old == 0 { SR.Z = 1 } else { SR.Z = 0 } 120 | "jne 2f", // if SR.Z == 0 { jump 'cmp-fail } 121 | concat!("mov.", $suffix, " {new}, 0({dst})"), // *dst = new 122 | "2:", // 'cmp-fail: 123 | "nop {{ mov {sr}, r2 {{ nop", // SR = sr } 124 | dst = in(reg) dst, 125 | old = inout(reg) old => r, 126 | new = in(reg) new, 127 | out = out(reg) out, 128 | sr = out(reg) _, 129 | // XOR modifies the status register, but `preserves_flags` is okay since SREG is restored at the end. 130 | options(nostack, preserves_flags), 131 | ); 132 | (out, r == 0) 133 | } 134 | } 135 | } 136 | }; 137 | } 138 | 139 | atomic!(u8, "b"); 140 | atomic!(u16, "w"); 141 | 142 | // ----------------------------------------------------------------------------- 143 | // cfg macros 144 | 145 | #[macro_export] 146 | macro_rules! cfg_has_atomic_8 { 147 | ($($tt:tt)*) => { $($tt)* }; 148 | } 149 | #[macro_export] 150 | macro_rules! cfg_no_atomic_8 { 151 | ($($tt:tt)*) => {}; 152 | } 153 | #[macro_export] 154 | macro_rules! cfg_has_atomic_16 { 155 | ($($tt:tt)*) => { $($tt)* }; 156 | } 157 | #[macro_export] 158 | macro_rules! cfg_no_atomic_16 { 159 | ($($tt:tt)*) => {}; 160 | } 161 | #[macro_export] 162 | macro_rules! cfg_has_atomic_32 { 163 | ($($tt:tt)*) => {}; 164 | } 165 | #[macro_export] 166 | macro_rules! cfg_no_atomic_32 { 167 | ($($tt:tt)*) => { $($tt)* }; 168 | } 169 | #[macro_export] 170 | macro_rules! cfg_has_atomic_64 { 171 | ($($tt:tt)*) => {}; 172 | } 173 | #[macro_export] 174 | macro_rules! cfg_no_atomic_64 { 175 | ($($tt:tt)*) => { $($tt)* }; 176 | } 177 | #[macro_export] 178 | macro_rules! cfg_has_atomic_128 { 179 | ($($tt:tt)*) => {}; 180 | } 181 | #[macro_export] 182 | macro_rules! cfg_no_atomic_128 { 183 | ($($tt:tt)*) => { $($tt)* }; 184 | } 185 | #[macro_export] 186 | macro_rules! cfg_has_atomic_cas { 187 | ($($tt:tt)*) => { $($tt)* }; 188 | } 189 | #[macro_export] 190 | macro_rules! cfg_no_atomic_cas { 191 | ($($tt:tt)*) => {}; 192 | } 193 | -------------------------------------------------------------------------------- /tests/include/basic.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | macro_rules! __test_atomic { 4 | ($ty:ident) => { 5 | load_store(); 6 | fn load_store() { 7 | static VAR: AtomicMaybeUninit<$ty> = 8 | AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(10)); 9 | for (load_order, store_order) in LOAD_ORDERINGS.into_iter().zip(STORE_ORDERINGS) { 10 | unsafe { 11 | assert_eq!(VAR.load(load_order).assume_init(), 10); 12 | VAR.store(MaybeUninit::new(5), store_order); 13 | assert_eq!(VAR.load(load_order).assume_init(), 5); 14 | VAR.store(MaybeUninit::uninit(), store_order); 15 | let _v = VAR.load(load_order); 16 | VAR.store(MaybeUninit::new(10), store_order); 17 | 18 | let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(1)); 19 | assert_eq!(a.load(load_order).assume_init(), 1); 20 | a.store(MaybeUninit::new($ty::MIN), store_order); 21 | assert_eq!(a.load(load_order).assume_init(), $ty::MIN); 22 | a.store(MaybeUninit::new($ty::MAX), store_order); 23 | assert_eq!(a.load(load_order).assume_init(), $ty::MAX); 24 | let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::uninit()); 25 | let _v = a.load(load_order); 26 | a.store(MaybeUninit::new(2), store_order); 27 | assert_eq!(a.load(load_order).assume_init(), 2); 28 | a.store(MaybeUninit::uninit(), store_order); 29 | let _v = a.load(load_order); 30 | } 31 | } 32 | } 33 | cfg_has_atomic_cas! { 34 | swap(); 35 | fn swap() { 36 | for order in SWAP_ORDERINGS { 37 | unsafe { 38 | let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(5)); 39 | assert_eq!(a.swap(MaybeUninit::new($ty::MIN), order).assume_init(), 5); 40 | assert_eq!(a.swap(MaybeUninit::new($ty::MAX), order).assume_init(), $ty::MIN); 41 | assert_eq!(a.swap(MaybeUninit::new(10), order).assume_init(), $ty::MAX); 42 | assert_eq!(a.swap(MaybeUninit::uninit(), order).assume_init(), 10); 43 | let _v = a.swap(MaybeUninit::new(15), order); 44 | let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::uninit()); 45 | let _v = a.swap(MaybeUninit::new(10), order); 46 | assert_eq!(a.swap(MaybeUninit::uninit(), order).assume_init(), 10); 47 | } 48 | } 49 | } 50 | compare_exchange(); 51 | fn compare_exchange() { 52 | for (success, failure) in COMPARE_EXCHANGE_ORDERINGS { 53 | unsafe { 54 | for (x, y) in [(5, 10), ($ty::MAX, $ty::MIN), ($ty::MIN, $ty::MAX)] { 55 | let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(x)); 56 | assert_eq!( 57 | a.compare_exchange( 58 | MaybeUninit::new(x), 59 | MaybeUninit::new(y), 60 | success, 61 | failure 62 | ) 63 | .unwrap() 64 | .assume_init(), 65 | x 66 | ); 67 | assert_eq!(a.load(Ordering::Relaxed).assume_init(), y); 68 | assert_eq!( 69 | a.compare_exchange( 70 | MaybeUninit::new(6), 71 | MaybeUninit::new(12), 72 | success, 73 | failure 74 | ) 75 | .unwrap_err() 76 | .assume_init(), 77 | y 78 | ); 79 | assert_eq!(a.load(Ordering::Relaxed).assume_init(), y); 80 | } 81 | } 82 | } 83 | } 84 | compare_exchange_weak(); 85 | fn compare_exchange_weak() { 86 | for (success, failure) in COMPARE_EXCHANGE_ORDERINGS { 87 | unsafe { 88 | for x in [4, $ty::MAX, $ty::MIN] { 89 | let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(x)); 90 | assert_eq!( 91 | a.compare_exchange_weak( 92 | MaybeUninit::new(6), 93 | MaybeUninit::new(8), 94 | success, 95 | failure 96 | ) 97 | .unwrap_err() 98 | .assume_init(), 99 | x 100 | ); 101 | let mut old = a.load(Ordering::Relaxed); 102 | loop { 103 | let new = MaybeUninit::new(old.assume_init().wrapping_add(2)); 104 | match a.compare_exchange_weak(old, new, success, failure) { 105 | Ok(_) => break, 106 | Err(x) => old = x, 107 | } 108 | } 109 | assert_eq!(a.load(Ordering::Relaxed).assume_init(), x.wrapping_add(2)); 110 | } 111 | } 112 | } 113 | } 114 | fetch_update(); 115 | fn fetch_update() { 116 | for (success, failure) in COMPARE_EXCHANGE_ORDERINGS { 117 | unsafe { 118 | let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(7)); 119 | assert_eq!( 120 | a.fetch_update(success, failure, |_| None).unwrap_err().assume_init(), 121 | 7 122 | ); 123 | assert_eq!( 124 | a.fetch_update(success, failure, |x| Some(MaybeUninit::new( 125 | x.assume_init() + 1 126 | ))) 127 | .unwrap() 128 | .assume_init(), 129 | 7 130 | ); 131 | assert_eq!( 132 | a.fetch_update(success, failure, |x| Some(MaybeUninit::new( 133 | x.assume_init() + 1 134 | ))) 135 | .unwrap() 136 | .assume_init(), 137 | 8 138 | ); 139 | assert_eq!(a.load(Ordering::Relaxed).assume_init(), 9); 140 | } 141 | } 142 | } 143 | } 144 | }; 145 | } 146 | 147 | const LOAD_ORDERINGS: [Ordering; 3] = [Ordering::Relaxed, Ordering::Acquire, Ordering::SeqCst]; 148 | const STORE_ORDERINGS: [Ordering; 3] = [Ordering::Relaxed, Ordering::Release, Ordering::SeqCst]; 149 | cfg_has_atomic_cas! { 150 | const SWAP_ORDERINGS: [Ordering; 5] = 151 | [Ordering::Relaxed, Ordering::Release, Ordering::Acquire, Ordering::AcqRel, Ordering::SeqCst]; 152 | const COMPARE_EXCHANGE_ORDERINGS: [(Ordering, Ordering); 15] = [ 153 | (Ordering::Relaxed, Ordering::Relaxed), 154 | (Ordering::Relaxed, Ordering::Acquire), 155 | (Ordering::Relaxed, Ordering::SeqCst), 156 | (Ordering::Acquire, Ordering::Relaxed), 157 | (Ordering::Acquire, Ordering::Acquire), 158 | (Ordering::Acquire, Ordering::SeqCst), 159 | (Ordering::Release, Ordering::Relaxed), 160 | (Ordering::Release, Ordering::Acquire), 161 | (Ordering::Release, Ordering::SeqCst), 162 | (Ordering::AcqRel, Ordering::Relaxed), 163 | (Ordering::AcqRel, Ordering::Acquire), 164 | (Ordering::AcqRel, Ordering::SeqCst), 165 | (Ordering::SeqCst, Ordering::Relaxed), 166 | (Ordering::SeqCst, Ordering::Acquire), 167 | (Ordering::SeqCst, Ordering::SeqCst), 168 | ]; 169 | } 170 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "atomic-maybe-uninit" 3 | version = "0.3.13" #publish:version 4 | edition = "2021" 5 | # NB: Sync with rust-version field in other Cargo.toml files and msrv badge in README.md 6 | rust-version = "1.74" # For MaybeUninit registers in asm!: https://github.com/rust-lang/rust/pull/114790 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/taiki-e/atomic-maybe-uninit" 9 | keywords = ["atomic"] 10 | categories = ["concurrency", "embedded", "hardware-support", "no-std", "no-std::no-alloc"] 11 | exclude = ["/.*", "/tools", "/target-specs"] 12 | description = """ 13 | Atomic operations on potentially uninitialized integers. 14 | """ 15 | 16 | [package.metadata.docs.rs] 17 | targets = [ 18 | "aarch64-unknown-linux-gnu", 19 | "armv7-unknown-linux-gnueabihf", 20 | "i686-unknown-linux-gnu", 21 | "riscv64gc-unknown-linux-gnu", 22 | "x86_64-unknown-linux-gnu", 23 | ] 24 | 25 | [package.metadata.cargo_check_external_types] 26 | # The following are external types that are allowed to be exposed in our public API. 27 | allowed_external_types = [ 28 | ] 29 | 30 | [lib] 31 | doc-scrape-examples = false 32 | 33 | [dev-dependencies] 34 | fastrand = "2" 35 | paste = "1" 36 | quickcheck = { version = "1", default-features = false, git = "https://github.com/taiki-e/quickcheck.git", rev = "f0c7237" } # https://github.com/BurntSushi/quickcheck/pull/304 + https://github.com/BurntSushi/quickcheck/pull/282 + https://github.com/BurntSushi/quickcheck/pull/296 + rand -> fastrand + lower MSRV 37 | 38 | [target.'cfg(valgrind)'.dev-dependencies] 39 | crabgrind = "0.1" 40 | 41 | [lints] 42 | workspace = true 43 | 44 | [workspace] 45 | resolver = "2" 46 | 47 | # This table is shared by projects under github.com/taiki-e. 48 | # Expect for unexpected_cfgs.check-cfg, it is not intended for manual editing. 49 | [workspace.lints.rust] 50 | deprecated_safe = "warn" 51 | improper_ctypes = "warn" 52 | improper_ctypes_definitions = "warn" 53 | non_ascii_idents = "warn" 54 | rust_2018_idioms = "warn" 55 | single_use_lifetimes = "warn" 56 | unexpected_cfgs = { level = "warn", check-cfg = [ 57 | 'cfg(target_arch,values("xtensa"))', # 1.81+ https://github.com/rust-lang/rust/pull/125141 58 | 'cfg(target_arch,values("amdgpu"))', # 1.86+ https://github.com/rust-lang/rust/pull/134740 59 | 'cfg(target_arch,values("loongarch32"))', # 1.89+ https://github.com/rust-lang/rust/pull/142053 60 | 'cfg(target_feature,values("lse2","lse128","rcpc3"))', # 1.82+ https://github.com/rust-lang/rust/pull/128192 61 | 'cfg(target_feature,values("partword-atomics","quadword-atomics"))', # 1.83+ https://github.com/rust-lang/rust/pull/130873 62 | 'cfg(target_feature,values("zaamo","zabha","zalrsc"))', # 1.83+ https://github.com/rust-lang/rust/pull/130877 63 | 'cfg(target_feature,values("leoncasa","v9"))', # 1.84+ https://github.com/rust-lang/rust/pull/132552 64 | 'cfg(target_feature,values("x87"))', # 1.85+ https://github.com/rust-lang/rust/pull/133099 65 | 'cfg(target_feature,values("isa-68020"))', # 1.85+ https://github.com/rust-lang/rust/pull/134329 66 | 'cfg(target_feature,values("zacas"))', # 1.87+ https://github.com/rust-lang/rust/pull/137417 67 | 'cfg(target_feature,values("msync"))', # 1.87+ https://github.com/rust-lang/rust/pull/137860 68 | 'cfg(target_pointer_width,values("128"))', 69 | # Known custom cfgs, excluding those that may be set by build script. 70 | # Public APIs, considered unstable unless documented in readme. 71 | # arm: Use cp15_barrier instead of __kuser_memory_barrier on Armv6 Linux/Android. 72 | # Armv6 binaries compiled with this cfg may cause problems when run on Armv7+ chips: 73 | # https://github.com/rust-lang/rust/issues/60605 74 | 'cfg(atomic_maybe_uninit_use_cp15_barrier)', 75 | # These cfgs are auto-detected for known builtin targets and -C target-cpu 76 | # by the build script, but may need to be set manually, for example if the 77 | # CPU is specified in a custom target, since rustc does not have target 78 | # features to indicate them, and target name beginning with i386- may 79 | # actually be i686 (e.g., i386-apple-ios) 80 | # https://github.com/rust-lang/rust/blob/1.90.0/compiler/rustc_target/src/spec/base/apple/mod.rs#L68 81 | # x86: Assume CPU does not have CMPXCHG8B instruction. 82 | # This is a cfg for compatibility with 80486/80386, but note that 83 | # LLVM does not support those CPUs well: https://reviews.llvm.org/D18802 84 | # 'cfg(atomic_maybe_uninit_no_cmpxchg8b)', // check-cfg is already set by build script 85 | # x86: Assume CPU does not have CMPXCHG instruction. 86 | # This is a cfg for compatibility with 80386, but note that 87 | # LLVM does not support those CPUs well: https://reviews.llvm.org/D18802 88 | # When setting this cfg you also need to set atomic_maybe_uninit_no_cmpxchg8b. 89 | # 'cfg(atomic_maybe_uninit_no_cmpxchg)', // check-cfg is already set by build script 90 | # mips: Assume CPU does not have SYNC instruction. 91 | # This is a cfg for compatibility with MIPS I. 92 | # 'cfg(atomic_maybe_uninit_no_sync)', // check-cfg is already set by build script 93 | # Not public API. 94 | 'cfg(atomic_maybe_uninit_test_prefer_x87_over_sse,atomic_maybe_uninit_test_prefer_cmpxchg8b_over_x87,atomic_maybe_uninit_test_prefer_kuser_memory_barrier,atomic_maybe_uninit_test_prefer_kuser_cmpxchg,atomic_maybe_uninit_test_prefer_bx,atomic_maybe_uninit_test_prefer_zalrsc_over_zaamo,atomic_maybe_uninit_test_prefer_zacas_over_zalrsc_for_sub_word,atomic_maybe_uninit_test_prefer_st_ll_sc_over_amswap,qemu,valgrind)', 95 | ] } 96 | unnameable_types = "warn" 97 | unreachable_pub = "warn" 98 | unsafe_op_in_unsafe_fn = "warn" 99 | [workspace.lints.clippy] 100 | all = "warn" # Downgrade deny-by-default lints 101 | pedantic = "warn" 102 | as_ptr_cast_mut = "warn" 103 | as_underscore = "warn" 104 | default_union_representation = "warn" 105 | inline_asm_x86_att_syntax = "warn" 106 | trailing_empty_array = "warn" 107 | transmute_undefined_repr = "warn" 108 | undocumented_unsafe_blocks = "warn" 109 | unused_trait_names = "warn" 110 | # Suppress buggy or noisy clippy lints 111 | bool_assert_comparison = { level = "allow", priority = 1 } 112 | borrow_as_ptr = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/8286 113 | cast_lossless = { level = "allow", priority = 1 } # https://godbolt.org/z/Pv6vbGG6E 114 | declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665 115 | doc_markdown = { level = "allow", priority = 1 } 116 | float_cmp = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7725 117 | incompatible_msrv = { level = "allow", priority = 1 } # buggy: doesn't consider cfg, https://github.com/rust-lang/rust-clippy/issues/12280, https://github.com/rust-lang/rust-clippy/issues/12257#issuecomment-2093667187 118 | lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920 119 | manual_assert = { level = "allow", priority = 1 } 120 | manual_range_contains = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/6455#issuecomment-1225966395 121 | missing_errors_doc = { level = "allow", priority = 1 } 122 | module_name_repetitions = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+module_name_repetitions 123 | naive_bytecount = { level = "allow", priority = 1 } 124 | nonminimal_bool = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+nonminimal_bool 125 | range_plus_one = { level = "allow", priority = 1 } # buggy: https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+range_plus_one 126 | similar_names = { level = "allow", priority = 1 } 127 | single_match = { level = "allow", priority = 1 } 128 | single_match_else = { level = "allow", priority = 1 } 129 | struct_excessive_bools = { level = "allow", priority = 1 } 130 | struct_field_names = { level = "allow", priority = 1 } 131 | too_many_arguments = { level = "allow", priority = 1 } 132 | too_many_lines = { level = "allow", priority = 1 } 133 | type_complexity = { level = "allow", priority = 1 } 134 | unreadable_literal = { level = "allow", priority = 1 } 135 | 136 | [profile.release] 137 | debug = true 138 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /tools/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-License-Identifier: Apache-2.0 OR MIT 3 | set -CeEuo pipefail 4 | IFS=$'\n\t' 5 | trap -- 's=$?; printf >&2 "%s\n" "${0##*/}:${LINENO}: \`${BASH_COMMAND}\` exit with ${s}"; exit ${s}' ERR 6 | trap -- 'printf >&2 "%s\n" "${0##*/}: trapped SIGINT"; exit 1' SIGINT 7 | cd -- "$(dirname -- "$0")"/.. 8 | 9 | # USAGE: 10 | # ./tools/test.sh [+toolchain] [cargo_options]... 11 | # ./tools/test.sh [+toolchain] build|build-valgrind|valgrind [cargo_options]... 12 | 13 | x() { 14 | ( 15 | set -x 16 | "$@" 17 | ) 18 | } 19 | x_cargo() { 20 | if [[ -n "${RUSTFLAGS:-}" ]]; then 21 | printf '%s\n' "+ RUSTFLAGS='${RUSTFLAGS}' \\" 22 | fi 23 | if [[ -n "${RUSTDOCFLAGS:-}" ]]; then 24 | printf '%s\n' "+ RUSTDOCFLAGS='${RUSTDOCFLAGS}' \\" 25 | fi 26 | if [[ -n "${CARGO_PROFILE_RELEASE_CODEGEN_UNITS:-}" ]]; then 27 | printf '%s\n' "+ CARGO_PROFILE_RELEASE_CODEGEN_UNITS='${CARGO_PROFILE_RELEASE_CODEGEN_UNITS}' \\" 28 | fi 29 | if [[ -n "${CARGO_PROFILE_RELEASE_LTO:-}" ]]; then 30 | printf '%s\n' "+ CARGO_PROFILE_RELEASE_LTO='${CARGO_PROFILE_RELEASE_LTO}' \\" 31 | fi 32 | if [[ -n "${TS:-}" ]]; then 33 | x "${cargo}" ${pre_args[@]+"${pre_args[@]}"} "$@" 2>&1 | "${TS}" -i '%.s ' 34 | else 35 | x "${cargo}" ${pre_args[@]+"${pre_args[@]}"} "$@" 36 | fi 37 | printf '\n' 38 | } 39 | retry() { 40 | for i in {1..10}; do 41 | if "$@"; then 42 | return 0 43 | else 44 | sleep "${i}" 45 | fi 46 | done 47 | "$@" 48 | } 49 | bail() { 50 | printf >&2 'error: %s\n' "$*" 51 | exit 1 52 | } 53 | 54 | pre_args=() 55 | if [[ "${1:-}" == "+"* ]]; then 56 | pre_args+=("$1") 57 | shift 58 | fi 59 | cmd='test' 60 | case "${1:-}" in 61 | build | build-valgrind | valgrind) 62 | cmd="$1" 63 | shift 64 | ;; 65 | esac 66 | target='' 67 | build_std=() 68 | release=() 69 | tests=() 70 | cargo_options=() 71 | rest_cargo_options=() 72 | while [[ $# -gt 0 ]]; do 73 | case "$1" in 74 | --) 75 | rest_cargo_options=("$@") 76 | break 77 | ;; 78 | --target) 79 | if [[ -n "${target}" ]]; then 80 | bail "multiple --target option" 81 | fi 82 | shift 83 | target="$1" 84 | ;; 85 | --target=*) 86 | if [[ -n "${target}" ]]; then 87 | bail "multiple --target option" 88 | fi 89 | target="${1#--target=}" 90 | ;; 91 | --tests) tests=("$1") ;; 92 | -Z*) 93 | arg="$1" 94 | case "${arg}" in 95 | -Z) 96 | shift 97 | arg="$1" 98 | ;; 99 | -Z*) arg="${arg#-Z}" ;; 100 | esac 101 | case "${arg}" in 102 | build-std | build-std=*) 103 | if [[ ${#build_std[@]} -gt 0 ]]; then 104 | bail "multiple -Z build-std option" 105 | fi 106 | build_std=(-Z "${arg}") 107 | ;; 108 | *) cargo_options+=(-Z "${arg}") ;; 109 | esac 110 | ;; 111 | -r | --release) release=(--release) ;; 112 | *) cargo_options+=("$1") ;; 113 | esac 114 | shift 115 | done 116 | 117 | cargo="${cargo:-cargo}" 118 | if type -P rustup >/dev/null; then 119 | rustup_target_list=$(rustup ${pre_args[@]+"${pre_args[@]}"} target list | cut -d' ' -f1) 120 | fi 121 | rustc_target_list=$(rustc ${pre_args[@]+"${pre_args[@]}"} --print target-list) 122 | rustc_version=$(rustc ${pre_args[@]+"${pre_args[@]}"} -vV | grep -E '^release:' | cut -d' ' -f2) 123 | rustc_minor_version="${rustc_version#*.}" 124 | rustc_minor_version="${rustc_minor_version%%.*}" 125 | host=$(rustc ${pre_args[@]+"${pre_args[@]}"} -vV | grep -E '^host:' | cut -d' ' -f2) 126 | workspace_dir=$(pwd) 127 | target_dir="${workspace_dir}/target" 128 | nightly='' 129 | if [[ "${rustc_version}" =~ nightly|dev ]]; then 130 | nightly=1 131 | if type -P rustup >/dev/null; then 132 | retry rustup ${pre_args[@]+"${pre_args[@]}"} component add rust-src &>/dev/null 133 | fi 134 | fi 135 | export RUST_TEST_THREADS=1 136 | export ATOMIC_MAYBE_UNINIT_DENY_WARNINGS=1 137 | if [[ -n "${CI:-}" ]]; then 138 | if type -P ts >/dev/null; then 139 | TS=ts 140 | elif [[ -e C:/msys64/usr/bin/ts ]]; then 141 | TS=C:/msys64/usr/bin/ts 142 | fi 143 | fi 144 | 145 | args=() 146 | if [[ -z "${target}" ]] && [[ ${#build_std[@]} -gt 0 ]]; then 147 | target="${target:-"${host}"}" 148 | fi 149 | if [[ -n "${target}" ]]; then 150 | if ! grep -Eq "^${target}$" <<<"${rustc_target_list}" || [[ -f "target-specs/${target}.json" ]]; then 151 | if [[ ! -f "target-specs/${target}.json" ]]; then 152 | bail "target '${target}' not available on ${rustc_version}" 153 | fi 154 | target_flags=(--target "${workspace_dir}/target-specs/${target}.json") 155 | else 156 | target_flags=(--target "${target}") 157 | fi 158 | args+=("${target_flags[@]}") 159 | if type -P rustup >/dev/null; then 160 | if grep -Eq "^${target}$" <<<"${rustup_target_list}"; then 161 | retry rustup ${pre_args[@]+"${pre_args[@]}"} target add "${target}" &>/dev/null 162 | elif [[ -n "${nightly}" ]]; then 163 | if [[ ${#build_std[@]} -eq 0 ]]; then 164 | build_std=(-Z build-std) 165 | fi 166 | else 167 | bail "target '${target}' requires nightly compiler" 168 | fi 169 | fi 170 | fi 171 | args+=(--all-features) 172 | case "${cmd}" in 173 | build*) ;; 174 | *) args+=(--workspace) ;; 175 | esac 176 | target="${target:-"${host}"}" 177 | target_lower="${target//-/_}" 178 | target_lower="${target_lower//./_}" 179 | target_upper=$(tr '[:lower:]' '[:upper:]' <<<"${target_lower}") 180 | randomize_layout=' -Z randomize-layout' 181 | case "${target}" in 182 | hexagon-unknown-linux-musl) 183 | release=(--release) 184 | build_std+=(-Z build-std-features="panic-unwind,llvm-libunwind") 185 | flags=' -C link-args=-lclang_rt.builtins-hexagon -C opt-level=z' 186 | export RUSTFLAGS="${RUSTFLAGS:-}${flags}" 187 | export RUSTDOCFLAGS="${RUSTDOCFLAGS:-}${flags}" 188 | ;; 189 | esac 190 | cranelift='' 191 | if [[ "${RUSTFLAGS:-}" =~ -Z\ *codegen-backend=cranelift ]]; then 192 | cranelift=1 193 | retry rustup ${pre_args[@]+"${pre_args[@]}"} component add rustc-codegen-cranelift-preview 194 | else 195 | case "$(basename -- "${cargo%.exe}")" in 196 | cargo-clif) cranelift=1 ;; 197 | esac 198 | fi 199 | if [[ -n "${cranelift}" ]]; then 200 | # panic=unwind is not supported yet. 201 | # https://github.com/rust-lang/rustc_codegen_cranelift#not-yet-supported 202 | flags=' -C panic=abort -Z panic_abort_tests' 203 | export RUSTFLAGS="${RUSTFLAGS:-}${flags}" 204 | export RUSTDOCFLAGS="${RUSTDOCFLAGS:-}${flags}" 205 | fi 206 | 207 | case "${cmd}" in 208 | *valgrind) 209 | # TODO: always pass randomize-layout 210 | export RUSTFLAGS="${RUSTFLAGS:-} --cfg valgrind" 211 | export RUSTDOCFLAGS="${RUSTDOCFLAGS:-} --cfg valgrind" 212 | ;; 213 | esac 214 | case "${cmd}" in 215 | build*) 216 | TS='' 217 | args+=(--no-run ${release[@]+"${release[@]}"}) 218 | x_cargo test ${build_std[@]+"${build_std[@]}"} ${cargo_options[@]+"${cargo_options[@]}"} "${args[@]}" >&2 219 | manifest_path=$(cargo ${pre_args[@]+"${pre_args[@]}"} locate-project --message-format=plain) 220 | binary_path=$( 221 | "${cargo}" ${pre_args[@]+"${pre_args[@]}"} test ${build_std[@]+"${build_std[@]}"} ${cargo_options[@]+"${cargo_options[@]}"} "${args[@]}" -q --message-format=json \ 222 | | jq -r --arg manifest_path "${manifest_path}" 'select(.manifest_path == $manifest_path) | if .executable then .executable else empty end' 223 | ) 224 | printf '%s\n' "${binary_path}" 225 | exit 0 226 | ;; 227 | valgrind) 228 | # Refs: https://valgrind.org/docs/manual/mc-manual.html 229 | # See also https://wiki.wxwidgets.org/Valgrind_Suppression_File_Howto for suppression file. 230 | # NB: Sync with arguments in valgrind-other job in .github/workflows/ci.yml. 231 | valgrind="valgrind -v --error-exitcode=1 --error-limit=no --leak-check=full --track-origins=yes --fair-sched=yes --gen-suppressions=all" 232 | supp="${workspace_dir}/tools/valgrind/${target%%-*}.supp" 233 | if [[ -f "${supp}" ]]; then 234 | valgrind+=" --suppressions=${supp}" 235 | fi 236 | export "CARGO_TARGET_${target_upper}_RUNNER"="${valgrind}" 237 | # doctest on Valgrind is very slow 238 | if [[ ${#tests[@]} -eq 0 ]]; then 239 | tests=(--tests) 240 | fi 241 | ;; 242 | test) ;; 243 | *) bail "unrecognized command '${cmd}'" ;; 244 | esac 245 | 246 | run() { 247 | if [[ ${#release[@]} -eq 0 ]]; then 248 | x_cargo test ${build_std[@]+"${build_std[@]}"} ${tests[@]+"${tests[@]}"} "$@" 249 | fi 250 | 251 | # release mode + doctests is slow on some platforms (probably related to the fact that they compile binaries for each example) 252 | x_cargo test ${build_std[@]+"${build_std[@]}"} --release --tests "$@" 253 | 254 | if [[ -n "${cranelift}" ]]; then 255 | return # LTO is not supported 256 | fi 257 | 258 | case "${target}" in 259 | # TODO(hexagon): segmentation fault (also happen without this crate) 260 | hexagon-unknown-linux-musl) ;; 261 | *) 262 | # LTO + doctests is very slow on some platforms (probably related to the fact that they compile binaries for each example) 263 | CARGO_TARGET_DIR="${target_dir}/fat-lto" \ 264 | CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 \ 265 | CARGO_PROFILE_RELEASE_LTO=fat \ 266 | x_cargo test ${build_std[@]+"${build_std[@]}"} --release --tests "$@" 267 | ;; 268 | esac 269 | 270 | # cargo-careful only supports nightly. 271 | if [[ -n "${nightly}" ]] && type -P cargo-careful >/dev/null && [[ "${cargo}" == "cargo" ]]; then 272 | # Since nightly-2022-12-23, -Z build-std + -Z randomize-layout + release mode on Windows 273 | # sometimes causes segfault in build script or proc-macro. 274 | if [[ "${target}" == *"-windows"* ]]; then 275 | randomize_layout='' 276 | fi 277 | flags="${randomize_layout}" 278 | case "${target}" in 279 | *-linux-musl*) flags+=" -C target-feature=-crt-static" ;; 280 | esac 281 | case "${target}" in 282 | # cannot find crt2.o/rsbegin.o/rsend.o when building std 283 | i686-pc-windows-gnu) ;; 284 | # TODO(hexagon): haw to pass build-std-features to cargo-careful? 285 | hexagon-unknown-linux-musl) ;; 286 | *) 287 | if [[ ${#build_std[@]} -gt 0 ]]; then 288 | CARGO_TARGET_DIR="${target_dir}/careful" \ 289 | RUSTFLAGS="${RUSTFLAGS:-}${flags}" \ 290 | RUSTDOCFLAGS="${RUSTDOCFLAGS:-}${flags}" \ 291 | x_cargo careful test -Z doctest-xcompile ${release[@]+"${release[@]}"} ${tests[@]+"${tests[@]}"} "$@" 292 | else 293 | # -Z doctest-xcompile is already passed 294 | CARGO_TARGET_DIR="${target_dir}/careful" \ 295 | RUSTFLAGS="${RUSTFLAGS:-}${flags}" \ 296 | RUSTDOCFLAGS="${RUSTDOCFLAGS:-}${flags}" \ 297 | x_cargo careful test ${release[@]+"${release[@]}"} ${tests[@]+"${tests[@]}"} "$@" 298 | fi 299 | ;; 300 | esac 301 | fi 302 | } 303 | 304 | run ${cargo_options[@]+"${cargo_options[@]}"} "${args[@]}" ${rest_cargo_options[@]+"${rest_cargo_options[@]}"} 305 | -------------------------------------------------------------------------------- /src/raw.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | //! Low level API. 4 | 5 | #[cfg(doc)] 6 | use core::{ 7 | cell::UnsafeCell, 8 | sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst}, 9 | }; 10 | use core::{mem::MaybeUninit, sync::atomic::Ordering}; 11 | 12 | // TODO: merge AtomicLoad and AtomicStore and rename to AtomicLoadStore? 13 | 14 | /// Primitive types that may support atomic operations. 15 | /// 16 | /// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`. 17 | /// 18 | /// Currently this is implemented only for integer types. 19 | pub trait Primitive: crate::private::PrimitivePriv {} 20 | 21 | /// Atomic load. 22 | /// 23 | /// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`. 24 | #[cfg_attr( 25 | not(atomic_maybe_uninit_no_diagnostic_namespace), 26 | diagnostic::on_unimplemented( 27 | message = "atomic load of `{Self}` is not available on this target", 28 | label = "this associated function is not available on this target", 29 | note = "see for more." 30 | ) 31 | )] 32 | pub trait AtomicLoad: Primitive { 33 | /// Loads a value from `src`. 34 | /// 35 | /// `atomic_load` takes an [`Ordering`] argument which describes the memory ordering of this operation. 36 | /// Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. 37 | /// 38 | /// # Safety 39 | /// 40 | /// Behavior is undefined if any of the following conditions are violated: 41 | /// 42 | /// - `src` must be valid for reads. 43 | /// - `src` must be properly aligned **to the size of `Self`**. 44 | /// (For example, if `Self` is `u128`, `src` must be aligned to 16-byte even if the alignment of `u128` is 8-byte.) 45 | /// - `order` must be [`SeqCst`], [`Acquire`], or [`Relaxed`]. 46 | /// 47 | /// The rules for the validity of the pointer follow [the rules applied to 48 | /// functions exposed by the standard library's `ptr` module][validity], 49 | /// except that concurrent atomic operations on `src` are allowed if the 50 | /// pointer go through [`UnsafeCell::get`]. 51 | /// 52 | /// See the ["Atomic accesses to read-only memory" section in the `core::sync::atomic` docs][read-only-memory] 53 | /// for compatibility with read-only memory. 54 | /// 55 | /// [read-only-memory]: core::sync::atomic#atomic-accesses-to-read-only-memory 56 | /// [validity]: core::ptr#safety 57 | unsafe fn atomic_load(src: *const MaybeUninit, order: Ordering) -> MaybeUninit; 58 | } 59 | 60 | /// Atomic store. 61 | /// 62 | /// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`. 63 | #[cfg_attr( 64 | not(atomic_maybe_uninit_no_diagnostic_namespace), 65 | diagnostic::on_unimplemented( 66 | message = "atomic store of `{Self}` is not available on this target", 67 | label = "this associated function is not available on this target", 68 | note = "see for more." 69 | ) 70 | )] 71 | pub trait AtomicStore: Primitive { 72 | /// Stores a value into `dst`. 73 | /// 74 | /// `atomic_store` takes an [`Ordering`] argument which describes the memory ordering of this operation. 75 | /// Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. 76 | /// 77 | /// # Safety 78 | /// 79 | /// Behavior is undefined if any of the following conditions are violated: 80 | /// 81 | /// - `dst` must be valid for writes 82 | /// - `dst` must be properly aligned **to the size of `Self`**. 83 | /// (For example, if `Self` is `u128`, `dst` must be aligned to 16-byte even if the alignment of `u128` is 8-byte.) 84 | /// - `order` must be [`SeqCst`], [`Release`], or [`Relaxed`]. 85 | /// 86 | /// The rules for the validity of the pointer follow [the rules applied to 87 | /// functions exposed by the standard library's `ptr` module][validity], 88 | /// except that concurrent atomic operations on `dst` are allowed if the 89 | /// pointer go through [`UnsafeCell::get`]. 90 | /// 91 | /// [validity]: core::ptr#safety 92 | unsafe fn atomic_store(dst: *mut MaybeUninit, val: MaybeUninit, order: Ordering); 93 | } 94 | 95 | /// Atomic swap. 96 | /// 97 | /// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`. 98 | #[cfg_attr( 99 | not(atomic_maybe_uninit_no_diagnostic_namespace), 100 | diagnostic::on_unimplemented( 101 | message = "atomic swap of `{Self}` is not available on this target", 102 | label = "this associated function is not available on this target", 103 | note = "see for more." 104 | ) 105 | )] 106 | pub trait AtomicSwap: AtomicLoad + AtomicStore { 107 | /// Stores a value into `dst`, returning the previous value. 108 | /// 109 | /// `atomic_swap` takes an [`Ordering`] argument which describes the memory ordering 110 | /// of this operation. All ordering modes are possible. Note that using 111 | /// [`Acquire`] makes the store part of this operation [`Relaxed`], and 112 | /// using [`Release`] makes the load part [`Relaxed`]. 113 | /// 114 | /// # Safety 115 | /// 116 | /// Behavior is undefined if any of the following conditions are violated: 117 | /// 118 | /// - `dst` must be valid for both reads and writes. 119 | /// - `dst` must be properly aligned **to the size of `Self`**. 120 | /// (For example, if `Self` is `u128`, `dst` must be aligned to 16-byte even if the alignment of `u128` is 8-byte.) 121 | /// - `order` must be [`SeqCst`], [`AcqRel`], [`Acquire`], [`Release`], or [`Relaxed`]. 122 | /// 123 | /// The rules for the validity of the pointer follow [the rules applied to 124 | /// functions exposed by the standard library's `ptr` module][validity], 125 | /// except that concurrent atomic operations on `dst` are allowed if the 126 | /// pointer go through [`UnsafeCell::get`]. 127 | /// 128 | /// [validity]: core::ptr#safety 129 | unsafe fn atomic_swap( 130 | dst: *mut MaybeUninit, 131 | val: MaybeUninit, 132 | order: Ordering, 133 | ) -> MaybeUninit; 134 | } 135 | 136 | /// Atomic compare and exchange. 137 | /// 138 | /// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`. 139 | #[cfg_attr( 140 | not(atomic_maybe_uninit_no_diagnostic_namespace), 141 | diagnostic::on_unimplemented( 142 | message = "atomic compare and exchange of `{Self}` is not available on this target", 143 | label = "this associated function is not available on this target", 144 | note = "see for more." 145 | ) 146 | )] 147 | pub trait AtomicCompareExchange: AtomicLoad + AtomicStore { 148 | /// Stores a value into `dst` if the current value is the same as 149 | /// the `current` value. Here, "the same" is determined using byte-wise 150 | /// equality, not `PartialEq`. 151 | /// 152 | /// The return value is a tuple of the previous value and the result indicating whether the new 153 | /// value was written and containing the previous value. On success, the returned value is 154 | /// guaranteed to be equal to the value at `current`. 155 | /// 156 | /// `atomic_compare_exchange` takes two [`Ordering`] arguments to describe the memory 157 | /// ordering of this operation. `success` describes the required ordering for the 158 | /// read-modify-write operation that takes place if the comparison with `current` succeeds. 159 | /// `failure` describes the required ordering for the load operation that takes place when 160 | /// the comparison fails. Using [`Acquire`] as success ordering makes the store part 161 | /// of this operation [`Relaxed`], and using [`Release`] makes the successful load 162 | /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. 163 | /// 164 | /// # Safety 165 | /// 166 | /// Behavior is undefined if any of the following conditions are violated: 167 | /// 168 | /// - `dst` must be valid for both reads and writes. 169 | /// - `dst` must be properly aligned **to the size of `Self`**. 170 | /// (For example, if `Self` is `u128`, `dst` must be aligned to 16-byte even if the alignment of `u128` is 8-byte.) 171 | /// - `success` must be [`SeqCst`], [`AcqRel`], [`Acquire`], [`Release`], or [`Relaxed`]. 172 | /// - `failure` must be [`SeqCst`], [`Acquire`], or [`Relaxed`]. 173 | /// 174 | /// The rules for the validity of the pointer follow [the rules applied to 175 | /// functions exposed by the standard library's `ptr` module][validity], 176 | /// except that concurrent atomic operations on `dst` are allowed if the 177 | /// pointer go through [`UnsafeCell::get`]. 178 | /// 179 | /// [validity]: core::ptr#safety 180 | /// 181 | /// # Notes 182 | /// 183 | /// Comparison of two values containing uninitialized bytes may fail even if 184 | /// they are equivalent as Rust's type, because values can be byte-wise 185 | /// inequal even when they are equal as Rust values. 186 | /// 187 | /// See [`AtomicMaybeUninit::compare_exchange`](crate::AtomicMaybeUninit::compare_exchange) for details. 188 | unsafe fn atomic_compare_exchange( 189 | dst: *mut MaybeUninit, 190 | current: MaybeUninit, 191 | new: MaybeUninit, 192 | success: Ordering, 193 | failure: Ordering, 194 | ) -> (MaybeUninit, bool); 195 | 196 | /// Stores a value into `dst` if the current value is the same as 197 | /// the `current` value. Here, "the same" is determined using byte-wise 198 | /// equality, not `PartialEq`. 199 | /// 200 | /// This function is allowed to spuriously fail even when the comparison succeeds, which can 201 | /// result in more efficient code on some platforms. The return value is a tuple of the previous 202 | /// value and the result indicating whether the new value was written and containing the 203 | /// previous value. 204 | /// 205 | /// `atomic_compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory 206 | /// ordering of this operation. `success` describes the required ordering for the 207 | /// read-modify-write operation that takes place if the comparison with `current` succeeds. 208 | /// `failure` describes the required ordering for the load operation that takes place when 209 | /// the comparison fails. Using [`Acquire`] as success ordering makes the store part 210 | /// of this operation [`Relaxed`], and using [`Release`] makes the successful load 211 | /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. 212 | /// 213 | /// # Safety 214 | /// 215 | /// Behavior is undefined if any of the following conditions are violated: 216 | /// 217 | /// - `dst` must be valid for both reads and writes. 218 | /// - `dst` must be properly aligned **to the size of `Self`**. 219 | /// (For example, if `Self` is `u128`, `dst` must be aligned to 16-byte even if the alignment of `u128` is 8-byte.) 220 | /// - `success` must be [`SeqCst`], [`AcqRel`], [`Acquire`], [`Release`], or [`Relaxed`]. 221 | /// - `failure` must be [`SeqCst`], [`Acquire`], or [`Relaxed`]. 222 | /// 223 | /// The rules for the validity of the pointer follow [the rules applied to 224 | /// functions exposed by the standard library's `ptr` module][validity], 225 | /// except that concurrent atomic operations on `dst` are allowed if the 226 | /// pointer go through [`UnsafeCell::get`]. 227 | /// 228 | /// [validity]: core::ptr#safety 229 | /// 230 | /// # Notes 231 | /// 232 | /// Comparison of two values containing uninitialized bytes may fail even if 233 | /// they are equivalent as Rust's type, because values can be byte-wise 234 | /// inequal even when they are equal as Rust values. 235 | /// 236 | /// See [`AtomicMaybeUninit::compare_exchange`](crate::AtomicMaybeUninit::compare_exchange) for details. 237 | #[inline] 238 | unsafe fn atomic_compare_exchange_weak( 239 | dst: *mut MaybeUninit, 240 | current: MaybeUninit, 241 | new: MaybeUninit, 242 | success: Ordering, 243 | failure: Ordering, 244 | ) -> (MaybeUninit, bool) { 245 | // SAFETY: the caller must uphold the safety contract. 246 | unsafe { Self::atomic_compare_exchange(dst, current, new, success, failure) } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/arch/avr.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | /* 4 | AVR 5 | 6 | See "Atomic operation overview by architecture" for atomic operations in this architecture: 7 | https://github.com/taiki-e/atomic-maybe-uninit/blob/HEAD/src/arch/README.md#avr 8 | 9 | Refs: 10 | - AVR® Instruction Set Manual, Rev. DS40002198B 11 | https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-InstructionSet-Manual-DS40002198.pdf 12 | - portable-atomic 13 | https://github.com/taiki-e/portable-atomic 14 | 15 | Generated asm: 16 | - avr https://godbolt.org/z/5TYW8x6T9 17 | */ 18 | 19 | delegate_size!(delegate_all); 20 | 21 | use core::{ 22 | arch::asm, 23 | mem::{self, MaybeUninit}, 24 | sync::atomic::Ordering, 25 | }; 26 | 27 | use crate::raw::{AtomicCompareExchange, AtomicLoad, AtomicStore, AtomicSwap}; 28 | 29 | // See portable-atomic's interrupt module for more. 30 | #[inline(always)] 31 | fn disable() -> u8 { 32 | let sreg: u8; 33 | // SAFETY: reading the status register (SREG) and disabling interrupts are safe. 34 | unsafe { 35 | asm!( 36 | "in {sreg}, 0x3F", // sreg = SREG 37 | "cli", // SREG.I = 0 38 | sreg = out(reg) sreg, 39 | // Do not use `nomem` and `readonly` because prevent subsequent memory accesses from being reordered before interrupts are disabled. 40 | // Do not use `preserves_flags` because CLI modifies the I bit of the status register (SREG). 41 | options(nostack), 42 | ); 43 | } 44 | sreg 45 | } 46 | #[inline(always)] 47 | unsafe fn restore(prev_sreg: u8) { 48 | // SAFETY: the caller must guarantee that the state was retrieved by the previous `disable`, 49 | unsafe { 50 | // This clobbers the entire status register. See msp430.rs to safety on this. 51 | asm!( 52 | "out 0x3F, {prev_sreg}", // SREG = prev_sreg 53 | prev_sreg = in(reg) prev_sreg, 54 | // Do not use `nomem` and `readonly` because prevent preceding memory accesses from being reordered after interrupts are enabled. 55 | // Do not use `preserves_flags` because OUT modifies the status register (SREG). 56 | options(nostack), 57 | ); 58 | } 59 | } 60 | 61 | #[inline(always)] 62 | fn xor8(a: MaybeUninit, b: MaybeUninit) -> u8 { 63 | let out; 64 | // SAFETY: calling EOR is safe. 65 | unsafe { 66 | asm!( 67 | "eor {a}, {b}", // a ^= b 68 | a = inout(reg) a => out, 69 | b = in(reg) b, 70 | // Do not use `preserves_flags` because EOR modifies Z, N, V, and S bits in the status register (SREG). 71 | options(pure, nomem, nostack), 72 | ); 73 | } 74 | out 75 | } 76 | #[inline(always)] 77 | fn cmp16(a: MaybeUninit, b: MaybeUninit) -> bool { 78 | // SAFETY: same layout. 79 | let [a1, a2] = unsafe { mem::transmute::, [MaybeUninit; 2]>(a) }; 80 | // SAFETY: same layout. 81 | let [b1, b2] = unsafe { mem::transmute::, [MaybeUninit; 2]>(b) }; 82 | xor8(a1, b1) | xor8(a2, b2) == 0 83 | } 84 | 85 | macro_rules! atomic8 { 86 | ($ty:ident) => { 87 | delegate_signed!(delegate_all, $ty); 88 | impl AtomicLoad for $ty { 89 | #[inline] 90 | unsafe fn atomic_load( 91 | src: *const MaybeUninit, 92 | _order: Ordering, 93 | ) -> MaybeUninit { 94 | let out: MaybeUninit; 95 | 96 | // SAFETY: the caller must uphold the safety contract. 97 | unsafe { 98 | asm!( 99 | "ld {out}, Z", // atomic { out = *Z } 100 | out = out(reg) out, 101 | in("Z") src, 102 | options(nostack, preserves_flags), 103 | ); 104 | } 105 | out 106 | } 107 | } 108 | impl AtomicStore for $ty { 109 | #[inline] 110 | unsafe fn atomic_store( 111 | dst: *mut MaybeUninit, 112 | val: MaybeUninit, 113 | _order: Ordering, 114 | ) { 115 | // SAFETY: the caller must uphold the safety contract. 116 | unsafe { 117 | asm!( 118 | "st Z, {val}", // atomic { *Z = val } 119 | val = in(reg) val, 120 | in("Z") dst, 121 | options(nostack, preserves_flags), 122 | ); 123 | } 124 | } 125 | } 126 | impl AtomicSwap for $ty { 127 | #[inline] 128 | unsafe fn atomic_swap( 129 | dst: *mut MaybeUninit, 130 | val: MaybeUninit, 131 | _order: Ordering, 132 | ) -> MaybeUninit { 133 | let out: MaybeUninit; 134 | 135 | // SAFETY: the caller must uphold the safety contract. 136 | unsafe { 137 | #[cfg(any(target_feature = "rmw", atomic_maybe_uninit_target_feature = "rmw"))] 138 | asm!( 139 | "xch Z, {val}", // atomic { _x = *Z; *Z = val; val = _x } 140 | val = inout(reg) val => out, 141 | in("Z") dst, 142 | options(nostack, preserves_flags), 143 | ); 144 | #[cfg(not(any(target_feature = "rmw", atomic_maybe_uninit_target_feature = "rmw")))] 145 | asm!( 146 | "in {sreg}, 0x3F", // sreg = SREG 147 | "cli", // atomic { SREG.I = 0 148 | "ld {out}, Z", // out = *Z 149 | "st Z, {val}", // *Z = val 150 | "out 0x3F, {sreg}", // SREG = sreg } 151 | val = in(reg) val, 152 | out = out(reg) out, 153 | sreg = out(reg) _, 154 | in("Z") dst, 155 | options(nostack, preserves_flags), 156 | ); 157 | } 158 | out 159 | } 160 | } 161 | impl AtomicCompareExchange for $ty { 162 | #[inline] 163 | unsafe fn atomic_compare_exchange( 164 | dst: *mut MaybeUninit, 165 | old: MaybeUninit, 166 | new: MaybeUninit, 167 | _success: Ordering, 168 | _failure: Ordering, 169 | ) -> (MaybeUninit, bool) { 170 | let out: MaybeUninit; 171 | let mut r: u8; 172 | 173 | // SAFETY: the caller must uphold the safety contract. 174 | unsafe { 175 | asm!( 176 | "in {sreg}, 0x3F", // sreg = SREG 177 | "cli", // atomic { SREG.I = 0 178 | "ld {out}, Z", // out = *Z 179 | "eor {old}, {out}", // old ^= out; if old == 0 { SREG.Z = 1 } else { SREG.Z = 0 } 180 | "brne 2f", // if SREG.Z == 0 { jump 'cmp-fail } 181 | "st Z, {new}", // *Z = new 182 | "2:", // 'cmp-fail: 183 | "out 0x3F, {sreg}", // SREG = sreg } 184 | old = inout(reg) old => r, 185 | new = in(reg) new, 186 | out = out(reg) out, 187 | sreg = out(reg) _, 188 | in("Z") dst, 189 | // EOR modifies the status register (SREG), but `preserves_flags` is okay since SREG is restored at the end. 190 | options(nostack, preserves_flags), 191 | ); 192 | (out, r == 0) 193 | } 194 | } 195 | } 196 | }; 197 | } 198 | 199 | macro_rules! atomic16 { 200 | ($ty:ident) => { 201 | delegate_signed!(delegate_all, $ty); 202 | impl AtomicLoad for $ty { 203 | #[inline] 204 | unsafe fn atomic_load( 205 | src: *const MaybeUninit, 206 | _order: Ordering, 207 | ) -> MaybeUninit { 208 | let s = disable(); 209 | // SAFETY: the caller must guarantee that pointer is valid and properly aligned. 210 | // On single-core systems, disabling interrupts is enough to prevent data race. 211 | let out = unsafe { src.read() }; 212 | // SAFETY: the state was retrieved by the previous `disable`. 213 | unsafe { restore(s) } 214 | out 215 | } 216 | } 217 | impl AtomicStore for $ty { 218 | #[inline] 219 | unsafe fn atomic_store( 220 | dst: *mut MaybeUninit, 221 | val: MaybeUninit, 222 | _order: Ordering, 223 | ) { 224 | let s = disable(); 225 | // SAFETY: the caller must guarantee that pointer is valid and properly aligned. 226 | // On single-core systems, disabling interrupts is enough to prevent data race. 227 | unsafe { dst.write(val) } 228 | // SAFETY: the state was retrieved by the previous `disable`. 229 | unsafe { restore(s) } 230 | } 231 | } 232 | impl AtomicSwap for $ty { 233 | #[inline] 234 | unsafe fn atomic_swap( 235 | dst: *mut MaybeUninit, 236 | val: MaybeUninit, 237 | _order: Ordering, 238 | ) -> MaybeUninit { 239 | let s = disable(); 240 | // SAFETY: the caller must guarantee that pointer is valid and properly aligned. 241 | // On single-core systems, disabling interrupts is enough to prevent data race. 242 | let out = unsafe { dst.read() }; 243 | // SAFETY: see dst.read() 244 | unsafe { dst.write(val) } 245 | // SAFETY: the state was retrieved by the previous `disable`. 246 | unsafe { restore(s) } 247 | out 248 | } 249 | } 250 | impl AtomicCompareExchange for $ty { 251 | #[inline] 252 | unsafe fn atomic_compare_exchange( 253 | dst: *mut MaybeUninit, 254 | old: MaybeUninit, 255 | new: MaybeUninit, 256 | _success: Ordering, 257 | _failure: Ordering, 258 | ) -> (MaybeUninit, bool) { 259 | let s = disable(); 260 | // SAFETY: the caller must guarantee that pointer is valid and properly aligned. 261 | // On single-core systems, disabling interrupts is enough to prevent data race. 262 | let out = unsafe { dst.read() }; 263 | // transmute from MaybeUninit<{i,u}{16,size}> to MaybeUninit 264 | #[allow(clippy::useless_transmute)] // only useless when Self is u16 265 | // SAFETY: Self and $cmp_ty has the same layout 266 | let r = unsafe { 267 | cmp16( 268 | mem::transmute::, MaybeUninit>(old), 269 | mem::transmute::, MaybeUninit>(out), 270 | ) 271 | }; 272 | if r { 273 | // SAFETY: see dst.read() 274 | unsafe { dst.write(new) } 275 | } 276 | // SAFETY: the state was retrieved by the previous `disable`. 277 | unsafe { restore(s) } 278 | (out, r) 279 | } 280 | } 281 | }; 282 | } 283 | 284 | atomic8!(u8); 285 | atomic16!(u16); 286 | 287 | // ----------------------------------------------------------------------------- 288 | // cfg macros 289 | 290 | #[macro_export] 291 | macro_rules! cfg_has_atomic_8 { 292 | ($($tt:tt)*) => { $($tt)* }; 293 | } 294 | #[macro_export] 295 | macro_rules! cfg_no_atomic_8 { 296 | ($($tt:tt)*) => {}; 297 | } 298 | #[macro_export] 299 | macro_rules! cfg_has_atomic_16 { 300 | ($($tt:tt)*) => { $($tt)* }; 301 | } 302 | #[macro_export] 303 | macro_rules! cfg_no_atomic_16 { 304 | ($($tt:tt)*) => {}; 305 | } 306 | #[macro_export] 307 | macro_rules! cfg_has_atomic_32 { 308 | ($($tt:tt)*) => {}; 309 | } 310 | #[macro_export] 311 | macro_rules! cfg_no_atomic_32 { 312 | ($($tt:tt)*) => { $($tt)* }; 313 | } 314 | #[macro_export] 315 | macro_rules! cfg_has_atomic_64 { 316 | ($($tt:tt)*) => {}; 317 | } 318 | #[macro_export] 319 | macro_rules! cfg_no_atomic_64 { 320 | ($($tt:tt)*) => { $($tt)* }; 321 | } 322 | #[macro_export] 323 | macro_rules! cfg_has_atomic_128 { 324 | ($($tt:tt)*) => {}; 325 | } 326 | #[macro_export] 327 | macro_rules! cfg_no_atomic_128 { 328 | ($($tt:tt)*) => { $($tt)* }; 329 | } 330 | #[macro_export] 331 | macro_rules! cfg_has_atomic_cas { 332 | ($($tt:tt)*) => { $($tt)* }; 333 | } 334 | #[macro_export] 335 | macro_rules! cfg_no_atomic_cas { 336 | ($($tt:tt)*) => {}; 337 | } 338 | -------------------------------------------------------------------------------- /tools/no-std.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-License-Identifier: Apache-2.0 OR MIT 3 | set -CeEuo pipefail 4 | IFS=$'\n\t' 5 | trap -- 's=$?; printf >&2 "%s\n" "${0##*/}:${LINENO}: \`${BASH_COMMAND}\` exit with ${s}"; exit ${s}' ERR 6 | trap -- 'printf >&2 "%s\n" "${0##*/}: trapped SIGINT"; exit 1' SIGINT 7 | cd -- "$(dirname -- "$0")"/.. 8 | 9 | # USAGE: 10 | # ./tools/no-std.sh [+toolchain] [target]... 11 | 12 | # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value.os then empty else .key end' 13 | default_targets=( 14 | # arm 15 | # v6-M 16 | thumbv6m-none-eabi 17 | # v7-M 18 | thumbv7m-none-eabi 19 | thumbv7em-none-eabi 20 | thumbv7em-none-eabihf 21 | # v8-M 22 | thumbv8m.base-none-eabi 23 | thumbv8m.main-none-eabi 24 | thumbv8m.main-none-eabihf 25 | 26 | # riscv32 27 | riscv32i-unknown-none-elf 28 | riscv32im-unknown-none-elf 29 | riscv32imc-unknown-none-elf 30 | riscv32ima-unknown-none-elf 31 | riscv32imac-unknown-none-elf 32 | riscv32imafc-unknown-none-elf 33 | riscv32gc-unknown-none-elf # custom target 34 | riscv32e-unknown-none-elf 35 | riscv32em-unknown-none-elf 36 | riscv32emc-unknown-none-elf 37 | # riscv64 38 | riscv64i-unknown-none-elf # custom target 39 | riscv64imac-unknown-none-elf 40 | riscv64gc-unknown-none-elf 41 | 42 | # sparc 43 | sparc-unknown-none-elf 44 | 45 | # avr 46 | avr-unknown-gnu-atmega2560 # custom target 47 | 48 | # msp430 49 | msp430-none-elf 50 | 51 | # m68k 52 | m68k-unknown-linux-gnu 53 | ) 54 | 55 | x() { 56 | ( 57 | set -x 58 | "$@" 59 | ) 60 | } 61 | x_cargo() { 62 | if [[ -n "${RUSTFLAGS:-}" ]]; then 63 | printf '%s\n' "+ RUSTFLAGS='${RUSTFLAGS}' \\" 64 | fi 65 | x cargo ${pre_args[@]+"${pre_args[@]}"} "${subcmd}" "$@" 66 | printf '\n' 67 | } 68 | retry() { 69 | for i in {1..10}; do 70 | if "$@"; then 71 | return 0 72 | else 73 | sleep "${i}" 74 | fi 75 | done 76 | "$@" 77 | } 78 | bail() { 79 | printf >&2 'error: %s\n' "$*" 80 | exit 1 81 | } 82 | 83 | pre_args=() 84 | is_custom_toolchain='' 85 | if [[ "${1:-}" == "+"* ]]; then 86 | if [[ "$1" == "+esp" ]]; then 87 | # shellcheck disable=SC1091 88 | . "${HOME}/export-esp.sh" 89 | is_custom_toolchain=1 90 | fi 91 | pre_args+=("$1") 92 | shift 93 | fi 94 | if [[ $# -gt 0 ]]; then 95 | targets=("$@") 96 | else 97 | targets=("${default_targets[@]}") 98 | fi 99 | 100 | rustup_target_list='' 101 | if [[ -z "${is_custom_toolchain}" ]]; then 102 | rustup_target_list=$(rustup ${pre_args[@]+"${pre_args[@]}"} target list | cut -d' ' -f1) 103 | fi 104 | rustc_target_list=$(rustc ${pre_args[@]+"${pre_args[@]}"} --print target-list) 105 | rustc_version=$(rustc ${pre_args[@]+"${pre_args[@]}"} -vV | grep -E '^release:' | cut -d' ' -f2) 106 | rustc_minor_version="${rustc_version#*.}" 107 | rustc_minor_version="${rustc_minor_version%%.*}" 108 | llvm_version=$(rustc ${pre_args[@]+"${pre_args[@]}"} -vV | { grep -E '^LLVM version:' || true; } | cut -d' ' -f3) 109 | llvm_version="${llvm_version%%.*}" 110 | commit_date=$(rustc ${pre_args[@]+"${pre_args[@]}"} -vV | grep -E '^commit-date:' | cut -d' ' -f2) 111 | workspace_dir=$(pwd) 112 | target_dir="${workspace_dir}/target" 113 | nightly='' 114 | if [[ "${rustc_version}" =~ nightly|dev ]]; then 115 | nightly=1 116 | if [[ -z "${is_custom_toolchain}" ]]; then 117 | retry rustup ${pre_args[@]+"${pre_args[@]}"} component add rust-src &>/dev/null 118 | fi 119 | fi 120 | export QEMU_AUDIO_DRV=none 121 | export ATOMIC_MAYBE_UNINIT_DENY_WARNINGS=1 122 | 123 | run() { 124 | local target="$1" 125 | shift 126 | target_lower="${target//-/_}" 127 | target_lower="${target_lower//./_}" 128 | target_upper=$(tr '[:lower:]' '[:upper:]' <<<"${target_lower}") 129 | local target_rustflags="${RUSTFLAGS:-}" 130 | if ! grep -Eq "^${target}$" <<<"${rustc_target_list}" || [[ -f "target-specs/${target}.json" ]]; then 131 | if [[ ! -f "target-specs/${target}.json" ]]; then 132 | printf '%s\n' "target '${target}' not available on ${rustc_version} (skipped)" 133 | return 0 134 | fi 135 | if [[ "${rustc_minor_version}" -lt 91 ]] && [[ "${target}" != "avr"* ]]; then 136 | # Skip pre-1.91 because target-pointer-width change 137 | printf '%s\n' "target '${target}' requires 1.91-nightly or later (skipped)" 138 | return 0 139 | fi 140 | local target_flags=(--target "${workspace_dir}/target-specs/${target}.json") 141 | else 142 | local target_flags=(--target "${target}") 143 | fi 144 | subcmd=run 145 | if [[ -z "${CI:-}" ]]; then 146 | case "${target}" in 147 | sparc*) 148 | if ! type -P tsim-leon3 >/dev/null; then 149 | printf '%s\n' "no-std test for ${target} requires tsim-leon3 (switched to build-only)" 150 | subcmd=build 151 | fi 152 | ;; 153 | avr*) 154 | if ! type -P simavr >/dev/null; then 155 | printf '%s\n' "no-std test for ${target} requires simavr (switched to build-only)" 156 | subcmd=build 157 | fi 158 | ;; 159 | msp430*) 160 | if ! type -P mspdebug >/dev/null; then 161 | printf '%s\n' "no-std test for ${target} requires mspdebug (switched to build-only)" 162 | subcmd=build 163 | fi 164 | ;; 165 | esac 166 | fi 167 | case "${target}" in 168 | xtensa*) 169 | # TODO(xtensa): run test with simulator on CI 170 | if ! type -P wokwi-server >/dev/null; then 171 | printf '%s\n' "no-std test for ${target} requires wokwi-server (switched to build-only)" 172 | subcmd=build 173 | fi 174 | ;; 175 | esac 176 | local args=("${target_flags[@]}") 177 | if grep -Eq "^${target}$" <<<"${rustup_target_list}"; then 178 | retry rustup ${pre_args[@]+"${pre_args[@]}"} target add "${target}" &>/dev/null 179 | elif [[ -n "${nightly}" ]]; then 180 | args+=(-Z build-std="core") 181 | else 182 | printf '%s\n' "target '${target}' requires nightly compiler (skipped)" 183 | return 0 184 | fi 185 | 186 | local test_dir 187 | case "${target}" in 188 | thumb* | riscv*) 189 | test_dir=tests/no-std-qemu 190 | linker=link.x 191 | target_rustflags+=" -C link-arg=-T${linker}" 192 | ;; 193 | sparc*) 194 | case "${commit_date}" in 195 | 2023-08-23) 196 | # no asm support 197 | printf '%s\n' "target '${target}' is not supported on this version (skipped)" 198 | return 0 199 | ;; 200 | esac 201 | test_dir=tests/sparc 202 | export "CARGO_TARGET_${target_upper}_RUNNER"="${workspace_dir}/tools/runner.sh tsim-leon3 ${target}" 203 | # Refs: https://github.com/ferrous-systems/sparc-experiments/blob/ff502602ffe57a0ac03a461563f8d84870b475a0/sparc-demo-rust/.cargo/config.toml 204 | target_rustflags+=" -C target-cpu=gr712rc -C link-arg=-mcpu=leon3 -C link-arg=-qbsp=leon3" 205 | ;; 206 | avr*) 207 | test_dir=tests/avr 208 | export "CARGO_TARGET_${target_upper}_RUNNER"="${workspace_dir}/tools/runner.sh simavr ${target}" 209 | ;; 210 | msp430*) 211 | case "${commit_date}" in 212 | 2023-08-23) 213 | # multiple definition of `__muldi3' 214 | printf '%s\n' "target '${target}' in broken on this version (skipped)" 215 | return 0 216 | ;; 217 | esac 218 | test_dir=tests/msp430 219 | export "CARGO_TARGET_${target_upper}_RUNNER"="${workspace_dir}/tools/runner.sh mspdebug ${target}" 220 | # Refs: https://github.com/rust-embedded/msp430-quickstart/blob/535cd3c810ec6096a1dd0546ea290ed94aa6fd01/.cargo/config 221 | linker=link.x 222 | target_rustflags+=" -C link-arg=-T${linker}" 223 | target_rustflags+=" -C link-arg=-nostartfiles" 224 | target_rustflags+=" -C link-arg=-mcpu=msp430" 225 | target_rustflags+=" -C link-arg=-lmul_f5" 226 | target_rustflags+=" -C link-arg=-lgcc" 227 | ;; 228 | xtensa*) 229 | test_dir=tests/xtensa 230 | export "CARGO_TARGET_${target_upper}_RUNNER"="${workspace_dir}/tools/runner.sh wokwi-server ${target}" 231 | linker=linkall.x 232 | target_rustflags+=" -C link-arg=-Wl,-T${linker} -C link-arg=-nostartfiles" 233 | local cpu 234 | cpu=$(cut -d- -f2 <<<"${target}") 235 | args+=(--features "esp-println/${cpu},esp-hal/${cpu}") 236 | ;; 237 | m68k*) 238 | if [[ "${llvm_version}" -lt 20 ]]; then 239 | # pre-20 LLVM bug https://github.com/llvm/llvm-project/issues/107939 240 | printf '%s\n' "target '${target}' is not supported on this version (skipped)" 241 | return 0 242 | fi 243 | test_dir=tests/no-std-linux 244 | ;; 245 | *) bail "unrecognized target '${target}'" ;; 246 | esac 247 | case "${target}" in 248 | m68k*) ;; 249 | *) args+=(--all-features) ;; 250 | esac 251 | 252 | ( 253 | cd -- "${test_dir}" 254 | CARGO_TARGET_DIR="${target_dir}/no-std-test" \ 255 | RUSTFLAGS="${target_rustflags}" \ 256 | x_cargo "${args[@]}" "$@" 257 | CARGO_TARGET_DIR="${target_dir}/no-std-test" \ 258 | RUSTFLAGS="${target_rustflags}" \ 259 | x_cargo "${args[@]}" --release "$@" 260 | case "${target}" in 261 | riscv*) 262 | case "${target}" in 263 | riscv??[ie]-* | riscv??[ie]m-* | riscv??[ie]mc-*) 264 | # With Zalrsc without Zaamo 265 | CARGO_TARGET_DIR="${target_dir}/no-std-test-zalrsc" \ 266 | RUSTFLAGS="${target_rustflags} -C target-feature=+zalrsc" \ 267 | x_cargo "${args[@]}" "$@" 268 | CARGO_TARGET_DIR="${target_dir}/no-std-test-zalrsc" \ 269 | RUSTFLAGS="${target_rustflags} -C target-feature=+zalrsc" \ 270 | x_cargo "${args[@]}" --release "$@" 271 | ;; 272 | esac 273 | local arch 274 | case "${target}" in 275 | riscv32*) arch=riscv32 ;; 276 | riscv64*) arch=riscv64 ;; 277 | *) bail "${target}" ;; 278 | esac 279 | # Support for Zabha extension requires LLVM 19+ and QEMU 9.1+. 280 | # https://github.com/qemu/qemu/commit/be4a8db7f304347395b081ae5848bad2f507d0c4 281 | qemu_version=$(qemu-system-"${arch}" --version | sed -En '1 s/QEMU emulator version [^ ]+ \(v([^ )]+)\)/\1/p') 282 | if [[ -z "${qemu_version}" ]]; then 283 | qemu_version=$(qemu-system-"${arch}" --version | sed -En '1 s/QEMU emulator version ([^ )]+)/\1/p') 284 | fi 285 | if [[ "${llvm_version}" -ge 19 ]] && [[ "${qemu_version}" =~ ^(9\.[^0]|[1-9][0-9]+\.) ]]; then 286 | export "CARGO_TARGET_${target_upper}_RUNNER"="qemu-system-${arch} -M virt -cpu max -display none -semihosting -kernel" 287 | CARGO_TARGET_DIR="${target_dir}/no-std-test-zabha" \ 288 | RUSTFLAGS="${target_rustflags} -C target-feature=+zaamo,+zabha" \ 289 | x_cargo "${args[@]}" "$@" 290 | CARGO_TARGET_DIR="${target_dir}/no-std-test-zabha" \ 291 | RUSTFLAGS="${target_rustflags} -C target-feature=+zaamo,+zabha" \ 292 | x_cargo "${args[@]}" --release "$@" 293 | CARGO_TARGET_DIR="${target_dir}/no-std-test-zacas" \ 294 | RUSTFLAGS="${target_rustflags} -C target-feature=+zaamo,+zacas" \ 295 | x_cargo "${args[@]}" "$@" 296 | CARGO_TARGET_DIR="${target_dir}/no-std-test-zacas" \ 297 | RUSTFLAGS="${target_rustflags} -C target-feature=+zaamo,+zacas" \ 298 | x_cargo "${args[@]}" --release "$@" 299 | CARGO_TARGET_DIR="${target_dir}/no-std-test-zabha-zacas" \ 300 | RUSTFLAGS="${target_rustflags} -C target-feature=+zaamo,+zabha,+zacas" \ 301 | x_cargo "${args[@]}" "$@" 302 | CARGO_TARGET_DIR="${target_dir}/no-std-test-zabha-zacas" \ 303 | RUSTFLAGS="${target_rustflags} -C target-feature=+zaamo,+zabha,+zacas" \ 304 | x_cargo "${args[@]}" --release "$@" 305 | fi 306 | ;; 307 | avr*) 308 | # Run with qemu-system-avr. 309 | subcmd=run 310 | export "CARGO_TARGET_${target_upper}_RUNNER"="${workspace_dir}/tools/runner.sh qemu-system ${target}" 311 | CARGO_TARGET_DIR="${target_dir}/no-std-test" \ 312 | RUSTFLAGS="${target_rustflags}" \ 313 | x_cargo "${args[@]}" "$@" 314 | CARGO_TARGET_DIR="${target_dir}/no-std-test" \ 315 | RUSTFLAGS="${target_rustflags}" \ 316 | x_cargo "${args[@]}" --release "$@" 317 | ;; 318 | sparc*) 319 | # Run with qemu-system-sparc. 320 | subcmd=run 321 | export "CARGO_TARGET_${target_upper}_RUNNER"="${workspace_dir}/tools/runner.sh qemu-system ${target}" 322 | CARGO_TARGET_DIR="${target_dir}/no-std-test" \ 323 | RUSTFLAGS="${target_rustflags}" \ 324 | x_cargo "${args[@]}" "$@" 325 | CARGO_TARGET_DIR="${target_dir}/no-std-test" \ 326 | RUSTFLAGS="${target_rustflags}" \ 327 | x_cargo "${args[@]}" --release "$@" 328 | ;; 329 | m68k*) 330 | # Note: We cannot test everything at once due to size. 331 | # isize, usize, i8, u8 are covered by the run with the default feature. 332 | # NB: Sync feature list with tests/m68k/Cargo.toml 333 | feature=i16,u16,i32,u32,i64,u64 334 | CARGO_TARGET_DIR="${target_dir}/no-std-test" \ 335 | RUSTFLAGS="${target_rustflags}" \ 336 | x_cargo "${args[@]}" --no-default-features --features "${feature}" "$@" 337 | CARGO_TARGET_DIR="${target_dir}/no-std-test" \ 338 | RUSTFLAGS="${target_rustflags}" \ 339 | x_cargo "${args[@]}" --no-default-features --features "${feature}" --release "$@" 340 | ;; 341 | esac 342 | ) 343 | } 344 | 345 | for target in "${targets[@]}"; do 346 | run "${target}" 347 | done 348 | -------------------------------------------------------------------------------- /src/arch/csky.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | /* 4 | C-SKY 5 | 6 | Refs: 7 | - CSKY Architecture user_guide 8 | https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/CSKY%20Architecture%20user_guide.pdf 9 | - Linux kernel's C-SKY atomic implementation 10 | https://github.com/torvalds/linux/blob/v6.16/arch/csky/include/asm/atomic.h 11 | https://github.com/torvalds/linux/blob/v6.16/arch/csky/include/asm/cmpxchg.h 12 | 13 | Generated asm: 14 | - csky https://godbolt.org/z/jK4c68WeG 15 | */ 16 | 17 | #[cfg(atomic_maybe_uninit_no_ldex_stex)] 18 | delegate_size!(delegate_load_store); 19 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 20 | delegate_size!(delegate_all); 21 | 22 | use core::{ 23 | arch::asm, 24 | mem::{self, MaybeUninit}, 25 | sync::atomic::Ordering, 26 | }; 27 | 28 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 29 | use crate::raw::{AtomicCompareExchange, AtomicSwap}; 30 | use crate::raw::{AtomicLoad, AtomicStore}; 31 | 32 | // According to Linux kernel, there is a more efficient BAR instruction for this purpose, but that 33 | // instruction is not mentioned in CSKY Architecture user_guide, so we always use SYNC for now. 34 | // https://github.com/torvalds/linux/blob/v6.16/arch/csky/include/asm/barrier.h 35 | macro_rules! atomic_rmw { 36 | ($op:ident, $order:ident) => { 37 | match $order { 38 | Ordering::Relaxed => $op!("", ""), 39 | Ordering::Acquire => $op!("sync32", ""), 40 | Ordering::Release => $op!("", "sync32"), 41 | Ordering::AcqRel | Ordering::SeqCst => $op!("sync32", "sync32"), 42 | _ => unreachable!(), 43 | } 44 | }; 45 | } 46 | 47 | #[rustfmt::skip] 48 | macro_rules! atomic_load_store { 49 | ($ty:ident, $suffix:tt) => { 50 | #[cfg(atomic_maybe_uninit_no_ldex_stex)] 51 | delegate_signed!(delegate_load_store, $ty); 52 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 53 | delegate_signed!(delegate_all, $ty); 54 | impl AtomicLoad for $ty { 55 | #[inline] 56 | unsafe fn atomic_load( 57 | src: *const MaybeUninit, 58 | order: Ordering, 59 | ) -> MaybeUninit { 60 | let out: MaybeUninit; 61 | 62 | // SAFETY: the caller must uphold the safety contract. 63 | unsafe { 64 | macro_rules! atomic_load { 65 | ($acquire:tt, $release:tt) => { 66 | asm!( 67 | $release, // fence 68 | concat!("ld32.", $suffix, " {out}, ({src}, 0)"), // atomic { out = *src } 69 | $acquire, // fence 70 | src = in(reg) ptr_reg!(src), 71 | out = lateout(reg) out, 72 | options(nostack, preserves_flags), 73 | ) 74 | }; 75 | } 76 | match order { 77 | Ordering::Relaxed => atomic_load!("", ""), 78 | Ordering::Acquire => atomic_load!("sync32", ""), 79 | Ordering::SeqCst => atomic_load!("sync32", "sync32"), 80 | _ => unreachable!(), 81 | } 82 | } 83 | out 84 | } 85 | } 86 | impl AtomicStore for $ty { 87 | #[inline] 88 | unsafe fn atomic_store( 89 | dst: *mut MaybeUninit, 90 | val: MaybeUninit, 91 | order: Ordering, 92 | ) { 93 | // SAFETY: the caller must uphold the safety contract. 94 | unsafe { 95 | macro_rules! store { 96 | ($acquire:tt, $release:tt) => { 97 | asm!( 98 | $release, // fence 99 | concat!("st32.", $suffix, " {val}, ({dst}, 0)"), // atomic { *dst = val } 100 | $acquire, // fence 101 | dst = in(reg) ptr_reg!(dst), 102 | val = in(reg) val, 103 | options(nostack, preserves_flags), 104 | ) 105 | }; 106 | } 107 | atomic_rmw!(store, order); 108 | } 109 | } 110 | } 111 | }; 112 | } 113 | 114 | #[rustfmt::skip] 115 | macro_rules! atomic { 116 | ($ty:ident) => { 117 | atomic_load_store!($ty, "w"); 118 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 119 | impl AtomicSwap for $ty { 120 | #[inline] 121 | unsafe fn atomic_swap( 122 | dst: *mut MaybeUninit, 123 | val: MaybeUninit, 124 | order: Ordering, 125 | ) -> MaybeUninit { 126 | let mut out: MaybeUninit; 127 | 128 | // SAFETY: the caller must uphold the safety contract. 129 | unsafe { 130 | macro_rules! swap { 131 | ($acquire:tt, $release:tt) => { 132 | asm!( 133 | $release, // fence 134 | "2:", // 'retry: 135 | "ldex32.w {out}, ({dst}, 0)", // atomic { out = *dst; EXCLUSIVE = dst } 136 | "or32 {tmp}, {val}, {val}", // tmp = val 137 | "stex32.w {tmp}, ({dst}, 0)", // atomic { if EXCLUSIVE == dst { *dst = tmp; tmp = 1 } else { tmp = 0 }; EXCLUSIVE = None } 138 | "bez32 {tmp}, 2b", // if tmp == 0 { jump 'retry } 139 | $acquire, // fence 140 | dst = in(reg) ptr_reg!(dst), 141 | val = in(reg) val, 142 | out = out(reg) out, 143 | tmp = out(reg) _, 144 | options(nostack, preserves_flags), 145 | ) 146 | }; 147 | } 148 | atomic_rmw!(swap, order); 149 | } 150 | out 151 | } 152 | } 153 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 154 | impl AtomicCompareExchange for $ty { 155 | #[inline] 156 | unsafe fn atomic_compare_exchange( 157 | dst: *mut MaybeUninit, 158 | old: MaybeUninit, 159 | new: MaybeUninit, 160 | success: Ordering, 161 | failure: Ordering, 162 | ) -> (MaybeUninit, bool) { 163 | let order = crate::utils::upgrade_success_ordering(success, failure); 164 | let mut out: MaybeUninit; 165 | 166 | // SAFETY: the caller must uphold the safety contract. 167 | unsafe { 168 | let mut r: u32 = 0; 169 | macro_rules! cmpxchg { 170 | ($acquire:tt, $release:tt) => { 171 | asm!( 172 | $release, // fence 173 | "2:", // 'retry: 174 | "ldex32.w {out}, ({dst}, 0)", // atomic { out = *dst; EXCLUSIVE = dst } 175 | "cmpne32 {out}, {old}", // if out != old { C = 1 } else { C = 0 } 176 | "bt32 3f", // if C == 1 { jump 'cmp-fail } 177 | "or32 {tmp}, {new}, {new}", // tmp = new 178 | "stex32.w {tmp}, ({dst}, 0)", // atomic { if EXCLUSIVE == dst { *dst = tmp; tmp = 1 } else { tmp = 0 }; EXCLUSIVE = None } 179 | "bez32 {tmp}, 2b", // if tmp == 0 { jump 'retry } 180 | "3:", // 'cmp-fail: 181 | $acquire, // fence 182 | dst = in(reg) ptr_reg!(dst), 183 | old = in(reg) old, 184 | new = in(reg) new, 185 | out = out(reg) out, 186 | tmp = inout(reg) r, 187 | // Do not use `preserves_flags` because CMPNE modifies condition bit C. 188 | options(nostack), 189 | ) 190 | }; 191 | } 192 | atomic_rmw!(cmpxchg, order); 193 | crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test 194 | (out, r != 0) 195 | } 196 | } 197 | } 198 | }; 199 | } 200 | 201 | #[rustfmt::skip] 202 | macro_rules! atomic_sub_word { 203 | ($ty:ident, $suffix:tt) => { 204 | atomic_load_store!($ty, $suffix); 205 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 206 | impl AtomicSwap for $ty { 207 | #[inline] 208 | unsafe fn atomic_swap( 209 | dst: *mut MaybeUninit, 210 | val: MaybeUninit, 211 | order: Ordering, 212 | ) -> MaybeUninit { 213 | let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); 214 | let mut out: MaybeUninit; 215 | 216 | // SAFETY: the caller must uphold the safety contract. 217 | unsafe { 218 | macro_rules! swap { 219 | ($acquire:tt, $release:tt) => { 220 | // Implement sub-word atomic operations using word-sized LL/SC loop. 221 | // See also create_sub_word_mask_values. 222 | asm!( 223 | "lsl32 {val}, {val}, {shift}", // val <<= shift 224 | $release, // fence 225 | "2:", // 'retry: 226 | "ldex32.w {out}, ({dst}, 0)", // atomic { out = *dst; EXCLUSIVE = dst } 227 | "andn32 {tmp}, {out}, {mask}", // tmp = out & !mask 228 | "or32 {tmp}, {tmp}, {val}", // tmp |= val 229 | "stex32.w {tmp}, ({dst}, 0)", // atomic { if EXCLUSIVE == dst { *dst = tmp; tmp = 1 } else { tmp = 0 }; EXCLUSIVE = None } 230 | "bez32 {tmp}, 2b", // if tmp == 0 { jump 'retry } 231 | $acquire, // fence 232 | "lsr32 {out}, {out}, {shift}", // out >>= shift 233 | dst = in(reg) ptr_reg!(dst), 234 | val = inout(reg) crate::utils::extend32::$ty::zero(val) => _, 235 | out = out(reg) out, 236 | shift = in(reg) shift, 237 | mask = in(reg) mask, 238 | tmp = out(reg) _, 239 | options(nostack, preserves_flags), 240 | ) 241 | }; 242 | } 243 | atomic_rmw!(swap, order); 244 | } 245 | out 246 | } 247 | } 248 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 249 | impl AtomicCompareExchange for $ty { 250 | #[inline] 251 | unsafe fn atomic_compare_exchange( 252 | dst: *mut MaybeUninit, 253 | old: MaybeUninit, 254 | new: MaybeUninit, 255 | success: Ordering, 256 | failure: Ordering, 257 | ) -> (MaybeUninit, bool) { 258 | let order = crate::utils::upgrade_success_ordering(success, failure); 259 | let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); 260 | let mut out: MaybeUninit; 261 | 262 | // SAFETY: the caller must uphold the safety contract. 263 | unsafe { 264 | let mut r: u32; 265 | macro_rules! cmpxchg { 266 | ($acquire:tt, $release:tt) => { 267 | // Implement sub-word atomic operations using word-sized LL/SC loop. 268 | // See also create_sub_word_mask_values. 269 | asm!( 270 | "lsl32 {old}, {old}, {shift}", // old <<= shift 271 | "lsl32 {new}, {new}, {shift}", // new <<= shift 272 | $release, // fence 273 | "2:", // 'retry: 274 | "ldex32.w {tmp}, ({dst}, 0)", // atomic { tmp = *dst; EXCLUSIVE = dst } 275 | "and32 {out}, {tmp}, {mask}", // out = tmp & mask 276 | "cmpne32 {out}, {old}", // if out != old { C = 1 } else { C = 0 } 277 | "bt32 3f", // if C == 1 { jump 'cmp-fail } 278 | "andn32 {tmp}, {tmp}, {mask}", // tmp &= !mask 279 | "or32 {tmp}, {tmp}, {new}", // tmp |= new 280 | "stex32.w {tmp}, ({dst}, 0)", // atomic { if EXCLUSIVE == dst { *dst = tmp; tmp = 1 } else { tmp = 0 }; EXCLUSIVE = None } 281 | "bez32 {tmp}, 2b", // if tmp == 0 { jump 'retry } 282 | "br32 4f", // jump 'success 283 | "3:", // 'cmp-fail: 284 | "movi32 {tmp}, 0", // tmp = 0 285 | "4:", // 'success: 286 | $acquire, // fence 287 | "lsr32 {out}, {out}, {shift}", // out >>= shift 288 | dst = in(reg) ptr_reg!(dst), 289 | old = inout(reg) crate::utils::extend32::$ty::zero(old) => _, 290 | new = inout(reg) crate::utils::extend32::$ty::zero(new) => _, 291 | out = out(reg) out, 292 | shift = in(reg) shift, 293 | mask = in(reg) mask, 294 | tmp = out(reg) r, 295 | // Do not use `preserves_flags` because CMPNE modifies condition bit C. 296 | options(nostack), 297 | ) 298 | }; 299 | } 300 | atomic_rmw!(cmpxchg, order); 301 | crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test 302 | (out, r != 0) 303 | } 304 | } 305 | } 306 | }; 307 | } 308 | 309 | atomic_sub_word!(u8, "b"); 310 | atomic_sub_word!(u16, "h"); 311 | atomic!(u32); 312 | 313 | // ----------------------------------------------------------------------------- 314 | // cfg macros 315 | 316 | #[macro_export] 317 | macro_rules! cfg_has_atomic_8 { 318 | ($($tt:tt)*) => { $($tt)* }; 319 | } 320 | #[macro_export] 321 | macro_rules! cfg_no_atomic_8 { 322 | ($($tt:tt)*) => {}; 323 | } 324 | #[macro_export] 325 | macro_rules! cfg_has_atomic_16 { 326 | ($($tt:tt)*) => { $($tt)* }; 327 | } 328 | #[macro_export] 329 | macro_rules! cfg_no_atomic_16 { 330 | ($($tt:tt)*) => {}; 331 | } 332 | #[macro_export] 333 | macro_rules! cfg_has_atomic_32 { 334 | ($($tt:tt)*) => { $($tt)* }; 335 | } 336 | #[macro_export] 337 | macro_rules! cfg_no_atomic_32 { 338 | ($($tt:tt)*) => {}; 339 | } 340 | #[macro_export] 341 | macro_rules! cfg_has_atomic_64 { 342 | ($($tt:tt)*) => {}; 343 | } 344 | #[macro_export] 345 | macro_rules! cfg_no_atomic_64 { 346 | ($($tt:tt)*) => { $($tt)* }; 347 | } 348 | #[macro_export] 349 | macro_rules! cfg_has_atomic_128 { 350 | ($($tt:tt)*) => {}; 351 | } 352 | #[macro_export] 353 | macro_rules! cfg_no_atomic_128 { 354 | ($($tt:tt)*) => { $($tt)* }; 355 | } 356 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 357 | #[macro_export] 358 | macro_rules! cfg_has_atomic_cas { 359 | ($($tt:tt)*) => { $($tt)* }; 360 | } 361 | #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] 362 | #[macro_export] 363 | macro_rules! cfg_no_atomic_cas { 364 | ($($tt:tt)*) => {}; 365 | } 366 | #[cfg(atomic_maybe_uninit_no_ldex_stex)] 367 | #[macro_export] 368 | macro_rules! cfg_has_atomic_cas { 369 | ($($tt:tt)*) => {}; 370 | } 371 | #[cfg(atomic_maybe_uninit_no_ldex_stex)] 372 | #[macro_export] 373 | macro_rules! cfg_no_atomic_cas { 374 | ($($tt:tt)*) => { $($tt)* }; 375 | } 376 | -------------------------------------------------------------------------------- /src/arch/xtensa.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | /* 4 | Xtensa 5 | 6 | Refs: 7 | - Xtensa Instruction Set Architecture (ISA) Summary for all Xtensa LX Processors 8 | https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/silicon-solutions/compute-ip/isa-summary.pdf 9 | - https://github.com/espressif/llvm-project/blob/xtensa_release_19.1.2/llvm/test/CodeGen/Xtensa/atomic-load-store.ll 10 | - https://github.com/espressif/llvm-project/blob/xtensa_release_19.1.2/llvm/test/CodeGen/Xtensa/atomicrmw.ll 11 | Note that LLVM's codegen for sub-word atomics seem to be suboptimal (extra branch, memw, l32i.n, etc.). 12 | 13 | Note that l32ai (acquire load), s32ri (release store), and l32ex/s32ex/getex (LL/SC) are not yet supported in LLVM. 14 | https://github.com/espressif/llvm-project/blob/xtensa_release_19.1.2/llvm/lib/Target/Xtensa/XtensaInstrInfo.td 15 | */ 16 | 17 | #[cfg(not(target_feature = "s32c1i"))] 18 | delegate_size!(delegate_load_store); 19 | #[cfg(target_feature = "s32c1i")] 20 | delegate_size!(delegate_all); 21 | 22 | use core::{ 23 | arch::asm, 24 | mem::{self, MaybeUninit}, 25 | sync::atomic::Ordering, 26 | }; 27 | 28 | #[cfg(target_feature = "s32c1i")] 29 | use crate::raw::{AtomicCompareExchange, AtomicSwap}; 30 | use crate::raw::{AtomicLoad, AtomicStore}; 31 | 32 | macro_rules! atomic_rmw { 33 | ($op:ident, $order:ident) => { 34 | match $order { 35 | Ordering::Relaxed => $op!("", ""), 36 | Ordering::Acquire => $op!("memw", ""), 37 | Ordering::Release => $op!("", "memw"), 38 | Ordering::AcqRel | Ordering::SeqCst => $op!("memw", "memw"), 39 | _ => unreachable!(), 40 | } 41 | }; 42 | } 43 | 44 | #[rustfmt::skip] 45 | macro_rules! atomic_load_store { 46 | ($ty:ident, $bits:tt, $narrow:tt, $unsigned:tt) => { 47 | #[cfg(not(target_feature = "s32c1i"))] 48 | delegate_signed!(delegate_load_store, $ty); 49 | #[cfg(target_feature = "s32c1i")] 50 | delegate_signed!(delegate_all, $ty); 51 | impl AtomicLoad for $ty { 52 | #[inline] 53 | unsafe fn atomic_load( 54 | src: *const MaybeUninit, 55 | order: Ordering, 56 | ) -> MaybeUninit { 57 | let out: MaybeUninit; 58 | 59 | // SAFETY: the caller must uphold the safety contract. 60 | unsafe { 61 | macro_rules! atomic_load { 62 | ($acquire:tt) => { 63 | asm!( 64 | concat!("l", $bits, $unsigned, "i", $narrow, " {out}, {src}, 0"), // atomic { out = *src } 65 | $acquire, // fence 66 | src = in(reg) ptr_reg!(src), 67 | out = lateout(reg) out, 68 | options(nostack, preserves_flags), 69 | ) 70 | }; 71 | } 72 | match order { 73 | Ordering::Relaxed => atomic_load!(""), 74 | Ordering::Acquire | Ordering::SeqCst => atomic_load!("memw"), 75 | _ => unreachable!(), 76 | } 77 | } 78 | out 79 | } 80 | } 81 | impl AtomicStore for $ty { 82 | #[inline] 83 | unsafe fn atomic_store( 84 | dst: *mut MaybeUninit, 85 | val: MaybeUninit, 86 | order: Ordering, 87 | ) { 88 | // SAFETY: the caller must uphold the safety contract. 89 | unsafe { 90 | macro_rules! store { 91 | ($acquire:tt, $release:tt) => { 92 | asm!( 93 | $release, // fence 94 | concat!("s", $bits, "i", $narrow, " {val}, {dst}, 0"), // atomic { *dst = val } 95 | $acquire, // fence 96 | dst = in(reg) ptr_reg!(dst), 97 | val = in(reg) val, 98 | options(nostack, preserves_flags), 99 | ) 100 | }; 101 | } 102 | atomic_rmw!(store, order); 103 | } 104 | } 105 | } 106 | }; 107 | } 108 | 109 | #[rustfmt::skip] 110 | macro_rules! atomic { 111 | ($ty:ident) => { 112 | atomic_load_store!($ty, "32", ".n", ""); 113 | #[cfg(target_feature = "s32c1i")] 114 | impl AtomicSwap for $ty { 115 | #[inline] 116 | unsafe fn atomic_swap( 117 | dst: *mut MaybeUninit, 118 | val: MaybeUninit, 119 | order: Ordering, 120 | ) -> MaybeUninit { 121 | let mut out: MaybeUninit; 122 | 123 | // SAFETY: the caller must uphold the safety contract. 124 | unsafe { 125 | macro_rules! swap { 126 | ($acquire:tt, $release:tt) => { 127 | asm!( 128 | $release, // fence 129 | "l32i.n {out}, {dst}, 0", // atomic { out = *dst } 130 | "2:", // 'retry: 131 | "mov.n {tmp}, {out}", // tmp = out 132 | "wsr {tmp}, scompare1", // scompare1 = tmp 133 | "mov.n {out}, {val}", // out = val 134 | "s32c1i {out}, {dst}, 0", // atomic { _x = *dst; if _x == scompare1 { *dst = out }; out = _x } 135 | "bne {tmp}, {out}, 2b", // if tmp != out { jump 'retry } 136 | $acquire, // fence 137 | dst = in(reg) ptr_reg!(dst), 138 | val = in(reg) val, 139 | out = out(reg) out, 140 | tmp = out(reg) _, 141 | out("scompare1") _, 142 | options(nostack, preserves_flags), 143 | ) 144 | }; 145 | } 146 | atomic_rmw!(swap, order); 147 | } 148 | out 149 | } 150 | } 151 | #[cfg(target_feature = "s32c1i")] 152 | impl AtomicCompareExchange for $ty { 153 | #[inline] 154 | unsafe fn atomic_compare_exchange( 155 | dst: *mut MaybeUninit, 156 | old: MaybeUninit, 157 | new: MaybeUninit, 158 | success: Ordering, 159 | failure: Ordering, 160 | ) -> (MaybeUninit, bool) { 161 | let order = crate::utils::upgrade_success_ordering(success, failure); 162 | let out: MaybeUninit; 163 | 164 | // SAFETY: the caller must uphold the safety contract. 165 | unsafe { 166 | let mut r: u32 = 1; 167 | macro_rules! cmpxchg { 168 | ($acquire:tt, $release:tt) => { 169 | asm!( 170 | $release, // fence 171 | "wsr {old}, scompare1", // scompare1 = old 172 | "s32c1i {out}, {dst}, 0", // atomic { _x = *dst; if _x == scompare1 { *dst = out }; out = _x } 173 | $acquire, // fence 174 | "beq {old}, {out}, 2f", // if old == out { jump 'success } 175 | "movi {r}, 0", // r = 0 176 | "2:", // 'success: 177 | dst = in(reg) ptr_reg!(dst), 178 | old = in(reg) old, 179 | out = inout(reg) new => out, 180 | r = inout(reg) r, 181 | out("scompare1") _, 182 | options(nostack, preserves_flags), 183 | ) 184 | }; 185 | } 186 | atomic_rmw!(cmpxchg, order); 187 | crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test 188 | (out, r != 0) 189 | } 190 | } 191 | } 192 | }; 193 | } 194 | 195 | #[rustfmt::skip] 196 | macro_rules! atomic_sub_word { 197 | ($ty:ident, $bits:tt) => { 198 | atomic_load_store!($ty, $bits, "", "u"); 199 | #[cfg(target_feature = "s32c1i")] 200 | impl AtomicSwap for $ty { 201 | #[inline] 202 | unsafe fn atomic_swap( 203 | dst: *mut MaybeUninit, 204 | val: MaybeUninit, 205 | order: Ordering, 206 | ) -> MaybeUninit { 207 | let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); 208 | let mut out: MaybeUninit; 209 | 210 | // SAFETY: the caller must uphold the safety contract. 211 | unsafe { 212 | macro_rules! swap { 213 | ($acquire:tt, $release:tt) => { 214 | // Implement sub-word atomic operations using word-sized CAS loop. 215 | // See also create_sub_word_mask_values. 216 | asm!( 217 | "ssl {shift}", // sar = for_sll(shift & 31) 218 | "sll {mask}, {mask}", // mask <<= sar 219 | "sll {val}, {val}", // val <<= sar 220 | $release, // fence 221 | "l32i.n {out}, {dst}, 0", // atomic { out = *dst } 222 | "2:", // 'retry: 223 | "mov.n {tmp}, {out}", // tmp = out 224 | "wsr {tmp}, scompare1", // scompare1 = tmp 225 | "xor {out}, {tmp}, {val}", // out = tmp ^ val 226 | "and {out}, {out}, {mask}", // out &= mask 227 | "xor {out}, {out}, {tmp}", // out ^= out 228 | "s32c1i {out}, {dst}, 0", // atomic { _x = *dst; if _x == scompare1 { *dst = out }; out = _x } 229 | "bne {tmp}, {out}, 2b", // if tmp != out { jump 'retry } 230 | "ssr {shift}", // sar = for_srl(shift & 31) 231 | "srl {out}, {out}", // out >>= sar 232 | $acquire, // fence 233 | dst = in(reg) ptr_reg!(dst), 234 | val = inout(reg) crate::utils::extend32::$ty::zero(val) => _, 235 | out = out(reg) out, 236 | shift = in(reg) shift, 237 | mask = inout(reg) mask => _, 238 | tmp = out(reg) _, 239 | out("scompare1") _, 240 | out("sar") _, 241 | options(nostack, preserves_flags), 242 | ) 243 | }; 244 | } 245 | atomic_rmw!(swap, order); 246 | } 247 | out 248 | } 249 | } 250 | #[cfg(target_feature = "s32c1i")] 251 | impl AtomicCompareExchange for $ty { 252 | #[inline] 253 | unsafe fn atomic_compare_exchange( 254 | dst: *mut MaybeUninit, 255 | old: MaybeUninit, 256 | new: MaybeUninit, 257 | success: Ordering, 258 | failure: Ordering, 259 | ) -> (MaybeUninit, bool) { 260 | let order = crate::utils::upgrade_success_ordering(success, failure); 261 | let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); 262 | let mut out: MaybeUninit; 263 | 264 | // SAFETY: the caller must uphold the safety contract. 265 | unsafe { 266 | let mut r: u32 = 0; 267 | macro_rules! cmpxchg { 268 | ($acquire:tt, $release:tt) => { 269 | // Implement sub-word atomic operations using word-sized CAS loop. 270 | // See also create_sub_word_mask_values. 271 | asm!( 272 | "ssl {shift}", // sar = for_sll(shift & 31) 273 | "sll {mask}, {mask}", // mask <<= sar 274 | "sll {old}, {old}", // old <<= sar 275 | "sll {new}, {new}", // new <<= sar 276 | $release, // fence 277 | "l32i.n {out}, {dst}, 0", // atomic { out = *dst } 278 | "2:", // 'retry: 279 | "and {tmp}, {out}, {mask}", // tmp = out & mask 280 | "bne {tmp}, {old}, 3f", // if tmp != old { jump 'cmp-fail } 281 | "mov.n {tmp}, {out}", // tmp = out 282 | "wsr {tmp}, scompare1", // scompare1 = tmp 283 | "xor {out}, {tmp}, {new}", // out = tmp ^ new 284 | "and {out}, {out}, {mask}", // out &= mask 285 | "xor {out}, {out}, {tmp}", // out ^= tmp 286 | "s32c1i {out}, {dst}, 0", // atomic { _x = *dst; if _x == scompare1 { *dst = out }; out = _x } 287 | "bne {tmp}, {out}, 2b", // if tmp != out { jump 'retry } 288 | "movi {r}, 1", // r = 1 289 | "3:", // 'cmp-fail: 290 | "ssr {shift}", // sar = for_srl(shift & 31) 291 | "srl {out}, {out}", // out >>= sar 292 | $acquire, // fence 293 | dst = in(reg) ptr_reg!(dst), 294 | old = inout(reg) crate::utils::extend32::$ty::zero(old) => _, 295 | new = inout(reg) crate::utils::extend32::$ty::zero(new) => _, 296 | out = out(reg) out, 297 | shift = in(reg) shift, 298 | mask = inout(reg) mask => _, 299 | tmp = out(reg) _, 300 | r = inout(reg) r, 301 | out("scompare1") _, 302 | out("sar") _, 303 | options(nostack, preserves_flags), 304 | ) 305 | }; 306 | } 307 | atomic_rmw!(cmpxchg, order); 308 | crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test 309 | (out, r != 0) 310 | } 311 | } 312 | } 313 | }; 314 | } 315 | 316 | atomic_sub_word!(u8, "8"); 317 | atomic_sub_word!(u16, "16"); 318 | atomic!(u32); 319 | 320 | // ----------------------------------------------------------------------------- 321 | // cfg macros 322 | 323 | #[macro_export] 324 | macro_rules! cfg_has_atomic_8 { 325 | ($($tt:tt)*) => { $($tt)* }; 326 | } 327 | #[macro_export] 328 | macro_rules! cfg_no_atomic_8 { 329 | ($($tt:tt)*) => {}; 330 | } 331 | #[macro_export] 332 | macro_rules! cfg_has_atomic_16 { 333 | ($($tt:tt)*) => { $($tt)* }; 334 | } 335 | #[macro_export] 336 | macro_rules! cfg_no_atomic_16 { 337 | ($($tt:tt)*) => {}; 338 | } 339 | #[macro_export] 340 | macro_rules! cfg_has_atomic_32 { 341 | ($($tt:tt)*) => { $($tt)* }; 342 | } 343 | #[macro_export] 344 | macro_rules! cfg_no_atomic_32 { 345 | ($($tt:tt)*) => {}; 346 | } 347 | #[macro_export] 348 | macro_rules! cfg_has_atomic_64 { 349 | ($($tt:tt)*) => {}; 350 | } 351 | #[macro_export] 352 | macro_rules! cfg_no_atomic_64 { 353 | ($($tt:tt)*) => { $($tt)* }; 354 | } 355 | #[macro_export] 356 | macro_rules! cfg_has_atomic_128 { 357 | ($($tt:tt)*) => {}; 358 | } 359 | #[macro_export] 360 | macro_rules! cfg_no_atomic_128 { 361 | ($($tt:tt)*) => { $($tt)* }; 362 | } 363 | #[cfg(target_feature = "s32c1i")] 364 | #[macro_export] 365 | macro_rules! cfg_has_atomic_cas { 366 | ($($tt:tt)*) => { $($tt)* }; 367 | } 368 | #[cfg(target_feature = "s32c1i")] 369 | #[macro_export] 370 | macro_rules! cfg_no_atomic_cas { 371 | ($($tt:tt)*) => {}; 372 | } 373 | #[cfg(not(target_feature = "s32c1i"))] 374 | #[macro_export] 375 | macro_rules! cfg_has_atomic_cas { 376 | ($($tt:tt)*) => {}; 377 | } 378 | #[cfg(not(target_feature = "s32c1i"))] 379 | #[macro_export] 380 | macro_rules! cfg_no_atomic_cas { 381 | ($($tt:tt)*) => { $($tt)* }; 382 | } 383 | -------------------------------------------------------------------------------- /src/arch/mips.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT 2 | 3 | /* 4 | MIPS32 and MIPS64 5 | 6 | See "Atomic operation overview by architecture" for atomic operations in this architecture: 7 | https://github.com/taiki-e/atomic-maybe-uninit/blob/HEAD/src/arch/README.md#mips 8 | 9 | Refs: 10 | - The MIPS32® Instruction Set Manual, Revision 6.06 (MD00086) 11 | https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00086-2B-MIPS32BIS-AFP-6.06.pdf 12 | - The MIPS64® Instruction Set Reference Manual, Revision 6.06 (MD00087) 13 | https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00087-2B-MIPS64BIS-AFP-6.06.pdf 14 | - MIPS® Coherence Protocol Specification, Revision 01.01 (MD00605) 15 | https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00605-2B-CMPCOHERE-AFP-01.01.pdf 16 | 17 | Generated asm: 18 | - mips https://godbolt.org/z/KMYoovEWe 19 | - mipsel https://godbolt.org/z/5n1c1M4Ev 20 | - mips64 https://godbolt.org/z/qErPfjKM9 21 | - mips64el https://godbolt.org/z/n1P1vGvfe 22 | */ 23 | 24 | delegate_size!(delegate_all); 25 | 26 | use core::{ 27 | arch::asm, 28 | mem::{self, MaybeUninit}, 29 | sync::atomic::Ordering, 30 | }; 31 | 32 | use crate::raw::{AtomicCompareExchange, AtomicLoad, AtomicStore, AtomicSwap}; 33 | 34 | macro_rules! atomic_rmw { 35 | ($op:ident, $order:ident) => { 36 | match $order { 37 | Ordering::Relaxed => $op!("", ""), 38 | Ordering::Acquire => $op!("sync", ""), 39 | Ordering::Release => $op!("", "sync"), 40 | // AcqRel and SeqCst RMWs are equivalent. 41 | Ordering::AcqRel | Ordering::SeqCst => $op!("sync", "sync"), 42 | _ => unreachable!(), 43 | } 44 | }; 45 | } 46 | 47 | #[rustfmt::skip] 48 | macro_rules! atomic_load_store { 49 | ($ty:ident, $suffix:tt, $l_u_suffix:tt) => { 50 | delegate_signed!(delegate_all, $ty); 51 | impl AtomicLoad for $ty { 52 | #[inline] 53 | unsafe fn atomic_load( 54 | src: *const MaybeUninit, 55 | order: Ordering, 56 | ) -> MaybeUninit { 57 | debug_assert_atomic_unsafe_precondition!(src, $ty); 58 | let out: MaybeUninit; 59 | 60 | // SAFETY: the caller must uphold the safety contract. 61 | unsafe { 62 | macro_rules! atomic_load { 63 | ($acquire:tt) => { 64 | asm!( 65 | ".set push", 66 | ".set noat", 67 | concat!("l", $suffix, " {out}, 0({src})"), // atomic { out = *src } 68 | $acquire, // fence 69 | ".set pop", 70 | src = in(reg) ptr_reg!(src), 71 | out = out(reg) out, 72 | options(nostack, preserves_flags), 73 | ) 74 | }; 75 | } 76 | match order { 77 | Ordering::Relaxed => atomic_load!(""), 78 | // Acquire and SeqCst loads are equivalent. 79 | Ordering::Acquire | Ordering::SeqCst => atomic_load!("sync"), 80 | _ => unreachable!(), 81 | } 82 | } 83 | out 84 | } 85 | } 86 | impl AtomicStore for $ty { 87 | #[inline] 88 | unsafe fn atomic_store( 89 | dst: *mut MaybeUninit, 90 | val: MaybeUninit, 91 | order: Ordering, 92 | ) { 93 | debug_assert_atomic_unsafe_precondition!(dst, $ty); 94 | 95 | // SAFETY: the caller must uphold the safety contract. 96 | unsafe { 97 | macro_rules! store { 98 | ($acquire:tt, $release:tt) => { 99 | asm!( 100 | ".set push", 101 | ".set noat", 102 | $release, // fence 103 | concat!("s", $suffix, " {val}, 0({dst})"), // atomic { *dst = val } 104 | $acquire, // fence 105 | ".set pop", 106 | dst = in(reg) ptr_reg!(dst), 107 | val = in(reg) val, 108 | options(nostack, preserves_flags), 109 | ) 110 | }; 111 | } 112 | atomic_rmw!(store, order); 113 | } 114 | } 115 | } 116 | }; 117 | } 118 | 119 | #[rustfmt::skip] 120 | macro_rules! atomic { 121 | ($ty:ident, $suffix:tt, $ll_sc_suffix:tt) => { 122 | atomic_load_store!($ty, $suffix, ""); 123 | impl AtomicSwap for $ty { 124 | #[inline] 125 | unsafe fn atomic_swap( 126 | dst: *mut MaybeUninit, 127 | val: MaybeUninit, 128 | order: Ordering, 129 | ) -> MaybeUninit { 130 | debug_assert_atomic_unsafe_precondition!(dst, $ty); 131 | let mut out: MaybeUninit; 132 | 133 | // SAFETY: the caller must uphold the safety contract. 134 | unsafe { 135 | macro_rules! swap { 136 | ($acquire:tt, $release:tt) => { 137 | asm!( 138 | ".set push", 139 | ".set noat", 140 | $release, // fence 141 | "2:", // 'retry: 142 | concat!("ll", $ll_sc_suffix, " {out}, 0({dst})"), // atomic { out = *dst; LL = dst } 143 | "move {r}, {val}", // r = val 144 | concat!("sc", $ll_sc_suffix, " {r}, 0({dst})"), // atomic { if LL == dst { *dst = r; r = 1 } else { r = 0 }; LL = None } 145 | "beqz {r}, 2b", // if r == 0 { jump 'retry } 146 | $acquire, // fence 147 | ".set pop", 148 | dst = in(reg) ptr_reg!(dst), 149 | val = in(reg) val, 150 | out = out(reg) out, 151 | r = out(reg) _, 152 | options(nostack, preserves_flags), 153 | ) 154 | }; 155 | } 156 | atomic_rmw!(swap, order); 157 | } 158 | out 159 | } 160 | } 161 | impl AtomicCompareExchange for $ty { 162 | #[inline] 163 | unsafe fn atomic_compare_exchange( 164 | dst: *mut MaybeUninit, 165 | old: MaybeUninit, 166 | new: MaybeUninit, 167 | success: Ordering, 168 | failure: Ordering, 169 | ) -> (MaybeUninit, bool) { 170 | debug_assert_atomic_unsafe_precondition!(dst, $ty); 171 | let order = crate::utils::upgrade_success_ordering(success, failure); 172 | let mut out: MaybeUninit; 173 | 174 | // SAFETY: the caller must uphold the safety contract. 175 | unsafe { 176 | let mut r: crate::utils::RegSize = 0; 177 | macro_rules! cmpxchg { 178 | ($acquire:tt, $release:tt) => { 179 | asm!( 180 | ".set push", 181 | ".set noat", 182 | $release, // fence 183 | "2:", // 'retry: 184 | concat!("ll", $ll_sc_suffix, " {out}, 0({dst})"), // atomic { out = *dst; LL = dst } 185 | "bne {out}, {old}, 3f", // if out != old { jump 'cmp-fail } 186 | "move {r}, {new}", // r = new 187 | concat!("sc", $ll_sc_suffix, " {r}, 0({dst})"), // atomic { if LL == dst { *dst = r; r = 1 } else { r = 0 }; LL = None } 188 | "beqz {r}, 2b", // if r == 0 { jump 'retry } 189 | "3:", // 'cmp-fail: 190 | $acquire, // fence 191 | ".set pop", 192 | dst = in(reg) ptr_reg!(dst), 193 | old = in(reg) old, 194 | new = in(reg) new, 195 | out = out(reg) out, 196 | r = inout(reg) r, 197 | options(nostack, preserves_flags), 198 | ) 199 | }; 200 | } 201 | atomic_rmw!(cmpxchg, order); 202 | crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test 203 | (out, r != 0) 204 | } 205 | } 206 | } 207 | }; 208 | } 209 | 210 | #[rustfmt::skip] 211 | macro_rules! atomic_sub_word { 212 | ($ty:ident, $suffix:tt) => { 213 | atomic_load_store!($ty, $suffix, "u"); 214 | impl AtomicSwap for $ty { 215 | #[inline] 216 | unsafe fn atomic_swap( 217 | dst: *mut MaybeUninit, 218 | val: MaybeUninit, 219 | order: Ordering, 220 | ) -> MaybeUninit { 221 | debug_assert_atomic_unsafe_precondition!(dst, $ty); 222 | let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); 223 | let mut out: MaybeUninit; 224 | 225 | // SAFETY: the caller must uphold the safety contract. 226 | unsafe { 227 | // Implement sub-word atomic operations using word-sized LL/SC loop. 228 | // See also create_sub_word_mask_values. 229 | macro_rules! swap { 230 | ($acquire:tt, $release:tt) => { 231 | asm!( 232 | ".set push", 233 | ".set noat", 234 | "sllv {mask}, {mask}, {shift}", // mask <<= shift & 31 235 | "sllv {val}, {val}, {shift}", // val <<= shift & 31 236 | "nor {mask}, {mask}, $zero", // mask = !mask 237 | $release, // fence 238 | "2:", // 'retry: 239 | "ll {out}, 0({dst})", // atomic { out = *dst; LL = dst } 240 | "and {r}, {out}, {mask}", // r = out & mask 241 | "or {r}, {r}, {val}", // r |= val 242 | "sc {r}, 0({dst})", // atomic { if LL == dst { *dst = r; r = 1 } else { r = 0 }; LL = None } 243 | "beqz {r}, 2b", // if r == 0 { jump 'retry } 244 | "srlv {out}, {out}, {shift}", // out >>= shift & 31 245 | $acquire, // fence 246 | ".set pop", 247 | dst = in(reg) ptr_reg!(dst), 248 | val = inout(reg) crate::utils::extend32::$ty::zero(val) => _, 249 | out = out(reg) out, 250 | shift = in(reg) shift, 251 | mask = inout(reg) mask => _, 252 | r = out(reg) _, 253 | options(nostack, preserves_flags), 254 | ) 255 | }; 256 | } 257 | atomic_rmw!(swap, order); 258 | } 259 | out 260 | } 261 | } 262 | impl AtomicCompareExchange for $ty { 263 | #[inline] 264 | unsafe fn atomic_compare_exchange( 265 | dst: *mut MaybeUninit, 266 | old: MaybeUninit, 267 | new: MaybeUninit, 268 | success: Ordering, 269 | failure: Ordering, 270 | ) -> (MaybeUninit, bool) { 271 | debug_assert_atomic_unsafe_precondition!(dst, $ty); 272 | let order = crate::utils::upgrade_success_ordering(success, failure); 273 | let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); 274 | let mut out: MaybeUninit; 275 | 276 | // SAFETY: the caller must uphold the safety contract. 277 | unsafe { 278 | let mut r: crate::utils::RegSize = 0; 279 | // Implement sub-word atomic operations using word-sized LL/SC loop. 280 | // See also create_sub_word_mask_values. 281 | macro_rules! cmpxchg { 282 | ($acquire:tt, $release:tt) => { 283 | asm!( 284 | ".set push", 285 | ".set noat", 286 | "sllv {mask}, {mask}, {shift}", // mask <<= shift & 31 287 | "sllv {old}, {old}, {shift}", // old <<= shift & 31 288 | "sllv {new}, {new}, {shift}", // new <<= shift & 31 289 | $release, // fence 290 | "2:", // 'retry: 291 | "ll {out}, 0({dst})", // atomic { out = *dst; LL = dst } 292 | "and {tmp}, {out}, {mask}", // tmp = out & mask 293 | "bne {tmp}, {old}, 3f", // if tmp != old { jump 'cmp-fail } 294 | "xor {r}, {out}, {new}", // r = out ^ new 295 | "and {r}, {r}, {mask}", // r &= mask 296 | "xor {r}, {r}, {out}", // r ^= out 297 | "sc {r}, 0({dst})", // atomic { if LL == dst { *dst = r; r = 1 } else { r = 0 }; LL = None } 298 | "beqz {r}, 2b", // if r == 0 { jump 'retry } 299 | "3:", // 'cmp-fail: 300 | "srlv {out}, {out}, {shift}", // out >>= shift & 31 301 | $acquire, // fence 302 | ".set pop", 303 | dst = in(reg) ptr_reg!(dst), 304 | old = inout(reg) crate::utils::extend32::$ty::zero(old) => _, 305 | new = inout(reg) crate::utils::extend32::$ty::zero(new) => _, 306 | out = out(reg) out, 307 | shift = in(reg) shift, 308 | mask = inout(reg) mask => _, 309 | tmp = out(reg) _, 310 | r = inout(reg) r, 311 | options(nostack, preserves_flags), 312 | ) 313 | }; 314 | } 315 | atomic_rmw!(cmpxchg, order); 316 | crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test 317 | (out, r != 0) 318 | } 319 | } 320 | } 321 | }; 322 | } 323 | 324 | atomic_sub_word!(u8, "b"); 325 | atomic_sub_word!(u16, "h"); 326 | atomic!(u32, "w", ""); 327 | #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] 328 | atomic!(u64, "d", "d"); 329 | 330 | // ----------------------------------------------------------------------------- 331 | // cfg macros 332 | 333 | #[macro_export] 334 | macro_rules! cfg_has_atomic_8 { 335 | ($($tt:tt)*) => { $($tt)* }; 336 | } 337 | #[macro_export] 338 | macro_rules! cfg_no_atomic_8 { 339 | ($($tt:tt)*) => {}; 340 | } 341 | #[macro_export] 342 | macro_rules! cfg_has_atomic_16 { 343 | ($($tt:tt)*) => { $($tt)* }; 344 | } 345 | #[macro_export] 346 | macro_rules! cfg_no_atomic_16 { 347 | ($($tt:tt)*) => {}; 348 | } 349 | #[macro_export] 350 | macro_rules! cfg_has_atomic_32 { 351 | ($($tt:tt)*) => { $($tt)* }; 352 | } 353 | #[macro_export] 354 | macro_rules! cfg_no_atomic_32 { 355 | ($($tt:tt)*) => {}; 356 | } 357 | #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] 358 | #[macro_export] 359 | macro_rules! cfg_has_atomic_64 { 360 | ($($tt:tt)*) => {}; 361 | } 362 | #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] 363 | #[macro_export] 364 | macro_rules! cfg_no_atomic_64 { 365 | ($($tt:tt)*) => { $($tt)* }; 366 | } 367 | #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] 368 | #[macro_export] 369 | macro_rules! cfg_has_atomic_64 { 370 | ($($tt:tt)*) => { $($tt)* }; 371 | } 372 | #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] 373 | #[macro_export] 374 | macro_rules! cfg_no_atomic_64 { 375 | ($($tt:tt)*) => {}; 376 | } 377 | #[macro_export] 378 | macro_rules! cfg_has_atomic_128 { 379 | ($($tt:tt)*) => {}; 380 | } 381 | #[macro_export] 382 | macro_rules! cfg_no_atomic_128 { 383 | ($($tt:tt)*) => { $($tt)* }; 384 | } 385 | #[macro_export] 386 | macro_rules! cfg_has_atomic_cas { 387 | ($($tt:tt)*) => { $($tt)* }; 388 | } 389 | #[macro_export] 390 | macro_rules! cfg_no_atomic_cas { 391 | ($($tt:tt)*) => {}; 392 | } 393 | --------------------------------------------------------------------------------