Skip to content

Commit be920cf

Browse files
committed
Add additional inline-assembly tests/examples
1 parent 303275b commit be920cf

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
@@ -552,8 +552,16 @@ The availability of supported types for a particular register class may depend o
552552

553553
> **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).
554554
555+
```rust
556+
# #[cfg(target_arch = "x86_64")] {
557+
let x = 5i32;
558+
let y = -1i8;
559+
let z = unsafe{core::arch::x86_64::_mm_set_epi64x(1,0)};
560+
unsafe { core::arch::asm!("/* {} {} {} */", in(reg) x, in(reg_byte) y, in(xmm_reg) z, out("tmm0") _); }
561+
# }
562+
```
563+
555564
```rust,compile_fail
556-
let x = 5;
557565
# #[cfg(target_arch = "x86_64")] {
558566
let z = unsafe{core::arch::x86_64::_mm_set_epi64x(1,0)};
559567
// We can't pass an `__m128i` to a `reg` input
@@ -673,6 +681,14 @@ Here is the list of all supported register aliases:
673681
| LoongArch | `$f[8-23]` | `$ft[0-15]` |
674682
| LoongArch | `$f[24-31]` | `$fs[0-7]` |
675683

684+
```rust
685+
# #[cfg(target_arch = "x86_64")] {
686+
let z = 0i64;
687+
// rax is an alias for eax and ax
688+
unsafe { core::arch::asm!("", in("rax") z); }
689+
# }
690+
```
691+
676692
r[asm.register-names.not-for-io]
677693
Some registers cannot be used for input or output operands:
678694

@@ -693,6 +709,14 @@ Some registers cannot be used for input or output operands:
693709
| LoongArch | `$r2` or `$tp` | This is reserved for TLS. |
694710
| LoongArch | `$r21` | This is reserved by the ABI. |
695711

712+
```rust,compile_fail
713+
# #[cfg(target_arch = "x86_64")] {
714+
// bp is reserved
715+
unsafe { core::arch::asm!("", in("bp") 5i32); }
716+
# }
717+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
718+
```
719+
696720
r[asm.register-names.fp-bp-reserved]
697721
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.
698722

@@ -707,6 +731,15 @@ These modifiers do not affect register allocation, but change the way operands a
707731
r[asm.template-modifiers.only-one]
708732
Only one modifier is allowed per template placeholder.
709733

734+
```rust,compile_fail
735+
# #[cfg(target_arch = "x86_64")] {
736+
// bp is reserved
737+
unsafe { core::arch::asm!("/* {:er}", in(reg) 5i32); }
738+
# }
739+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
740+
```
741+
742+
710743
r[asm.template-modifiers.supported-modifiers]
711744
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.
712745

@@ -770,12 +803,41 @@ r[asm.abi-clobbers.intro]
770803
The `clobber_abi` keyword can be used to apply a default set of clobbers to an `asm!` block.
771804
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).
772805

806+
```rust
807+
extern "C" fn foo() -> i32{ 0 }
808+
# #[cfg(target_arch = "x86_64")] {
809+
let z: i32;
810+
unsafe { core::arch::asm!("call {}", sym foo, out("rax") z, clobber_abi("C")); }
811+
assert_eq!(z, 0);
812+
# }
813+
```
814+
773815
r[asm.abi-clobbers.many]
774816
`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.
775817

818+
```rust
819+
extern "sysv64" fn foo() -> i32{ 0 }
820+
extern "win64" fn bar(x: i32) -> i32{ x + 1}
821+
# #[cfg(target_arch = "x86_64")] {
822+
let z: i32;
823+
unsafe { core::arch::asm!("call {}", "mov ecx, eax", "call {}", sym foo, sym bar, out("rax") z, clobber_abi("C")); }
824+
assert_eq!(z, 1);
825+
# }
826+
```
827+
776828
r[asm.abi-clobbers.must-specify]
777829
Generic register class outputs are disallowed by the compiler when `clobber_abi` is used: all outputs must specify an explicit register.
778830

831+
```rust,compile_fail
832+
extern "C" fn foo(x: i32) -> i32{ 0 }
833+
# #[cfg(target_arch = "x86_64")] {
834+
let z: i32;
835+
unsafe { core::arch::asm!("mov eax, {:e}", "call {}", out(reg) z, sym foo, clobber_abi("C")); }
836+
assert_eq!(z, 0);
837+
# }
838+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
839+
```
840+
779841
r[asm.abi-clobbers.explicit-have-precedence]
780842
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.
781843

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

875+
```rust
876+
# #[cfg(target_arch = "x86_64")] {
877+
let x: i32 = 0;
878+
let z: i32;
879+
unsafe { core::arch::asm!("inc {}", inout(reg) x => z, options(pure, nomem)); }
880+
assert_eq!(z, 1);
881+
# }
882+
```
883+
884+
```rust,compile_fail
885+
# #[cfg(target_arch = "x86_64")] {
886+
let z: i32;
887+
unsafe { core::arch::asm!("inc {}", inout(reg) x => z, options(pure)); }
888+
assert_eq!(z, 0);
889+
# }
890+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
891+
```
892+
813893
r[asm.options.supported-options.nomem]
814894
- `nomem`: The `asm!` block does not read from or write to any memory accessible outside of the `asm!` block.
815895
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!`.
816896
The compiler also assumes that this `asm!` block does not perform any kind of synchronization with other threads, e.g. via fences.
817897

898+
<!-- no_run: This test has unpredictable or undefined behaviour at runtime -->
899+
```rust,no_run
900+
# #[cfg(target_arch = "x86_64")] {
901+
let mut x = 0i32;
902+
let z: i32;
903+
// The following line has undefined behaviour
904+
unsafe { core::arch::asm!("mov {val:e}, dword ptr [{ptr}]", ptr = in(reg) &mut x, val = lateout(reg) z, options(nomem))}
905+
# }
906+
```
907+
818908
r[asm.options.supported-options.readonly]
819909
- `readonly`: The `asm!` block does not write to any memory accessible outside of the `asm!` block.
820910
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!`.
821911
The compiler also assumes that this `asm!` block does not perform any kind of synchronization with other threads, e.g. via fences.
822912

913+
<!-- no_run: This test has undefined behaviour at runtime -->
914+
```rust,no_run
915+
# #[cfg(target_arch = "x86_64")] {
916+
let mut x = 0;
917+
// The following line has undefined behaviour
918+
unsafe { core::arch::asm!("mov dword ptr[{}], 1", in(reg) &mut x, options(readonly))}
919+
# }
920+
```
921+
823922
r[asm.options.supported-options.preserves_flags]
824923
- `preserves_flags`: The `asm!` block does not modify the flags register (defined in the rules below).
825924
This allows the compiler to avoid recomputing the condition flags after the `asm!` block.
@@ -829,14 +928,50 @@ r[asm.options.supported-options.noreturn]
829928
Behavior is undefined if execution falls through past the end of the asm code.
830929
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.
831930

931+
<!-- no_run: This test aborts at runtime -->
932+
```rust,no_run
933+
fn main() -> !{
934+
# #[cfg(target_arch = "x86_64")] {
935+
// We can use an instruction to trap execution inside of a noreturn block
936+
unsafe { core::arch::asm!("ud2", options(noreturn)); }
937+
# }
938+
}
939+
```
940+
941+
<!-- no_run: Test has undefined behaviour at runtime -->
942+
```rust,no_run
943+
# #[cfg(target_arch = "x86_64")] {
944+
// You are responsible for not falling past the end of a noreturn asm block
945+
unsafe { core::arch::asm!("", options(noreturn)); }
946+
# }
947+
```
948+
832949
r[asm.options.supported-options.nostack]
833950
- `nostack`: The `asm!` block does not push data to the stack, or write to the stack red-zone (if supported by the target).
834951
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.
835952

953+
<!-- no_run: Test has undefined behaviour at runtime -->
954+
```rust,no_run
955+
# #[cfg(target_arch = "x86_64")] {
956+
// `push` and `pop` are UB when used with nostack
957+
unsafe { core::arch::asm!("push rax", "pop rax", options(nostack)); }
958+
# }
959+
```
960+
836961
r[asm.options.supported-options.att_syntax]
837962
- `att_syntax`: This option is only valid on x86, and causes the assembler to use the `.att_syntax prefix` mode of the GNU assembler.
838963
Register operands are substituted in with a leading `%`.
839964

965+
```rust
966+
# #[cfg(target_arch = "x86_64")] {
967+
let x: i32;
968+
let y = 1i32;
969+
// We need to use AT&T Syntax here. src, dest order for operands
970+
unsafe { core::arch::asm!("mov {y:e}, {x:e}", x = lateout(reg) x, y = in(reg) y, options(att_syntax)); }
971+
assert_eq!(x, y);
972+
# }
973+
```
974+
840975
r[asm.options.supported-options.raw]
841976
- `raw`: This causes the template string to be parsed as a raw assembly string, with no special handling for `{` and `}`.
842977
This is primarily useful when including raw assembly code from an external file using `include_str!`.
@@ -847,16 +982,51 @@ The compiler performs some additional checks on options:
847982
r[asm.options.checks.mutually-exclusive]
848983
- The `nomem` and `readonly` options are mutually exclusive: it is a compile-time error to specify both.
849984

985+
```rust,compile_fail
986+
# #[cfg(target_arch = "x86_64")] {
987+
// nomem is strictly stronger than readonly, they can't be specified together
988+
unsafe { core::arch::asm!("", options(nomem, readonly)); }
989+
# }
990+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
991+
```
992+
850993
r[asm.options.checks.pure]
851994
- It is a compile-time error to specify `pure` on an asm block with no outputs or only discarded outputs (`_`).
852995

996+
```rust,compile_fail
997+
# #[cfg(target_arch = "x86_64")] {
998+
// pure blocks need at least one output
999+
unsafe { core::arch::asm!("", options(pure)); }
1000+
# }
1001+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
1002+
```
1003+
8531004
r[asm.options.checks.noreturn]
8541005
- It is a compile-time error to specify `noreturn` on an asm block with outputs.
8551006

1007+
```rust,compile_fail
1008+
# #[cfg(target_arch = "x86_64")] {
1009+
let z: i32;
1010+
// noreturn can't have outputs
1011+
unsafe { core::arch::asm!("mov {:e}, 1", out(reg) z, options(noreturn)); }
1012+
# }
1013+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
1014+
```
1015+
8561016
r[asm.options.global_asm-restriction]
8571017
`global_asm!` only supports the `att_syntax` and `raw` options.
8581018
The remaining options are not meaningful for global-scope inline assembly
8591019

1020+
```rust,compile_fail
1021+
# fn main(){}
1022+
1023+
# #[cfg(target_arch = "x86_64")]
1024+
// nomem is useless on global_asm!
1025+
core::arch::global_asm!("", options(nomem));
1026+
1027+
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch");
1028+
```
1029+
8601030
## Rules for inline assembly
8611031

8621032
r[asm.rules]

0 commit comments

Comments
 (0)