-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Clang][CodeGen] Do not set inbounds flag in EmitMemberDataPointerAddress
when the base pointer is null
#130952
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
Conversation
@llvm/pr-subscribers-clang-codegen Author: Yingwei Zheng (dtcxzyw) ChangesSee also #130734 for the original motivation. This pattern ( llvm-project/llvm/include/llvm/IR/SymbolTableListTraits.h Lines 77 to 87 in 1d89d7d
https://github.com/nodejs/node/blob/a2a53cb728ec5f776ac16879ce0f480a8e838320/src/util-inl.h#L134-L137 https://github.com/search?q=*%29nullptr-%3E*&type=code Full diff: https://github.com/llvm/llvm-project/pull/130952.diff 4 Files Affected:
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index b145da0f0ec09..b2eaea683ebe7 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -872,9 +872,14 @@ llvm::Value *ItaniumCXXABI::EmitMemberDataPointerAddress(
CGBuilderTy &Builder = CGF.Builder;
- // Apply the offset, which we assume is non-null.
- return Builder.CreateInBoundsGEP(CGF.Int8Ty, Base.emitRawPointer(CGF), MemPtr,
- "memptr.offset");
+ // Apply the offset.
+ // Specially, we don't add inbounds flags if the base pointer is a constant
+ // null. This is a workaround for old-style container_of macros.
+ llvm::Value *BaseAddr = Base.emitRawPointer(CGF);
+ return Builder.CreateGEP(CGF.Int8Ty, BaseAddr, MemPtr, "memptr.offset",
+ isa<llvm::ConstantPointerNull>(BaseAddr)
+ ? llvm::GEPNoWrapFlags::none()
+ : llvm::GEPNoWrapFlags::inBounds());
}
// See if it's possible to return a constant signed pointer.
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 4b55fc3f17bd7..3501b2b2fdca8 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -3265,9 +3265,13 @@ llvm::Value *MicrosoftCXXABI::EmitMemberDataPointerAddress(
Addr = Base.emitRawPointer(CGF);
}
- // Apply the offset, which we assume is non-null.
- return Builder.CreateInBoundsGEP(CGF.Int8Ty, Addr, FieldOffset,
- "memptr.offset");
+ // Apply the offset.
+ // Specially, we don't add inbounds flags if the base pointer is a constant
+ // null. This is a workaround for old-style container_of macros.
+ return Builder.CreateGEP(CGF.Int8Ty, Addr, FieldOffset, "memptr.offset",
+ isa<llvm::ConstantPointerNull>(Addr)
+ ? llvm::GEPNoWrapFlags::none()
+ : llvm::GEPNoWrapFlags::inBounds());
}
llvm::Value *
diff --git a/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp b/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
index fc8a31e0350e5..fe4cab164249f 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
@@ -964,3 +964,24 @@ static_assert(sizeof(b::d) == 16, "");
static_assert(sizeof(void (a<int>::*)()) == 16, "");
#endif
}
+
+namespace ContainerOf {
+ using size_t = unsigned long long;
+
+ struct List {
+ int data;
+ };
+
+ struct Node {
+ int data;
+ struct List list1;
+ struct List list2;
+ };
+
+ // CHECK-LABEL: define{{.*}} ptr @"?getOwner@ContainerOf@@
+ // CHECK: %memptr.offset = getelementptr i8, ptr null, {{.*}}
+ Node* getOwner(List *list, List Node::*member) {
+ size_t offset = reinterpret_cast<size_t>(&((Node*)nullptr->*member));
+ return reinterpret_cast<Node*>(reinterpret_cast<char*>(list) - offset);
+ }
+}
diff --git a/clang/test/CodeGenCXX/pointers-to-data-members.cpp b/clang/test/CodeGenCXX/pointers-to-data-members.cpp
index cf1d6c018409d..2ee6c65cf167d 100644
--- a/clang/test/CodeGenCXX/pointers-to-data-members.cpp
+++ b/clang/test/CodeGenCXX/pointers-to-data-members.cpp
@@ -265,3 +265,24 @@ namespace PR47864 {
struct D : B { int m; };
auto x = (int B::*)&D::m;
}
+
+namespace ContainerOf {
+ using size_t = unsigned long long;
+
+ struct List {
+ int data;
+ };
+
+ struct Node {
+ int data;
+ struct List list1;
+ struct List list2;
+ };
+
+ // CHECK-LABEL: define{{.*}} ptr @_ZN11ContainerOf8getOwnerEPNS_4ListEMNS_4NodeES0_
+ // CHECK: %memptr.offset = getelementptr i8, ptr null, i64 {{.*}}
+ Node* getOwner(List *list, List Node::*member) {
+ size_t offset = reinterpret_cast<size_t>(&((Node*)nullptr->*member));
+ return reinterpret_cast<Node*>(reinterpret_cast<char*>(list) - offset);
+ }
+}
|
@llvm/pr-subscribers-clang Author: Yingwei Zheng (dtcxzyw) ChangesSee also #130734 for the original motivation. This pattern ( llvm-project/llvm/include/llvm/IR/SymbolTableListTraits.h Lines 77 to 87 in 1d89d7d
https://github.com/nodejs/node/blob/a2a53cb728ec5f776ac16879ce0f480a8e838320/src/util-inl.h#L134-L137 https://github.com/search?q=*%29nullptr-%3E*&type=code Full diff: https://github.com/llvm/llvm-project/pull/130952.diff 4 Files Affected:
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index b145da0f0ec09..b2eaea683ebe7 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -872,9 +872,14 @@ llvm::Value *ItaniumCXXABI::EmitMemberDataPointerAddress(
CGBuilderTy &Builder = CGF.Builder;
- // Apply the offset, which we assume is non-null.
- return Builder.CreateInBoundsGEP(CGF.Int8Ty, Base.emitRawPointer(CGF), MemPtr,
- "memptr.offset");
+ // Apply the offset.
+ // Specially, we don't add inbounds flags if the base pointer is a constant
+ // null. This is a workaround for old-style container_of macros.
+ llvm::Value *BaseAddr = Base.emitRawPointer(CGF);
+ return Builder.CreateGEP(CGF.Int8Ty, BaseAddr, MemPtr, "memptr.offset",
+ isa<llvm::ConstantPointerNull>(BaseAddr)
+ ? llvm::GEPNoWrapFlags::none()
+ : llvm::GEPNoWrapFlags::inBounds());
}
// See if it's possible to return a constant signed pointer.
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 4b55fc3f17bd7..3501b2b2fdca8 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -3265,9 +3265,13 @@ llvm::Value *MicrosoftCXXABI::EmitMemberDataPointerAddress(
Addr = Base.emitRawPointer(CGF);
}
- // Apply the offset, which we assume is non-null.
- return Builder.CreateInBoundsGEP(CGF.Int8Ty, Addr, FieldOffset,
- "memptr.offset");
+ // Apply the offset.
+ // Specially, we don't add inbounds flags if the base pointer is a constant
+ // null. This is a workaround for old-style container_of macros.
+ return Builder.CreateGEP(CGF.Int8Ty, Addr, FieldOffset, "memptr.offset",
+ isa<llvm::ConstantPointerNull>(Addr)
+ ? llvm::GEPNoWrapFlags::none()
+ : llvm::GEPNoWrapFlags::inBounds());
}
llvm::Value *
diff --git a/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp b/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
index fc8a31e0350e5..fe4cab164249f 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
@@ -964,3 +964,24 @@ static_assert(sizeof(b::d) == 16, "");
static_assert(sizeof(void (a<int>::*)()) == 16, "");
#endif
}
+
+namespace ContainerOf {
+ using size_t = unsigned long long;
+
+ struct List {
+ int data;
+ };
+
+ struct Node {
+ int data;
+ struct List list1;
+ struct List list2;
+ };
+
+ // CHECK-LABEL: define{{.*}} ptr @"?getOwner@ContainerOf@@
+ // CHECK: %memptr.offset = getelementptr i8, ptr null, {{.*}}
+ Node* getOwner(List *list, List Node::*member) {
+ size_t offset = reinterpret_cast<size_t>(&((Node*)nullptr->*member));
+ return reinterpret_cast<Node*>(reinterpret_cast<char*>(list) - offset);
+ }
+}
diff --git a/clang/test/CodeGenCXX/pointers-to-data-members.cpp b/clang/test/CodeGenCXX/pointers-to-data-members.cpp
index cf1d6c018409d..2ee6c65cf167d 100644
--- a/clang/test/CodeGenCXX/pointers-to-data-members.cpp
+++ b/clang/test/CodeGenCXX/pointers-to-data-members.cpp
@@ -265,3 +265,24 @@ namespace PR47864 {
struct D : B { int m; };
auto x = (int B::*)&D::m;
}
+
+namespace ContainerOf {
+ using size_t = unsigned long long;
+
+ struct List {
+ int data;
+ };
+
+ struct Node {
+ int data;
+ struct List list1;
+ struct List list2;
+ };
+
+ // CHECK-LABEL: define{{.*}} ptr @_ZN11ContainerOf8getOwnerEPNS_4ListEMNS_4NodeES0_
+ // CHECK: %memptr.offset = getelementptr i8, ptr null, i64 {{.*}}
+ Node* getOwner(List *list, List Node::*member) {
+ size_t offset = reinterpret_cast<size_t>(&((Node*)nullptr->*member));
+ return reinterpret_cast<Node*>(reinterpret_cast<char*>(list) - offset);
+ }
+}
|
Same concerns about null pointer checking as #130734. |
824a5e7
to
425801d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Object = EmitCXXMemberDataPointerAddress(E, Object, Ptr, | ||
Adjustment.Ptr.MPT); | ||
Object = EmitCXXMemberDataPointerAddress( | ||
E, Object, Ptr, Adjustment.Ptr.MPT, /*IsInBounds=*/true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sent me down a really deep rabbit-hole to figure out when this code actually runs... apparently, in C++98 mode, we delay creating temporaries for things that would be xvalues in newer standard versions, and then we try to fix things up later using skipRValueSubobjectAdjustments(). We don't have an in-tree tests for this particular codepath. The whole thing is really ugly, and we should really come up with a better solution...
In any case, the base object should be an alloca here, so inbounds should be fine.
425801d
to
e71ca8b
Compare
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/10/builds/3246 Here is the relevant piece of the build log for the reference
|
…dress` when the base pointer is null (llvm#130952) See also llvm#130734 for the original motivation. This pattern (`container_of`) is also widely used by real-world programs. Examples: https://github.com/llvm/llvm-project/blob/1d89d7d5d76e391b035f50343e2a4890506c6f2b/llvm/include/llvm/IR/SymbolTableListTraits.h#L77-L87 https://github.com/nodejs/node/blob/a2a53cb728ec5f776ac16879ce0f480a8e838320/src/util-inl.h#L134-L137 https://github.com/search?q=*%29nullptr-%3E*&type=code
This patch fixes the "dereferencing null" UB. Unfortunately, C++ doesn't provide an inverse operation for `p->*pmf`. See also #130952.
This patch fixes the "dereferencing null" UB. Unfortunately, C++ doesn't provide an inverse operation for `p->*pmf`. See also llvm/llvm-project#130952.
This patch fixes the "dereferencing null" UB. Unfortunately, C++ doesn't provide an inverse operation for `p->*pmf`. See also llvm#130952.
See also #130734 for the original motivation.
This pattern (
container_of
) is also widely used by real-world programs.Examples:
llvm-project/llvm/include/llvm/IR/SymbolTableListTraits.h
Lines 77 to 87 in 1d89d7d
https://github.com/nodejs/node/blob/a2a53cb728ec5f776ac16879ce0f480a8e838320/src/util-inl.h#L134-L137
https://github.com/search?q=*%29nullptr-%3E*&type=code