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/asm.md b/src/asm.md index b5857d546..9a8791853 100644 --- a/src/asm.md +++ b/src/asm.md @@ -3,11 +3,13 @@ ## 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 -`InlineAsm` generally consists of 3 components: +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 -represents either a literal or a placeholder for an operand (just like format strings). +- 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 pub enum InlineAsmTemplatePiece { @@ -16,23 +18,29 @@ 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. +- 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. - `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`. - -- 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. + 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. ```rust bitflags::bitflags! { @@ -54,9 +62,10 @@ 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 -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, +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, 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 +75,22 @@ 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 -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 +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 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 be used for an operand based on the type that was passed into it. ## THIR @@ -85,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 @@ -104,50 +117,59 @@ 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. +- `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, + 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. +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 +[RFC]: https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir ## 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 -`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. +- 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. ## Tests diff --git a/src/backend/backend-agnostic.md b/src/backend/backend-agnostic.md index 1fb991738..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 @@ -17,17 +15,15 @@ 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; -* 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 @@ -37,33 +33,33 @@ 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 -the logic of the code nor its performance. +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 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 - 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 the 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). +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). ### 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, +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`. +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 struct CodegenCx<'ll, 'tcx> { @@ -83,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 @@ -100,19 +94,20 @@ 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 -implementor than to allow just a narrow spot via a generic field for the -backend's context. +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. ### Traits and interface 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,9 +120,10 @@ 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`. +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`. On the trait side, here is an example with part of the definition of `BuilderMethods` in `traits/builder.rs`: @@ -158,27 +154,27 @@ 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`. +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 -called. However, when implementing a Rust backend for `rustc`, these methods +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 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 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. +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. 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`. @@ -191,17 +187,17 @@ 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. Only its high-level features have been traitified. +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`. We can say that this refactoring allowed the reuse of +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 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). 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. diff --git a/src/panic-implementation.md b/src/panic-implementation.md index 8ce949ae2..510155694 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,45 +52,54 @@ 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` -(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. +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. 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. +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. @@ -113,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 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 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.