-
Notifications
You must be signed in to change notification settings - Fork 520
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
naked functions #1689
base: master
Are you sure you want to change the base?
naked functions #1689
Changes from all commits
944211f
53e9fd7
77d0d90
b92de04
99b94d4
f952889
5cf8d87
c557f74
670e8d9
63cab33
68a8076
84c9b7d
0677e1e
54ddde1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,11 @@ r[asm] | |
# Inline assembly | ||
|
||
r[asm.intro] | ||
Support for inline assembly is provided via the [`asm!`] and [`global_asm!`] macros. | ||
Support for inline assembly is provided via the [`asm!`], [`naked_asm!`] and [`global_asm!`] macros. | ||
It can be used to embed handwritten assembly in the assembly output generated by the compiler. | ||
|
||
[`asm!`]: core::arch::asm | ||
[`naked_asm!`]: core::arch::naked_asm | ||
[`global_asm!`]: core::arch::global_asm | ||
|
||
r[asm.stable-targets] | ||
|
@@ -58,14 +59,15 @@ option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nost | |
options := "options(" option *("," option) [","] ")" | ||
operand := reg_operand / clobber_abi / options | ||
asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")" | ||
naked_asm := "naked_asm!(" format_string *("," format_string) *("," operand) [","] ")" | ||
global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" | ||
``` | ||
|
||
r[asm.scope] | ||
## Scope | ||
|
||
r[asm.scope.intro] | ||
Inline assembly can be used in one of two ways. | ||
Inline assembly can be used in one of three ways. | ||
|
||
r[asm.scope.asm] | ||
With the `asm!` macro, the assembly code is emitted in a function scope and integrated into the compiler-generated assembly code of a function. | ||
|
@@ -78,6 +80,10 @@ unsafe { core::arch::asm!("/* {} */", in(reg) 0); } | |
# } | ||
``` | ||
|
||
r[asm.scope.naked_asm] | ||
With the `naked_asm!` macro, the assembly code is emitted in a function scope and constitutes the full assembly code of a function. | ||
The `naked_asm!` macro is only allowed in [naked functions](attributes/codegen.md#the-naked-attribute). | ||
|
||
r[asm.scope.global_asm] | ||
With the `global_asm!` macro, the assembly code is emitted in a global scope, outside a function. | ||
This can be used to hand-write entire functions using assembly code, and generally provides much more freedom to use arbitrary registers and assembler directives. | ||
|
@@ -368,8 +374,11 @@ assert_eq!(y, 1); | |
# } | ||
``` | ||
|
||
r[asm.operand-type.naked_asm-restriction] | ||
Because `naked_asm!` defines a whole function body, it can only use `sym` and `const` operands. | ||
|
||
r[asm.operand-type.global_asm-restriction] | ||
Since `global_asm!` exists outside a function, it can only use `sym` and `const` operands. | ||
Because `global_asm!` exists outside a function, it can only use `sym` and `const` operands. | ||
|
||
```rust,compile_fail | ||
# fn main() {} | ||
|
@@ -1176,9 +1185,13 @@ unsafe { core::arch::asm!("mov {:e}, 1", out(reg) z, options(noreturn)); } | |
# #[cfg(not(target_arch = "x86_64"))] core::compile_error!("Test not supported on this arch"); | ||
``` | ||
|
||
r[asm.options.naked_asm-restriction] | ||
`naked_asm!` only supports the `att_syntax` and `raw` options. | ||
The remaining options are not meaningful because the inline assembly defines the whole function body. | ||
|
||
r[asm.options.global_asm-restriction] | ||
`global_asm!` only supports the `att_syntax` and `raw` options. | ||
The remaining options are not meaningful for global-scope inline assembly | ||
The remaining options are not meaningful for global-scope inline assembly. | ||
|
||
```rust,compile_fail | ||
# fn main() {} | ||
|
@@ -1207,7 +1220,7 @@ r[asm.rules.reg-not-output] | |
Code should not rely on this however since it depends on the results of register allocation. | ||
|
||
r[asm.rules.unwind] | ||
- Behavior is undefined if execution unwinds out of an asm block. | ||
- Behavior is undefined if execution unwinds out of an `asm!` block. | ||
- This also applies if the assembly code calls a function which then unwinds. | ||
|
||
r[asm.rules.mem-same-as-ffi] | ||
|
@@ -1331,6 +1344,70 @@ r[asm.rules.x86-prefix-restriction] | |
r[asm.rules.preserves_flags] | ||
> **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call. | ||
|
||
r[asm.naked-rules] | ||
## Rules for naked inline assembly | ||
|
||
r[asm.naked-rules.intro] | ||
To avoid undefined behavior, these rules must be followed when using function-scope inline assembly in naked functions (`naked_asm!`): | ||
|
||
r[asm.naked-rules.reg-not-input] | ||
- Any registers not used for function inputs according to the calling convention and function signature will contain an undefined value on entry to the `naked_asm!` block. | ||
- An "undefined value" in the context of inline assembly means that the register can (non-deterministically) have any one of the possible values allowed by the architecture. | ||
Notably it is not the same as an LLVM `undef` which can have a different value every time you read it (since such a concept does not exist in assembly code). | ||
|
||
r[asm.naked-rules.reg-not-output] | ||
- Any callee-saved registers must have the same value upon return as they had on entry, otherwise behavior is undefined. | ||
- Caller-saved registes may be used freely, even if they are not used for the return value. | ||
|
||
r[asm.naked-rules.noreturn] | ||
- Behavior is undefined if execution falls through to the end of the `naked_asm!` block. | ||
- the assembly code is expected to contain a return instruction or to diverge | ||
|
||
r[asm.naked-rules.mem-same-as-ffi] | ||
- The set of memory locations that assembly code is allowed to read and write are the same as those allowed for an FFI function. | ||
- Refer to the unsafe code guidelines for the exact rules. | ||
- These rules do not apply to memory which is private to the asm code, such as stack space allocated within the `naked_asm!` block. | ||
|
||
r[asm.naked-rules.black-box] | ||
- The compiler cannot assume that the instructions in the `naked_asm!` block are the ones that will actually be executed. | ||
- This effectively means that the compiler must treat the `naked_asm!` as a black box and only take the interface specification into account, not the instructions themselves. | ||
- Runtime code patching is allowed, via target-specific mechanisms. | ||
|
||
r[asm.naked-rules.unwind] | ||
- Unwinding out of a `naked_asm!` block is allowed. | ||
- For correct behavior, the appropriate assembler directives that emit unwinding metadata must be used. | ||
|
||
<!-- #[naked] is currently unstable, tests would fail if this were marked `rust` --> | ||
```txt | ||
# #[cfg(target_arch = "x86_64")] { | ||
#[naked] | ||
extern "C-unwind" fn naked_function() { | ||
unsafe { | ||
core::arch::naked_asm!( | ||
".cfi_startproc", | ||
"push rbp", | ||
".cfi_def_cfa_offset 16", | ||
".cfi_offset rbp, -16", | ||
"mov rbp, rsp", | ||
".cfi_def_cfa_register rbp", | ||
"", | ||
"call {function}", | ||
"", | ||
"pop rbp", | ||
".cfi_def_cfa rsp, 8", | ||
"ret", | ||
".cfi_endproc", | ||
function = sym function_that_panics, | ||
) | ||
} | ||
} | ||
|
||
extern "C-unwind" fn function_that_panics() { | ||
panic!("unwind!"); | ||
} | ||
# } | ||
Comment on lines
+1382
to
+1408
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think I can currently run this because cc @traviscross There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We won't merge this anyway until the stabilization lands, so you can just update it and leave the CI red until then (add the feature flag and test it locally though). That's what we normally do. |
||
``` | ||
|
||
r[asm.validity] | ||
### Correctness and Validity | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're unwrapping lines on new things added, so go ahead and unwrap these all if you would.