Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/build/reference/decorated-names.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ The form of decoration for a C function depends on the calling convention used i
| **`__stdcall`** | Leading underscore (**`_`**) and a trailing at sign (**`@`**) followed by the number of bytes in the parameter list in decimal |
| **`__fastcall`** | Leading and trailing at signs (**`@`**) followed by a decimal number representing the number of bytes in the parameter list |
| **`__vectorcall`** | Two trailing at signs (**`@@`**) followed by a decimal number of bytes in the parameter list |
| **`__preserve_none`** | Two trailing at signs, an underscore and the 'A' character (**`@@_A`**) |

For ARM64EC functions with C linkage (whether compiled as C or by using `extern "C"`), a **`#`** is prepended to the decorated name.

Expand Down
4 changes: 2 additions & 2 deletions docs/c-language/summary-of-declarations.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ms.assetid: 53a5e9e5-1a33-40b5-9dea-7f669b479329
&emsp;*`attribute`*<sup>1</sup> *`attribute-seq`*<sub>opt</sub><sup>1</sup>

*`attribute`*<sup>1, 2</sup>: one of\
&emsp;**`__asm`** **`__based`** **`__cdecl`** **`__clrcall`** **`__fastcall`** **`__inline`** **`__stdcall`** **`__thiscall`** **`__vectorcall`**
&emsp;**`__asm`** **`__based`** **`__cdecl`** **`__clrcall`** **`__fastcall`** **`__inline`** **`__stdcall`** **`__thiscall`** **`__vectorcall`** **`__preserve_none`**

*`init-declarator-list`*:\
&emsp;*`init-declarator`*\
Expand Down Expand Up @@ -209,7 +209,7 @@ ms.assetid: 53a5e9e5-1a33-40b5-9dea-7f669b479329
&emsp;**`_Static_assert`** **`(`** *`constant-expression`* **`,`** *`string-literal`* **`)`** **`;`**

<sup>1</sup> This grammar element is Microsoft-specific.\
<sup>2</sup> For more information about these elements, see [`__asm`](../assembler/inline/asm.md), [`__clrcall`](../cpp/clrcall.md), [`__stdcall`](../cpp/stdcall.md), [`__based`](../cpp/based-grammar.md), [`__fastcall`](../cpp/fastcall.md), [`__thiscall`](../cpp/thiscall.md), [`__cdecl`](../cpp/cdecl.md), [`__inline`](../cpp/inline-functions-cpp.md), and [`__vectorcall`](../cpp/vectorcall.md).\
<sup>2</sup> For more information about these elements, see [`__asm`](../assembler/inline/asm.md), [`__clrcall`](../cpp/clrcall.md), [`__stdcall`](../cpp/stdcall.md), [`__based`](../cpp/based-grammar.md), [`__fastcall`](../cpp/fastcall.md), [`__thiscall`](../cpp/thiscall.md), [`__cdecl`](../cpp/cdecl.md), [`__inline`](../cpp/inline-functions-cpp.md), [`__preserve_none`](../cpp/preserve-none.md) and [`__vectorcall`](../cpp/vectorcall.md).\
<sup>3</sup> This style is obsolete.

## See also
Expand Down
1 change: 1 addition & 0 deletions docs/cpp/argument-passing-and-naming-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ The following calling conventions are supported by the Visual C/C++ compiler.
|[__fastcall](../cpp/fastcall.md)|Callee|Stored in registers, then pushed on stack|
|[__thiscall](../cpp/thiscall.md)|Callee|Pushed on stack; **`this`** pointer stored in ECX|
|[__vectorcall](../cpp/vectorcall.md)|Callee|Stored in registers, then pushed on stack in reverse order (right to left)|
|[__preserve_none](../cpp/preserve-none.md)|Callee|Stored in registers only|

For related information, see [Obsolete Calling Conventions](../cpp/obsolete-calling-conventions.md).

Expand Down
74 changes: 74 additions & 0 deletions docs/cpp/preserve-none.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
description: "Learn more about: __preserve_none"
title: "__preserve_none"
ms.date: 02/11/2026
f1_keywords: ["__preserve_none", "register spilling", "non preserving calling convention"]
helpviewer_keywords: ["__preserve_none keyword"]
---
# __preserve_none
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR reviewers may ask you to change the filename to something like preserve-none.md Might do it now to save yourself the bother later.


**Microsoft Specific**

> [!IMPORTANT]
> The **`__preserve_none`** calling convention is experimental and subject to change.

The **`__preserve_none`** calling convention specifies that arguments to functions are to be passed in registers, with most general-purpose registers treated as volatile. This calling convention is only supported for C programs and only applies to the x64 architecture.

This calling convention is designed to minimize register spilling and improve performance.

The following list shows the implementation of this calling convention.

| Element | Implementation |
|---------|----------------|
| Argument-passing order | Arguments are passed in up to 10 registers in the following order: r13, r14, r15, rbx, rsi, rdi, r9, r8, rdx, rcx. If a hidden parameter is required for struct returns, it is passed in r13 (the first parameter register), reducing available parameter registers to 9. Registers r10-r12 are reserved for various CRT and Windows runtimes. All parameters must be passed through registers; stack-based parameters are not currently supported. |
| Register allocation strategy | To help minimize register spilling, the allocator assigns r9, r8, rdx, and rcx only after other registers have been used. |
| Argument limit | Functions are restricted to a maximum of 10 parameters. An error is generated if this limit is exceeded. |
| Return value convention | Follows the regular x64 calling convention rules. Scalar return values are returned in rax. Structs of size 1, 2, 4, or 8 bytes are returned through the rax register. For structs of other sizes, a pointer to memory allocated by the caller and passed through the hidden parameter is returned in rax. |
| Volatile registers | All general-purpose registers except rsp (stack pointer) and rbp (base pointer) are treated as volatile and do not need to be preserved across function calls. While r10 and r11 are volatile, they are not used for parameter passing to maintain compatibility with existing programs. |
| Nonvolatile registers | Only rsp, rbp, and r12 are nonvolatile. |
| Stack alignment | The stack must maintain 16-byte alignment. |
| Frame pointer | The rbp register and frame chain follow the [/Gy switch](../build/reference/gy-enable-function-level-linking.md) settings. |
| Stack-maintenance responsibility | The callee is responsible for cleaning up its own stack space. |
| Shadow space | A 32 byte shadow space is reserved on the stack to maintain compatibility with profilers and debugging tools. *(Shadow space is a reserved area on the stack where register parameters can be spilled if needed. It's typically 32 bytes (4 registers × 8 bytes each) reserved by the caller for the first 4 register parameters.)* |
| Floating-point support | Floating-point parameters are not supported. |
| Name-decoration convention | Function names are decorated with @@_A suffix. |
| Case-translation convention | No case translation performed. |

## Restrictions and Limitations

The **`__preserve_none`** calling convention has the following restrictions:

- **C only**: Only supported for C programs.
- **x64 only**: Only supported on x64.
- **No floating-point**: Floating-point parameters are not supported.
- **No variadic functions**: Variadic functions (varargs) are not supported.
- **Parameter limit**: Maximum of 10 parameters, all passed through registers.

## Use Cases

The **`__preserve_none`** calling convention is designed for performance-critical scenarios where:

- Most functions in the codebase use the **`__preserve_none`** calling convention
- Used in conjunction with `msvc::musttail` to tail call between functions with no stack usage
- Minimizing register spilling is important for performance
- The codebase is compatible with treating most registers as volatile

## Example

In the following example, the function `ProcessData` uses the **`__preserve_none`** calling convention:

```c
// Example of the __preserve_none keyword
void __preserve_none ProcessData(int a, int b, int c, int d, int e);

// Example of the __preserve_none keyword on function pointer
typedef int (__preserve_none *callback_ptr)(void* context, int value, int flags);
```

**END Microsoft Specific**

## See also

[Argument Passing and Naming Conventions](argument-passing-and-naming-conventions.md)
[x64 Calling Convention](../build/x64-calling-convention.md)
[Keywords](keywords-cpp.md)
2 changes: 2 additions & 0 deletions docs/cpp/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,8 @@ items:
href: ../cpp/thiscall.md
- name: __vectorcall
href: ../cpp/vectorcall.md
- name: __preserve_none
href: ../cpp/preserve-none.md
- name: "Calling example: Function prototype and call"
items:
- name: "Calling example: Function prototype and call"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ The articles in this section of the documentation explain a subset of the error
| Compiler error C7740 | cannot jump to case label |
| Compiler error C7741 | ABI inconsistency: '*function*' was originally assumed to use '`C`' return semantics but now it requires '`C++`' return semantics |
| [Compiler error C7742](compiler-error-c7742.md) | '*identifier*': a forward declaration of an enum can only use a simple identifier |
| Compiler error C7743 | [`__preserve_none` calling convention](../../cpp/preserve-none.md) is not supported |
| Compiler error C7800 | duplicate explicit instantiation definition of '*name*' |
| Compiler error C7801 | '*function*': if one declaration of '*identifier*' has the '`[[msvc::disptach]]`' attribute then all functions must have the attribute |
| Compiler error C7802 | '*identifier*': a capability must resolve to an enumerator |
Expand Down
1 change: 1 addition & 0 deletions docs/error-messages/tool-errors/name-decoration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The following table shows the linker name for various calling conventions.
|Fast call naming convention (**`__fastcall`**)|`@test@0`|`?test@@YIXXZ`|
|Standard call naming convention (**`__stdcall`**)|`_test@0`|`?test@@YGXXZ`|
|Vector call naming convention (**`__vectorcall`**)|`test@@0`|`?test@@YQXXZ`|
|Preserve None naming convention (**`__preserve_none`**)|`test@@_A`|`NA`|

Use `extern "C"` to call a C function from C++. `extern "C"` forces use of the C naming convention for non-class C++ functions. Be aware of compiler switches **/Tc** or **/Tp**, which tell the compiler to ignore the filename extension and compile the file as C or C++, respectively. These options may cause linker names you don't expect.

Expand Down