Skip to content

Commit fc7622c

Browse files
chorman0773ehuss
authored andcommitted
Add additional inline-assembly tests/examples
1 parent 7dae7c6 commit fc7622c

File tree

1 file changed

+171
-1
lines changed

1 file changed

+171
-1
lines changed

src/inline-assembly.md

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,8 +563,16 @@ The availability of supported types for a particular register class may depend o
563563

564564
> **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target).
565565
566+
```rust
567+
# #[cfg(target_arch = "x86_64")] {
568+
let x = 5i32;
569+
let y = -1i8;
570+
let z = unsafe{core::arch::x86_64::_mm_set_epi64x(1,0)};
571+
unsafe { core::arch::asm!("/* {} {} {} */", in(reg) x, in(reg_byte) y, in(xmm_reg) z, out("tmm0") _); }
572+
# }
573+
```
574+
566575
```rust,compile_fail
567-
let x = 5;
568576
# #[cfg(target_arch = "x86_64")] {
569577
let z = unsafe{core::arch::x86_64::_mm_set_epi64x(1,0)};
570578
// We can't pass an `__m128i` to a `reg` input
@@ -689,6 +697,14 @@ Here is the list of all supported register aliases:
689697
| LoongArch | `$f[8-23]` | `$ft[0-15]` |
690698
| LoongArch | `$f[24-31]` | `$fs[0-7]` |
691699

700+
```rust
701+
# #[cfg(target_arch = "x86_64")] {
702+
let z = 0i64;
703+
// rax is an alias for eax and ax
704+
unsafe { core::arch::asm!("", in("rax") z); }
705+
# }
706+
```
707+
692708
r[asm.register-names.not-for-io]
693709
Some registers cannot be used for input or output operands:
694710

@@ -714,6 +730,14 @@ Some registers cannot be used for input or output operands:
714730
| s390x | `c[0-15]` | Reserved by the kernel. |
715731
| s390x | `a[0-1]` | Reserved for system use. |
716732

733+
```rust,compile_fail
734+
# #[cfg(target_arch = "x86_64")] {
735+
// bp is reserved
736+
unsafe { core::arch::asm!("", in("bp") 5i32); }
737+
# }
738+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
739+
```
740+
717741
r[asm.register-names.fp-bp-reserved]
718742
The frame pointer and base pointer registers are reserved for internal use by LLVM. While `asm!` statements cannot explicitly specify the use of reserved registers, in some cases LLVM will allocate one of these reserved registers for `reg` operands. Assembly code making use of reserved registers should be careful since `reg` operands may use the same registers.
719743

@@ -727,6 +751,15 @@ These modifiers do not affect register allocation, but change the way operands a
727751
r[asm.template-modifiers.only-one]
728752
Only one modifier is allowed per template placeholder.
729753

754+
```rust,compile_fail
755+
# #[cfg(target_arch = "x86_64")] {
756+
// bp is reserved
757+
unsafe { core::arch::asm!("/* {:er}", in(reg) 5i32); }
758+
# }
759+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
760+
```
761+
762+
730763
r[asm.template-modifiers.supported-modifiers]
731764
The supported modifiers are a subset of LLVM's (and GCC's) [asm template argument modifiers][llvm-argmod], but do not use the same letter codes.
732765

@@ -792,12 +825,41 @@ r[asm.abi-clobbers.intro]
792825
The `clobber_abi` keyword can be used to apply a default set of clobbers to an `asm!` block.
793826
This will automatically insert the necessary clobber constraints as needed for calling a function with a particular calling convention: if the calling convention does not fully preserve the value of a register across a call then `lateout("...") _` is implicitly added to the operands list (where the `...` is replaced by the register's name).
794827

828+
```rust
829+
extern "C" fn foo() -> i32{ 0 }
830+
# #[cfg(target_arch = "x86_64")] {
831+
let z: i32;
832+
unsafe { core::arch::asm!("call {}", sym foo, out("rax") z, clobber_abi("C")); }
833+
assert_eq!(z, 0);
834+
# }
835+
```
836+
795837
r[asm.abi-clobbers.many]
796838
`clobber_abi` may be specified any number of times. It will insert a clobber for all unique registers in the union of all specified calling conventions.
797839

840+
```rust
841+
extern "sysv64" fn foo() -> i32{ 0 }
842+
extern "win64" fn bar(x: i32) -> i32{ x + 1}
843+
# #[cfg(target_arch = "x86_64")] {
844+
let z: i32;
845+
unsafe { core::arch::asm!("call {}", "mov ecx, eax", "call {}", sym foo, sym bar, out("rax") z, clobber_abi("C")); }
846+
assert_eq!(z, 1);
847+
# }
848+
```
849+
798850
r[asm.abi-clobbers.must-specify]
799851
Generic register class outputs are disallowed by the compiler when `clobber_abi` is used: all outputs must specify an explicit register.
800852

853+
```rust,compile_fail
854+
extern "C" fn foo(x: i32) -> i32{ 0 }
855+
# #[cfg(target_arch = "x86_64")] {
856+
let z: i32;
857+
unsafe { core::arch::asm!("mov eax, {:e}", "call {}", out(reg) z, sym foo, clobber_abi("C")); }
858+
assert_eq!(z, 0);
859+
# }
860+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
861+
```
862+
801863
r[asm.abi-clobbers.explicit-have-precedence]
802864
Explicit register outputs have precedence over the implicit clobbers inserted by `clobber_abi`: a clobber will only be inserted for a register if that register is not used as an output.
803865

@@ -834,16 +896,53 @@ r[asm.options.supported-options.pure]
834896
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.
835897
The `pure` option must be combined with either the `nomem` or `readonly` options, otherwise a compile-time error is emitted.
836898

899+
```rust
900+
# #[cfg(target_arch = "x86_64")] {
901+
let x: i32 = 0;
902+
let z: i32;
903+
unsafe { core::arch::asm!("inc {}", inout(reg) x => z, options(pure, nomem)); }
904+
assert_eq!(z, 1);
905+
# }
906+
```
907+
908+
```rust,compile_fail
909+
# #[cfg(target_arch = "x86_64")] {
910+
let z: i32;
911+
unsafe { core::arch::asm!("inc {}", inout(reg) x => z, options(pure)); }
912+
assert_eq!(z, 0);
913+
# }
914+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
915+
```
916+
837917
r[asm.options.supported-options.nomem]
838918
- `nomem`: The `asm!` block does not read from or write to any memory accessible outside of the `asm!` block.
839919
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!`.
840920
The compiler also assumes that this `asm!` block does not perform any kind of synchronization with other threads, e.g. via fences.
841921

922+
<!-- no_run: This test has unpredictable or undefined behaviour at runtime -->
923+
```rust,no_run
924+
# #[cfg(target_arch = "x86_64")] {
925+
let mut x = 0i32;
926+
let z: i32;
927+
// The following line has undefined behaviour
928+
unsafe { core::arch::asm!("mov {val:e}, dword ptr [{ptr}]", ptr = in(reg) &mut x, val = lateout(reg) z, options(nomem))}
929+
# }
930+
```
931+
842932
r[asm.options.supported-options.readonly]
843933
- `readonly`: The `asm!` block does not write to any memory accessible outside of the `asm!` block.
844934
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!`.
845935
The compiler also assumes that this `asm!` block does not perform any kind of synchronization with other threads, e.g. via fences.
846936

937+
<!-- no_run: This test has undefined behaviour at runtime -->
938+
```rust,no_run
939+
# #[cfg(target_arch = "x86_64")] {
940+
let mut x = 0;
941+
// The following line has undefined behaviour
942+
unsafe { core::arch::asm!("mov dword ptr[{}], 1", in(reg) &mut x, options(readonly))}
943+
# }
944+
```
945+
847946
r[asm.options.supported-options.preserves_flags]
848947
- `preserves_flags`: The `asm!` block does not modify the flags register (defined in the rules below).
849948
This allows the compiler to avoid recomputing the condition flags after the `asm!` block.
@@ -853,14 +952,50 @@ r[asm.options.supported-options.noreturn]
853952
Behavior is undefined if execution falls through past the end of the asm code.
854953
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.
855954

955+
<!-- no_run: This test aborts at runtime -->
956+
```rust,no_run
957+
fn main() -> !{
958+
# #[cfg(target_arch = "x86_64")] {
959+
// We can use an instruction to trap execution inside of a noreturn block
960+
unsafe { core::arch::asm!("ud2", options(noreturn)); }
961+
# }
962+
}
963+
```
964+
965+
<!-- no_run: Test has undefined behaviour at runtime -->
966+
```rust,no_run
967+
# #[cfg(target_arch = "x86_64")] {
968+
// You are responsible for not falling past the end of a noreturn asm block
969+
unsafe { core::arch::asm!("", options(noreturn)); }
970+
# }
971+
```
972+
856973
r[asm.options.supported-options.nostack]
857974
- `nostack`: The `asm!` block does not push data to the stack, or write to the stack red-zone (if supported by the target).
858975
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.
859976

977+
<!-- no_run: Test has undefined behaviour at runtime -->
978+
```rust,no_run
979+
# #[cfg(target_arch = "x86_64")] {
980+
// `push` and `pop` are UB when used with nostack
981+
unsafe { core::arch::asm!("push rax", "pop rax", options(nostack)); }
982+
# }
983+
```
984+
860985
r[asm.options.supported-options.att_syntax]
861986
- `att_syntax`: This option is only valid on x86, and causes the assembler to use the `.att_syntax prefix` mode of the GNU assembler.
862987
Register operands are substituted in with a leading `%`.
863988

989+
```rust
990+
# #[cfg(target_arch = "x86_64")] {
991+
let x: i32;
992+
let y = 1i32;
993+
// We need to use AT&T Syntax here. src, dest order for operands
994+
unsafe { core::arch::asm!("mov {y:e}, {x:e}", x = lateout(reg) x, y = in(reg) y, options(att_syntax)); }
995+
assert_eq!(x, y);
996+
# }
997+
```
998+
864999
r[asm.options.supported-options.raw]
8651000
- `raw`: This causes the template string to be parsed as a raw assembly string, with no special handling for `{` and `}`.
8661001
This is primarily useful when including raw assembly code from an external file using `include_str!`.
@@ -871,16 +1006,51 @@ The compiler performs some additional checks on options:
8711006
r[asm.options.checks.mutually-exclusive]
8721007
- The `nomem` and `readonly` options are mutually exclusive: it is a compile-time error to specify both.
8731008

1009+
```rust,compile_fail
1010+
# #[cfg(target_arch = "x86_64")] {
1011+
// nomem is strictly stronger than readonly, they can't be specified together
1012+
unsafe { core::arch::asm!("", options(nomem, readonly)); }
1013+
# }
1014+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
1015+
```
1016+
8741017
r[asm.options.checks.pure]
8751018
- It is a compile-time error to specify `pure` on an asm block with no outputs or only discarded outputs (`_`).
8761019

1020+
```rust,compile_fail
1021+
# #[cfg(target_arch = "x86_64")] {
1022+
// pure blocks need at least one output
1023+
unsafe { core::arch::asm!("", options(pure)); }
1024+
# }
1025+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
1026+
```
1027+
8771028
r[asm.options.checks.noreturn]
8781029
- It is a compile-time error to specify `noreturn` on an asm block with outputs.
8791030

1031+
```rust,compile_fail
1032+
# #[cfg(target_arch = "x86_64")] {
1033+
let z: i32;
1034+
// noreturn can't have outputs
1035+
unsafe { core::arch::asm!("mov {:e}, 1", out(reg) z, options(noreturn)); }
1036+
# }
1037+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
1038+
```
1039+
8801040
r[asm.options.global_asm-restriction]
8811041
`global_asm!` only supports the `att_syntax` and `raw` options.
8821042
The remaining options are not meaningful for global-scope inline assembly
8831043

1044+
```rust,compile_fail
1045+
# fn main(){}
1046+
1047+
# #[cfg(target_arch = "x86_64")]
1048+
// nomem is useless on global_asm!
1049+
core::arch::global_asm!("", options(nomem));
1050+
1051+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
1052+
```
1053+
8841054
r[asm.rules]
8851055
## Rules for inline assembly
8861056

0 commit comments

Comments
 (0)