This document covers advanced inline assembly usage in the Linux kernel. The focus is asm-goto (as per project goal), but it extends to a few other asm features used in Linux kernel but it not provided by stable Rust as of today.
asm goto
Kernel has hundreds of asm goto
usage, I compiled them into the following categories:
Example is arch/x86/kvm/vmx/vmx.c:
Many other examples in implementing uaccess (user-space access), which can fault and must be handled gracefully. Performance matters a lot in this case too, so using an extra output register and polyfill without asm goto is bad.
The corresponding Rust pattern is:
In this use case, an issue with current design is that the fault
block becomes unsafe. The following workaround can be used to avoid this unsafe:
Example in arch/riscv/lib/csum.c
This corresponds to Rust pattern:
The code inside the block is immediate another control flow transfer. The code inside label block is minimal so current design works fine.
Needed when need to use different code on different CPUs. Too costly to perform function calls.
Example in arch/riscv/include/asm/bitops.h:
The old content (j legacy) is emitted, but will be replaced with a nop if Zbb ISA extension is detected.
The implementation would be more involved in Rust because it can't stringification of constants. It'll probably be something like this:
This is similar to the fault handling case.
Similar to alternative mechanism, but allow run-time switching.
This translates to something like this in Rust:
Note that this static inline function cannot be translated to a function in Rust, due to Rust's lack of i
constraints (covered later).
The usage here is to return true/false from within block and then immediately use the value with if:
This is easily optimised by LLVM's SimplifyCFG pass to remove the additional if. We prefer to have if
to avoid having custom control flow in macros, e.g.
This pattern is most common. Another example is the following Rust impl of ctrl_dep/volatile_cond
(not mainline yet):
Userspace, kernel code limited to selftests only. Jump to different cases to handle different rseq results.
asm goto
with outputsPreferred action is to have asm_goto_outputs
be a separate feature gate, and defer its stabilisation until GCC supports it and LLVM stop miscompiling.
asm!
blockFor the static branch case:
can be written as:
However this adds complexity to a rarely (in terms of code appearance frequency not runtime frequency) used asm feature.
I am not certain what should happen here, need inputs.
Should asm goto
include a fall-through block?
asm!
cannot return value, then adding fall-through block would not help with any of the above use cases.Preferred action would be to not implement it unless a use case arise.
i
constrainti
constraint in C means that a constant is passed to the assembler. This only needs to be assemble-time constant, not compile time. Relocation is also allowed.
In contrast Rust const
is string-interpolation only, and its usage is quite limited.
Currently in Rust sym
can be used to pass address of a static
, but there is no way to embed address of &'static Foo
into assembly.
The idea is to extend const to any CTFE constants. I don't think Rust needs to support use of assemble-time constants, CTFE constants should be sufficient. This would make line 8 & 9 above legal.
Issue: https://github.com/rust-lang/rust/issues/128464
Of course, to be able to pass multiple different pointers to the function still is not ergnomic. This still needs a trait and assoc constant.
If a language feature allows constant function arguments, then this can be:
These const values do not flow into the type system, so they don't need adt_const_params
, and can depend on generics, similar to const {}
block.
This is helpful for other scenarios, e.g. atomic ordering:
or make sure something can be checked in compile time:
Rust notably lacks:
m
memory constraintsri
to allow choose of register or immediate depending on whether the value can be optimised to constant in compile-time. Or rm
to allow a value to be either in memory or register (useful for x86 assembly).