From 7f862424b43e537d9ca4981c6656a5bc0084a6c8 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 19 Jun 2024 16:30:52 -0400 Subject: [PATCH 01/30] Start reformat for inline-assembly chapter --- src/inline-assembly.md | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 9d505a5d1..b85f807e0 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1,21 +1,22 @@ -# Inline assembly +# Inline assembly r[dynamic.asm] -Support for inline assembly is provided via the [`asm!`] and [`global_asm!`] macros. -It can be used to embed handwritten assembly in the assembly output generated by the compiler. +r[dynamic.asm.syntax] +The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] expand to inline assembly syntax when used in the expression position and item position respectively. The macros shall not be expanded in any other context. -[`asm!`]: ../core/arch/macro.asm.html -[`global_asm!`]: ../core/arch/macro.global_asm.html +> [!NOTE] +> The expansion of the macros has no stable syntax equivalent. This section will refer to the expansion of the macro, rather than the surface syntax. -Support for inline assembly is stable on the following architectures: +r[dynamic.asm.safety] +The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. + +r[dynamic.asm.support] +Inline assembly is supported only when compiling for a target using one of the following architectures. A program that contains inline assembly is ill-formed on any other target: - x86 and x86-64 - ARM - AArch64 - RISC-V - LoongArch -The compiler will emit an error if `asm!` is used on an unsupported target. - -## Example ```rust # #[cfg(target_arch = "x86_64")] { @@ -37,11 +38,9 @@ assert_eq!(x, 4 * 6); # } ``` -## Syntax +## Syntax r[dynamic.asm.syntax] -The following ABNF specifies the general syntax: - -```text +```abnf format_string := STRING_LITERAL / RAW_STRING_LITERAL dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout" reg_spec := / "\"" "\"" @@ -51,20 +50,16 @@ clobber_abi := "clobber_abi(" *("," ) [","] ")" option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw" options := "options(" option *("," option) [","] ")" operand := reg_operand / clobber_abi / options -asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")" -global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" +asm_inner := format_string *("," format_string) *("," operand) [","] +asm := "asm!(" asm_inner ")" +global_asm := "global_asm!(" asm_inner ")" ``` -## Scope - -Inline assembly can be used in one of two ways. +## Scope r[dynamic.asm.invocation] -With the `asm!` macro, the assembly code is emitted in a function scope and integrated into the compiler-generated assembly code of a function. -This assembly code must obey [strict rules](#rules-for-inline-assembly) to avoid undefined behavior. -Note that in some cases the compiler may choose to emit the assembly code as a separate function and generate a call to it. +r[dynamic.asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. -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. +r[dynamic.asm.invocation.global_asm] The [`core::arch::global_asm!`] macro shall be expanded in an item context only. The input tokens shall match the `asm_inner` production. If the macro is expanded in a function, the program is ill-formed. ## Template string arguments From 72df943d3a3d38d978cf3dbf4cdf15f5fae94ada Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 2 Jul 2024 10:31:53 -0400 Subject: [PATCH 02/30] Remove rule prefix from section header ids --- src/inline-assembly.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index b85f807e0..f4f04d49d 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1,4 +1,4 @@ -# Inline assembly r[dynamic.asm] +# Inline assembly [dynamic.asm] r[dynamic.asm.syntax] The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] expand to inline assembly syntax when used in the expression position and item position respectively. The macros shall not be expanded in any other context. @@ -38,7 +38,7 @@ assert_eq!(x, 4 * 6); # } ``` -## Syntax r[dynamic.asm.syntax] +## Syntax [dynamic.asm.syntax] ```abnf format_string := STRING_LITERAL / RAW_STRING_LITERAL @@ -55,7 +55,7 @@ asm := "asm!(" asm_inner ")" global_asm := "global_asm!(" asm_inner ")" ``` -## Scope r[dynamic.asm.invocation] +## Scope [dynamic.asm.invocation] r[dynamic.asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. From 5a75b5154f42d096b25943d6c728b1ce64245351 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 3 Jul 2024 10:48:20 -0400 Subject: [PATCH 03/30] Add [dynamic.asm.template] and [dynamic.asm.operands] --- src/inline-assembly.md | 205 ++++++++++++++++++++++++----------------- 1 file changed, 122 insertions(+), 83 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index f4f04d49d..e304ac20f 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -44,92 +44,139 @@ assert_eq!(x, 4 * 6); format_string := STRING_LITERAL / RAW_STRING_LITERAL dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout" reg_spec := / "\"" "\"" -operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_" +input_expr := expr +output_expr := expr / "_" +inout_expr := input_expr ["=>" output_expr] +operand_expr := input_expr / output_expr / inout_expr +sym_expr := path reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr +sym_operand := [ident "="] "sym" sym_expr clobber_abi := "clobber_abi(" *("," ) [","] ")" option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw" options := "options(" option *("," option) [","] ")" -operand := reg_operand / clobber_abi / options +operand := reg_operand / sym_operand / clobber_abi / options asm_inner := format_string *("," format_string) *("," operand) [","] asm := "asm!(" asm_inner ")" global_asm := "global_asm!(" asm_inner ")" + +non_format_char := ANY_CHAR // except "{" and "}" +operand_specifier := ident / DEC_LITERAL +expansion_specifier := *non_format_char +format_specifier := "{" [operand_specifier] [":" *expansion_specifier] "}" +format_escape := "{{" / "}}" +asm_string_piece := non_format_char / format_specifier / format_escape +asm_string_content := [*asm_string_piece] ``` ## Scope [dynamic.asm.invocation] -r[dynamic.asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. - -r[dynamic.asm.invocation.global_asm] The [`core::arch::global_asm!`] macro shall be expanded in an item context only. The input tokens shall match the `asm_inner` production. If the macro is expanded in a function, the program is ill-formed. - -## Template string arguments - -The assembler template uses the same syntax as [format strings][format-syntax] (i.e. placeholders are specified by curly braces). -The corresponding arguments are accessed in order, by index, or by name. -However, implicit named arguments (introduced by [RFC #2795][rfc-2795]) are not supported. - -An `asm!` invocation may have one or more template string arguments; an `asm!` with multiple template string arguments is treated as if all the strings were concatenated with a `\n` between them. -The expected usage is for each template string argument to correspond to a line of assembly code. -All template string arguments must appear before any other arguments. - -As with format strings, positional arguments must appear before named arguments and explicit [register operands](#register-operands). - -Explicit register operands cannot be used by placeholders in the template string. -All other named and positional operands must appear at least once in the template string, otherwise a compiler error is generated. - -The exact assembly code syntax is target-specific and opaque to the compiler except for the way operands are substituted into the template string to form the code passed to the assembler. - -Currently, all supported targets follow the assembly code syntax used by LLVM's internal assembler which usually corresponds to that of the GNU assembler (GAS). -On x86, the `.intel_syntax noprefix` mode of GAS is used by default. -On ARM, the `.syntax unified` mode is used. -These targets impose an additional restriction on the assembly code: any assembler state (e.g. the current section which can be changed with `.section`) must be restored to its original value at the end of the asm string. -Assembly code that does not conform to the GAS syntax will result in assembler-specific behavior. -Further constraints on the directives used by inline assembly are indicated by [Directives Support](#directives-support). - -[format-syntax]: ../std/fmt/index.html#syntax -[rfc-2795]: https://github.com/rust-lang/rfcs/pull/2795 - -## Operand type - -Several types of operands are supported: - -* `in() ` - - `` can refer to a register class or an explicit register. - The allocated register name is substituted into the asm template string. - - The allocated register will contain the value of `` at the start of the asm code. - - The allocated register must contain the same value at the end of the asm code (except if a `lateout` is allocated to the same register). -* `out() ` - - `` can refer to a register class or an explicit register. - The allocated register name is substituted into the asm template string. - - The allocated register will contain an undefined value at the start of the asm code. - - `` must be a (possibly uninitialized) place expression, to which the contents of the allocated register are written at the end of the asm code. - - An underscore (`_`) may be specified instead of an expression, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber). -* `lateout() ` - - Identical to `out` except that the register allocator can reuse a register allocated to an `in`. - - You should only write to the register after all inputs are read, otherwise you may clobber an input. -* `inout() ` - - `` can refer to a register class or an explicit register. - The allocated register name is substituted into the asm template string. - - The allocated register will contain the value of `` at the start of the asm code. - - `` must be a mutable initialized place expression, to which the contents of the allocated register are written at the end of the asm code. -* `inout() => ` - - Same as `inout` except that the initial value of the register is taken from the value of ``. - - `` must be a (possibly uninitialized) place expression, to which the contents of the allocated register are written at the end of the asm code. - - An underscore (`_`) may be specified instead of an expression for ``, which will cause the contents of the register to be discarded at the end of the asm code (effectively acting as a clobber). - - `` and `` may have different types. -* `inlateout() ` / `inlateout() => ` - - Identical to `inout` except that the register allocator can reuse a register allocated to an `in` (this can happen if the compiler knows the `in` has the same initial value as the `inlateout`). - - You should only write to the register after all inputs are read, otherwise you may clobber an input. -* `sym ` - - `` must refer to a `fn` or `static`. - - A mangled symbol name referring to the item is substituted into the asm template string. - - The substituted string does not include any modifiers (e.g. GOT, PLT, relocations, etc). - - `` is allowed to point to a `#[thread_local]` static, in which case the asm code can combine the symbol with relocations (e.g. `@plt`, `@TPOFF`) to read from thread-local data. - -Operand expressions are evaluated from left to right, just like function call arguments. -After the `asm!` has executed, outputs are written to in left to right order. -This is significant if two outputs point to the same place: that place will contain the value of the rightmost output. - -Since `global_asm!` exists outside a function, it can only use `sym` operands. +r[dynamic.asm.invocation.asm] +The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. + + +r[dynamic.asm.invocation.global_asm] +The [`core::arch::global_asm!`] macro shall be expanded in an item context only. The input tokens shall match the `asm_inner` production. If the macro is expanded in a function, the program is ill-formed. + +## Template string arguments [dynamic.asm.template] + +r[dynamic.asm.invocation.format-string] +Each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_asm!`] macros shall be an expanded string literal for which the content matches the `asm_string_piece` production. + +>[!NOTE] +> an expanded string literal is a string literal (after expanding macros like [`core::concat!`]) that has had every unicode escape sequence replaced with the (appropriately escaped as needed) matching character, and which has been normalized from a raw string literal. + + +r[dynamic.asm.invocation.concat] +If multiple `format_string` inputs are provided, then they are concatenated as though by the [`core::concat!`] macro, separating each `format_string` with a string containing a single newline character. If any `format_string` begins a `format_specifier` that is not terminated before the end of the `format_string`, the program is ill-formed. The resulting string is known as the *joined asm-string* + +r[dynamic.asm.invocation.operands] +Each operand, other than an explicit register operand ([dynamic.asm.operands.register]) shall be mentioned by at least one format_specifier in the *joined asm-string*. Explicit registers may not be referred to be a format_specifier. + +r[dynamic.asm.invocation.positional] +A `format_specifier` that does not specify an `operand_specifier` is called a positional specifier, and refers to the `nth` successive positional operand, where `n` is `0` for the first positional specifier in the *joined asm-string* and increases by 1 for each successive positional specifier in the *joined asm-string*. + +r[dynamic.asm.invocation.explicit-positional] +A `format_specifier` that has an `operand_specifier` which is a DEC_LITERAL is called an explicit positional specifier, and refers to the `nth` successive positional operand, where `n` is the value of the DEC_LITERAL. + +r[dynamic.asm.invocation.named] +A `format_specifier` that has an `operand_specifier` which is an ident is called a named specifier, and refers to the named operand with the specified name. + +r[dynamic.asm.invocation.expansion] +The *joined asm-string* is expanded as defined in [dynamic.asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. + +r[dynamic.asm.invocation.syntax] +The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the target. Invoking the macro with a *expanded asm-string* that does not match syntax requirements is *conditionally supported* and has *assembler dependent behaviour*. Invoking a directive that is not specified by [dynamic.asm.directives] is *conditionally supported* and has *assembler dependent behaviour*. + +>[TARGET-SPECIFIC] +> On x86 and x86_64 targets, the syntax of the *expanded asm-string* acts as though the directive `.intel_syntax noprefix` is issued before parsing the *expanded asm-string*, except that the `option(att_syntax)` causes the syntax to act as though the directive `.att_syntax prefix` is issued before parsing the *expanded asm-string* instead. +> On ARM and Aarch64 targets, the syntax of the *expanded asm-string* acts as though the directive `.syntax unified` is issued before parsing the *expanded asm-string*. + + +## Operand type [dynamic.asm.operands] + +r[dynamic.asm.operands.positional] +Operands that do not specify an ident and are not explicit register operands are known as positional operands. Positional operands may be referred to only by positional operand specifiers and explicit positional operand specifiers, and each Positional operand must be specified before Named Operands or Explicit Register Operands. + +r[dynamic.asm.operands.named] +Operands that specify an ident are named operands. A named operand shall not specify an explicit register `reg_spec`. Named operand specifiers may be referred to only by named operand specifiers. + +r[dynamic.asm.operands.registers] +Operands that specify an explicit register `reg_spec` are explicit register operands. + +>[!NOTE] +> Explicit Register Operands have no `ident` name and cannot be referred to by an operand specifier + +r[dynamic.asm.operands.types] +Each operand, other than a placeholder expression shall be of an integer type, floating-point type, function pointer type, pointer type, or target-specific vector type. These types are collectively called *asm operand types*. A pointer type is an *asm operand type* only if the pointee type has no metadata-type. + +>[!TARGET-SPECIFIC] +> On x86 platforms, the types [`core::arch::x86::__m128`], [`core::arch::x86::__m256`], and variants of those types are *asm operand types*. + +r[dynamic.asm.operands.in-expr] +An `input_expr` shall be a value expression of an *asm operand type*. + +r[dynamic.asm.operands.out-expr] +An `output_expr` shall be the placeholder expression `_` or a (potentially unitialized) place expression of an *asm operand type*. + +r[dynamic.asm.operands.inout-expr] +An `inout_expr` shall either be an (initialized) place expression of an *asm operand type*, or shall specify both an `input_expr` and an `output_expr`. If only a single expression is specified, it is treated as both the `input_expr` and `output_expr` of the operand. + +r[dynamic.asm.operands.in] +An `in` operand is an reg_operand with the `in` dir_spec. The `operand_expr` of the operand shall be an `input_expr`. The `input_expr` initializes the value of the register before entering the asm block. + +r[dynamic.asm.operands.out] +An `out` operand is a reg_operand with the `out` dir_spec, and a `lateout` operand is a reg_operand with the `lateout` dir_spec. The `operand_expr` of an `out` operand or `lateout` operand shall be an `output_expr`. The value of the register at the exit of the asm block is written to the `output_expr` place if it is not a placeholder expression + +>[!NOTE] +> A `lateout` operand differs from an `out` operand only in that the implementation may assume that no `in`, `inout`, or `inlateout` operands are read after a `lateout` operand is modified by the *expanded asm-string*. + +r[dynamic.asm.operands.inout] +An `inout` operand is a reg_operand with the `inout` dir_spec, and a `inlateout` operand is a reg_operand with the `inlateout` dir_spec. The `operand_expr` of an `inout` operand or an `inlateout` operand shall be an `inout_expr`. The `input_expr` and `output_expr` of an `inout` or `inlateout` operand is used as though the `inout` operand is replaced with a separate `in` and `out` operand, and the `inlateout` operand is replaced with a separate `in` and `lateout` operand, except that both have the same position if they are positional, or the same name if they are named operands, and both refer to the same register. + +>[!NOTE] +> An `inlateout` operand differs from an `inout` operand only in that implementation may assume that no other `in`, `inout`, or `inlateout` operands are read after an `inlateout` operand is modified by the *expanded asm-string*. + + +r[dynamic.asm.operands.clobbers] +An `output_expr` that is the placeholder expression `_` is a clobber output. The resulting value of the register is discarded. + +>[!NOTE] +> Some registers and register classes cannot be used as an operand, other than as a clobber operand. + +r[dynamic.asm.operands.sym-expr] +A sym-expr is a path-expr. If the `path-expr` does not refer to a `static` item or a `fn` item, the program is ill-formed. + +>[!NOTE] +> the path-expr may have any type, including a type that isn't an *asm operand type*, and may be either mutable or immutable. + +r[dynamic.asm.operands.expansion] +Each operand_spec is expanded in the *joined asm-string* according to the modifiers in `modifier_spec` and the operand. Each reg_operand is assigned to a register according to the reg_spec, and expands to the appropriate version of the `reg_operand`, in the format expected by the asm syntax in effect to specify the appropriate register. A sym-expr expand to the linkage name ([dynamic.linkage.name]) of the item referred to by the `path-expr`, if it has either the `#[no_mangle]` or `#[export_name]` attribute, or is defined in an `extern` block, and otherwise, it expands to an unspecified string that can be used within the *expanded asm-string* to refer to the item. + +>[!NOTE] +> The name given to an item used by a sym-expr that does not have a linkage name may be known as the "mangled" name of the item. + +>[!TARGET-SPECIFIC] +> On x86 and x86_64 targets, the register name is expanded as-is if the `options(att_syntax)` is not used, and with the `%` prefix if `options(att_syntax)` is used. ## Register operands @@ -140,14 +187,6 @@ Note that explicit registers treat register aliases (e.g. `r14` vs `lr` on ARM) It is a compile-time error to use the same explicit register for two input operands or two output operands. Additionally, it is also a compile-time error to use overlapping registers (e.g. ARM VFP) in input operands or in output operands. -Only the following types are allowed as operands for inline assembly: -- Integers (signed and unsigned) -- Floating-point numbers -- Pointers (thin only) -- Function pointers -- SIMD vectors (structs defined with `#[repr(simd)]` and which implement `Copy`). -This includes architecture-specific vector types defined in `std::arch` such as `__m128` (x86) or `int8x16_t` (ARM). - Here is the list of currently supported register classes: | Architecture | Register class | Registers | LLVM constraint code | From 0397d44ace34f5fbb08da7c59dcdbeb08560a4e5 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 4 Jul 2024 13:36:43 -0400 Subject: [PATCH 04/30] Add dynamic.asm.options --- src/inline-assembly.md | 139 +++++++++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 53 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index e304ac20f..10320ed4e 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -166,18 +166,29 @@ An `output_expr` that is the placeholder expression `_` is a clobber output. The r[dynamic.asm.operands.sym-expr] A sym-expr is a path-expr. If the `path-expr` does not refer to a `static` item or a `fn` item, the program is ill-formed. + >[!NOTE] > the path-expr may have any type, including a type that isn't an *asm operand type*, and may be either mutable or immutable. +r[dynamic.asm.operand.sym] +A sym operand is an operand that uses the `sym` keyword. The operand contains a `sym-expr` that specifies the item the symbol refers to. + + r[dynamic.asm.operands.expansion] -Each operand_spec is expanded in the *joined asm-string* according to the modifiers in `modifier_spec` and the operand. Each reg_operand is assigned to a register according to the reg_spec, and expands to the appropriate version of the `reg_operand`, in the format expected by the asm syntax in effect to specify the appropriate register. A sym-expr expand to the linkage name ([dynamic.linkage.name]) of the item referred to by the `path-expr`, if it has either the `#[no_mangle]` or `#[export_name]` attribute, or is defined in an `extern` block, and otherwise, it expands to an unspecified string that can be used within the *expanded asm-string* to refer to the item. +Each operand_spec is expanded in the *joined asm-string* according to the modifiers in `modifier_spec` and the operand. Each reg_operand is assigned to a register according to the reg_spec, and expands to the appropriate version of the `reg_operand`, in the format expected by the asm syntax in effect to specify the appropriate register. A sym operand expand to the linkage name ([dynamic.linkage.name]) of the item referred to by the `path-expr`, if it has either the `#[no_mangle]` or `#[export_name]` attribute, or is defined in an `extern` block, and otherwise, it expands to an unspecified string that can be used within the *expanded asm-string* to refer to the item. >[!NOTE] > The name given to an item used by a sym-expr that does not have a linkage name may be known as the "mangled" name of the item. +>[!NOTE] +> A sym operand does not include any relocation modifeirs such as `@plt` or `@tpoff`. The *joined asm-string* is responsible for including these as necessary. + >[!TARGET-SPECIFIC] > On x86 and x86_64 targets, the register name is expanded as-is if the `options(att_syntax)` is not used, and with the `%` prefix if `options(att_syntax)` is used. +r[dynamic.asm.operands.global] +The program shall not use an operand, other than a sym operand in the expansion of the [`core::arch::global_asm!`] macro. + ## Register operands Input and output operands can be specified either as an explicit register or as a register class from which the register allocator can select a register. @@ -442,38 +453,80 @@ The following ABIs can be used with `clobber_abi`: The list of clobbered registers for each ABI is updated in rustc as architectures gain new registers: this ensures that `asm!` clobbers will continue to be correct when LLVM starts using these new registers in its generated code. -## Options - -Flags are used to further influence the behavior of the inline assembly block. -Currently the following options are defined: -- `pure`: The `asm!` block has no side effects, must eventually return, and its outputs depend only on its direct inputs (i.e. the values themselves, not what they point to) or values read from memory (unless the `nomem` options is also set). - This allows the compiler to execute the `asm!` block fewer times than specified in the program (e.g. by hoisting it out of a loop) or even eliminate it entirely if the outputs are not used. - The `pure` option must be combined with either the `nomem` or `readonly` options, otherwise a compile-time error is emitted. -- `nomem`: The `asm!` blocks does not read or write to any memory. - This allows the compiler to cache the values of modified global variables in registers across the `asm!` block since it knows that they are not read or written to by the `asm!`. - The compiler also assumes that this `asm!` block does not perform any kind of synchronization with other threads, e.g. via fences. -- `readonly`: The `asm!` block does not write to any memory. - This allows the compiler to cache the values of unmodified global variables in registers across the `asm!` block since it knows that they are not written to by the `asm!`. - The compiler also assumes that this `asm!` block does not perform any kind of synchronization with other threads, e.g. via fences. -- `preserves_flags`: The `asm!` block does not modify the flags register (defined in the rules below). - This allows the compiler to avoid recomputing the condition flags after the `asm!` block. -- `noreturn`: The `asm!` block never returns, and its return type is defined as `!` (never). - Behavior is undefined if execution falls through past the end of the asm code. - A `noreturn` asm block behaves just like a function which doesn't return; notably, local variables in scope are not dropped before it is invoked. -- `nostack`: The `asm!` block does not push data to the stack, or write to the stack red-zone (if supported by the target). - If this option is *not* used then the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call. -- `att_syntax`: This option is only valid on x86, and causes the assembler to use the `.att_syntax prefix` mode of the GNU assembler. - Register operands are substituted in with a leading `%`. -- `raw`: This causes the template string to be parsed as a raw assembly string, with no special handling for `{` and `}`. - This is primarily useful when including raw assembly code from an external file using `include_str!`. - -The compiler performs some additional checks on options: -- The `nomem` and `readonly` options are mutually exclusive: it is a compile-time error to specify both. -- It is a compile-time error to specify `pure` on an asm block with no outputs or only discarded outputs (`_`). -- It is a compile-time error to specify `noreturn` on an asm block with outputs. - -`global_asm!` only supports the `att_syntax` and `raw` options. -The remaining options are not meaningful for global-scope inline assembly +## Behaviour of an asm block [dynamic.asm.evaluation] + +r[dynamic.asm.evaluation.general] +Each evaluation of an assembly block shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. + +>[!NOTE] +> The operation the assembly block performs may differ between evaluations of the same assembly block. + +>[!TARGET-SPECIFIC] +> The correspondance between the operation performed by the assembly block is target-dependant and implementation-dependant, subject to the rules set in [dynamic.asm.operands]. + +r[dynamic.asm.evaluation.constraints] +Certain constraints may be placed on the asm block, by default or + +## Options [dynamic.asm.options] + +r[dynamic.asm.options.general] +An options-spec provided in the asm invocation places constraints on the assembly block. + +r[dynamic.asm.options.att_syntax] +The `att_syntax` option may be specfied on the x86 and x86_64 target. The program shall not specify the `att_syntax` option on any other target. + +>[!TARGET-SPECIFIC] +> The `att_syntax` option modifies the syntax used to parse the *expanded asm-string* as though the `.att_syntax prefix` directive was issued before parsing the *expanded asm-string*, and modifies the expansion of register operands to include a `%` prefix. + +r[dynamic.asm.options.nomem] +The `nomem` option may be specified. The behaviour is undefined if the assembly block modifies any allocation, disables or activates any tag, *synchronizes-with* any other thread of execution or signal handler, and the implementation may assume that the behaviour or outputs of the assembly block does not depend on the contents of any allocation. + +r[dynamic.asm.options.readonly] +The `readonly` option may be specified. The behaviour is undefined if the assembly block modifies any allocation or activates any tag. + +r[dynamic.asm.options.exclusive] +The program shall not specify both the `nomem` and `readonly` options. + +r[dynamic.asm.options.pure] +The `pure` option may be specfied. The evaluation of the assembly block shall not produce any observable behaviour or consume input, and the implementation may assume that the outputs of the assembly block depends only on the inputs and the contents of any allocation. If the program specifies the `pure` option, it shall specify either the `nomem` or `readonly` option. + +r[dynamic.asm.options.nostack] +The `nostack` option may be specified. The implementation may assume that the assembly block does not modify or access the stack, except an allocation placed in that region by the implementation. + +>[!TARGET-SPECIFIC] +> The stack is defined by an target-specific register and is a target-specific memory region. It may include a "red zone". +> On x86 and x86-64 targets, if the `nostack` option is not specified, the `rsp` register will be aligned to 16 bytes. + +r[dynamic.asm.options.preserve_flags] +The `preserves_flags` option may be specified. The implementation may assume that the value of the status flags are preserved by the assembly block. + +>[!TARGET-SPECFIC] +> - These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set: +> - x86 +> - Status flags in `EFLAGS` (CF, PF, AF, ZF, SF, OF). +> - Floating-point status word (all). +> - Floating-point exception flags in `MXCSR` (PE, UE, OE, ZE, DE, IE). +> - ARM +> - Condition flags in `CPSR` (N, Z, C, V) +> - Saturation flag in `CPSR` (Q) +> - Greater than or equal flags in `CPSR` (GE). +> - Condition flags in `FPSCR` (N, Z, C, V) +> - Saturation flag in `FPSCR` (QC) +> - Floating-point exception flags in `FPSCR` (IDC, IXC, UFC, OFC, DZC, IOC). +> - AArch64 +> - Condition flags (`NZCV` register). +> - Floating-point status (`FPSR` register). +> - RISC-V +> - Floating-point exception flags in `fcsr` (`fflags`). +> - Vector extension state (`vtype`, `vl`, `vcsr`). +> - LoongArch +> - Floating-point condition flags in `$fcc[0-7]`. + +r[dynamic.asm.options.noreturn] +The `noreturn` option may be specifed. An invocation of the [`core::arch::asm!`] macro that specifies the `noreturn` option expands to an expression of type `!`. The behaviour is undefined if an evaluation of the assembly block exits. The program shall not specify the `clobber_abi` specification, or an operand that is an `out`, `lateout`, `inout`, or `inlateout` operand. + +r[dynamic.asm.options.global] +A program shall not specify an option, other than the `att_syntax` option, in an invocation of the [`core::arch::global_asm!`] macro. ## Rules for inline assembly @@ -507,26 +560,6 @@ To avoid undefined behavior, these rules must be followed when using function-sc Behavior is also undefined if two executions of the `asm!` code with the same inputs result in different outputs. - When used with the `nomem` option, "inputs" are just the direct inputs of the `asm!`. - When used with the `readonly` option, "inputs" comprise the direct inputs of the `asm!` and any memory that the `asm!` block is allowed to read. -- These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set: - - x86 - - Status flags in `EFLAGS` (CF, PF, AF, ZF, SF, OF). - - Floating-point status word (all). - - Floating-point exception flags in `MXCSR` (PE, UE, OE, ZE, DE, IE). - - ARM - - Condition flags in `CPSR` (N, Z, C, V) - - Saturation flag in `CPSR` (Q) - - Greater than or equal flags in `CPSR` (GE). - - Condition flags in `FPSCR` (N, Z, C, V) - - Saturation flag in `FPSCR` (QC) - - Floating-point exception flags in `FPSCR` (IDC, IXC, UFC, OFC, DZC, IOC). - - AArch64 - - Condition flags (`NZCV` register). - - Floating-point status (`FPSR` register). - - RISC-V - - Floating-point exception flags in `fcsr` (`fflags`). - - Vector extension state (`vtype`, `vl`, `vcsr`). - - LoongArch - - Floating-point condition flags in `$fcc[0-7]`. - On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit. - Behavior is undefined if the direction flag is set on exiting an asm block. - On x86, the x87 floating-point register stack must remain unchanged unless all of the `st([0-7])` registers have been marked as clobbered with `out("st(0)") _, out("st(1)") _, ...`. From 2fdf1fb24476211773e55a46df8bdb393745c632 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 9 Jul 2024 10:39:11 -0400 Subject: [PATCH 05/30] Finish dynamic.asm.evaluation and dynamic.asm.directives --- src/inline-assembly.md | 283 +++++++++++++++++------------------------ 1 file changed, 116 insertions(+), 167 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 10320ed4e..fa43ca716 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -68,7 +68,7 @@ asm_string_piece := non_format_char / format_specifier / format_escape asm_string_content := [*asm_string_piece] ``` -## Scope [dynamic.asm.invocation] +## Invocation [dynamic.asm.invocation] r[dynamic.asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. @@ -77,8 +77,6 @@ The [`core::arch::asm!`] macro shall be expanded in an expression context only. r[dynamic.asm.invocation.global_asm] The [`core::arch::global_asm!`] macro shall be expanded in an item context only. The input tokens shall match the `asm_inner` production. If the macro is expanded in a function, the program is ill-formed. -## Template string arguments [dynamic.asm.template] - r[dynamic.asm.invocation.format-string] Each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_asm!`] macros shall be an expanded string literal for which the content matches the `asm_string_piece` production. @@ -111,6 +109,22 @@ The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the > On x86 and x86_64 targets, the syntax of the *expanded asm-string* acts as though the directive `.intel_syntax noprefix` is issued before parsing the *expanded asm-string*, except that the `option(att_syntax)` causes the syntax to act as though the directive `.att_syntax prefix` is issued before parsing the *expanded asm-string* instead. > On ARM and Aarch64 targets, the syntax of the *expanded asm-string* acts as though the directive `.syntax unified` is issued before parsing the *expanded asm-string*. +r[dynamic.asm.invocation.duplication] +The number of times, locations, and the order in which a given invocation of [`core::arch::asm!`] is expanded is unspecified. + +>[!NOTE] +> In particular, an asm block may be duplicated, for example if the containing function is inlined, or omitted from the output entirely. +> As a consequence, asm blocks should not use directives that have non-idempotent non-local effects, or named labels and symbol definitions. +> Additionally, two asm blocks may not rely upon being adjacent in executable memory, even if they are adjacent in the source. + +r[dynamic.asm.invocation.global-order] +The order in which invocations of [`core::arch::global_asm!`] are expanded is unspecified. + +r[dynamic.asm.invocation.directive-state] +The *expanded asm-string* shall not issue a directive that modifies the global state of the assembler for processing inputs unless it issues a directive to restore that state it had upon entering the block. No diagnostic is required. + +>[!NOTE] +> This include state such as the current section of the assembler, the syntax mode, or the kind of assembly output being generated. ## Operand type [dynamic.asm.operands] @@ -187,7 +201,7 @@ Each operand_spec is expanded in the *joined asm-string* according to the modifi > On x86 and x86_64 targets, the register name is expanded as-is if the `options(att_syntax)` is not used, and with the `%` prefix if `options(att_syntax)` is used. r[dynamic.asm.operands.global] -The program shall not use an operand, other than a sym operand in the expansion of the [`core::arch::global_asm!`] macro. +The program shall not use an operand, other than a sym operand, in the expansion of the [`core::arch::global_asm!`] macro. ## Register operands @@ -438,17 +452,18 @@ Generic register class outputs are disallowed by the compiler when `clobber_abi` Explicit register outputs have precedence over the implicit clobbers inserted by `clobber_abi`: a clobber will only be inserted for a register if that register is not used as an output. The following ABIs can be used with `clobber_abi`: -| Architecture | ABI name | Clobbered registers | -| ------------ | -------- | ------------------- | -| x86-32 | `"C"`, `"system"`, `"efiapi"`, `"cdecl"`, `"stdcall"`, `"fastcall"` | `ax`, `cx`, `dx`, `xmm[0-7]`, `mm[0-7]`, `k[0-7]`, `st([0-7])` | -| x86-64 | `"C"`, `"system"` (on Windows), `"efiapi"`, `"win64"` | `ax`, `cx`, `dx`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `k[0-7]`, `st([0-7])`, `tmm[0-7]` | -| x86-64 | `"C"`, `"system"` (on non-Windows), `"sysv64"` | `ax`, `cx`, `dx`, `si`, `di`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `k[0-7]`, `st([0-7])`, `tmm[0-7]` | -| AArch64 | `"C"`, `"system"`, `"efiapi"` | `x[0-17]`, `x18`\*, `x30`, `v[0-31]`, `p[0-15]`, `ffr` | -| ARM | `"C"`, `"system"`, `"efiapi"`, `"aapcs"` | `r[0-3]`, `r12`, `r14`, `s[0-15]`, `d[0-7]`, `d[16-31]` | -| RISC-V | `"C"`, `"system"`, `"efiapi"` | `x1`, `x[5-7]`, `x[10-17]`, `x[28-31]`, `f[0-7]`, `f[10-17]`, `f[28-31]`, `v[0-31]` | -| LoongArch | `"C"`, `"system"`, `"efiapi"` | `$r1`, `$r[4-20]`, `$f[0-23]` | - -> Notes: +>[!TARGET-SPECIFIC] +> | Architecture | ABI name | Clobbered registers | +> | ------------ | -------- | ------------------- | +> | x86-32 | `"C"`, `"system"`, `"efiapi"`, `"cdecl"`, `"stdcall"`, `"fastcall"` | `ax`, `cx`, `dx`, `xmm[0-7]`, `mm[0-7]`, `k[0-7]`, `st([0-7])` | +> | x86-64 | `"C"`, `"system"` (on Windows), `"efiapi"`, `"win64"` | `ax`, `cx`, `dx`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `k[0-7]`, `st([0-7])`, `tmm[0-7]` | +> | x86-64 | `"C"`, `"system"` (on non-Windows), `"sysv64"` | `ax`, `cx`, `dx`, `si`, `di`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `k[0-7]`, `st([0-7])`, `tmm[0-7]` | +> | AArch64 | `"C"`, `"system"`, `"efiapi"` | `x[0-17]`, `x18`\*, `x30`, `v[0-31]`, `p[0-15]`, `ffr` | +> | ARM | `"C"`, `"system"`, `"efiapi"`, `"aapcs"` | `r[0-3]`, `r12`, `r14`, `s[0-15]`, `d[0-7]`, `d[16-31]` | +> | RISC-V | `"C"`, `"system"`, `"efiapi"` | `x1`, `x[5-7]`, `x[10-17]`, `x[28-31]`, `f[0-7]`, `f[10-17]`, `f[28-31]`, `v[0-31]` | +> | LoongArch | `"C"`, `"system"`, `"efiapi"` | `$r1`, `$r[4-20]`, `$f[0-23]` | + +>[!NOTE] > - On AArch64 `x18` only included in the clobber list if it is not considered as a reserved register on the target. The list of clobbered registers for each ABI is updated in rustc as architectures gain new registers: this ensures that `asm!` clobbers will continue to be correct when LLVM starts using these new registers in its generated code. @@ -456,16 +471,35 @@ The list of clobbered registers for each ABI is updated in rustc as architecture ## Behaviour of an asm block [dynamic.asm.evaluation] r[dynamic.asm.evaluation.general] -Each evaluation of an assembly block shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. +Each evaluation of an asm block (invocation of [`core::arch::asm!`]) shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. >[!NOTE] -> The operation the assembly block performs may differ between evaluations of the same assembly block. +> The operation the asm block performs may differ between evaluations of the same asm block. >[!TARGET-SPECIFIC] -> The correspondance between the operation performed by the assembly block is target-dependant and implementation-dependant, subject to the rules set in [dynamic.asm.operands]. +> The correspondance between the operation performed by the asm block is target-dependant and implementation-dependant, subject to the rules set in [dynamic.asm.operands]. r[dynamic.asm.evaluation.constraints] -Certain constraints may be placed on the asm block, by default or +Certain constraints may be placed on the asm block, and on the requirements of the correspondance, by default or by an option explicitly specified on the asm block. The behaviour is undefined if any such constraint is violated. + +r[dynamic.asm.evaluation.unwind] +The behaviour is undefined if an inline assembly block exits by unwinding from a panic or a foreign exception. + +r[dynamic.asm.evaluation.prefix-instr] +The behaviour is undefined if the inline assembly block ends by evaluating an instruction considered a prefix instruction on the target. Such errors may be diagnosed when statically detected. + +>[!TARGET-SPECIFIC] +> On x86 and x86-64, the `lock`, `repnz`, `rep`, `repz`, as well as GNU AS specific address-size, data-size, and explicit rex, vex, and evex prefixes. +> It is assembler- and implementation-dependent whether or not use of these prefixes before the end of the asm string is diagnosed. + +r[dynamic.asm.evaluation.register-value] +The behaviour is undefined upon exiting an asm block unless the stack pointer register and each operand-usable register not mentioned by an `out` , `lateout`, `inout`, or `inlateout` operand has the value the register held upon entry to the asm block. + +>[!TARGET-SPECIFIC] +> In addition to operand-usable registers, certain other registers on a target may require being preserved, or have specific rules regarding the value at exit. +> On x86 and x86-64 targets: +> * The Direction flag (`flags.DF`) is clear upon entry and must be clear upon exit +> * The x87 Stack (that is the `TOP` field of the floating-point status word, and each bit in the floating-point tag word) must be preserved and restored upon exit. If all x87 `st` registers are marked as clobbered, the stack is guaranteed to be empty on entry to the asm block (that is, `TOP` is set to `0x7` and the `ftw` is set to `0xFFFF`). ## Options [dynamic.asm.options] @@ -528,90 +562,12 @@ The `noreturn` option may be specifed. An invocation of the [`core::arch::asm!`] r[dynamic.asm.options.global] A program shall not specify an option, other than the `att_syntax` option, in an invocation of the [`core::arch::global_asm!`] macro. -## Rules for inline assembly - -To avoid undefined behavior, these rules must be followed when using function-scope inline assembly (`asm!`): - -- Any registers not specified as inputs will contain an undefined value on entry to the 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). -- Any registers not specified as outputs must have the same value upon exiting the asm block as they had on entry, otherwise behavior is undefined. - - This only applies to registers which can be specified as an input or output. - Other registers follow target-specific rules. - - Note that a `lateout` may be allocated to the same register as an `in`, in which case this rule does not apply. - Code should not rely on this however since it depends on the results of register allocation. -- Behavior is undefined if execution unwinds out of an asm block. - - This also applies if the assembly code calls a function which then unwinds. -- 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. - - If the `readonly` option is set, then only memory reads are allowed. - - If the `nomem` option is set then no reads or writes to memory are allowed. - - These rules do not apply to memory which is private to the asm code, such as stack space allocated within the asm block. -- The compiler cannot assume that the instructions in the asm are the ones that will actually end up executed. - - This effectively means that the compiler must treat the `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. -- Unless the `nostack` option is set, asm code is allowed to use stack space below the stack pointer. - - On entry to the asm block the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call. - - You are responsible for making sure you don't overflow the stack (e.g. use stack probing to ensure you hit a guard page). - - You should adjust the stack pointer when allocating stack memory as required by the target ABI. - - The stack pointer must be restored to its original value before leaving the asm block. -- If the `noreturn` option is set then behavior is undefined if execution falls through to the end of the asm block. -- If the `pure` option is set then behavior is undefined if the `asm!` has side-effects other than its direct outputs. - Behavior is also undefined if two executions of the `asm!` code with the same inputs result in different outputs. - - When used with the `nomem` option, "inputs" are just the direct inputs of the `asm!`. - - When used with the `readonly` option, "inputs" comprise the direct inputs of the `asm!` and any memory that the `asm!` block is allowed to read. -- On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit. - - Behavior is undefined if the direction flag is set on exiting an asm block. -- On x86, the x87 floating-point register stack must remain unchanged unless all of the `st([0-7])` registers have been marked as clobbered with `out("st(0)") _, out("st(1)") _, ...`. - - If all x87 registers are clobbered then the x87 register stack is guaranteed to be empty upon entering an `asm` block. Assembly code must ensure that the x87 register stack is also empty when exiting the asm block. -- The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting an `asm!` block. - - This means that `asm!` blocks that never return (even if not marked `noreturn`) don't need to preserve these registers. - - When returning to a different `asm!` block than you entered (e.g. for context switching), these registers must contain the value they had upon entering the `asm!` block that you are *exiting*. - - You cannot exit an `asm!` block that has not been entered. - Neither can you exit an `asm!` block that has already been exited (without first entering it again). - - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds). - - You cannot jump from an address in one `asm!` block to an address in another, even within the same function or block, without treating their contexts as potentially different and requiring context switching. You cannot assume that any particular value in those contexts (e.g. current stack pointer or temporary values below the stack pointer) will remain unchanged between the two `asm!` blocks. - - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited. -- You cannot assume that two `asm!` blocks adjacent in source code, even without any other code between them, will end up in successive addresses in the binary without any other instructions between them. -- You cannot assume that an `asm!` block will appear exactly once in the output binary. - The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places. -- On x86, inline assembly must not end with an instruction prefix (such as `LOCK`) that would apply to instructions generated by the compiler. - - The compiler is currently unable to detect this due to the way inline assembly is compiled, but may catch and reject this in the future. - -> **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call. - -### Correctness and Validity - -In addition to all of the previous rules, the string argument to `asm!` must ultimately become--- -after all other arguments are evaluated, formatting is performed, and operands are translated--- -assembly that is both syntactically correct and semantically valid for the target architecture. -The formatting rules allow the compiler to generate assembly with correct syntax. -Rules concerning operands permit valid translation of Rust operands into and out of `asm!`. -Adherence to these rules is necessary, but not sufficient, for the final expanded assembly to be -both correct and valid. For instance: - -- arguments may be placed in positions which are syntactically incorrect after formatting -- an instruction may be correctly written, but given architecturally invalid operands -- an architecturally unspecified instruction may be assembled into unspecified code -- a set of instructions, each correct and valid, may cause undefined behavior if placed in immediate succession - -As a result, these rules are _non-exhaustive_. The compiler is not required to check the -correctness and validity of the initial string nor the final assembly that is generated. -The assembler may check for correctness and validity but is not required to do so. -When using `asm!`, a typographical error may be sufficient to make a program unsound, -and the rules for assembly may include thousands of pages of architectural reference manuals. -Programmers should exercise appropriate care, as invoking this `unsafe` capability comes with -assuming the responsibility of not violating rules of both the compiler or the architecture. - -### Directives Support - -Inline assembly supports a subset of the directives supported by both GNU AS and LLVM's internal assembler, given as follows. -The result of using other directives is assembler-specific (and may cause an error, or may be accepted as-is). - -If inline assembly includes any "stateful" directive that modifies how subsequent assembly is processed, the block must undo the effects of any such directives before the inline assembly ends. - -The following directives are guaranteed to be supported by the assembler: + +## Directives Support [dynamic.asm.directives] + +r[dynamic.asm.directives] +The common subset of the LLVM and GNU AS Assembly Syntax used for the *expanded asm-string* is guaranteed to support the following directives - `.2byte` - `.4byte` - `.8byte` @@ -660,74 +616,67 @@ The following directives are guaranteed to be supported by the assembler: - `.uleb128` - `.word` +>[!NOTE] +> These directives are generally ones that solely emit sequences of bytes, or that modify the property of symbols. +r[dynamic.asm.directives.dwarf] -#### Target Specific Directive Support - -##### Dwarf Unwinding - -The following directives are supported on ELF targets that support DWARF unwind info: - - -- `.cfi_adjust_cfa_offset` -- `.cfi_def_cfa` -- `.cfi_def_cfa_offset` -- `.cfi_def_cfa_register` -- `.cfi_endproc` -- `.cfi_escape` -- `.cfi_lsda` -- `.cfi_offset` -- `.cfi_personality` -- `.cfi_register` -- `.cfi_rel_offset` -- `.cfi_remember_state` -- `.cfi_restore` -- `.cfi_restore_state` -- `.cfi_return_column` -- `.cfi_same_value` -- `.cfi_sections` -- `.cfi_signal_frame` -- `.cfi_startproc` -- `.cfi_undefined` -- `.cfi_window_save` - - -##### Structured Exception Handling - -On targets with structured exception Handling, the following additional directives are guaranteed to be supported: - -- `.seh_endproc` -- `.seh_endprologue` -- `.seh_proc` -- `.seh_pushreg` -- `.seh_savereg` -- `.seh_setframe` -- `.seh_stackalloc` - - -##### x86 (32-bit and 64-bit) - -On x86 targets, both 32-bit and 64-bit, the following additional directives are guaranteed to be supported: -- `.nops` -- `.code16` -- `.code32` -- `.code64` - - -Use of `.code16`, `.code32`, and `.code64` directives are only supported if the state is reset to the default before exiting the assembly block. -32-bit x86 uses `.code32` by default, and x86_64 uses `.code64` by default. - +>[!TARGET-SPECIFIC] +> The following Directives are guaranteed to be supported on ELF Targets that use DWARF Debug Information and DWARF Unwind Tables +> - `.cfi_adjust_cfa_offset` +> - `.cfi_def_cfa` +> - `.cfi_def_cfa_offset` +> - `.cfi_def_cfa_register` +> - `.cfi_endproc` +> - `.cfi_escape` +> - `.cfi_lsda` +> - `.cfi_offset` +> - `.cfi_personality` +> - `.cfi_register` +> - `.cfi_rel_offset` +> - `.cfi_remember_state` +> - `.cfi_restore` +> - `.cfi_restore_state` +> - `.cfi_return_column` +> - `.cfi_same_value` +> - `.cfi_sections` +> - `.cfi_signal_frame` +> - `.cfi_startproc` +> - `.cfi_undefined` +> - `.cfi_window_save` + +r[dynamic.asm.directives.seh] +>[!TARGET-SPECIFIC] +> The following directives are guaranteed to be supported on platforms that use Structured Exception Handling +> - `.seh_endproc` +> - `.seh_endprologue` +> - `.seh_proc` +> - `.seh_pushreg` +> - `.seh_savereg` +> - `.seh_setframe` +> - `.seh_stackalloc` -##### ARM (32-bit) -On ARM, the following additional directives are guaranteed to be supported: +r[dynamic.asm.directives.x86] -- `.even` -- `.fnstart` -- `.fnend` -- `.save` -- `.movsp` -- `.code` -- `.thumb` -- `.thumb_func` +>[!TARGET-SPECIFIC] +> The following directives are guaranteed to be supported on x86 and x86-64 platforms +> - `.nops` +> - `.code16` +> - `.code32` +> - `.code64` +> Use of `.code16`, `.code32`, and `.code64` directives are only supported if the state is reset to the default before exiting the assembly block. +> 32-bit x86 uses `.code32` by default, and x86_64 uses `.code64` by default. + +r[dynamic.asm.directives.arm] + +> The following directives are guaranteed to be supported on 32-bit ARM platforms +> - `.even` +> - `.fnstart` +> - `.fnend` +> - `.save` +> - `.movsp` +> - `.code` +> - `.thumb` +> - `.thumb_func` From 320b41452dee9d0478dbd8914df2985ac93239a1 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 9 Jul 2024 14:06:06 -0400 Subject: [PATCH 06/30] Format dynamic.asm.registers --- src/inline-assembly.md | 386 ++++++++++++++++++++--------------------- 1 file changed, 186 insertions(+), 200 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index fa43ca716..5d6ee00a7 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -195,7 +195,7 @@ Each operand_spec is expanded in the *joined asm-string* according to the modifi > The name given to an item used by a sym-expr that does not have a linkage name may be known as the "mangled" name of the item. >[!NOTE] -> A sym operand does not include any relocation modifeirs such as `@plt` or `@tpoff`. The *joined asm-string* is responsible for including these as necessary. +> A sym operand does not include any relocation modifiers such as `@plt` or `@tpoff`. The *joined asm-string* is responsible for including these as required. >[!TARGET-SPECIFIC] > On x86 and x86_64 targets, the register name is expanded as-is if the `options(att_syntax)` is not used, and with the `%` prefix if `options(att_syntax)` is used. @@ -203,184 +203,197 @@ Each operand_spec is expanded in the *joined asm-string* according to the modifi r[dynamic.asm.operands.global] The program shall not use an operand, other than a sym operand, in the expansion of the [`core::arch::global_asm!`] macro. -## Register operands - -Input and output operands can be specified either as an explicit register or as a register class from which the register allocator can select a register. -Explicit registers are specified as string literals (e.g. `"eax"`) while register classes are specified as identifiers (e.g. `reg`). - -Note that explicit registers treat register aliases (e.g. `r14` vs `lr` on ARM) and smaller views of a register (e.g. `eax` vs `rax`) as equivalent to the base register. -It is a compile-time error to use the same explicit register for two input operands or two output operands. -Additionally, it is also a compile-time error to use overlapping registers (e.g. ARM VFP) in input operands or in output operands. - -Here is the list of currently supported register classes: - -| Architecture | Register class | Registers | LLVM constraint code | -| ------------ | -------------- | --------- | -------------------- | -| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp`, `r[8-15]` (x86-64 only) | `r` | -| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` | -| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` | -| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `bpl`, `r[8-15]b` | `q` | -| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` | -| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` | -| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` | -| x86 | `kreg` | `k[1-7]` | `Yk` | -| x86 | `kreg0` | `k0` | Only clobbers | -| x86 | `x87_reg` | `st([0-7])` | Only clobbers | -| x86 | `mmx_reg` | `mm[0-7]` | Only clobbers | -| x86-64 | `tmm_reg` | `tmm[0-7]` | Only clobbers | -| AArch64 | `reg` | `x[0-30]` | `r` | -| AArch64 | `vreg` | `v[0-31]` | `w` | -| AArch64 | `vreg_low16` | `v[0-15]` | `x` | -| AArch64 | `preg` | `p[0-15]`, `ffr` | Only clobbers | -| ARM (ARM/Thumb2) | `reg` | `r[0-12]`, `r14` | `r` | -| ARM (Thumb1) | `reg` | `r[0-7]` | `r` | -| ARM | `sreg` | `s[0-31]` | `t` | -| ARM | `sreg_low16` | `s[0-15]` | `x` | -| ARM | `dreg` | `d[0-31]` | `w` | -| ARM | `dreg_low16` | `d[0-15]` | `t` | -| ARM | `dreg_low8` | `d[0-8]` | `x` | -| ARM | `qreg` | `q[0-15]` | `w` | -| ARM | `qreg_low8` | `q[0-7]` | `t` | -| ARM | `qreg_low4` | `q[0-3]` | `x` | -| RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | -| RISC-V | `freg` | `f[0-31]` | `f` | -| RISC-V | `vreg` | `v[0-31]` | Only clobbers | -| LoongArch | `reg` | `$r1`, `$r[4-20]`, `$r[23,30]` | `r` | -| LoongArch | `freg` | `$f[0-31]` | `f` | +r[dynamic.asm.operands.clobbers_abi] +A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, the no reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. + +>[!NOTE] +> Multiple `clobbers_abi` operands may be specified. If a register is considered clobbered by multiple `clobbers_abi` operands, it acts as though only one of those `clobbers_abi` operands specifies that register. + +>[!TARGET-SPECIFIC] +> The list of supported ABI strings and current list of clobbered registers are +> | Architecture | ABI name | Clobbered registers | +> | ------------ | -------- | ------------------- | +> | x86-32 | `"C"`, `"system"`, `"efiapi"`, `"cdecl"`, `"stdcall"`, `"fastcall"` | `ax`, `cx`, `dx`, `xmm[0-7]`, `mm[0-7]`, `k[0-7]`, `st([0-7])` | +> | x86-64 | `"C"`, `"system"` (on Windows), `"efiapi"`, `"win64"` | `ax`, `cx`, `dx`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `k[0-7]`, `st([0-7])`, `tmm[0-7]` | +> | x86-64 | `"C"`, `"system"` (on non-Windows), `"sysv64"` | `ax`, `cx`, `dx`, `si`, `di`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `k[0-7]`, `st([0-7])`, `tmm[0-7]` | +> | AArch64 | `"C"`, `"system"`, `"efiapi"` | `x[0-17]`, `x18`\*, `x30`, `v[0-31]`, `p[0-15]`, `ffr` | +> | ARM | `"C"`, `"system"`, `"efiapi"`, `"aapcs"` | `r[0-3]`, `r12`, `r14`, `s[0-15]`, `d[0-7]`, `d[16-31]` | +> | RISC-V | `"C"`, `"system"`, `"efiapi"` | `x1`, `x[5-7]`, `x[10-17]`, `x[28-31]`, `f[0-7]`, `f[10-17]`, `f[28-31]`, `v[0-31]` | +> | LoongArch | `"C"`, `"system"`, `"efiapi"` | `$r1`, `$r[4-20]`, `$f[0-23]` | + +>[!NOTE] +> - On AArch64 `x18` only included in the clobber list if it is not considered as a reserved register on the target. + +## Register operands [dynamic.asm.registers] + +r[dynamic.asm.registers.explicit] +An explicit register operand specifies the name of a valid operand register that is not a reserved register, or an alias name. Multiple explicit register operands shall not specify the same register or aliases of the same register. + +r[dynamic.asm.registers.class] +A register operand that is not an explicit register operand specifies the name of a register class as an identifier. When a register class is specified, the implementation assigns an unspecified register belonging to that class to the operand. + +r[dynamic.asm.registers.valid-types] +Each register class, and the explicit registers within those classes, may restrict the set of types allowed for operands referring to that class or those registers. + +>[!NOTE] +> When an integer type which is as wide as `isize` is valid for a given register class, `isize` is also considered valid, and the same for function pointers. +> When a signed integer is considered valid for a given register class, the corresponding unsigned integer is also considered valid. + +r[dynamic.asm.registers.target-feature] +Each register class, and the explicit registers within that class may require that a specified target_feature is enabled in the ambient target_feature set, or by using the `target_feature` function attribute. The program shall not specify such registers or register classes, except as clobber output, when the feature is not enabled. Additionally specific types may be valid only if certain additional features are enabled. + +>[!NOTE] +> The set of features in the ambient target_feature set are implementation-defined, and may be queried by [parse.macros.cfg]. + +r[dynamic.asm.registers.class-list] + +>[!TARGET-SPECIFIC] +> The list of valid register classes, the constituent registers, the required target feature (if any), and the valid types for those classes are: +> | Architecture | Register class | Registers | Target feature | Allowed types | +> | ------------ | -------------- | --------- | -------------- | ------------- | +> | x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp`, `r[8-15]` (x86-64 only) | None | `i16`, `i32`, `f32` | +> | x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | None | `i16`, `i32`, `f32`, `i64`, `f64` | +> | x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | None | `i8` | +> | x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `bpl`, `r[8-15]b` | None | `i8` | +> | x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `sse` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +> | x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `avx` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` | +> | x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4`
`i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` | +> | x86 | `kreg` | `k[1-7]` | `avx512f` | `i8`, `i16`, `i32` (requires `avx512bw`), `i64` (requires `avx512bw`) | +> | x86 | `kreg0` | `k0` | N/A | Only clobbers | +> | x86 | `x87_reg` | `st([0-7])` | N/A | Only clobbers | +> | x86 | `mmx_reg` | `mm[0-7]` | N/A | Only clobbers | +> | x86-64 | `tmm_reg` | `tmm[0-7]` | N/A | Only clobbers | +> | AArch64 | `reg` | `x[0-30]` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | +> | AArch64 | `vreg` | `v[0-31]` | `neon` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`,
`i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +> | AArch64 | `vreg_low16` | `v[0-15]` | `neon` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`,
`i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +> | AArch64 | `preg` | `p[0-15]`, `ffr` | N/A | Only clobbers | +> | ARM (ARM/Thumb2) | `reg` | `r[0-12]`, `r14` | None | `i8`, `i16`, `i32`, `f32` | +> | ARM (Thumb1) | `reg` | `r[0-7]` | None | `i8`, `i16`, `i32`, `f32` | +> | ARM | `sreg` | `s[0-31]` | `vfp2` | `i32`, `f32` | +> | ARM | `sreg_low16` | `s[0-15]` | `vfp2` | `i32`, `f32` | +> | ARM | `dreg` | `d[0-31]` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | +> | ARM | `dreg_low16` | `d[0-15]` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | +> | ARM | `dreg_low8` | `d[0-8]` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | +> | ARM | `qreg` | `q[0-15]` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` | +> | ARM | `qreg_low8` | `q[0-7]` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` | +> | ARM | `qreg_low4` | `q[0-3]` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` | +> | RISC-V32 | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | None | `i8`, `i16`, `i32`, `f32` | +> | RISC-V64 | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | +> | RISC-V | `freg` | `f[0-31]` | `f` | `f32`, `f64` (requires `d`) | +> | RISC-V | `vreg` | `v[0-31]` | N/A | Only clobbers | +> | LoongArch | `reg` | `$r1`, `$r[4-20]`, `$r[23,30]` | None | `i8`, `i16`, `i32`, `i64`, `f32`, `f64` | +> | LoongArch | `freg` | `$f[0-31]` | None | `f32`, `f64` | > **Notes**: > - On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. > > - On x86-64 the high byte registers (e.g. `ah`) are not available in the `reg_byte` register class. > -> - Some register classes are marked as "Only clobbers" which means that registers in these classes cannot be used for inputs or outputs, only clobbers of the form `out() _` or `lateout() _`. - -Each register class has constraints on which value types they can be used with. -This is necessary because the way a value is loaded into a register depends on its type. -For example, on big-endian systems, loading a `i32x4` and a `i8x16` into a SIMD register may result in different register contents even if the byte-wise memory representation of both values is identical. -The availability of supported types for a particular register class may depend on what target features are currently enabled. - -| Architecture | Register class | Target feature | Allowed types | -| ------------ | -------------- | -------------- | ------------- | -| x86-32 | `reg` | None | `i16`, `i32`, `f32` | -| x86-64 | `reg` | None | `i16`, `i32`, `f32`, `i64`, `f64` | -| x86 | `reg_byte` | None | `i8` | -| x86 | `xmm_reg` | `sse` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | -| x86 | `ymm_reg` | `avx` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` | -| x86 | `zmm_reg` | `avx512f` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4`
`i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` | -| x86 | `kreg` | `avx512f` | `i8`, `i16` | -| x86 | `kreg` | `avx512bw` | `i32`, `i64` | -| x86 | `mmx_reg` | N/A | Only clobbers | -| x86 | `x87_reg` | N/A | Only clobbers | -| x86 | `tmm_reg` | N/A | Only clobbers | -| AArch64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | -| AArch64 | `vreg` | `neon` | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`,
`i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | -| AArch64 | `preg` | N/A | Only clobbers | -| ARM | `reg` | None | `i8`, `i16`, `i32`, `f32` | -| ARM | `sreg` | `vfp2` | `i32`, `f32` | -| ARM | `dreg` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | -| ARM | `qreg` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` | -| RISC-V32 | `reg` | None | `i8`, `i16`, `i32`, `f32` | -| RISC-V64 | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | -| RISC-V | `freg` | `f` | `f32` | -| RISC-V | `freg` | `d` | `f64` | -| RISC-V | `vreg` | N/A | Only clobbers | -| LoongArch64 | `reg` | None | `i8`, `i16`, `i32`, `i64`, `f32`, `f64` | -| LoongArch64 | `freg` | None | `f32`, `f64` | - -> **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target). - -If a value is of a smaller size than the register it is allocated in then the upper bits of that register will have an undefined value for inputs and will be ignored for outputs. -The only exception is the `freg` register class on RISC-V where `f32` values are NaN-boxed in a `f64` as required by the RISC-V architecture. - -When separate input and output expressions are specified for an `inout` operand, both expressions must have the same type. -The only exception is if both operands are pointers or integers, in which case they are only required to have the same size. -This restriction exists because the register allocators in LLVM and GCC sometimes cannot handle tied operands with different types. - -## Register names - -Some registers have multiple names. -These are all treated by the compiler as identical to the base register name. -Here is the list of all supported register aliases: - -| Architecture | Base register | Aliases | -| ------------ | ------------- | ------- | -| x86 | `ax` | `eax`, `rax` | -| x86 | `bx` | `ebx`, `rbx` | -| x86 | `cx` | `ecx`, `rcx` | -| x86 | `dx` | `edx`, `rdx` | -| x86 | `si` | `esi`, `rsi` | -| x86 | `di` | `edi`, `rdi` | -| x86 | `bp` | `bpl`, `ebp`, `rbp` | -| x86 | `sp` | `spl`, `esp`, `rsp` | -| x86 | `ip` | `eip`, `rip` | -| x86 | `st(0)` | `st` | -| x86 | `r[8-15]` | `r[8-15]b`, `r[8-15]w`, `r[8-15]d` | -| x86 | `xmm[0-31]` | `ymm[0-31]`, `zmm[0-31]` | -| AArch64 | `x[0-30]` | `w[0-30]` | -| AArch64 | `x29` | `fp` | -| AArch64 | `x30` | `lr` | -| AArch64 | `sp` | `wsp` | -| AArch64 | `xzr` | `wzr` | -| AArch64 | `v[0-31]` | `b[0-31]`, `h[0-31]`, `s[0-31]`, `d[0-31]`, `q[0-31]` | -| ARM | `r[0-3]` | `a[1-4]` | -| ARM | `r[4-9]` | `v[1-6]` | -| ARM | `r9` | `rfp` | -| ARM | `r10` | `sl` | -| ARM | `r11` | `fp` | -| ARM | `r12` | `ip` | -| ARM | `r13` | `sp` | -| ARM | `r14` | `lr` | -| ARM | `r15` | `pc` | -| RISC-V | `x0` | `zero` | -| RISC-V | `x1` | `ra` | -| RISC-V | `x2` | `sp` | -| RISC-V | `x3` | `gp` | -| RISC-V | `x4` | `tp` | -| RISC-V | `x[5-7]` | `t[0-2]` | -| RISC-V | `x8` | `fp`, `s0` | -| RISC-V | `x9` | `s1` | -| RISC-V | `x[10-17]` | `a[0-7]` | -| RISC-V | `x[18-27]` | `s[2-11]` | -| RISC-V | `x[28-31]` | `t[3-6]` | -| RISC-V | `f[0-7]` | `ft[0-7]` | -| RISC-V | `f[8-9]` | `fs[0-1]` | -| RISC-V | `f[10-17]` | `fa[0-7]` | -| RISC-V | `f[18-27]` | `fs[2-11]` | -| RISC-V | `f[28-31]` | `ft[8-11]` | -| LoongArch | `$r0` | `$zero` | -| LoongArch | `$r1` | `$ra` | -| LoongArch | `$r2` | `$tp` | -| LoongArch | `$r3` | `$sp` | -| LoongArch | `$r[4-11]` | `$a[0-7]` | -| LoongArch | `$r[12-20]` | `$t[0-8]` | -| LoongArch | `$r21` | | -| LoongArch | `$r22` | `$fp`, `$s9` | -| LoongArch | `$r[23-31]` | `$s[0-8]` | -| LoongArch | `$f[0-7]` | `$fa[0-7]` | -| LoongArch | `$f[8-23]` | `$ft[0-15]` | -| LoongArch | `$f[24-31]` | `$fs[0-7]` | - -Some registers cannot be used for input or output operands: - -| Architecture | Unsupported register | Reason | -| ------------ | -------------------- | ------ | -| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | -| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `$fp` (LoongArch) | The frame pointer cannot be used as an input or output. | -| ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. | -| All | `si` (x86-32), `bx` (x86-64), `r6` (ARM), `x19` (AArch64), `x9` (RISC-V), `$s8` (LoongArch) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. | -| x86 | `ip` | This is the program counter, not a real register. | -| AArch64 | `xzr` | This is a constant zero register which can't be modified. | -| AArch64 | `x18` | This is an OS-reserved register on some AArch64 targets. | -| ARM | `pc` | This is the program counter, not a real register. | -| ARM | `r9` | This is an OS-reserved register on some ARM targets. | -| RISC-V | `x0` | This is a constant zero register which can't be modified. | -| RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | -| LoongArch | `$r0` or `$zero` | This is a constant zero register which can't be modified. | -| LoongArch | `$r2` or `$tp` | This is reserved for TLS. | -| LoongArch | `$r21` | This is reserved by the ABI. | - -The frame pointer and base pointer registers are reserved for internal use by LLVM. While `asm!` statements cannot explicitly specify the use of reserved registers, in some cases LLVM will allocate one of these reserved registers for `reg` operands. Assembly code making use of reserved registers should be careful since `reg` operands may use the same registers. + +r[dynamic.asm.register.clobbers_only] +Certain registers and register classes are *clobbers only*. Such register names or register classes shall not be specified by an operand, other than a clobbers output. + +>[!TARGET-SPECIFIC] +> The list of such classes and registers are: +> * On x86 and x86-64: the `kreg0`, `x87_reg`, `mmx_reg`, and `tmm_reg` classes, as well as the registers belonging to these classes +> * On AArch64: the `preg` class, and the registers belonging to that class +> * On RISC-V: The `vreg` class, and the registers belonging to that class. + + +r[dynamic.asm.register.small-values] +If a register input is specified with a type that has a smaller width than the register class according to the target, the remaining bits of the register are set to an unspecified value. + +>[!TARGET-SPECIFIC] +> On RISC-V, in the case of an `freg` input of type `f32`, the upper bits are instead set to all 1s according to the `D` extension of the RISC-V specification. + +r[dynamic.asm.register.aliases] +Certain explicit register names have defined aliases. These register names are considered identical to canonical register name and may be specified in place of the canonical name in an explicit register operand + +>[!TARGET-SPECIFIC] +> The List of register alias names is: +> | Architecture | Base register | Aliases | +> | ------------ | ------------- | ------- | +> | x86 | `ax` | `eax`, `rax` | +> | x86 | `bx` | `ebx`, `rbx` | +> | x86 | `cx` | `ecx`, `rcx` | +> | x86 | `dx` | `edx`, `rdx` | +> | x86 | `si` | `esi`, `rsi` | +> | x86 | `di` | `edi`, `rdi` | +> | x86 | `bp` | `bpl`, `ebp`, `rbp` | +> | x86 | `sp` | `spl`, `esp`, `rsp` | +> | x86 | `ip` | `eip`, `rip` | +> | x86 | `st(0)` | `st` | +> | x86 | `r[8-15]` | `r[8-15]b`, `r[8-15]w`, `r[8-15]d` | +> | x86 | `xmm[0-31]` | `ymm[0-31]`, `zmm[0-31]` | +> | AArch64 | `x[0-30]` | `w[0-30]` | +> | AArch64 | `x29` | `fp` | +> | AArch64 | `x30` | `lr` | +> | AArch64 | `sp` | `wsp` | +> | AArch64 | `xzr` | `wzr` | +> | AArch64 | `v[0-31]` | `b[0-31]`, `h[0-31]`, `s[0-31]`, `d[0-31]`, `q[0-31]` | +> | ARM | `r[0-3]` | `a[1-4]` | +> | ARM | `r[4-9]` | `v[1-6]` | +> | ARM | `r9` | `rfp` | +> | ARM | `r10` | `sl` | +> | ARM | `r11` | `fp` | +> | ARM | `r12` | `ip` | +> | ARM | `r13` | `sp` | +> | ARM | `r14` | `lr` | +> | ARM | `r15` | `pc` | +> | RISC-V | `x0` | `zero` | +> | RISC-V | `x1` | `ra` | +> | RISC-V | `x2` | `sp` | +> | RISC-V | `x3` | `gp` | +> | RISC-V | `x4` | `tp` | +> | RISC-V | `x[5-7]` | `t[0-2]` | +> | RISC-V | `x8` | `fp`, `s0` | +> | RISC-V | `x9` | `s1` | +> | RISC-V | `x[10-17]` | `a[0-7]` | +> | RISC-V | `x[18-27]` | `s[2-11]` | +> | RISC-V | `x[28-31]` | `t[3-6]` | +> | RISC-V | `f[0-7]` | `ft[0-7]` | +> | RISC-V | `f[8-9]` | `fs[0-1]` | +> | RISC-V | `f[10-17]` | `fa[0-7]` | +> | RISC-V | `f[18-27]` | `fs[2-11]` | +> | RISC-V | `f[28-31]` | `ft[8-11]` | +> | LoongArch | `$r0` | `$zero` | +> | LoongArch | `$r1` | `$ra` | +> | LoongArch | `$r2` | `$tp` | +> | LoongArch | `$r3` | `$sp` | +> | LoongArch | `$r[4-11]` | `$a[0-7]` | +> | LoongArch | `$r[12-20]` | `$t[0-8]` | +> | LoongArch | `$r21` | | +> | LoongArch | `$r22` | `$fp`, `$s9` | +> | LoongArch | `$r[23-31]` | `$s[0-8]` | +> | LoongArch | `$f[0-7]` | `$fa[0-7]` | +> | LoongArch | `$f[8-23]` | `$ft[0-15]` | +> | LoongArch | `$f[24-31]` | `$fs[0-7]` | + +r[dynamic.asm.register.reserved] +Certain registers are reserved registers. Reserved Registers shall not be named by an explicit register operand. + +>[!NOTE] +> Reserved Registers that belong to a register class may still be assigned to register operands regardless + +>[!TARGET-SPECIFIC] +> | Architecture | Unsupported register | Reason | +> | ------------ | -------------------- | ------ | +> | All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | +> | All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `$fp` (LoongArch) | The frame pointer cannot be used as an input or output. | +> | ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. | +> | All | `si` (x86-32), `bx` (x86-64), `r6` (ARM), `x19` (AArch64), `x9` (RISC-V), `$s8` (LoongArch) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. | +> | x86 | `ip` | This is the program counter, not a real register. | +> | AArch64 | `xzr` | This is a constant zero register which can't be modified. | +> | AArch64 | `x18` | This is an OS-reserved register on some AArch64 targets. | +> | ARM | `pc` | This is the program counter, not a real register. | +> | ARM | `r9` | This is an OS-reserved register on some ARM targets. | +> | RISC-V | `x0` | This is a constant zero register which can't be modified. | +> | RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | +> | LoongArch | `$r0` or `$zero` | This is a constant zero register which can't be modified. | +> | LoongArch | `$r2` or `$tp` | This is reserved for TLS. | +> | LoongArch | `$r21` | This is reserved by the ABI. | + + ## Template modifiers @@ -441,33 +454,6 @@ If all references to an operand already have modifiers then the warning is suppr [llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers -## ABI clobbers - -The `clobber_abi` keyword can be used to apply a default set of clobbers to an `asm!` block. -This will automatically insert the necessary clobber constraints as needed for calling a function with a particular calling convention: if the calling convention does not fully preserve the value of a register across a call then `lateout("...") _` is implicitly added to the operands list (where the `...` is replaced by the register's name). - -`clobber_abi` may be specified any number of times. It will insert a clobber for all unique registers in the union of all specified calling conventions. - -Generic register class outputs are disallowed by the compiler when `clobber_abi` is used: all outputs must specify an explicit register. -Explicit register outputs have precedence over the implicit clobbers inserted by `clobber_abi`: a clobber will only be inserted for a register if that register is not used as an output. -The following ABIs can be used with `clobber_abi`: - ->[!TARGET-SPECIFIC] -> | Architecture | ABI name | Clobbered registers | -> | ------------ | -------- | ------------------- | -> | x86-32 | `"C"`, `"system"`, `"efiapi"`, `"cdecl"`, `"stdcall"`, `"fastcall"` | `ax`, `cx`, `dx`, `xmm[0-7]`, `mm[0-7]`, `k[0-7]`, `st([0-7])` | -> | x86-64 | `"C"`, `"system"` (on Windows), `"efiapi"`, `"win64"` | `ax`, `cx`, `dx`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `k[0-7]`, `st([0-7])`, `tmm[0-7]` | -> | x86-64 | `"C"`, `"system"` (on non-Windows), `"sysv64"` | `ax`, `cx`, `dx`, `si`, `di`, `r[8-11]`, `xmm[0-31]`, `mm[0-7]`, `k[0-7]`, `st([0-7])`, `tmm[0-7]` | -> | AArch64 | `"C"`, `"system"`, `"efiapi"` | `x[0-17]`, `x18`\*, `x30`, `v[0-31]`, `p[0-15]`, `ffr` | -> | ARM | `"C"`, `"system"`, `"efiapi"`, `"aapcs"` | `r[0-3]`, `r12`, `r14`, `s[0-15]`, `d[0-7]`, `d[16-31]` | -> | RISC-V | `"C"`, `"system"`, `"efiapi"` | `x1`, `x[5-7]`, `x[10-17]`, `x[28-31]`, `f[0-7]`, `f[10-17]`, `f[28-31]`, `v[0-31]` | -> | LoongArch | `"C"`, `"system"`, `"efiapi"` | `$r1`, `$r[4-20]`, `$f[0-23]` | - ->[!NOTE] -> - On AArch64 `x18` only included in the clobber list if it is not considered as a reserved register on the target. - -The list of clobbered registers for each ABI is updated in rustc as architectures gain new registers: this ensures that `asm!` clobbers will continue to be correct when LLVM starts using these new registers in its generated code. - ## Behaviour of an asm block [dynamic.asm.evaluation] r[dynamic.asm.evaluation.general] From 80bf3cfca3546cccdc72e4f1577779356affa870 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 9 Jul 2024 15:28:58 -0400 Subject: [PATCH 07/30] Format dynamic.asm.template --- src/inline-assembly.md | 105 +++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 5d6ee00a7..1120d08c4 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -395,62 +395,67 @@ Certain registers are reserved registers. Reserved Registers shall not be named -## Template modifiers - -The placeholders can be augmented by modifiers which are specified after the `:` in the curly braces. -These modifiers do not affect register allocation, but change the way operands are formatted when inserted into the template string. -Only one modifier is allowed per template placeholder. - -The supported modifiers are a subset of LLVM's (and GCC's) [asm template argument modifiers][llvm-argmod], but do not use the same letter codes. - -| Architecture | Register class | Modifier | Example output | LLVM modifier | -| ------------ | -------------- | -------- | -------------- | ------------- | -| x86-32 | `reg` | None | `eax` | `k` | -| x86-64 | `reg` | None | `rax` | `q` | -| x86-32 | `reg_abcd` | `l` | `al` | `b` | -| x86-64 | `reg` | `l` | `al` | `b` | -| x86 | `reg_abcd` | `h` | `ah` | `h` | -| x86 | `reg` | `x` | `ax` | `w` | -| x86 | `reg` | `e` | `eax` | `k` | -| x86-64 | `reg` | `r` | `rax` | `q` | -| x86 | `reg_byte` | None | `al` / `ah` | None | -| x86 | `xmm_reg` | None | `xmm0` | `x` | -| x86 | `ymm_reg` | None | `ymm0` | `t` | -| x86 | `zmm_reg` | None | `zmm0` | `g` | -| x86 | `*mm_reg` | `x` | `xmm0` | `x` | -| x86 | `*mm_reg` | `y` | `ymm0` | `t` | -| x86 | `*mm_reg` | `z` | `zmm0` | `g` | -| x86 | `kreg` | None | `k1` | None | -| AArch64 | `reg` | None | `x0` | `x` | -| AArch64 | `reg` | `w` | `w0` | `w` | -| AArch64 | `reg` | `x` | `x0` | `x` | -| AArch64 | `vreg` | None | `v0` | None | -| AArch64 | `vreg` | `v` | `v0` | None | -| AArch64 | `vreg` | `b` | `b0` | `b` | -| AArch64 | `vreg` | `h` | `h0` | `h` | -| AArch64 | `vreg` | `s` | `s0` | `s` | -| AArch64 | `vreg` | `d` | `d0` | `d` | -| AArch64 | `vreg` | `q` | `q0` | `q` | -| ARM | `reg` | None | `r0` | None | -| ARM | `sreg` | None | `s0` | None | -| ARM | `dreg` | None | `d0` | `P` | -| ARM | `qreg` | None | `q0` | `q` | -| ARM | `qreg` | `e` / `f` | `d0` / `d1` | `e` / `f` | -| RISC-V | `reg` | None | `x1` | None | -| RISC-V | `freg` | None | `f0` | None | -| LoongArch | `reg` | None | `$r1` | None | -| LoongArch | `freg` | None | `$f0` | None | +## Template modifiers r[dynamic.asm.template] -> **Notes**: +r[dynamic.asm.template.modifier] +An operand spec that refers to a register operand may specify a modifier as part of the format specifier. + +r[dynamic.asm.template.class] +A format specifier shall only use a modifier that is supported for the register class specified by the register opernd. + + +>[!TARGET-SPECIFIC] +> The list of supported modifiers for each register class is as follows +> | Architecture | Register class | Modifier | Example output | LLVM modifier | +> | ------------ | -------------- | -------- | -------------- | ------------- | +> | x86-32 | `reg` | None | `eax` | `k` | +> | x86-64 | `reg` | None | `rax` | `q` | +> | x86-32 | `reg_abcd` | `l` | `al` | `b` | +> | x86-64 | `reg` | `l` | `al` | `b` | +> | x86 | `reg_abcd` | `h` | `ah` | `h` | +> | x86 | `reg` | `x` | `ax` | `w` | +> | x86 | `reg` | `e` | `eax` | `k` | +> | x86-64 | `reg` | `r` | `rax` | `q` | +> | x86 | `reg_byte` | None | `al` / `ah` | None | +> | x86 | `xmm_reg` | None | `xmm0` | `x` | +> | x86 | `ymm_reg` | None | `ymm0` | `t` | +> | x86 | `zmm_reg` | None | `zmm0` | `g` | +> | x86 | `*mm_reg` | `x` | `xmm0` | `x` | +> | x86 | `*mm_reg` | `y` | `ymm0` | `t` | +> | x86 | `*mm_reg` | `z` | `zmm0` | `g` | +> | x86 | `kreg` | None | `k1` | None | +> | AArch64 | `reg` | None | `x0` | `x` | +> | AArch64 | `reg` | `w` | `w0` | `w` | +> | AArch64 | `reg` | `x` | `x0` | `x` | +> | AArch64 | `vreg` | None | `v0` | None | +> | AArch64 | `vreg` | `v` | `v0` | None | +> | AArch64 | `vreg` | `b` | `b0` | `b` | +> | AArch64 | `vreg` | `h` | `h0` | `h` | +> | AArch64 | `vreg` | `s` | `s0` | `s` | +> | AArch64 | `vreg` | `d` | `d0` | `d` | +> | AArch64 | `vreg` | `q` | `q0` | `q` | +> | ARM | `reg` | None | `r0` | None | +> | ARM | `sreg` | None | `s0` | None | +> | ARM | `dreg` | None | `d0` | `P` | +> | ARM | `qreg` | None | `q0` | `q` | +> | ARM | `qreg` | `e` / `f` | `d0` / `d1` | `e` / `f` | +> | RISC-V | `reg` | None | `x1` | None | +> | RISC-V | `freg` | None | `f0` | None | +> | LoongArch | `reg` | None | `$r1` | None | +> | LoongArch | `freg` | None | `$f0` | None | + + +>[!NOTE] +> The supported modifiers are a subset of LLVM's (and GCC's) [asm template argument modifiers][llvm-argmod], but do not use the same letter codes. + +>[!NOTE] > - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register. > - on x86: our behavior for `reg` with no modifiers differs from what GCC does. > GCC will infer the modifier based on the operand value type, while we default to the full register size. > - on x86 `xmm_reg`: the `x`, `t` and `g` LLVM modifiers are not yet implemented in LLVM (they are supported by GCC only), but this should be a simple change. -As stated in the previous section, passing an input value smaller than the register width will result in the upper bits of the register containing undefined values. -This is not a problem if the inline asm only accesses the lower bits of the register, which can be done by using a template modifier to use a subregister name in the asm code (e.g. `ax` instead of `rax`). -Since this an easy pitfall, the compiler will suggest a template modifier to use where appropriate given the input type. -If all references to an operand already have modifiers then the warning is suppressed for that operand. +r[dynamic.asm.template.diagnostic] +A lint diagnostic should be emitted if a modifier is omitted, or a modifier is used such that the modified expanded register is of an inappropriate width for the type used to initialize the operand [llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers From bf615a9c90a9b1db3429569a108fbdae14bc11e1 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 9 Jul 2024 17:04:12 -0400 Subject: [PATCH 08/30] Add examples/tests to the inline-assembly chapter --- src/inline-assembly.md | 389 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 372 insertions(+), 17 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 1120d08c4..f79b8d2ed 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -3,12 +3,25 @@ r[dynamic.asm.syntax] The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] expand to inline assembly syntax when used in the expression position and item position respectively. The macros shall not be expanded in any other context. -> [!NOTE] +>[!NOTE] > The expansion of the macros has no stable syntax equivalent. This section will refer to the expansion of the macro, rather than the surface syntax. r[dynamic.asm.safety] The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. +>[!NOTE] +> Inline assembly is inherently unsafe. +> It requires asserting various constraints to the compiler that it cannot check, and can perform operations equivalent to calling a foreign function. + + +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] +# fn main(){ + use core::arch::asm; + asm!("/*inline assembly is inherently unsafe*/"); +# } +``` + r[dynamic.asm.support] Inline assembly is supported only when compiling for a target using one of the following architectures. A program that contains inline assembly is ill-formed on any other target: - x86 and x86-64 @@ -73,10 +86,30 @@ asm_string_content := [*asm_string_piece] r[dynamic.asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. +```rust +# #[cfg(target_arch = "x86_64")] +pub fn main(){ + unsafe{ + core::arch::asm!("") + } +} +``` r[dynamic.asm.invocation.global_asm] The [`core::arch::global_asm!`] macro shall be expanded in an item context only. The input tokens shall match the `asm_inner` production. If the macro is expanded in a function, the program is ill-formed. +```rust +# #[cfg(target_arch = "x86_64")] +core::arch::global_asm!(".rodata", "FOO:", ".ascii \"Hello World\""); +``` + +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] +pub fn main(){ + core::arch::global_asm!("FOO:", ".ascii \"Hello World\""); +} +``` + r[dynamic.asm.invocation.format-string] Each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_asm!`] macros shall be an expanded string literal for which the content matches the `asm_string_piece` production. @@ -87,18 +120,55 @@ Each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_ r[dynamic.asm.invocation.concat] If multiple `format_string` inputs are provided, then they are concatenated as though by the [`core::concat!`] macro, separating each `format_string` with a string containing a single newline character. If any `format_string` begins a `format_specifier` that is not terminated before the end of the `format_string`, the program is ill-formed. The resulting string is known as the *joined asm-string* +```rust +# #[cfg(target_arch = "x86_64")] {unsafe{ +let mut x: i32; +// The following lines are equivalent +core::arch::asm!("mov rax, 5", "mov rcx, rax", out("rax") x, out("rcx") _); +core::arch::asm!("mov rax, 5\nmov rcx, rax", out("rax") x, out("rcx") _); +# }} +``` + r[dynamic.asm.invocation.operands] Each operand, other than an explicit register operand ([dynamic.asm.operands.register]) shall be mentioned by at least one format_specifier in the *joined asm-string*. Explicit registers may not be referred to be a format_specifier. +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("", in(reg) 5i64); +# }} +``` + r[dynamic.asm.invocation.positional] A `format_specifier` that does not specify an `operand_specifier` is called a positional specifier, and refers to the `nth` successive positional operand, where `n` is `0` for the first positional specifier in the *joined asm-string* and increases by 1 for each successive positional specifier in the *joined asm-string*. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("mov rax, {}", in(reg) 5i64, out("eax") x); +# }} +``` + r[dynamic.asm.invocation.explicit-positional] A `format_specifier` that has an `operand_specifier` which is a DEC_LITERAL is called an explicit positional specifier, and refers to the `nth` successive positional operand, where `n` is the value of the DEC_LITERAL. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("mov {1}, {0}", in(reg) 5i64, out(reg) x); +# }} +``` + r[dynamic.asm.invocation.named] A `format_specifier` that has an `operand_specifier` which is an ident is called a named specifier, and refers to the named operand with the specified name. + +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, output = out("eax") x); +# }} +``` + r[dynamic.asm.invocation.expansion] The *joined asm-string* is expanded as defined in [dynamic.asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. @@ -112,11 +182,32 @@ The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the r[dynamic.asm.invocation.duplication] The number of times, locations, and the order in which a given invocation of [`core::arch::asm!`] is expanded is unspecified. +```rust,ignore +// The following code may have suprising results, and may fail to compile or link. +// The results, including whether it succesfully compiles, may depend on non-local use sites of the function, and on optimization settings. +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("foo: jmp foo", input = in(reg) 5i64, output = out("eax") x); +# }} +``` + >[!NOTE] > In particular, an asm block may be duplicated, for example if the containing function is inlined, or omitted from the output entirely. > As a consequence, asm blocks should not use directives that have non-idempotent non-local effects, or named labels and symbol definitions. > Additionally, two asm blocks may not rely upon being adjacent in executable memory, even if they are adjacent in the source. +>[!NOTE] +> Local Labels (a decimal literal that doesn't solely consist of 0s and 1s) may be used freely if the asm block needs to define a label. +> See [The GNU AS Manual on Local Labels](https://sourceware.org/binutils/docs/as/Symbol-Names.html) for details on local labels. +> It is not guaranteed that a local label defined in one asm block will be accessible from an adjacent asm block. + +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("2: jmp 2b", input = in(reg) 5i64, output = out("eax") x); +# }} +``` + r[dynamic.asm.invocation.global-order] The order in which invocations of [`core::arch::global_asm!`] are expanded is unspecified. @@ -125,36 +216,101 @@ The *expanded asm-string* shall not issue a directive that modifies the global s >[!NOTE] > This include state such as the current section of the assembler, the syntax mode, or the kind of assembly output being generated. +> Failing to obey this requirement can have significant impact on code generation, including code unrelated to the asm block. For example, an asm block that issues a `.data` directive without resetting to the appropriate section for the function can cause the following code in the function to be generated in the `.data` section, and for execution to fall off the asm block into improper memory. -## Operand type [dynamic.asm.operands] +r[dynamic.asm.invocation.global-section] +The *expanded asm-string* of a [`core::arch::global_asm!`] invocation acts as though an target-dependant directive is issued before the *expanded asm-string* which causes code to be generated in the default section on the target for executable code. + +>[!NOTE] +> This section is typically named `.text`. + + +## Operand types [dynamic.asm.operands] r[dynamic.asm.operands.positional] Operands that do not specify an ident and are not explicit register operands are known as positional operands. Positional operands may be referred to only by positional operand specifiers and explicit positional operand specifiers, and each Positional operand must be specified before Named Operands or Explicit Register Operands. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let mut x: i32; +core::arch::asm!("mov rax, {}", in(reg) 5i64, out("eax") x); +core::arch::asm!("mov {1}, {0}", in(reg) 5i64, out(reg) x); +# }} +``` + r[dynamic.asm.operands.named] Operands that specify an ident are named operands. A named operand shall not specify an explicit register `reg_spec`. Named operand specifiers may be referred to only by named operand specifiers. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); +# }} +``` r[dynamic.asm.operands.registers] Operands that specify an explicit register `reg_spec` are explicit register operands. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("mov eax, ecx", in("rcx") 5i64, out("eax") x); +# }} +``` + >[!NOTE] > Explicit Register Operands have no `ident` name and cannot be referred to by an operand specifier r[dynamic.asm.operands.types] Each operand, other than a placeholder expression shall be of an integer type, floating-point type, function pointer type, pointer type, or target-specific vector type. These types are collectively called *asm operand types*. A pointer type is an *asm operand type* only if the pointee type has no metadata-type. +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +struct Foo{x: i32} +// Complex types like structs can't be used for asm +let x: Foo; +core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); +# }} +``` + +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +// ... nor can wide pointers +let x: *mut [i32]; +core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); +# }} +``` + + >[!TARGET-SPECIFIC] > On x86 platforms, the types [`core::arch::x86::__m128`], [`core::arch::x86::__m256`], and variants of those types are *asm operand types*. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +// But vector types are allowed. +let x: __m128; +core::arch::asm!("xorps xmm0, xmm0", out("xmm0") x); +# }} +``` + r[dynamic.asm.operands.in-expr] An `input_expr` shall be a value expression of an *asm operand type*. r[dynamic.asm.operands.out-expr] -An `output_expr` shall be the placeholder expression `_` or a (potentially unitialized) place expression of an *asm operand type*. +An `output_expr` shall be the placeholder expression `_` or a (potentially unitialized) place expression of an *asm operand type*. If the place expression is initialized, it shall be a mutable place. + +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +let mut x: i32; +core::arch::asm!("", out("eax") x); +# }} +``` r[dynamic.asm.operands.inout-expr] An `inout_expr` shall either be an (initialized) place expression of an *asm operand type*, or shall specify both an `input_expr` and an `output_expr`. If only a single expression is specified, it is treated as both the `input_expr` and `output_expr` of the operand. +>[!NOTE] +> When a single expression is specified, it must be an initialized mutable place expression. + r[dynamic.asm.operands.in] An `in` operand is an reg_operand with the `in` dir_spec. The `operand_expr` of the operand shall be an `input_expr`. The `input_expr` initializes the value of the register before entering the asm block. @@ -177,16 +333,29 @@ An `output_expr` that is the placeholder expression `_` is a clobber output. The >[!NOTE] > Some registers and register classes cannot be used as an operand, other than as a clobber operand. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let mut x: i32; +core::arch::asm!("mov eax, 5", out("eax") _); +# }} +``` + r[dynamic.asm.operands.sym-expr] A sym-expr is a path-expr. If the `path-expr` does not refer to a `static` item or a `fn` item, the program is ill-formed. - >[!NOTE] > the path-expr may have any type, including a type that isn't an *asm operand type*, and may be either mutable or immutable. r[dynamic.asm.operand.sym] A sym operand is an operand that uses the `sym` keyword. The operand contains a `sym-expr` that specifies the item the symbol refers to. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +# use core::mem::MaybeUninit; +static FOO: MaybeUninit = MaybeUninit::zeroed(); +core::arch::asm!("mov eax, dword ptr [{}]", sym FOO, out("eax") x); +# }} +``` r[dynamic.asm.operands.expansion] Each operand_spec is expanded in the *joined asm-string* according to the modifiers in `modifier_spec` and the operand. Each reg_operand is assigned to a register according to the reg_spec, and expands to the appropriate version of the `reg_operand`, in the format expected by the asm syntax in effect to specify the appropriate register. A sym operand expand to the linkage name ([dynamic.linkage.name]) of the item referred to by the `path-expr`, if it has either the `#[no_mangle]` or `#[export_name]` attribute, or is defined in an `extern` block, and otherwise, it expands to an unspecified string that can be used within the *expanded asm-string* to refer to the item. @@ -203,8 +372,20 @@ Each operand_spec is expanded in the *joined asm-string* according to the modifi r[dynamic.asm.operands.global] The program shall not use an operand, other than a sym operand, in the expansion of the [`core::arch::global_asm!`] macro. +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] +core::arch::global_asm!("", in("eax") 5); +``` + +```rust +static FOO: () = (); +# #[cfg(target_arch = "x86_64")] +core::arch::global_asm!("/*{}*/", sym FOO); +``` + r[dynamic.asm.operands.clobbers_abi] -A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, the no reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. +A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, the no reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. + >[!NOTE] > Multiple `clobbers_abi` operands may be specified. If a register is considered clobbered by multiple `clobbers_abi` operands, it acts as though only one of those `clobbers_abi` operands specifies that register. @@ -224,14 +405,37 @@ A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand >[!NOTE] > - On AArch64 `x18` only included in the clobber list if it is not considered as a reserved register on the target. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("", clobbers_abi("C")); +# }} +``` + +r[dynamic.asm.operands.clobbers_abi_ref] +A `clobbers_abi` special operand shall be specified after each positional operand, and shall not be a named operand. A `clobbers_abi` special operand cannot be referred to by an operand_specifier + ## Register operands [dynamic.asm.registers] r[dynamic.asm.registers.explicit] An explicit register operand specifies the name of a valid operand register that is not a reserved register, or an alias name. Multiple explicit register operands shall not specify the same register or aliases of the same register. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i64; +core::arch::asm!("mov eax, 5", out("eax") x); +# }} +``` + r[dynamic.asm.registers.class] A register operand that is not an explicit register operand specifies the name of a register class as an identifier. When a register class is specified, the implementation assigns an unspecified register belonging to that class to the operand. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i64; +core::arch::asm!("mov {}, 5", out(reg) x); +# }} +``` + r[dynamic.asm.registers.valid-types] Each register class, and the explicit registers within those classes, may restrict the set of types allowed for operands referring to that class or those registers. @@ -251,8 +455,10 @@ r[dynamic.asm.registers.class-list] > The list of valid register classes, the constituent registers, the required target feature (if any), and the valid types for those classes are: > | Architecture | Register class | Registers | Target feature | Allowed types | > | ------------ | -------------- | --------- | -------------- | ------------- | -> | x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp`, `r[8-15]` (x86-64 only) | None | `i16`, `i32`, `f32` | -> | x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | None | `i16`, `i32`, `f32`, `i64`, `f64` | +> | x86-32 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp` | None | `i16`, `i32`, `f32` | +> | x86-64 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp`, `r[8-15]` | None | `i16`, `i32`, `f32`, `i64`, `f64` | +> | x86-32 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | None | `i16`, `i32`, `f32` | +> | x86-64 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | None | `i16`, `i32`, `f32`, `i64`, `f64` | > | x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | None | `i8` | > | x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `bpl`, `r[8-15]b` | None | `i8` | > | x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `sse` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | @@ -299,6 +505,12 @@ Certain registers and register classes are *clobbers only*. Such register names > * On AArch64: the `preg` class, and the registers belonging to that class > * On RISC-V: The `vreg` class, and the registers belonging to that class. +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i64; +core::arch::asm!("mov {}, 5", out("k0") x); +# }} +``` r[dynamic.asm.register.small-values] If a register input is specified with a type that has a smaller width than the register class according to the target, the remaining bits of the register are set to an unspecified value. @@ -306,6 +518,16 @@ If a register input is specified with a type that has a smaller width than the r >[!TARGET-SPECIFIC] > On RISC-V, in the case of an `freg` input of type `f32`, the upper bits are instead set to all 1s according to the `D` extension of the RISC-V specification. +```rust,ignore +// The following code may have unpredictable results +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32 = 0; +let y: i64; +core::arch::asm!("mov {}, {}", out(reg) y, in(reg) x); +println!("{y}"); +# }} +``` + r[dynamic.asm.register.aliases] Certain explicit register names have defined aliases. These register names are considered identical to canonical register name and may be specified in place of the canonical name in an explicit register operand @@ -369,6 +591,13 @@ Certain explicit register names have defined aliases. These register names are c > | LoongArch | `$f[8-23]` | `$ft[0-15]` | > | LoongArch | `$f[24-31]` | `$fs[0-7]` | +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i64; +core::arch::asm!("mov {}, 5", out("rax") x); +# }} +``` + r[dynamic.asm.register.reserved] Certain registers are reserved registers. Reserved Registers shall not be named by an explicit register operand. @@ -393,7 +622,11 @@ Certain registers are reserved registers. Reserved Registers shall not be named > | LoongArch | `$r2` or `$tp` | This is reserved for TLS. | > | LoongArch | `$r21` | This is reserved by the ABI. | - +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("mov rsp, 5", out("rsp") x); +# }} +``` ## Template modifiers r[dynamic.asm.template] @@ -403,7 +636,6 @@ An operand spec that refers to a register operand may specify a modifier as part r[dynamic.asm.template.class] A format specifier shall only use a modifier that is supported for the register class specified by the register opernd. - >[!TARGET-SPECIFIC] > The list of supported modifiers for each register class is as follows > | Architecture | Register class | Modifier | Example output | LLVM modifier | @@ -444,6 +676,13 @@ A format specifier shall only use a modifier that is supported for the register > | LoongArch | `reg` | None | `$r1` | None | > | LoongArch | `freg` | None | `$f0` | None | +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32 = 5; +let y: i32; +core::arch::asm!("mov {:e}, {:e}", out(reg) y, in(reg) x); +# }} +``` >[!NOTE] > The supported modifiers are a subset of LLVM's (and GCC's) [asm template argument modifiers][llvm-argmod], but do not use the same letter codes. @@ -470,18 +709,33 @@ Each evaluation of an asm block (invocation of [`core::arch::asm!`]) shall perfo >[!TARGET-SPECIFIC] > The correspondance between the operation performed by the asm block is target-dependant and implementation-dependant, subject to the rules set in [dynamic.asm.operands]. +r[dynamic.asm.evaluation.reg-values] +The value of each register mentioned in an input operand is set according to [dynamic.asm.operands] before evaluating any instructions in the asm block. The value of each other *operand-usable register* is unspecified. The value of all other registers is target-dependant. + +>[!NOTE] +> The target may define that the register value (or some portion thereof) is undefined. + r[dynamic.asm.evaluation.constraints] Certain constraints may be placed on the asm block, and on the requirements of the correspondance, by default or by an option explicitly specified on the asm block. The behaviour is undefined if any such constraint is violated. +r[dynamic.asm.evaluation.memory] +The behaviour is undefined if the asm block accesses any allocation, or disables, freezes, or activates any tags, except via: +* An access to a static item, +* A pointer tag which has been exposed, +* A pointer tag which was passed as an input operand, or +* A pointer tag which is accessible by reading any memory the asm block can read under this clause. + r[dynamic.asm.evaluation.unwind] The behaviour is undefined if an inline assembly block exits by unwinding from a panic or a foreign exception. -r[dynamic.asm.evaluation.prefix-instr] -The behaviour is undefined if the inline assembly block ends by evaluating an instruction considered a prefix instruction on the target. Such errors may be diagnosed when statically detected. +```rust,ignore +// The following snippet has undefined behaviour +extern "C-unwind" fn panics(){panic!("unwind through asm")} +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("call {}", sym panics); +# }} +``` ->[!TARGET-SPECIFIC] -> On x86 and x86-64, the `lock`, `repnz`, `rep`, `repz`, as well as GNU AS specific address-size, data-size, and explicit rex, vex, and evex prefixes. -> It is assembler- and implementation-dependent whether or not use of these prefixes before the end of the asm string is diagnosed. r[dynamic.asm.evaluation.register-value] The behaviour is undefined upon exiting an asm block unless the stack pointer register and each operand-usable register not mentioned by an `out` , `lateout`, `inout`, or `inlateout` operand has the value the register held upon entry to the asm block. @@ -492,6 +746,19 @@ The behaviour is undefined upon exiting an asm block unless the stack pointer re > * The Direction flag (`flags.DF`) is clear upon entry and must be clear upon exit > * The x87 Stack (that is the `TOP` field of the floating-point status word, and each bit in the floating-point tag word) must be preserved and restored upon exit. If all x87 `st` registers are marked as clobbered, the stack is guaranteed to be empty on entry to the asm block (that is, `TOP` is set to `0x7` and the `ftw` is set to `0xFFFF`). +r[dynamic.asm.invocation.prefix-instr] +The behaviour is undefined if the program exits an asm block that ends with a prefix instruction that modifies the interpretation of subsequent instructions. Violations of this rule should be diagnosed if they can be detected. + +>[!TARGET-SPECIFIC] +> On x86 and x86-64, the `lock`, `repnz`, `rep`, `repz`, as well as GNU AS specific address-size, data-size, and explicit rex, vex, and evex prefixes. +> It is assembler- and implementation-dependent whether or not use of these prefixes before the end of the asm string is diagnosed. + +```rust,ignore +// The following snippet has undefined behaviour +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("lock"); +# }} +``` ## Options [dynamic.asm.options] r[dynamic.asm.options.general] @@ -503,17 +770,65 @@ The `att_syntax` option may be specfied on the x86 and x86_64 target. The progra >[!TARGET-SPECIFIC] > The `att_syntax` option modifies the syntax used to parse the *expanded asm-string* as though the `.att_syntax prefix` directive was issued before parsing the *expanded asm-string*, and modifies the expansion of register operands to include a `%` prefix. +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("movd {}, %eax", in("reg") 5, out("eax") x); +# }} +``` + r[dynamic.asm.options.nomem] -The `nomem` option may be specified. The behaviour is undefined if the assembly block modifies any allocation, disables or activates any tag, *synchronizes-with* any other thread of execution or signal handler, and the implementation may assume that the behaviour or outputs of the assembly block does not depend on the contents of any allocation. +The `nomem` option may be specified. The behaviour is undefined if the assembly block modifies any allocation, disables, freezes, or activates any tag, *synchronizes-with* any other thread of execution or signal handler, and the implementation may assume that the behaviour or outputs of the assembly block does not depend on the contents of any allocation. + + +```rust,ignore +// The following snippet has undefined behaviour +static mut FOO: i32 = 5; +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("mov dword ptr [{}], 3", sym FOO, options(nomem)); +# }} +``` + +```rust +// The following snippet may have unpredictable results +static mut FOO: i32 = 5; +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("mov {:e}, dword ptr [{}]", out(reg) x, sym FOO, options(readonly)); +# }} +``` r[dynamic.asm.options.readonly] The `readonly` option may be specified. The behaviour is undefined if the assembly block modifies any allocation or activates any tag. +```rust,ignore +// The following snippet has undefined behaviour +static mut FOO: i32 = 5; +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("mov dword ptr [{}], 3", sym FOO, options(readonly)); +# }} +``` + r[dynamic.asm.options.exclusive] The program shall not specify both the `nomem` and `readonly` options. +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("mov dword ptr [FOO], 3", sym panics); +# }} +``` + r[dynamic.asm.options.pure] -The `pure` option may be specfied. The evaluation of the assembly block shall not produce any observable behaviour or consume input, and the implementation may assume that the outputs of the assembly block depends only on the inputs and the contents of any allocation. If the program specifies the `pure` option, it shall specify either the `nomem` or `readonly` option. +The `pure` option may be specfied. The evaluation of the assembly block shall not produce any observable behaviour, consume input, or terminate execution, and the implementation may assume that the outputs of the assembly block depends only on the inputs and the contents of any allocation. If the program specifies the `pure` option, it shall specify either the `nomem` or `readonly` option. + +```rust,ignore +// The following snippet has undefined behaviour +static mut FOO: i32 = 5; +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("xor edi, edi","call exit@plt", options(pure, readonly)); +# }} +``` + r[dynamic.asm.options.nostack] The `nostack` option may be specified. The implementation may assume that the assembly block does not modify or access the stack, except an allocation placed in that region by the implementation. @@ -522,6 +837,14 @@ The `nostack` option may be specified. The implementation may assume that the as > The stack is defined by an target-specific register and is a target-specific memory region. It may include a "red zone". > On x86 and x86-64 targets, if the `nostack` option is not specified, the `rsp` register will be aligned to 16 bytes. +```rust,ignore +// The following snippet has undefined behaviour +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("push 5", "pop rax", out("eax") x, options(nostack)); +# }} +``` + r[dynamic.asm.options.preserve_flags] The `preserves_flags` option may be specified. The implementation may assume that the value of the status flags are preserved by the assembly block. @@ -547,13 +870,45 @@ The `preserves_flags` option may be specified. The implementation may assume tha > - LoongArch > - Floating-point condition flags in `$fcc[0-7]`. +```rust,ignore +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("cmp eax, eax", in("eax") 5, options(preserve_flags)); +# }} +``` + r[dynamic.asm.options.noreturn] The `noreturn` option may be specifed. An invocation of the [`core::arch::asm!`] macro that specifies the `noreturn` option expands to an expression of type `!`. The behaviour is undefined if an evaluation of the assembly block exits. The program shall not specify the `clobber_abi` specification, or an operand that is an `out`, `lateout`, `inout`, or `inlateout` operand. +```rust +# #[cfg(target_arch = "x86_64")] +pub fn main() -> ! { + unsafe{ + core::arch::asm!("xor edi, edi", "call exit@plt", options(noreturn)); + } +} +``` + +```rust,ignore +// The following snippet has undefined behaviour +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("", options(noreturn)); +# }} +``` + +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x: i32; +core::arch::asm!("xor edi, edi", "call exit@plt", out("edi") x, options(noreturn)); +# }} +``` + r[dynamic.asm.options.global] A program shall not specify an option, other than the `att_syntax` option, in an invocation of the [`core::arch::global_asm!`] macro. - +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] +core::arch::global_asm!("", options(noreturn)); +``` ## Directives Support [dynamic.asm.directives] From 6512bc6966159dfebccbfc742593bd580fa2bf3d Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 9 Jul 2024 17:27:39 -0400 Subject: [PATCH 09/30] Fix doc links in dynamic.asm.operands.types --- src/inline-assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index f79b8d2ed..47e5bd9ea 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -282,7 +282,7 @@ core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); >[!TARGET-SPECIFIC] -> On x86 platforms, the types [`core::arch::x86::__m128`], [`core::arch::x86::__m256`], and variants of those types are *asm operand types*. +> On x86 platforms, the types [`__m128`][core::arch::x86_64::__m128], [`__m256`][core::arch::x86_64::__m256], and variants of those types are *asm operand types*. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ From 2a9f58d2b105cb03b95df4c28b97db68a79ffe53 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 10 Jul 2024 13:55:42 -0400 Subject: [PATCH 10/30] Edit styling of dynamic.asm and fix test failures --- src/inline-assembly.md | 177 +++++++++++++++++++++-------------------- theme/reference.css | 27 +++++++ 2 files changed, 118 insertions(+), 86 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 47e5bd9ea..38f6b2332 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1,28 +1,27 @@ # Inline assembly [dynamic.asm] -r[dynamic.asm.syntax] +r[dynamic.asm.macros] The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] expand to inline assembly syntax when used in the expression position and item position respectively. The macros shall not be expanded in any other context. ->[!NOTE] +> [!NOTE] > The expansion of the macros has no stable syntax equivalent. This section will refer to the expansion of the macro, rather than the surface syntax. -r[dynamic.asm.safety] +r[dynamic.asm.safety] The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. ->[!NOTE] +> [!NOTE] > Inline assembly is inherently unsafe. > It requires asserting various constraints to the compiler that it cannot check, and can perform operations equivalent to calling a foreign function. ```rust,compile_fail -# #[cfg(target_arch = "x86_64")] -# fn main(){ +# #[cfg(target_arch = "x86_64")] { use core::arch::asm; asm!("/*inline assembly is inherently unsafe*/"); # } ``` -r[dynamic.asm.support] +r[dynamic.asm.support] Inline assembly is supported only when compiling for a target using one of the following architectures. A program that contains inline assembly is ill-formed on any other target: - x86 and x86-64 - ARM @@ -98,7 +97,9 @@ pub fn main(){ r[dynamic.asm.invocation.global_asm] The [`core::arch::global_asm!`] macro shall be expanded in an item context only. The input tokens shall match the `asm_inner` production. If the macro is expanded in a function, the program is ill-formed. -```rust + + +```rust,ignore # #[cfg(target_arch = "x86_64")] core::arch::global_asm!(".rodata", "FOO:", ".ascii \"Hello World\""); ``` @@ -113,7 +114,7 @@ pub fn main(){ r[dynamic.asm.invocation.format-string] Each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_asm!`] macros shall be an expanded string literal for which the content matches the `asm_string_piece` production. ->[!NOTE] +> [!NOTE] > an expanded string literal is a string literal (after expanding macros like [`core::concat!`]) that has had every unicode escape sequence replaced with the (appropriately escaped as needed) matching character, and which has been normalized from a raw string literal. @@ -161,11 +162,10 @@ core::arch::asm!("mov {1}, {0}", in(reg) 5i64, out(reg) x); r[dynamic.asm.invocation.named] A `format_specifier` that has an `operand_specifier` which is an ident is called a named specifier, and refers to the named operand with the specified name. - ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ let x: i32; -core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, output = out("eax") x); +core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, output = out(reg) x); # }} ``` @@ -175,7 +175,7 @@ The *joined asm-string* is expanded as defined in [dynamic.asm.operands.expansio r[dynamic.asm.invocation.syntax] The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the target. Invoking the macro with a *expanded asm-string* that does not match syntax requirements is *conditionally supported* and has *assembler dependent behaviour*. Invoking a directive that is not specified by [dynamic.asm.directives] is *conditionally supported* and has *assembler dependent behaviour*. ->[TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > On x86 and x86_64 targets, the syntax of the *expanded asm-string* acts as though the directive `.intel_syntax noprefix` is issued before parsing the *expanded asm-string*, except that the `option(att_syntax)` causes the syntax to act as though the directive `.att_syntax prefix` is issued before parsing the *expanded asm-string* instead. > On ARM and Aarch64 targets, the syntax of the *expanded asm-string* acts as though the directive `.syntax unified` is issued before parsing the *expanded asm-string*. @@ -187,24 +187,24 @@ The number of times, locations, and the order in which a given invocation of [`c // The results, including whether it succesfully compiles, may depend on non-local use sites of the function, and on optimization settings. # #[cfg(target_arch = "x86_64")] { unsafe{ let x: i32; -core::arch::asm!("foo: jmp foo", input = in(reg) 5i64, output = out("eax") x); +core::arch::asm!("foo: jmp foo"); # }} ``` ->[!NOTE] +> [!NOTE] > In particular, an asm block may be duplicated, for example if the containing function is inlined, or omitted from the output entirely. > As a consequence, asm blocks should not use directives that have non-idempotent non-local effects, or named labels and symbol definitions. > Additionally, two asm blocks may not rely upon being adjacent in executable memory, even if they are adjacent in the source. ->[!NOTE] +> [!NOTE] > Local Labels (a decimal literal that doesn't solely consist of 0s and 1s) may be used freely if the asm block needs to define a label. > See [The GNU AS Manual on Local Labels](https://sourceware.org/binutils/docs/as/Symbol-Names.html) for details on local labels. > It is not guaranteed that a local label defined in one asm block will be accessible from an adjacent asm block. -```rust +```rust,no_run # #[cfg(target_arch = "x86_64")] { unsafe{ let x: i32; -core::arch::asm!("2: jmp 2b", input = in(reg) 5i64, output = out("eax") x); +core::arch::asm!("2: jmp 2b"); # }} ``` @@ -214,17 +214,31 @@ The order in which invocations of [`core::arch::global_asm!`] are expanded is un r[dynamic.asm.invocation.directive-state] The *expanded asm-string* shall not issue a directive that modifies the global state of the assembler for processing inputs unless it issues a directive to restore that state it had upon entering the block. No diagnostic is required. ->[!NOTE] +> [!NOTE] > This include state such as the current section of the assembler, the syntax mode, or the kind of assembly output being generated. > Failing to obey this requirement can have significant impact on code generation, including code unrelated to the asm block. For example, an asm block that issues a `.data` directive without resetting to the appropriate section for the function can cause the following code in the function to be generated in the `.data` section, and for execution to fall off the asm block into improper memory. r[dynamic.asm.invocation.global-section] -The *expanded asm-string* of a [`core::arch::global_asm!`] invocation acts as though an target-dependant directive is issued before the *expanded asm-string* which causes code to be generated in the default section on the target for executable code. +The *expanded asm-string* of a [`core::arch::global_asm!`] invocation acts as though a `.section` directive is issued before the *expanded asm-string* which causes code to be generated in the default section on the target for executable code. ->[!NOTE] +> [!NOTE] > This section is typically named `.text`. +r[dynamic.asm.invocation.prefix-instr] +An *expanded asm-string* shall not end with an instruction that is interpreted as a prefix on the architecture. No Diagnostic is required + +> [!TARGET-SPECIFIC] +> On x86 and x86-64, the `lock`, `repnz`, `rep`, `repz`, as well as GNU AS specific address-size, data-size, and explicit rex, vex, and evex prefixes. + + +```rust,ignore +// The following snippet is ill-formed +# #[cfg(target_arch = "x86_64")] { unsafe{ +core::arch::asm!("lock"); +# }} +``` + ## Operand types [dynamic.asm.operands] r[dynamic.asm.operands.positional] @@ -243,7 +257,7 @@ Operands that specify an ident are named operands. A named operand shall not spe ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ let x: i32; -core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); +core::arch::asm!("mov rax, {input}", input = in(reg) 5i64, out("eax") x); # }} ``` @@ -257,7 +271,7 @@ core::arch::asm!("mov eax, ecx", in("rcx") 5i64, out("eax") x); # }} ``` ->[!NOTE] +> [!NOTE] > Explicit Register Operands have no `ident` name and cannot be referred to by an operand specifier r[dynamic.asm.operands.types] @@ -281,11 +295,12 @@ core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); ``` ->[!TARGET-SPECIFIC] -> On x86 platforms, the types [`__m128`][core::arch::x86_64::__m128], [`__m256`][core::arch::x86_64::__m256], and variants of those types are *asm operand types*. +> [!TARGET-SPECIFIC] +> On x86 platforms, the types [`__m128`](core::arch::x86_64::__m128), [`__m256`](core::arch::x86_64::__m256), and variants of those types are *asm operand types*. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ +# use core::arch::x86_64::__m128; // But vector types are allowed. let x: __m128; core::arch::asm!("xorps xmm0, xmm0", out("xmm0") x); @@ -300,7 +315,7 @@ An `output_expr` shall be the placeholder expression `_` or a (potentially uniti ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { unsafe{ -let mut x: i32; +let x: i32 = 0; core::arch::asm!("", out("eax") x); # }} ``` @@ -308,7 +323,7 @@ core::arch::asm!("", out("eax") x); r[dynamic.asm.operands.inout-expr] An `inout_expr` shall either be an (initialized) place expression of an *asm operand type*, or shall specify both an `input_expr` and an `output_expr`. If only a single expression is specified, it is treated as both the `input_expr` and `output_expr` of the operand. ->[!NOTE] +> [!NOTE] > When a single expression is specified, it must be an initialized mutable place expression. r[dynamic.asm.operands.in] @@ -317,20 +332,20 @@ An `in` operand is an reg_operand with the `in` dir_spec. The `operand_expr` of r[dynamic.asm.operands.out] An `out` operand is a reg_operand with the `out` dir_spec, and a `lateout` operand is a reg_operand with the `lateout` dir_spec. The `operand_expr` of an `out` operand or `lateout` operand shall be an `output_expr`. The value of the register at the exit of the asm block is written to the `output_expr` place if it is not a placeholder expression ->[!NOTE] +> [!NOTE] > A `lateout` operand differs from an `out` operand only in that the implementation may assume that no `in`, `inout`, or `inlateout` operands are read after a `lateout` operand is modified by the *expanded asm-string*. r[dynamic.asm.operands.inout] An `inout` operand is a reg_operand with the `inout` dir_spec, and a `inlateout` operand is a reg_operand with the `inlateout` dir_spec. The `operand_expr` of an `inout` operand or an `inlateout` operand shall be an `inout_expr`. The `input_expr` and `output_expr` of an `inout` or `inlateout` operand is used as though the `inout` operand is replaced with a separate `in` and `out` operand, and the `inlateout` operand is replaced with a separate `in` and `lateout` operand, except that both have the same position if they are positional, or the same name if they are named operands, and both refer to the same register. ->[!NOTE] +> [!NOTE] > An `inlateout` operand differs from an `inout` operand only in that implementation may assume that no other `in`, `inout`, or `inlateout` operands are read after an `inlateout` operand is modified by the *expanded asm-string*. r[dynamic.asm.operands.clobbers] An `output_expr` that is the placeholder expression `_` is a clobber output. The resulting value of the register is discarded. ->[!NOTE] +> [!NOTE] > Some registers and register classes cannot be used as an operand, other than as a clobber operand. ```rust @@ -343,7 +358,7 @@ core::arch::asm!("mov eax, 5", out("eax") _); r[dynamic.asm.operands.sym-expr] A sym-expr is a path-expr. If the `path-expr` does not refer to a `static` item or a `fn` item, the program is ill-formed. ->[!NOTE] +> [!NOTE] > the path-expr may have any type, including a type that isn't an *asm operand type*, and may be either mutable or immutable. r[dynamic.asm.operand.sym] @@ -353,31 +368,34 @@ A sym operand is an operand that uses the `sym` keyword. The operand contains a # #[cfg(target_arch = "x86_64")] { unsafe{ # use core::mem::MaybeUninit; static FOO: MaybeUninit = MaybeUninit::zeroed(); -core::arch::asm!("mov eax, dword ptr [{}]", sym FOO, out("eax") x); +let x: i32; +core::arch::asm!("mov eax, dword ptr [{}+rip]", sym FOO, out("eax") x); # }} ``` r[dynamic.asm.operands.expansion] Each operand_spec is expanded in the *joined asm-string* according to the modifiers in `modifier_spec` and the operand. Each reg_operand is assigned to a register according to the reg_spec, and expands to the appropriate version of the `reg_operand`, in the format expected by the asm syntax in effect to specify the appropriate register. A sym operand expand to the linkage name ([dynamic.linkage.name]) of the item referred to by the `path-expr`, if it has either the `#[no_mangle]` or `#[export_name]` attribute, or is defined in an `extern` block, and otherwise, it expands to an unspecified string that can be used within the *expanded asm-string* to refer to the item. ->[!NOTE] +> [!NOTE] > The name given to an item used by a sym-expr that does not have a linkage name may be known as the "mangled" name of the item. ->[!NOTE] +> [!NOTE] > A sym operand does not include any relocation modifiers such as `@plt` or `@tpoff`. The *joined asm-string* is responsible for including these as required. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > On x86 and x86_64 targets, the register name is expanded as-is if the `options(att_syntax)` is not used, and with the `%` prefix if `options(att_syntax)` is used. r[dynamic.asm.operands.global] The program shall not use an operand, other than a sym operand, in the expansion of the [`core::arch::global_asm!`] macro. -```rust,compile_fail + + +```rust,compile_fail,ignore # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("", in("eax") 5); ``` -```rust +```rust,ignore static FOO: () = (); # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("/*{}*/", sym FOO); @@ -387,10 +405,10 @@ r[dynamic.asm.operands.clobbers_abi] A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, the no reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. ->[!NOTE] +> [!NOTE] > Multiple `clobbers_abi` operands may be specified. If a register is considered clobbered by multiple `clobbers_abi` operands, it acts as though only one of those `clobbers_abi` operands specifies that register. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The list of supported ABI strings and current list of clobbered registers are > | Architecture | ABI name | Clobbered registers | > | ------------ | -------- | ------------------- | @@ -402,12 +420,12 @@ A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand > | RISC-V | `"C"`, `"system"`, `"efiapi"` | `x1`, `x[5-7]`, `x[10-17]`, `x[28-31]`, `f[0-7]`, `f[10-17]`, `f[28-31]`, `v[0-31]` | > | LoongArch | `"C"`, `"system"`, `"efiapi"` | `$r1`, `$r[4-20]`, `$f[0-23]` | ->[!NOTE] +> [!NOTE] > - On AArch64 `x18` only included in the clobber list if it is not considered as a reserved register on the target. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ -core::arch::asm!("", clobbers_abi("C")); +core::arch::asm!("", clobber_abi("C")); # }} ``` @@ -439,19 +457,19 @@ core::arch::asm!("mov {}, 5", out(reg) x); r[dynamic.asm.registers.valid-types] Each register class, and the explicit registers within those classes, may restrict the set of types allowed for operands referring to that class or those registers. ->[!NOTE] +> [!NOTE] > When an integer type which is as wide as `isize` is valid for a given register class, `isize` is also considered valid, and the same for function pointers. > When a signed integer is considered valid for a given register class, the corresponding unsigned integer is also considered valid. r[dynamic.asm.registers.target-feature] Each register class, and the explicit registers within that class may require that a specified target_feature is enabled in the ambient target_feature set, or by using the `target_feature` function attribute. The program shall not specify such registers or register classes, except as clobber output, when the feature is not enabled. Additionally specific types may be valid only if certain additional features are enabled. ->[!NOTE] +> [!NOTE] > The set of features in the ambient target_feature set are implementation-defined, and may be queried by [parse.macros.cfg]. r[dynamic.asm.registers.class-list] ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The list of valid register classes, the constituent registers, the required target feature (if any), and the valid types for those classes are: > | Architecture | Register class | Registers | Target feature | Allowed types | > | ------------ | -------------- | --------- | -------------- | ------------- | @@ -463,7 +481,7 @@ r[dynamic.asm.registers.class-list] > | x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `bpl`, `r[8-15]b` | None | `i8` | > | x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `sse` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | > | x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `avx` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` | -> | x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4`
`i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` | +> | x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `avx512f` | `i32`, `f32`, `i64`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4`
`i8x64`, `i16x32`, `i32x16`, `i64x8`, `f32x16`, `f64x8` | > | x86 | `kreg` | `k[1-7]` | `avx512f` | `i8`, `i16`, `i32` (requires `avx512bw`), `i64` (requires `avx512bw`) | > | x86 | `kreg0` | `k0` | N/A | Only clobbers | > | x86 | `x87_reg` | `st([0-7])` | N/A | Only clobbers | @@ -499,7 +517,7 @@ r[dynamic.asm.registers.class-list] r[dynamic.asm.register.clobbers_only] Certain registers and register classes are *clobbers only*. Such register names or register classes shall not be specified by an operand, other than a clobbers output. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The list of such classes and registers are: > * On x86 and x86-64: the `kreg0`, `x87_reg`, `mmx_reg`, and `tmm_reg` classes, as well as the registers belonging to these classes > * On AArch64: the `preg` class, and the registers belonging to that class @@ -515,7 +533,7 @@ core::arch::asm!("mov {}, 5", out("k0") x); r[dynamic.asm.register.small-values] If a register input is specified with a type that has a smaller width than the register class according to the target, the remaining bits of the register are set to an unspecified value. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > On RISC-V, in the case of an `freg` input of type `f32`, the upper bits are instead set to all 1s according to the `D` extension of the RISC-V specification. ```rust,ignore @@ -531,7 +549,7 @@ println!("{y}"); r[dynamic.asm.register.aliases] Certain explicit register names have defined aliases. These register names are considered identical to canonical register name and may be specified in place of the canonical name in an explicit register operand ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The List of register alias names is: > | Architecture | Base register | Aliases | > | ------------ | ------------- | ------- | @@ -594,17 +612,17 @@ Certain explicit register names have defined aliases. These register names are c ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ let x: i64; -core::arch::asm!("mov {}, 5", out("rax") x); +core::arch::asm!("mov eax, 5", out("rax") x); # }} ``` r[dynamic.asm.register.reserved] Certain registers are reserved registers. Reserved Registers shall not be named by an explicit register operand. ->[!NOTE] +> [!NOTE] > Reserved Registers that belong to a register class may still be assigned to register operands regardless ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > | Architecture | Unsupported register | Reason | > | ------------ | -------------------- | ------ | > | All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | @@ -636,7 +654,7 @@ An operand spec that refers to a register operand may specify a modifier as part r[dynamic.asm.template.class] A format specifier shall only use a modifier that is supported for the register class specified by the register opernd. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The list of supported modifiers for each register class is as follows > | Architecture | Register class | Modifier | Example output | LLVM modifier | > | ------------ | -------------- | -------- | -------------- | ------------- | @@ -684,10 +702,10 @@ core::arch::asm!("mov {:e}, {:e}", out(reg) y, in(reg) x); # }} ``` ->[!NOTE] +> [!NOTE] > The supported modifiers are a subset of LLVM's (and GCC's) [asm template argument modifiers][llvm-argmod], but do not use the same letter codes. ->[!NOTE] +> [!NOTE] > - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register. > - on x86: our behavior for `reg` with no modifiers differs from what GCC does. > GCC will infer the modifier based on the operand value type, while we default to the full register size. @@ -703,16 +721,16 @@ A lint diagnostic should be emitted if a modifier is omitted, or a modifier is u r[dynamic.asm.evaluation.general] Each evaluation of an asm block (invocation of [`core::arch::asm!`]) shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. ->[!NOTE] +> [!NOTE] > The operation the asm block performs may differ between evaluations of the same asm block. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The correspondance between the operation performed by the asm block is target-dependant and implementation-dependant, subject to the rules set in [dynamic.asm.operands]. r[dynamic.asm.evaluation.reg-values] The value of each register mentioned in an input operand is set according to [dynamic.asm.operands] before evaluating any instructions in the asm block. The value of each other *operand-usable register* is unspecified. The value of all other registers is target-dependant. ->[!NOTE] +> [!NOTE] > The target may define that the register value (or some portion thereof) is undefined. r[dynamic.asm.evaluation.constraints] @@ -720,10 +738,10 @@ Certain constraints may be placed on the asm block, and on the requirements of t r[dynamic.asm.evaluation.memory] The behaviour is undefined if the asm block accesses any allocation, or disables, freezes, or activates any tags, except via: -* An access to a static item, +* An access to a static item which is declared with the `#[no_mangle]` attribute, the `#[export_name]` attribute, or which is visible to an expression within the function in which the asm block is expanded, * A pointer tag which has been exposed, * A pointer tag which was passed as an input operand, or -* A pointer tag which is accessible by reading any memory the asm block can read under this clause. +* A pointer tag which is accessible by reading any memory the asm block can read under this clause, recursively. r[dynamic.asm.evaluation.unwind] The behaviour is undefined if an inline assembly block exits by unwinding from a panic or a foreign exception. @@ -740,25 +758,12 @@ core::arch::asm!("call {}", sym panics); r[dynamic.asm.evaluation.register-value] The behaviour is undefined upon exiting an asm block unless the stack pointer register and each operand-usable register not mentioned by an `out` , `lateout`, `inout`, or `inlateout` operand has the value the register held upon entry to the asm block. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > In addition to operand-usable registers, certain other registers on a target may require being preserved, or have specific rules regarding the value at exit. > On x86 and x86-64 targets: > * The Direction flag (`flags.DF`) is clear upon entry and must be clear upon exit > * The x87 Stack (that is the `TOP` field of the floating-point status word, and each bit in the floating-point tag word) must be preserved and restored upon exit. If all x87 `st` registers are marked as clobbered, the stack is guaranteed to be empty on entry to the asm block (that is, `TOP` is set to `0x7` and the `ftw` is set to `0xFFFF`). -r[dynamic.asm.invocation.prefix-instr] -The behaviour is undefined if the program exits an asm block that ends with a prefix instruction that modifies the interpretation of subsequent instructions. Violations of this rule should be diagnosed if they can be detected. - ->[!TARGET-SPECIFIC] -> On x86 and x86-64, the `lock`, `repnz`, `rep`, `repz`, as well as GNU AS specific address-size, data-size, and explicit rex, vex, and evex prefixes. -> It is assembler- and implementation-dependent whether or not use of these prefixes before the end of the asm string is diagnosed. - -```rust,ignore -// The following snippet has undefined behaviour -# #[cfg(target_arch = "x86_64")] { unsafe{ -core::arch::asm!("lock"); -# }} -``` ## Options [dynamic.asm.options] r[dynamic.asm.options.general] @@ -767,13 +772,13 @@ An options-spec provided in the asm invocation places constraints on the assembl r[dynamic.asm.options.att_syntax] The `att_syntax` option may be specfied on the x86 and x86_64 target. The program shall not specify the `att_syntax` option on any other target. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The `att_syntax` option modifies the syntax used to parse the *expanded asm-string* as though the `.att_syntax prefix` directive was issued before parsing the *expanded asm-string*, and modifies the expansion of register operands to include a `%` prefix. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ let x: i32; -core::arch::asm!("movd {}, %eax", in("reg") 5, out("eax") x); +core::arch::asm!("mov {:e}, %eax", in(reg) 5, out("eax") x, options(att_syntax)); # }} ``` @@ -785,7 +790,7 @@ The `nomem` option may be specified. The behaviour is undefined if the assembly // The following snippet has undefined behaviour static mut FOO: i32 = 5; # #[cfg(target_arch = "x86_64")] { unsafe{ -core::arch::asm!("mov dword ptr [{}], 3", sym FOO, options(nomem)); +core::arch::asm!("mov dword ptr [{}+rip], 3", sym FOO, options(nomem)); # }} ``` @@ -794,7 +799,7 @@ core::arch::asm!("mov dword ptr [{}], 3", sym FOO, options(nomem)); static mut FOO: i32 = 5; # #[cfg(target_arch = "x86_64")] { unsafe{ let x: i32; -core::arch::asm!("mov {:e}, dword ptr [{}]", out(reg) x, sym FOO, options(readonly)); +core::arch::asm!("mov {:e}, dword ptr [{}+rip]", out(reg) x, sym FOO, options(nomem)); # }} ``` @@ -805,7 +810,7 @@ The `readonly` option may be specified. The behaviour is undefined if the assemb // The following snippet has undefined behaviour static mut FOO: i32 = 5; # #[cfg(target_arch = "x86_64")] { unsafe{ -core::arch::asm!("mov dword ptr [{}], 3", sym FOO, options(readonly)); +core::arch::asm!("mov dword ptr [{}+rip], 3", sym FOO, options(readonly)); # }} ``` @@ -814,7 +819,7 @@ The program shall not specify both the `nomem` and `readonly` options. ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { unsafe{ -core::arch::asm!("mov dword ptr [FOO], 3", sym panics); +core::arch::asm!("mov dword ptr [FOO+rip], 3", options(readonly, nomem)); # }} ``` @@ -833,9 +838,9 @@ core::arch::asm!("xor edi, edi","call exit@plt", options(pure, readonly)); r[dynamic.asm.options.nostack] The `nostack` option may be specified. The implementation may assume that the assembly block does not modify or access the stack, except an allocation placed in that region by the implementation. ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The stack is defined by an target-specific register and is a target-specific memory region. It may include a "red zone". -> On x86 and x86-64 targets, if the `nostack` option is not specified, the `rsp` register will be aligned to 16 bytes. +> If the `nostack` option is *not* specified, then the stack pointer is guaranteed to point to memory that can be allocated by the asm block, which is aligned at least as much as is required by the ABI for a function call. ```rust,ignore // The following snippet has undefined behaviour @@ -848,7 +853,7 @@ core::arch::asm!("push 5", "pop rax", out("eax") x, options(nostack)); r[dynamic.asm.options.preserve_flags] The `preserves_flags` option may be specified. The implementation may assume that the value of the status flags are preserved by the assembly block. ->[!TARGET-SPECFIC] +> [!TARGET-SPECFIC] > - These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set: > - x86 > - Status flags in `EFLAGS` (CF, PF, AF, ZF, SF, OF). @@ -912,7 +917,7 @@ core::arch::global_asm!("", options(noreturn)); ## Directives Support [dynamic.asm.directives] -r[dynamic.asm.directives] +r[dynamic.asm.directives.gen] The common subset of the LLVM and GNU AS Assembly Syntax used for the *expanded asm-string* is guaranteed to support the following directives - `.2byte` - `.4byte` @@ -962,12 +967,12 @@ The common subset of the LLVM and GNU AS Assembly Syntax used for the *expanded - `.uleb128` - `.word` ->[!NOTE] +> [!NOTE] > These directives are generally ones that solely emit sequences of bytes, or that modify the property of symbols. r[dynamic.asm.directives.dwarf] ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The following Directives are guaranteed to be supported on ELF Targets that use DWARF Debug Information and DWARF Unwind Tables > - `.cfi_adjust_cfa_offset` > - `.cfi_def_cfa` @@ -993,7 +998,7 @@ r[dynamic.asm.directives.dwarf] r[dynamic.asm.directives.seh] ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The following directives are guaranteed to be supported on platforms that use Structured Exception Handling > - `.seh_endproc` > - `.seh_endprologue` @@ -1006,7 +1011,7 @@ r[dynamic.asm.directives.seh] r[dynamic.asm.directives.x86] ->[!TARGET-SPECIFIC] +> [!TARGET-SPECIFIC] > The following directives are guaranteed to be supported on x86 and x86-64 platforms > - `.nops` > - `.code16` diff --git a/theme/reference.css b/theme/reference.css index 8d000fbf0..1621b9af1 100644 --- a/theme/reference.css +++ b/theme/reference.css @@ -54,6 +54,12 @@ main .warning p::before { color: #80d0d0; } +.coal main .target-specific p, +.navy main .target-specific p, +.ayu main .target-specific p { + background: #005f10; +} + /* tags can be used to highlight specific character elements. */ kbd { border: 1px solid #999; @@ -143,6 +149,8 @@ span.version { font-size: 0.9em; } + + /* tags are used to indicate a specific word or phrase is being defined. */ dfn { font-style: italic; @@ -187,6 +195,25 @@ blockquote > p { margin-bottom: 0px; } +.target-specific blockquote { + margin-left: 1em; + margin-bottom: 1em; + margin-right: 6vw; + background: #00f82e; + padding: 0.2ex 0.5ex; + border-radius: 5px; + display: block; + box-shadow: inset -1px -1px 0 #06a430; + font-size: 0.9em; +} + +.ayu .target-specific blockquote, +.navy .target-specific blockquote, +.coal .target-specific blockquote { + background: #008f2e; + box-shadow: inset -1px -1px 0 #03a430; +} + /* When the sidebar is visible, reduce the spacing of rules so that the content doesn't get shifted too far, and make the text too narrow. */ From c3af3cadaf49a0e2da4665a255e04cd49528a568 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 11 Jul 2024 12:09:44 -0400 Subject: [PATCH 11/30] Change chapter identifier to be `asm` rather than `dynamic.asm` --- src/inline-assembly.md | 165 ++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 83 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 2c0acd67a..d1fee38d5 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1,12 +1,12 @@ -# Inline assembly [dynamic.asm] +# Inline assembly [asm] -r[dynamic.asm.macros] +r[asm.macros] The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] expand to inline assembly syntax when used in the expression position and item position respectively. The macros shall not be expanded in any other context. > [!NOTE] > The expansion of the macros has no stable syntax equivalent. This section will refer to the expansion of the macro, rather than the surface syntax. -r[dynamic.asm.safety] +r[asm.safety] The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. > [!NOTE] @@ -21,7 +21,7 @@ The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. # } ``` -r[dynamic.asm.support] +r[asm.support] Inline assembly is supported only when compiling for a target using one of the following architectures. A program that contains inline assembly is ill-formed on any other target: - x86 and x86-64 - ARM @@ -50,7 +50,7 @@ assert_eq!(x, 4 * 6); # } ``` -## Syntax [dynamic.asm.syntax] +## Syntax [asm.syntax] ```abnf format_string := STRING_LITERAL / RAW_STRING_LITERAL @@ -80,9 +80,9 @@ asm_string_piece := non_format_char / format_specifier / format_escape asm_string_content := [*asm_string_piece] ``` -## Invocation [dynamic.asm.invocation] +## Invocation [asm.invocation] -r[dynamic.asm.invocation.asm] +r[asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. ```rust @@ -94,7 +94,7 @@ pub fn main(){ } ``` -r[dynamic.asm.invocation.global_asm] +r[asm.invocation.global_asm] The [`core::arch::global_asm!`] macro shall be expanded in an item context only. The input tokens shall match the `asm_inner` production. If the macro is expanded in a function, the program is ill-formed. @@ -111,14 +111,14 @@ pub fn main(){ } ``` -r[dynamic.asm.invocation.format-string] +r[asm.invocation.format-string] Each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_asm!`] macros shall be an expanded string literal for which the content matches the `asm_string_piece` production. > [!NOTE] > an expanded string literal is a string literal (after expanding macros like [`core::concat!`]) that has had every unicode escape sequence replaced with the (appropriately escaped as needed) matching character, and which has been normalized from a raw string literal. -r[dynamic.asm.invocation.concat] +r[asm.invocation.concat] If multiple `format_string` inputs are provided, then they are concatenated as though by the [`core::concat!`] macro, separating each `format_string` with a string containing a single newline character. If any `format_string` begins a `format_specifier` that is not terminated before the end of the `format_string`, the program is ill-formed. The resulting string is known as the *joined asm-string* ```rust @@ -130,8 +130,8 @@ core::arch::asm!("mov rax, 5\nmov rcx, rax", out("rax") x, out("rcx") _); # }} ``` -r[dynamic.asm.invocation.operands] -Each operand, other than an explicit register operand ([dynamic.asm.operands.register]) shall be mentioned by at least one format_specifier in the *joined asm-string*. Explicit registers may not be referred to be a format_specifier. +r[asm.invocation.operands] +Each operand, other than an explicit register operand ([asm.operands.register]) shall be mentioned by at least one format_specifier in the *joined asm-string*. Explicit registers may not be referred to be a format_specifier. ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { unsafe{ @@ -139,7 +139,7 @@ core::arch::asm!("", in(reg) 5i64); # }} ``` -r[dynamic.asm.invocation.positional] +r[asm.invocation.positional] A `format_specifier` that does not specify an `operand_specifier` is called a positional specifier, and refers to the `nth` successive positional operand, where `n` is `0` for the first positional specifier in the *joined asm-string* and increases by 1 for each successive positional specifier in the *joined asm-string*. ```rust @@ -149,7 +149,7 @@ core::arch::asm!("mov rax, {}", in(reg) 5i64, out("eax") x); # }} ``` -r[dynamic.asm.invocation.explicit-positional] +r[asm.invocation.explicit-positional] A `format_specifier` that has an `operand_specifier` which is a DEC_LITERAL is called an explicit positional specifier, and refers to the `nth` successive positional operand, where `n` is the value of the DEC_LITERAL. ```rust @@ -159,7 +159,7 @@ core::arch::asm!("mov {1}, {0}", in(reg) 5i64, out(reg) x); # }} ``` -r[dynamic.asm.invocation.named] +r[asm.invocation.named] A `format_specifier` that has an `operand_specifier` which is an ident is called a named specifier, and refers to the named operand with the specified name. ```rust @@ -169,17 +169,17 @@ core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, output = out(reg # }} ``` -r[dynamic.asm.invocation.expansion] -The *joined asm-string* is expanded as defined in [dynamic.asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. +r[asm.invocation.expansion] +The *joined asm-string* is expanded as defined in [asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. -r[dynamic.asm.invocation.syntax] -The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the target. Invoking the macro with a *expanded asm-string* that does not match syntax requirements is *conditionally supported* and has *assembler dependent behaviour*. Invoking a directive that is not specified by [dynamic.asm.directives] is *conditionally supported* and has *assembler dependent behaviour*. +r[asm.invocation.syntax] +The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the target. Invoking the macro with a *expanded asm-string* that does not match syntax requirements is *conditionally supported* and has *assembler dependent behaviour*. Invoking a directive that is not specified by [asm.directives] is *conditionally supported* and has *assembler dependent behaviour*. > [!TARGET-SPECIFIC] > On x86 and x86_64 targets, the syntax of the *expanded asm-string* acts as though the directive `.intel_syntax noprefix` is issued before parsing the *expanded asm-string*, except that the `option(att_syntax)` causes the syntax to act as though the directive `.att_syntax prefix` is issued before parsing the *expanded asm-string* instead. > On ARM and Aarch64 targets, the syntax of the *expanded asm-string* acts as though the directive `.syntax unified` is issued before parsing the *expanded asm-string*. -r[dynamic.asm.invocation.duplication] +r[asm.invocation.duplication] The number of times, locations, and the order in which a given invocation of [`core::arch::asm!`] is expanded is unspecified. ```rust,ignore @@ -208,24 +208,24 @@ core::arch::asm!("2: jmp 2b"); # }} ``` -r[dynamic.asm.invocation.global-order] +r[asm.invocation.global-order] The order in which invocations of [`core::arch::global_asm!`] are expanded is unspecified. -r[dynamic.asm.invocation.directive-state] +r[asm.invocation.directive-state] The *expanded asm-string* shall not issue a directive that modifies the global state of the assembler for processing inputs unless it issues a directive to restore that state it had upon entering the block. No diagnostic is required. > [!NOTE] > This include state such as the current section of the assembler, the syntax mode, or the kind of assembly output being generated. > Failing to obey this requirement can have significant impact on code generation, including code unrelated to the asm block. For example, an asm block that issues a `.data` directive without resetting to the appropriate section for the function can cause the following code in the function to be generated in the `.data` section, and for execution to fall off the asm block into improper memory. -r[dynamic.asm.invocation.global-section] +r[asm.invocation.global-section] The *expanded asm-string* of a [`core::arch::global_asm!`] invocation acts as though a `.section` directive is issued before the *expanded asm-string* which causes code to be generated in the default section on the target for executable code. > [!NOTE] > This section is typically named `.text`. -r[dynamic.asm.invocation.prefix-instr] +r[asm.invocation.prefix-instr] An *expanded asm-string* shall not end with an instruction that is interpreted as a prefix on the architecture. No Diagnostic is required > [!TARGET-SPECIFIC] @@ -239,9 +239,9 @@ core::arch::asm!("lock"); # }} ``` -## Operand types [dynamic.asm.operands] +## Operand types [asm.operands] -r[dynamic.asm.operands.positional] +r[asm.operands.positional] Operands that do not specify an ident and are not explicit register operands are known as positional operands. Positional operands may be referred to only by positional operand specifiers and explicit positional operand specifiers, and each Positional operand must be specified before Named Operands or Explicit Register Operands. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ @@ -252,7 +252,7 @@ core::arch::asm!("mov {1}, {0}", in(reg) 5i64, out(reg) x); ``` -r[dynamic.asm.operands.named] +r[asm.operands.named] Operands that specify an ident are named operands. A named operand shall not specify an explicit register `reg_spec`. Named operand specifiers may be referred to only by named operand specifiers. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ @@ -261,7 +261,7 @@ core::arch::asm!("mov rax, {input}", input = in(reg) 5i64, out("eax") x); # }} ``` -r[dynamic.asm.operands.registers] +r[asm.operands.registers] Operands that specify an explicit register `reg_spec` are explicit register operands. ```rust @@ -274,7 +274,7 @@ core::arch::asm!("mov eax, ecx", in("rcx") 5i64, out("eax") x); > [!NOTE] > Explicit Register Operands have no `ident` name and cannot be referred to by an operand specifier -r[dynamic.asm.operands.types] +r[asm.operands.types] Each operand, other than a placeholder expression shall be of an integer type, floating-point type, function pointer type, pointer type, or target-specific vector type. These types are collectively called *asm operand types*. A pointer type is an *asm operand type* only if the pointee type has no metadata-type. ```rust,compile_fail @@ -307,10 +307,10 @@ core::arch::asm!("xorps xmm0, xmm0", out("xmm0") x); # }} ``` -r[dynamic.asm.operands.in-expr] +r[asm.operands.in-expr] An `input_expr` shall be a value expression of an *asm operand type*. -r[dynamic.asm.operands.out-expr] +r[asm.operands.out-expr] An `output_expr` shall be the placeholder expression `_` or a (potentially unitialized) place expression of an *asm operand type*. If the place expression is initialized, it shall be a mutable place. ```rust,compile_fail @@ -320,29 +320,29 @@ core::arch::asm!("", out("eax") x); # }} ``` -r[dynamic.asm.operands.inout-expr] +r[asm.operands.inout-expr] An `inout_expr` shall either be an (initialized) place expression of an *asm operand type*, or shall specify both an `input_expr` and an `output_expr`. If only a single expression is specified, it is treated as both the `input_expr` and `output_expr` of the operand. > [!NOTE] > When a single expression is specified, it must be an initialized mutable place expression. -r[dynamic.asm.operands.in] +r[asm.operands.in] An `in` operand is an reg_operand with the `in` dir_spec. The `operand_expr` of the operand shall be an `input_expr`. The `input_expr` initializes the value of the register before entering the asm block. -r[dynamic.asm.operands.out] +r[asm.operands.out] An `out` operand is a reg_operand with the `out` dir_spec, and a `lateout` operand is a reg_operand with the `lateout` dir_spec. The `operand_expr` of an `out` operand or `lateout` operand shall be an `output_expr`. The value of the register at the exit of the asm block is written to the `output_expr` place if it is not a placeholder expression > [!NOTE] > A `lateout` operand differs from an `out` operand only in that the implementation may assume that no `in`, `inout`, or `inlateout` operands are read after a `lateout` operand is modified by the *expanded asm-string*. -r[dynamic.asm.operands.inout] +r[asm.operands.inout] An `inout` operand is a reg_operand with the `inout` dir_spec, and a `inlateout` operand is a reg_operand with the `inlateout` dir_spec. The `operand_expr` of an `inout` operand or an `inlateout` operand shall be an `inout_expr`. The `input_expr` and `output_expr` of an `inout` or `inlateout` operand is used as though the `inout` operand is replaced with a separate `in` and `out` operand, and the `inlateout` operand is replaced with a separate `in` and `lateout` operand, except that both have the same position if they are positional, or the same name if they are named operands, and both refer to the same register. > [!NOTE] > An `inlateout` operand differs from an `inout` operand only in that implementation may assume that no other `in`, `inout`, or `inlateout` operands are read after an `inlateout` operand is modified by the *expanded asm-string*. -r[dynamic.asm.operands.clobbers] +r[asm.operands.clobbers] An `output_expr` that is the placeholder expression `_` is a clobber output. The resulting value of the register is discarded. > [!NOTE] @@ -355,13 +355,13 @@ core::arch::asm!("mov eax, 5", out("eax") _); # }} ``` -r[dynamic.asm.operands.sym-expr] +r[asm.operands.sym-expr] A sym-expr is a path-expr. If the `path-expr` does not refer to a `static` item or a `fn` item, the program is ill-formed. > [!NOTE] > the path-expr may have any type, including a type that isn't an *asm operand type*, and may be either mutable or immutable. -r[dynamic.asm.operand.sym] +r[asm.operand.sym] A sym operand is an operand that uses the `sym` keyword. The operand contains a `sym-expr` that specifies the item the symbol refers to. ```rust @@ -373,7 +373,7 @@ core::arch::asm!("mov eax, dword ptr [{}+rip]", sym FOO, out("eax") x); # }} ``` -r[dynamic.asm.operands.expansion] +r[asm.operands.expansion] Each operand_spec is expanded in the *joined asm-string* according to the modifiers in `modifier_spec` and the operand. Each reg_operand is assigned to a register according to the reg_spec, and expands to the appropriate version of the `reg_operand`, in the format expected by the asm syntax in effect to specify the appropriate register. A sym operand expand to the linkage name ([dynamic.linkage.name]) of the item referred to by the `path-expr`, if it has either the `#[no_mangle]` or `#[export_name]` attribute, or is defined in an `extern` block, and otherwise, it expands to an unspecified string that can be used within the *expanded asm-string* to refer to the item. > [!NOTE] @@ -385,7 +385,7 @@ Each operand_spec is expanded in the *joined asm-string* according to the modifi > [!TARGET-SPECIFIC] > On x86 and x86_64 targets, the register name is expanded as-is if the `options(att_syntax)` is not used, and with the `%` prefix if `options(att_syntax)` is used. -r[dynamic.asm.operands.global] +r[asm.operands.global] The program shall not use an operand, other than a sym operand, in the expansion of the [`core::arch::global_asm!`] macro. @@ -401,7 +401,7 @@ static FOO: () = (); core::arch::global_asm!("/*{}*/", sym FOO); ``` -r[dynamic.asm.operands.clobbers_abi] +r[asm.operands.clobbers_abi] A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, the no reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. @@ -429,12 +429,12 @@ core::arch::asm!("", clobber_abi("C")); # }} ``` -r[dynamic.asm.operands.clobbers_abi_ref] +r[asm.operands.clobbers_abi_ref] A `clobbers_abi` special operand shall be specified after each positional operand, and shall not be a named operand. A `clobbers_abi` special operand cannot be referred to by an operand_specifier -## Register operands [dynamic.asm.registers] +## Register operands [asm.registers] -r[dynamic.asm.registers.explicit] +r[asm.registers.explicit] An explicit register operand specifies the name of a valid operand register that is not a reserved register, or an alias name. Multiple explicit register operands shall not specify the same register or aliases of the same register. ```rust @@ -444,7 +444,7 @@ core::arch::asm!("mov eax, 5", out("eax") x); # }} ``` -r[dynamic.asm.registers.class] +r[asm.registers.class] A register operand that is not an explicit register operand specifies the name of a register class as an identifier. When a register class is specified, the implementation assigns an unspecified register belonging to that class to the operand. ```rust @@ -454,20 +454,20 @@ core::arch::asm!("mov {}, 5", out(reg) x); # }} ``` -r[dynamic.asm.registers.valid-types] +r[asm.registers.valid-types] Each register class, and the explicit registers within those classes, may restrict the set of types allowed for operands referring to that class or those registers. > [!NOTE] > When an integer type which is as wide as `isize` is valid for a given register class, `isize` is also considered valid, and the same for function pointers. > When a signed integer is considered valid for a given register class, the corresponding unsigned integer is also considered valid. -r[dynamic.asm.registers.target-feature] +r[asm.registers.target-feature] Each register class, and the explicit registers within that class may require that a specified target_feature is enabled in the ambient target_feature set, or by using the `target_feature` function attribute. The program shall not specify such registers or register classes, except as clobber output, when the feature is not enabled. Additionally specific types may be valid only if certain additional features are enabled. > [!NOTE] > The set of features in the ambient target_feature set are implementation-defined, and may be queried by [parse.macros.cfg]. -r[dynamic.asm.registers.class-list] +r[asm.registers.class-list] > [!TARGET-SPECIFIC] > The list of valid register classes, the constituent registers, the required target feature (if any), and the valid types for those classes are: @@ -514,7 +514,7 @@ r[dynamic.asm.registers.class-list] > - On x86-64 the high byte registers (e.g. `ah`) are not available in the `reg_byte` register class. > -r[dynamic.asm.register.clobbers_only] +r[asm.register.clobbers_only] Certain registers and register classes are *clobbers only*. Such register names or register classes shall not be specified by an operand, other than a clobbers output. > [!TARGET-SPECIFIC] @@ -530,7 +530,7 @@ core::arch::asm!("mov {}, 5", out("k0") x); # }} ``` -r[dynamic.asm.register.small-values] +r[asm.register.small-values] If a register input is specified with a type that has a smaller width than the register class according to the target, the remaining bits of the register are set to an unspecified value. > [!TARGET-SPECIFIC] @@ -546,7 +546,7 @@ println!("{y}"); # }} ``` -r[dynamic.asm.register.aliases] +r[asm.register.aliases] Certain explicit register names have defined aliases. These register names are considered identical to canonical register name and may be specified in place of the canonical name in an explicit register operand > [!TARGET-SPECIFIC] @@ -616,7 +616,7 @@ core::arch::asm!("mov eax, 5", out("rax") x); # }} ``` -r[dynamic.asm.register.reserved] +r[asm.register.reserved] Certain registers are reserved registers. Reserved Registers shall not be named by an explicit register operand. > [!NOTE] @@ -646,12 +646,12 @@ core::arch::asm!("mov rsp, 5", out("rsp") x); # }} ``` -## Template modifiers r[dynamic.asm.template] +## Template modifiers r[asm.template] -r[dynamic.asm.template.modifier] +r[asm.template.modifier] An operand spec that refers to a register operand may specify a modifier as part of the format specifier. -r[dynamic.asm.template.class] +r[asm.template.class] A format specifier shall only use a modifier that is supported for the register class specified by the register opernd. > [!TARGET-SPECIFIC] @@ -711,39 +711,39 @@ core::arch::asm!("mov {:e}, {:e}", out(reg) y, in(reg) x); > GCC will infer the modifier based on the operand value type, while we default to the full register size. > - on x86 `xmm_reg`: the `x`, `t` and `g` LLVM modifiers are not yet implemented in LLVM (they are supported by GCC only), but this should be a simple change. -r[dynamic.asm.template.diagnostic] +r[asm.template.diagnostic] A lint diagnostic should be emitted if a modifier is omitted, or a modifier is used such that the modified expanded register is of an inappropriate width for the type used to initialize the operand [llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers -## Behaviour of an asm block [dynamic.asm.evaluation] +## Behaviour of an asm block [asm.evaluation] -r[dynamic.asm.evaluation.general] +r[asm.evaluation.general] Each evaluation of an asm block (invocation of [`core::arch::asm!`]) shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. > [!NOTE] > The operation the asm block performs may differ between evaluations of the same asm block. > [!TARGET-SPECIFIC] -> The correspondance between the operation performed by the asm block is target-dependant and implementation-dependant, subject to the rules set in [dynamic.asm.operands]. +> The correspondance between the operation performed by the asm block is target-dependant and implementation-dependant, subject to the rules set in [asm.operands]. -r[dynamic.asm.evaluation.reg-values] -The value of each register mentioned in an input operand is set according to [dynamic.asm.operands] before evaluating any instructions in the asm block. The value of each other *operand-usable register* is unspecified. The value of all other registers is target-dependant. +r[asm.evaluation.reg-values] +The value of each register mentioned in an input operand is set according to [asm.operands] before evaluating any instructions in the asm block. The value of each other *operand-usable register* is unspecified. The value of all other registers is target-dependant. > [!NOTE] > The target may define that the register value (or some portion thereof) is undefined. -r[dynamic.asm.evaluation.constraints] +r[asm.evaluation.constraints] Certain constraints may be placed on the asm block, and on the requirements of the correspondance, by default or by an option explicitly specified on the asm block. The behaviour is undefined if any such constraint is violated. -r[dynamic.asm.evaluation.memory] +r[asm.evaluation.memory] The behaviour is undefined if the asm block accesses any allocation, or disables, freezes, or activates any tags, except via: * An access to a static item which is declared with the `#[no_mangle]` attribute, the `#[export_name]` attribute, or which is visible to an expression within the function in which the asm block is expanded, * A pointer tag which has been exposed, * A pointer tag which was passed as an input operand, or * A pointer tag which is accessible by reading any memory the asm block can read under this clause, recursively. -r[dynamic.asm.evaluation.unwind] +r[asm.evaluation.unwind] The behaviour is undefined if an inline assembly block exits by unwinding from a panic or a foreign exception. ```rust,ignore @@ -755,8 +755,7 @@ core::arch::asm!("call {}", sym panics); ``` - -r[dynamic.asm.evaluation.register-value] +r[asm.evaluation.register-value] The behaviour is undefined upon exiting an asm block unless the stack pointer register and each operand-usable register not mentioned by an `out` , `lateout`, `inout`, or `inlateout` operand has the value the register held upon entry to the asm block. > [!TARGET-SPECIFIC] @@ -765,12 +764,12 @@ The behaviour is undefined upon exiting an asm block unless the stack pointer re > * The Direction flag (`flags.DF`) is clear upon entry and must be clear upon exit > * The x87 Stack (that is the `TOP` field of the floating-point status word, and each bit in the floating-point tag word) must be preserved and restored upon exit. If all x87 `st` registers are marked as clobbered, the stack is guaranteed to be empty on entry to the asm block (that is, `TOP` is set to `0x7` and the `ftw` is set to `0xFFFF`). -## Options [dynamic.asm.options] +## Options [asm.options] -r[dynamic.asm.options.general] +r[asm.options.general] An options-spec provided in the asm invocation places constraints on the assembly block. -r[dynamic.asm.options.att_syntax] +r[asm.options.att_syntax] The `att_syntax` option may be specfied on the x86 and x86_64 target. The program shall not specify the `att_syntax` option on any other target. > [!TARGET-SPECIFIC] @@ -783,7 +782,7 @@ core::arch::asm!("mov {:e}, %eax", in(reg) 5, out("eax") x, options(att_syntax)) # }} ``` -r[dynamic.asm.options.nomem] +r[asm.options.nomem] The `nomem` option may be specified. The behaviour is undefined if the assembly block modifies any allocation, disables, freezes, or activates any tag, *synchronizes-with* any other thread of execution or signal handler, and the implementation may assume that the behaviour or outputs of the assembly block does not depend on the contents of any allocation. @@ -804,7 +803,7 @@ core::arch::asm!("mov {:e}, dword ptr [{}+rip]", out(reg) x, sym FOO, options(no # }} ``` -r[dynamic.asm.options.readonly] +r[asm.options.readonly] The `readonly` option may be specified. The behaviour is undefined if the assembly block modifies any allocation or activates any tag. ```rust,ignore @@ -815,7 +814,7 @@ core::arch::asm!("mov dword ptr [{}+rip], 3", sym FOO, options(readonly)); # }} ``` -r[dynamic.asm.options.exclusive] +r[asm.options.exclusive] The program shall not specify both the `nomem` and `readonly` options. ```rust,compile_fail @@ -824,7 +823,7 @@ core::arch::asm!("mov dword ptr [FOO+rip], 3", options(readonly, nomem)); # }} ``` -r[dynamic.asm.options.pure] +r[asm.options.pure] The `pure` option may be specfied. The evaluation of the assembly block shall not produce any observable behaviour, consume input, or terminate execution, and the implementation may assume that the outputs of the assembly block depends only on the inputs and the contents of any allocation. If the program specifies the `pure` option, it shall specify either the `nomem` or `readonly` option. ```rust,ignore @@ -836,7 +835,7 @@ core::arch::asm!("xor edi, edi","call exit@plt", options(pure, readonly)); ``` -r[dynamic.asm.options.nostack] +r[asm.options.nostack] The `nostack` option may be specified. The implementation may assume that the assembly block does not modify or access the stack, except an allocation placed in that region by the implementation. > [!TARGET-SPECIFIC] @@ -851,7 +850,7 @@ core::arch::asm!("push 5", "pop rax", out("eax") x, options(nostack)); # }} ``` -r[dynamic.asm.options.preserve_flags] +r[asm.options.preserve_flags] The `preserves_flags` option may be specified. The implementation may assume that the value of the status flags are preserved by the assembly block. > [!TARGET-SPECFIC] @@ -882,7 +881,7 @@ core::arch::asm!("cmp eax, eax", in("eax") 5, options(preserve_flags)); # }} ``` -r[dynamic.asm.options.noreturn] +r[asm.options.noreturn] The `noreturn` option may be specifed. An invocation of the [`core::arch::asm!`] macro that specifies the `noreturn` option expands to an expression of type `!`. The behaviour is undefined if an evaluation of the assembly block exits. The program shall not specify the `clobber_abi` specification, or an operand that is an `out`, `lateout`, `inout`, or `inlateout` operand. ```rust @@ -908,7 +907,7 @@ core::arch::asm!("xor edi, edi", "call exit@plt", out("edi") x, options(noreturn # }} ``` -r[dynamic.asm.options.global] +r[asm.options.global] A program shall not specify an option, other than the `att_syntax` option, in an invocation of the [`core::arch::global_asm!`] macro. ```rust,compile_fail @@ -916,9 +915,9 @@ A program shall not specify an option, other than the `att_syntax` option, in an core::arch::global_asm!("", options(noreturn)); ``` -## Directives Support [dynamic.asm.directives] +## Directives Support [asm.directives] -r[dynamic.asm.directives.gen] +r[asm.directives.gen] The common subset of the LLVM and GNU AS Assembly Syntax used for the *expanded asm-string* is guaranteed to support the following directives - `.2byte` - `.4byte` @@ -971,7 +970,7 @@ The common subset of the LLVM and GNU AS Assembly Syntax used for the *expanded > [!NOTE] > These directives are generally ones that solely emit sequences of bytes, or that modify the property of symbols. -r[dynamic.asm.directives.dwarf] +r[asm.directives.dwarf] > [!TARGET-SPECIFIC] > The following Directives are guaranteed to be supported on ELF Targets that use DWARF Debug Information and DWARF Unwind Tables @@ -997,7 +996,7 @@ r[dynamic.asm.directives.dwarf] > - `.cfi_undefined` > - `.cfi_window_save` -r[dynamic.asm.directives.seh] +r[asm.directives.seh] > [!TARGET-SPECIFIC] > The following directives are guaranteed to be supported on platforms that use Structured Exception Handling @@ -1010,7 +1009,7 @@ r[dynamic.asm.directives.seh] > - `.seh_stackalloc` -r[dynamic.asm.directives.x86] +r[asm.directives.x86] > [!TARGET-SPECIFIC] > The following directives are guaranteed to be supported on x86 and x86-64 platforms @@ -1021,7 +1020,7 @@ r[dynamic.asm.directives.x86] > Use of `.code16`, `.code32`, and `.code64` directives are only supported if the state is reset to the default before exiting the assembly block. > 32-bit x86 uses `.code32` by default, and x86_64 uses `.code64` by default. -r[dynamic.asm.directives.arm] +r[asm.directives.arm] > The following directives are guaranteed to be supported on 32-bit ARM platforms > - `.even` From a82d8f99d9fc2706c28fbda5f989431c16e3a75f Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 11 Jul 2024 13:41:55 -0400 Subject: [PATCH 12/30] Switch link for `__m128` and `__m256` Co-authored-by: Eric Huss --- src/inline-assembly.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index d1fee38d5..15f3f6ca8 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -296,7 +296,10 @@ core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); > [!TARGET-SPECIFIC] -> On x86 platforms, the types [`__m128`](core::arch::x86_64::__m128), [`__m256`](core::arch::x86_64::__m256), and variants of those types are *asm operand types*. +> On x86 platforms, the types [`__m128`], [`__m256`], and variants of those types are *asm operand types*. + +[`__m128`]: https://doc.rust-lang.org/core/arch/x86_64/struct.__m128.html +[`__m256`]: https://doc.rust-lang.org/core/arch/x86_64/struct.__m256.html ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ From 3563e5ada50db5ee85345306b114dc856a668c2b Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 16 Jul 2024 09:47:56 -0400 Subject: [PATCH 13/30] Remove colour from .target-specific --- theme/reference.css | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/theme/reference.css b/theme/reference.css index 1621b9af1..a6e86030e 100644 --- a/theme/reference.css +++ b/theme/reference.css @@ -54,11 +54,6 @@ main .warning p::before { color: #80d0d0; } -.coal main .target-specific p, -.navy main .target-specific p, -.ayu main .target-specific p { - background: #005f10; -} /* tags can be used to highlight specific character elements. */ kbd { @@ -199,21 +194,12 @@ blockquote > p { margin-left: 1em; margin-bottom: 1em; margin-right: 6vw; - background: #00f82e; padding: 0.2ex 0.5ex; border-radius: 5px; display: block; - box-shadow: inset -1px -1px 0 #06a430; font-size: 0.9em; } -.ayu .target-specific blockquote, -.navy .target-specific blockquote, -.coal .target-specific blockquote { - background: #008f2e; - box-shadow: inset -1px -1px 0 #03a430; -} - /* When the sidebar is visible, reduce the spacing of rules so that the content doesn't get shifted too far, and make the text too narrow. */ From 38d2dae617caf4234d3c844e18bf4797e83514d8 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Tue, 16 Jul 2024 09:56:41 -0400 Subject: [PATCH 14/30] Fix inline assembly tests to be handled "properly" off of x86_64 --- src/inline-assembly.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 15f3f6ca8..3c9ef37de 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -86,8 +86,8 @@ r[asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. ```rust -# #[cfg(target_arch = "x86_64")] pub fn main(){ + # #[cfg(target_arch = "x86_64")] unsafe{ core::arch::asm!("") } @@ -105,10 +105,14 @@ core::arch::global_asm!(".rodata", "FOO:", ".ascii \"Hello World\""); ``` ```rust,compile_fail -# #[cfg(target_arch = "x86_64")] pub fn main(){ +# #[cfg(target_arch = "x86_64")] +# { core::arch::global_asm!("FOO:", ".ascii \"Hello World\""); +# } } +# #[cfg(not(target_arch = "x86_64"))] +# core::compile_error!("asm tests are not yet available off of x86_64"); ``` r[asm.invocation.format-string] @@ -284,6 +288,7 @@ struct Foo{x: i32} let x: Foo; core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); # }} +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` ```rust,compile_fail @@ -292,6 +297,7 @@ core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); let x: *mut [i32]; core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); # }} +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` @@ -321,6 +327,7 @@ An `output_expr` shall be the placeholder expression `_` or a (potentially uniti let x: i32 = 0; core::arch::asm!("", out("eax") x); # }} +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` r[asm.operands.inout-expr] @@ -396,6 +403,7 @@ The program shall not use an operand, other than a sym operand, in the expansion ```rust,compile_fail,ignore # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("", in("eax") 5); +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` ```rust,ignore @@ -531,6 +539,7 @@ Certain registers and register classes are *clobbers only*. Such register names let x: i64; core::arch::asm!("mov {}, 5", out("k0") x); # }} +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` r[asm.register.small-values] @@ -647,6 +656,7 @@ Certain registers are reserved registers. Reserved Registers shall not be named # #[cfg(target_arch = "x86_64")] { unsafe{ core::arch::asm!("mov rsp, 5", out("rsp") x); # }} +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` ## Template modifiers r[asm.template] @@ -824,6 +834,7 @@ The program shall not specify both the `nomem` and `readonly` options. # #[cfg(target_arch = "x86_64")] { unsafe{ core::arch::asm!("mov dword ptr [FOO+rip], 3", options(readonly, nomem)); # }} +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` r[asm.options.pure] @@ -908,14 +919,17 @@ core::arch::asm!("", options(noreturn)); let x: i32; core::arch::asm!("xor edi, edi", "call exit@plt", out("edi") x, options(noreturn)); # }} +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` r[asm.options.global] A program shall not specify an option, other than the `att_syntax` option, in an invocation of the [`core::arch::global_asm!`] macro. -```rust,compile_fail +```rust,compile_fail,ignore # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("", options(noreturn)); + +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` ## Directives Support [asm.directives] From a464e1a35eb5073232aa36e06717f189bf30f0f9 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 17 Jul 2024 15:58:56 +0000 Subject: [PATCH 15/30] Apply suggestions from PR Review --- src/inline-assembly.md | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 3c9ef37de..0c8b329e0 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -86,7 +86,7 @@ r[asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. ```rust -pub fn main(){ +pub fn main() { # #[cfg(target_arch = "x86_64")] unsafe{ core::arch::asm!("") @@ -105,7 +105,7 @@ core::arch::global_asm!(".rodata", "FOO:", ".ascii \"Hello World\""); ``` ```rust,compile_fail -pub fn main(){ +pub fn main() { # #[cfg(target_arch = "x86_64")] # { core::arch::global_asm!("FOO:", ".ascii \"Hello World\""); @@ -116,7 +116,7 @@ pub fn main(){ ``` r[asm.invocation.format-string] -Each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_asm!`] macros shall be an expanded string literal for which the content matches the `asm_string_piece` production. +Unless the `raw` option is specified, each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_asm!`] macros shall be an expanded string literal for which the content matches the `asm_string_piece` production. > [!NOTE] > an expanded string literal is a string literal (after expanding macros like [`core::concat!`]) that has had every unicode escape sequence replaced with the (appropriately escaped as needed) matching character, and which has been normalized from a raw string literal. @@ -141,6 +141,8 @@ Each operand, other than an explicit register operand ([asm.operands.register]) # #[cfg(target_arch = "x86_64")] { unsafe{ core::arch::asm!("", in(reg) 5i64); # }} +# #[cfg(not(target_arch = "x86_64"))] +# core::compile_error!("asm tests are not yet available off of x86_64"); ``` r[asm.invocation.positional] @@ -174,14 +176,14 @@ core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, output = out(reg ``` r[asm.invocation.expansion] -The *joined asm-string* is expanded as defined in [asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. +If the `raw` option is not specified, the *joined asm-string* is expanded as defined in [asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. If the `raw` option is specified, the *expanded asm-string* is the *joined asm-string* verbatim. r[asm.invocation.syntax] The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the target. Invoking the macro with a *expanded asm-string* that does not match syntax requirements is *conditionally supported* and has *assembler dependent behaviour*. Invoking a directive that is not specified by [asm.directives] is *conditionally supported* and has *assembler dependent behaviour*. > [!TARGET-SPECIFIC] > On x86 and x86_64 targets, the syntax of the *expanded asm-string* acts as though the directive `.intel_syntax noprefix` is issued before parsing the *expanded asm-string*, except that the `option(att_syntax)` causes the syntax to act as though the directive `.att_syntax prefix` is issued before parsing the *expanded asm-string* instead. -> On ARM and Aarch64 targets, the syntax of the *expanded asm-string* acts as though the directive `.syntax unified` is issued before parsing the *expanded asm-string*. +> On ARM targets, the syntax of the *expanded asm-string* acts as though the directive `.syntax unified` is issued before parsing the *expanded asm-string*. r[asm.invocation.duplication] The number of times, locations, and the order in which a given invocation of [`core::arch::asm!`] is expanded is unspecified. @@ -201,7 +203,7 @@ core::arch::asm!("foo: jmp foo"); > Additionally, two asm blocks may not rely upon being adjacent in executable memory, even if they are adjacent in the source. > [!NOTE] -> Local Labels (a decimal literal that doesn't solely consist of 0s and 1s) may be used freely if the asm block needs to define a label. +> Local Labels (a decimal literal) may be used freely if the asm block needs to define a label. Due to a bug, literals that solely consist of 1s and 0s are not valid local labels. > See [The GNU AS Manual on Local Labels](https://sourceware.org/binutils/docs/as/Symbol-Names.html) for details on local labels. > It is not guaranteed that a local label defined in one asm block will be accessible from an adjacent asm block. @@ -317,7 +319,7 @@ core::arch::asm!("xorps xmm0, xmm0", out("xmm0") x); ``` r[asm.operands.in-expr] -An `input_expr` shall be a value expression of an *asm operand type*. +An `input_expr` shall be a value expression that coerces to an *asm operand type*. r[asm.operands.out-expr] An `output_expr` shall be the placeholder expression `_` or a (potentially unitialized) place expression of an *asm operand type*. If the place expression is initialized, it shall be a mutable place. @@ -353,7 +355,7 @@ An `inout` operand is a reg_operand with the `inout` dir_spec, and a `inlateout` r[asm.operands.clobbers] -An `output_expr` that is the placeholder expression `_` is a clobber output. The resulting value of the register is discarded. +An `output_expr` that is the placeholder expression `_` is a clobbers output. The resulting value of the register is discarded. An `out` operand that is a clobbers output shall be an *explicit register operand*. > [!NOTE] > Some registers and register classes cannot be used as an operand, other than as a clobber operand. @@ -413,7 +415,7 @@ core::arch::global_asm!("/*{}*/", sym FOO); ``` r[asm.operands.clobbers_abi] -A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, the no reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. +A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, the no `out`, `lateout`, `inout`, or `inlateout` reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. > [!NOTE] @@ -441,7 +443,7 @@ core::arch::asm!("", clobber_abi("C")); ``` r[asm.operands.clobbers_abi_ref] -A `clobbers_abi` special operand shall be specified after each positional operand, and shall not be a named operand. A `clobbers_abi` special operand cannot be referred to by an operand_specifier +A `clobbers_abi` special operand shall be specified after all positional operands, and shall not be a named operand. A `clobbers_abi` special operand cannot be referred to by an operand_specifier ## Register operands [asm.registers] @@ -469,7 +471,7 @@ r[asm.registers.valid-types] Each register class, and the explicit registers within those classes, may restrict the set of types allowed for operands referring to that class or those registers. > [!NOTE] -> When an integer type which is as wide as `isize` is valid for a given register class, `isize` is also considered valid, and the same for function pointers. +> The types `isize`, `usize`, and function pointer types are considered valid for a given register class if and only if an integer type of the same width is considered valid. > When a signed integer is considered valid for a given register class, the corresponding unsigned integer is also considered valid. r[asm.registers.target-feature] @@ -732,13 +734,13 @@ A lint diagnostic should be emitted if a modifier is omitted, or a modifier is u ## Behaviour of an asm block [asm.evaluation] r[asm.evaluation.general] -Each evaluation of an asm block (invocation of [`core::arch::asm!`]) shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. +Each evaluation of an asm block (invocation of [`core::arch::asm!`]) shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. The behaviour is undefined if the operations performed by the asm block do not validly correspond to a valid sequence of Minirust operations. > [!NOTE] > The operation the asm block performs may differ between evaluations of the same asm block. > [!TARGET-SPECIFIC] -> The correspondance between the operation performed by the asm block is target-dependant and implementation-dependant, subject to the rules set in [asm.operands]. +> The correspondance between the operation performed by the asm block is target-dependant and implementation-dependant, subject to the rules set in [asm.operands]. Unless the program modifies the execution state, the basic operation performed by the asm block is the one performed by executing the sequence of instructions specified in the *expanded asm-string* starting with the first instruction. r[asm.evaluation.reg-values] The value of each register mentioned in an input operand is set according to [asm.operands] before evaluating any instructions in the asm block. The value of each other *operand-usable register* is unspecified. The value of all other registers is target-dependant. @@ -795,6 +797,13 @@ core::arch::asm!("mov {:e}, %eax", in(reg) 5, out("eax") x, options(att_syntax)) # }} ``` +r[asm.options.raw] +The `raw` option may be specified. If the `raw` option is specified, the asm block shall not have any operands, other than explicit register operands, and the `clobbers_abi` special operand. + +> [!NOTE] +> The `raw` option causes the *joined asm-string* to be handled verbatim without being interpreted as a format string and expanded. + + r[asm.options.nomem] The `nomem` option may be specified. The behaviour is undefined if the assembly block modifies any allocation, disables, freezes, or activates any tag, *synchronizes-with* any other thread of execution or signal handler, and the implementation may assume that the behaviour or outputs of the assembly block does not depend on the contents of any allocation. @@ -923,7 +932,7 @@ core::arch::asm!("xor edi, edi", "call exit@plt", out("edi") x, options(noreturn ``` r[asm.options.global] -A program shall not specify an option, other than the `att_syntax` option, in an invocation of the [`core::arch::global_asm!`] macro. +A program shall not specify an option, other than the `att_syntax` or `raw` options, in an invocation of the [`core::arch::global_asm!`] macro. ```rust,compile_fail,ignore # #[cfg(target_arch = "x86_64")] From f69509b1074c140046b72fd805105f7af846e8e1 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 17 Jul 2024 16:44:48 -0400 Subject: [PATCH 16/30] Apply suggestions from code review Co-authored-by: Amanieu d'Antras --- src/inline-assembly.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 0c8b329e0..783335899 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -415,7 +415,7 @@ core::arch::global_asm!("/*{}*/", sym FOO); ``` r[asm.operands.clobbers_abi] -A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, the no `out`, `lateout`, `inout`, or `inlateout` reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. +A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, then no `out`, `lateout`, `inout`, or `inlateout` reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. > [!NOTE] @@ -448,7 +448,7 @@ A `clobbers_abi` special operand shall be specified after all positional operand ## Register operands [asm.registers] r[asm.registers.explicit] -An explicit register operand specifies the name of a valid operand register that is not a reserved register, or an alias name. Multiple explicit register operands shall not specify the same register or aliases of the same register. +An explicit register operand specifies the name of a valid operand register that is not a reserved register, or an alias name of a valid operand register. Multiple explicit register operands shall not specify the same register or aliases of the same register. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ From 15db17f2499ff1dbbf6a6dd2bc974bfa5438cc06 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 17 Jul 2024 20:58:02 +0000 Subject: [PATCH 17/30] Finish applying suggestions from PR review --- src/inline-assembly.md | 60 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 783335899..6514c2f65 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -1,4 +1,6 @@ -# Inline assembly [asm] +# Inline assembly + +r[asm] r[asm.macros] The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] expand to inline assembly syntax when used in the expression position and item position respectively. The macros shall not be expanded in any other context. @@ -50,7 +52,9 @@ assert_eq!(x, 4 * 6); # } ``` -## Syntax [asm.syntax] +## Syntax + +r[asm.syntax] ```abnf format_string := STRING_LITERAL / RAW_STRING_LITERAL @@ -80,7 +84,9 @@ asm_string_piece := non_format_char / format_specifier / format_escape asm_string_content := [*asm_string_piece] ``` -## Invocation [asm.invocation] +## Invocation + +r[asm.invocation] r[asm.invocation.asm] The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. @@ -245,7 +251,9 @@ core::arch::asm!("lock"); # }} ``` -## Operand types [asm.operands] +## Operand types + +r[asm.operands] r[asm.operands.positional] Operands that do not specify an ident and are not explicit register operands are known as positional operands. Positional operands may be referred to only by positional operand specifiers and explicit positional operand specifiers, and each Positional operand must be specified before Named Operands or Explicit Register Operands. @@ -281,7 +289,7 @@ core::arch::asm!("mov eax, ecx", in("rcx") 5i64, out("eax") x); > Explicit Register Operands have no `ident` name and cannot be referred to by an operand specifier r[asm.operands.types] -Each operand, other than a placeholder expression shall be of an integer type, floating-point type, function pointer type, pointer type, or target-specific vector type. These types are collectively called *asm operand types*. A pointer type is an *asm operand type* only if the pointee type has no metadata-type. +Each operand, other than a placeholder expression shall be of an integer type, floating-point type, function pointer type, pointer type, target-specific vector type, or [`MaybeUninit`][core::mem::MaybeUninit] where `T` is an *asm operand type* other than [`MaybeUninit`][core::mem::MaybeUninit]. These types are collectively called *asm operand types*. A pointer type is an *asm operand type* only if the pointee type has no metadata-type. ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { unsafe{ @@ -318,8 +326,28 @@ core::arch::asm!("xorps xmm0, xmm0", out("xmm0") x); # }} ``` +r[asm.operands.input-coerceable-types] +Each reference type, where the pointee type has no metadata-type, and each function item type are collectively called *input coerceable types*. + +```rust +# #[cfg(target_arch = "x86_64")] { unsafe{ +let x = 5; +let y: i32; +core::arch::asm!("mov eax, dword ptr [{}]", in(reg) &x, out("eax") y); // equivalent to asm!("mov eax, dword ptr [{}]", in(reg) (&x) as *const i32, out("eax") y); +#}} +``` + +```rust,compile_fail +# #[cfg(target_arch = "x86_64")] { unsafe{ +let y: &mut i32; +core::arch::asm!("mov {}, 0", out(reg) 5); +#}} +# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +``` + + r[asm.operands.in-expr] -An `input_expr` shall be a value expression that coerces to an *asm operand type*. +An `input_expr` shall be a value expression of an *asm operand type* or an *input coerceable type*. If the expression is of an *input coerceable type*, it is coerced to an *asm operand type*. r[asm.operands.out-expr] An `output_expr` shall be the placeholder expression `_` or a (potentially unitialized) place expression of an *asm operand type*. If the place expression is initialized, it shall be a mutable place. @@ -445,7 +473,9 @@ core::arch::asm!("", clobber_abi("C")); r[asm.operands.clobbers_abi_ref] A `clobbers_abi` special operand shall be specified after all positional operands, and shall not be a named operand. A `clobbers_abi` special operand cannot be referred to by an operand_specifier -## Register operands [asm.registers] +## Register operands + +r[asm.registers] r[asm.registers.explicit] An explicit register operand specifies the name of a valid operand register that is not a reserved register, or an alias name of a valid operand register. Multiple explicit register operands shall not specify the same register or aliases of the same register. @@ -661,7 +691,9 @@ core::arch::asm!("mov rsp, 5", out("rsp") x); # #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` -## Template modifiers r[asm.template] +## Template modifiers r + +r[asm.template] r[asm.template.modifier] An operand spec that refers to a register operand may specify a modifier as part of the format specifier. @@ -731,7 +763,9 @@ A lint diagnostic should be emitted if a modifier is omitted, or a modifier is u [llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers -## Behaviour of an asm block [asm.evaluation] +## Behaviour of an asm block + +r[asm.evaluation] r[asm.evaluation.general] Each evaluation of an asm block (invocation of [`core::arch::asm!`]) shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. The behaviour is undefined if the operations performed by the asm block do not validly correspond to a valid sequence of Minirust operations. @@ -779,7 +813,9 @@ The behaviour is undefined upon exiting an asm block unless the stack pointer re > * The Direction flag (`flags.DF`) is clear upon entry and must be clear upon exit > * The x87 Stack (that is the `TOP` field of the floating-point status word, and each bit in the floating-point tag word) must be preserved and restored upon exit. If all x87 `st` registers are marked as clobbered, the stack is guaranteed to be empty on entry to the asm block (that is, `TOP` is set to `0x7` and the `ftw` is set to `0xFFFF`). -## Options [asm.options] +## Options + +r[asm.options] r[asm.options.general] An options-spec provided in the asm invocation places constraints on the assembly block. @@ -941,7 +977,9 @@ core::arch::global_asm!("", options(noreturn)); # #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` -## Directives Support [asm.directives] +## Directives Support + +r[asm.directives] r[asm.directives.gen] The common subset of the LLVM and GNU AS Assembly Syntax used for the *expanded asm-string* is guaranteed to support the following directives From b3627786f84e0ef07b193dd4ef052806b9095c62 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 16:20:05 +0000 Subject: [PATCH 18/30] Change wording for asm.safety note --- src/inline-assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 6514c2f65..b9e614a6b 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -13,7 +13,7 @@ The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. > [!NOTE] > Inline assembly is inherently unsafe. -> It requires asserting various constraints to the compiler that it cannot check, and can perform operations equivalent to calling a foreign function. +> It requires asserting that various constraints are satisfied that the compiler cannot check, and can perform operations equivalent to calling a foreign function. ```rust,compile_fail From 363303484b31d3a2cdd0a063ef7c8bf748e7fdae Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 12:20:49 -0400 Subject: [PATCH 19/30] Use `that` instead of `which` in asm.safety note --- src/inline-assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index b9e614a6b..07e23e39d 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -13,7 +13,7 @@ The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. > [!NOTE] > Inline assembly is inherently unsafe. -> It requires asserting that various constraints are satisfied that the compiler cannot check, and can perform operations equivalent to calling a foreign function. +> It requires asserting that various constraints are satisfied, which the compiler cannot check, and can perform operations equivalent to calling a foreign function. ```rust,compile_fail From 7a320d3ebcf4cbd1a6d0227f9c5c7ce794fe42ef Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 12:26:00 -0400 Subject: [PATCH 20/30] Fix global_asm tests to actually run --- src/inline-assembly.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 07e23e39d..1b6b19364 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -105,9 +105,11 @@ The [`core::arch::global_asm!`] macro shall be expanded in an item context only. -```rust,ignore +```rust # #[cfg(target_arch = "x86_64")] core::arch::global_asm!(".rodata", "FOO:", ".ascii \"Hello World\""); + +# fn main(){} ``` ```rust,compile_fail @@ -334,14 +336,14 @@ Each reference type, where the pointee type has no metadata-type, and each funct let x = 5; let y: i32; core::arch::asm!("mov eax, dword ptr [{}]", in(reg) &x, out("eax") y); // equivalent to asm!("mov eax, dword ptr [{}]", in(reg) (&x) as *const i32, out("eax") y); -#}} +# }} ``` ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { unsafe{ let y: &mut i32; core::arch::asm!("mov {}, 0", out(reg) 5); -#}} +# }} # #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` @@ -430,16 +432,20 @@ The program shall not use an operand, other than a sym operand, in the expansion -```rust,compile_fail,ignore +```rust,compile_fail # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("", in("eax") 5); # #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); + +# fn main(){} ``` -```rust,ignore +```rust static FOO: () = (); # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("/*{}*/", sym FOO); + +# fn main(){} ``` r[asm.operands.clobbers_abi] @@ -799,7 +805,7 @@ The behaviour is undefined if an inline assembly block exits by unwinding from a // The following snippet has undefined behaviour extern "C-unwind" fn panics(){panic!("unwind through asm")} # #[cfg(target_arch = "x86_64")] { unsafe{ -core::arch::asm!("call {}", sym panics); +core::arch::asm!("call {}", sym panics, clobber_abi("C")); # }} ``` @@ -970,11 +976,13 @@ core::arch::asm!("xor edi, edi", "call exit@plt", out("edi") x, options(noreturn r[asm.options.global] A program shall not specify an option, other than the `att_syntax` or `raw` options, in an invocation of the [`core::arch::global_asm!`] macro. -```rust,compile_fail,ignore +```rust,compile_fail # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("", options(noreturn)); # #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); + +# fn main(){} ``` ## Directives Support From 012d7f68a05ceab15180b6a6e0b3e2e5dc61ac2a Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 12:53:55 -0400 Subject: [PATCH 21/30] Use *asm block* and *global asm block* instead of "Expansion of [`core::arch::asm!`]" --- src/inline-assembly.md | 54 +++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 1b6b19364..939e10b2b 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -3,7 +3,7 @@ r[asm] r[asm.macros] -The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] expand to inline assembly syntax when used in the expression position and item position respectively. The macros shall not be expanded in any other context. +The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] are defined to supply inline assembly to a Rust program. They expand to *exposition-only* syntax defined > [!NOTE] > The expansion of the macros has no stable syntax equivalent. This section will refer to the expansion of the macro, rather than the surface syntax. @@ -82,6 +82,9 @@ format_specifier := "{" [operand_specifier] [":" *expansion_specifier] "}" format_escape := "{{" / "}}" asm_string_piece := non_format_char / format_specifier / format_escape asm_string_content := [*asm_string_piece] + +exposition-only_asm = /*asm-block*/ "(" asm_inner ")" // Exposition Only +exposition-only_global_asm = /*global-asm*/ "(" asm_inner ")" // Exposition Only ``` ## Invocation @@ -89,7 +92,10 @@ asm_string_content := [*asm_string_piece] r[asm.invocation] r[asm.invocation.asm] -The [`core::arch::asm!`] macro shall be expanded in an expression context only. The input tokens shall match the `asm_inner` production. The expansion is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. +The [`core::arch::asm!`] macro expands to an `/*asm-block*/` when expanded in an expression context. The input tokens shall match the `asm_inner` production, and the expansion contains the same `asm_inner`. The `/*asm-block*/` is [`unsafe`][static.expr.safety] and has type `()`, unless the option `noreturn` is specified, in which case it has type `!`. The [`core::arch::asm!`] macro shall not be expanded in any other context. The expansion of the [`core::arch::asm!`] macro is referred to as an *asm-block*. + +> [!NOTE] +> `/*asm-block*/` is an exposition-only construct used to define the expansion of the [`core::arch::asm!`] macro. It has no equivalent in stable rule syntax except for the [`core::arch::asm!`] macro. ```rust pub fn main() { @@ -101,9 +107,7 @@ pub fn main() { ``` r[asm.invocation.global_asm] -The [`core::arch::global_asm!`] macro shall be expanded in an item context only. The input tokens shall match the `asm_inner` production. If the macro is expanded in a function, the program is ill-formed. - - +The [`core::arch::global_asm!`] macro expands to a `/*global-asm*/` when expanded in an item context. The input tokens shall match the `asm_inner` production, and the expansion contains the same `asm_inner`. The [`core::arch::global_asm!`] macro shall not be expanded in any other context. The expansion of the [`core::arch::global_asm!`] block is referred to as a *global-asm-block*. ```rust # #[cfg(target_arch = "x86_64")] @@ -194,7 +198,7 @@ The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the > On ARM targets, the syntax of the *expanded asm-string* acts as though the directive `.syntax unified` is issued before parsing the *expanded asm-string*. r[asm.invocation.duplication] -The number of times, locations, and the order in which a given invocation of [`core::arch::asm!`] is expanded is unspecified. +The number of times, locations, and the order in which a given *asm block* appears in the binary is unspecified. ```rust,ignore // The following code may have suprising results, and may fail to compile or link. @@ -207,7 +211,7 @@ core::arch::asm!("foo: jmp foo"); > [!NOTE] > In particular, an asm block may be duplicated, for example if the containing function is inlined, or omitted from the output entirely. -> As a consequence, asm blocks should not use directives that have non-idempotent non-local effects, or named labels and symbol definitions. +> As a consequence, asm blocks should not use directives that have non-idempotent non-local effects, or named labels and symbol definitions, and should not rely on the non-local effects of other asm blocks. > Additionally, two asm blocks may not rely upon being adjacent in executable memory, even if they are adjacent in the source. > [!NOTE] @@ -223,7 +227,7 @@ core::arch::asm!("2: jmp 2b"); ``` r[asm.invocation.global-order] -The order in which invocations of [`core::arch::global_asm!`] are expanded is unspecified. +The order in which each *global-asm-block* appears in the program is unspecified. r[asm.invocation.directive-state] The *expanded asm-string* shall not issue a directive that modifies the global state of the assembler for processing inputs unless it issues a directive to restore that state it had upon entering the block. No diagnostic is required. @@ -233,7 +237,7 @@ The *expanded asm-string* shall not issue a directive that modifies the global s > Failing to obey this requirement can have significant impact on code generation, including code unrelated to the asm block. For example, an asm block that issues a `.data` directive without resetting to the appropriate section for the function can cause the following code in the function to be generated in the `.data` section, and for execution to fall off the asm block into improper memory. r[asm.invocation.global-section] -The *expanded asm-string* of a [`core::arch::global_asm!`] invocation acts as though a `.section` directive is issued before the *expanded asm-string* which causes code to be generated in the default section on the target for executable code. +The *expanded asm-string* of *global-asm-block* invocation acts as though a `.section` directive is issued before the *expanded asm-string* which causes code to be generated in the default section on the target for executable code. > [!NOTE] > This section is typically named `.text`. @@ -774,13 +778,13 @@ A lint diagnostic should be emitted if a modifier is omitted, or a modifier is u r[asm.evaluation] r[asm.evaluation.general] -Each evaluation of an asm block (invocation of [`core::arch::asm!`]) shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. The behaviour is undefined if the operations performed by the asm block do not validly correspond to a valid sequence of Minirust operations. +Each evaluation of an *asm block* shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. The behaviour is undefined if the operations performed by the asm block do not validly correspond to a valid sequence of Minirust operations. > [!NOTE] > The operation the asm block performs may differ between evaluations of the same asm block. > [!TARGET-SPECIFIC] -> The correspondance between the operation performed by the asm block is target-dependant and implementation-dependant, subject to the rules set in [asm.operands]. Unless the program modifies the execution state, the basic operation performed by the asm block is the one performed by executing the sequence of instructions specified in the *expanded asm-string* starting with the first instruction. +> The correspondance between the operation performed by the *asm block* is target-dependant and implementation-dependant, subject to the rules set in [asm.operands]. Unless the program modifies the execution state, the basic operation performed by the asm block is the one performed by executing the sequence of instructions specified in the *expanded asm-string* starting with the first instruction. r[asm.evaluation.reg-values] The value of each register mentioned in an input operand is set according to [asm.operands] before evaluating any instructions in the asm block. The value of each other *operand-usable register* is unspecified. The value of all other registers is target-dependant. @@ -789,17 +793,17 @@ The value of each register mentioned in an input operand is set according to [as > The target may define that the register value (or some portion thereof) is undefined. r[asm.evaluation.constraints] -Certain constraints may be placed on the asm block, and on the requirements of the correspondance, by default or by an option explicitly specified on the asm block. The behaviour is undefined if any such constraint is violated. +Certain constraints may be placed on the *asm block*, and on the requirements of the correspondance, by default or by an option explicitly specified on the asm block. The behaviour is undefined if any such constraint is violated, or the correspondance does not hold. r[asm.evaluation.memory] -The behaviour is undefined if the asm block accesses any allocation, or disables, freezes, or activates any tags, except via: +The behaviour is undefined if the *asm block* accesses any allocation, disables, freezes, activates any tags, or *synchronizes-with* a store to a given memory location, except via: * An access to a static item which is declared with the `#[no_mangle]` attribute, the `#[export_name]` attribute, or which is visible to an expression within the function in which the asm block is expanded, * A pointer tag which has been exposed, * A pointer tag which was passed as an input operand, or * A pointer tag which is accessible by reading any memory the asm block can read under this clause, recursively. r[asm.evaluation.unwind] -The behaviour is undefined if an inline assembly block exits by unwinding from a panic or a foreign exception. +The behaviour is undefined if an *asm block* exits by unwinding from a panic or a foreign exception. ```rust,ignore // The following snippet has undefined behaviour @@ -811,7 +815,7 @@ core::arch::asm!("call {}", sym panics, clobber_abi("C")); r[asm.evaluation.register-value] -The behaviour is undefined upon exiting an asm block unless the stack pointer register and each operand-usable register not mentioned by an `out` , `lateout`, `inout`, or `inlateout` operand has the value the register held upon entry to the asm block. +The behaviour is undefined upon exiting an *asm block* unless the stack pointer register and each operand-usable register not mentioned by an `out` , `lateout`, `inout`, or `inlateout` operand has the value the register held upon entry to the *asm block*. > [!TARGET-SPECIFIC] > In addition to operand-usable registers, certain other registers on a target may require being preserved, or have specific rules regarding the value at exit. @@ -824,7 +828,7 @@ The behaviour is undefined upon exiting an asm block unless the stack pointer re r[asm.options] r[asm.options.general] -An options-spec provided in the asm invocation places constraints on the assembly block. +An options-spec provided in the *asm block* places constraints on the *asm block*. r[asm.options.att_syntax] The `att_syntax` option may be specfied on the x86 and x86_64 target. The program shall not specify the `att_syntax` option on any other target. @@ -840,14 +844,14 @@ core::arch::asm!("mov {:e}, %eax", in(reg) 5, out("eax") x, options(att_syntax)) ``` r[asm.options.raw] -The `raw` option may be specified. If the `raw` option is specified, the asm block shall not have any operands, other than explicit register operands, and the `clobbers_abi` special operand. +The `raw` option may be specified. If the `raw` option is specified, the *asm block* or *global asm block* shall not have any operands, other than explicit register operands, and the `clobbers_abi` special operand. > [!NOTE] > The `raw` option causes the *joined asm-string* to be handled verbatim without being interpreted as a format string and expanded. r[asm.options.nomem] -The `nomem` option may be specified. The behaviour is undefined if the assembly block modifies any allocation, disables, freezes, or activates any tag, *synchronizes-with* any other thread of execution or signal handler, and the implementation may assume that the behaviour or outputs of the assembly block does not depend on the contents of any allocation. +The `nomem` option may be specified. The behaviour is undefined if the *asm block* modifies any allocation, disables, freezes, or activates any tag, *synchronizes-with* any other thread of execution or signal handler, and the implementation may assume that the behaviour or outputs of the *asm block* does not depend on the contents of any allocation. ```rust,ignore @@ -868,7 +872,7 @@ core::arch::asm!("mov {:e}, dword ptr [{}+rip]", out(reg) x, sym FOO, options(no ``` r[asm.options.readonly] -The `readonly` option may be specified. The behaviour is undefined if the assembly block modifies any allocation or activates any tag. +The `readonly` option may be specified. The behaviour is undefined if the *asm block* modifies any allocation or activates any tag. ```rust,ignore // The following snippet has undefined behaviour @@ -879,7 +883,7 @@ core::arch::asm!("mov dword ptr [{}+rip], 3", sym FOO, options(readonly)); ``` r[asm.options.exclusive] -The program shall not specify both the `nomem` and `readonly` options. +An *asm block* shall not specify both the `nomem` and `readonly` options. ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { unsafe{ @@ -889,7 +893,7 @@ core::arch::asm!("mov dword ptr [FOO+rip], 3", options(readonly, nomem)); ``` r[asm.options.pure] -The `pure` option may be specfied. The evaluation of the assembly block shall not produce any observable behaviour, consume input, or terminate execution, and the implementation may assume that the outputs of the assembly block depends only on the inputs and the contents of any allocation. If the program specifies the `pure` option, it shall specify either the `nomem` or `readonly` option. +The `pure` option may be specfied. The evaluation of the *asm* shall not produce any observable behaviour, consume input, or terminate execution, and the implementation may assume that the outputs of the *asm block* depends only on the inputs and the contents of any allocation. If the program specifies the `pure` option, it shall specify either the `nomem` or `readonly` option. ```rust,ignore // The following snippet has undefined behaviour @@ -901,7 +905,7 @@ core::arch::asm!("xor edi, edi","call exit@plt", options(pure, readonly)); r[asm.options.nostack] -The `nostack` option may be specified. The implementation may assume that the assembly block does not modify or access the stack, except an allocation placed in that region by the implementation. +The `nostack` option may be specified. The implementation may assume that the *asm block* does not modify or access the stack, except an allocation placed in that region by the implementation. > [!TARGET-SPECIFIC] > The stack is defined by an target-specific register and is a target-specific memory region. It may include a "red zone". @@ -916,7 +920,7 @@ core::arch::asm!("push 5", "pop rax", out("eax") x, options(nostack)); ``` r[asm.options.preserve_flags] -The `preserves_flags` option may be specified. The implementation may assume that the value of the status flags are preserved by the assembly block. +The `preserves_flags` option may be specified. The implementation may assume that the value of the status flags are preserved by the *asm block*. > [!TARGET-SPECFIC] > - These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set: @@ -947,7 +951,7 @@ core::arch::asm!("cmp eax, eax", in("eax") 5, options(preserve_flags)); ``` r[asm.options.noreturn] -The `noreturn` option may be specifed. An invocation of the [`core::arch::asm!`] macro that specifies the `noreturn` option expands to an expression of type `!`. The behaviour is undefined if an evaluation of the assembly block exits. The program shall not specify the `clobber_abi` specification, or an operand that is an `out`, `lateout`, `inout`, or `inlateout` operand. +The `noreturn` option may be specifed. An *asm block* that specifies the `noreturn` option is an expression of type `!`. The behaviour is undefined if an evaluation of the *asm block* exits. The program shall not specify the `clobber_abi` specification, or an operand that is an `out`, `lateout`, `inout`, or `inlateout` operand. ```rust # #[cfg(target_arch = "x86_64")] @@ -974,7 +978,7 @@ core::arch::asm!("xor edi, edi", "call exit@plt", out("edi") x, options(noreturn ``` r[asm.options.global] -A program shall not specify an option, other than the `att_syntax` or `raw` options, in an invocation of the [`core::arch::global_asm!`] macro. +A program shall not specify an option, other than the `att_syntax` or `raw` options, in a *global asm block*. ```rust,compile_fail # #[cfg(target_arch = "x86_64")] From f886a47e627d4937d472a7bfd7d54cb44ed84a78 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 16:55:07 +0000 Subject: [PATCH 22/30] Fix extraneous `r` in "Template Modifiers" header --- src/inline-assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 939e10b2b..a956d8b98 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -701,7 +701,7 @@ core::arch::asm!("mov rsp, 5", out("rsp") x); # #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` -## Template modifiers r +## Template modifiers r[asm.template] From b6f4bbb80f55e5a7feed63c71da3ddc6b6a6bfa1 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 15:37:09 -0400 Subject: [PATCH 23/30] Apply suggestions from code review Co-authored-by: Eric Huss --- src/inline-assembly.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index a956d8b98..f55701699 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -131,7 +131,7 @@ r[asm.invocation.format-string] Unless the `raw` option is specified, each `format_string` input to the [`core::arch::asm!`] and [`core::arch::global_asm!`] macros shall be an expanded string literal for which the content matches the `asm_string_piece` production. > [!NOTE] -> an expanded string literal is a string literal (after expanding macros like [`core::concat!`]) that has had every unicode escape sequence replaced with the (appropriately escaped as needed) matching character, and which has been normalized from a raw string literal. +> An expanded string literal is a string literal (after expanding macros like [`core::concat!`]) that has had every unicode escape sequence replaced with the (appropriately escaped as needed) matching character, and which has been normalized from a raw string literal. r[asm.invocation.concat] @@ -191,7 +191,7 @@ r[asm.invocation.expansion] If the `raw` option is not specified, the *joined asm-string* is expanded as defined in [asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. If the `raw` option is specified, the *expanded asm-string* is the *joined asm-string* verbatim. r[asm.invocation.syntax] -The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the target. Invoking the macro with a *expanded asm-string* that does not match syntax requirements is *conditionally supported* and has *assembler dependent behaviour*. Invoking a directive that is not specified by [asm.directives] is *conditionally supported* and has *assembler dependent behaviour*. +The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the target. Invoking the macro with a *expanded asm-string* that does not match syntax requirements is *conditionally supported* and has *assembler dependent behavior*. Invoking a directive that is not specified by [asm.directives] is *conditionally supported* and has *assembler dependent behavior*. > [!TARGET-SPECIFIC] > On x86 and x86_64 targets, the syntax of the *expanded asm-string* acts as though the directive `.intel_syntax noprefix` is issued before parsing the *expanded asm-string*, except that the `option(att_syntax)` causes the syntax to act as though the directive `.att_syntax prefix` is issued before parsing the *expanded asm-string* instead. @@ -201,8 +201,9 @@ r[asm.invocation.duplication] The number of times, locations, and the order in which a given *asm block* appears in the binary is unspecified. ```rust,ignore -// The following code may have suprising results, and may fail to compile or link. -// The results, including whether it succesfully compiles, may depend on non-local use sites of the function, and on optimization settings. +// The following code may have surprising results, and may fail to compile or +// link. The results, including whether it successfully compiles, may depend on +// non-local use sites of the function, and on optimization settings. # #[cfg(target_arch = "x86_64")] { unsafe{ let x: i32; core::arch::asm!("foo: jmp foo"); @@ -244,7 +245,7 @@ The *expanded asm-string* of *global-asm-block* invocation acts as though a `.se r[asm.invocation.prefix-instr] -An *expanded asm-string* shall not end with an instruction that is interpreted as a prefix on the architecture. No Diagnostic is required +An *expanded asm-string* shall not end with an instruction that is interpreted as a prefix on the architecture. No diagnostic is required. > [!TARGET-SPECIFIC] > On x86 and x86-64, the `lock`, `repnz`, `rep`, `repz`, as well as GNU AS specific address-size, data-size, and explicit rex, vex, and evex prefixes. @@ -356,7 +357,7 @@ r[asm.operands.in-expr] An `input_expr` shall be a value expression of an *asm operand type* or an *input coerceable type*. If the expression is of an *input coerceable type*, it is coerced to an *asm operand type*. r[asm.operands.out-expr] -An `output_expr` shall be the placeholder expression `_` or a (potentially unitialized) place expression of an *asm operand type*. If the place expression is initialized, it shall be a mutable place. +An `output_expr` shall be the placeholder expression `_` or a (potentially uninitialized) place expression of an *asm operand type*. If the place expression is initialized, it shall be a mutable place. ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { unsafe{ @@ -831,7 +832,7 @@ r[asm.options.general] An options-spec provided in the *asm block* places constraints on the *asm block*. r[asm.options.att_syntax] -The `att_syntax` option may be specfied on the x86 and x86_64 target. The program shall not specify the `att_syntax` option on any other target. +The `att_syntax` option may be specified on the x86 and x86_64 target. The program shall not specify the `att_syntax` option on any other target. > [!TARGET-SPECIFIC] > The `att_syntax` option modifies the syntax used to parse the *expanded asm-string* as though the `.att_syntax prefix` directive was issued before parsing the *expanded asm-string*, and modifies the expansion of register operands to include a `%` prefix. From 88a39645562293a49081b1b9fdc3bfcb3a1ce215 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 15:41:53 -0400 Subject: [PATCH 24/30] Fix code block formatting and use "fails to compile" instead of "ill-formed" --- src/inline-assembly.md | 58 ++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index f55701699..426a9b4ff 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -9,7 +9,7 @@ The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] are defined to s > The expansion of the macros has no stable syntax equivalent. This section will refer to the expansion of the macro, rather than the surface syntax. r[asm.safety] -The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. +The macro [`core::arch::asm!`] shall be invoked only within an `unsafe` block. > [!NOTE] > Inline assembly is inherently unsafe. @@ -18,13 +18,13 @@ The macro [`core::arch::asm!`] shall be expanded only within an `unsafe` block. ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { - use core::arch::asm; - asm!("/*inline assembly is inherently unsafe*/"); +use core::arch::asm; +asm!("/*inline assembly is inherently unsafe*/"); # } ``` r[asm.support] -Inline assembly is supported only when compiling for a target using one of the following architectures. A program that contains inline assembly is ill-formed on any other target: +Inline assembly is supported only when compiling for a target using one of the following architectures. A program that contains inline assembly fails to compile on any other target: - x86 and x86-64 - ARM - AArch64 @@ -83,8 +83,10 @@ format_escape := "{{" / "}}" asm_string_piece := non_format_char / format_specifier / format_escape asm_string_content := [*asm_string_piece] -exposition-only_asm = /*asm-block*/ "(" asm_inner ")" // Exposition Only -exposition-only_global_asm = /*global-asm*/ "(" asm_inner ")" // Exposition Only + // Exposition Only, not valid in rust code +asm-block = /*asm-block*/ "(" asm_inner ")" +// Exposition Only, not valid in rust code +global-asm-block = /*global-asm*/ "(" asm_inner ")" ``` ## Invocation @@ -135,14 +137,14 @@ Unless the `raw` option is specified, each `format_string` input to the [`core:: r[asm.invocation.concat] -If multiple `format_string` inputs are provided, then they are concatenated as though by the [`core::concat!`] macro, separating each `format_string` with a string containing a single newline character. If any `format_string` begins a `format_specifier` that is not terminated before the end of the `format_string`, the program is ill-formed. The resulting string is known as the *joined asm-string* +If multiple `format_string` inputs are provided, then they are concatenated as though by the [`core::concat!`] macro, separating each `format_string` with a string containing a single newline character. If any `format_string` begins a `format_specifier` that is not terminated before the end of the `format_string`, the program fails to compile. The resulting string is known as the *joined asm-string* ```rust # #[cfg(target_arch = "x86_64")] {unsafe{ let mut x: i32; // The following lines are equivalent -core::arch::asm!("mov rax, 5", "mov rcx, rax", out("rax") x, out("rcx") _); -core::arch::asm!("mov rax, 5\nmov rcx, rax", out("rax") x, out("rcx") _); +core::arch::asm!("mov rax, 5", "mov rcx, rax", out("rax") x, out("rcx") _); +core::arch::asm!("mov rax, 5\nmov rcx, rax", out("rax") x, out("rcx") _); # }} ``` @@ -154,7 +156,7 @@ Each operand, other than an explicit register operand ([asm.operands.register]) core::arch::asm!("", in(reg) 5i64); # }} # #[cfg(not(target_arch = "x86_64"))] -# core::compile_error!("asm tests are not yet available off of x86_64"); +# core::compile_error!("asm tests are not yet available off of x86_64"); ``` r[asm.invocation.positional] @@ -305,7 +307,8 @@ struct Foo{x: i32} let x: Foo; core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); # }} -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` ```rust,compile_fail @@ -314,7 +317,8 @@ core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); let x: *mut [i32]; core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, out("eax") x); # }} -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` @@ -340,7 +344,8 @@ Each reference type, where the pointee type has no metadata-type, and each funct # #[cfg(target_arch = "x86_64")] { unsafe{ let x = 5; let y: i32; -core::arch::asm!("mov eax, dword ptr [{}]", in(reg) &x, out("eax") y); // equivalent to asm!("mov eax, dword ptr [{}]", in(reg) (&x) as *const i32, out("eax") y); +core::arch::asm!("mov eax, dword ptr [{}]", in(reg) &x, out("eax") y); +// equivalent to asm!("mov eax, dword ptr [{}]", in(reg) (&x) as *const i32, out("eax") y); # }} ``` @@ -349,7 +354,8 @@ core::arch::asm!("mov eax, dword ptr [{}]", in(reg) &x, out("eax") y); // equiva let y: &mut i32; core::arch::asm!("mov {}, 0", out(reg) 5); # }} -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` @@ -364,7 +370,8 @@ An `output_expr` shall be the placeholder expression `_` or a (potentially unini let x: i32 = 0; core::arch::asm!("", out("eax") x); # }} -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` r[asm.operands.inout-expr] @@ -403,7 +410,7 @@ core::arch::asm!("mov eax, 5", out("eax") _); ``` r[asm.operands.sym-expr] -A sym-expr is a path-expr. If the `path-expr` does not refer to a `static` item or a `fn` item, the program is ill-formed. +A sym-expr is a path-expr. If the `path-expr` does not refer to a `static` item or a `fn` item, the program fails to compile. > [!NOTE] > the path-expr may have any type, including a type that isn't an *asm operand type*, and may be either mutable or immutable. @@ -440,7 +447,8 @@ The program shall not use an operand, other than a sym operand, in the expansion ```rust,compile_fail # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("", in("eax") 5); -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); # fn main(){} ``` @@ -582,7 +590,8 @@ Certain registers and register classes are *clobbers only*. Such register names let x: i64; core::arch::asm!("mov {}, 5", out("k0") x); # }} -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` r[asm.register.small-values] @@ -699,7 +708,8 @@ Certain registers are reserved registers. Reserved Registers shall not be named # #[cfg(target_arch = "x86_64")] { unsafe{ core::arch::asm!("mov rsp, 5", out("rsp") x); # }} -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` ## Template modifiers @@ -890,7 +900,8 @@ An *asm block* shall not specify both the `nomem` and `readonly` options. # #[cfg(target_arch = "x86_64")] { unsafe{ core::arch::asm!("mov dword ptr [FOO+rip], 3", options(readonly, nomem)); # }} -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` r[asm.options.pure] @@ -975,7 +986,8 @@ core::arch::asm!("", options(noreturn)); let x: i32; core::arch::asm!("xor edi, edi", "call exit@plt", out("edi") x, options(noreturn)); # }} -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` r[asm.options.global] @@ -985,7 +997,8 @@ A program shall not specify an option, other than the `att_syntax` or `raw` opti # #[cfg(target_arch = "x86_64")] core::arch::global_asm!("", options(noreturn)); -# #[cfg(not(target_arch = "x86_64"))] compile_error!("Inline Assembly Tests are not supported off of x86_64"); +# #[cfg(not(target_arch = "x86_64"))] +# compile_error!("Inline Assembly Tests are not supported off of x86_64"); # fn main(){} ``` @@ -1099,6 +1112,7 @@ r[asm.directives.x86] r[asm.directives.arm] +> [!TARGET-SPECIFIC] > The following directives are guaranteed to be supported on 32-bit ARM platforms > - `.even` > - `.fnstart` From 13884d069ad874e91cfc08b31ee294443532be53 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 15:45:23 -0400 Subject: [PATCH 25/30] Remove trailing spaces from markdown source lines --- src/inline-assembly.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 426a9b4ff..b0ada016b 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -3,7 +3,7 @@ r[asm] r[asm.macros] -The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] are defined to supply inline assembly to a Rust program. They expand to *exposition-only* syntax defined +The macros [`core::arch::asm!`] and [`core::arch::global_asm!`] are defined to supply inline assembly to a Rust program. They expand to *exposition-only* syntax defined > [!NOTE] > The expansion of the macros has no stable syntax equivalent. This section will refer to the expansion of the macro, rather than the surface syntax. @@ -52,7 +52,7 @@ assert_eq!(x, 4 * 6); # } ``` -## Syntax +## Syntax r[asm.syntax] @@ -89,7 +89,7 @@ asm-block = /*asm-block*/ "(" asm_inner ")" global-asm-block = /*global-asm*/ "(" asm_inner ")" ``` -## Invocation +## Invocation r[asm.invocation] @@ -260,7 +260,7 @@ core::arch::asm!("lock"); # }} ``` -## Operand types +## Operand types r[asm.operands] @@ -428,7 +428,7 @@ core::arch::asm!("mov eax, dword ptr [{}+rip]", sym FOO, out("eax") x); ``` r[asm.operands.expansion] -Each operand_spec is expanded in the *joined asm-string* according to the modifiers in `modifier_spec` and the operand. Each reg_operand is assigned to a register according to the reg_spec, and expands to the appropriate version of the `reg_operand`, in the format expected by the asm syntax in effect to specify the appropriate register. A sym operand expand to the linkage name ([dynamic.linkage.name]) of the item referred to by the `path-expr`, if it has either the `#[no_mangle]` or `#[export_name]` attribute, or is defined in an `extern` block, and otherwise, it expands to an unspecified string that can be used within the *expanded asm-string* to refer to the item. +Each operand_spec is expanded in the *joined asm-string* according to the modifiers in `modifier_spec` and the operand. Each reg_operand is assigned to a register according to the reg_spec, and expands to the appropriate version of the `reg_operand`, in the format expected by the asm syntax in effect to specify the appropriate register. A sym operand expand to the linkage name ([dynamic.linkage.name]) of the item referred to by the `path-expr`, if it has either the `#[no_mangle]` or `#[export_name]` attribute, or is defined in an `extern` block, and otherwise, it expands to an unspecified string that can be used within the *expanded asm-string* to refer to the item. > [!NOTE] > The name given to an item used by a sym-expr that does not have a linkage name may be known as the "mangled" name of the item. @@ -492,7 +492,7 @@ core::arch::asm!("", clobber_abi("C")); r[asm.operands.clobbers_abi_ref] A `clobbers_abi` special operand shall be specified after all positional operands, and shall not be a named operand. A `clobbers_abi` special operand cannot be referred to by an operand_specifier -## Register operands +## Register operands r[asm.registers] @@ -712,7 +712,7 @@ core::arch::asm!("mov rsp, 5", out("rsp") x); # compile_error!("Inline Assembly Tests are not supported off of x86_64"); ``` -## Template modifiers +## Template modifiers r[asm.template] @@ -784,7 +784,7 @@ A lint diagnostic should be emitted if a modifier is omitted, or a modifier is u [llvm-argmod]: http://llvm.org/docs/LangRef.html#asm-template-argument-modifiers -## Behaviour of an asm block +## Behaviour of an asm block r[asm.evaluation] @@ -834,7 +834,7 @@ The behaviour is undefined upon exiting an *asm block* unless the stack pointer > * The Direction flag (`flags.DF`) is clear upon entry and must be clear upon exit > * The x87 Stack (that is the `TOP` field of the floating-point status word, and each bit in the floating-point tag word) must be preserved and restored upon exit. If all x87 `st` registers are marked as clobbered, the stack is guaranteed to be empty on entry to the asm block (that is, `TOP` is set to `0x7` and the `ftw` is set to `0xFFFF`). -## Options +## Options r[asm.options] @@ -1003,7 +1003,7 @@ core::arch::global_asm!("", options(noreturn)); # fn main(){} ``` -## Directives Support +## Directives Support r[asm.directives] From 5c0e505bc3a9510b5b20c86fe0c1f0c61ffbd5c6 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 15:47:08 -0400 Subject: [PATCH 26/30] Don't link to a rule that does not yet exist --- src/inline-assembly.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index b0ada016b..9b53bb220 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -260,6 +260,9 @@ core::arch::asm!("lock"); # }} ``` + +[static.expr.safety]: unsafety.md + ## Operand types r[asm.operands] From e2a7a47b85da2afe653b299b339301263211b3a9 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 15:50:45 -0400 Subject: [PATCH 27/30] Properly fix end of line spaces --- src/inline-assembly.md | 52 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 9b53bb220..1dc794999 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -86,7 +86,7 @@ asm_string_content := [*asm_string_piece] // Exposition Only, not valid in rust code asm-block = /*asm-block*/ "(" asm_inner ")" // Exposition Only, not valid in rust code -global-asm-block = /*global-asm*/ "(" asm_inner ")" +global-asm-block = /*global-asm*/ "(" asm_inner ")" ``` ## Invocation @@ -101,7 +101,7 @@ The [`core::arch::asm!`] macro expands to an `/*asm-block*/` when expanded in an ```rust pub fn main() { - # #[cfg(target_arch = "x86_64")] + # #[cfg(target_arch = "x86_64")] unsafe{ core::arch::asm!("") } @@ -120,13 +120,13 @@ core::arch::global_asm!(".rodata", "FOO:", ".ascii \"Hello World\""); ```rust,compile_fail pub fn main() { -# #[cfg(target_arch = "x86_64")] +# #[cfg(target_arch = "x86_64")] # { core::arch::global_asm!("FOO:", ".ascii \"Hello World\""); # } } # #[cfg(not(target_arch = "x86_64"))] -# core::compile_error!("asm tests are not yet available off of x86_64"); +# core::compile_error!("asm tests are not yet available off of x86_64"); ``` r[asm.invocation.format-string] @@ -144,7 +144,7 @@ If multiple `format_string` inputs are provided, then they are concatenated as t let mut x: i32; // The following lines are equivalent core::arch::asm!("mov rax, 5", "mov rcx, rax", out("rax") x, out("rcx") _); -core::arch::asm!("mov rax, 5\nmov rcx, rax", out("rax") x, out("rcx") _); +core::arch::asm!("mov rax, 5\nmov rcx, rax", out("rax") x, out("rcx") _); # }} ``` @@ -190,7 +190,7 @@ core::arch::asm!("mov {output}, {input}", input = in(reg) 5i64, output = out(reg ``` r[asm.invocation.expansion] -If the `raw` option is not specified, the *joined asm-string* is expanded as defined in [asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. If the `raw` option is specified, the *expanded asm-string* is the *joined asm-string* verbatim. +If the `raw` option is not specified, the *joined asm-string* is expanded as defined in [asm.operands.expansion], replacing each `format_specifier` with the appropriate expansion for the operand. The resulting string is called the *expanded asm-string*. If the `raw` option is specified, the *expanded asm-string* is the *joined asm-string* verbatim. r[asm.invocation.syntax] The syntax of the *expanded asm-string* is a subset of the GNU AS syntax for the target. Invoking the macro with a *expanded asm-string* that does not match syntax requirements is *conditionally supported* and has *assembler dependent behavior*. Invoking a directive that is not specified by [asm.directives] is *conditionally supported* and has *assembler dependent behavior*. @@ -214,7 +214,7 @@ core::arch::asm!("foo: jmp foo"); > [!NOTE] > In particular, an asm block may be duplicated, for example if the containing function is inlined, or omitted from the output entirely. -> As a consequence, asm blocks should not use directives that have non-idempotent non-local effects, or named labels and symbol definitions, and should not rely on the non-local effects of other asm blocks. +> As a consequence, asm blocks should not use directives that have non-idempotent non-local effects, or named labels and symbol definitions, and should not rely on the non-local effects of other asm blocks. > Additionally, two asm blocks may not rely upon being adjacent in executable memory, even if they are adjacent in the source. > [!NOTE] @@ -243,7 +243,7 @@ r[asm.invocation.global-section] The *expanded asm-string* of *global-asm-block* invocation acts as though a `.section` directive is issued before the *expanded asm-string* which causes code to be generated in the default section on the target for executable code. > [!NOTE] -> This section is typically named `.text`. +> This section is typically named `.text`. r[asm.invocation.prefix-instr] @@ -288,7 +288,7 @@ core::arch::asm!("mov rax, {input}", input = in(reg) 5i64, out("eax") x); ``` r[asm.operands.registers] -Operands that specify an explicit register `reg_spec` are explicit register operands. +Operands that specify an explicit register `reg_spec` are explicit register operands. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ @@ -347,15 +347,15 @@ Each reference type, where the pointee type has no metadata-type, and each funct # #[cfg(target_arch = "x86_64")] { unsafe{ let x = 5; let y: i32; -core::arch::asm!("mov eax, dword ptr [{}]", in(reg) &x, out("eax") y); -// equivalent to asm!("mov eax, dword ptr [{}]", in(reg) (&x) as *const i32, out("eax") y); +core::arch::asm!("mov eax, dword ptr [{}]", in(reg) &x, out("eax") y); +// equivalent to asm!("mov eax, dword ptr [{}]", in(reg) (&x) as *const i32, out("eax") y); # }} ``` ```rust,compile_fail # #[cfg(target_arch = "x86_64")] { unsafe{ let y: &mut i32; -core::arch::asm!("mov {}, 0", out(reg) 5); +core::arch::asm!("mov {}, 0", out(reg) 5); # }} # #[cfg(not(target_arch = "x86_64"))] # compile_error!("Inline Assembly Tests are not supported off of x86_64"); @@ -363,7 +363,7 @@ core::arch::asm!("mov {}, 0", out(reg) 5); r[asm.operands.in-expr] -An `input_expr` shall be a value expression of an *asm operand type* or an *input coerceable type*. If the expression is of an *input coerceable type*, it is coerced to an *asm operand type*. +An `input_expr` shall be a value expression of an *asm operand type* or an *input coerceable type*. If the expression is of an *input coerceable type*, it is coerced to an *asm operand type*. r[asm.operands.out-expr] An `output_expr` shall be the placeholder expression `_` or a (potentially uninitialized) place expression of an *asm operand type*. If the place expression is initialized, it shall be a mutable place. @@ -396,11 +396,11 @@ r[asm.operands.inout] An `inout` operand is a reg_operand with the `inout` dir_spec, and a `inlateout` operand is a reg_operand with the `inlateout` dir_spec. The `operand_expr` of an `inout` operand or an `inlateout` operand shall be an `inout_expr`. The `input_expr` and `output_expr` of an `inout` or `inlateout` operand is used as though the `inout` operand is replaced with a separate `in` and `out` operand, and the `inlateout` operand is replaced with a separate `in` and `lateout` operand, except that both have the same position if they are positional, or the same name if they are named operands, and both refer to the same register. > [!NOTE] -> An `inlateout` operand differs from an `inout` operand only in that implementation may assume that no other `in`, `inout`, or `inlateout` operands are read after an `inlateout` operand is modified by the *expanded asm-string*. +> An `inlateout` operand differs from an `inout` operand only in that implementation may assume that no other `in`, `inout`, or `inlateout` operands are read after an `inlateout` operand is modified by the *expanded asm-string*. r[asm.operands.clobbers] -An `output_expr` that is the placeholder expression `_` is a clobbers output. The resulting value of the register is discarded. An `out` operand that is a clobbers output shall be an *explicit register operand*. +An `output_expr` that is the placeholder expression `_` is a clobbers output. The resulting value of the register is discarded. An `out` operand that is a clobbers output shall be an *explicit register operand*. > [!NOTE] > Some registers and register classes cannot be used as an operand, other than as a clobber operand. @@ -440,7 +440,7 @@ Each operand_spec is expanded in the *joined asm-string* according to the modifi > A sym operand does not include any relocation modifiers such as `@plt` or `@tpoff`. The *joined asm-string* is responsible for including these as required. > [!TARGET-SPECIFIC] -> On x86 and x86_64 targets, the register name is expanded as-is if the `options(att_syntax)` is not used, and with the `%` prefix if `options(att_syntax)` is used. +> On x86 and x86_64 targets, the register name is expanded as-is if the `options(att_syntax)` is not used, and with the `%` prefix if `options(att_syntax)` is used. r[asm.operands.global] The program shall not use an operand, other than a sym operand, in the expansion of the [`core::arch::global_asm!`] macro. @@ -465,7 +465,7 @@ core::arch::global_asm!("/*{}*/", sym FOO); ``` r[asm.operands.clobbers_abi] -A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, then no `out`, `lateout`, `inout`, or `inlateout` reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. +A special operand `clobbers_abi` may be specified. If the `clobers_abi` operand is specified, then no `out`, `lateout`, `inout`, or `inlateout` reg_operand, other than an *explicit register operand*, shall be specified. When specified, it accepts a string literal which shall belong to a subset of the string literals accepted for an `extern` calling convention specification. The `clobbers_abi` special operand acts as though it is replaced by a `lateout` operand with an out-expr of `_` for each register considered by the specified calling convention to not be preserved by a function call. > [!NOTE] @@ -500,7 +500,7 @@ A `clobbers_abi` special operand shall be specified after all positional operand r[asm.registers] r[asm.registers.explicit] -An explicit register operand specifies the name of a valid operand register that is not a reserved register, or an alias name of a valid operand register. Multiple explicit register operands shall not specify the same register or aliases of the same register. +An explicit register operand specifies the name of a valid operand register that is not a reserved register, or an alias name of a valid operand register. Multiple explicit register operands shall not specify the same register or aliases of the same register. ```rust # #[cfg(target_arch = "x86_64")] { unsafe{ @@ -520,7 +520,7 @@ core::arch::asm!("mov {}, 5", out(reg) x); ``` r[asm.registers.valid-types] -Each register class, and the explicit registers within those classes, may restrict the set of types allowed for operands referring to that class or those registers. +Each register class, and the explicit registers within those classes, may restrict the set of types allowed for operands referring to that class or those registers. > [!NOTE] > The types `isize`, `usize`, and function pointer types are considered valid for a given register class if and only if an integer type of the same width is considered valid. @@ -720,7 +720,7 @@ core::arch::asm!("mov rsp, 5", out("rsp") x); r[asm.template] r[asm.template.modifier] -An operand spec that refers to a register operand may specify a modifier as part of the format specifier. +An operand spec that refers to a register operand may specify a modifier as part of the format specifier. r[asm.template.class] A format specifier shall only use a modifier that is supported for the register class specified by the register opernd. @@ -812,7 +812,7 @@ Certain constraints may be placed on the *asm block*, and on the requirements of r[asm.evaluation.memory] The behaviour is undefined if the *asm block* accesses any allocation, disables, freezes, activates any tags, or *synchronizes-with* a store to a given memory location, except via: * An access to a static item which is declared with the `#[no_mangle]` attribute, the `#[export_name]` attribute, or which is visible to an expression within the function in which the asm block is expanded, -* A pointer tag which has been exposed, +* A pointer tag which has been exposed, * A pointer tag which was passed as an input operand, or * A pointer tag which is accessible by reading any memory the asm block can read under this clause, recursively. @@ -842,7 +842,7 @@ The behaviour is undefined upon exiting an *asm block* unless the stack pointer r[asm.options] r[asm.options.general] -An options-spec provided in the *asm block* places constraints on the *asm block*. +An options-spec provided in the *asm block* places constraints on the *asm block*. r[asm.options.att_syntax] The `att_syntax` option may be specified on the x86 and x86_64 target. The program shall not specify the `att_syntax` option on any other target. @@ -858,10 +858,10 @@ core::arch::asm!("mov {:e}, %eax", in(reg) 5, out("eax") x, options(att_syntax)) ``` r[asm.options.raw] -The `raw` option may be specified. If the `raw` option is specified, the *asm block* or *global asm block* shall not have any operands, other than explicit register operands, and the `clobbers_abi` special operand. +The `raw` option may be specified. If the `raw` option is specified, the *asm block* or *global asm block* shall not have any operands, other than explicit register operands, and the `clobbers_abi` special operand. > [!NOTE] -> The `raw` option causes the *joined asm-string* to be handled verbatim without being interpreted as a format string and expanded. +> The `raw` option causes the *joined asm-string* to be handled verbatim without being interpreted as a format string and expanded. r[asm.options.nomem] @@ -886,7 +886,7 @@ core::arch::asm!("mov {:e}, dword ptr [{}+rip]", out(reg) x, sym FOO, options(no ``` r[asm.options.readonly] -The `readonly` option may be specified. The behaviour is undefined if the *asm block* modifies any allocation or activates any tag. +The `readonly` option may be specified. The behaviour is undefined if the *asm block* modifies any allocation or activates any tag. ```rust,ignore // The following snippet has undefined behaviour @@ -966,7 +966,7 @@ core::arch::asm!("cmp eax, eax", in("eax") 5, options(preserve_flags)); ``` r[asm.options.noreturn] -The `noreturn` option may be specifed. An *asm block* that specifies the `noreturn` option is an expression of type `!`. The behaviour is undefined if an evaluation of the *asm block* exits. The program shall not specify the `clobber_abi` specification, or an operand that is an `out`, `lateout`, `inout`, or `inlateout` operand. +The `noreturn` option may be specifed. An *asm block* that specifies the `noreturn` option is an expression of type `!`. The behaviour is undefined if an evaluation of the *asm block* exits. The program shall not specify the `clobber_abi` specification, or an operand that is an `out`, `lateout`, `inout`, or `inlateout` operand. ```rust # #[cfg(target_arch = "x86_64")] From 457e1d6944fddd03c2181a8a0cd950fbc40cce13 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 18 Jul 2024 15:55:48 -0400 Subject: [PATCH 28/30] Change link to inline-assembly.html to point to asm.evaluation --- src/behavior-considered-undefined.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index 3e4c8a04d..ce722de82 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -82,8 +82,7 @@ Please read the [Rustonomicon] before writing unsafe code. > **Note**: `rustc` achieves this with the unstable > `rustc_layout_scalar_valid_range_*` attributes. -* Incorrect use of inline assembly. For more details, refer to the [rules] to - follow when writing code that uses inline assembly. +* Incorrect use of inline assembly. For more details, refer to [asm.evaluation] for the requirements that an inline assembly block must uphold * **In [const context](const_eval.md#const-context)**: transmuting or otherwise reinterpreting a pointer (reference, raw pointer, or function pointer) into some allocated object as a non-pointer type (such as integers). @@ -162,7 +161,6 @@ must never exceed `isize::MAX`. [`NonNull`]: ../core/ptr/struct.NonNull.html [`NonZero*`]: ../core/num/index.html [place expression context]: expressions.md#place-expressions-and-value-expressions -[rules]: inline-assembly.md#rules-for-inline-assembly [points to]: #pointed-to-bytes [pointed to]: #pointed-to-bytes [offset]: ../std/primitive.pointer.html#method.offset From d59ad1f78b816524858e640401be844bb074a108 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Wed, 24 Jul 2024 22:32:43 -0400 Subject: [PATCH 29/30] Update src/inline-assembly.md Co-authored-by: Eric Huss --- src/inline-assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 1dc794999..358d4327f 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -250,7 +250,7 @@ r[asm.invocation.prefix-instr] An *expanded asm-string* shall not end with an instruction that is interpreted as a prefix on the architecture. No diagnostic is required. > [!TARGET-SPECIFIC] -> On x86 and x86-64, the `lock`, `repnz`, `rep`, `repz`, as well as GNU AS specific address-size, data-size, and explicit rex, vex, and evex prefixes. +> On x86 and x86-64, prefix instructions include `lock`, `repnz`, `rep`, `repz`, as well as GNU AS specific address-size, data-size, and explicit rex, vex, and evex prefixes. ```rust,ignore From 90ef1080830d11c91984337506f7c589f1aa1743 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Fri, 26 Jul 2024 15:33:09 -0400 Subject: [PATCH 30/30] Remove added claims from inline-assembly document. --- src/inline-assembly.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/inline-assembly.md b/src/inline-assembly.md index 7556637f1..2650e463b 100644 --- a/src/inline-assembly.md +++ b/src/inline-assembly.md @@ -229,8 +229,6 @@ core::arch::asm!("2: jmp 2b"); # }} ``` -r[asm.invocation.global-order] -The order in which each *global-asm-block* appears in the program is unspecified. r[asm.invocation.directive-state] The *expanded asm-string* shall not issue a directive that modifies the global state of the assembler for processing inputs unless it issues a directive to restore that state it had upon entering the block. No diagnostic is required. @@ -239,12 +237,6 @@ The *expanded asm-string* shall not issue a directive that modifies the global s > This include state such as the current section of the assembler, the syntax mode, or the kind of assembly output being generated. > Failing to obey this requirement can have significant impact on code generation, including code unrelated to the asm block. For example, an asm block that issues a `.data` directive without resetting to the appropriate section for the function can cause the following code in the function to be generated in the `.data` section, and for execution to fall off the asm block into improper memory. -r[asm.invocation.global-section] -The *expanded asm-string* of *global-asm-block* invocation acts as though a `.section` directive is issued before the *expanded asm-string* which causes code to be generated in the default section on the target for executable code. - -> [!NOTE] -> This section is typically named `.text`. - r[asm.invocation.prefix-instr] An *expanded asm-string* shall not end with an instruction that is interpreted as a prefix on the architecture. No diagnostic is required. @@ -792,7 +784,7 @@ A lint diagnostic should be emitted if a modifier is omitted, or a modifier is u r[asm.evaluation] r[asm.evaluation.general] -Each evaluation of an *asm block* shall perform an operation that correpsonds to the result of a valid sequence of operations on the Minirust Abstract Machine. The behaviour is undefined if the operations performed by the asm block do not validly correspond to a valid sequence of Minirust operations. +Each evaluation of an *asm block* performs an operation that correpsonds to the result of a valid sequence of operations on the Rust Abstract Machine. The behaviour is undefined if the operations performed by the asm block do not validly correspond to a valid sequence of operations on the Abstract Machine. > [!NOTE] > The operation the asm block performs may differ between evaluations of the same asm block.