Skip to content

Commit 97663b6

Browse files
committed
Auto merge of #82834 - nikic:mutable-noalias, r=nagisa
Enable mutable noalias for LLVM >= 12 Enable mutable noalias by default on LLVM 12, as previously known miscompiles have been resolved. Now it's time to find the next one ;) * The `-Z mutable-noalias` option no longer has an explicit default and accepts `-Z mutable-noalias=yes` and `-Z mutable-noalias=no` to override the LLVM version based default behavior. * The decision on whether to apply the noalias attribute is moved into rustc_codegen_llvm. rustc_middle only provides us with the necessary information to make the decision. * `noalias` is not emitted for types that are `!Unpin`, as a heuristic for self-referential structures (see #54878 and #63818).
2 parents f826641 + 68a62b7 commit 97663b6

File tree

13 files changed

+165
-57
lines changed

13 files changed

+165
-57
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

+47-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::builder::Builder;
22
use crate::context::CodegenCx;
33
use crate::llvm::{self, AttributePlace};
4+
use crate::llvm_util;
45
use crate::type_::Type;
56
use crate::type_of::LayoutLlvmExt;
67
use crate::value::Value;
@@ -41,12 +42,29 @@ impl ArgAttributeExt for ArgAttribute {
4142
}
4243

4344
pub trait ArgAttributesExt {
44-
fn apply_attrs_to_llfn(&self, idx: AttributePlace, llfn: &Value);
45-
fn apply_attrs_to_callsite(&self, idx: AttributePlace, callsite: &Value);
45+
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value);
46+
fn apply_attrs_to_callsite(
47+
&self,
48+
idx: AttributePlace,
49+
cx: &CodegenCx<'_, '_>,
50+
callsite: &Value,
51+
);
52+
}
53+
54+
fn should_use_mutable_noalias(cx: &CodegenCx<'_, '_>) -> bool {
55+
// LLVM prior to version 12 has known miscompiles in the presence of
56+
// noalias attributes (see #54878). Only enable mutable noalias by
57+
// default for versions we believe to be safe.
58+
cx.tcx
59+
.sess
60+
.opts
61+
.debugging_opts
62+
.mutable_noalias
63+
.unwrap_or_else(|| llvm_util::get_version() >= (12, 0, 0))
4664
}
4765

4866
impl ArgAttributesExt for ArgAttributes {
49-
fn apply_attrs_to_llfn(&self, idx: AttributePlace, llfn: &Value) {
67+
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value) {
5068
let mut regular = self.regular;
5169
unsafe {
5270
let deref = self.pointee_size.bytes();
@@ -62,6 +80,9 @@ impl ArgAttributesExt for ArgAttributes {
6280
llvm::LLVMRustAddAlignmentAttr(llfn, idx.as_uint(), align.bytes() as u32);
6381
}
6482
regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn));
83+
if regular.contains(ArgAttribute::NoAliasMutRef) && should_use_mutable_noalias(cx) {
84+
llvm::Attribute::NoAlias.apply_llfn(idx, llfn);
85+
}
6586
match self.arg_ext {
6687
ArgExtension::None => {}
6788
ArgExtension::Zext => {
@@ -74,7 +95,12 @@ impl ArgAttributesExt for ArgAttributes {
7495
}
7596
}
7697

77-
fn apply_attrs_to_callsite(&self, idx: AttributePlace, callsite: &Value) {
98+
fn apply_attrs_to_callsite(
99+
&self,
100+
idx: AttributePlace,
101+
cx: &CodegenCx<'_, '_>,
102+
callsite: &Value,
103+
) {
78104
let mut regular = self.regular;
79105
unsafe {
80106
let deref = self.pointee_size.bytes();
@@ -98,6 +124,9 @@ impl ArgAttributesExt for ArgAttributes {
98124
);
99125
}
100126
regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite));
127+
if regular.contains(ArgAttribute::NoAliasMutRef) && should_use_mutable_noalias(cx) {
128+
llvm::Attribute::NoAlias.apply_callsite(idx, callsite);
129+
}
101130
match self.arg_ext {
102131
ArgExtension::None => {}
103132
ArgExtension::Zext => {
@@ -419,13 +448,13 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
419448

420449
let mut i = 0;
421450
let mut apply = |attrs: &ArgAttributes| {
422-
attrs.apply_attrs_to_llfn(llvm::AttributePlace::Argument(i), llfn);
451+
attrs.apply_attrs_to_llfn(llvm::AttributePlace::Argument(i), cx, llfn);
423452
i += 1;
424453
i - 1
425454
};
426455
match self.ret.mode {
427456
PassMode::Direct(ref attrs) => {
428-
attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, llfn);
457+
attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);
429458
}
430459
PassMode::Indirect { ref attrs, extra_attrs: _, on_stack } => {
431460
assert!(!on_stack);
@@ -480,18 +509,18 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
480509
// FIXME(wesleywiser, eddyb): We should apply `nounwind` and `noreturn` as appropriate to this callsite.
481510

482511
let mut i = 0;
483-
let mut apply = |attrs: &ArgAttributes| {
484-
attrs.apply_attrs_to_callsite(llvm::AttributePlace::Argument(i), callsite);
512+
let mut apply = |cx: &CodegenCx<'_, '_>, attrs: &ArgAttributes| {
513+
attrs.apply_attrs_to_callsite(llvm::AttributePlace::Argument(i), cx, callsite);
485514
i += 1;
486515
i - 1
487516
};
488517
match self.ret.mode {
489518
PassMode::Direct(ref attrs) => {
490-
attrs.apply_attrs_to_callsite(llvm::AttributePlace::ReturnValue, callsite);
519+
attrs.apply_attrs_to_callsite(llvm::AttributePlace::ReturnValue, &bx.cx, callsite);
491520
}
492521
PassMode::Indirect { ref attrs, extra_attrs: _, on_stack } => {
493522
assert!(!on_stack);
494-
let i = apply(attrs);
523+
let i = apply(bx.cx, attrs);
495524
unsafe {
496525
llvm::LLVMRustAddStructRetCallSiteAttr(
497526
callsite,
@@ -517,12 +546,12 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
517546
}
518547
for arg in &self.args {
519548
if arg.pad.is_some() {
520-
apply(&ArgAttributes::new());
549+
apply(bx.cx, &ArgAttributes::new());
521550
}
522551
match arg.mode {
523552
PassMode::Ignore => {}
524553
PassMode::Indirect { ref attrs, extra_attrs: None, on_stack: true } => {
525-
let i = apply(attrs);
554+
let i = apply(bx.cx, attrs);
526555
unsafe {
527556
llvm::LLVMRustAddByValCallSiteAttr(
528557
callsite,
@@ -533,22 +562,22 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
533562
}
534563
PassMode::Direct(ref attrs)
535564
| PassMode::Indirect { ref attrs, extra_attrs: None, on_stack: false } => {
536-
apply(attrs);
565+
apply(bx.cx, attrs);
537566
}
538567
PassMode::Indirect {
539568
ref attrs,
540569
extra_attrs: Some(ref extra_attrs),
541570
on_stack: _,
542571
} => {
543-
apply(attrs);
544-
apply(extra_attrs);
572+
apply(bx.cx, attrs);
573+
apply(bx.cx, extra_attrs);
545574
}
546575
PassMode::Pair(ref a, ref b) => {
547-
apply(a);
548-
apply(b);
576+
apply(bx.cx, a);
577+
apply(bx.cx, b);
549578
}
550579
PassMode::Cast(_) => {
551-
apply(&ArgAttributes::new());
580+
apply(bx.cx, &ArgAttributes::new());
552581
}
553582
}
554583
}

compiler/rustc_interface/src/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ fn test_debugging_options_tracking_hash() {
566566
tracked!(merge_functions, Some(MergeFunctions::Disabled));
567567
tracked!(mir_emit_retag, true);
568568
tracked!(mir_opt_level, Some(4));
569-
tracked!(mutable_noalias, true);
569+
tracked!(mutable_noalias, Some(true));
570570
tracked!(new_llvm_pass_manager, true);
571571
tracked!(no_codegen, true);
572572
tracked!(no_generate_arange_section, true);

compiler/rustc_middle/src/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,10 @@ rustc_queries! {
993993
query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
994994
desc { "computing whether `{}` is freeze", env.value }
995995
}
996+
/// Query backing `TyS::is_unpin`.
997+
query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
998+
desc { "computing whether `{}` is `Unpin`", env.value }
999+
}
9961000
/// Query backing `TyS::needs_drop`.
9971001
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
9981002
desc { "computing whether `{}` needs drop", env.value }

compiler/rustc_middle/src/ty/layout.rs

+34-27
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_hir as hir;
1111
use rustc_hir::lang_items::LangItem;
1212
use rustc_index::bit_set::BitSet;
1313
use rustc_index::vec::{Idx, IndexVec};
14-
use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
14+
use rustc_session::{config::OptLevel, DataTypeKind, FieldInfo, SizeKind, VariantInfo};
1515
use rustc_span::symbol::{Ident, Symbol};
1616
use rustc_span::DUMMY_SP;
1717
use rustc_target::abi::call::{
@@ -2318,31 +2318,30 @@ where
23182318
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
23192319
let address_space = addr_space_of_ty(ty);
23202320
let tcx = cx.tcx();
2321-
let is_freeze = ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env());
2322-
let kind = match mt {
2323-
hir::Mutability::Not => {
2324-
if is_freeze {
2325-
PointerKind::Frozen
2326-
} else {
2327-
PointerKind::Shared
2321+
let kind = if tcx.sess.opts.optimize == OptLevel::No {
2322+
// Use conservative pointer kind if not optimizing. This saves us the
2323+
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
2324+
// attributes in LLVM have compile-time cost even in unoptimized builds).
2325+
PointerKind::Shared
2326+
} else {
2327+
match mt {
2328+
hir::Mutability::Not => {
2329+
if ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()) {
2330+
PointerKind::Frozen
2331+
} else {
2332+
PointerKind::Shared
2333+
}
23282334
}
2329-
}
2330-
hir::Mutability::Mut => {
2331-
// Previously we would only emit noalias annotations for LLVM >= 6 or in
2332-
// panic=abort mode. That was deemed right, as prior versions had many bugs
2333-
// in conjunction with unwinding, but later versions didn’t seem to have
2334-
// said issues. See issue #31681.
2335-
//
2336-
// Alas, later on we encountered a case where noalias would generate wrong
2337-
// code altogether even with recent versions of LLVM in *safe* code with no
2338-
// unwinding involved. See #54462.
2339-
//
2340-
// For now, do not enable mutable_noalias by default at all, while the
2341-
// issue is being figured out.
2342-
if tcx.sess.opts.debugging_opts.mutable_noalias {
2343-
PointerKind::UniqueBorrowed
2344-
} else {
2345-
PointerKind::Shared
2335+
hir::Mutability::Mut => {
2336+
// References to self-referential structures should not be considered
2337+
// noalias, as another pointer to the structure can be obtained, that
2338+
// is not based-on the original reference. We consider all !Unpin
2339+
// types to be potentially self-referential here.
2340+
if ty.is_unpin(tcx.at(DUMMY_SP), cx.param_env()) {
2341+
PointerKind::UniqueBorrowed
2342+
} else {
2343+
PointerKind::Shared
2344+
}
23462345
}
23472346
}
23482347
};
@@ -2775,10 +2774,14 @@ where
27752774
// and can be marked as both `readonly` and `noalias`, as
27762775
// LLVM's definition of `noalias` is based solely on memory
27772776
// dependencies rather than pointer equality
2777+
//
2778+
// Due to miscompiles in LLVM < 12, we apply a separate NoAliasMutRef attribute
2779+
// for UniqueBorrowed arguments, so that the codegen backend can decide
2780+
// whether or not to actually emit the attribute.
27782781
let no_alias = match kind {
2779-
PointerKind::Shared => false,
2782+
PointerKind::Shared | PointerKind::UniqueBorrowed => false,
27802783
PointerKind::UniqueOwned => true,
2781-
PointerKind::Frozen | PointerKind::UniqueBorrowed => !is_return,
2784+
PointerKind::Frozen => !is_return,
27822785
};
27832786
if no_alias {
27842787
attrs.set(ArgAttribute::NoAlias);
@@ -2787,6 +2790,10 @@ where
27872790
if kind == PointerKind::Frozen && !is_return {
27882791
attrs.set(ArgAttribute::ReadOnly);
27892792
}
2793+
2794+
if kind == PointerKind::UniqueBorrowed && !is_return {
2795+
attrs.set(ArgAttribute::NoAliasMutRef);
2796+
}
27902797
}
27912798
}
27922799
};

compiler/rustc_middle/src/ty/util.rs

+40
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,46 @@ impl<'tcx> ty::TyS<'tcx> {
741741
}
742742
}
743743

744+
/// Checks whether values of this type `T` implement the `Unpin` trait.
745+
pub fn is_unpin(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
746+
self.is_trivially_unpin() || tcx_at.is_unpin_raw(param_env.and(self))
747+
}
748+
749+
/// Fast path helper for testing if a type is `Unpin`.
750+
///
751+
/// Returning true means the type is known to be `Unpin`. Returning
752+
/// `false` means nothing -- could be `Unpin`, might not be.
753+
fn is_trivially_unpin(&self) -> bool {
754+
match self.kind() {
755+
ty::Int(_)
756+
| ty::Uint(_)
757+
| ty::Float(_)
758+
| ty::Bool
759+
| ty::Char
760+
| ty::Str
761+
| ty::Never
762+
| ty::Ref(..)
763+
| ty::RawPtr(_)
764+
| ty::FnDef(..)
765+
| ty::Error(_)
766+
| ty::FnPtr(_) => true,
767+
ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_unpin),
768+
ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_unpin(),
769+
ty::Adt(..)
770+
| ty::Bound(..)
771+
| ty::Closure(..)
772+
| ty::Dynamic(..)
773+
| ty::Foreign(_)
774+
| ty::Generator(..)
775+
| ty::GeneratorWitness(_)
776+
| ty::Infer(_)
777+
| ty::Opaque(..)
778+
| ty::Param(_)
779+
| ty::Placeholder(_)
780+
| ty::Projection(_) => false,
781+
}
782+
}
783+
744784
/// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely
745785
/// non-copy and *might* have a destructor attached; if it returns
746786
/// `false`, then `ty` definitely has no destructor (i.e., no drop glue).

compiler/rustc_session/src/options.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -997,8 +997,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
997997
(default: no)"),
998998
mir_opt_level: Option<usize> = (None, parse_opt_uint, [TRACKED],
999999
"MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
1000-
mutable_noalias: bool = (false, parse_bool, [TRACKED],
1001-
"emit noalias metadata for mutable references (default: no)"),
1000+
mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
1001+
"emit noalias metadata for mutable references (default: yes for LLVM >= 12, otherwise no)"),
10021002
new_llvm_pass_manager: bool = (false, parse_bool, [TRACKED],
10031003
"use new LLVM pass manager (default: no)"),
10041004
nll_facts: bool = (false, parse_bool, [UNTRACKED],

compiler/rustc_target/src/abi/call/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ mod attr_impl {
6565
const NoCapture = 1 << 2;
6666
const NonNull = 1 << 3;
6767
const ReadOnly = 1 << 4;
68-
const InReg = 1 << 8;
68+
const InReg = 1 << 5;
69+
// NoAlias on &mut arguments can only be used with LLVM >= 12 due to miscompiles
70+
// in earlier versions. FIXME: Remove this distinction once possible.
71+
const NoAliasMutRef = 1 << 6;
6972
}
7073
}
7174
}

compiler/rustc_target/src/abi/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ pub enum PointerKind {
11121112
/// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`.
11131113
Frozen,
11141114

1115-
/// `&mut T`, when we know `noalias` is safe for LLVM.
1115+
/// `&mut T` which is `noalias` but not `readonly`.
11161116
UniqueBorrowed,
11171117

11181118
/// `Box<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns.

compiler/rustc_ty_utils/src/common_traits.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
1818
is_item_raw(tcx, query, LangItem::Freeze)
1919
}
2020

21+
fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
22+
is_item_raw(tcx, query, LangItem::Unpin)
23+
}
24+
2125
fn is_item_raw<'tcx>(
2226
tcx: TyCtxt<'tcx>,
2327
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -37,5 +41,11 @@ fn is_item_raw<'tcx>(
3741
}
3842

3943
pub(crate) fn provide(providers: &mut ty::query::Providers) {
40-
*providers = ty::query::Providers { is_copy_raw, is_sized_raw, is_freeze_raw, ..*providers };
44+
*providers = ty::query::Providers {
45+
is_copy_raw,
46+
is_sized_raw,
47+
is_freeze_raw,
48+
is_unpin_raw,
49+
..*providers
50+
};
4151
}

0 commit comments

Comments
 (0)