-
Notifications
You must be signed in to change notification settings - Fork 525
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
810ffe2
4ae15f8
9c408be
735d902
f26c33a
2542a2e
6b809b2
70c859b
c9e97cc
a74ce30
14ad4fc
aab5418
9d54480
81ad95f
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. | ||||||
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.
Suggested change
|
||||||
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. | ||||||
|
@@ -384,8 +390,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. | ||||||
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.
Suggested change
|
||||||
|
||||||
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() {} | ||||||
|
@@ -1206,9 +1215,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() {} | ||||||
|
@@ -1362,6 +1375,70 @@ 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
+1413
to
+1439
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.