Skip to content
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

Document RFC 2867: instruction_set attribute #1253

Merged
merged 9 commits into from
Nov 17, 2022
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/attributes/codegen.md
Original file line number Diff line number Diff line change
@@ -352,3 +352,18 @@ trait object whose methods are attributed.
[`core::intrinsics::caller_location`]: ../../core/intrinsics/fn.caller_location.html
[`core::panic::Location::caller`]: ../../core/panic/struct.Location.html#method.caller
[`Location`]: ../../core/panic/struct.Location.html

## The `instruction_set` attribute

The `instruction_set` attribute may be applied to a function to enable code generation for a specific
instruction set supported by the target architecture. Currently, this is only available for `ARMv4T`
devices where the architecture has "ARM code" and "Thumb code" available and a single program may
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of "ARMv4T devices", might it perhaps be a little more specific to say "the ARMv4T architecture"? I know there likely won't be any confusion, but I'd like to use precise phrasing when possible.

Part of the problem talking about this clearly is that the reference has shied away from platform specific stuff, so it can be difficult to talk about things without also talking about target triples and such. AFAIK, the presence of ARMv4T can't be detected through cfg can it?

Also, can this maybe be structured so that it can be easily be extended in the future, and makes it clear which values to use? Perhaps something like:

The following values are available on targets for the ARMv4T architecture:

  • arm::a32 — Uses ARM code.
  • arm::t32 — Uses Thumb code.

Or something like that? Then, if support for other architectures is added, they can just add more lists (or if more ARM modes are added, just extend the list).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the armv4t target triple to detect it afaik, @Lokathor is the best person to ask as he's the most prevalent user currently

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So first minor point: ARMv? (with a v) and ARM? (without a v) are different numbering systems, because of course. So, if you go looking up details about this stuff keep that in mind.

Starting with ARMv4T and onward, ARM CPUs support a system called "interworking". This is currently what the instruction_set attribute is all about. I'm told that theoretically MIPS also has some sort of system where the instruction_set attribute would be appropriate, but I was told that once ever in passing so we can reasonably treat this attribute at ARM only for now.

Interworking is available in most ARM architectures that came after ARMv4T, but not all of them.

I had to go double check on wikipedia:

All ARMv7 chips support the Thumb instruction set. All chips in the Cortex-A series, Cortex-R series, and ARM11 series support both "ARM instruction set state" and "Thumb instruction set state", while chips in the Cortex-M series support only the Thumb instruction set.

Looking at Rust's target list, thumbv6m-none-eabi is described as "Bare Cortex-M0, M0+, M1", so I have to assume that you should not use the instruction_set attribute on that target to set a function for a32 code. However, it's still logically consistent to set a function for t32 on a Cortex-M. That's already the default, and you'd basically be just more strongly re-stating that.

In terms of what you can check with cfg at compile time, beyond target_arch="arm", there's also target_feature = "thumb-mode" which at least tells you the default code mode.

As far as I know, there isn't a specific way to check for the presence of interworking. If I ask rustc what it thinks about the thumbv4t-none-eabi target:

$ rustc --print cfg --target thumbv4t-none-eabi
debug_assertions
panic="abort"
target_abi="eabi"
target_arch="arm"
target_endian="little"
target_env=""
target_feature="llvm14-builtins-abi"   
target_feature="thumb-mode"
target_has_atomic_equal_alignment="16" 
target_has_atomic_equal_alignment="32" 
target_has_atomic_equal_alignment="8"  
target_has_atomic_equal_alignment="ptr"
target_has_atomic_load_store="16"      
target_has_atomic_load_store="32"      
target_has_atomic_load_store="8"       
target_has_atomic_load_store="ptr"     
target_os="none"
target_pointer_width="32"
target_vendor="unknown"

Which... sure doesn't seem to indicate if interworking is specifically allowed.

I know that the assembler knows if it's assembling interworking code (example, but I don't know if rustc lets you know this in any way.

But if we go check the thumbv6m-none-eabi target we see:

$ rustc --print cfg --target thumbv6m-none-eabi
debug_assertions
panic="abort"
target_abi="eabi"
target_arch="arm"
target_endian="little"
target_env=""
target_feature="llvm14-builtins-abi"
target_feature="mclass"
target_feature="thumb-mode"
target_feature="v5te"
target_feature="v6"
target_has_atomic_equal_alignment="16"
target_has_atomic_equal_alignment="32"
target_has_atomic_equal_alignment="8"
target_has_atomic_equal_alignment="ptr"
target_has_atomic_load_store="16"
target_has_atomic_load_store="32"
target_has_atomic_load_store="8"
target_has_atomic_load_store="ptr"
target_os="none"
target_pointer_width="32"
target_vendor="unknown"

So we can probably say that if target_feature="mclass" is set then you should not try interworking.

@xd009642 how does the rustc decide if the attribute is allowed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking back at code to refresh myself, there's a has_thumb_interworking bool for each target, it's only true for:

  • armv4t_unknown_linux_gnueabi
  • armv5te_unknown_linux_gnueabi
  • armv5te_unknown_linux_musleabi

So docs should be adjusted in some way to mention armv5te as well (I'll do that tomorrow it's after midnight here)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uhhhhhhhhhhhhhhhhhh, I've certainly used the attribute on the thumbv4t-none-eabi target, so either your check is broken somehow or you're looking at the wrong part of the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope I just missed it, each spec is in their own file and I stopped reading when I hit the module mod in commit diff, should have read thumbv4t_none_eabi

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's a way to query the info in each spec i.e. https://github.com/rust-lang/rust/blob/master/compiler/rustc_target/src/spec/armv4t_none_eabi.rs we could amend the docs to instead tell people how to see if their target supports interworking given has_thumb_interworking: true, is a requirement for the attribute

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that the assembler knows if it's assembling interworking code (example, but I don't know if rustc lets you know this in any way.

asm_args is entirely unused.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh maybe that's why my code breaks randomly sometimes

utilize both of these. If not specified the default instruction set for the target will be used.

```rust
#[instruction_set(arm::a32)]
fn foo_arm_code() {}

#[instruction_set(arm::t32)]
fn bar_thumb_code() {}
```