Skip to content

Commit dbcf345

Browse files
committed
Auto merge of #84015 - tdelabro:issue-76704-fix, r=Amanieu
doc asm feature - Added new 'Label' section with example and explanations Fixes #76704
2 parents dae9d6a + 1f7de3f commit dbcf345

File tree

1 file changed

+53
-19
lines changed
  • src/doc/unstable-book/src/library-features

1 file changed

+53
-19
lines changed

src/doc/unstable-book/src/library-features/asm.md

+53-19
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Inline assembly is currently supported on the following architectures:
3535
Let us start with the simplest possible example:
3636

3737
```rust,allow_fail
38-
# #![feature(asm)]
38+
#![feature(asm)]
3939
unsafe {
4040
asm!("nop");
4141
}
@@ -52,7 +52,7 @@ Now inserting an instruction that does nothing is rather boring. Let us do somet
5252
actually acts on data:
5353

5454
```rust,allow_fail
55-
# #![feature(asm)]
55+
#![feature(asm)]
5656
let x: u64;
5757
unsafe {
5858
asm!("mov {}, 5", out(reg) x);
@@ -74,7 +74,7 @@ the template and will read the variable from there after the inline assembly fin
7474
Let us see another example that also uses an input:
7575

7676
```rust,allow_fail
77-
# #![feature(asm)]
77+
#![feature(asm)]
7878
let i: u64 = 3;
7979
let o: u64;
8080
unsafe {
@@ -114,7 +114,7 @@ readability, and allows reordering instructions without changing the argument or
114114
We can further refine the above example to avoid the `mov` instruction:
115115

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

130130
```rust,allow_fail
131-
# #![feature(asm)]
131+
#![feature(asm)]
132132
let x: u64 = 3;
133133
let y: u64;
134134
unsafe {
@@ -150,7 +150,7 @@ There is also a `inlateout` variant of this specifier.
150150
Here is an example where `inlateout` *cannot* be used:
151151

152152
```rust,allow_fail
153-
# #![feature(asm)]
153+
#![feature(asm)]
154154
let mut a: u64 = 4;
155155
let b: u64 = 4;
156156
let c: u64 = 4;
@@ -171,7 +171,7 @@ Here the compiler is free to allocate the same register for inputs `b` and `c` s
171171
However the following example can use `inlateout` since the output is only modified after all input registers have been read:
172172

173173
```rust,allow_fail
174-
# #![feature(asm)]
174+
#![feature(asm)]
175175
let mut a: u64 = 4;
176176
let b: u64 = 4;
177177
unsafe {
@@ -190,7 +190,7 @@ While `reg` is generally available on any architecture, these are highly archite
190190
among others can be addressed by their name.
191191

192192
```rust,allow_fail,no_run
193-
# #![feature(asm)]
193+
#![feature(asm)]
194194
let cmd = 0xd1;
195195
unsafe {
196196
asm!("out 0x64, eax", in("eax") cmd);
@@ -206,7 +206,7 @@ Note that unlike other operand types, explicit register operands cannot be used
206206
Consider this example which uses the x86 `mul` instruction:
207207

208208
```rust,allow_fail
209-
# #![feature(asm)]
209+
#![feature(asm)]
210210
fn mul(a: u64, b: u64) -> u128 {
211211
let lo: u64;
212212
let hi: u64;
@@ -242,7 +242,7 @@ We need to tell the compiler about this since it may need to save and restore th
242242
around the inline assembly block.
243243

244244
```rust,allow_fail
245-
# #![feature(asm)]
245+
#![feature(asm)]
246246
let ebx: u32;
247247
let ecx: u32;
248248
@@ -272,7 +272,7 @@ However we still need to tell the compiler that `eax` and `edx` have been modifi
272272
This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code:
273273

274274
```rust,allow_fail
275-
# #![feature(asm)]
275+
#![feature(asm)]
276276
// Multiply x by 6 using shifts and adds
277277
let mut x: u64 = 4;
278278
unsafe {
@@ -294,7 +294,7 @@ A special operand type, `sym`, allows you to use the symbol name of a `fn` or `s
294294
This allows you to call a function or access a global variable without needing to keep its address in a register.
295295

296296
```rust,allow_fail
297-
# #![feature(asm)]
297+
#![feature(asm)]
298298
extern "C" fn foo(arg: i32) {
299299
println!("arg = {}", arg);
300300
}
@@ -316,7 +316,7 @@ fn call_foo(arg: i32) {
316316
// Also mark AVX-512 registers as clobbered. This is accepted by the
317317
// compiler even if AVX-512 is not enabled on the current target.
318318
out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _,
319-
out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm13") _,
319+
out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm23") _,
320320
out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _,
321321
out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _,
322322
)
@@ -336,7 +336,7 @@ By default the compiler will always choose the name that refers to the full regi
336336
This default can be overriden by using modifiers on the template string operands, just like you would with format strings:
337337

338338
```rust,allow_fail
339-
# #![feature(asm)]
339+
#![feature(asm)]
340340
let mut x: u16 = 0xab;
341341
342342
unsafe {
@@ -361,7 +361,7 @@ For example, in x86/x86_64 and intel assembly syntax, you should wrap inputs/out
361361
to indicate they are memory operands:
362362

363363
```rust,allow_fail
364-
# #![feature(asm, llvm_asm)]
364+
#![feature(asm, llvm_asm)]
365365
# fn load_fpu_control_word(control: u16) {
366366
unsafe {
367367
asm!("fldcw [{}]", in(reg) &control, options(nostack));
@@ -372,14 +372,51 @@ unsafe {
372372
# }
373373
```
374374

375+
## Labels
376+
377+
The compiler is allowed to instantiate multiple copies an `asm!` block, for example when the function containing it is inlined in multiple places. As a consequence, you should only use GNU assembler [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions.
378+
379+
Moreover, due to [an llvm bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values.
380+
381+
```rust,allow_fail
382+
#![feature(asm)]
383+
384+
let mut a = 0;
385+
unsafe {
386+
asm!(
387+
"mov {0}, 10",
388+
"2:",
389+
"sub {0}, 1",
390+
"cmp {0}, 3",
391+
"jle 2f",
392+
"jmp 2b",
393+
"2:",
394+
"add {0}, 2",
395+
out(reg) a
396+
);
397+
}
398+
assert_eq!(a, 5);
399+
```
400+
401+
This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`.
402+
403+
This example show a few thing:
404+
405+
First that the same number can be used as a label multiple times in the same inline block.
406+
407+
Second, that when a numeric label is used as a reference (as an instruction operand, for example), the suffixes b (“backward”) or f (“forward”) should be added to the numeric label. It will then refer to the nearest label defined by this number in this direction.
408+
409+
[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels
410+
[an llvm bug]: https://bugs.llvm.org/show_bug.cgi?id=36144
411+
375412
## Options
376413

377414
By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better.
378415

379416
Let's take our previous example of an `add` instruction:
380417

381418
```rust,allow_fail
382-
# #![feature(asm)]
419+
#![feature(asm)]
383420
let mut a: u64 = 4;
384421
let b: u64 = 4;
385422
unsafe {
@@ -787,8 +824,5 @@ The compiler performs some additional checks on options:
787824
- You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds).
788825
- The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited.
789826
- You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places.
790-
- As a consequence, you should only use [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions.
791827

792828
> **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call.
793-
794-
[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels

0 commit comments

Comments
 (0)