Skip to content

Commit

Permalink
Fix docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Amanieu committed May 18, 2020
1 parent 9215ead commit 6f8be8c
Showing 1 changed file with 27 additions and 13 deletions.
40 changes: 27 additions & 13 deletions src/doc/unstable-book/src/library-features/asm.md
Expand Up @@ -25,6 +25,7 @@ cannot be otherwise achieved. Accessing low level hardware primitives, e.g. in k
Let us start with the simplest possible example:

```rust
# #![feature(asm)]
unsafe {
asm!("nop");
}
Expand All @@ -41,6 +42,7 @@ Now inserting an instruction that does nothing is rather boring. Let us do somet
actually acts on data:

```rust
# #![feature(asm)]
let x: u64;
unsafe {
asm!("mov {}, 5", out(reg) x);
Expand All @@ -62,6 +64,7 @@ the template and will read the variable from there after the inline assembly fin
Let us see another example that also uses an input:

```rust
# #![feature(asm)]
let i: u64 = 3;
let o: u64;
unsafe {
Expand Down Expand Up @@ -93,6 +96,7 @@ readability, and allows reordering instructions without changing the argument or
We can further refine the above example to avoid the `mov` instruction:

```rust
# #![feature(asm)]
let mut x: u64 = 3;
unsafe {
asm!("add {0}, {number}", inout(reg) x, number = const 5);
Expand All @@ -106,6 +110,7 @@ This is different from specifying an input and output separately in that it is g
It is also possible to specify different variables for the input and output parts of an `inout` operand:

```rust
# #![feature(asm)]
let x: u64 = 3;
let y: u64;
unsafe {
Expand All @@ -127,6 +132,7 @@ There is also a `inlateout` variant of this specifier.
Here is an example where `inlateout` *cannot* be used:

```rust
# #![feature(asm)]
let mut a: u64 = 4;
let b: u64 = 4;
let c: u64 = 4;
Expand All @@ -144,6 +150,7 @@ Here the compiler is free to allocate the same register for inputs `b` and `c` s
However the following example can use `inlateout` since the output is only modified after all input registers have been read:

```rust
# #![feature(asm)]
let mut a: u64 = 4;
let b: u64 = 4;
unsafe {
Expand All @@ -161,21 +168,24 @@ Therefore, Rust inline assembly provides some more specific constraint specifier
While `reg` is generally available on any architecture, these are highly architecture specific. E.g. for x86 the general purpose registers `eax`, `ebx`, `ecx`, `edx`, `ebp`, `esi`, and `edi`
among others can be addressed by their name.

```rust
```rust,no_run
# #![feature(asm)]
let cmd = 0xd1;
unsafe {
asm!("out 0x64, rax", in("rax") cmd);
asm!("out 0x64, eax", in("eax") cmd);
}
```

In this example we call the `out` instruction to output the content of the `cmd` variable
to port `0x64`. Since the `out` instruction only accepts `rax` (and its sub registers) as operand
we had to use the `rax` constraint specifier.
to port `0x64`. Since the `out` instruction only accepts `eax` (and its sub registers) as operand
we had to use the `eax` constraint specifier.

Note that unlike other operand types, explicit register operands cannot be used in the template string: you can't use `{}` and should write the register name directly instead. Also, they must appear at the end of the operand list after all other operand types.

Consider this example which uses the x86 `mul` instruction:

```rust
# #![feature(asm)]
fn mul(a: u64, b: u64) -> u128 {
let lo: u64;
let hi: u64;
Expand All @@ -191,7 +201,7 @@ fn mul(a: u64, b: u64) -> u128 {
);
}

hi as u128 << 64 + lo as u128
(hi as u128) << 64 + lo as u128
}
```

Expand All @@ -211,8 +221,9 @@ We need to tell the compiler about this since it may need to save and restore th
around the inline assembly block.

```rust
let ebx: u64;
let ecx: u64;
# #![feature(asm)]
let ebx: u32;
let ecx: u32;

unsafe {
asm!(
Expand Down Expand Up @@ -240,6 +251,7 @@ However we still need to tell the compiler that `eax` and `edx` have been modifi
This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code:

```rust
# #![feature(asm)]
// Multiply x by 6 using shifts and adds
let mut x: u64 = 4;
unsafe {
Expand All @@ -259,14 +271,15 @@ A special operand type, `sym`, allows you to use the symbol name of a `fn` or `s
This allows you to call a function or access a global variable without needing to keep its address in a register.

```rust
# #![feature(asm)]
extern "C" fn foo(arg: i32) {
println!("arg = {}", arg);
}

fn call_foo(arg: i32) {
unsafe {
asm!(
"call {}"
"call {}",
sym foo,
// 1st argument in rdi, which is caller-saved
inout("rdi") arg => _,
Expand Down Expand Up @@ -294,10 +307,11 @@ By default the compiler will always choose the name that refers to the full regi
This default can be overriden by using modifiers on the template string operands, just like you would with format strings:

```rust
# #![feature(asm)]
let mut x: u16 = 0xab;

unsafe {
asm!("mov {0:h}, {0:b}", inout(reg_abcd) x);
asm!("mov {0:h}, {0:l}", inout(reg_abcd) x);
}

assert_eq!(x, 0xabab);
Expand All @@ -306,7 +320,7 @@ assert_eq!(x, 0xabab);
In this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 register (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently.

Let us assume that the register allocator has chosen to allocate `x` in the `ax` register.
The `h` modifier will emit the register name for the high byte of that register and the `b` modifier will emit the register name for the low byte. The asm code will therefore be expanded as `mov ah, al` which copies the low byte of the value into the high byte.
The `h` modifier will emit the register name for the high byte of that register and the `l` modifier will emit the register name for the low byte. The asm code will therefore be expanded as `mov ah, al` which copies the low byte of the value into the high byte.

If you use a smaller data type (e.g. `u16`) with an operand and forget the use template modifiers, the compiler will emit a warning and suggest the correct modifier to use.

Expand All @@ -317,6 +331,7 @@ By default, an inline assembly block is treated the same way as an external FFI
Let's take our previous example of an `add` instruction:

```rust
# #![feature(asm)]
let mut a: u64 = 4;
let b: u64 = 4;
unsafe {
Expand Down Expand Up @@ -348,7 +363,7 @@ When required, options are specified as the final argument.

The following ABNF specifies the general syntax:

```
```ignore
dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout"
reg_spec := <register class> / "<explicit register>"
operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
Expand Down Expand Up @@ -612,7 +627,7 @@ Currently the following options are defined:
- `pure`: The `asm` block has no side effects, and its outputs depend only on its direct inputs (i.e. the values themselves, not what they point to) or values read from memory (unless the `nomem` options is also set). This allows the compiler to execute the `asm` block fewer times than specified in the program (e.g. by hoisting it out of a loop) or even eliminate it entirely if the outputs are not used.
- `nomem`: The `asm` blocks does not read or write to any memory. This allows the compiler to cache the values of modified global variables in registers across the `asm` block since it knows that they are not read or written to by the `asm`.
- `readonly`: The `asm` block does not write to any memory. This allows the compiler to cache the values of unmodified global variables in registers across the `asm` block since it knows that they are not written to by the `asm`.
- `preserves_flags`: The `asm` block does not modify the flags register (defined in the [rules][rules] below). This allows the compiler to avoid recomputing the condition flags after the `asm` block.
- `preserves_flags`: The `asm` block does not modify the flags register (defined in the rules below). This allows the compiler to avoid recomputing the condition flags after the `asm` block.
- `noreturn`: The `asm` block never returns, and its return type is defined as `!` (never). Behavior is undefined if execution falls through past the end of the asm code. A `noreturn` asm block behaves just like a function which doesn't return; notably, local variables in scope are not dropped before it is invoked.
- `nostack`: The `asm` block does not push data to the stack, or write to the stack red-zone (if supported by the target). If this option is *not* used then the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call.
- `att_syntax`: This option is only valid on x86, and causes the assembler to use the `.att_syntax prefix` mode of the GNU assembler. Register operands are substituted in with a leading `%`.
Expand All @@ -624,7 +639,6 @@ The compiler performs some additional checks on options:
- It is a compile-time error to specify `noreturn` on an asm block with outputs.

## Rules for inline assembly
[rules]: #rules

- Any registers not specified as inputs will contain an undefined value on entry to the asm block.
- An "undefined value" in the context of inline assembly means that the register can (non-deterministically) have any one of the possible values allowed by the architecture. Notably it is not the same as an LLVM `undef` which can have a different value every time you read it (since such a concept does not exist in assembly code).
Expand Down

0 comments on commit 6f8be8c

Please sign in to comment.