diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 00000000..86281707 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --bin xtask --" diff --git a/.travis.yml b/.travis.yml index bbe3d487..7f25eae8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,7 +72,6 @@ before_install: set -e install: - bash ci/install.sh - - export PATH="$PATH:$PWD/gcc/bin" script: - bash ci/script.sh diff --git a/Cargo.toml b/Cargo.toml index 2e55f6ad..eddc6fab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,14 @@ volatile-register = "0.2.0" bitfield = "0.13.2" embedded-hal = "0.2.4" +[features] +cm7-r0p1 = [] +inline-asm = [] +linker-plugin-lto = [] + +[workspace] +members = ["xtask"] + [package.metadata.docs.rs] targets = [ "thumbv8m.main-none-eabihf", @@ -31,7 +39,3 @@ targets = [ "thumbv8m.base-none-eabi", "thumbv8m.main-none-eabi" ] - -[features] -cm7-r0p1 = [] -inline-asm = [] diff --git a/asm-cm7-r0p1.s b/asm-cm7-r0p1.s deleted file mode 100644 index 28c3384e..00000000 --- a/asm-cm7-r0p1.s +++ /dev/null @@ -1,35 +0,0 @@ - .cfi_sections .debug_frame - - .section .text.__basepri_max_cm7_r0p1 - .global __basepri_max_cm7_r0p1 - .syntax unified - .thumb_func - .cfi_startproc -__basepri_max_cm7_r0p1: - mrs r1, PRIMASK - cpsid i - tst.w r1, #1 - msr BASEPRI_MAX, r0 - it ne - bxne lr - cpsie i - bx lr - .cfi_endproc - .size __basepri_max_cm7_r0p1, . - __basepri_max_cm7_r0p1 - - .section .text.__basepri_w_cm7_r0p1 - .global __basepri_w_cm7_r0p1 - .syntax unified - .thumb_func - .cfi_startproc -__basepri_w_cm7_r0p1: - mrs r1, PRIMASK - cpsid i - tst.w r1, #1 - msr BASEPRI, r0 - it ne - bxne lr - cpsie i - bx lr - .cfi_endproc - .size __basepri_w_cm7_r0p1, . - __basepri_w_cm7_r0p1 diff --git a/asm-fpu.s b/asm-fpu.s deleted file mode 100644 index 417d199a..00000000 --- a/asm-fpu.s +++ /dev/null @@ -1,21 +0,0 @@ - .cfi_sections .debug_frame - - .section .text.__get_FPSCR - .global __get_FPSCR - .thumb_func - .cfi_startproc -__get_FPSCR: - vmrs r0, fpscr - bx lr - .cfi_endproc - .size __get_FPSCR, . - __get_FPSCR - - .section .text.__set_FPSCR - .global __set_FPSCR - .thumb_func - .cfi_startproc -__set_FPSCR: - vmsr fpscr, r0 - bx lr - .cfi_endproc - .size __set_FPSCR, . - __set_FPSCR diff --git a/asm-toolchain b/asm-toolchain new file mode 100644 index 00000000..a36829b6 --- /dev/null +++ b/asm-toolchain @@ -0,0 +1 @@ +nightly-2020-08-26 diff --git a/asm-v7.s b/asm-v7.s deleted file mode 100644 index 17d7110e..00000000 --- a/asm-v7.s +++ /dev/null @@ -1,78 +0,0 @@ - .syntax unified - .cfi_sections .debug_frame - - .section .text.__basepri_max - .global __basepri_max - .thumb_func - .cfi_startproc -__basepri_max: - msr BASEPRI_MAX, r0 - bx lr - .cfi_endproc - .size __basepri_max, . - __basepri_max - - .section .text.__basepri_r - .global __basepri_r - .thumb_func - .cfi_startproc -__basepri_r: - mrs r0, BASEPRI - bx lr - .cfi_endproc - .size __basepri_r, . - __basepri_r - - .section .text.__basepri_w - .global __basepri_w - .thumb_func - .cfi_startproc -__basepri_w: - msr BASEPRI, r0 - bx lr - .cfi_endproc - .size __basepri_w, . - __basepri_w - - .section .text.__faultmask - .global __faultmask - .thumb_func - .cfi_startproc -__faultmask: - mrs r0, FAULTMASK - bx lr - .cfi_endproc - .size __faultmask, . - __faultmask - - .section .text.__enable_icache - .global __enable_icache - .thumb_func - .cfi_startproc -__enable_icache: - ldr r0, =0xE000ED14 @ CCR - mrs r2, PRIMASK @ save critical nesting info - cpsid i @ mask interrupts - ldr r1, [r0] @ read CCR - orr.w r1, r1, #(1 << 17) @ Set bit 17, IC - str r1, [r0] @ write it back - dsb @ ensure store completes - isb @ synchronize pipeline - msr PRIMASK, r2 @ unnest critical section - bx lr - .cfi_endproc - .size __enable_icache, . - __enable_icache - - .section .text.__enable_dcache - .global __enable_dcache - .thumb_func - .cfi_startproc -__enable_dcache: - ldr r0, =0xE000ED14 @ CCR - mrs r2, PRIMASK @ save critical nesting info - cpsid i @ mask interrupts - ldr r1, [r0] @ read CCR - orr.w r1, r1, #(1 << 16) @ Set bit 16, DC - str r1, [r0] @ write it back - dsb @ ensure store completes - isb @ synchronize pipeline - msr PRIMASK, r2 @ unnest critical section - bx lr - .cfi_endproc - .size __enable_dcache, . - __enable_dcache diff --git a/asm-v8-main.s b/asm-v8-main.s deleted file mode 100644 index 1fad1555..00000000 --- a/asm-v8-main.s +++ /dev/null @@ -1,43 +0,0 @@ - - .cfi_sections .debug_frame - - .section .text.__msplim_r - .global __msplim_r - .thumb_func - .cfi_startproc -__msplim_r: - mrs r0, MSPLIM - bx lr - .cfi_endproc - .size __msplim_r, . - __msplim_r - - .section .text.__msplim_w - .global __msplim_w - .thumb_func - .cfi_startproc -__msplim_w: - msr MSPLIM, r0 - bx lr - .cfi_endproc - .size __msplim_w, . - __msplim_w - - .section .text.__psplim_r - .global __psplim_r - .thumb_func - .cfi_startproc -__psplim_r: - mrs r0, PSPLIM - bx lr - .cfi_endproc - .size __psplim_r, . - __psplim_r - - .section .text.__psplim_w - .global __psplim_w - .thumb_func - .cfi_startproc -__psplim_w: - msr PSPLIM, r0 - bx lr - .cfi_endproc - .size __psplim_w, . - __psplim_w - diff --git a/asm-v8.s b/asm-v8.s deleted file mode 100644 index 7d3a8c94..00000000 --- a/asm-v8.s +++ /dev/null @@ -1,42 +0,0 @@ - .cfi_sections .debug_frame - - .section .text.__tt - .global __tt - .thumb_func - .cfi_startproc -__tt: - tt r0, r0 - bx lr - .cfi_endproc - .size __tt, . - __tt - - .section .text.__ttt - .global __ttt - .thumb_func - .cfi_startproc -__ttt: - ttt r0, r0 - bx lr - .cfi_endproc - .size __ttt, . - __ttt - - .section .text.__tta - .global __tta - .thumb_func - .cfi_startproc -__tta: - tta r0, r0 - bx lr - .cfi_endproc - .size __tta, . - __tta - - - .section .text.__ttat - .global __ttat - .thumb_func - .cfi_startproc -__ttat: - ttat r0, r0 - bx lr - .cfi_endproc - .size __ttat, . - __ttat diff --git a/asm.rs b/asm.rs new file mode 100644 index 00000000..f5b0179b --- /dev/null +++ b/asm.rs @@ -0,0 +1,346 @@ +//! Assembly stubs for the `cortex-m` crate. +//! +//! We use this file to precompile some assembly stubs into the static libraries you can find in +//! `bin`. Apps using the `cortex-m` crate then link against those static libraries and don't need +//! to build this file themselves. +//! +//! Nowadays the assembly stubs are no longer actual assembly files, but actually just this Rust +//! file `asm.rs` that uses unstable inline assembly, coupled with the `xtask` tool to invoke rustc +//! and build the files. +//! +//! Precompiling this to a static lib allows users to call assembly routines from stable Rust, but +//! also perform [linker plugin LTO] with the precompiled artifacts to completely inline the +//! assembly routines into their code, which brings the "outline assembly" on par with "real" inline +//! assembly. +//! +//! For developers and contributors to `cortex-m`, this setup means that they don't have to install +//! any binutils, assembler, or C compiler to hack on the crate. All they need is to run `cargo +//! xtask assemble` to rebuild the archives from this file. +//! +//! Cool, right? +//! +//! # Rust version management +//! +//! Since inline assembly is still unstable, and we want to ensure that the created blobs are +//! up-to-date in CI, we have to pin the nightly version we use for this. The nightly toolchain is +//! stored in `asm-toolchain`. +//! +//! The `cargo xtask` automation will automatically install the `asm-toolchain` as well as all +//! Cortex-M targets needed to generate the blobs. +//! +//! [linker plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html + +#![feature(asm)] +#![no_std] +#![crate_type = "staticlib"] +#![deny(warnings)] + +#[no_mangle] +pub unsafe extern "C" fn __bkpt() { + asm!("bkpt"); +} + +#[no_mangle] +pub unsafe extern "C" fn __control_r() -> u32 { + let r; + asm!("mrs {}, CONTROL", out(reg) r); + r +} + +#[no_mangle] +pub unsafe extern "C" fn __control_w(w: u32) { + asm!("msr CONTROL, {}", in(reg) w); +} + +#[no_mangle] +pub unsafe extern "C" fn __cpsid() { + asm!("cpsid i"); +} + +#[no_mangle] +pub unsafe extern "C" fn __cpsie() { + asm!("cpsie i"); +} + +#[no_mangle] +pub unsafe extern "C" fn __delay(cyc: u32) { + asm!(" + 1: + nop + subs {}, #1 + bne 1b + // Branch to 1 instead of __delay does not generate R_ARM_THM_JUMP8 relocation, which breaks + // linking on the thumbv6m-none-eabi target + ", in(reg) cyc); +} + +// FIXME do we need compiler fences here or should we expect them in the caller? +#[no_mangle] +pub unsafe extern "C" fn __dmb() { + asm!("dmb 0xF"); +} + +#[no_mangle] +pub unsafe extern "C" fn __dsb() { + asm!("dsb 0xF"); +} + +#[no_mangle] +pub unsafe extern "C" fn __isb() { + asm!("isb 0xF"); +} + +#[no_mangle] +pub unsafe extern "C" fn __msp_r() -> u32 { + let r; + asm!("mrs {}, MSP", out(reg) r); + r +} + +#[no_mangle] +pub unsafe extern "C" fn __msp_w(val: u32) { + asm!("msr MSP, {}", in(reg) val); +} + +#[no_mangle] +pub unsafe extern "C" fn __nop() { + // NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate + // the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N + // nops when they call `nop` N times, let's not add that option. + asm!("nop"); +} + +#[no_mangle] +pub unsafe extern "C" fn __primask() -> u32 { + // FIXME: rename to __primask_r + let r; + asm!("mrs {}, PRIMASK", out(reg) r); + r +} + +#[no_mangle] +pub unsafe extern "C" fn __psp_r() -> u32 { + let r; + asm!("mrs {}, PSP", out(reg) r); + r +} + +#[no_mangle] +pub unsafe extern "C" fn __psp_w(val: u32) { + asm!("msr PSP, {}", in(reg) val); +} + +#[no_mangle] +pub unsafe extern "C" fn __sev() { + asm!("sev"); +} + +#[no_mangle] +pub unsafe extern "C" fn __udf() { + asm!("udf #0"); +} + +#[no_mangle] +pub unsafe extern "C" fn __wfe() { + asm!("wfe"); +} + +#[no_mangle] +pub unsafe extern "C" fn __wfi() { + asm!("wfi"); +} + +#[cfg(armv7m)] +pub mod v7m { + #[no_mangle] + pub unsafe extern "C" fn __basepri_max(val: u8) { + asm!("msr BASEPRI_MAX, {}", in(reg) val); + } + + #[no_mangle] + pub unsafe extern "C" fn __basepri_r() -> u8 { + let r; + asm!("mrs {}, BASEPRI", out(reg) r); + r + } + + #[no_mangle] + pub unsafe extern "C" fn __basepri_w(val: u8) { + asm!("msr BASEPRI, {}", in(reg) val); + } + + #[no_mangle] + pub unsafe extern "C" fn __faultmask() -> u32 { + let r; + asm!("mrs {}, FAULTMASK", out(reg) r); + r + } + + // FIXME: compiler_fences necessary? + #[no_mangle] + pub unsafe extern "C" fn __enable_icache() { + asm!( + " + ldr r0, =0xE000ED14 @ CCR + mrs r2, PRIMASK @ save critical nesting info + cpsid i @ mask interrupts + ldr r1, [r0] @ read CCR + orr.w r1, r1, #(1 << 17) @ Set bit 17, IC + str r1, [r0] @ write it back + dsb @ ensure store completes + isb @ synchronize pipeline + msr PRIMASK, r2 @ unnest critical section + ", + out("r0") _, + out("r1") _, + out("r2") _, + ); + } + + #[no_mangle] + pub unsafe extern "C" fn __enable_dcache() { + asm!( + " + ldr r0, =0xE000ED14 @ CCR + mrs r2, PRIMASK @ save critical nesting info + cpsid i @ mask interrupts + ldr r1, [r0] @ read CCR + orr.w r1, r1, #(1 << 16) @ Set bit 16, DC + str r1, [r0] @ write it back + dsb @ ensure store completes + isb @ synchronize pipeline + msr PRIMASK, r2 @ unnest critical section + ", + out("r0") _, + out("r1") _, + out("r2") _, + ); + } +} + +#[cfg(armv7em)] +mod v7em { + #[no_mangle] + pub unsafe extern "C" fn __basepri_max_cm7_r0p1(val: u8) { + asm!( + " + mrs r1, PRIMASK + cpsid i + tst.w r1, #1 + msr BASEPRI_MAX, {} + it ne + bxne lr + cpsie i + ", + in(reg) val, + out("r1") _, + ); + } + + #[no_mangle] + pub unsafe extern "C" fn __basepri_w_cm7_r0p1(val: u8) { + asm!( + " + mrs r1, PRIMASK + cpsid i + tst.w r1, #1 + msr BASEPRI, {} + it ne + bxne lr + cpsie i + ", + in(reg) val, + out("r1") _, + ); + } +} + +/// Baseline and Mainline. +#[cfg(armv8m)] +pub mod v8m { + #[no_mangle] + pub unsafe extern "C" fn __tt(mut target: u32) -> u32 { + asm!("tt {target}, {target}", target = inout(reg) target); + target + } + + #[no_mangle] + pub unsafe extern "C" fn __ttt(mut target: u32) -> u32 { + asm!("ttt {target}, {target}", target = inout(reg) target); + target + } + + #[no_mangle] + pub unsafe extern "C" fn __tta(mut target: u32) -> u32 { + asm!("tta {target}, {target}", target = inout(reg) target); + target + } + + #[no_mangle] + pub unsafe extern "C" fn __ttat(mut target: u32) -> u32 { + asm!("ttat {target}, {target}", target = inout(reg) target); + target + } +} + +/// Mainline only. +#[cfg(armv8m_main)] +pub mod v8m_main { + #[no_mangle] + pub unsafe extern "C" fn __msplim_r() -> u32 { + let r; + asm!("mrs {}, MSPLIM", out(reg) r); + r + } + + #[no_mangle] + pub unsafe extern "C" fn __msplim_w(val: u32) { + asm!("msr MSPLIM, {}", in(reg) val); + } + + #[no_mangle] + pub unsafe extern "C" fn __psplim_r() -> u32 { + let r; + asm!("mrs {}, PSPLIM", out(reg) r); + r + } + + #[no_mangle] + pub unsafe extern "C" fn __psplim_w(val: u32) { + asm!("msr PSPLIM, {}", in(reg) val); + } +} + +/// All targets with FPU. +#[cfg(has_fpu)] +pub mod fpu { + #[no_mangle] + pub unsafe extern "C" fn __get_FPSCR() -> u32 { + let r; + asm!("vmrs {}, fpscr", out(reg) r); + r + } + + #[no_mangle] + pub unsafe extern "C" fn __set_FPSCR(val: u32) { + asm!("vmsr fpscr, {}", in(reg) val); + } +} + +/// We *must* define a panic handler here, even though nothing here should ever be able to panic. +/// +/// We prove that nothing will ever panic by calling a function that doesn't exist. If the panic +/// handler gets linked in, this causes a linker error. We always build this file with optimizations +/// enabled, but even without them the panic handler should never be linked in. +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + extern "C" { + #[link_name = "cortex-m internal error: panic handler not optimized out, please file an \ + issue at https://github.com/rust-embedded/cortex-m"] + fn __cortex_m_should_not_panic() -> !; + } + + unsafe { + __cortex_m_should_not_panic(); + } +} diff --git a/asm.s b/asm.s deleted file mode 100644 index 3d1a54bb..00000000 --- a/asm.s +++ /dev/null @@ -1,192 +0,0 @@ - .cfi_sections .debug_frame - - .section .text.__bkpt - .global __bkpt - .thumb_func - .cfi_startproc -__bkpt: - bkpt - bx lr - .cfi_endproc - .size __bkpt, . - __bkpt - - .section .text.__control_r - .global __control_r - .thumb_func - .cfi_startproc -__control_r: - mrs r0, CONTROL - bx lr - .cfi_endproc - .size __control_r, . - __control_r - - .section .text.__control_w - .global __control_w - .thumb_func - .cfi_startproc -__control_w: - msr CONTROL, r0 - bx lr - .cfi_endproc - .size __control_w, . - __control_w - - - .section .text.__cpsid - .global __cpsid - .thumb_func - .cfi_startproc -__cpsid: - cpsid i - bx lr - .cfi_endproc - .size __cpsid, . - __cpsid - - .section .text.__cpsie - .global __cpsie - .thumb_func - .cfi_startproc -__cpsie: - cpsie i - bx lr - .cfi_endproc - .size __cpsie, . - __cpsie - - .section .text.__delay - .global __delay - .syntax unified - .thumb_func - .cfi_startproc -__delay: -1: - nop - subs r0, #1 - bne 1b // Branch to 1 instead of __delay does not generate R_ARM_THM_JUMP8 relocation, which breaks linking on the thumbv6m-none-eabi target - bx lr - .cfi_endproc - .size __delay, . - __delay - - .section .text.__dmb - .global __dmb - .thumb_func - .cfi_startproc -__dmb: - dmb 0xF - bx lr - .cfi_endproc - .size __dmb, . - __dmb - - .section .text.__dsb - .global __dsb - .thumb_func - .cfi_startproc -__dsb: - dsb 0xF - bx lr - .cfi_endproc - .size __dsb, . - __dsb - - .section .text.__isb - .global __isb - .thumb_func - .cfi_startproc -__isb: - isb 0xF - bx lr - .cfi_endproc - .size __isb, . - __isb - - .section .text.__msp_r - .global __msp_r - .thumb_func - .cfi_startproc -__msp_r: - mrs r0, MSP - bx lr - .cfi_endproc - .size __msp_r, . - __msp_r - - .section .text.__msp_w - .global __msp_w - .thumb_func -__msp_w: - msr MSP, r0 - bx lr - .size __msp_w, . - __msp_w - - .section .text.__nop - .global __nop - .thumb_func - .cfi_startproc -__nop: - bx lr - .cfi_endproc - .size __nop, . - __nop - - .section .text.__primask - .global __primask - .thumb_func - .cfi_startproc -__primask: - mrs r0, PRIMASK - bx lr - .cfi_endproc - .size __primask, . - __primask - - .section .text.__psp_r - .global __psp_r - .thumb_func - .cfi_startproc -__psp_r: - mrs r0, PSP - bx lr - .cfi_endproc - .size __psp_r, . - __psp_r - - .section .text.__psp_w - .global __psp_w - .thumb_func -__psp_w: - msr PSP, r0 - bx lr - .size __psp_w, . - __psp_w - - .section .text.__sev - .global __sev - .thumb_func - .cfi_startproc -__sev: - sev - bx lr - .cfi_endproc - .size __sev, . - __sev - - - .section .text.__udf - .global __udf - .thumb_func - .cfi_startproc -__udf: - udf - .cfi_endproc - .size __udf, . - __udf - - .section .text.__wfe - .global __wfe - .thumb_func - .cfi_startproc -__wfe: - wfe - bx lr - .cfi_endproc - .size __wfe, . - __wfe - - - .section .text.__wfi - .global __wfi - .thumb_func - .cfi_startproc -__wfi: - wfi - bx lr - .cfi_endproc - .size __wfi, . - __wfi diff --git a/assemble.sh b/assemble.sh deleted file mode 100755 index 070481a7..00000000 --- a/assemble.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -# cflags taken from cc 1.0.22 - -crate=cortex-m - -# remove existing blobs because otherwise this will append object files to the old blobs -rm -f bin/*.a - -pwd=$(pwd) -flags=(-g --debug-prefix-map "$pwd=.") - -arm-none-eabi-as "${flags[@]}" -march=armv6s-m asm.s -o bin/$crate.o -ar crs bin/thumbv6m-none-eabi.a bin/$crate.o - -arm-none-eabi-as "${flags[@]}" -march=armv7-m asm.s -o bin/$crate.o -arm-none-eabi-as "${flags[@]}" -march=armv7-m asm-v7.s -o bin/$crate-v7.o -ar crs bin/thumbv7m-none-eabi.a bin/$crate.o bin/$crate-v7.o - -arm-none-eabi-as "${flags[@]}" -march=armv7e-m asm.s -o bin/$crate.o -arm-none-eabi-as "${flags[@]}" -march=armv7e-m asm-fpu.s -mfpu=fpv4-sp-d16 -o bin/$crate-v7-fpu.o -arm-none-eabi-as "${flags[@]}" -march=armv7e-m asm-cm7-r0p1.s -o bin/$crate-cm7-r0p1.o -arm-none-eabi-as "${flags[@]}" -march=armv7e-m asm-v7.s -o bin/$crate-v7.o -ar crs bin/thumbv7em-none-eabi.a bin/$crate.o bin/$crate-v7.o bin/$crate-cm7-r0p1.o -ar crs bin/thumbv7em-none-eabihf.a bin/$crate.o bin/$crate-v7.o bin/$crate-cm7-r0p1.o bin/$crate-v7-fpu.o - -arm-none-eabi-as "${flags[@]}" -march=armv8-m.base asm.s -o bin/$crate.o -arm-none-eabi-as "${flags[@]}" -march=armv8-m.base asm-v8.s -o bin/$crate-v8.o -ar crs bin/thumbv8m.base-none-eabi.a bin/$crate.o bin/$crate-v8.o - -arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm.s -o bin/$crate.o -arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm-v7.s -o bin/$crate-v7.o -arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm-v8.s -o bin/$crate-v8.o -arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm-v8-main.s -o bin/$crate-v8-main.o -arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm-fpu.s -mfpu=fpv5-sp-d16 -o bin/$crate-v8-fpu.o -ar crs bin/thumbv8m.main-none-eabi.a bin/$crate.o bin/$crate-v7.o bin/$crate-v8.o bin/$crate-v8-main.o -ar crs bin/thumbv8m.main-none-eabihf.a bin/$crate.o bin/$crate-v7.o bin/$crate-v8.o bin/$crate-v8-main.o bin/$crate-v8-fpu.o - -rm bin/$crate.o -rm bin/$crate-v7.o -rm bin/$crate-v7-fpu.o -rm bin/$crate-v8-fpu.o -rm bin/$crate-cm7-r0p1.o -rm bin/$crate-v8.o -rm bin/$crate-v8-main.o diff --git a/bin/thumbv6m-none-eabi-lto.a b/bin/thumbv6m-none-eabi-lto.a new file mode 100644 index 00000000..4d2f02cf Binary files /dev/null and b/bin/thumbv6m-none-eabi-lto.a differ diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a index b2f72b4c..beedd73b 100644 Binary files a/bin/thumbv6m-none-eabi.a and b/bin/thumbv6m-none-eabi.a differ diff --git a/bin/thumbv7em-none-eabi-lto.a b/bin/thumbv7em-none-eabi-lto.a new file mode 100644 index 00000000..7b8a4f87 Binary files /dev/null and b/bin/thumbv7em-none-eabi-lto.a differ diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a index 5d5c48fb..588e5cdb 100644 Binary files a/bin/thumbv7em-none-eabi.a and b/bin/thumbv7em-none-eabi.a differ diff --git a/bin/thumbv7em-none-eabihf-lto.a b/bin/thumbv7em-none-eabihf-lto.a new file mode 100644 index 00000000..4efadd80 Binary files /dev/null and b/bin/thumbv7em-none-eabihf-lto.a differ diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a index 1570f837..fd08ba59 100644 Binary files a/bin/thumbv7em-none-eabihf.a and b/bin/thumbv7em-none-eabihf.a differ diff --git a/bin/thumbv7m-none-eabi-lto.a b/bin/thumbv7m-none-eabi-lto.a new file mode 100644 index 00000000..28e5860a Binary files /dev/null and b/bin/thumbv7m-none-eabi-lto.a differ diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a index 51ab7103..4559ad4a 100644 Binary files a/bin/thumbv7m-none-eabi.a and b/bin/thumbv7m-none-eabi.a differ diff --git a/bin/thumbv8m.base-none-eabi-lto.a b/bin/thumbv8m.base-none-eabi-lto.a new file mode 100644 index 00000000..ee467925 Binary files /dev/null and b/bin/thumbv8m.base-none-eabi-lto.a differ diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a index 33bcff24..26d25bd3 100644 Binary files a/bin/thumbv8m.base-none-eabi.a and b/bin/thumbv8m.base-none-eabi.a differ diff --git a/bin/thumbv8m.main-none-eabi-lto.a b/bin/thumbv8m.main-none-eabi-lto.a new file mode 100644 index 00000000..e3aa0cf1 Binary files /dev/null and b/bin/thumbv8m.main-none-eabi-lto.a differ diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a index 1b2418da..10bbf53f 100644 Binary files a/bin/thumbv8m.main-none-eabi.a and b/bin/thumbv8m.main-none-eabi.a differ diff --git a/bin/thumbv8m.main-none-eabihf-lto.a b/bin/thumbv8m.main-none-eabihf-lto.a new file mode 100644 index 00000000..1c25494b Binary files /dev/null and b/bin/thumbv8m.main-none-eabihf-lto.a differ diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a index 312dcd23..06517880 100644 Binary files a/bin/thumbv8m.main-none-eabihf.a and b/bin/thumbv8m.main-none-eabihf.a differ diff --git a/build.rs b/build.rs index d53dea5c..dc9b3a02 100644 --- a/build.rs +++ b/build.rs @@ -7,10 +7,17 @@ fn main() { let name = env::var("CARGO_PKG_NAME").unwrap(); if target.starts_with("thumb") { + let suffix = if env::var_os("CARGO_FEATURE_LINKER_PLUGIN_LTO").is_some() { + "-lto" + } else { + "" + }; + fs::copy( - format!("bin/{}.a", target), + format!("bin/{}{}.a", target, suffix), out_dir.join(format!("lib{}.a", name)), - ).unwrap(); + ) + .unwrap(); println!("cargo:rustc-link-lib=static={}", name); println!("cargo:rustc-link-search={}", out_dir.display()); @@ -25,7 +32,7 @@ fn main() { } else if target.starts_with("thumbv7em-") { println!("cargo:rustc-cfg=cortex_m"); println!("cargo:rustc-cfg=armv7m"); - println!("cargo:rustc-cfg=armv7em"); // (not currently used) + println!("cargo:rustc-cfg=armv7em"); // (not currently used) } else if target.starts_with("thumbv8m.base") { println!("cargo:rustc-cfg=cortex_m"); println!("cargo:rustc-cfg=armv8m"); diff --git a/check-blobs.sh b/check-blobs.sh deleted file mode 100755 index 166b4a4c..00000000 --- a/check-blobs.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -# Checks that the blobs are up to date with the committed assembly files - -set -euxo pipefail - -for lib in bin/*.a; do - filename=$(basename "$lib") - arm-none-eabi-objdump -Cd "$lib" > "bin/${filename%.a}.before" -done - -./assemble.sh - -for lib in bin/*.a; do - filename=$(basename "$lib") - arm-none-eabi-objdump -Cd "$lib" > "bin/${filename%.a}.after" -done - -for cksum in bin/*.after; do - diff -u "$cksum" "${cksum%.after}.before" -done diff --git a/ci/install.sh b/ci/install.sh index 371cc8e4..fb3e52dc 100644 --- a/ci/install.sh +++ b/ci/install.sh @@ -6,10 +6,6 @@ main() { rustup target add $TARGET ;; esac - - mkdir gcc - - curl -L https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2?revision=bc2c96c0-14b5-4bb4-9f18-bceb4050fee7?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,7-2018-q2-update | tar --strip-components=1 -C gcc -xj } main diff --git a/ci/script.sh b/ci/script.sh index 7c309935..fc1e7547 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -29,7 +29,7 @@ main() { esac if [ $TARGET = x86_64-unknown-linux-gnu ]; then - ./check-blobs.sh + cargo xtask check-blobs fi if [ $TRAVIS_RUST_VERSION = nightly ]; then diff --git a/src/lib.rs b/src/lib.rs index 5dbb8f56..723816aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,32 @@ //! //! The disadvantage is that `inline-asm` requires a nightly toolchain. //! +//! ## `cm7-r0p1` +//! +//! This feature enables workarounds for errata found on Cortex-M7 chips with revision r0p1. Some +//! functions in this crate only work correctly on those chips if this Cargo feature is enabled +//! (the functions are documented accordingly). +//! +//! ## `linker-plugin-lto` +//! +//! This feature links against prebuilt assembly blobs that are compatible with [Linker-Plugin LTO]. +//! This allows inlining assembly routines into the caller, even without the `inline-asm` feature, +//! and works on stable Rust (but note the drawbacks below!). +//! +//! If you want to use this feature, you need to be aware of a few things: +//! +//! - You need to make sure that `-Clinker-plugin-lto` is passed to rustc. Please refer to the +//! [Linker-Plugin LTO] documentation for details. +//! +//! - You have to use a Rust version whose LLVM version is compatible with the toolchain in +//! `asm-toolchain`. +//! +//! - Due to a [Rust bug][rust-lang/rust#75940], this option does not work with optimization +//! levels `s` and `z`. +//! +//! [Linker-Plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html +//! [rust-lang/rust#75940]: https://github.com/rust-lang/rust/issues/75940 +//! //! # Minimum Supported Rust Version (MSRV) //! //! This crate is guaranteed to compile on stable Rust 1.31 and up. It *might* diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 00000000..9d35164d --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "xtask" +version = "0.0.0" +authors = ["The Cortex-M Team "] +edition = "2018" +publish = false + +[dependencies] +ar = "0.8.0" diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 00000000..ab59f57b --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,216 @@ +//! `cargo xtask` automation. +//! +//! Please refer to for an explanation of the concept. +//! +//! Also see the docs in `asm.rs`. + +use process::Stdio; +use std::env::{self, current_dir}; +use std::{ + collections::BTreeMap, + fs::{self, File}, + process::{self, Command}, +}; + +fn toolchain() -> String { + fs::read_to_string("asm-toolchain") + .unwrap() + .trim() + .to_string() +} + +fn rustc() -> Command { + let mut cmd = Command::new("rustc"); + cmd.arg(format!("+{}", toolchain())); + cmd +} + +fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { + let mut cmd = rustc(); + + // Set the codegen target. + cmd.arg("--target").arg(target); + // Set all the `--cfg` directives for the target. + cmd.args(cfgs.iter().map(|cfg| format!("--cfg={}", cfg))); + + // We want some level of debuginfo to allow unwinding through the functions. + cmd.arg("-g"); + // We always optimize the assembly shims. There's not really any reason not to. + cmd.arg("-O"); + + // rustc will usually add frame pointers by default to aid with debugging, but that is a high + // overhead for the tiny assembly routines. + cmd.arg("-Cforce-frame-pointers=no"); + + // We don't want any system-specific paths to show up since we ship the result to other users. + // Add `--remap-path-prefix $(pwd)=.`. + let mut dir = current_dir().unwrap().as_os_str().to_os_string(); + dir.push("=."); + cmd.arg("--remap-path-prefix").arg(dir); + + // We let rustc build a single object file, not a staticlib, since the latter pulls in loads of + // code that will never be used (`compiler_builtins` and `core::fmt`, etc.). We build the static + // archive by hand after compiling. + cmd.arg("--emit=obj"); + + if plugin_lto { + // Make artifacts compatible with Linker-Plugin LTO (and incompatible with everything else). + cmd.arg("-Clinker-plugin-lto"); + } + + let file_stub = if plugin_lto { + format!("{}-lto", target) + } else { + target.to_string() + }; + + let obj_file = format!("bin/{}.o", file_stub); + + // Pass output and input file. + cmd.arg("-o").arg(&obj_file); + cmd.arg("asm.rs"); + + println!("{:?}", cmd); + let status = cmd.status().unwrap(); + assert!(status.success()); + + // Archive `target.o` -> `bin/target.a`. + let mut builder = ar::Builder::new(File::create(format!("bin/{}.a", file_stub)).unwrap()); + + // Use `append`, not `append_path`, to avoid adding any filesystem metadata (modification times, + // etc.). + let file = fs::read(&obj_file).unwrap(); + builder + .append( + &ar::Header::new(obj_file.as_bytes().to_vec(), file.len() as u64), + &*file, + ) + .unwrap(); + + fs::remove_file(&obj_file).unwrap(); +} + +fn assemble(target: &str, cfgs: &[&str]) { + assemble_really(target, cfgs, false); + assemble_really(target, cfgs, true); +} + +// `--target` -> `--cfg` list (mirrors what `build.rs` does). +static TARGETS: &[(&str, &[&str])] = &[ + ("thumbv6m-none-eabi", &[]), + ("thumbv7m-none-eabi", &["armv7m"]), + ("thumbv7em-none-eabi", &["armv7m", "armv7em"]), + ("thumbv7em-none-eabihf", &["armv7m", "armv7em", "has_fpu"]), + ("thumbv8m.base-none-eabi", &["armv8m", "armv8m_base"]), + ( + "thumbv8m.main-none-eabi", + &["armv7m", "armv8m", "armv8m_main"], + ), + ( + "thumbv8m.main-none-eabihf", + &["armv7m", "armv8m", "armv8m_main", "has_fpu"], + ), +]; + +fn assemble_blobs() { + let mut cmd = rustc(); + cmd.arg("-V"); + cmd.stdout(Stdio::null()); + let status = cmd.status().unwrap(); + + if !status.success() { + let toolchain = toolchain(); + println!( + "asm toolchain {} does not seem to be installed. installing it now.", + toolchain + ); + + let mut rustup = Command::new("rustup"); + let status = rustup.arg("install").arg(&toolchain).status().unwrap(); + assert!(status.success(), "rustup command failed: {:?}", rustup); + + let mut rustup = Command::new("rustup"); + let status = rustup + .arg("target") + .arg("add") + .args(TARGETS.iter().map(|(target, _)| *target)) + .arg("--toolchain") + .arg(toolchain) + .status() + .unwrap(); + assert!(status.success(), "rustup command failed: {:?}", rustup); + } + + for (target, cfgs) in TARGETS { + println!("building artifacts for {}", target); + assemble(target, cfgs); + } +} + +fn check_blobs() { + // Load each `.a` file in `bin` into memory. + let mut files_before = BTreeMap::new(); + for entry in fs::read_dir("bin").unwrap() { + let entry = entry.unwrap(); + if entry.path().extension().unwrap() == "a" { + files_before.insert( + entry + .path() + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + fs::read(entry.path()).unwrap(), + ); + } + } + + assemble_blobs(); + + let mut files_after = BTreeMap::new(); + for entry in fs::read_dir("bin").unwrap() { + let entry = entry.unwrap(); + if entry.path().extension().unwrap() == "a" { + files_after.insert( + entry + .path() + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + fs::read(entry.path()).unwrap(), + ); + } + } + + // Ensure they contain the same files. + let before = files_before.keys().collect::>(); + let after = files_after.keys().collect::>(); + assert_eq!(before, after); + + for ((file, before), (_, after)) in files_before.iter().zip(files_after.iter()) { + if before != after { + panic!("{} differs between rebuilds", file); + } + } + + println!("Blobs identical."); +} + +fn main() { + let subcommand = env::args().skip(1).next(); + match subcommand.as_deref() { + Some("assemble") => assemble_blobs(), + Some("check-blobs") => check_blobs(), + _ => { + eprintln!("usage: cargo xtask "); + eprintln!(); + eprintln!("subcommands:"); + eprintln!(" assemble Reassemble the pre-built artifacts"); + eprintln!(" check-blobs Check that the pre-built artifacts are up-to-date and reproducible"); + process::exit(1); + } + } +}