Skip to content

Apply noundef attribute to &T, &mut T, Box<T>, bool #93670

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

Merged
merged 5 commits into from
Feb 13, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
6 changes: 5 additions & 1 deletion compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl ArgAttributeExt for ArgAttribute {
where
F: FnMut(llvm::Attribute),
{
for_each_kind!(self, f, NoAlias, NoCapture, NonNull, ReadOnly, InReg)
for_each_kind!(self, f, NoAlias, NoCapture, NonNull, ReadOnly, InReg, NoUndef)
}
}

Expand Down Expand Up @@ -69,7 +69,9 @@ impl ArgAttributesExt for ArgAttributes {
} else {
llvm::LLVMRustAddDereferenceableOrNullAttr(llfn, idx.as_uint(), deref);
}
// dereferenceable implies nonnull noundef; dereferenceable_or_null implies noundef
regular -= ArgAttribute::NonNull;
regular -= ArgAttribute::NoUndef;
}
if let Some(align) = self.pointee_align {
llvm::LLVMRustAddAlignmentAttr(llfn, idx.as_uint(), align.bytes() as u32);
Expand Down Expand Up @@ -109,7 +111,9 @@ impl ArgAttributesExt for ArgAttributes {
deref,
);
}
// dereferenceable implies nonnull noundef; dereferenceable_or_null implies noundef
regular -= ArgAttribute::NonNull;
regular -= ArgAttribute::NoUndef;
}
if let Some(align) = self.pointee_align {
llvm::LLVMRustAddAlignmentCallSiteAttr(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ pub enum Attribute {
StackProtectReq = 30,
StackProtectStrong = 31,
StackProtect = 32,
NoUndef = 33,
}

/// LLVMIntPredicate
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ enum LLVMRustAttribute {
StackProtectReq = 30,
StackProtectStrong = 31,
StackProtect = 32,
NoUndef = 33,
};

typedef struct OpaqueRustString *RustStringRef;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
return Attribute::StackProtectStrong;
case StackProtect:
return Attribute::StackProtect;
case NoUndef:
return Attribute::NoUndef;
}
report_fatal_error("bad AttributeKind");
}
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3048,9 +3048,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
layout: TyAndLayout<'tcx>,
offset: Size,
is_return: bool| {
// Booleans are always an i1 that needs to be zero-extended.
// Booleans are always a noundef i1 that needs to be zero-extended.
if scalar.is_bool() {
attrs.ext(ArgExtension::Zext);
attrs.set(ArgAttribute::NoUndef);
return;
}

Expand All @@ -3075,6 +3076,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
_ => pointee.size,
};

// `Box`, `&T`, and `&mut T` cannot be undef.
// Note that this only applies to the value of the pointer itself;
// this attribute doesn't make it UB for the pointed-to data to be undef.
attrs.set(ArgAttribute::NoUndef);

// `Box` pointer parameters never alias because ownership is transferred
// `&mut` pointer parameters never alias other parameters,
// or mutable global data
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_target/src/abi/call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ mod attr_impl {
// or not to actually emit the attribute. It can also be controlled
// with the `-Zmutable-noalias` debugging option.
const NoAliasMutRef = 1 << 6;
const NoUndef = 1 << 7;
}
}
}
Expand Down Expand Up @@ -494,7 +495,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
// For non-immediate arguments the callee gets its own copy of
// the value on the stack, so there are no aliases. It's also
// program-invisible so can't possibly capture
attrs.set(ArgAttribute::NoAlias).set(ArgAttribute::NoCapture).set(ArgAttribute::NonNull);
attrs
.set(ArgAttribute::NoAlias)
.set(ArgAttribute::NoCapture)
.set(ArgAttribute::NonNull)
.set(ArgAttribute::NoUndef);
attrs.pointee_size = layout.size;
// FIXME(eddyb) We should be doing this, but at least on
// i686-pc-windows-msvc, it results in wrong stack offsets.
Expand Down
37 changes: 26 additions & 11 deletions src/test/codegen/function-arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub struct UnsafeInner {
_field: std::cell::UnsafeCell<i16>,
}

// CHECK: zeroext i1 @boolean(i1 zeroext %x)
// CHECK: noundef zeroext i1 @boolean(i1 noundef zeroext %x)
#[no_mangle]
pub fn boolean(x: bool) -> bool {
x
Expand Down Expand Up @@ -64,9 +64,14 @@ pub fn indirect_struct(_: S) {
pub fn borrowed_struct(_: &S) {
}

// CHECK: @raw_struct(%S* %_1)
#[no_mangle]
pub fn raw_struct(_: *const S) {
}

// `Box` can get deallocated during execution of the function, so it should
// not get `dereferenceable`.
// CHECK: noalias nonnull align 4 i32* @_box(i32* noalias nonnull align 4 %x)
// CHECK: noalias noundef nonnull align 4 i32* @_box(i32* noalias noundef nonnull align 4 %x)
#[no_mangle]
pub fn _box(x: Box<i32>) -> Box<i32> {
x
Expand All @@ -86,48 +91,58 @@ pub fn struct_return() -> S {
pub fn helper(_: usize) {
}

// CHECK: @slice([0 x i8]* noalias nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
// CHECK: @slice([0 x i8]* noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn slice(_: &[u8]) {
}

// CHECK: @mutable_slice([0 x i8]* noalias nonnull align 1 %_1.0, [[USIZE]] %_1.1)
// CHECK: @mutable_slice([0 x i8]* noalias noundef nonnull align 1 %_1.0, [[USIZE]] %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn mutable_slice(_: &mut [u8]) {
}

// CHECK: @unsafe_slice([0 x i16]* nonnull align 2 %_1.0, [[USIZE]] %_1.1)
// CHECK: @unsafe_slice([0 x i16]* noundef nonnull align 2 %_1.0, [[USIZE]] %_1.1)
// unsafe interior means this isn't actually readonly and there may be aliases ...
#[no_mangle]
pub fn unsafe_slice(_: &[UnsafeInner]) {
}

// CHECK: @str([0 x i8]* noalias nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
// CHECK: @raw_slice([0 x i8]* %_1.0, [[USIZE]] %_1.1)
#[no_mangle]
pub fn raw_slice(_: *const [u8]) {
}

// CHECK: @str([0 x i8]* noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn str(_: &[u8]) {
}

// CHECK: @trait_borrow({}* nonnull align 1 %_1.0, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// CHECK: @trait_borrow({}* noundef nonnull align 1 %_1.0, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn trait_borrow(_: &Drop) {
}

// CHECK: @trait_box({}* noalias nonnull align 1{{( %0)?}}, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
// CHECK: @trait_raw({}* %_1.0, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
#[no_mangle]
pub fn trait_raw(_: *const Drop) {
}

// CHECK: @trait_box({}* noalias noundef nonnull align 1{{( %0)?}}, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box(_: Box<Drop>) {
}

// CHECK: { i8*, i8* } @trait_option(i8* noalias align 1 %x.0, i8* %x.1)
// CHECK: { i8*, i8* } @trait_option(i8* noalias noundef align 1 %x.0, i8* %x.1)
#[no_mangle]
pub fn trait_option(x: Option<Box<Drop>>) -> Option<Box<Drop>> {
x
}

// CHECK: { [0 x i16]*, [[USIZE]] } @return_slice([0 x i16]* noalias nonnull readonly align 2 %x.0, [[USIZE]] %x.1)
// CHECK: { [0 x i16]*, [[USIZE]] } @return_slice([0 x i16]* noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] %x.1)
#[no_mangle]
pub fn return_slice(x: &[u16]) -> &[u16] {
x
Expand All @@ -139,7 +154,7 @@ pub fn enum_id_1(x: Option<Result<u16, u16>>) -> Option<Result<u16, u16>> {
x
}

// CHECK: { i8, i8 } @enum_id_2(i1 zeroext %x.0, i8 %x.1)
// CHECK: { i8, i8 } @enum_id_2(i1 noundef zeroext %x.0, i8 %x.1)
#[no_mangle]
pub fn enum_id_2(x: Option<u8>) -> Option<u8> {
x
Expand Down
10 changes: 5 additions & 5 deletions src/test/codegen/scalar-pair-bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@

#![crate_type = "lib"]

// CHECK: define{{.*}}{ i8, i8 } @pair_bool_bool(i1 zeroext %pair.0, i1 zeroext %pair.1)
// CHECK: define{{.*}}{ i8, i8 } @pair_bool_bool(i1 noundef zeroext %pair.0, i1 noundef zeroext %pair.1)
#[no_mangle]
pub fn pair_bool_bool(pair: (bool, bool)) -> (bool, bool) {
pair
}

// CHECK: define{{.*}}{ i8, i32 } @pair_bool_i32(i1 zeroext %pair.0, i32 %pair.1)
// CHECK: define{{.*}}{ i8, i32 } @pair_bool_i32(i1 noundef zeroext %pair.0, i32 %pair.1)
#[no_mangle]
pub fn pair_bool_i32(pair: (bool, i32)) -> (bool, i32) {
pair
}

// CHECK: define{{.*}}{ i32, i8 } @pair_i32_bool(i32 %pair.0, i1 zeroext %pair.1)
// CHECK: define{{.*}}{ i32, i8 } @pair_i32_bool(i32 %pair.0, i1 noundef zeroext %pair.1)
#[no_mangle]
pub fn pair_i32_bool(pair: (i32, bool)) -> (i32, bool) {
pair
}

// CHECK: define{{.*}}{ i8, i8 } @pair_and_or(i1 zeroext %_1.0, i1 zeroext %_1.1)
// CHECK: define{{.*}}{ i8, i8 } @pair_and_or(i1 noundef zeroext %_1.0, i1 noundef zeroext %_1.1)
#[no_mangle]
pub fn pair_and_or((a, b): (bool, bool)) -> (bool, bool) {
// Make sure it can operate directly on the unpacked args
Expand All @@ -30,7 +30,7 @@ pub fn pair_and_or((a, b): (bool, bool)) -> (bool, bool) {
(a && b, a || b)
}

// CHECK: define{{.*}}void @pair_branches(i1 zeroext %_1.0, i1 zeroext %_1.1)
// CHECK: define{{.*}}void @pair_branches(i1 noundef zeroext %_1.0, i1 noundef zeroext %_1.1)
#[no_mangle]
pub fn pair_branches((a, b): (bool, bool)) {
// Make sure it can branch directly on the unpacked bool args
Expand Down
4 changes: 2 additions & 2 deletions src/test/codegen/transmute-scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn f32_to_bits(x: f32) -> u32 {
unsafe { std::mem::transmute(x) }
}

// CHECK-LABEL: define{{.*}}i8 @bool_to_byte(i1 zeroext %b)
// CHECK-LABEL: define{{.*}}i8 @bool_to_byte(i1 noundef zeroext %b)
// CHECK: %1 = zext i1 %b to i8
// CHECK-NEXT: store i8 %1, i8* %0
// CHECK-NEXT: %2 = load i8, i8* %0
Expand All @@ -25,7 +25,7 @@ pub fn bool_to_byte(b: bool) -> u8 {
unsafe { std::mem::transmute(b) }
}

// CHECK-LABEL: define{{.*}}zeroext i1 @byte_to_bool(i8 %byte)
// CHECK-LABEL: define{{.*}}noundef zeroext i1 @byte_to_bool(i8 %byte)
// CHECK: %1 = trunc i8 %byte to i1
// CHECK-NEXT: %2 = zext i1 %1 to i8
// CHECK-NEXT: store i8 %2, i8* %0
Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/union-abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub union CUnionU128x2{a:(u128, u128)}
pub fn test_CUnionU128x2(_: CUnionU128x2) { loop {} }

pub union UnionBool { b:bool }
// CHECK: define zeroext i1 @test_UnionBool(i8 %b)
// CHECK: define noundef zeroext i1 @test_UnionBool(i8 %b)
#[no_mangle]
pub fn test_UnionBool(b: UnionBool) -> bool { unsafe { b.b } }
// CHECK: %0 = trunc i8 %b to i1