From e8837382a9e9ab1a86d7895273a6fde31f233554 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 18:23:50 +0200 Subject: [PATCH 01/12] sembr src/backend/backend-agnostic.md --- src/backend/backend-agnostic.md | 56 +++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/backend/backend-agnostic.md b/src/backend/backend-agnostic.md index 1fb991738..c285cd7e7 100644 --- a/src/backend/backend-agnostic.md +++ b/src/backend/backend-agnostic.md @@ -17,8 +17,8 @@ by Denis Merigoux, October 23rd 2018 ### State of the code before the refactoring All the code related to the compilation of MIR into LLVM IR was contained -inside the `rustc_codegen_llvm` crate. Here is the breakdown of the most -important elements: +inside the `rustc_codegen_llvm` crate. +Here is the breakdown of the most important elements: * the `back` folder (7,800 LOC) implements the mechanisms for creating the different object files and archive through LLVM, but also the communication mechanisms for parallel code generation; @@ -37,9 +37,10 @@ important elements: * the `type_.rs` (300 LOC) defines most of the type translations to LLVM IR. The goal of this refactoring is to separate inside this crate code that is -specific to the LLVM from code that can be reused for other rustc backends. For -instance, the `mir` folder is almost entirely backend-specific but it relies -heavily on other parts of the crate. The separation of the code must not affect +specific to the LLVM from code that can be reused for other rustc backends. +For instance, the `mir` folder is almost entirely backend-specific but it relies +heavily on other parts of the crate. +The separation of the code must not affect the logic of the code nor its performance. For these reasons, the separation process involves two transformations that @@ -57,13 +58,14 @@ suggestion by @eddyb). ### Generic types and structures @irinagpopa started to parametrize the types of `rustc_codegen_llvm` by a -generic `Value` type, implemented in LLVM by a reference `&'ll Value`. This +generic `Value` type, implemented in LLVM by a reference `&'ll Value`. +This work has been extended to all structures inside the `mir` folder and elsewhere, as well as for LLVM's `BasicBlock` and `Type` types. The two most important structures for the LLVM codegen are `CodegenCx` and -`Builder`. They are parametrized by multiple lifetime parameters and the type -for `Value`. +`Builder`. +They are parametrized by multiple lifetime parameters and the type for `Value`. ```rust,ignore struct CodegenCx<'ll, 'tcx> { @@ -101,7 +103,8 @@ struct LocalAnalyzer<'mir, 'a, 'tcx> { ``` However, the two most important structures `CodegenCx` and `Builder` are not -defined in the backend-agnostic code. Indeed, their content is highly specific +defined in the backend-agnostic code. +Indeed, their content is highly specific of the backend and it makes more sense to leave their definition to the backend implementor than to allow just a narrow spot via a generic field for the backend's context. @@ -111,8 +114,8 @@ backend's context. Because they have to be defined by the backend, `CodegenCx` and `Builder` will be the structures implementing all the traits defining the backend's interface. These traits are defined in the folder `rustc_codegen_ssa/traits` and all the -backend-agnostic code is parametrized by them. For instance, let us explain how -a function in `base.rs` is parametrized: +backend-agnostic code is parametrized by them. +For instance, let us explain how a function in `base.rs` is parametrized: ```rust,ignore pub fn codegen_instance<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( @@ -125,7 +128,8 @@ pub fn codegen_instance<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( In this signature, we have the two lifetime parameters explained earlier and the master type `Bx` which satisfies the trait `BuilderMethods` corresponding -to the interface satisfied by the `Builder` struct. The `BuilderMethods` +to the interface satisfied by the `Builder` struct. +The `BuilderMethods` defines an associated type `Bx::CodegenCx` that itself satisfies the `CodegenMethods` traits implemented by the struct `CodegenCx`. @@ -158,24 +162,28 @@ pub trait BuilderMethods<'a, 'tcx>: ``` Finally, a master structure implementing the `ExtraBackendMethods` trait is -used for high-level codegen-driving functions like `codegen_crate` in -`base.rs`. For LLVM, it is the empty `LlvmCodegenBackend`. +used for high-level codegen-driving functions like `codegen_crate` in `base.rs`. +For LLVM, it is the empty `LlvmCodegenBackend`. `ExtraBackendMethods` should be implemented by the same structure that implements the `CodegenBackend` defined in `rustc_codegen_ssa/src/traits/backend.rs`. During the traitification process, certain functions have been converted from methods of a local structure to methods of `CodegenCx` or `Builder` and a -corresponding `self` parameter has been added. Indeed, LLVM stores information -internally that it can access when called through its API. This information +corresponding `self` parameter has been added. +Indeed, LLVM stores information +internally that it can access when called through its API. +This information does not show up in a Rust data structure carried around when these methods are -called. However, when implementing a Rust backend for `rustc`, these methods +called. +However, when implementing a Rust backend for `rustc`, these methods will need information from `CodegenCx`, hence the additional parameter (unused in the LLVM implementation of the trait). ### State of the code after the refactoring -The traits offer an API which is very similar to the API of LLVM. This is not +The traits offer an API which is very similar to the API of LLVM. +This is not the best solution since LLVM has a very special way of doing things: when adding another backend, the traits definition might be changed in order to offer more flexibility. @@ -192,16 +200,18 @@ most important elements: * `common.rs`: 350 (BA) vs 350 (LLVM); The `debuginfo` folder has been left almost untouched by the splitting and is -specific to LLVM. Only its high-level features have been traitified. +specific to LLVM. +Only its high-level features have been traitified. -The new `traits` folder has 1500 LOC only for trait definitions. Overall, the +The new `traits` folder has 1500 LOC only for trait definitions. +Overall, the 27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new 18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized -`rustc_codegen_ssa`. We can say that this refactoring allowed the reuse of +`rustc_codegen_ssa`. +We can say that this refactoring allowed the reuse of approximately 10,000 LOC that would otherwise have had to be duplicated between the multiple backends of `rustc`. The refactored version of `rustc`'s backend introduced no regression over the test suite nor in performance benchmark, which is in coherence with the nature -of the refactoring that used only compile-time parametricity (no trait -objects). +of the refactoring that used only compile-time parametricity (no trait objects). From 81d099b66b3b4a317893dcb0704462a7e50415c9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 19:00:07 +0200 Subject: [PATCH 02/12] "the LLVM" sounds wrong --- src/backend/backend-agnostic.md | 6 +++--- src/debugging-support-in-rustc.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/backend-agnostic.md b/src/backend/backend-agnostic.md index c285cd7e7..0e93970ab 100644 --- a/src/backend/backend-agnostic.md +++ b/src/backend/backend-agnostic.md @@ -37,7 +37,7 @@ Here is the breakdown of the most important elements: * the `type_.rs` (300 LOC) defines most of the type translations to LLVM IR. The goal of this refactoring is to separate inside this crate code that is -specific to the LLVM from code that can be reused for other rustc backends. +specific to LLVM from code that can be reused for other rustc backends. For instance, the `mir` folder is almost entirely backend-specific but it relies heavily on other parts of the crate. The separation of the code must not affect @@ -46,12 +46,12 @@ the logic of the code nor its performance. For these reasons, the separation process involves two transformations that have to be done at the same time for the resulting code to compile: -1. replace all the LLVM-specific types by generics inside function signatures +1. replace all LLVM-specific types by generics inside function signatures and structure definitions; 2. encapsulate all functions calling the LLVM FFI inside a set of traits that will define the interface between backend-agnostic code and the backend. -While the LLVM-specific code will be left in `rustc_codegen_llvm`, all the new +While LLVM-specific code will be left in `rustc_codegen_llvm`, all the new traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name suggestion by @eddyb). diff --git a/src/debugging-support-in-rustc.md b/src/debugging-support-in-rustc.md index e69844170..bd1ec6e9b 100644 --- a/src/debugging-support-in-rustc.md +++ b/src/debugging-support-in-rustc.md @@ -247,7 +247,7 @@ The steps of this process are as follows: 1. LLVM needs changing. - LLVM does not emit Interface types at all, so this needs to be implemented in the LLVM first. + LLVM does not emit Interface types at all, so this needs to be implemented in LLVM first. Get sign off on LLVM maintainers that this is a good idea. From b7f310cab2c722633b0e77266eddc99c650f9b4b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 19:01:09 +0200 Subject: [PATCH 03/12] capitalise first word in sentence --- src/backend/backend-agnostic.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/backend-agnostic.md b/src/backend/backend-agnostic.md index 0e93970ab..152feb9bd 100644 --- a/src/backend/backend-agnostic.md +++ b/src/backend/backend-agnostic.md @@ -46,10 +46,10 @@ the logic of the code nor its performance. For these reasons, the separation process involves two transformations that have to be done at the same time for the resulting code to compile: -1. replace all LLVM-specific types by generics inside function signatures - and structure definitions; -2. encapsulate all functions calling the LLVM FFI inside a set of traits that - will define the interface between backend-agnostic code and the backend. +1. Replace all LLVM-specific types by generics inside function signatures + and structure definitions +2. Encapsulate all functions calling the LLVM FFI inside a set of traits that + will define the interface between backend-agnostic code and the backend While LLVM-specific code will be left in `rustc_codegen_llvm`, all the new traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name From 14b88d687042c70eedfa64aaacba8669ffdd33b4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 19:01:35 +0200 Subject: [PATCH 04/12] sembr src/asm.md --- src/asm.md | 60 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/asm.md b/src/asm.md index b5857d546..ae2c653db 100644 --- a/src/asm.md +++ b/src/asm.md @@ -3,10 +3,12 @@ ## Overview Inline assembly in rustc mostly revolves around taking an `asm!` macro invocation and plumbing it -through all of the compiler layers down to LLVM codegen. Throughout the various stages, an +through all of the compiler layers down to LLVM codegen. +Throughout the various stages, an `InlineAsm` generally consists of 3 components: -- The template string, which is stored as an array of `InlineAsmTemplatePiece`. Each piece +- The template string, which is stored as an array of `InlineAsmTemplatePiece`. + Each piece represents either a literal or a placeholder for an operand (just like format strings). ```rust @@ -16,7 +18,8 @@ represents either a literal or a placeholder for an operand (just like format st } ``` -- The list of operands to the `asm!` (`in`, `[late]out`, `in[late]out`, `sym`, `const`). These are +- The list of operands to the `asm!` (`in`, `[late]out`, `in[late]out`, `sym`, `const`). + These are represented differently at each stage of lowering, but follow a common pattern: - `in`, `out` and `inout` all have an associated register class (`reg`) or explicit register (`"eax"`). @@ -25,14 +28,17 @@ one with two separate expressions for the input and output parts. - `out` and `inout` have a `late` flag (`lateout` / `inlateout`) to indicate that the register allocator is allowed to reuse an input register for this output. - `out` and the split variant of `inout` allow `_` to be specified for an output, which means -that the output is discarded. This is used to allocate scratch registers for assembly code. +that the output is discarded. +This is used to allocate scratch registers for assembly code. - `const` refers to an anonymous constants and generally works like an inline const. - `sym` is a bit special since it only accepts a path expression, which must point to a `static` or a `fn`. -- The options set at the end of the `asm!` macro. The only ones that are of particular interest to +- The options set at the end of the `asm!` macro. + The only ones that are of particular interest to rustc are `NORETURN` which makes `asm!` return `!` instead of `()`, and `RAW` which disables format -string parsing. The remaining options are mostly passed through to LLVM with little processing. +string parsing. +The remaining options are mostly passed through to LLVM with little processing. ```rust bitflags::bitflags! { @@ -54,9 +60,11 @@ string parsing. The remaining options are mostly passed through to LLVM with lit `InlineAsm` is represented as an expression in the AST with the [`ast::InlineAsm` type][inline_asm_ast]. -The `asm!` macro is implemented in `rustc_builtin_macros` and outputs an `InlineAsm` AST node. The +The `asm!` macro is implemented in `rustc_builtin_macros` and outputs an `InlineAsm` AST node. +The template string is parsed using `fmt_macros`, positional and named operands are resolved to -explicit operand indices. Since target information is not available to macro invocations, +explicit operand indices. +Since target information is not available to macro invocations, validation of the registers and register classes is deferred to AST lowering. [inline_asm_ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.InlineAsm.html @@ -66,18 +74,23 @@ validation of the registers and register classes is deferred to AST lowering. `InlineAsm` is represented as an expression in the HIR with the [`hir::InlineAsm` type][inline_asm_hir]. AST lowering is where `InlineAsmRegOrRegClass` is converted from `Symbol`s to an actual register or -register class. If any modifiers are specified for a template string placeholder, these are -validated against the set allowed for that operand type. Finally, explicit registers for inputs and +register class. +If any modifiers are specified for a template string placeholder, these are +validated against the set allowed for that operand type. +Finally, explicit registers for inputs and outputs are checked for conflicts (same register used for different operands). [inline_asm_hir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.InlineAsm.html ## Type checking -Each register class has a whitelist of types that it may be used with. After the types of all +Each register class has a whitelist of types that it may be used with. +After the types of all operands have been determined, the `intrinsicck` pass will check that these types are in the -whitelist. It also checks that split `inout` operands have compatible types and that `const` -operands are integers or floats. Suggestions are emitted where needed if a template modifier should +whitelist. +It also checks that split `inout` operands have compatible types and that `const` +operands are integers or floats. +Suggestions are emitted where needed if a template modifier should be used for an operand based on the type that was passed into it. ## THIR @@ -107,7 +120,8 @@ multiple output places where a `Call` only has a single return place output. 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`. The operands are lowered to LLVM operands and constraint codes as follows: -- `out` and the output part of `inout` operands are added first, as required by LLVM. Late output +- `out` and the output part of `inout` operands are added first, as required by LLVM. + Late output operands have a `=` prefix added to their constraint code, non-late output operands have a `=&` prefix added to their constraint code. - `in` operands are added normally. @@ -119,14 +133,16 @@ The template string is converted to LLVM form: - `$` characters are escaped as `$$`. - `const` operands are converted to strings and inserted directly. - Placeholders are formatted as `${X:M}` where `X` is the operand index and `M` is the modifier -character. Modifiers are converted from the Rust form to the LLVM form. +character. +Modifiers are converted from the Rust form to the LLVM form. The various options are converted to clobber constraints or LLVM attributes, refer to the [RFC](https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir) for more details. Note that LLVM is sometimes rather picky about what types it accepts for certain constraint codes -so we sometimes need to insert conversions to/from a supported type. See the target-specific +so we sometimes need to insert conversions to/from a supported type. +See the target-specific ISelLowering.cpp files in LLVM for details of what types are supported for each register class. [inline_asm_codegen]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/traits/enum.InlineAsmOperandRef.html @@ -134,18 +150,22 @@ ISelLowering.cpp files in LLVM for details of what types are supported for each ## Adding support for new architectures Adding inline assembly support to an architecture is mostly a matter of defining the registers and -register classes for that architecture. All the definitions for register classes are located in +register classes for that architecture. +All the definitions for register classes are located in `compiler/rustc_target/asm/`. Additionally you will need to implement lowering of these register classes to LLVM constraint codes in `compiler/rustc_codegen_llvm/asm.rs`. When adding a new architecture, make sure to cross-reference with the LLVM source code: -- LLVM has restrictions on which types can be used with a particular constraint code. Refer to the +- LLVM has restrictions on which types can be used with a particular constraint code. + Refer to the `getRegForInlineAsmConstraint` function in `lib/Target/${ARCH}/${ARCH}ISelLowering.cpp`. - LLVM reserves certain registers for its internal use, which causes them to not be saved/restored -properly around inline assembly blocks. These registers are listed in the `getReservedRegs` -function in `lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp`. Any "conditionally" reserved register +properly around inline assembly blocks. +These registers are listed in the `getReservedRegs` +function in `lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp`. +Any "conditionally" reserved register such as the frame/base pointer must always be treated as reserved for Rust purposes because we can't know ahead of time whether a function will require a frame/base pointer. From 8bc4aae0a546e32ac1070a4b047165f9201a9fad Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:02:35 +0200 Subject: [PATCH 05/12] reflow --- src/asm.md | 102 +++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/src/asm.md b/src/asm.md index ae2c653db..8a027e3db 100644 --- a/src/asm.md +++ b/src/asm.md @@ -4,12 +4,12 @@ Inline assembly in rustc mostly revolves around taking an `asm!` macro invocation and plumbing it through all of the compiler layers down to LLVM codegen. -Throughout the various stages, an -`InlineAsm` generally consists of 3 components: +Throughout the various stages, +an `InlineAsm` generally consists of 3 components: - The template string, which is stored as an array of `InlineAsmTemplatePiece`. - Each piece -represents either a literal or a placeholder for an operand (just like format strings). + Each piece represents either a literal or a placeholder for an operand + (just like format strings). ```rust pub enum InlineAsmTemplatePiece { @@ -19,26 +19,28 @@ represents either a literal or a placeholder for an operand (just like format st ``` - The list of operands to the `asm!` (`in`, `[late]out`, `in[late]out`, `sym`, `const`). - These are -represented differently at each stage of lowering, but follow a common pattern: - - `in`, `out` and `inout` all have an associated register class (`reg`) or explicit register -(`"eax"`). - - `inout` has 2 forms: one with a single expression that is both read from and written to, and -one with two separate expressions for the input and output parts. + These are represented differently at each stage of lowering, + but follow a common pattern: + - `in`, `out`, and `inout` all have an associated register class (`reg`) + or explicit register (`"eax"`). + - `inout` has 2 forms: + one with a single expression that is both read from and written to, + and one with two separate expressions for the input and output parts. - `out` and `inout` have a `late` flag (`lateout` / `inlateout`) to indicate that the register -allocator is allowed to reuse an input register for this output. - - `out` and the split variant of `inout` allow `_` to be specified for an output, which means -that the output is discarded. -This is used to allocate scratch registers for assembly code. - - `const` refers to an anonymous constants and generally works like an inline const. - - `sym` is a bit special since it only accepts a path expression, which must point to a `static` -or a `fn`. + allocator is allowed to reuse an input register for this output. + - `out` and the split variant of `inout` allow `_` to be specified for an output, + which means that the output is discarded. + This is used to allocate scratch registers for assembly code. + - `const` refers to an anonymous constants, + and generally works like an inline const. + - `sym` is a bit special since it only accepts a path expression, + which must point to a `static` or a `fn`. - The options set at the end of the `asm!` macro. The only ones that are of particular interest to -rustc are `NORETURN` which makes `asm!` return `!` instead of `()`, and `RAW` which disables format -string parsing. -The remaining options are mostly passed through to LLVM with little processing. + rustc are `NORETURN` which makes `asm!` return `!` instead of `()`, + and `RAW` which disables format string parsing. + The remaining options are mostly passed through to LLVM with little processing. ```rust bitflags::bitflags! { @@ -61,9 +63,8 @@ The remaining options are mostly passed through to LLVM with little processing. `InlineAsm` is represented as an expression in the AST with the [`ast::InlineAsm` type][inline_asm_ast]. The `asm!` macro is implemented in `rustc_builtin_macros` and outputs an `InlineAsm` AST node. -The -template string is parsed using `fmt_macros`, positional and named operands are resolved to -explicit operand indices. +The template string is parsed using `fmt_macros`, +positional and named operands are resolved to explicit operand indices. Since target information is not available to macro invocations, validation of the registers and register classes is deferred to AST lowering. @@ -85,9 +86,8 @@ outputs are checked for conflicts (same register used for different operands). ## Type checking Each register class has a whitelist of types that it may be used with. -After the types of all -operands have been determined, the `intrinsicck` pass will check that these types are in the -whitelist. +After the types of all operands have been determined, +the `intrinsicck` pass will check that these types are in the whitelist. It also checks that split `inout` operands have compatible types and that `const` operands are integers or floats. Suggestions are emitted where needed if a template modifier should @@ -98,8 +98,8 @@ be used for an operand based on the type that was passed into it. `InlineAsm` is represented as an expression in the THIR with the [`InlineAsmExpr` type][inline_asm_thir]. The only significant change compared to HIR is that `Sym` has been lowered to either a `SymFn` -whose `expr` is a `Literal` ZST of the `fn`, or a `SymStatic` which points to the `DefId` of a -`static`. +whose `expr` is a `Literal` ZST of the `fn`, +or a `SymStatic` which points to the `DefId` of a `static`. [inline_asm_thir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/thir/struct.InlineAsmExpr.html @@ -117,35 +117,36 @@ multiple output places where a `Call` only has a single return place output. ## Codegen -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`. +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`. The operands are lowered to LLVM operands and constraint codes as follows: - `out` and the output part of `inout` operands are added first, as required by LLVM. - Late output -operands have a `=` prefix added to their constraint code, non-late output operands have a `=&` -prefix added to their constraint code. + Late output operands have a `=` prefix added to their constraint code, + and non-late output operands have a `=&` prefix added to their constraint code. - `in` operands are added normally. - `inout` operands are tied to the matching output operand. -- `sym` operands are passed as function pointers or pointers, using the `"s"` constraint. +- `sym` operands are passed as function pointers or pointers, + using the `"s"` constraint. - `const` operands are formatted to a string and directly inserted in the template string. The template string is converted to LLVM form: - `$` characters are escaped as `$$`. - `const` operands are converted to strings and inserted directly. -- Placeholders are formatted as `${X:M}` where `X` is the operand index and `M` is the modifier -character. -Modifiers are converted from the Rust form to the LLVM form. +- Placeholders are formatted as `${X:M}`, + where `X` is the operand index and `M` is the modifier character. + Modifiers are converted from the Rust form to the LLVM form. -The various options are converted to clobber constraints or LLVM attributes, refer to the -[RFC](https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir) -for more details. +The various options are converted to clobber constraints or LLVM attributes; +refer to the [RFC] for more details. Note that LLVM is sometimes rather picky about what types it accepts for certain constraint codes so we sometimes need to insert conversions to/from a supported type. -See the target-specific -ISelLowering.cpp files in LLVM for details of what types are supported for each register class. +See the target-specific ISelLowering.cpp files in LLVM +for details of what types are supported for each register class. [inline_asm_codegen]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/traits/enum.InlineAsmOperandRef.html +[RFC]: https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir ## Adding support for new architectures @@ -159,15 +160,16 @@ in `compiler/rustc_codegen_llvm/asm.rs`. When adding a new architecture, make sure to cross-reference with the LLVM source code: - LLVM has restrictions on which types can be used with a particular constraint code. - Refer to the -`getRegForInlineAsmConstraint` function in `lib/Target/${ARCH}/${ARCH}ISelLowering.cpp`. -- LLVM reserves certain registers for its internal use, which causes them to not be saved/restored -properly around inline assembly blocks. -These registers are listed in the `getReservedRegs` -function in `lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp`. -Any "conditionally" reserved register -such as the frame/base pointer must always be treated as reserved for Rust purposes because we -can't know ahead of time whether a function will require a frame/base pointer. + Refer to the `getRegForInlineAsmConstraint` function + in `lib/Target/${ARCH}/${ARCH}ISelLowering.cpp`. +- LLVM reserves certain registers for its internal use, + which causes them to not be saved/restored properly around inline assembly blocks. + These registers are listed in the `getReservedRegs` + function in `lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp`. + Any "conditionally" reserved register, + such as the frame/base pointer, + must always be treated as reserved for Rust purposes because we + can't know ahead of time whether a function will require a frame/base pointer. ## Tests From 321029334682d15f762ca327af881294292d0ad4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:07:22 +0200 Subject: [PATCH 06/12] inclusive wording --- src/asm.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/asm.md b/src/asm.md index 8a027e3db..9a8791853 100644 --- a/src/asm.md +++ b/src/asm.md @@ -85,9 +85,9 @@ outputs are checked for conflicts (same register used for different operands). ## Type checking -Each register class has a whitelist of types that it may be used with. +Each register class has an allowlist of types that it may be used with. After the types of all operands have been determined, -the `intrinsicck` pass will check that these types are in the whitelist. +the `intrinsicck` pass will check that these types are in the allowlist. It also checks that split `inout` operands have compatible types and that `const` operands are integers or floats. Suggestions are emitted where needed if a template modifier should From 47830946d77d7d8bb5f30c0c1a869e875c4c546c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:28:39 +0200 Subject: [PATCH 07/12] reflow --- src/backend/backend-agnostic.md | 35 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/backend/backend-agnostic.md b/src/backend/backend-agnostic.md index 152feb9bd..566c7b04f 100644 --- a/src/backend/backend-agnostic.md +++ b/src/backend/backend-agnostic.md @@ -38,10 +38,11 @@ Here is the breakdown of the most important elements: The goal of this refactoring is to separate inside this crate code that is specific to LLVM from code that can be reused for other rustc backends. -For instance, the `mir` folder is almost entirely backend-specific but it relies -heavily on other parts of the crate. -The separation of the code must not affect -the logic of the code nor its performance. +For instance, +the `mir` folder is almost entirely backend-specific, +but it relies heavily on other parts of the crate. +The separation of the code must not affect the logic of the code, +nor its performance. For these reasons, the separation process involves two transformations that have to be done at the same time for the resulting code to compile: @@ -102,10 +103,11 @@ struct LocalAnalyzer<'mir, 'a, 'tcx> { } ``` -However, the two most important structures `CodegenCx` and `Builder` are not -defined in the backend-agnostic code. -Indeed, their content is highly specific -of the backend and it makes more sense to leave their definition to the backend +However, the two most important structures, +`CodegenCx` and `Builder`, +are not defined in the backend-agnostic code. +Indeed, their content is highly specific to the backend, +and it makes more sense to leave their definition to the backend implementor than to allow just a narrow spot via a generic field for the backend's context. @@ -129,9 +131,9 @@ pub fn codegen_instance<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( In this signature, we have the two lifetime parameters explained earlier and the master type `Bx` which satisfies the trait `BuilderMethods` corresponding to the interface satisfied by the `Builder` struct. -The `BuilderMethods` -defines an associated type `Bx::CodegenCx` that itself satisfies the -`CodegenMethods` traits implemented by the struct `CodegenCx`. +The `BuilderMethods` defines an associated type, `Bx::CodegenCx`, +that itself satisfies the `CodegenMethods` traits implemented by the struct, +`CodegenCx`. On the trait side, here is an example with part of the definition of `BuilderMethods` in `traits/builder.rs`: @@ -183,10 +185,9 @@ in the LLVM implementation of the trait). ### State of the code after the refactoring The traits offer an API which is very similar to the API of LLVM. -This is not -the best solution since LLVM has a very special way of doing things: when -adding another backend, the traits definition might be changed in order to -offer more flexibility. +This is not the best solution since LLVM has a very special way of doing things: +when adding another backend, +the traits definition might be changed in order to offer more flexibility. However, the current separation between backend-agnostic and LLVM-specific code has allowed the reuse of a significant part of the old `rustc_codegen_llvm`. @@ -204,8 +205,8 @@ specific to LLVM. Only its high-level features have been traitified. The new `traits` folder has 1500 LOC only for trait definitions. -Overall, the -27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new +Overall, +the 27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new 18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized `rustc_codegen_ssa`. We can say that this refactoring allowed the reuse of From d0d3ee4c140238c41651b9912d561f5688dea9f3 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:31:25 +0200 Subject: [PATCH 08/12] sembr src/backend/backend-agnostic.md It has required much reflowing (and sembr tool is not fancy enough yet) --- ci/sembr/src/main.rs | 2 +- src/backend/backend-agnostic.md | 45 +++++++++++---------------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/ci/sembr/src/main.rs b/ci/sembr/src/main.rs index a9b4d182b..4038f112d 100644 --- a/ci/sembr/src/main.rs +++ b/ci/sembr/src/main.rs @@ -15,7 +15,7 @@ struct Cli { /// Modify files that do not comply overwrite: bool, /// Applies to lines that are to be split - #[arg(long, default_value_t = 80)] + #[arg(long, default_value_t = 100)] line_length_limit: usize, } diff --git a/src/backend/backend-agnostic.md b/src/backend/backend-agnostic.md index 566c7b04f..ad0dbb3aa 100644 --- a/src/backend/backend-agnostic.md +++ b/src/backend/backend-agnostic.md @@ -1,15 +1,13 @@ # Backend Agnostic Codegen -[`rustc_codegen_ssa`] -provides an abstract interface for all backends to implement, +[`rustc_codegen_ssa`] provides an abstract interface for all backends to implement, namely LLVM, [Cranelift], and [GCC]. [Cranelift]: https://github.com/rust-lang/rustc_codegen_cranelift [GCC]: https://github.com/rust-lang/rustc_codegen_gcc [`rustc_codegen_ssa`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html -Below is some background information on the refactoring that created this -abstract interface. +Below is some background information on the refactoring that created this abstract interface. ## Refactoring of `rustc_codegen_llvm` by Denis Merigoux, October 23rd 2018 @@ -22,12 +20,10 @@ Here is the breakdown of the most important elements: * the `back` folder (7,800 LOC) implements the mechanisms for creating the different object files and archive through LLVM, but also the communication mechanisms for parallel code generation; -* the `debuginfo` (3,200 LOC) folder contains all code that passes debug - information down to LLVM; +* the `debuginfo` (3,200 LOC) folder contains all code that passes debug information down to LLVM; * the `llvm` (2,200 LOC) folder defines the FFI necessary to communicate with LLVM using the C++ API; -* the `mir` (4,300 LOC) folder implements the actual lowering from MIR to LLVM - IR; +* the `mir` (4,300 LOC) folder implements the actual lowering from MIR to LLVM IR; * the `base.rs` (1,300 LOC) file contains some helper functions but also the high-level code that launches the code generation and distributes the work. * the `builder.rs` (1,200 LOC) file contains all the functions generating @@ -53,19 +49,16 @@ have to be done at the same time for the resulting code to compile: will define the interface between backend-agnostic code and the backend While LLVM-specific code will be left in `rustc_codegen_llvm`, all the new -traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name -suggestion by @eddyb). +traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name suggestion by @eddyb). ### Generic types and structures @irinagpopa started to parametrize the types of `rustc_codegen_llvm` by a generic `Value` type, implemented in LLVM by a reference `&'ll Value`. -This -work has been extended to all structures inside the `mir` folder and elsewhere, +This work has been extended to all structures inside the `mir` folder and elsewhere, as well as for LLVM's `BasicBlock` and `Type` types. -The two most important structures for the LLVM codegen are `CodegenCx` and -`Builder`. +The two most important structures for the LLVM codegen are `CodegenCx` and `Builder`. They are parametrized by multiple lifetime parameters and the type for `Value`. ```rust,ignore @@ -86,10 +79,8 @@ The code in `rustc_codegen_llvm` has to deal with multiple explicit lifetime parameters, that correspond to the following: * `'tcx` is the longest lifetime, that corresponds to the original `TyCtxt` containing the program's information; -* `'a` is a short-lived reference of a `CodegenCx` or another object inside a - struct; -* `'ll` is the lifetime of references to LLVM objects such as `Value` or - `Type`. +* `'a` is a short-lived reference of a `CodegenCx` or another object inside a struct; +* `'ll` is the lifetime of references to LLVM objects such as `Value` or `Type`. Although there are already many lifetime parameters in the code, making it generic uncovered situations where the borrow-checker was passing only due to @@ -108,8 +99,7 @@ However, the two most important structures, are not defined in the backend-agnostic code. Indeed, their content is highly specific to the backend, and it makes more sense to leave their definition to the backend -implementor than to allow just a narrow spot via a generic field for the -backend's context. +implementor than to allow just a narrow spot via a generic field for the backend's context. ### Traits and interface @@ -167,16 +157,13 @@ Finally, a master structure implementing the `ExtraBackendMethods` trait is used for high-level codegen-driving functions like `codegen_crate` in `base.rs`. For LLVM, it is the empty `LlvmCodegenBackend`. `ExtraBackendMethods` should be implemented by the same structure that -implements the `CodegenBackend` defined in -`rustc_codegen_ssa/src/traits/backend.rs`. +implements the `CodegenBackend` defined in `rustc_codegen_ssa/src/traits/backend.rs`. During the traitification process, certain functions have been converted from methods of a local structure to methods of `CodegenCx` or `Builder` and a corresponding `self` parameter has been added. -Indeed, LLVM stores information -internally that it can access when called through its API. -This information -does not show up in a Rust data structure carried around when these methods are +Indeed, LLVM stores information internally that it can access when called through its API. +This information does not show up in a Rust data structure carried around when these methods are called. However, when implementing a Rust backend for `rustc`, these methods will need information from `CodegenCx`, hence the additional parameter (unused @@ -200,15 +187,13 @@ most important elements: * `builder.rs`: 1,400 (BA) vs 0 (LLVM); * `common.rs`: 350 (BA) vs 350 (LLVM); -The `debuginfo` folder has been left almost untouched by the splitting and is -specific to LLVM. +The `debuginfo` folder has been left almost untouched by the splitting and is specific to LLVM. Only its high-level features have been traitified. The new `traits` folder has 1500 LOC only for trait definitions. Overall, the 27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new -18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized -`rustc_codegen_ssa`. +18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized `rustc_codegen_ssa`. We can say that this refactoring allowed the reuse of approximately 10,000 LOC that would otherwise have had to be duplicated between the multiple backends of `rustc`. From bdae23332eeb58098f8d8515793e9a78c2378f3a Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:33:33 +0200 Subject: [PATCH 09/12] sembr src/tests/compiletest.md --- src/tests/compiletest.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/compiletest.md b/src/tests/compiletest.md index 53a6c0cb5..81084132f 100644 --- a/src/tests/compiletest.md +++ b/src/tests/compiletest.md @@ -349,8 +349,8 @@ If you need to work with `#![no_std]` cross-compiling tests, consult the #### Conditional assembly tests based on instruction support Tests that depend on specific assembly instructions being available can use the -`//@ needs-asm-mnemonic: ` directive. This will skip the test if the -target backend does not support the specified instruction mnemonic. +`//@ needs-asm-mnemonic: ` directive. +This will skip the test if the target backend does not support the specified instruction mnemonic. For example, a test that requires the `RET` instruction: ```rust,ignore From 7fcb691bb65140da4b9f589205fc3f3c8ef725eb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:34:39 +0200 Subject: [PATCH 10/12] sembr src/tests/directives.md --- src/tests/directives.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tests/directives.md b/src/tests/directives.md index caa400b57..6c8e787dd 100644 --- a/src/tests/directives.md +++ b/src/tests/directives.md @@ -183,8 +183,7 @@ The following directives will check rustc build settings and target settings: - `needs-threads` — ignores if the target does not have threading support - `needs-subprocess` — ignores if the target does not have subprocess support - `needs-symlink` — ignores if the target does not support symlinks. - This can be the case on Windows if the developer did not enable privileged symlink - permissions. + This can be the case on Windows if the developer did not enable privileged symlink permissions. - `ignore-std-debug-assertions` — ignores if std was built with debug assertions. - `needs-std-debug-assertions` — ignores if std was not built with debug assertions. - `ignore-std-remap-debuginfo` — ignores if std was built with remapping of it's sources. From 9739d3a81cfe7e81af4b8f2a52ea9f43ef05dd29 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:35:27 +0200 Subject: [PATCH 11/12] sembr src/panic-implementation.md --- src/panic-implementation.md | 54 ++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/panic-implementation.md b/src/panic-implementation.md index 8ce949ae2..efbbf2a2a 100644 --- a/src/panic-implementation.md +++ b/src/panic-implementation.md @@ -3,9 +3,9 @@ ## Step 1: Invocation of the `panic!` macro. There are actually two panic macros - one defined in `core`, and one defined in `std`. -This is due to the fact that code in `core` can panic. `core` is built before `std`, -but we want panics to use the same machinery at runtime, whether they originate in `core` -or `std`. +This is due to the fact that code in `core` can panic. +`core` is built before `std`, +but we want panics to use the same machinery at runtime, whether they originate in `core` or `std`. ### core definition of panic! @@ -30,8 +30,8 @@ unsafe { panic_impl(&pi) } Actually resolving this goes through several layers of indirection: 1. In [`compiler/rustc_hir/src/weak_lang_items.rs`], `panic_impl` is - declared as 'weak lang item', with the symbol `rust_begin_unwind`. This is - used in `rustc_hir_analysis/src/collect.rs` to set the actual symbol name to + declared as 'weak lang item', with the symbol `rust_begin_unwind`. + This is used in `rustc_hir_analysis/src/collect.rs` to set the actual symbol name to `rust_begin_unwind`. Note that `panic_impl` is declared in an `extern "Rust"` block, @@ -52,26 +52,33 @@ pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { The special `panic_handler` attribute is resolved via [`compiler/rustc_passes/src/lang_items.rs`]. The [`extract_ast`] function converts the `panic_handler` attribute to a `panic_impl` lang item. -Now, we have a matching `panic_handler` lang item in the `std`. This function goes +Now, we have a matching `panic_handler` lang item in the `std`. +This function goes through the same process as the `extern { fn panic_impl }` definition in `core`, ending -up with a symbol name of `rust_begin_unwind`. At link time, the symbol reference in `core` +up with a symbol name of `rust_begin_unwind`. +At link time, the symbol reference in `core` will be resolved to the definition of `std` (the function called `panic_handler` in the Rust source). -Thus, control flow will pass from core to std at runtime. This allows panics from `core` +Thus, control flow will pass from core to std at runtime. +This allows panics from `core` to go through the same infrastructure that other panics use (panic hooks, unwinding, etc) ### std implementation of panic! -This is where the actual panic-related logic begins. In [`library/std/src/panicking.rs`], -control passes to `panic_with_hook`. This method is responsible -for invoking the global panic hook, and checking for double panics. Finally, +This is where the actual panic-related logic begins. +In [`library/std/src/panicking.rs`], +control passes to `panic_with_hook`. +This method is responsible for invoking the global panic hook, and checking for double panics. +Finally, we call `__rust_start_panic`, which is provided by the panic runtime. The call to `__rust_start_panic` is very weird - it is passed a `*mut &mut dyn PanicPayload`, -converted to an `usize`. Let's break this type down: +converted to an `usize`. +Let's break this type down: -1. `PanicPayload` is an internal trait. It is implemented for `PanicPayload` +1. `PanicPayload` is an internal trait. + It is implemented for `PanicPayload` (a wrapper around the user-supplied payload type), and has a method `fn take_box(&mut self) -> *mut (dyn Any + Send)`. This method takes the user-provided payload (`T: Any + Send`), @@ -81,16 +88,18 @@ boxes it, and converts the box to a raw pointer. However, this is a fat pointer (twice the size of a `usize`). To pass this to the panic runtime across an FFI boundary, we take a mutable reference *to this mutable reference* (`&mut &mut dyn PanicPayload`), and convert it to a raw -pointer (`*mut &mut dyn PanicPayload`). The outer raw pointer is a thin pointer, since it points to -a `Sized` type (a mutable reference). Therefore, we can convert this thin pointer into a `usize`, +pointer (`*mut &mut dyn PanicPayload`). +The outer raw pointer is a thin pointer, since it points to a `Sized` type (a mutable reference). +Therefore, we can convert this thin pointer into a `usize`, which is suitable for passing across an FFI boundary. -Finally, we call `__rust_start_panic` with this `usize`. We have now entered the panic runtime. +Finally, we call `__rust_start_panic` with this `usize`. +We have now entered the panic runtime. ## Step 2: The panic runtime -Rust provides two panic runtimes: `panic_abort` and `panic_unwind`. The user chooses -between them at build time via their `Cargo.toml` +Rust provides two panic runtimes: `panic_abort` and `panic_unwind`. +The user chooses between them at build time via their `Cargo.toml` `panic_abort` is extremely simple: its implementation of `__rust_start_panic` just aborts, as you would expect. @@ -99,13 +108,14 @@ as you would expect. In its implementation of `__rust_start_panic`, we take the `usize`, convert it back to a `*mut &mut dyn PanicPayload`, dereference it, and call `take_box` -on the `&mut dyn PanicPayload`. At this point, we have a raw pointer to the payload +on the `&mut dyn PanicPayload`. +At this point, we have a raw pointer to the payload itself (a `*mut (dyn Send + Any)`): that is, a raw pointer to the actual value provided by the user who called `panic!`. -At this point, the platform-independent code ends. We now call into -platform-specific unwinding logic (e.g `unwind`). This code is -responsible for unwinding the stack, running any 'landing pads' associated +At this point, the platform-independent code ends. +We now call into platform-specific unwinding logic (e.g `unwind`). +This code is responsible for unwinding the stack, running any 'landing pads' associated with each frame (currently, running destructors), and transferring control to the `catch_unwind` frame. From 405546f31d4ecabf9e3b4cce896555d0181ff375 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:37:36 +0200 Subject: [PATCH 12/12] reflow --- src/panic-implementation.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/panic-implementation.md b/src/panic-implementation.md index efbbf2a2a..510155694 100644 --- a/src/panic-implementation.md +++ b/src/panic-implementation.md @@ -79,19 +79,19 @@ Let's break this type down: 1. `PanicPayload` is an internal trait. It is implemented for `PanicPayload` -(a wrapper around the user-supplied payload type), and has a method -`fn take_box(&mut self) -> *mut (dyn Any + Send)`. -This method takes the user-provided payload (`T: Any + Send`), -boxes it, and converts the box to a raw pointer. + (a wrapper around the user-supplied payload type), and has a method + `fn take_box(&mut self) -> *mut (dyn Any + Send)`. + This method takes the user-provided payload (`T: Any + Send`), + boxes it, and converts the box to a raw pointer. 2. When we call `__rust_start_panic`, we have an `&mut dyn PanicPayload`. -However, this is a fat pointer (twice the size of a `usize`). -To pass this to the panic runtime across an FFI boundary, we take a mutable -reference *to this mutable reference* (`&mut &mut dyn PanicPayload`), and convert it to a raw -pointer (`*mut &mut dyn PanicPayload`). -The outer raw pointer is a thin pointer, since it points to a `Sized` type (a mutable reference). -Therefore, we can convert this thin pointer into a `usize`, -which is suitable for passing across an FFI boundary. + However, this is a fat pointer (twice the size of a `usize`). + To pass this to the panic runtime across an FFI boundary, we take a mutable + reference *to this mutable reference* (`&mut &mut dyn PanicPayload`), and convert it to a raw + pointer (`*mut &mut dyn PanicPayload`). + The outer raw pointer is a thin pointer, since it points to a `Sized` type (a mutable reference). + Therefore, we can convert this thin pointer into a `usize`, + which is suitable for passing across an FFI boundary. Finally, we call `__rust_start_panic` with this `usize`. We have now entered the panic runtime. @@ -123,7 +123,6 @@ Note that all panics either abort the process or get caught by some call to `cat In particular, in std's [runtime service], the call to the user-provided `main` function is wrapped in `catch_unwind`. - [runtime service]: https://github.com/rust-lang/rust/blob/HEAD/library/std/src/rt.rs [`library/core/src/panicking.rs`]: https://doc.rust-lang.org/core/panicking/index.html [`compiler/rustc_hir/src/weak_lang_items.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_hir/src/weak_lang_items.rs