Skip to content

Commit 7b4dbc5

Browse files
authored
Merge pull request #1498 from asquared31415/asm-cpuid-docs
Fix `asm!` docs and improve code
2 parents 817887c + 298a79d commit 7b4dbc5

File tree

1 file changed

+9
-9
lines changed

1 file changed

+9
-9
lines changed

src/unsafe/asm.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,19 @@ unsafe {
249249
}
250250

251251
// Turn the resulting values into a string
252-
let mut s = Vec::new();
253-
for val in [ebx, edx, ecx] {
254-
for b in val.to_ne_bytes() {
255-
s.push(b);
256-
}
257-
}
258-
println!("CPU Manufacturer ID: {}", std::str::from_utf8(&s).unwrap());
252+
let mut s = String::with_capacity(12);
253+
ebx.to_ne_bytes().map(|b| s.push(char::from(b)));
254+
edx.to_ne_bytes().map(|b| s.push(char::from(b)));
255+
ecx.to_ne_bytes().map(|b| s.push(char::from(b)));
256+
println!("CPU Manufacturer ID: {}", s);
259257
```
260258

261259
In the example above we use the `cpuid` instruction to read the CPU manufacturer ID.
262-
This instruction writes to `eax`, `ebx`, `ecx`, and `edx`, but for the cache size we only care about the contents of `ebx` and `ecx`.
260+
This instruction writes to `eax` with the maximum supported `cpuid` argument and `ebx`, `esx`, and `ecx` with the CPU manufacturer ID as ASCII bytes in that order.
261+
262+
Even though `eax` is never read we still need to tell the compiler that the register has been modified so that the compiler can save any values that were in these registers before the asm. This is done by declaring it as an output but with `_` instead of a variable name, which indicates that the output value is to be discarded.
263263

264-
However we still need to tell the compiler that `eax` and `edx` have been modified so that it can save any values that were in these registers before the asm. This is done by declaring these as outputs but with `_` instead of a variable name, which indicates that the output value is to be discarded.
264+
This code also works around the limitation that `ebx` is a reserved register by LLVM. That means that LLVM assumes that it has full control over the register and it must be restored to its original state before exiting the asm block, so it cannot be used as an output. To work around this we save the register via `push`, read from `ebx` inside the asm block into a temporary register allocated with `out(reg)` and then restoring `ebx` to its original state via `pop`. The `push` and `pop` use the full 64-bit `rbx` version of the register to ensure that the entire register is saved. On 32 bit targets the code would instead use `ebx` in the `push`/`pop`.
265265

266266
This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code:
267267

0 commit comments

Comments
 (0)