You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/unsafe/asm.md
+34Lines changed: 34 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -18,11 +18,13 @@ Inline assembly is currently supported on the following architectures:
18
18
Let us start with the simplest possible example:
19
19
20
20
```rust
21
+
# #[cfg(target_arch ="x86_64")] {
21
22
usestd::arch::asm;
22
23
23
24
unsafe {
24
25
asm!("nop");
25
26
}
27
+
# }
26
28
```
27
29
28
30
This will insert a NOP (no operation) instruction into the assembly generated by the compiler.
@@ -36,13 +38,15 @@ Now inserting an instruction that does nothing is rather boring. Let us do somet
36
38
actually acts on data:
37
39
38
40
```rust
41
+
# #[cfg(target_arch ="x86_64")] {
39
42
usestd::arch::asm;
40
43
41
44
letx:u64;
42
45
unsafe {
43
46
asm!("mov {}, 5", out(reg) x);
44
47
}
45
48
assert_eq!(x, 5);
49
+
# }
46
50
```
47
51
48
52
This will write the value `5` into the `u64` variable `x`.
@@ -61,6 +65,7 @@ the template and will read the variable from there after the inline assembly fin
61
65
Let us see another example that also uses an input:
62
66
63
67
```rust
68
+
# #[cfg(target_arch ="x86_64")] {
64
69
usestd::arch::asm;
65
70
66
71
leti:u64=3;
@@ -74,6 +79,7 @@ unsafe {
74
79
);
75
80
}
76
81
assert_eq!(o, 8);
82
+
# }
77
83
```
78
84
79
85
This will add `5` to the input in variable `i` and write the result to variable `o`.
@@ -97,13 +103,15 @@ readability, and allows reordering instructions without changing the argument or
97
103
We can further refine the above example to avoid the `mov` instruction:
98
104
99
105
```rust
106
+
# #[cfg(target_arch ="x86_64")] {
100
107
usestd::arch::asm;
101
108
102
109
letmutx:u64=3;
103
110
unsafe {
104
111
asm!("add {0}, 5", inout(reg) x);
105
112
}
106
113
assert_eq!(x, 8);
114
+
# }
107
115
```
108
116
109
117
We can see that `inout` is used to specify an argument that is both input and output.
@@ -112,6 +120,7 @@ This is different from specifying an input and output separately in that it is g
112
120
It is also possible to specify different variables for the input and output parts of an `inout` operand:
113
121
114
122
```rust
123
+
# #[cfg(target_arch ="x86_64")] {
115
124
usestd::arch::asm;
116
125
117
126
letx:u64=3;
@@ -120,6 +129,7 @@ unsafe {
120
129
asm!("add {0}, 5", inout(reg) x=>y);
121
130
}
122
131
assert_eq!(y, 8);
132
+
# }
123
133
```
124
134
125
135
## Late output operands
@@ -135,6 +145,7 @@ There is also a `inlateout` variant of this specifier.
135
145
Here is an example where `inlateout`*cannot* be used in `release` mode or other optimized cases:
136
146
137
147
```rust
148
+
# #[cfg(target_arch ="x86_64")] {
138
149
usestd::arch::asm;
139
150
140
151
letmuta:u64=4;
@@ -150,6 +161,7 @@ unsafe {
150
161
);
151
162
}
152
163
assert_eq!(a, 12);
164
+
# }
153
165
```
154
166
The above could work well in unoptimized cases (`Debug` mode), but if you want optimized performance (`release` mode or other optimized cases), it could not work.
155
167
@@ -158,6 +170,7 @@ That is because in optimized cases, the compiler is free to allocate the same re
158
170
However the following example can use `inlateout` since the output is only modified after all input registers have been read:
159
171
160
172
```rust
173
+
# #[cfg(target_arch ="x86_64")] {
161
174
usestd::arch::asm;
162
175
163
176
letmuta:u64=4;
@@ -166,6 +179,7 @@ unsafe {
166
179
asm!("add {0}, {1}", inlateout(reg) a, in(reg) b);
167
180
}
168
181
assert_eq!(a, 8);
182
+
# }
169
183
```
170
184
171
185
As you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register.
@@ -177,12 +191,14 @@ Therefore, Rust inline assembly provides some more specific constraint specifier
177
191
While `reg` is generally available on any architecture, explicit registers 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.
178
192
179
193
```rust,no_run
194
+
# #[cfg(target_arch = "x86_64")] {
180
195
use std::arch::asm;
181
196
182
197
let cmd = 0xd1;
183
198
unsafe {
184
199
asm!("out 0x64, eax", in("eax") cmd);
185
200
}
201
+
# }
186
202
```
187
203
188
204
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 `eax` (and its sub registers) as operand we had to use the `eax` constraint specifier.
@@ -192,6 +208,7 @@ In this example we call the `out` instruction to output the content of the `cmd`
192
208
Consider this example which uses the x86 `mul` instruction:
@@ -336,6 +362,7 @@ By default the compiler will always choose the name that refers to the full regi
336
362
This default can be overridden by using modifiers on the template string operands, just like you would with format strings:
337
363
338
364
```rust
365
+
# #[cfg(target_arch ="x86_64")] {
339
366
usestd::arch::asm;
340
367
341
368
letmutx:u16=0xab;
@@ -345,6 +372,7 @@ unsafe {
345
372
}
346
373
347
374
assert_eq!(x, 0xabab);
375
+
# }
348
376
```
349
377
350
378
In this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 registers (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently.
@@ -361,13 +389,15 @@ You have to manually use the memory address syntax specified by the target archi
361
389
For example, on x86/x86_64 using Intel assembly syntax, you should wrap inputs/outputs in `[]` to indicate they are memory operands:
@@ -383,6 +413,7 @@ As a consequence, you should only use GNU assembler **numeric** [local labels] i
383
413
Moreover, on x86 when using the default Intel syntax, 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. Using `options(att_syntax)` will avoid any ambiguity, but that affects the syntax of the _entire_`asm!` block. (See [Options](#options), below, for more on `options`.)
384
414
385
415
```rust
416
+
# #[cfg(target_arch ="x86_64")] {
386
417
usestd::arch::asm;
387
418
388
419
letmuta=0;
@@ -400,6 +431,7 @@ unsafe {
400
431
);
401
432
}
402
433
assert_eq!(a, 5);
434
+
# }
403
435
```
404
436
405
437
This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`.
@@ -419,6 +451,7 @@ By default, an inline assembly block is treated the same way as an external FFI
419
451
Let's take our previous example of an `add` instruction:
420
452
421
453
```rust
454
+
# #[cfg(target_arch ="x86_64")] {
422
455
usestd::arch::asm;
423
456
424
457
letmuta:u64=4;
@@ -431,6 +464,7 @@ unsafe {
431
464
);
432
465
}
433
466
assert_eq!(a, 8);
467
+
# }
434
468
```
435
469
436
470
Options can be provided as an optional final argument to the `asm!` macro. We specified three options here:
0 commit comments