-
Notifications
You must be signed in to change notification settings - Fork 275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add CMSIS / Cortex-M instrinsics #518
Conversation
These instrinsics are also provided by ACLE, so I don't think that it is necessary to also expose them on Cortex-A/R |
All listed intrinsics works on Cortex-Rx too, but formally are not available on CMSIS-5 yet. Instead the most notable Cortex-R CMSIS-DSP implementation is Hercules-DSPLIB |
coresimd/arm/cmsis.rs
Outdated
/// Enables IRQ interrupts by clearing the I-bit in the CPSR. Can only be | ||
/// executed in Privileged modes. | ||
#[inline(always)] | ||
pub unsafe fn __enable_irq() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should use the assert_instr
macro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alexcrichton these are all in a module guarded by cfg(target_feature = "mclass")
, so they don't technically need #[target_feature(enable = "mclass")]
, but I think we still do always use target_feature(enable)
everywhere else in the library, including these cases.
I think I would prefer to have it here as well just in case we end up moving functions around internally in the future.
@japaric is it possible to detect cmsis at run-time? Or is it something that must always be configured statically at compile-time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think it's probably prudent to go ahead and add the attribute to manually enable it just in case.
} | ||
|
||
#[cfg(target_feature = "v7")] | ||
mod v7 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make it more clear to move this to its own file. We have a different v7
module in the arm submodule already, maybe one needs to refactor things a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@japaric this looks good. There are only two nitpicks:
- all functions should use the
#[target_feature(enable = ...)]
attribute even if it should be implied anyways - all functions should use the
assert_instr
attribute to test that the code Rust emits contains the appropriate assembly instruction
I suppose that it is not easy to write run-time tests for this right now, but it would be nice if we could open an issue to explore how we could test these.
@@ -0,0 +1,318 @@ | |||
#![allow(non_snake_case)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a top-level comment here linking to the document / documents that specifies these intrinsics?
@gnzlbg done, but as it is the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this LGTM, but I have never used these, so it would be nice if we could add a link to the specification, and if @Amanieu or someone else familiar with these could review that these follow that properly, since none of these will really be tested for the time being beyond "compiles".
coresimd/arm/cmsis.rs
Outdated
|
||
/// Extracted from [CMSIS 5]'s CMSIS/Core/Include/cmsis_gcc.h | ||
/// | ||
/// [CMSIS 5]: https://github.com/ARM-software/CMSIS_5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be //!
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also prefer this to be a top-module documentation string (using //!
).
/// Enables IRQ interrupts by clearing the I-bit in the CPSR. Can only be | ||
/// executed in Privileged modes. | ||
#[inline] | ||
#[target_feature(enable = "mclass")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am 99% sure that #[target_feature(enable = "mclass")]
is the wrong thing to do here. Instead the whole module should be guarded by #[cfg(target_feature = "mclass")]
.
M-class ARM is very different from A/R-class ARM and these intrinsics only make sense when targeting a M-class architecture. Keep in mind that code compiled for M-class ARM is not binary compatible with A/R-class ARM, so "enabling" the mclass
feature does not make any sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead the whole module should be guarded by #[cfg(target_feature = "mclass")].
That is already the case, and you are correct, #[target_feature(enable = "mclass")]
isn't technically necessary.
We had issues in the past with the whole binary being compiled with a feature enabled not being 100% equivalent with all the functions in the binary having the feature enabled. These annotations should be redundant if everything works fine, view them as just being extra explicit here (we do the same for all other intrinsics / targets).
I don't know exactly which targets have the target-feature
It adds a new test to the crate that disassembles the exported intrinsic function, and just searches the disassembly for the instruction. So it requires If |
Updated the module level doc.
Only the
Yes, some intrinsics but this PR only makes these intrinsics available on the
Neither test or backtrace work nor compile for the thumb targets.
QEMU may not implement the full semantics of these intrinsics; some of these instructions may be interpreted as "nop" in the emulator -- I have seen this behavior before with register access (i.e. invalid memory access doesn't crash / segfault the emulator). But, @jamesmunns and rust-reach participants are looking into adding test support for the thumb targets (which may include testing on QEMU) to rust-lang/rust. Some of that CI work could be ported to this repo. |
Thanks! I restarted the travis build and everything was green. |
@gnzlbg @alexcrichton two follow up questions:
|
@japaric hm they probably don't show up due to the extra For stabilization it's basically the same as the Intel intrinsics for now, which is to say that it's required to have "vendor specification" for what these intrinsics are (or the equivalent thereof). For Intel intrinsics this was Intel's intrinsics guide, so for these ARM intrinsics we're gonna want the same. The second optional, but highly desired, piece would be automatic verification of the ARM intrinsics in this repository. Currently there's verification of Intel intrinsics whenever we check them in, but that's not currently implemented for ARM. That's mostly it! I would personally also like to understand why these intrinsics are feature gated rather than runtime detected (aka defined with |
For Here, because we are using inline assembly, we might not have that limitation (I am not sure), but that doesn't mean that it is useless to add it. It depends on whether the intrinsics must be enabled at compile-time, or can be detected and enabled at run-time. This has a lot of consequences. The biggest one is that people can only use these if either their target has them enabled (e.g. the Rust target, or they are using Or in other words, i don't think it is a big issue to have features that must be enabled statically (we already have pretty big ones, like arm + v7 + neon where v7 and neon must be enabled statically). The problem is, however, that users cannot easily recompile std with these features enabled, and therefore, cannot easily use them without moving to |
@gnzlbg but do we know why compilation fails? Is it just a bug in LLVM or something fundamental to ARM/the target itself? |
@alexcrichton The LLVM assembler will restrict itself only to the instructions supported by the currently enabled features. You can enable more features with the
|
Hm ok, so is the style of SIMD on x86 where you cpuid at runtime and compile in multiple functions just entirely not used on ARM at all? Or is it supported in some C compilers and not others? |
For the CMSIS API added here and in the follow up DSP PRs we have these specifications:
These are referenced in the source code but don't show up in the generated documentation. Should we modify the public doc comments to point to these specifications?
By this do you mean: checking that the signature of the functions we check in matches the CMSIS specification, or checking that the functions generate the instructions they are suppose to generate, or adding both kind of tests? I'd love to help with such effort but don't have the spare cycles until after the Preview 2 is out.
Cortex-M and Cortex-A do support CPUID, and I assume Cortex-R does too but couldn't find docs on the topic. I'm not sure if the older ARMv4 and ARMv5 support cpuid; didn't investigate. But, I expect that most Cortex-M applications would prefer to statically configure everything instead of using a runtime mechanism like CPUID (for code size and, maybe, performance reasons). Cortex-A applications that do use CPUID can't access the Cortex-M specific instructions that have been added in this PR. So, having the Cortex-M instructions available only in a static manner (i.e. depending on the compilation target) seems fine to me. |
For your information CPUID is available on Cortex-R too. Some extra information is available using the 'feature register 0'. BTW runtime detection is not a good idea on embedded devices. Please note that DSP intrinsics are available on Cortex-A, but new 'ACLE Q2 2018' specification report: "In general, these intrinsics are aimed at DSP algorithm optimization on M-profile and R-profile. Use on A-profile is deprecated." |
Perfect!
That'd be great yeah! Not required, but it was a last-minute thing we did for all the Intel intrinsics as well, making them a bit easier to browse in the Rust docs.
Both! The
This may be pretty difficult for the standard library though. We don't support recompiling libstd on the fly so if you're using a precompiled version we are deciding at compile time what set of intrinsics you have access to (which may not be the subset you want). That's one of the major things that unblocked x86 SIMD where we're shipping a "dumb" libstd that doesn't use AVX but still gives you access to all the AVX goodies. This does sort of depend on LLVM/bugs/etc but it may be a blocker if it's that different from x86 Now if everyone's using xargo anyway, this perhaps isn't a problem! Overall the next step for stabilization may be an RFC like we had for the x86 intrinsics where the layouts are defined and the way we use the vendor definitions is also defined. |
The |
The intrinsics that would bring the most benefits by being stabilized first are the non-SIMD ones. These intrinsics would let us drop the build dependency on This subset of intrinsics doesn't require adding any new stable type to I'm not quite sure what you meant by "where the layouts are defined", but should I make an RFC that lists these intrinisics and lays out the rationale for stabilizing them ahead of the SIMD ones? |
I'm not sure it makes sense to have the CMSIS intrinsics in stdsimd. ACLE are the instrinsics Arm recommend compilers implement. The CMSIS instrinsics are more of a wrapper around the various different compilers. |
@japaric sounds great to me! And yeah sorry by "where the layouts are defined" I basically just mean an RFC which covers topics like:
Basically just a standard RFC for these intrinsics :) @parched "stdsimd" is probably a misnomer for this repository in that it's largely intended to do SIMD on x86/x86_64, but it's in general a good location for vendor intrinsics, so in that sense if there's published documentation for what compilers to do that seems reasonable to me to follow! |
It's also worth pointing out that |
@parched from a cursory look at the documentation you have linked I see that there is a lot of overlap in terms of exposed functionality between CMSIS and ACLE. The main differences I see are:
Personally, I don't see a reason to not expose the union of ACLE and CMSIS. We just have to sort out points (1) and (2). |
From my point of view is not a problem of overlap or (minimal) differences on functions signatures.
ACLE call it |
This might be a dumb question, but can’t we just implement both and let
users choose?
I mean, even if ACLE deprecates stuff, won’t C compilers have to support
that forever anyways to avoid breaking client code?
|
Yes, but because there is a pre-existing code-base. That's not true for Rust. |
That’s true for Rust as well: we can’t remove deprecated stuff from the
standard library. And no matter which ARM spec you pick (ACLE, CMSIS, or
both) the Moment ARM deprecates something that’s already stabilized, we’ll
be in that exact situation.
I think there is value in exposing the same intrinsics as C, in particular
when we consider porting of C components to Rust. We should strive to make
that easy as long as is technically feasible.
The only intrinsics that have been excluded from stdsimd were either
unsound, impossible to use correctly, or not technically implementable with
the current rustc/LLVM. I don’t know what’s ARM policy about deprecating
intrinsics. If it aligns with our technical problems then that’s ok, but if
a deprecated intrinsic is sound and works correctly I don’t see yet why we
shouldn’t provide it. We would just be making it harder for people to use
Rust AFAICT.
… |
The problem I see with literally implementing both is that in some cases there would be two ways (functions) available to do the exact same thing. It could lead to these situtations:
|
A: Why are there two similarly named functions that do the same?
That’s a good question. Why does ARM has two specs to do the same thing?
AFAIK this is an ARM problem, and C compilers and stdsimd implementing both
would just be reflecting the reality of working with the architecture.
The job of stdsimd is just to expose the architecture intrinsics without
being opinionated about it. People are expected to use these from higher
level wrappers anyways, which hopefully would be implementable in safe Rust.
If ARM has two specs that do the same thing, and C compilers implement
both, then that’s just how working with ARM at this low level is :/
Maybe we could provide one in stdsimd and the other in a crate by building
on top of it. But I’d like the avoid the situation where we implement CMSIS
and not ACLE, and then some devs that only know ACLE have to choose between
learning CMSIS or just using C instead. We shouldnt leave any room for that
to happen.
|
CMSIS is an HAL spec for Cortex-M (include RTOS api etc), ACLE is a C compiler spec. |
I guess that is because in the architecture reference manual all the instructions are in capitals, but lowercase is normal C function name style. Personally I prefer just
I think that can probably be handled with a small match for the limited set of registers exposed now, and then later when compile time arguments are supported everything can be supported.
The C compilers, in general, don't implement the CMSIS instrinsics (unless by conicidence) they implement ACLE. (e.g., you won't find any reference to CMSIS in clang, ACLE is here) |
Then I do not follow why is CMSIS being added to |
The implementation uses inline assembly and it's pretty much a copy paste of the CMSIS
implementation. Most of these intrinsics are only available on Cortex-M; this initial
implementation doesn't attempt to make the more general intrinsics available on Cortex-A or
Cortex-R.
Should we attempt to make some of these intrinsics available on Cortex-A and Cortex-R before
landing this PR? CMSIS provides information about Cortex-A; these intrinsics are available on both
Cortex-M and Cortex-A:
But CMSIS doesn't provide any information about Cortex-R.
This PR depends on the "mclass" target feature which PR rust-lang/rust#52120 adds.
r? @gnzlbg
maybe @andre-richter, @Lakier15, or @paoloteti can tell us which of these intrinsics should also be
available on Cortex-A / Cortex-R?
closes #437
P.S. CMSIS also lists a bunch of DSP instructions but those can be added in a follow up PR. Adding
those intrinsics would require exposing the "dsp" target feature which is enabled only in 1 of the 4
thumb*
targets and a few of the Linux targets.