Skip to content

Commit 9358d09

Browse files
committed
Auto merge of #100759 - fee1-dead-contrib:const_eval_select_real_intrinsic, r=oli-obk,RalfJung
Make `const_eval_select` a real intrinsic This fixes issues where `track_caller` functions do not have nice panic messages anymore when there is a call to the function, and uses the MIR system to replace the call instead of dispatching via lang items. Fixes #100696.
2 parents e7cdd4c + 65b685e commit 9358d09

File tree

27 files changed

+438
-285
lines changed

27 files changed

+438
-285
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ use rustc_ast as ast;
1313
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
1414
use rustc_hir::lang_items::LangItem;
1515
use rustc_index::vec::Idx;
16-
use rustc_middle::mir::AssertKind;
17-
use rustc_middle::mir::{self, SwitchTargets};
16+
use rustc_middle::mir::{self, AssertKind, SwitchTargets};
1817
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
1918
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
2019
use rustc_middle::ty::{self, Instance, Ty, TypeVisitable};

compiler/rustc_const_eval/src/const_eval/machine.rs

+1-15
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
3535
// All `#[rustc_do_not_const_check]` functions should be hooked here.
3636
let def_id = instance.def_id();
3737

38-
if Some(def_id) == self.tcx.lang_items().const_eval_select() {
39-
// redirect to const_eval_select_ct
40-
if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() {
41-
return Ok(Some(
42-
ty::Instance::resolve(
43-
*self.tcx,
44-
ty::ParamEnv::reveal_all(),
45-
const_eval_select,
46-
instance.substs,
47-
)
48-
.unwrap()
49-
.unwrap(),
50-
));
51-
}
52-
} else if Some(def_id) == self.tcx.lang_items().panic_display()
38+
if Some(def_id) == self.tcx.lang_items().panic_display()
5339
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
5440
{
5541
// &str or &&str

compiler/rustc_hir/src/lang_items.rs

-2
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,6 @@ language_item_table! {
269269
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
270270
Oom, sym::oom, oom, Target::Fn, GenericRequirement::None;
271271
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
272-
ConstEvalSelect, sym::const_eval_select, const_eval_select, Target::Fn, GenericRequirement::Exact(4);
273-
ConstConstEvalSelect, sym::const_eval_select_ct,const_eval_select_ct, Target::Fn, GenericRequirement::Exact(4);
274272

275273
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
276274

compiler/rustc_middle/src/ty/consts/valtree.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
1818
/// `ValTree` does not have this problem with representation, as it only contains integers or
1919
/// lists of (nested) `ValTree`.
2020
pub enum ValTree<'tcx> {
21-
/// ZSTs, integers, `bool`, `char` are represented as scalars.
21+
/// integers, `bool`, `char` are represented as scalars.
2222
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
2323
/// of these types have the same representation.
2424
Leaf(ScalarInt),
@@ -27,8 +27,11 @@ pub enum ValTree<'tcx> {
2727
// dont use SliceOrStr for now
2828
/// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
2929
/// listing their fields' values in order.
30+
///
3031
/// Enums are represented by storing their discriminant as a field, followed by all
3132
/// the fields of the variant.
33+
///
34+
/// ZST types are represented as an empty slice.
3235
Branch(&'tcx [ValTree<'tcx>]),
3336
}
3437

compiler/rustc_middle/src/ty/layout.rs

+89-86
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ fn layout_of<'tcx>(
263263
Ok(layout)
264264
}
265265

266+
#[derive(Clone, Copy)]
266267
pub struct LayoutCx<'tcx, C> {
267268
pub tcx: C,
268269
pub param_env: ty::ParamEnv<'tcx>,
@@ -3063,6 +3064,93 @@ fn fn_abi_of_instance<'tcx>(
30633064
)
30643065
}
30653066

3067+
// Handle safe Rust thin and fat pointers.
3068+
pub fn adjust_for_rust_scalar<'tcx>(
3069+
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
3070+
attrs: &mut ArgAttributes,
3071+
scalar: Scalar,
3072+
layout: TyAndLayout<'tcx>,
3073+
offset: Size,
3074+
is_return: bool,
3075+
) {
3076+
// Booleans are always a noundef i1 that needs to be zero-extended.
3077+
if scalar.is_bool() {
3078+
attrs.ext(ArgExtension::Zext);
3079+
attrs.set(ArgAttribute::NoUndef);
3080+
return;
3081+
}
3082+
3083+
// Scalars which have invalid values cannot be undef.
3084+
if !scalar.is_always_valid(&cx) {
3085+
attrs.set(ArgAttribute::NoUndef);
3086+
}
3087+
3088+
// Only pointer types handled below.
3089+
let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
3090+
3091+
if !valid_range.contains(0) {
3092+
attrs.set(ArgAttribute::NonNull);
3093+
}
3094+
3095+
if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
3096+
if let Some(kind) = pointee.safe {
3097+
attrs.pointee_align = Some(pointee.align);
3098+
3099+
// `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
3100+
// for the entire duration of the function as they can be deallocated
3101+
// at any time. Same for shared mutable references. If LLVM had a
3102+
// way to say "dereferenceable on entry" we could use it here.
3103+
attrs.pointee_size = match kind {
3104+
PointerKind::UniqueBorrowed
3105+
| PointerKind::UniqueBorrowedPinned
3106+
| PointerKind::Frozen => pointee.size,
3107+
PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
3108+
};
3109+
3110+
// `Box`, `&T`, and `&mut T` cannot be undef.
3111+
// Note that this only applies to the value of the pointer itself;
3112+
// this attribute doesn't make it UB for the pointed-to data to be undef.
3113+
attrs.set(ArgAttribute::NoUndef);
3114+
3115+
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
3116+
// `noalias` for it. This can be turned off using an unstable flag.
3117+
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
3118+
let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias.unwrap_or(true);
3119+
3120+
// `&mut` pointer parameters never alias other parameters,
3121+
// or mutable global data
3122+
//
3123+
// `&T` where `T` contains no `UnsafeCell<U>` is immutable,
3124+
// and can be marked as both `readonly` and `noalias`, as
3125+
// LLVM's definition of `noalias` is based solely on memory
3126+
// dependencies rather than pointer equality
3127+
//
3128+
// Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
3129+
// for UniqueBorrowed arguments, so that the codegen backend can decide whether
3130+
// or not to actually emit the attribute. It can also be controlled with the
3131+
// `-Zmutable-noalias` debugging option.
3132+
let no_alias = match kind {
3133+
PointerKind::SharedMutable
3134+
| PointerKind::UniqueBorrowed
3135+
| PointerKind::UniqueBorrowedPinned => false,
3136+
PointerKind::UniqueOwned => noalias_for_box,
3137+
PointerKind::Frozen => !is_return,
3138+
};
3139+
if no_alias {
3140+
attrs.set(ArgAttribute::NoAlias);
3141+
}
3142+
3143+
if kind == PointerKind::Frozen && !is_return {
3144+
attrs.set(ArgAttribute::ReadOnly);
3145+
}
3146+
3147+
if kind == PointerKind::UniqueBorrowed && !is_return {
3148+
attrs.set(ArgAttribute::NoAliasMutRef);
3149+
}
3150+
}
3151+
}
3152+
}
3153+
30663154
impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
30673155
// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
30683156
// arguments of this method, into a separate `struct`.
@@ -3118,91 +3206,6 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
31183206
use SpecAbi::*;
31193207
let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
31203208

3121-
// Handle safe Rust thin and fat pointers.
3122-
let adjust_for_rust_scalar = |attrs: &mut ArgAttributes,
3123-
scalar: Scalar,
3124-
layout: TyAndLayout<'tcx>,
3125-
offset: Size,
3126-
is_return: bool| {
3127-
// Booleans are always a noundef i1 that needs to be zero-extended.
3128-
if scalar.is_bool() {
3129-
attrs.ext(ArgExtension::Zext);
3130-
attrs.set(ArgAttribute::NoUndef);
3131-
return;
3132-
}
3133-
3134-
// Scalars which have invalid values cannot be undef.
3135-
if !scalar.is_always_valid(self) {
3136-
attrs.set(ArgAttribute::NoUndef);
3137-
}
3138-
3139-
// Only pointer types handled below.
3140-
let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
3141-
3142-
if !valid_range.contains(0) {
3143-
attrs.set(ArgAttribute::NonNull);
3144-
}
3145-
3146-
if let Some(pointee) = layout.pointee_info_at(self, offset) {
3147-
if let Some(kind) = pointee.safe {
3148-
attrs.pointee_align = Some(pointee.align);
3149-
3150-
// `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
3151-
// for the entire duration of the function as they can be deallocated
3152-
// at any time. Same for shared mutable references. If LLVM had a
3153-
// way to say "dereferenceable on entry" we could use it here.
3154-
attrs.pointee_size = match kind {
3155-
PointerKind::UniqueBorrowed
3156-
| PointerKind::UniqueBorrowedPinned
3157-
| PointerKind::Frozen => pointee.size,
3158-
PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
3159-
};
3160-
3161-
// `Box`, `&T`, and `&mut T` cannot be undef.
3162-
// Note that this only applies to the value of the pointer itself;
3163-
// this attribute doesn't make it UB for the pointed-to data to be undef.
3164-
attrs.set(ArgAttribute::NoUndef);
3165-
3166-
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
3167-
// `noalias` for it. This can be turned off using an unstable flag.
3168-
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
3169-
let noalias_for_box =
3170-
self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true);
3171-
3172-
// `&mut` pointer parameters never alias other parameters,
3173-
// or mutable global data
3174-
//
3175-
// `&T` where `T` contains no `UnsafeCell<U>` is immutable,
3176-
// and can be marked as both `readonly` and `noalias`, as
3177-
// LLVM's definition of `noalias` is based solely on memory
3178-
// dependencies rather than pointer equality
3179-
//
3180-
// Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
3181-
// for UniqueBorrowed arguments, so that the codegen backend can decide whether
3182-
// or not to actually emit the attribute. It can also be controlled with the
3183-
// `-Zmutable-noalias` debugging option.
3184-
let no_alias = match kind {
3185-
PointerKind::SharedMutable
3186-
| PointerKind::UniqueBorrowed
3187-
| PointerKind::UniqueBorrowedPinned => false,
3188-
PointerKind::UniqueOwned => noalias_for_box,
3189-
PointerKind::Frozen => !is_return,
3190-
};
3191-
if no_alias {
3192-
attrs.set(ArgAttribute::NoAlias);
3193-
}
3194-
3195-
if kind == PointerKind::Frozen && !is_return {
3196-
attrs.set(ArgAttribute::ReadOnly);
3197-
}
3198-
3199-
if kind == PointerKind::UniqueBorrowed && !is_return {
3200-
attrs.set(ArgAttribute::NoAliasMutRef);
3201-
}
3202-
}
3203-
}
3204-
};
3205-
32063209
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
32073210
let is_return = arg_idx.is_none();
32083211

@@ -3218,7 +3221,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
32183221

32193222
let mut arg = ArgAbi::new(self, layout, |layout, scalar, offset| {
32203223
let mut attrs = ArgAttributes::new();
3221-
adjust_for_rust_scalar(&mut attrs, scalar, *layout, offset, is_return);
3224+
adjust_for_rust_scalar(*self, &mut attrs, scalar, *layout, offset, is_return);
32223225
attrs
32233226
});
32243227

compiler/rustc_mir_transform/src/lib.rs

+68-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#![feature(trusted_step)]
1111
#![feature(try_blocks)]
1212
#![feature(yeet_expr)]
13+
#![feature(if_let_guard)]
1314
#![recursion_limit = "256"]
1415

1516
#[macro_use]
@@ -27,10 +28,13 @@ use rustc_hir::intravisit::{self, Visitor};
2728
use rustc_index::vec::IndexVec;
2829
use rustc_middle::mir::visit::Visitor as _;
2930
use rustc_middle::mir::{
30-
traversal, AnalysisPhase, Body, ConstQualifs, MirPass, MirPhase, Promoted, RuntimePhase,
31+
traversal, AnalysisPhase, Body, ConstQualifs, Constant, LocalDecl, MirPass, MirPhase, Operand,
32+
Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, Statement, StatementKind,
33+
TerminatorKind,
3134
};
3235
use rustc_middle::ty::query::Providers;
3336
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
37+
use rustc_span::sym;
3438

3539
#[macro_use]
3640
mod pass_manager;
@@ -140,6 +144,64 @@ pub fn provide(providers: &mut Providers) {
140144
};
141145
}
142146

147+
fn remap_mir_for_const_eval_select<'tcx>(
148+
tcx: TyCtxt<'tcx>,
149+
mut body: Body<'tcx>,
150+
context: hir::Constness,
151+
) -> Body<'tcx> {
152+
for bb in body.basic_blocks.as_mut().iter_mut() {
153+
let terminator = bb.terminator.as_mut().expect("invalid terminator");
154+
match terminator.kind {
155+
TerminatorKind::Call {
156+
func: Operand::Constant(box Constant { ref literal, .. }),
157+
ref mut args,
158+
destination,
159+
target,
160+
cleanup,
161+
fn_span,
162+
..
163+
} if let ty::FnDef(def_id, _) = *literal.ty().kind()
164+
&& tcx.item_name(def_id) == sym::const_eval_select
165+
&& tcx.is_intrinsic(def_id) =>
166+
{
167+
let [tupled_args, called_in_const, called_at_rt]: [_; 3] = std::mem::take(args).try_into().unwrap();
168+
let ty = tupled_args.ty(&body.local_decls, tcx);
169+
let fields = ty.tuple_fields();
170+
let num_args = fields.len();
171+
let func = if context == hir::Constness::Const { called_in_const } else { called_at_rt };
172+
let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = match tupled_args {
173+
Operand::Constant(_) => {
174+
// there is no good way of extracting a tuple arg from a constant (const generic stuff)
175+
// so we just create a temporary and deconstruct that.
176+
let local = body.local_decls.push(LocalDecl::new(ty, fn_span));
177+
bb.statements.push(Statement {
178+
source_info: SourceInfo::outermost(fn_span),
179+
kind: StatementKind::Assign(Box::new((local.into(), Rvalue::Use(tupled_args.clone())))),
180+
});
181+
(Operand::Move, local.into())
182+
}
183+
Operand::Move(place) => (Operand::Move, place),
184+
Operand::Copy(place) => (Operand::Copy, place),
185+
};
186+
let place_elems = place.projection;
187+
let arguments = (0..num_args).map(|x| {
188+
let mut place_elems = place_elems.to_vec();
189+
place_elems.push(ProjectionElem::Field(x.into(), fields[x]));
190+
let projection = tcx.intern_place_elems(&place_elems);
191+
let place = Place {
192+
local: place.local,
193+
projection,
194+
};
195+
method(place)
196+
}).collect();
197+
terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, cleanup, from_hir_call: false, fn_span };
198+
}
199+
_ => {}
200+
}
201+
}
202+
body
203+
}
204+
143205
fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
144206
let def_id = def_id.expect_local();
145207
tcx.mir_keys(()).contains(&def_id)
@@ -325,7 +387,9 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -
325387
.body_const_context(def.did)
326388
.expect("mir_for_ctfe should not be used for runtime functions");
327389

328-
let mut body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone();
390+
let body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone();
391+
392+
let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const);
329393

330394
match context {
331395
// Do not const prop functions, either they get executed at runtime or exported to metadata,
@@ -558,8 +622,9 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
558622
Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other),
559623
}
560624
debug!("about to call mir_drops_elaborated...");
561-
let mut body =
625+
let body =
562626
tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did)).steal();
627+
let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst);
563628
debug!("body: {:#?}", body);
564629
run_optimization_passes(tcx, &mut body);
565630

compiler/rustc_monomorphize/src/collector.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,6 @@
112112
//! method in operand position, we treat it as a neighbor of the current
113113
//! mono item. Calls are just a special case of that.
114114
//!
115-
//! #### Closures
116-
//! In a way, closures are a simple case. Since every closure object needs to be
117-
//! constructed somewhere, we can reliably discover them by observing
118-
//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also
119-
//! true for closures inlined from other crates.
120-
//!
121115
//! #### Drop glue
122116
//! Drop glue mono items are introduced by MIR drop-statements. The
123117
//! generated mono item will again have drop-glue item neighbors if the
@@ -835,7 +829,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
835829
mir::TerminatorKind::Call { ref func, .. } => {
836830
let callee_ty = func.ty(self.body, tcx);
837831
let callee_ty = self.monomorphize(callee_ty);
838-
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output);
832+
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output)
839833
}
840834
mir::TerminatorKind::Drop { ref place, .. }
841835
| mir::TerminatorKind::DropAndReplace { ref place, .. } => {

0 commit comments

Comments
 (0)