Skip to content

[Clang] Add noalias to this pointer in C++ constructors #136792

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
9 changes: 7 additions & 2 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2731,8 +2731,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
llvm::AttributeSet::get(getLLVMContext(), Attrs);
}

// Apply `nonnull`, `dereferenceable(N)` and `align N` to the `this` argument,
// unless this is a thunk function.
// Apply `nonnull`, `dereferenceable(N)`, `align N` (and `noalias` for
// constructors) to the `this` argument, unless this is a thunk function.
// FIXME: fix this properly, https://reviews.llvm.org/D100388
if (FI.isInstanceMethod() && !IRFunctionArgs.hasInallocaArg() &&
!FI.arg_begin()->type->isVoidPointerType() && !IsThunk) {
Expand All @@ -2744,6 +2744,11 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,

QualType ThisTy = FI.arg_begin()->type.getTypePtr()->getPointeeType();

// According to [class.cdtor]/2, the value of the object is unspecified if
// its elements are accessed not through `this`.
if (isa_and_nonnull<CXXConstructorDecl>(TargetDecl))
Attrs.addAttribute(llvm::Attribute::NoAlias);

if (!CodeGenOpts.NullPointerIsValid &&
getTypes().getTargetAddressSpace(FI.arg_begin()->type) == 0) {
Attrs.addAttribute(llvm::Attribute::NonNull);
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGen/attr-counted-by-pr88931.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct foo {
void init(void * __attribute__((pass_dynamic_object_size(0))));

// CHECK-LABEL: define dso_local void @_ZN3foo3barC1Ev(
// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(1) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
// CHECK-SAME: ptr noalias noundef nonnull align 4 dereferenceable(1) [[THIS:%.*]]) unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
// CHECK-NEXT: entry:
// CHECK-NEXT: tail call void @_Z4initPvU25pass_dynamic_object_size0(ptr noundef nonnull align 4 dereferenceable(1) [[THIS]], i64 noundef -1) #[[ATTR2:[0-9]+]]
// CHECK-NEXT: ret void
Expand Down
303 changes: 152 additions & 151 deletions clang/test/CodeGen/attr-noundef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,157 +10,158 @@
// TODO: No structs may currently be marked noundef

namespace check_structs {
struct Trivial {
int a;
};
Trivial ret_trivial() { return {}; }
void pass_trivial(Trivial e) {}
// CHECK-INTEL: [[DEF:define( dso_local)?]] i32 @{{.*}}ret_trivial
// CHECK-AARCH: [[DEF:define( dso_local)?]] i32 @{{.*}}ret_trivial
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i32 %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 %

struct NoCopy {
int a;
NoCopy(NoCopy &) = delete;
};
NoCopy ret_nocopy() { return {}; }
void pass_nocopy(NoCopy e) {}
// CHECK: [[DEF]] void @{{.*}}ret_nocopy{{.*}}(ptr dead_on_unwind noalias writable sret({{[^)]+}}) align 4 %
// CHECK: [[DEF]] void @{{.*}}pass_nocopy{{.*}}(ptr noundef %

struct Huge {
int a[1024];
};
Huge ret_huge() { return {}; }
void pass_huge(Huge h) {}
// CHECK: [[DEF]] void @{{.*}}ret_huge{{.*}}(ptr dead_on_unwind noalias writable sret({{[^)]+}}) align 4 %
// CHECK: [[DEF]] void @{{.*}}pass_huge{{.*}}(ptr noundef
} // namespace check_structs

//************ Passing unions by value
// No unions may be marked noundef

namespace check_unions {
union Trivial {
int a;
};
Trivial ret_trivial() { return {}; }
void pass_trivial(Trivial e) {}
// CHECK-INTEL: [[DEF]] i32 @{{.*}}ret_trivial
// CHECK-AARCH: [[DEF]] i32 @{{.*}}ret_trivial
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i32 %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 %

union NoCopy {
int a;
NoCopy(NoCopy &) = delete;
};
NoCopy ret_nocopy() { return {}; }
void pass_nocopy(NoCopy e) {}
// CHECK: [[DEF]] void @{{.*}}ret_nocopy{{.*}}(ptr dead_on_unwind noalias writable sret({{[^)]+}}) align 4 %
// CHECK: [[DEF]] void @{{.*}}pass_nocopy{{.*}}(ptr noundef %
} // namespace check_unions

//************ Passing `this` pointers
// `this` pointer must always be defined

namespace check_this {
struct Object {
int data[];

Object() {
this->data[0] = 0;
struct Trivial {
int a;
};
Trivial ret_trivial() { return {}; }
void pass_trivial(Trivial e) {}
// CHECK-INTEL: [[DEF:define( dso_local)?]] i32 @{{.*}}ret_trivial
// CHECK-AARCH: [[DEF:define( dso_local)?]] i32 @{{.*}}ret_trivial
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i32 %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 %

struct NoCopy {
int a;
NoCopy(NoCopy &) = delete;
};
NoCopy ret_nocopy() { return {}; }
void pass_nocopy(NoCopy e) {}
// CHECK: [[DEF]] void @{{.*}}ret_nocopy{{.*}}(ptr dead_on_unwind noalias writable sret({{[^)]+}}) align 4 %
// CHECK: [[DEF]] void @{{.*}}pass_nocopy{{.*}}(ptr noundef %

struct Huge {
int a[1024];
};
Huge ret_huge() { return {}; }
void pass_huge(Huge h) {}
// CHECK: [[DEF]] void @{{.*}}ret_huge{{.*}}(ptr dead_on_unwind noalias writable sret({{[^)]+}}) align 4 %
// CHECK: [[DEF]] void @{{.*}}pass_huge{{.*}}(ptr noundef
} // namespace check_structs

//************ Passing unions by value
// No unions may be marked noundef

namespace check_unions {
union Trivial {
int a;
};
Trivial ret_trivial() { return {}; }
void pass_trivial(Trivial e) {}
// CHECK-INTEL: [[DEF]] i32 @{{.*}}ret_trivial
// CHECK-AARCH: [[DEF]] i32 @{{.*}}ret_trivial
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i32 %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 %

union NoCopy {
int a;
NoCopy(NoCopy &) = delete;
};
NoCopy ret_nocopy() { return {}; }
void pass_nocopy(NoCopy e) {}
// CHECK: [[DEF]] void @{{.*}}ret_nocopy{{.*}}(ptr dead_on_unwind noalias writable sret({{[^)]+}}) align 4 %
// CHECK: [[DEF]] void @{{.*}}pass_nocopy{{.*}}(ptr noundef %
} // namespace check_unions

//************ Passing `this` pointers
// `this` pointer must always be defined

namespace check_this {
struct Object {
int data[];

Object() {
this->data[0] = 0;
}
int getData() {
return this->data[0];
}
Object *getThis() {
return this;
}
};

void use_object() {
Object obj;
obj.getData();
obj.getThis();
}
int getData() {
return this->data[0];
// CHECK: define linkonce_odr void @{{.*}}Object{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(1) %
// CHECK: define linkonce_odr noundef i32 @{{.*}}Object{{.*}}getData{{.*}}(ptr noundef nonnull align 4 dereferenceable(1) %
// CHECK: define linkonce_odr noundef ptr @{{.*}}Object{{.*}}getThis{{.*}}(ptr noundef nonnull align 4 dereferenceable(1) %
} // namespace check_this

//************ Passing vector types

namespace check_vecs {
typedef int __attribute__((vector_size(12))) i32x3;
i32x3 ret_vec() {
return {};
}
Object *getThis() {
return this;
void pass_vec(i32x3 v) {
}
};

void use_object() {
Object obj;
obj.getData();
obj.getThis();
}
// CHECK: define linkonce_odr void @{{.*}}Object{{.*}}(ptr noundef nonnull align 4 dereferenceable(1) %
// CHECK: define linkonce_odr noundef i32 @{{.*}}Object{{.*}}getData{{.*}}(ptr noundef nonnull align 4 dereferenceable(1) %
// CHECK: define linkonce_odr noundef ptr @{{.*}}Object{{.*}}getThis{{.*}}(ptr noundef nonnull align 4 dereferenceable(1) %
} // namespace check_this

//************ Passing vector types

namespace check_vecs {
typedef int __attribute__((vector_size(12))) i32x3;
i32x3 ret_vec() {
return {};
}
void pass_vec(i32x3 v) {
}

// CHECK: [[DEF]] noundef <3 x i32> @{{.*}}ret_vec{{.*}}()
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_vec{{.*}}(<3 x i32> noundef %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_vec{{.*}}(<4 x i32> %
} // namespace check_vecs

//************ Passing exotic types
// Function/Array pointers, Function member / Data member pointers, nullptr_t, ExtInt types

namespace check_exotic {
struct Object {
int mfunc();
int mdata;
};
typedef int Object::*mdptr;
typedef int (Object::*mfptr)();
typedef decltype(nullptr) nullptr_t;
typedef int (*arrptr)[32];
typedef int (*fnptr)(int);

arrptr ret_arrptr() {
return nullptr;
}
fnptr ret_fnptr() {
return nullptr;
}
mdptr ret_mdptr() {
return nullptr;
}
mfptr ret_mfptr() {
return nullptr;
}
nullptr_t ret_npt() {
return nullptr;
}
void pass_npt(nullptr_t t) {
}
_BitInt(3) ret_BitInt() {
return 0;
}
void pass_BitInt(_BitInt(3) e) {
}
void pass_large_BitInt(_BitInt(127) e) {
}

// Pointers to arrays/functions are always noundef
// CHECK: [[DEF]] noundef ptr @{{.*}}ret_arrptr{{.*}}()
// CHECK: [[DEF]] noundef ptr @{{.*}}ret_fnptr{{.*}}()

// Pointers to members are never noundef
// CHECK: [[DEF]] i64 @{{.*}}ret_mdptr{{.*}}()
// CHECK-INTEL: [[DEF]] { i64, i64 } @{{.*}}ret_mfptr{{.*}}()
// CHECK-AARCH: [[DEF]] [2 x i64] @{{.*}}ret_mfptr{{.*}}()

// nullptr_t is never noundef
// CHECK: [[DEF]] ptr @{{.*}}ret_npt{{.*}}()
// CHECK: [[DEF]] void @{{.*}}pass_npt{{.*}}(ptr %

// CHECK-INTEL: [[DEF]] noundef signext i3 @{{.*}}ret_BitInt{{.*}}()
// CHECK-AARCH: [[DEF]] noundef i3 @{{.*}}ret_BitInt{{.*}}()
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_BitInt{{.*}}(i3 noundef signext %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_BitInt{{.*}}(i3 noundef %
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_large_BitInt{{.*}}(i64 noundef %{{.*}}, i64 noundef %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_large_BitInt{{.*}}(i127 noundef %
} // namespace check_exotic

// CHECK: [[DEF]] noundef <3 x i32> @{{.*}}ret_vec{{.*}}()
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_vec{{.*}}(<3 x i32> noundef %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_vec{{.*}}(<4 x i32> %
} // namespace check_vecs

//************ Passing exotic types
// Function/Array pointers, Function member / Data member pointers, nullptr_t, ExtInt types

namespace check_exotic {
struct Object {
int mfunc();
int mdata;
};
typedef int Object::*mdptr;
typedef int (Object::*mfptr)();
typedef decltype(nullptr) nullptr_t;
typedef int (*arrptr)[32];
typedef int (*fnptr)(int);

arrptr ret_arrptr() {
return nullptr;
}
fnptr ret_fnptr() {
return nullptr;
}
mdptr ret_mdptr() {
return nullptr;
}
mfptr ret_mfptr() {
return nullptr;
}
nullptr_t ret_npt() {
return nullptr;
}
void pass_npt(nullptr_t t) {
}
_BitInt(3) ret_BitInt() {
return 0;
}
void pass_BitInt(_BitInt(3) e) {
}
void pass_large_BitInt(_BitInt(127) e) {
}

// Pointers to arrays/functions are always noundef
// CHECK: [[DEF]] noundef ptr @{{.*}}ret_arrptr{{.*}}()
// CHECK: [[DEF]] noundef ptr @{{.*}}ret_fnptr{{.*}}()

// Pointers to members are never noundef
// CHECK: [[DEF]] i64 @{{.*}}ret_mdptr{{.*}}()
// CHECK-INTEL: [[DEF]] { i64, i64 } @{{.*}}ret_mfptr{{.*}}()
// CHECK-AARCH: [[DEF]] [2 x i64] @{{.*}}ret_mfptr{{.*}}()

// nullptr_t is never noundef
// CHECK: [[DEF]] ptr @{{.*}}ret_npt{{.*}}()
// CHECK: [[DEF]] void @{{.*}}pass_npt{{.*}}(ptr %

// CHECK-INTEL: [[DEF]] noundef signext i3 @{{.*}}ret_BitInt{{.*}}()
// CHECK-AARCH: [[DEF]] noundef i3 @{{.*}}ret_BitInt{{.*}}()
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_BitInt{{.*}}(i3 noundef signext %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_BitInt{{.*}}(i3 noundef %
// CHECK-INTEL: [[DEF]] void @{{.*}}pass_large_BitInt{{.*}}(i64 noundef %{{.*}}, i64 noundef %
// CHECK-AARCH: [[DEF]] void @{{.*}}pass_large_BitInt{{.*}}(i127 noundef %
} // namespace check_exotic

8 changes: 4 additions & 4 deletions clang/test/CodeGen/paren-list-agg-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,9 @@ namespace gh61145 {
// CHECK-NEXT: [[V:%.*v.*]] = alloca [[STRUCT_VEC]], align 1
// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*agg.tmp.ensured.*]] = alloca [[STRUCT_S1]], align 1
// a.k.a. Vec::Vec()
// CHECK-NEXT: call void @_ZN7gh611453VecC1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: call void @_ZN7gh611453VecC1Ev(ptr noalias noundef nonnull align 1 dereferenceable(1) [[V]])
// a.k.a. Vec::Vec(Vec&&)
// CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noalias noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// a.k.a. S1::~S1()
// CHECK-NEXT: call void @_ZN7gh611452S1D1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]])
// a.k.a.Vec::~Vec()
Expand All @@ -410,9 +410,9 @@ namespace gh61145 {
// CHECK-NEXT: [[V:%.*v.*]] = alloca [[STRUCT_VEC]], align 1
// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*agg.tmp.ensured.*]] = alloca [[STRUCT_S2]], align 1
// a.k.a. Vec::Vec()
// CHECK-NEXT: call void @_ZN7gh611453VecC1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: call void @_ZN7gh611453VecC1Ev(ptr noalias noundef nonnull align 1 dereferenceable(1) [[V]])
// a.k.a. Vec::Vec(Vec&&)
// CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: call void @_ZN7gh611453VecC1EOS0_(ptr noalias noundef nonnull align 1 dereferenceable(1) [[AGG_TMP_ENSURED]], ptr noundef nonnull align 1 dereferenceable(1) [[V]])
// CHECK-NEXT: [[C:%.*c.*]] = getelementptr inbounds nuw [[STRUCT_S2]], ptr [[AGG_TMP_ENSURED]], i32 0, i32
// CHECK-NEXT: store i8 0, ptr [[C]], align 1
// a.k.a. S2::~S2()
Expand Down
Loading