Skip to content

Commit 2a8fd51

Browse files
committed
Make const_eval_select a real intrinsic
1 parent a9bb589 commit 2a8fd51

File tree

27 files changed

+431
-285
lines changed

27 files changed

+431
-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
@@ -267,8 +267,6 @@ language_item_table! {
267267
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
268268
Oom, sym::oom, oom, Target::Fn, GenericRequirement::None;
269269
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
270-
ConstEvalSelect, sym::const_eval_select, const_eval_select, Target::Fn, GenericRequirement::Exact(4);
271-
ConstConstEvalSelect, sym::const_eval_select_ct,const_eval_select_ct, Target::Fn, GenericRequirement::Exact(4);
272270

273271
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
274272

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
@@ -274,6 +274,7 @@ fn layout_of<'tcx>(
274274
})
275275
}
276276

277+
#[derive(Clone, Copy)]
277278
pub struct LayoutCx<'tcx, C> {
278279
pub tcx: C,
279280
pub param_env: ty::ParamEnv<'tcx>,
@@ -3074,6 +3075,93 @@ fn fn_abi_of_instance<'tcx>(
30743075
)
30753076
}
30763077

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

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

@@ -3229,7 +3232,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
32293232

32303233
let mut arg = ArgAbi::new(self, layout, |layout, scalar, offset| {
32313234
let mut attrs = ArgAttributes::new();
3232-
adjust_for_rust_scalar(&mut attrs, scalar, *layout, offset, is_return);
3235+
adjust_for_rust_scalar(*self, &mut attrs, scalar, *layout, offset, is_return);
32333236
attrs
32343237
});
32353238

compiler/rustc_mir_transform/src/lib.rs

+66-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#![feature(trusted_step)]
1010
#![feature(try_blocks)]
1111
#![feature(yeet_expr)]
12+
#![feature(if_let_guard)]
1213
#![recursion_limit = "256"]
1314

1415
#[macro_use]
@@ -25,9 +26,13 @@ use rustc_hir::def_id::{DefId, LocalDefId};
2526
use rustc_hir::intravisit::{self, Visitor};
2627
use rustc_index::vec::IndexVec;
2728
use rustc_middle::mir::visit::Visitor as _;
28-
use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPass, MirPhase, Promoted};
29+
use rustc_middle::mir::{
30+
traversal, Body, ConstQualifs, Constant, LocalDecl, MirPass, MirPhase, Operand, Place,
31+
ProjectionElem, Promoted, Rvalue, SourceInfo, Statement, StatementKind, TerminatorKind,
32+
};
2933
use rustc_middle::ty::query::Providers;
3034
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
35+
use rustc_span::sym;
3136

3237
#[macro_use]
3338
mod pass_manager;
@@ -137,6 +142,64 @@ pub fn provide(providers: &mut Providers) {
137142
};
138143
}
139144

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

348411
debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE");
349412

350-
body
413+
remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const)
351414
}
352415

353416
/// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs
@@ -537,7 +600,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
537600

538601
debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
539602

540-
body
603+
remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst)
541604
}
542605

543606
/// Fetch all the promoteds of an item and prepare their MIR bodies to be ready for

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
@@ -830,7 +824,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
830824
mir::TerminatorKind::Call { ref func, .. } => {
831825
let callee_ty = func.ty(self.body, tcx);
832826
let callee_ty = self.monomorphize(callee_ty);
833-
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output);
827+
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output)
834828
}
835829
mir::TerminatorKind::Drop { ref place, .. }
836830
| mir::TerminatorKind::DropAndReplace { ref place, .. } => {

0 commit comments

Comments
 (0)