-
Notifications
You must be signed in to change notification settings - Fork 582
naked functions #1689
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
Changes from 15 commits
4ee56d4
69d5534
e42068d
04d5b88
f64c8ca
2401d21
961ef79
6f74661
b17fcf3
5e43776
189004e
5928c4b
1c3e520
0f837d8
c4d68c9
09c8e4a
27b569a
d64c438
ac16cab
aca530a
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,19 @@ 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). | ||
|
|
||
| ```rust | ||
| # #[cfg(target_arch = "x86_64")] { | ||
| # #[unsafe(naked)] | ||
| # extern "C" fn wrapper() { | ||
| core::arch::naked_asm!("/* {} */", const 0); | ||
| # } | ||
| # } | ||
| ``` | ||
|
|
||
| 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 +399,11 @@ assert_eq!(y, 1); | |
| # } | ||
| ``` | ||
|
|
||
| r[asm.operand-type.naked_asm-restriction] | ||
| Because `naked_asm!` defines a whole function body and the compiler cannot emit any additional code to handle operands, 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() {} | ||
|
|
@@ -1206,9 +1224,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 +1384,67 @@ 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. | ||
|
traviscross marked this conversation as resolved.
Outdated
|
||
|
|
||
| r[asm.naked-rules.noreturn] | ||
| - Behavior is undefined if execution falls through to the end of the `naked_asm!` block. | ||
| - every path through the assembly code is expected to terminate with 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. | ||
|
traviscross marked this conversation as resolved.
Outdated
|
||
| - These rules do not apply to memory which is private to the assembly code, such as stack space allocated within the `naked_asm!` block. | ||
|
traviscross marked this conversation as resolved.
Outdated
|
||
|
|
||
| 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. | ||
|
|
||
| ```rust | ||
| # #[cfg(target_arch = "x86_64")] { | ||
| #[unsafe(naked)] | ||
| extern "C-unwind" fn naked_function() { | ||
| 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
+1475
Contributor
Author
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
Contributor
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.
Contributor
Author
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. Locally I still even get errors like this with these versions in the That feature has been stable on nightly for a while, so I must be missing something. Anyway, I believe the current code to be correct, and CI will catch it before merge if there are minor issues.
Contributor
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've had trouble with overrides too on the Reference for reasons I haven't yet worked out. Perhaps if you |
||
| ``` | ||
|
|
||
| r[asm.validity] | ||
| ### Correctness and Validity | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.