Skip to content

Commit 9a6e970

Browse files
committed
Revise asm_goto changes
Let's make some clarifications of substance and editorial tweaks to the changes for `asm_goto`. We add an example, we change various wording to be more clear -- in particular, we significantly revise the section on `noreturn` to unify the statement of the rules -- and we replace remaining uses of "asm code" with "assembly code" to match our verbiage elsewhere. The main clarification of substance is that for requirements that must be upheld when falling through the assembly, such as he requirement to restore the stack pointer, we must also note that the requirement must be satisfied before jumping to any `label` blocks.
1 parent 18c7807 commit 9a6e970

File tree

1 file changed

+40
-33
lines changed

1 file changed

+40
-33
lines changed

src/inline-assembly.md

+40-33
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ r[asm.operand-type.supported-operands.in]
225225
* `in(<reg>) <expr>`
226226
- `<reg>` can refer to a register class or an explicit register.
227227
The allocated register name is substituted into the asm template string.
228-
- The allocated register will contain the value of `<expr>` at the start of the asm code.
229-
- The allocated register must contain the same value at the end of the asm code (except if a `lateout` is allocated to the same register).
228+
- The allocated register will contain the value of `<expr>` at the start of the assembly code.
229+
- The allocated register must contain the same value at the end of the assembly code (except if a `lateout` is allocated to the same register).
230230

231231
```rust
232232
# #[cfg(target_arch = "x86_64")] {
@@ -239,9 +239,9 @@ r[asm.operand-type.supported-operands.out]
239239
* `out(<reg>) <expr>`
240240
- `<reg>` can refer to a register class or an explicit register.
241241
The allocated register name is substituted into the asm template string.
242-
- The allocated register will contain an undefined value at the start of the asm code.
243-
- `<expr>` must be a (possibly uninitialized) place expression, to which the contents of the allocated register are written at the end of the asm code.
244-
- An underscore (`_`) may be specified instead of an expression, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber).
242+
- The allocated register will contain an undefined value at the start of the assembly code.
243+
- `<expr>` must be a (possibly uninitialized) place expression, to which the contents of the allocated register are written at the end of the assembly code.
244+
- An underscore (`_`) may be specified instead of an expression, which will cause the contents of the register to be discarded at the end of the assembly code (effectively acting as a clobber).
245245

246246
```rust
247247
# #[cfg(target_arch = "x86_64")] {
@@ -271,8 +271,8 @@ r[asm.operand-type.supported-operands.inout]
271271
* `inout(<reg>) <expr>`
272272
- `<reg>` can refer to a register class or an explicit register.
273273
The allocated register name is substituted into the asm template string.
274-
- The allocated register will contain the value of `<expr>` at the start of the asm code.
275-
- `<expr>` must be a mutable initialized place expression, to which the contents of the allocated register are written at the end of the asm code.
274+
- The allocated register will contain the value of `<expr>` at the start of the assembly code.
275+
- `<expr>` must be a mutable initialized place expression, to which the contents of the allocated register are written at the end of the assembly code.
276276

277277
```rust
278278
# #[cfg(target_arch = "x86_64")] {
@@ -286,8 +286,8 @@ assert_eq!(x, 5);
286286
r[asm.operand-type.supported-operands.inout-arrow]
287287
* `inout(<reg>) <in expr> => <out expr>`
288288
- Same as `inout` except that the initial value of the register is taken from the value of `<in expr>`.
289-
- `<out expr>` must be a (possibly uninitialized) place expression, to which the contents of the allocated register are written at the end of the asm code.
290-
- An underscore (`_`) may be specified instead of an expression for `<out expr>`, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber).
289+
- `<out expr>` must be a (possibly uninitialized) place expression, to which the contents of the allocated register are written at the end of the assembly code.
290+
- An underscore (`_`) may be specified instead of an expression for `<out expr>`, which will cause the contents of the register to be discarded at the end of the assembly code (effectively acting as a clobber).
291291
- `<in expr>` and `<out expr>` may have different types.
292292

293293
```rust
@@ -318,7 +318,7 @@ r[asm.operand-type.supported-operands.sym]
318318
- `<path>` must refer to a `fn` or `static`.
319319
- A mangled symbol name referring to the item is substituted into the asm template string.
320320
- The substituted string does not include any modifiers (e.g. GOT, PLT, relocations, etc).
321-
- `<path>` is allowed to point to a `#[thread_local]` static, in which case the asm code can combine the symbol with relocations (e.g. `@plt`, `@TPOFF`) to read from thread-local data.
321+
- `<path>` is allowed to point to a `#[thread_local]` static, in which case the assembly code can combine the symbol with relocations (e.g. `@plt`, `@TPOFF`) to read from thread-local data.
322322

323323
```rust
324324
# #[cfg(target_arch = "x86_64")] {
@@ -359,7 +359,7 @@ r[asm.operand-type.supported-operands.label]
359359
- The address of the block is substituted into the asm template string. The assembly code may jump to the substituted address.
360360
- After execution of the block, the `asm!` expression returns.
361361
- The type of the block must be unit or `!` (never).
362-
- The block starts a new safety context: unsafe operations within the `label` block must be wrapped in an inner `unsafe` block, even though the entire `asm!` statement is already wrapped in `unsafe`.
362+
- The block starts a new safety context; unsafe operations within the `label` block must be wrapped in an inner `unsafe` block, even though the entire `asm!` expression is already wrapped in `unsafe`.
363363

364364
```rust
365365
# #[cfg(target_arch = "x86_64")]
@@ -370,7 +370,6 @@ unsafe {
370370
}
371371
```
372372

373-
374373
r[asm.operand-type.left-to-right]
375374
Operand expressions are evaluated from left to right, just like function call arguments.
376375
After the `asm!` has executed, outputs are written to in left to right order.
@@ -749,7 +748,7 @@ Some registers cannot be used for input or output operands:
749748

750749
| Architecture | Unsupported register | Reason |
751750
| ------------ | -------------------- | ------ |
752-
| All | `sp`, `r15` (s390x) | The stack pointer must be restored to its original value at the end of an assembly code. |
751+
| All | `sp`, `r15` (s390x) | The stack pointer must be restored to its original value at the end of the assembly code or before jumping to a `label` block. |
753752
| All | `bp` (x86), `x29` (AArch64 and Arm64EC), `x8` (RISC-V), `$fp` (LoongArch), `r11` (s390x) | The frame pointer cannot be used as an input or output. |
754753
| ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. |
755754
| All | `si` (x86-32), `bx` (x86-64), `r6` (ARM), `x19` (AArch64 and Arm64EC), `x9` (RISC-V), `$s8` (LoongArch) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. |
@@ -863,7 +862,7 @@ assert_eq!(x, 0x1000u16);
863862

864863
r[asm.template-modifiers.smaller-value]
865864
As stated in the previous section, passing an input value smaller than the register width will result in the upper bits of the register containing undefined values.
866-
This is not a problem if the inline asm only accesses the lower bits of the register, which can be done by using a template modifier to use a subregister name in the asm code (e.g. `ax` instead of `rax`).
865+
This is not a problem if the inline asm only accesses the lower bits of the register, which can be done by using a template modifier to use a subregister name in the assembly code (e.g. `ax` instead of `rax`).
867866
Since this an easy pitfall, the compiler will suggest a template modifier to use where appropriate given the input type.
868867
If all references to an operand already have modifiers then the warning is suppressed for that operand.
869868

@@ -999,15 +998,16 @@ assert_eq!(z, 0);
999998

1000999
r[asm.options.supported-options.nomem]
10011000
- `nomem`: The assembly code does not read from or write to any memory accessible outside of the assembly code.
1002-
This allows the compiler to cache the values of modified global variables in registers across the assembly code since it knows that they are not read or written to by the `asm!`.
1001+
This allows the compiler to cache the values of modified global variables in registers across execution of the assembly code since it knows that they are not read from or written to by it.
10031002
The compiler also assumes that the assembly code does not perform any kind of synchronization with other threads, e.g. via fences.
10041003

10051004
<!-- no_run: This test has unpredictable or undefined behavior at runtime -->
10061005
```rust,no_run
10071006
# #[cfg(target_arch = "x86_64")] {
10081007
let mut x = 0i32;
10091008
let z: i32;
1010-
// Accessing memory from assembly in a nomem asm block is disallowed
1009+
// Accessing outside memory from assembly when `nomem` is
1010+
// specified is disallowed
10111011
unsafe {
10121012
core::arch::asm!("mov {val:e}, dword ptr [{ptr}]",
10131013
ptr = in(reg) &mut x,
@@ -1016,7 +1016,8 @@ unsafe {
10161016
)
10171017
}
10181018
1019-
// Writing to memory from assembly in a nomem asm block is also undefined behaviour
1019+
// Writing to outside memory from assembly when `nomem` is
1020+
// specified is also undefined behaviour
10201021
unsafe {
10211022
core::arch::asm!("mov dword ptr [{ptr}], {val:e}",
10221023
ptr = in(reg) &mut x,
@@ -1045,14 +1046,14 @@ assert_eq!(z, 1);
10451046

10461047
r[asm.options.supported-options.readonly]
10471048
- `readonly`: The assembly code does not write to any memory accessible outside of the assembly code.
1048-
This allows the compiler to cache the values of unmodified global variables in registers across the assembly code since it knows that they are not written to by the `asm!`.
1049+
This allows the compiler to cache the values of unmodified global variables in registers across execution of the assembly code since it knows that they are not written to by it.
10491050
The compiler also assumes that this assembly code does not perform any kind of synchronization with other threads, e.g. via fences.
10501051

10511052
<!-- no_run: This test has undefined behaviour at runtime -->
10521053
```rust,no_run
10531054
# #[cfg(target_arch = "x86_64")] {
10541055
let mut x = 0;
1055-
// We cannot modify memory in readonly
1056+
// We cannot modify outside memory when `readonly` is specified
10561057
unsafe {
10571058
core::arch::asm!("mov dword ptr[{}], 1", in(reg) &mut x, options(readonly))
10581059
}
@@ -1091,14 +1092,10 @@ assert_eq!(z, 1);
10911092

10921093
r[asm.options.supported-options.preserves_flags]
10931094
- `preserves_flags`: The assembly code does not modify the flags register (defined in the rules below).
1094-
This allows the compiler to avoid recomputing the condition flags after the assembly code.
1095+
This allows the compiler to avoid recomputing the condition flags after execution of the assembly code.
10951096

10961097
r[asm.options.supported-options.noreturn]
1097-
- `noreturn`: The assembly code never returns, and its return type is defined as `!` (never).
1098-
Behavior is undefined if execution falls through past the end of the asm code.
1099-
A `noreturn` asm block with no `label` blocks behaves just like a function which doesn't return; notably, local variables in scope are not dropped before it is invoked.
1100-
- When any `label` blocks are present, `noreturn` means the execution of the assembly code never falls through; the assembly code may only exit by jumping to one of the specified blocks.
1101-
The entire `asm!` block will have unit type in this case, unless all `label` blocks diverge, in which case the return type is `!`.
1098+
- `noreturn`: The assembly code does not fall through; behavior is undefined if it does. It may still jump to `label` blocks. If any `label` blocks return unit, the `asm!` block will return unit. Otherwise it will return `!` (never). As with a call to a function that does not return, local variables in scope are not dropped before execution of the assembly code.
11021099

11031100
<!-- no_run: This test aborts at runtime -->
11041101
```rust,no_run
@@ -1119,6 +1116,16 @@ unsafe { core::arch::asm!("", options(noreturn)); }
11191116
# }
11201117
```
11211118

1119+
```rust
1120+
# #[cfg(target_arch = "x86_64")]
1121+
let _: () = unsafe {
1122+
// You may still jump to a `label` block
1123+
core::arch::asm!("jmp {}", label {
1124+
println!();
1125+
}, options(noreturn));
1126+
};
1127+
```
1128+
11221129
r[asm.options.supported-options.nostack]
11231130
- `nostack`: The assembly code does not push data to the stack, or write to the stack red-zone (if supported by the target).
11241131
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.
@@ -1237,29 +1244,29 @@ r[asm.rules.mem-same-as-ffi]
12371244
- Refer to the unsafe code guidelines for the exact rules.
12381245
- If the `readonly` option is set, then only memory reads are allowed.
12391246
- If the `nomem` option is set then no reads or writes to memory are allowed.
1240-
- These rules do not apply to memory which is private to the asm code, such as stack space allocated within the assembly code.
1247+
- These rules do not apply to memory which is private to the assembly code, such as stack space allocated within it.
12411248

12421249
r[asm.rules.black-box]
1243-
- The compiler cannot assume that the instructions in the asm are the ones that will actually end up executed.
1244-
- This effectively means that the compiler must treat the `asm!` as a black box and only take the interface specification into account, not the instructions themselves.
1250+
- The compiler cannot assume that the instructions in the assembly code are the ones that will actually end up executed.
1251+
- This effectively means that the compiler must treat the assembly code as a black box and only take the interface specification into account, not the instructions themselves.
12451252
- Runtime code patching is allowed, via target-specific mechanisms.
1246-
- However there is no guarantee that each `asm!` directly corresponds to a single instance of instructions in the object file: the compiler is free to duplicate or deduplicate the assembly code in `asm!` blocks.
1253+
- However there is no guarantee that each block of assembly code in the source directly corresponds to a single instance of instructions in the object file; the compiler is free to duplicate or deduplicate the assembly code in `asm!` blocks.
12471254

12481255
r[asm.rules.stack-below-sp]
1249-
- Unless the `nostack` option is set, asm code is allowed to use stack space below the stack pointer.
1256+
- Unless the `nostack` option is set, assembly code is allowed to use stack space below the stack pointer.
12501257
- On entry to the assembly code the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call.
12511258
- You are responsible for making sure you don't overflow the stack (e.g. use stack probing to ensure you hit a guard page).
12521259
- You should adjust the stack pointer when allocating stack memory as required by the target ABI.
12531260
- The stack pointer must be restored to its original value before leaving the assembly code.
12541261

12551262
r[asm.rules.noreturn]
1256-
- If the `noreturn` option is set then behavior is undefined if execution falls through to the end of the assembly code.
1263+
- If the `noreturn` option is set then behavior is undefined if execution falls through the end of the assembly code.
12571264

12581265
r[asm.rules.pure]
12591266
- If the `pure` option is set then behavior is undefined if the `asm!` has side-effects other than its direct outputs.
12601267
Behavior is also undefined if two executions of the `asm!` code with the same inputs result in different outputs.
12611268
- When used with the `nomem` option, "inputs" are just the direct inputs of the `asm!`.
1262-
- When used with the `readonly` option, "inputs" comprise the direct inputs of the `asm!` and any memory that the assembly code is allowed to read.
1269+
- When used with the `readonly` option, "inputs" comprise the direct inputs of the assembly code and any memory that it is allowed to read.
12631270

12641271
r[asm.rules.preserved-registers]
12651272
- These flags registers must be restored upon exiting the assembly code if the `preserves_flags` option is set:
@@ -1331,7 +1338,7 @@ r[asm.rules.arm64ec]
13311338

13321339
r[asm.rules.only-on-exit]
13331340
- The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting the assembly code.
1334-
- This means that assembly code that never returns (even if not marked `noreturn`) doesn't need to preserve these registers.
1341+
- This means that assembly code that does not fall through and does not jump to any `label` blocks, even if not marked `noreturn`, doesn't need to preserve these registers.
13351342
- When returning to the assembly code of a different `asm!` block than you entered (e.g. for context switching), these registers must contain the value they had upon entering the `asm!` block that you are *exiting*.
13361343
- You cannot exit the assembly code of an `asm!` block that has not been entered.
13371344
Neither can you exit the assembly code of an `asm!` block whose assembly code has already been exited (without first entering it again).

0 commit comments

Comments
 (0)