33## Overview
44
55Inline assembly in rustc mostly revolves around taking an ` asm! ` macro invocation and plumbing it
6- through all of the compiler layers down to LLVM codegen. Throughout the various stages, an
7- ` InlineAsm ` generally consists of 3 components:
6+ through all of the compiler layers down to LLVM codegen.
7+ Throughout the various stages,
8+ an ` InlineAsm ` generally consists of 3 components:
89
9- - The template string, which is stored as an array of ` InlineAsmTemplatePiece ` . Each piece
10- represents either a literal or a placeholder for an operand (just like format strings).
10+ - The template string, which is stored as an array of ` InlineAsmTemplatePiece ` .
11+ Each piece represents either a literal or a placeholder for an operand
12+ (just like format strings).
1113
1214 ``` rust
1315 pub enum InlineAsmTemplatePiece {
@@ -16,23 +18,29 @@ represents either a literal or a placeholder for an operand (just like format st
1618 }
1719 ```
1820
19- - The list of operands to the ` asm! ` (` in ` , ` [late]out ` , ` in[late]out ` , ` sym ` , ` const ` ). These are
20- represented differently at each stage of lowering, but follow a common pattern:
21- - ` in ` , ` out ` and ` inout ` all have an associated register class (` reg ` ) or explicit register
22- (` "eax" ` ).
23- - ` inout ` has 2 forms: one with a single expression that is both read from and written to, and
24- one with two separate expressions for the input and output parts.
21+ - The list of operands to the ` asm! ` (` in ` , ` [late]out ` , ` in[late]out ` , ` sym ` , ` const ` ).
22+ These are represented differently at each stage of lowering,
23+ but follow a common pattern:
24+ - ` in ` , ` out ` , and ` inout ` all have an associated register class (` reg ` )
25+ or explicit register (` "eax" ` ).
26+ - ` inout ` has 2 forms:
27+ one with a single expression that is both read from and written to,
28+ and one with two separate expressions for the input and output parts.
2529 - ` out ` and ` inout ` have a ` late ` flag (` lateout ` / ` inlateout ` ) to indicate that the register
26- allocator is allowed to reuse an input register for this output.
27- - ` out ` and the split variant of ` inout ` allow ` _ ` to be specified for an output, which means
28- that the output is discarded. This is used to allocate scratch registers for assembly code.
29- - ` const ` refers to an anonymous constants and generally works like an inline const.
30- - ` sym ` is a bit special since it only accepts a path expression, which must point to a ` static `
31- or a ` fn ` .
32-
33- - The options set at the end of the ` asm! ` macro. The only ones that are of particular interest to
34- rustc are ` NORETURN ` which makes ` asm! ` return ` ! ` instead of ` () ` , and ` RAW ` which disables format
35- string parsing. The remaining options are mostly passed through to LLVM with little processing.
30+ allocator is allowed to reuse an input register for this output.
31+ - ` out ` and the split variant of ` inout ` allow ` _ ` to be specified for an output,
32+ which means that the output is discarded.
33+ This is used to allocate scratch registers for assembly code.
34+ - ` const ` refers to an anonymous constants,
35+ and generally works like an inline const.
36+ - ` sym ` is a bit special since it only accepts a path expression,
37+ which must point to a ` static ` or a ` fn ` .
38+
39+ - The options set at the end of the ` asm! ` macro.
40+ The only ones that are of particular interest to
41+ rustc are ` NORETURN ` which makes ` asm! ` return ` ! ` instead of ` () ` ,
42+ and ` RAW ` which disables format string parsing.
43+ The remaining options are mostly passed through to LLVM with little processing.
3644
3745 ``` rust
3846 bitflags :: bitflags! {
@@ -54,9 +62,10 @@ string parsing. The remaining options are mostly passed through to LLVM with lit
5462
5563` InlineAsm ` is represented as an expression in the AST with the [ ` ast::InlineAsm ` type] [ inline_asm_ast ] .
5664
57- The ` asm! ` macro is implemented in ` rustc_builtin_macros ` and outputs an ` InlineAsm ` AST node. The
58- template string is parsed using ` fmt_macros ` , positional and named operands are resolved to
59- explicit operand indices. Since target information is not available to macro invocations,
65+ The ` asm! ` macro is implemented in ` rustc_builtin_macros ` and outputs an ` InlineAsm ` AST node.
66+ The template string is parsed using ` fmt_macros ` ,
67+ positional and named operands are resolved to explicit operand indices.
68+ Since target information is not available to macro invocations,
6069validation of the registers and register classes is deferred to AST lowering.
6170
6271[ inline_asm_ast ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.InlineAsm.html
@@ -66,27 +75,31 @@ validation of the registers and register classes is deferred to AST lowering.
6675` InlineAsm ` is represented as an expression in the HIR with the [ ` hir::InlineAsm ` type] [ inline_asm_hir ] .
6776
6877AST lowering is where ` InlineAsmRegOrRegClass ` is converted from ` Symbol ` s to an actual register or
69- register class. If any modifiers are specified for a template string placeholder, these are
70- validated against the set allowed for that operand type. Finally, explicit registers for inputs and
78+ register class.
79+ If any modifiers are specified for a template string placeholder, these are
80+ validated against the set allowed for that operand type.
81+ Finally, explicit registers for inputs and
7182outputs are checked for conflicts (same register used for different operands).
7283
7384[ inline_asm_hir ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.InlineAsm.html
7485
7586## Type checking
7687
77- Each register class has a whitelist of types that it may be used with. After the types of all
78- operands have been determined, the ` intrinsicck ` pass will check that these types are in the
79- whitelist. It also checks that split ` inout ` operands have compatible types and that ` const `
80- operands are integers or floats. Suggestions are emitted where needed if a template modifier should
88+ Each register class has an allowlist of types that it may be used with.
89+ After the types of all operands have been determined,
90+ the ` intrinsicck ` pass will check that these types are in the allowlist.
91+ It also checks that split ` inout ` operands have compatible types and that ` const `
92+ operands are integers or floats.
93+ Suggestions are emitted where needed if a template modifier should
8194be used for an operand based on the type that was passed into it.
8295
8396## THIR
8497
8598` InlineAsm ` is represented as an expression in the THIR with the [ ` InlineAsmExpr ` type] [ inline_asm_thir ] .
8699
87100The only significant change compared to HIR is that ` Sym ` has been lowered to either a ` SymFn `
88- whose ` expr ` is a ` Literal ` ZST of the ` fn ` , or a ` SymStatic ` which points to the ` DefId ` of a
89- ` static ` .
101+ whose ` expr ` is a ` Literal ` ZST of the ` fn ` ,
102+ or a ` SymStatic ` which points to the ` DefId ` of a ` static ` .
90103
91104[ inline_asm_thir ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/thir/struct.InlineAsmExpr.html
92105
@@ -104,50 +117,59 @@ multiple output places where a `Call` only has a single return place output.
104117
105118## Codegen
106119
107- Operands are lowered one more time before being passed to LLVM codegen, this is represented by the [ ` InlineAsmOperandRef ` type] [ inline_asm_codegen ] from ` rustc_codegen_ssa ` .
120+ Operands are lowered one more time before being passed to LLVM codegen.
121+ This is represented by the [ ` InlineAsmOperandRef ` type] [ inline_asm_codegen ] from ` rustc_codegen_ssa ` .
108122
109123The operands are lowered to LLVM operands and constraint codes as follows:
110- - ` out ` and the output part of ` inout ` operands are added first, as required by LLVM. Late output
111- operands have a ` = ` prefix added to their constraint code, non-late output operands have a ` =& `
112- prefix added to their constraint code.
124+ - ` out ` and the output part of ` inout ` operands are added first, as required by LLVM.
125+ Late output operands have a ` = ` prefix added to their constraint code,
126+ and non-late output operands have a ` =& ` prefix added to their constraint code.
113127- ` in ` operands are added normally.
114128- ` inout ` operands are tied to the matching output operand.
115- - ` sym ` operands are passed as function pointers or pointers, using the ` "s" ` constraint.
129+ - ` sym ` operands are passed as function pointers or pointers,
130+ using the ` "s" ` constraint.
116131- ` const ` operands are formatted to a string and directly inserted in the template string.
117132
118133The template string is converted to LLVM form:
119134- ` $ ` characters are escaped as ` $$ ` .
120135- ` const ` operands are converted to strings and inserted directly.
121- - Placeholders are formatted as ` ${X:M} ` where ` X ` is the operand index and ` M ` is the modifier
122- character. Modifiers are converted from the Rust form to the LLVM form.
136+ - Placeholders are formatted as ` ${X:M} ` ,
137+ where ` X ` is the operand index and ` M ` is the modifier character.
138+ Modifiers are converted from the Rust form to the LLVM form.
123139
124- The various options are converted to clobber constraints or LLVM attributes, refer to the
125- [ RFC] ( https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir )
126- for more details.
140+ The various options are converted to clobber constraints or LLVM attributes;
141+ refer to the [ RFC] for more details.
127142
128143Note that LLVM is sometimes rather picky about what types it accepts for certain constraint codes
129- so we sometimes need to insert conversions to/from a supported type. See the target-specific
130- ISelLowering.cpp files in LLVM for details of what types are supported for each register class.
144+ so we sometimes need to insert conversions to/from a supported type.
145+ See the target-specific ISelLowering.cpp files in LLVM
146+ for details of what types are supported for each register class.
131147
132148[ inline_asm_codegen ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/traits/enum.InlineAsmOperandRef.html
149+ [ RFC ] : https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir
133150
134151## Adding support for new architectures
135152
136153Adding inline assembly support to an architecture is mostly a matter of defining the registers and
137- register classes for that architecture. All the definitions for register classes are located in
154+ register classes for that architecture.
155+ All the definitions for register classes are located in
138156` compiler/rustc_target/asm/ ` .
139157
140158Additionally you will need to implement lowering of these register classes to LLVM constraint codes
141159in ` compiler/rustc_codegen_llvm/asm.rs ` .
142160
143161When adding a new architecture, make sure to cross-reference with the LLVM source code:
144- - LLVM has restrictions on which types can be used with a particular constraint code. Refer to the
145- ` getRegForInlineAsmConstraint ` function in ` lib/Target/${ARCH}/${ARCH}ISelLowering.cpp ` .
146- - LLVM reserves certain registers for its internal use, which causes them to not be saved/restored
147- properly around inline assembly blocks. These registers are listed in the ` getReservedRegs `
148- function in ` lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp ` . Any "conditionally" reserved register
149- such as the frame/base pointer must always be treated as reserved for Rust purposes because we
150- can't know ahead of time whether a function will require a frame/base pointer.
162+ - LLVM has restrictions on which types can be used with a particular constraint code.
163+ Refer to the ` getRegForInlineAsmConstraint ` function
164+ in ` lib/Target/${ARCH}/${ARCH}ISelLowering.cpp ` .
165+ - LLVM reserves certain registers for its internal use,
166+ which causes them to not be saved/restored properly around inline assembly blocks.
167+ These registers are listed in the ` getReservedRegs `
168+ function in ` lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp ` .
169+ Any "conditionally" reserved register,
170+ such as the frame/base pointer,
171+ must always be treated as reserved for Rust purposes because we
172+ can't know ahead of time whether a function will require a frame/base pointer.
151173
152174## Tests
153175
0 commit comments