Skip to content

Commit e6402cf

Browse files
authored
Merge pull request #2300 from folkertdev/extern-custom
Add `extern "custom"`
2 parents 6a5392a + 4956ce9 commit e6402cf

4 files changed

Lines changed: 85 additions & 1 deletion

File tree

src/attributes/codegen.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ The `cold` attribute may only be applied to functions with [bodies] --- [closure
110110
111111
<!-- TODO: rustc currently seems to allow cold on a trait function without a body, but it appears to be ignored. I think that may be a bug, and it should at least warn if not reject (like inline does). -->
112112
113+
r[attributes.codegen.cold.extern-custom]
114+
The `cold` attribute may not be applied to an [`extern "custom"` function].
115+
116+
```rust,compile_fail
117+
#[cold] // ERROR: Not allowed.
118+
#[unsafe(naked)]
119+
unsafe extern "custom" fn f() {
120+
core::arch::naked_asm!("ret")
121+
}
122+
```
123+
113124
r[attributes.codegen.cold.duplicates]
114125
Only the first use of `cold` on a function has effect.
115126
@@ -144,6 +155,9 @@ The *`naked` [attribute]* prevents the compiler from emitting a function prologu
144155
> # }
145156
> ```
146157
158+
> [!NOTE]
159+
> The assembly code of a naked function often does not follow the calling convention of any ABI known to the compiler. Such a function should be declared as an [`extern "custom"` function][items.fn.extern.custom].
160+
147161
r[attributes.codegen.naked.syntax]
148162
The `naked` attribute uses the [MetaWord] syntax.
149163
@@ -900,6 +914,7 @@ If the address of the function is taken as a function pointer, the low bit of th
900914
[`-C target-cpu`]: ../../rustc/codegen-options/index.html#target-cpu
901915
[`-C target-feature`]: ../../rustc/codegen-options/index.html#target-feature
902916
[`export_name`]: abi.export_name
917+
[`extern "custom"` function]: items.fn.extern.custom
903918
[`inline` attribute]: attributes.codegen.inline
904919
[`is_aarch64_feature_detected`]: ../../std/arch/macro.is_aarch64_feature_detected.html
905920
[`is_x86_feature_detected`]: ../../std/arch/macro.is_x86_feature_detected.html

src/items/external-blocks.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ r[items.extern.fn.param-patterns]
4848
Patterns are not allowed in parameters, only [IDENTIFIER] or `_` may be used.
4949

5050
r[items.extern.fn.qualifiers]
51-
The `safe` and `unsafe` function qualifiers are allowed, but other function qualifiers (e.g. `const`, `async`, `extern`) are not.
51+
The `safe` and `unsafe` function qualifiers are allowed, but other function qualifiers (e.g. `const`, `async`, `extern`) are not. The `safe` qualifier is rejected in `extern "custom"` blocks.
5252

5353
r[items.extern.fn.foreign-abi]
5454
Functions within external blocks may be called by Rust code, just like functions defined in Rust. The Rust compiler automatically translates between the Rust ABI and the foreign ABI.
@@ -112,6 +112,9 @@ r[items.extern.abi.system]
112112
r[items.extern.abi.unwind]
113113
* `extern "C-unwind"` and `extern "system-unwind"` --- Identical to `"C"` and `"system"`, respectively, but with [different behavior][unwind-behavior] when the callee unwinds (by panicking or throwing a C++ style exception).
114114
115+
r[items.extern.abi.custom]
116+
* `unsafe extern "custom"` --- A custom ABI that is not known to the compiler.
117+
115118
r[items.extern.abi.platform]
116119
There are also some platform-specific ABI strings:
117120

src/items/functions.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,67 @@ With `panic=unwind`, when a `panic` is turned into an abort by a non-unwinding A
262262

263263
For other considerations and limitations regarding unwinding across FFI boundaries, see the [relevant section in the Panic documentation][panic-ffi].
264264

265+
r[items.fn.extern.custom]
266+
### Extern "custom"
267+
268+
r[items.fn.extern.custom.intro]
269+
An `extern "custom"` function has an unknown, custom ABI. The only way to call such a function is via [inline assembly].
270+
271+
> [!EXAMPLE]
272+
> ```rust
273+
> # #[cfg(target_arch = "x86_64")] {
274+
> # use core::arch::{asm, naked_asm};
275+
> #
276+
> /// Adds 1 to `rax`.
277+
> ///
278+
> /// This function uses a custom calling convention: the argument is
279+
> /// passed in `rax`, the result is returned in `rax`, the flags may
280+
> /// be clobbered, and all other registers are preserved.
281+
> #[unsafe(naked)]
282+
> unsafe extern "custom" fn increment() {
283+
> naked_asm!(
284+
> "add rax, 1",
285+
> "ret",
286+
> )
287+
> }
288+
>
289+
> let mut x: u64 = 41;
290+
> // SAFETY: The inline assembly respects the calling convention of
291+
> // `increment`: the argument is passed in `rax`, the result is read
292+
> // from `rax`, and no other registers are affected.
293+
> unsafe {
294+
> asm!(
295+
> "call {}",
296+
> sym increment,
297+
> inout("rax") x,
298+
> );
299+
> }
300+
> assert_eq!(x, 42);
301+
> # }
302+
> ```
303+
304+
r[items.fn.extern.custom.signature]
305+
An `extern "custom"` function must:
306+
307+
- Be `unsafe`.
308+
- Not have any parameters.
309+
- Return the [unit type], with the return type either omitted or written explicitly as `()`.
310+
311+
> [!NOTE]
312+
> The rule is syntactic. The return type may not be a type alias, even one defined to be the [unit type].
313+
>
314+
> ```rust,compile_fail
315+
> type Unit = ();
316+
>
317+
> #[unsafe(naked)]
318+
> unsafe extern "custom" fn f() -> Unit { // ERROR: Not explicit `()`.
319+
> core::arch::naked_asm!("ret")
320+
> }
321+
> ```
322+
323+
r[items.fn.extern.custom.naked]
324+
An `extern "custom"` function definition must be a [naked function].
325+
265326
[forced-unwinding]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html#forced-unwinding
266327
[panic handler]: ../panic.md#the-panic_handler-attribute
267328
[panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries
@@ -646,6 +707,7 @@ fn foo_oof(#[some_inert_attribute] arg: u8) {
646707
[testing attributes]: ../attributes/testing.md
647708
[`cold`]: ../attributes/codegen.md#the-cold-attribute
648709
[`inline`]: ../attributes/codegen.md#the-inline-attribute
710+
[naked function]: ../attributes/codegen.md#the-naked-attribute
649711
[`deprecated`]: ../attributes/diagnostics.md#the-deprecated-attribute
650712
[`doc`]: ../../rustdoc/the-doc-attribute.html
651713
[`must_use`]: ../attributes/diagnostics.md#the-must_use-attribute
@@ -664,3 +726,4 @@ fn foo_oof(#[some_inert_attribute] arg: u8) {
664726
[`VaList<'_>`]: lang-types.va-list
665727
[`VaList`]: lang-types.va-list
666728
[zero-sized]: glossary.zst
729+
[inline assembly]: ../inline-assembly.md

src/types/function-pointer.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ The `unsafe` qualifier indicates that the type's value is an [unsafe function],
5050
r[type.fn-pointer.constraint-variadic]
5151
For the function to be variadic, its `extern` ABI must be one of those listed in [items.extern.variadic.conventions].
5252

53+
r[type.fn-pointer.extern-custom]
54+
An `extern "custom"` function pointer must follow the rules in [items.fn.extern.custom.signature].
55+
5356
r[type.fn-pointer.attributes]
5457
## Attributes on function pointer parameters
5558

0 commit comments

Comments
 (0)