rust/compiler
Chris Denton 1ca5e4f1c1
Rollup merge of #134213 - folkertdev:stabilize-naked-functions, r=tgross35,Amanieu,traviscross
Stabilize `naked_functions`

tracking issue: https://github.com/rust-lang/rust/issues/90957
request for stabilization on tracking issue: https://github.com/rust-lang/rust/issues/90957#issuecomment-2539270352
reference PR: https://github.com/rust-lang/reference/pull/1689

# Request for Stabilization

Two years later, we're ready to try this again. Even though this issue is already marked as having passed FCP, given the amount of time that has passed and the changes in implementation strategy, we should follow the process again.

## Summary

The `naked_functions` feature has two main parts: the `#[naked]` function attribute, and the `naked_asm!` macro.

An example of a naked function:

```rust
const THREE: usize = 3;

#[naked]
pub extern "sysv64" fn add_n(number: usize) -> usize {
    // SAFETY: the validity of the used registers
    // is guaranteed according to the "sysv64" ABI
    unsafe {
        core::arch::naked_asm!(
            "add rdi, {}",
            "mov rax, rdi",
            "ret",
            const THREE,
        )
    }
}
```

When the `#[naked]` attribute is applied to a function, the compiler won't emit a [function prologue](https://en.wikipedia.org/wiki/Function_prologue_and_epilogue) or epilogue when generating code for this function. This attribute is analogous to [`__attribute__((naked))`](https://developer.arm.com/documentation/100067/0608/Compiler-specific-Function--Variable--and-Type-Attributes/--attribute----naked---function-attribute) in C. The use of this feature allows the programmer to have precise control over the assembly that is generated for a given function.

The body of a naked function must consist of a single `naked_asm!` invocation, a heavily restricted variant of the `asm!` macro: the only legal operands are `const` and `sym`, and the only legal options are `raw` and `att_syntax`. In lieu of specifying operands, the `naked_asm!` within a naked function relies on the function's calling convention to determine the validity of registers.

## Documentation

The Rust Reference: https://github.com/rust-lang/reference/pull/1689
(Previous PR: https://github.com/rust-lang/reference/pull/1153)

## Tests

* [tests/run-make/naked-symbol-visiblity](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) verifies that `pub`, `#[no_mangle]` and `#[linkage = "..."]` work correctly for naked functions
* [tests/codegen/naked-fn](https://github.com/rust-lang/rust/tree/master/tests/codegen/naked-fn) has tests for function alignment, use of generics, and validates the exact assembly output on linux, macos, windows and thumb
* [tests/ui/asm/naked-*](https://github.com/rust-lang/rust/tree/master/tests/ui/asm) tests for incompatible attributes, generating errors around incorrect use of `naked_asm!`, etc

## Interaction with other (unstable) features

### [fn_align](https://github.com/rust-lang/rust/issues/82232)

Combining `#[naked]` with `#[repr(align(N))]` works well, and is tested e.g. here

- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/aligned.rs
- https://github.com/rust-lang/rust/blob/master/tests/codegen/naked-fn/min-function-alignment.rs

It's tested extensively because we do need to explicitly support the `repr(align)` attribute (and make sure we e.g. don't mistake powers of two for number of bytes).

## History

This feature was originally proposed in [RFC 1201](https://github.com/rust-lang/rfcs/pull/1201), filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in [#32410](https://github.com/rust-lang/rust/pull/32410), landing on 2016-03-23. Development languished for several years as it was realized that the semantics given in RFC 1201 were insufficiently specific. To address this, a minimal subset of naked functions was specified by [RFC 2972](https://github.com/rust-lang/rfcs/pull/2972), filed on 2020-08-07 and accepted on 2021-11-16. Prior to the acceptance of RFC 2972, all of the stricter behavior specified by RFC 2972 was implemented as a series of warn-by-default lints that would trigger on existing uses of the `naked` attribute; these lints became hard errors in [#93153](https://github.com/rust-lang/rust/pull/93153) on 2022-01-22. As a result, today RFC 2972 has completely superseded RFC 1201 in describing the semantics of the `naked` attribute.

More recently, the `naked_asm!` macro was added to replace the earlier use of a heavily restricted `asm!` invocation. The `naked_asm!` name is clearer in error messages, and provides a place for documenting the specific requirements of inline assembly in naked functions.

The implementation strategy was changed to emitting a global assembly block. In effect, an extern function

```rust
extern "C" fn foo() {
    core::arch::naked_asm!("ret")
}
```

is emitted as something similar to

```rust
core::arch::global_asm!(
    "foo:",
    "ret"
);

extern "C" {
    fn foo();
}
```

The codegen approach was chosen over the llvm naked function attribute because:

- the rust compiler can guarantee the behavior (no sneaky additional instructions, no inlining, etc.)
- behavior is the same on all backends (llvm, cranelift, gcc, etc)

Finally, there is now an allow list of compatible attributes on naked functions, so that e.g. `#[inline]` is rejected with an error. The `#[target_feature]` attribute on naked functions was later made separately unstable, because implementing it is complex and we did not want to block naked functions themselves on how target features work on them. See also https://github.com/rust-lang/rust/issues/138568.

relevant PRs for these recent changes

- https://github.com/rust-lang/rust/pull/127853
- https://github.com/rust-lang/rust/pull/128651
- https://github.com/rust-lang/rust/pull/128004
- https://github.com/rust-lang/rust/pull/138570
-
### Various historical notes

#### `noreturn`
[RFC 2972](https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md) mentions that naked functions

> must have a body which contains only a single asm!() statement which:
> iii. must contain the noreturn option.

Instead of `asm!`, the current implementation mandates that the body contain a single `naked_asm!` statement. The `naked_asm!` macro is a heavily restricted version of the `asm!` macro, making it easier to talk about and document the rules of assembly in naked functions and give dedicated error messages.

For `naked_asm!`, the behavior of the `asm!`'s `noreturn` option is implicit. The `noreturn` option means that it is UB for control flow to fall through the end of the assembly block. With `asm!`, this option is usually used for blocks that diverge (and thus have no return and can be typed as `!`). With `naked_asm!`, the intent is different: usually naked funtions do return, but they must do so from within the assembly block. The `noreturn` option was used so that the compiler would not itself also insert a `ret` instruction at the very end.

#### padding / `ud2`

A `naked_asm!` block that violates the safety assumption that control flow must not fall through the end of the assembly block is UB. Because no return instruction is emitted, whatever bytes follow the naked function will be executed, resulting in truly undefined behavior. There has been discussion whether rustc should emit an invalid instruction (e.g. `ud2`  on x86) after the `naked_asm!` block to at least fail early in the case of an invalid `naked_asm!`. It was however decided that it is more useful to guarantee that `#[naked]` functions NEVER contain any instructions besides those in the `naked_asm!` block.

# unresolved questions

None

r? ``@Amanieu``

I've validated the tests on x86_64 and aarch64
2025-04-21 18:53:15 +00:00
..
rustc Revert "Use workspace lints for crates in compiler/ #138084" 2025-03-10 18:12:47 +08:00
rustc_abi Initial UnsafePinned/UnsafeUnpin impl [Part 1: Libs] 2025-04-13 01:11:04 -04:00
rustc_arena Remove #![warn(unreachable_pub)] from all compiler/ crates. 2025-03-11 13:14:21 +11:00
rustc_ast Rollup merge of #139615 - nnethercote:rm-name_or_empty, r=jdonszelmann 2025-04-18 05:16:29 +02:00
rustc_ast_ir Use -Wunused_crate_dependencies for compiler crates. 2025-03-20 08:59:43 +11:00
rustc_ast_lowering Rollup merge of #139615 - nnethercote:rm-name_or_empty, r=jdonszelmann 2025-04-18 05:16:29 +02:00
rustc_ast_passes Rollup merge of #139615 - nnethercote:rm-name_or_empty, r=jdonszelmann 2025-04-18 05:16:29 +02:00
rustc_ast_pretty Auto merge of #124141 - nnethercote:rm-Nonterminal-and-TokenKind-Interpolated, r=petrochenkov 2025-04-14 03:56:55 +00:00
rustc_attr_data_structures Remove #[rustc_macro_edition_2021]. 2025-04-20 11:15:46 +02:00
rustc_attr_parsing Remove #[rustc_macro_edition_2021]. 2025-04-20 11:15:46 +02:00
rustc_baked_icu_data Add unreachable_pub to RUSTC_LINT_FLAGS for compiler/ crates. 2025-03-11 13:14:21 +11:00
rustc_borrowck Auto merge of #139114 - m-ou-se:super-let-pin, r=davidtwco 2025-04-19 08:01:53 +00:00
rustc_builtin_macros Make #[naked] an unsafe attribute 2025-04-19 00:03:35 +02:00
rustc_codegen_cranelift stabilize naked_functions 2025-04-20 11:18:38 +02:00
rustc_codegen_gcc Rollup merge of #137953 - RalfJung:simd-intrinsic-masks, r=WaffleLapkin 2025-04-20 13:02:48 +00:00
rustc_codegen_llvm Rollup merge of #137953 - RalfJung:simd-intrinsic-masks, r=WaffleLapkin 2025-04-20 13:02:48 +00:00
rustc_codegen_ssa Rollup merge of #137953 - RalfJung:simd-intrinsic-masks, r=WaffleLapkin 2025-04-20 13:02:48 +00:00
rustc_const_eval Rollup merge of #139974 - Patrick-6:change-visibility, r=RalfJung 2025-04-17 21:53:27 +02:00
rustc_data_structures Switch to diagnostic::on_unimplemented 2025-04-14 01:38:18 +02:00
rustc_driver Remove recursion_limit increases. 2025-04-02 16:25:27 +11:00
rustc_driver_impl Auto merge of #124141 - nnethercote:rm-Nonterminal-and-TokenKind-Interpolated, r=petrochenkov 2025-04-14 03:56:55 +00:00
rustc_error_codes stabilize naked_functions 2025-04-20 11:18:38 +02:00
rustc_error_messages update cfgs 2025-04-09 12:29:59 +01:00
rustc_errors Move eager translation to a method on Diag 2025-04-16 21:38:59 -04:00
rustc_expand Replace infallible name_or_empty methods with fallible name methods. 2025-04-17 09:50:52 +10:00
rustc_feature Rollup merge of #134213 - folkertdev:stabilize-naked-functions, r=tgross35,Amanieu,traviscross 2025-04-21 18:53:15 +00:00
rustc_fluent_macro Replace proc_macro::SourceFile by Span::{file, local_file}. 2025-04-11 15:07:08 +02:00
rustc_fs_util Revert "Use workspace lints for crates in compiler/ #138084" 2025-03-10 18:12:47 +08:00
rustc_graphviz Remove #![warn(unreachable_pub)] from all compiler/ crates. 2025-03-11 13:14:21 +11:00
rustc_hashes Revert "Use workspace lints for crates in compiler/ #138084" 2025-03-10 18:12:47 +08:00
rustc_hir Rollup merge of #139615 - nnethercote:rm-name_or_empty, r=jdonszelmann 2025-04-18 05:16:29 +02:00
rustc_hir_analysis Rollup merge of #138528 - dianne:implicit-deref-patterns, r=Nadrieril 2025-04-18 05:16:28 +02:00
rustc_hir_pretty Fix HIR pretty-printing of fns with just a variadic arg. 2025-04-15 10:41:10 +10:00
rustc_hir_typeck Rollup merge of #139615 - nnethercote:rm-name_or_empty, r=jdonszelmann 2025-04-18 05:16:29 +02:00
rustc_incremental Replace infallible name_or_empty methods with fallible name methods. 2025-04-17 09:50:52 +10:00
rustc_index Add copy_within to IndexSlice 2025-04-15 10:44:28 -04:00
rustc_index_macros In rustc_mir_tranform, iterate over index newtypes instead of ints 2025-04-12 11:53:07 +00:00
rustc_infer Auto merge of #139768 - compiler-errors:split-fold, r=lcnr 2025-04-16 01:46:01 +00:00
rustc_interface Rollup merge of #139042 - compiler-errors:do-not-optimize-switchint, r=saethlin 2025-04-19 19:30:46 +00:00
rustc_lexer Replace rustc_lexer/unescape with rustc-literal-escaper crate 2025-04-04 14:44:45 +02:00
rustc_lint Auto merge of #139949 - matthiaskrgr:rollup-pxc5tsx, r=matthiaskrgr 2025-04-17 11:21:54 +00:00
rustc_lint_defs Replace infallible name_or_empty methods with fallible name methods. 2025-04-17 09:50:52 +10:00
rustc_llvm Update the minimum external LLVM to 19 2025-04-05 11:44:38 -07:00
rustc_log Use -Wunused_crate_dependencies for compiler crates. 2025-03-20 08:59:43 +11:00
rustc_macros Move eager translation to a method on Diag 2025-04-16 21:38:59 -04:00
rustc_metadata Rollup merge of #139834 - ChrisDenton:spf, r=WaffleLapkin 2025-04-19 15:09:35 +00:00
rustc_middle Re-remove AdtFlags::IS_ANONYMOUS 2025-04-18 21:40:53 -04:00
rustc_mir_build Make #[naked] an unsafe attribute 2025-04-19 00:03:35 +02:00
rustc_mir_dataflow Avoid an unwrap in RustcMirAttrs::set_field. 2025-04-17 09:51:32 +10:00
rustc_mir_transform Rollup merge of #140024 - cjgillot:continue-jumping, r=compiler-errors 2025-04-19 19:30:49 +00:00
rustc_monomorphize Rollup merge of #139498 - alexcrichton:wasm-zst-safe, r=wesleywiser 2025-04-17 21:53:24 +02:00
rustc_next_trait_solver Don't assemble non-env/bound candidates if projection is rigid 2025-04-18 01:44:06 +00:00
rustc_parse Auto merge of #140043 - ChrisDenton:rollup-vwf0s9j, r=ChrisDenton 2025-04-20 02:08:02 +00:00
rustc_parse_format Update rustc-literal-escaper version to 0.0.2 2025-04-04 22:26:10 +02:00
rustc_passes stabilize naked_functions 2025-04-20 11:18:38 +02:00
rustc_pattern_analysis Move eager translation to a method on Diag 2025-04-16 21:38:59 -04:00
rustc_privacy Remove recursion_limit increases. 2025-04-02 16:25:27 +11:00
rustc_query_impl Auto merge of #124141 - nnethercote:rm-Nonterminal-and-TokenKind-Interpolated, r=petrochenkov 2025-04-14 03:56:55 +00:00
rustc_query_system Rollup merge of #139236 - Zoxc:anon-counter, r=davidtwco 2025-04-17 00:14:24 +02:00
rustc_resolve Remove #[rustc_macro_edition_2021]. 2025-04-20 11:15:46 +02:00
rustc_sanitizers Rollup merge of #139669 - nnethercote:overhaul-AssocItem, r=oli-obk 2025-04-15 15:47:27 +10:00
rustc_serialize Bump FileEncoder buffer size to 64 kB 2025-04-10 18:52:03 +02:00
rustc_session Rollup merge of #139042 - compiler-errors:do-not-optimize-switchint, r=saethlin 2025-04-19 19:30:46 +00:00
rustc_smir Rollup merge of #139669 - nnethercote:overhaul-AssocItem, r=oli-obk 2025-04-15 15:47:27 +10:00
rustc_span Remove #[rustc_macro_edition_2021]. 2025-04-20 11:15:46 +02:00
rustc_symbol_mangling Rollup merge of #139848 - nnethercote:kw-Empty-5, r=compiler-errors 2025-04-15 21:16:05 +02:00
rustc_target Auto merge of #140043 - ChrisDenton:rollup-vwf0s9j, r=ChrisDenton 2025-04-20 02:08:02 +00:00
rustc_trait_selection Rollup merge of #139091 - mejrs:format, r=compiler-errors 2025-04-19 15:09:33 +00:00
rustc_traits re-use sized fast path 2025-04-09 10:42:26 +00:00
rustc_transmute transmutability: remove NFA intermediate representation 2025-04-20 03:06:59 +00:00
rustc_ty_utils Rollup merge of #139669 - nnethercote:overhaul-AssocItem, r=oli-obk 2025-04-15 15:47:27 +10:00
rustc_type_ir Fix replacing supertrait aliases in ReplaceProjectionWith 2025-04-16 20:05:55 +00:00
rustc_type_ir_macros Split TypeFolder and FallibleTypeFolder 2025-04-15 18:30:35 +00:00
stable_mir let rustc_smir host stable_mir for refactoring 2025-04-05 18:23:07 +08:00