Skip to content

Rollup of 7 pull requests #95938

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 26 commits into from
Closed
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4f28344
Improve documentation of `Place` and `Operand`
JakobDegen Mar 24, 2022
8368590
Adjust computation of place types to detect more invalid places
JakobDegen Mar 24, 2022
e000179
Add documentation for the semantics of MIR rvalues
JakobDegen Mar 25, 2022
5fc8676
Extend the MIR validator to check many more things around rvalues.
JakobDegen Mar 25, 2022
148beaf
Improve documentation for MIR statement kinds.
JakobDegen Mar 25, 2022
c996bc0
Improve documentation for MIR terminators
JakobDegen Mar 26, 2022
3c169f3
Adjust MIR validator to check a few more things for terminators
JakobDegen Mar 26, 2022
8ef4af7
Improve MIR phases documentation with summaries of changes
JakobDegen Mar 26, 2022
14fb427
Address various comments and change some details around place to valu…
JakobDegen Mar 27, 2022
a5d2c04
Add more clarifications in response to Ralf's comments
JakobDegen Apr 8, 2022
9745a17
Remove rule that place loads may not happen with variant index set
JakobDegen Apr 9, 2022
bf3ef0d
Switch to the 'normal' basic block for writing asm outputs if needed.
luqmana Apr 9, 2022
0b2f360
Update asm-may_unwind test to handle use of asm with outputs.
luqmana Apr 9, 2022
bb3a071
Fix formatting error in pin.rs docs
nyanpasu64 Apr 10, 2022
b92cd1a
Clarify str::from_utf8_unchecked's invariants
CAD97 Apr 10, 2022
26781c3
Fix documentation for wasm32-unknown-unknown
o01eg Apr 10, 2022
986c168
Remove duplicate aliases for `codegen_{cranelift,gcc}`
jyn514 Apr 10, 2022
4c14383
Add `build compiler/rustc_codegen_gcc` as an alias for `CodegenBackend`
jyn514 Apr 10, 2022
aeb3df7
CI: do not compile libcore twice when performing LLVM PGO
Kobzol Apr 11, 2022
d4a1b19
Rollup merge of #95320 - JakobDegen:mir-docs, r=oli-obk
Dylan-DPC Apr 11, 2022
98cb6e8
Rollup merge of #95864 - luqmana:inline-asm-unwind-store-miscompile, …
Dylan-DPC Apr 11, 2022
e08c70a
Rollup merge of #95894 - nyanpasu64:fix-pin-docs, r=Dylan-DPC
Dylan-DPC Apr 11, 2022
ee9a149
Rollup merge of #95895 - CAD97:patch-2, r=Dylan-DPC
Dylan-DPC Apr 11, 2022
dd719a7
Rollup merge of #95900 - o01eg:fix-wasm-doc, r=Mark-Simulacrum
Dylan-DPC Apr 11, 2022
7450505
Rollup merge of #95901 - jyn514:remove-duplicate-aliases, r=Mark-Simu…
Dylan-DPC Apr 11, 2022
ba25dc9
Rollup merge of #95927 - Kobzol:ci-pgo-libcore, r=lqd
Dylan-DPC Apr 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_llvm/src/asm.rs
Original file line number Diff line number Diff line change
@@ -290,6 +290,11 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
}
attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs });

// Switch to the 'normal' basic block if we did an `invoke` instead of a `call`
if let Some((dest, _, _)) = dest_catch_funclet {
self.switch_to_block(dest);
}

// Write results to outputs
for (idx, op) in operands.iter().enumerate() {
if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }
229 changes: 185 additions & 44 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
@@ -3,15 +3,14 @@
use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::traversal;
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{
AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPass, MirPhase, Operand,
PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement, StatementKind, Terminator,
TerminatorKind, START_BLOCK,
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, Local, Location, MirPass,
MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement,
StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable};
use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::AlwaysLiveLocals;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
@@ -36,6 +35,13 @@ pub struct Validator {

impl<'tcx> MirPass<'tcx> for Validator {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// FIXME(JakobDegen): These bodies never instantiated in codegend anyway, so it's not
// terribly important that they pass the validator. However, I think other passes might
// still see them, in which case they might be surprised. It would probably be better if we
// didn't put this through the MIR pipeline at all.
if matches!(body.source.instance, InstanceDef::Intrinsic(..) | InstanceDef::Virtual(..)) {
return;
}
let def_id = body.source.def_id();
let param_env = tcx.param_env(def_id);
let mir_phase = self.mir_phase;
@@ -240,58 +246,179 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.super_projection_elem(local, proj_base, elem, context, location);
}

fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
match &statement.kind {
StatementKind::Assign(box (dest, rvalue)) => {
// LHS and RHS of the assignment must have the same type.
let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
if !self.mir_assign_valid_types(right_ty, left_ty) {
fn visit_place(&mut self, place: &Place<'tcx>, _: PlaceContext, _: Location) {
// Set off any `bug!`s in the type computation code
let _ = place.ty(&self.body.local_decls, self.tcx);
}

fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
macro_rules! check_kinds {
($t:expr, $text:literal, $($patterns:tt)*) => {
if !matches!(($t).kind(), $($patterns)*) {
self.fail(location, format!($text, $t));
}
};
}
match rvalue {
Rvalue::Use(_) => {}
Rvalue::Aggregate(agg_kind, _) => {
let disallowed = match **agg_kind {
AggregateKind::Array(..) => false,
AggregateKind::Generator(..) => self.mir_phase >= MirPhase::GeneratorsLowered,
_ => self.mir_phase >= MirPhase::Deaggregated,
};
if disallowed {
self.fail(
location,
format!(
"encountered `{:?}` with incompatible types:\n\
left-hand side has type: {}\n\
right-hand side has type: {}",
statement.kind, left_ty, right_ty,
),
format!("{:?} have been lowered to field assignments", rvalue),
)
}
}
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
if self.mir_phase >= MirPhase::DropsLowered {
self.fail(
location,
"`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
);
}
match rvalue {
// The sides of an assignment must not alias. Currently this just checks whether the places
// are identical.
Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
if dest == src {
}
Rvalue::Len(p) => {
let pty = p.ty(&self.body.local_decls, self.tcx).ty;
check_kinds!(
pty,
"Cannot compute length of non-array type {:?}",
ty::Array(..) | ty::Slice(..)
);
}
Rvalue::BinaryOp(op, vals) | Rvalue::CheckedBinaryOp(op, vals) => {
use BinOp::*;
let a = vals.0.ty(&self.body.local_decls, self.tcx);
let b = vals.1.ty(&self.body.local_decls, self.tcx);
match op {
Offset => {
check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
if b != self.tcx.types.isize && b != self.tcx.types.usize {
self.fail(location, format!("Cannot offset by non-isize type {:?}", b));
}
}
Eq | Lt | Le | Ne | Ge | Gt => {
for x in [a, b] {
check_kinds!(
x,
"Cannot compare type {:?}",
ty::Bool
| ty::Char
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::RawPtr(..)
| ty::FnPtr(..)
)
}
// None of the possible types have lifetimes, so we can just compare
// directly
if a != b {
self.fail(
location,
"encountered `Assign` statement with overlapping memory",
format!("Cannot compare unequal types {:?} and {:?}", a, b),
);
}
}
Rvalue::Aggregate(agg_kind, _) => {
let disallowed = match **agg_kind {
AggregateKind::Array(..) => false,
AggregateKind::Generator(..) => {
self.mir_phase >= MirPhase::GeneratorsLowered
}
_ => self.mir_phase >= MirPhase::Deaggregated,
};
if disallowed {
Shl | Shr => {
for x in [a, b] {
check_kinds!(
x,
"Cannot shift non-integer type {:?}",
ty::Uint(..) | ty::Int(..)
)
}
}
BitAnd | BitOr | BitXor => {
for x in [a, b] {
check_kinds!(
x,
"Cannot perform bitwise op on type {:?}",
ty::Uint(..) | ty::Int(..) | ty::Bool
)
}
if a != b {
self.fail(
location,
format!("{:?} have been lowered to field assignments", rvalue),
)
format!(
"Cannot perform bitwise op on unequal types {:?} and {:?}",
a, b
),
);
}
}
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
if self.mir_phase >= MirPhase::DropsLowered {
Add | Sub | Mul | Div | Rem => {
for x in [a, b] {
check_kinds!(
x,
"Cannot perform op on type {:?}",
ty::Uint(..) | ty::Int(..) | ty::Float(..)
)
}
if a != b {
self.fail(
location,
"`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
format!("Cannot perform op on unequal types {:?} and {:?}", a, b),
);
}
}
_ => {}
}
}
Rvalue::UnaryOp(op, operand) => {
let a = operand.ty(&self.body.local_decls, self.tcx);
match op {
UnOp::Neg => {
check_kinds!(a, "Cannot negate type {:?}", ty::Int(..) | ty::Float(..))
}
UnOp::Not => {
check_kinds!(
a,
"Cannot binary not type {:?}",
ty::Int(..) | ty::Uint(..) | ty::Bool
);
}
}
}
Rvalue::ShallowInitBox(operand, _) => {
let a = operand.ty(&self.body.local_decls, self.tcx);
check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..));
}
_ => {}
}
self.super_rvalue(rvalue, location);
}

fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
match &statement.kind {
StatementKind::Assign(box (dest, rvalue)) => {
// LHS and RHS of the assignment must have the same type.
let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
if !self.mir_assign_valid_types(right_ty, left_ty) {
self.fail(
location,
format!(
"encountered `{:?}` with incompatible types:\n\
left-hand side has type: {}\n\
right-hand side has type: {}",
statement.kind, left_ty, right_ty,
),
);
}
// FIXME(JakobDegen): Check this for all rvalues, not just this one.
if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
// The sides of an assignment must not alias. Currently this just checks whether
// the places are identical.
if dest == src {
self.fail(
location,
"encountered `Assign` statement with overlapping memory",
);
}
}
}
StatementKind::AscribeUserType(..) => {
@@ -497,6 +624,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
TerminatorKind::Yield { resume, drop, .. } => {
if self.body.generator.is_none() {
self.fail(location, "`Yield` cannot appear outside generator bodies");
}
if self.mir_phase >= MirPhase::GeneratorsLowered {
self.fail(location, "`Yield` should have been replaced by generator lowering");
}
@@ -536,18 +666,29 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
TerminatorKind::GeneratorDrop => {
if self.body.generator.is_none() {
self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
}
if self.mir_phase >= MirPhase::GeneratorsLowered {
self.fail(
location,
"`GeneratorDrop` should have been replaced by generator lowering",
);
}
}
// Nothing to validate for these.
TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable => {}
TerminatorKind::Resume | TerminatorKind::Abort => {
let bb = location.block;
if !self.body.basic_blocks()[bb].is_cleanup {
self.fail(location, "Cannot `Resume` or `Abort` from non-cleanup basic block")
}
}
TerminatorKind::Return => {
let bb = location.block;
if self.body.basic_blocks()[bb].is_cleanup {
self.fail(location, "Cannot `Return` from cleanup basic block")
}
}
TerminatorKind::Unreachable => {}
}

self.super_terminator(terminator, location);
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -59,6 +59,7 @@
#![feature(unwrap_infallible)]
#![feature(decl_macro)]
#![feature(drain_filter)]
#![feature(intra_doc_pointers)]
#![recursion_limit = "512"]
#![allow(rustc::potential_query_instability)]

353 changes: 300 additions & 53 deletions compiler/rustc_middle/src/mir/mod.rs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/mir/tcx.rs
Original file line number Diff line number Diff line change
@@ -76,6 +76,9 @@ impl<'tcx> PlaceTy<'tcx> {
V: ::std::fmt::Debug,
T: ::std::fmt::Debug + Copy,
{
if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
bug!("cannot use non field projection on downcasted place")
}
let answer = match *elem {
ProjectionElem::Deref => {
let ty = self
150 changes: 123 additions & 27 deletions compiler/rustc_middle/src/mir/terminator.rs
Original file line number Diff line number Diff line change
@@ -105,13 +105,34 @@ impl<'a> Iterator for SwitchTargetsIter<'a> {

impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}

/// A note on unwinding: Panics may occur during the execution of some terminators. Depending on the
/// `-C panic` flag, this may either cause the program to abort or the call stack to unwind. Such
/// terminators have a `cleanup: Option<BasicBlock>` field on them. If stack unwinding occurs, then
/// once the current function is reached, execution continues at the given basic block, if any. If
/// `cleanup` is `None` then no cleanup is performed, and the stack continues unwinding. This is
/// equivalent to the execution of a `Resume` terminator.
///
/// The basic block pointed to by a `cleanup` field must have its `cleanup` flag set. `cleanup`
/// basic blocks have a couple restrictions:
/// 1. All `cleanup` fields in them must be `None`.
/// 2. `Return` terminators are not allowed in them. `Abort` and `Unwind` terminators are.
/// 3. All other basic blocks (in the current body) that are reachable from `cleanup` basic blocks
/// must also be `cleanup`. This is a part of the type system and checked statically, so it is
/// still an error to have such an edge in the CFG even if it's known that it won't be taken at
/// runtime.
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
pub enum TerminatorKind<'tcx> {
/// Block should have one successor in the graph; we jump there.
/// Block has one successor; we continue execution there.
Goto { target: BasicBlock },

/// Operand evaluates to an integer; jump depending on its value
/// to one of the targets, and otherwise fallback to `otherwise`.
/// Switches based on the computed value.
///
/// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
/// integer, char, or bool, and must match the given type. Then, if the list of switch targets
/// contains the computed value, continues execution at the associated basic block. Otherwise,
/// continues execution at the "otherwise" basic block.
///
/// Target values may not appear more than once.
SwitchInt {
/// The discriminant value being tested.
discr: Operand<'tcx>,
@@ -124,29 +145,62 @@ pub enum TerminatorKind<'tcx> {
targets: SwitchTargets,
},

/// Indicates that the landing pad is finished and unwinding should
/// continue. Emitted by `build::scope::diverge_cleanup`.
/// Indicates that the landing pad is finished and that the process should continue unwinding.
///
/// Like a return, this marks the end of this invocation of the function.
///
/// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
/// deaggregation runs.
Resume,

/// Indicates that the landing pad is finished and that the process
/// should abort. Used to prevent unwinding for foreign items.
/// Indicates that the landing pad is finished and that the process should abort.
///
/// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
/// cleanup blocks.
Abort,

/// Indicates a normal return. The return place should have
/// been filled in before this executes. This can occur multiple times
/// in different basic blocks.
/// Returns from the function.
///
/// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
/// likely at least assigns the value currently in the return place (`_0`) to the place
/// specified in the associated `Call` terminator in the calling function, as if assigned via
/// `dest = move _0`. It might additionally do other things, like have side-effects in the
/// aliasing model.
///
/// If the body is a generator body, this has slightly different semantics; it instead causes a
/// `GeneratorState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
/// to the return place.
Return,

/// Indicates a terminator that can never be reached.
///
/// Executing this terminator is UB.
Unreachable,

/// Drop the `Place`.
/// The behavior of this statement differs significantly before and after drop elaboration.
/// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
/// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
/// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
/// issue tracking if drop glue has any interesting semantics in addition to those of a function
/// call?)
///
/// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
/// `Drop` will be executed if...
///
/// **Needs clarification**: End of that sentence. This in effect should document the exact
/// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
///
/// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
/// > the place or one of its "parents" occurred more recently than a move out of it. This does not
/// > consider indirect assignments.
Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> },

/// Drop the `Place` and assign the new value over it. This ensures
/// that the assignment to `P` occurs *even if* the destructor for
/// place unwinds. Its semantics are best explained by the
/// elaboration:
/// Drops the place and assigns a new value to it.
///
/// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
/// it then additionally assigns the `value` to the `place` as if by an assignment statement.
/// This assignment occurs both in the unwind and the regular code paths. The semantics are best
/// explained by the elaboration:
///
/// ```
/// BB0 {
@@ -170,15 +224,24 @@ pub enum TerminatorKind<'tcx> {
/// }
/// ```
///
/// Note that DropAndReplace is eliminated as part of the `ElaborateDrops` pass.
/// Disallowed after drop elaboration.
DropAndReplace {
place: Place<'tcx>,
value: Operand<'tcx>,
target: BasicBlock,
unwind: Option<BasicBlock>,
},

/// Block ends with a call of a function.
/// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
/// the referred to function. The operand types must match the argument types of the function.
/// The return place type must match the return type. The type of the `func` operand must be
/// callable, meaning either a function pointer, a function type, or a closure type.
///
/// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
/// operands not aliasing the return place. It is unclear how this is justified in MIR, see
/// [#71117].
///
/// [#71117]: https://github.com/rust-lang/rust/issues/71117
Call {
/// The function that’s being called.
func: Operand<'tcx>,
@@ -187,7 +250,7 @@ pub enum TerminatorKind<'tcx> {
/// This allows the memory occupied by "by-value" arguments to be
/// reused across function calls without duplicating the contents.
args: Vec<Operand<'tcx>>,
/// Destination for the return value. If some, the call is converging.
/// Destination for the return value. If none, the call necessarily diverges.
destination: Option<(Place<'tcx>, BasicBlock)>,
/// Cleanups to be done if the call unwinds.
cleanup: Option<BasicBlock>,
@@ -199,8 +262,12 @@ pub enum TerminatorKind<'tcx> {
fn_span: Span,
},

/// Jump to the target if the condition has the expected value,
/// otherwise panic with a message and a cleanup target.
/// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
/// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
/// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
/// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
/// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
/// assertion does not fail, execution continues at the specified basic block.
Assert {
cond: Operand<'tcx>,
expected: bool,
@@ -209,7 +276,18 @@ pub enum TerminatorKind<'tcx> {
cleanup: Option<BasicBlock>,
},

/// A suspend point.
/// Marks a suspend point.
///
/// Like `Return` terminators in generator bodies, this computes `value` and then a
/// `GeneratorState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
/// the return place of the function calling this one, and execution continues in the calling
/// function. When next invoked with the same first argument, execution of this function
/// continues at the `resume` basic block, with the second argument written to the `resume_arg`
/// place. If the generator is dropped before then, the `drop` basic block is invoked.
///
/// Not permitted in bodies that are not generator bodies, or after generator lowering.
///
/// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
Yield {
/// The value to return.
value: Operand<'tcx>,
@@ -221,21 +299,39 @@ pub enum TerminatorKind<'tcx> {
drop: Option<BasicBlock>,
},

/// Indicates the end of the dropping of a generator.
/// Indicates the end of dropping a generator.
///
/// Semantically just a `return` (from the generators drop glue). Only permitted in the same situations
/// as `yield`.
///
/// **Needs clarification**: Is that even correct? The generator drop code is always confusing
/// to me, because it's not even really in the current body.
///
/// **Needs clarification**: Are there type system constraints on these terminators? Should
/// there be a "block type" like `cleanup` blocks for them?
GeneratorDrop,

/// A block where control flow only ever takes one real path, but borrowck
/// needs to be more conservative.
/// A block where control flow only ever takes one real path, but borrowck needs to be more
/// conservative.
///
/// At runtime this is semantically just a goto.
///
/// Disallowed after drop elaboration.
FalseEdge {
/// The target normal control flow will take.
real_target: BasicBlock,
/// A block control flow could conceptually jump to, but won't in
/// practice.
imaginary_target: BasicBlock,
},
/// A terminator for blocks that only take one path in reality, but where we
/// reserve the right to unwind in borrowck, even if it won't happen in practice.
/// This can arise in infinite loops with no function calls for example.

/// A terminator for blocks that only take one path in reality, but where we reserve the right
/// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
/// with no function calls for example.
///
/// At runtime this is semantically just a goto.
///
/// Disallowed after drop elaboration.
FalseUnwind {
/// The target normal control flow will take.
real_target: BasicBlock,
2 changes: 1 addition & 1 deletion library/core/src/pin.rs
Original file line number Diff line number Diff line change
@@ -175,7 +175,7 @@
//! relies on pinning requires unsafe code, but be aware that deciding to make
//! use of pinning in your type (for example by implementing some operation on
//! <code>[Pin]<[&]Self></code> or <code>[Pin]<[&mut] Self></code>) has consequences for your
//! [`Drop`][Drop]implementation as well: if an element of your type could have been pinned,
//! [`Drop`][Drop] implementation as well: if an element of your type could have been pinned,
//! you must treat [`Drop`][Drop] as implicitly taking <code>[Pin]<[&mut] Self></code>.
//!
//! For example, you could implement [`Drop`][Drop] as follows:
6 changes: 1 addition & 5 deletions library/core/src/str/converts.rs
Original file line number Diff line number Diff line change
@@ -144,11 +144,7 @@ pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> {
///
/// # Safety
///
/// This function is unsafe because it does not check that the bytes passed to
/// it are valid UTF-8. If this constraint is violated, undefined behavior
/// results, as the rest of Rust assumes that [`&str`]s are valid UTF-8.
///
/// [`&str`]: str
/// The bytes passed in must be valid UTF-8.
///
/// # Examples
///
6 changes: 3 additions & 3 deletions library/std/src/os/fd/owned.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ use crate::fmt;
use crate::fs;
use crate::marker::PhantomData;
use crate::mem::forget;
#[cfg(not(any(target_os = "wasi", target_env = "sgx")))]
#[cfg(not(any(target_arch = "wasm32", target_env = "sgx")))]
use crate::sys::cvt;
use crate::sys_common::{AsInner, FromInner, IntoInner};

@@ -76,7 +76,7 @@ impl BorrowedFd<'_> {
impl OwnedFd {
/// Creates a new `OwnedFd` instance that shares the same underlying file handle
/// as the existing `OwnedFd` instance.
#[cfg(not(target_os = "wasi"))]
#[cfg(not(target_arch = "wasm32"))]
pub fn try_clone(&self) -> crate::io::Result<Self> {
// We want to atomically duplicate this file descriptor and set the
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
@@ -95,7 +95,7 @@ impl OwnedFd {
Ok(unsafe { Self::from_raw_fd(fd) })
}

#[cfg(target_os = "wasi")]
#[cfg(target_arch = "wasm32")]
pub fn try_clone(&self) -> crate::io::Result<Self> {
Err(crate::io::const_io_error!(
crate::io::ErrorKind::Unsupported,
2 changes: 1 addition & 1 deletion library/std/src/os/fd/raw.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
use crate::fs;
use crate::io;
use crate::os::raw;
#[cfg(doc)]
#[cfg(all(doc, any(unix, target_os = "wasi")))]
use crate::os::unix::io::AsFd;
#[cfg(unix)]
use crate::os::unix::io::OwnedFd;
7 changes: 1 addition & 6 deletions src/bootstrap/check.rs
Original file line number Diff line number Diff line change
@@ -243,12 +243,7 @@ impl Step for CodegenBackend {
const DEFAULT: bool = true;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&[
"compiler/rustc_codegen_cranelift",
"rustc_codegen_cranelift",
"compiler/rustc_codegen_gcc",
"rustc_codegen_gcc",
])
run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"])
}

fn make_run(run: RunConfig<'_>) {
2 changes: 1 addition & 1 deletion src/bootstrap/compile.rs
Original file line number Diff line number Diff line change
@@ -795,7 +795,7 @@ impl Step for CodegenBackend {
const DEFAULT: bool = true;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("compiler/rustc_codegen_cranelift")
run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"])
}

fn make_run(run: RunConfig<'_>) {
6 changes: 0 additions & 6 deletions src/ci/pgo.sh
Original file line number Diff line number Diff line change
@@ -47,12 +47,6 @@ python3 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \
--stage 2 library/std \
--llvm-profile-generate

# Profile libcore compilation in opt-level=0 and opt-level=3
RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \
--edition=2021 --crate-type=lib ../library/core/src/lib.rs
RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \
--edition=2021 --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs

# Compile rustc perf
cp -r /tmp/rustc-perf ./
chown -R $(whoami): ./rustc-perf
17 changes: 15 additions & 2 deletions src/test/codegen/asm-may_unwind.rs
Original file line number Diff line number Diff line change
@@ -18,10 +18,23 @@ impl Drop for Foo {
}
}

// CHECK-LABEL: @may_unwind
// CHECK-LABEL: @asm_may_unwind
#[no_mangle]
pub unsafe fn may_unwind() {
pub unsafe fn asm_may_unwind() {
let _m = Foo;
// CHECK: invoke void asm sideeffect alignstack inteldialect unwind ""
asm!("", options(may_unwind));
}

// CHECK-LABEL: @asm_with_result_may_unwind
#[no_mangle]
pub unsafe fn asm_with_result_may_unwind() -> u64 {
let _m = Foo;
let res: u64;
// CHECK: [[RES:%[0-9]+]] = invoke i64 asm sideeffect alignstack inteldialect unwind
// CHECK-NEXT: to label %[[NORMALBB:[a-b0-9]+]]
asm!("mov {}, 1", out(reg) res, options(may_unwind));
// CHECK: [[NORMALBB]]:
// CHECK: ret i64 [[RES:%[0-9]+]]
res
}
2 changes: 1 addition & 1 deletion src/test/mir-opt/lower_intrinsics.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
#![crate_type = "lib"]

// EMIT_MIR lower_intrinsics.wrapping.LowerIntrinsics.diff
pub fn wrapping<T: Copy>(a: T, b: T) {
pub fn wrapping(a: i32, b: i32) {
let _x = core::intrinsics::wrapping_add(a, b);
let _y = core::intrinsics::wrapping_sub(a, b);
let _z = core::intrinsics::wrapping_mul(a, b);
40 changes: 20 additions & 20 deletions src/test/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
- // MIR for `wrapping` before LowerIntrinsics
+ // MIR for `wrapping` after LowerIntrinsics

fn wrapping(_1: T, _2: T) -> () {
debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:6:26: 6:27
debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:6:32: 6:33
let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:6:38: 6:38
let _3: T; // in scope 0 at $DIR/lower_intrinsics.rs:7:9: 7:11
let mut _4: T; // in scope 0 at $DIR/lower_intrinsics.rs:7:45: 7:46
let mut _5: T; // in scope 0 at $DIR/lower_intrinsics.rs:7:48: 7:49
let mut _7: T; // in scope 0 at $DIR/lower_intrinsics.rs:8:45: 8:46
let mut _8: T; // in scope 0 at $DIR/lower_intrinsics.rs:8:48: 8:49
let mut _10: T; // in scope 0 at $DIR/lower_intrinsics.rs:9:45: 9:46
let mut _11: T; // in scope 0 at $DIR/lower_intrinsics.rs:9:48: 9:49
fn wrapping(_1: i32, _2: i32) -> () {
debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:6:17: 6:18
debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:6:25: 6:26
let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:6:33: 6:33
let _3: i32; // in scope 0 at $DIR/lower_intrinsics.rs:7:9: 7:11
let mut _4: i32; // in scope 0 at $DIR/lower_intrinsics.rs:7:45: 7:46
let mut _5: i32; // in scope 0 at $DIR/lower_intrinsics.rs:7:48: 7:49
let mut _7: i32; // in scope 0 at $DIR/lower_intrinsics.rs:8:45: 8:46
let mut _8: i32; // in scope 0 at $DIR/lower_intrinsics.rs:8:48: 8:49
let mut _10: i32; // in scope 0 at $DIR/lower_intrinsics.rs:9:45: 9:46
let mut _11: i32; // in scope 0 at $DIR/lower_intrinsics.rs:9:48: 9:49
scope 1 {
debug _x => _3; // in scope 1 at $DIR/lower_intrinsics.rs:7:9: 7:11
let _6: T; // in scope 1 at $DIR/lower_intrinsics.rs:8:9: 8:11
let _6: i32; // in scope 1 at $DIR/lower_intrinsics.rs:8:9: 8:11
scope 2 {
debug _y => _6; // in scope 2 at $DIR/lower_intrinsics.rs:8:9: 8:11
let _9: T; // in scope 2 at $DIR/lower_intrinsics.rs:9:9: 9:11
let _9: i32; // in scope 2 at $DIR/lower_intrinsics.rs:9:9: 9:11
scope 3 {
debug _z => _9; // in scope 3 at $DIR/lower_intrinsics.rs:9:9: 9:11
}
@@ -30,10 +30,10 @@
_4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:7:45: 7:46
StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:7:48: 7:49
_5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:7:48: 7:49
- _3 = wrapping_add::<T>(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:7:14: 7:50
- _3 = wrapping_add::<i32>(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:7:14: 7:50
- // mir::Constant
- // + span: $DIR/lower_intrinsics.rs:7:14: 7:44
- // + literal: Const { ty: extern "rust-intrinsic" fn(T, T) -> T {wrapping_add::<T>}, val: Value(Scalar(<ZST>)) }
- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_add::<i32>}, val: Value(Scalar(<ZST>)) }
+ _3 = Add(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:7:14: 7:50
+ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:7:14: 7:50
}
@@ -46,10 +46,10 @@
_7 = _1; // scope 1 at $DIR/lower_intrinsics.rs:8:45: 8:46
StorageLive(_8); // scope 1 at $DIR/lower_intrinsics.rs:8:48: 8:49
_8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:8:48: 8:49
- _6 = wrapping_sub::<T>(move _7, move _8) -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:8:14: 8:50
- _6 = wrapping_sub::<i32>(move _7, move _8) -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:8:14: 8:50
- // mir::Constant
- // + span: $DIR/lower_intrinsics.rs:8:14: 8:44
- // + literal: Const { ty: extern "rust-intrinsic" fn(T, T) -> T {wrapping_sub::<T>}, val: Value(Scalar(<ZST>)) }
- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_sub::<i32>}, val: Value(Scalar(<ZST>)) }
+ _6 = Sub(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:8:14: 8:50
+ goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:8:14: 8:50
}
@@ -62,18 +62,18 @@
_10 = _1; // scope 2 at $DIR/lower_intrinsics.rs:9:45: 9:46
StorageLive(_11); // scope 2 at $DIR/lower_intrinsics.rs:9:48: 9:49
_11 = _2; // scope 2 at $DIR/lower_intrinsics.rs:9:48: 9:49
- _9 = wrapping_mul::<T>(move _10, move _11) -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:9:14: 9:50
- _9 = wrapping_mul::<i32>(move _10, move _11) -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:9:14: 9:50
- // mir::Constant
- // + span: $DIR/lower_intrinsics.rs:9:14: 9:44
- // + literal: Const { ty: extern "rust-intrinsic" fn(T, T) -> T {wrapping_mul::<T>}, val: Value(Scalar(<ZST>)) }
- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_mul::<i32>}, val: Value(Scalar(<ZST>)) }
+ _9 = Mul(move _10, move _11); // scope 2 at $DIR/lower_intrinsics.rs:9:14: 9:50
+ goto -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:9:14: 9:50
}

bb3: {
StorageDead(_11); // scope 2 at $DIR/lower_intrinsics.rs:9:49: 9:50
StorageDead(_10); // scope 2 at $DIR/lower_intrinsics.rs:9:49: 9:50
_0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:6:38: 10:2
_0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:6:33: 10:2
StorageDead(_9); // scope 2 at $DIR/lower_intrinsics.rs:10:1: 10:2
StorageDead(_6); // scope 1 at $DIR/lower_intrinsics.rs:10:1: 10:2
StorageDead(_3); // scope 0 at $DIR/lower_intrinsics.rs:10:1: 10:2