-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[ObjC] Check entire chain of superclasses to see if class layout is statically known #81335
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-analysis @llvm/pr-subscribers-clang-codegen Author: AtariDreams (AtariDreams) ChangesIf an NSObject subclass has fixed offsets, then so must the subclasses of that subclass! Full diff: https://github.com/llvm/llvm-project/pull/81335.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index 27d77e9a8a5511..ed7b6533752664 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -1593,12 +1593,19 @@ class CGObjCNonFragileABIMac : public CGObjCCommonMac {
}
bool isClassLayoutKnownStatically(const ObjCInterfaceDecl *ID) {
+ if (!ID)
+ return false;
+
// NSObject is a fixed size. If we can see the @implementation of a class
- // which inherits from NSObject then we know that all it's offsets also must
- // be fixed. FIXME: Can we do this if see a chain of super classes with
- // implementations leading to NSObject?
- return ID->getImplementation() && ID->getSuperClass() &&
- ID->getSuperClass()->getName() == "NSObject";
+ // which inherits from NSObject, then we know that all its offsets must
+ // be fixed.
+ if (ID->getImplementation() && ID->getSuperClass() &&
+ ID->getSuperClass()->getName() == "NSObject") {
+ // Check recursively for all intermediate superclasses.
+ return isClassLayoutKnownStatically(ID->getSuperClass());
+ }
+
+ return false;
}
public:
diff --git a/clang/test/CodeGenObjC/constant-non-fragile-ivar-offset.m b/clang/test/CodeGenObjC/constant-non-fragile-ivar-offset.m
index 788b3220af3067..4701f1c5c9b93f 100644
--- a/clang/test/CodeGenObjC/constant-non-fragile-ivar-offset.m
+++ b/clang/test/CodeGenObjC/constant-non-fragile-ivar-offset.m
@@ -2,6 +2,7 @@
// CHECK: @"OBJC_IVAR_$_StaticLayout.static_layout_ivar" = hidden constant i64 20
// CHECK: @"OBJC_IVAR_$_NotStaticLayout.not_static_layout_ivar" = hidden global i64 12
+// CHECK: @"OBJC_IVAR_$_StaticLayoutSubClass.static_layout_ivar2" = hidden global i64 24
@interface NSObject {
int these, will, never, change, ever;
@@ -20,6 +21,18 @@ -(void)meth {
}
@end
+@interface StaticLayoutSubClass : StaticLayout
+@end
+
+@implementation StaticLayoutSubClass {
+ int static_layout_ivar2;
+}
+-(void)meth2 {
+ static_layout_ivar2 = 0;
+ // CHECK-NOT: load i64, ptr @"OBJC_IVAR_$_StaticLayoutSubClass.static_layout_ivar2
+}
+@end
+
@interface NotNSObject {
int these, might, change;
}
|
@llvm/pr-subscribers-clang Author: AtariDreams (AtariDreams) ChangesIf an NSObject subclass has fixed offsets, then so must the subclasses of that subclass! Full diff: https://github.com/llvm/llvm-project/pull/81335.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index 27d77e9a8a5511..ed7b6533752664 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -1593,12 +1593,19 @@ class CGObjCNonFragileABIMac : public CGObjCCommonMac {
}
bool isClassLayoutKnownStatically(const ObjCInterfaceDecl *ID) {
+ if (!ID)
+ return false;
+
// NSObject is a fixed size. If we can see the @implementation of a class
- // which inherits from NSObject then we know that all it's offsets also must
- // be fixed. FIXME: Can we do this if see a chain of super classes with
- // implementations leading to NSObject?
- return ID->getImplementation() && ID->getSuperClass() &&
- ID->getSuperClass()->getName() == "NSObject";
+ // which inherits from NSObject, then we know that all its offsets must
+ // be fixed.
+ if (ID->getImplementation() && ID->getSuperClass() &&
+ ID->getSuperClass()->getName() == "NSObject") {
+ // Check recursively for all intermediate superclasses.
+ return isClassLayoutKnownStatically(ID->getSuperClass());
+ }
+
+ return false;
}
public:
diff --git a/clang/test/CodeGenObjC/constant-non-fragile-ivar-offset.m b/clang/test/CodeGenObjC/constant-non-fragile-ivar-offset.m
index 788b3220af3067..4701f1c5c9b93f 100644
--- a/clang/test/CodeGenObjC/constant-non-fragile-ivar-offset.m
+++ b/clang/test/CodeGenObjC/constant-non-fragile-ivar-offset.m
@@ -2,6 +2,7 @@
// CHECK: @"OBJC_IVAR_$_StaticLayout.static_layout_ivar" = hidden constant i64 20
// CHECK: @"OBJC_IVAR_$_NotStaticLayout.not_static_layout_ivar" = hidden global i64 12
+// CHECK: @"OBJC_IVAR_$_StaticLayoutSubClass.static_layout_ivar2" = hidden global i64 24
@interface NSObject {
int these, will, never, change, ever;
@@ -20,6 +21,18 @@ -(void)meth {
}
@end
+@interface StaticLayoutSubClass : StaticLayout
+@end
+
+@implementation StaticLayoutSubClass {
+ int static_layout_ivar2;
+}
+-(void)meth2 {
+ static_layout_ivar2 = 0;
+ // CHECK-NOT: load i64, ptr @"OBJC_IVAR_$_StaticLayoutSubClass.static_layout_ivar2
+}
+@end
+
@interface NotNSObject {
int these, might, change;
}
|
|
e554230
to
2ef8091
Compare
6f5944a
to
f196660
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.
Okay, your description of the change here is very misleading; it doesn't even mention that this is specific to having an @implementation
for every intermediate class.
Your test seems completely inadequate — you need to test that we're doing the static layout properly, and you need to test with ivars coming from a variety of different places, including:
- ivars declared in the
@interface
- ivars declared in the
@implementation
- ivars synthesized explicitly in the
@implementation
- vars synthesized implicitly in the
@implementation
So this is a way to check for static linked; if the compiler can see the @implementation, right? Only then can we "opt-out" of the non-fragile ABI like NSObject and its subclasses do, right? |
I think it is the only way, otherwise it would have to be done at link time. Can we do it at link time? probably another PR would have to deal with that. |
3679a6b
to
698bb84
Compare
c23121c
to
40fc220
Compare
Refactored for code consistency. |
a490bdc
to
c606c0e
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.
Thanks, that looks great.
@rjmccall Ready to merge! |
@rjmccall I do not have merge permissions or whatever it is called. |
@rjmccall ping |
…tatically known As of now, we only check if a class directly inherits from NSObject to determine if said class has fixed offsets and can therefore have its memory layout statically known. However, if an NSObject subclass has fixed offsets, then so must the subclasses of that subclass, so this allows us to optimize instances of subclasses of subclasses that inherit from NSObject and so on. To determine this, we need to find that the compiler can see the implementation of each intermediate class, as that means it is statically linked.
@JOE1994 Thoughts on this? |
I'm not an ideal reviewer for this as I'm unfamiliar with ObjC side of Clang, |
Alright. Thank you so much! |
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
…out is statically known (llvm#81335) As of now, we only check if a class directly inherits from NSObject to determine if said class has fixed offsets and can therefore "opt-out" from the non-fragile ABI for ivars. However, if an NSObject subclass has fixed offsets, then so must the subclasses of that subclass, so this allows us to optimize instances of subclasses of subclasses that inherit from NSObject and so on. To determine this, we need to find that the compiler can see the implementation of each intermediate class, as that means it is statically linked. This would be nice to incorporate into the next Swift Toolchain, especially since WebKit, as it is currently configured to be built, would benefit greatly from this.
…tatically known (llvm#81335) As of now, we only check if a class directly inherits from NSObject to determine if said class has fixed offsets and can therefore "opt-out" from the non-fragile ABI for ivars. However, if an NSObject subclass has fixed offsets, then so must the subclasses of that subclass, so this allows us to optimize instances of subclasses of subclasses that inherit from NSObject and so on. To determine this, we need to find that the compiler can see the implementation of each intermediate class, as that means it is statically linked. Fixes: llvm#81369
@AZero13 Do you have any news or plans regarding LTO support? We have several projects with substantial existing Objective-C code, and the performance benefits of LTO support could be significant. I would be interested in taking on a portion or the entirety of this work if it is not currently on your priority list. |
If you can do it, go for it! |
As of now, we only check if a class directly inherits from NSObject to determine if said class has fixed offsets and can therefore "opt-out" from the non-fragile ABI for ivars.
However, if an NSObject subclass has fixed offsets, then so must the subclasses of that subclass, so this allows us to optimize instances of subclasses of subclasses that inherit from NSObject and so on.
To determine this, we need to find that the compiler can see the implementation of each intermediate class, as that means it is statically linked.
Fixes: #81369