-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
IRGen: Use any field of fixed-layout structs for extra inhabitants. #18630
IRGen: Use any field of fixed-layout structs for extra inhabitants. #18630
Conversation
@swift-ci Please test |
@swift-ci Please test source compatibility |
@slavapestov Is there anywhere in RemoteAST/RemoteMirror or lldb that extra inhabitant counting for structs is hardcoded that I should update? |
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.
@jckarter do you have an example where the layout of the struct changed before/after your change?
If so, I can try in lldb and let you know if there's anything broken there.
This is great and gets us a fair deal closer to optimal extra-inhabitant utilization! |
@dcci It would be different in a case where there's an object reference in a struct in some position other than first, and you use an Optional of that struct. For instance:
Previously, |
Build failed |
5cfc150
to
8bcab80
Compare
@swift-ci Please test |
Build failed |
Build failed |
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.
@swift-ci Please test source compatibility |
1 similar comment
@swift-ci Please test source compatibility |
@jckarter Remote mirrors do not know if the layout was static or dynamic. Look at stdlib/public/Reflection/TypeLowering.cpp. Also what about resilience? If the struct is resilient, you can't use its extra inhabitants with your scheme. I would suggest implementing the runtime support at the same time and getting rid of the special case. Other than that, this is a great idea! |
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.
Yeah, I don't think this is sound unfortunately. We need compile time and runtime to always produce the same result, even if some fields are non-fixed at compile time (or are fixed in the current module but resilient outside).
lib/IRGen/GenStruct.cpp
Outdated
|
||
for (auto &field : fields) { | ||
auto *ti = dyn_cast<FixedTypeInfo>(&field.getTypeInfo()); | ||
// If any field is non-fixed, we can't definitively pick a best one. |
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.
The correct check would be isAlwaysFixedSize(), which takes all resilience domains into account; otherwise, this will produce different results across resilience boundaries. However, there's no way currently to replicate that check at runtime.
Could we just call the dynamic extra inhabitant value witness functions in this case?
lib/IRGen/GenType.cpp
Outdated
@@ -1843,7 +1843,8 @@ TypeCacheEntry TypeConverter::convertAnyNominalType(CanType type, | |||
case DeclKind::Enum: | |||
return convertEnumType(type.getPointer(), type, cast<EnumDecl>(decl)); | |||
case DeclKind::Struct: | |||
return convertStructType(type.getPointer(), type, cast<StructDecl>(decl)); | |||
return convertStructType(type.getPointer(), type, cast<StructDecl>(decl), | |||
/*is type dependent*/ decl->isGenericContext()); |
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 doesn't actually mean its dependent, eg struct Foo<T> { var x: Int }
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.
That condition is checked further up before we enter this switch. I agree it's best to just do the runtime side of this though.
This allows us to layout-optimize Optional<T> when T is a struct with an extra-inhabitant-bearing field anywhere in its definition, not only at the beginning. rdar://problem/43019427
33de71d
to
9f02ecd
Compare
@swift-ci Please test |
@rjmccall @slavapestov I added handling for a runtime-selected extra inhabitant field as well. How does this look? The annoying conditional dance here has me thinking we probably ought to just have a non-executable representation of the extra inhabitant set, since all of our extra-inhabitant-bearing types' sets are representable as an (offset, width, starting value, stride) tuple. That would let us shed those value witnesses altogether, and would make forwarding the extra inhabitants from the chosen field at runtime here trivial. I'd like to investigate that separately from this PR, though. |
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, I think this is a lot better.
This allows us to layout-optimize Optional when T is a struct with an extra-inhabitant-bearing field anywhere in its definition, not only at the beginning. Since runtime struct layout instantiation does not yet handle this, limit the effect only to structs whose layout is always completely known at compile time independent of their type parameters. rdar://problem/19690160