|
25 | 25 | #include "IRGenFunction.h" |
26 | 26 | #include "IRGenModule.h" |
27 | 27 | #include "ProtocolInfo.h" |
| 28 | +#include "StructLayout.h" |
28 | 29 | #include "llvm/ADT/SetVector.h" |
29 | 30 | #include "swift/SIL/SILInstruction.h" |
30 | 31 | #include "swift/SIL/SILLocation.h" |
@@ -209,47 +210,98 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern, |
209 | 210 | auto &component = pattern->getComponents()[i]; |
210 | 211 | switch (auto kind = component.getKind()) { |
211 | 212 | case KeyPathPatternComponent::Kind::StoredProperty: { |
212 | | - // Try to get a constant offset if we can. |
213 | 213 | auto property = cast<VarDecl>(component.getStoredPropertyDecl()); |
214 | | - llvm::Constant *offset; |
215 | | - bool isResolved; |
216 | | - std::tie(offset, isResolved) |
217 | | - = getPropertyOffsetOrIndirectOffset(loweredBaseTy, property); |
218 | | - offset = llvm::ConstantExpr::getTruncOrBitCast(offset, Int32Ty); |
219 | | - bool isStruct = (bool)loweredBaseTy.getStructOrBoundGenericStruct(); |
220 | 214 |
|
221 | | - // If the projection is a statically known integer, try to pack it into |
222 | | - // the key path payload. |
223 | | - if (isResolved) { |
| 215 | + auto addFixedOffset = [&](bool isStruct, llvm::Constant *offset) { |
| 216 | + offset = llvm::ConstantExpr::getTruncOrBitCast(offset, Int32Ty); |
224 | 217 | if (auto offsetInt = dyn_cast_or_null<llvm::ConstantInt>(offset)) { |
225 | 218 | auto offsetValue = offsetInt->getValue().getZExtValue(); |
226 | 219 | if (KeyPathComponentHeader::offsetCanBeInline(offsetValue)) { |
227 | 220 | auto header = isStruct |
228 | 221 | ? KeyPathComponentHeader::forStructComponentWithInlineOffset(offsetValue) |
229 | 222 | : KeyPathComponentHeader::forClassComponentWithInlineOffset(offsetValue); |
230 | 223 | fields.addInt32(header.getData()); |
231 | | - break; |
| 224 | + return; |
232 | 225 | } |
233 | 226 | } |
234 | | - |
235 | 227 | auto header = isStruct |
236 | 228 | ? KeyPathComponentHeader::forStructComponentWithOutOfLineOffset() |
237 | 229 | : KeyPathComponentHeader::forClassComponentWithOutOfLineOffset(); |
238 | 230 | fields.addInt32(header.getData()); |
239 | | - |
240 | 231 | fields.add(offset); |
241 | | - } else { |
242 | | - // Otherwise, stash the offset of the field offset within the metadata |
243 | | - // object, so we can pull it out at instantiation time. |
244 | | - // TODO: We'll also need a way to handle resilient field offsets, once |
245 | | - // field offset vectors no longer cover all fields in the type. |
246 | | - KeyPathComponentHeader header = isStruct |
247 | | - ? KeyPathComponentHeader::forStructComponentWithUnresolvedOffset() |
248 | | - : KeyPathComponentHeader::forClassComponentWithUnresolvedOffset(); |
| 232 | + }; |
| 233 | + |
| 234 | + // For a struct stored property, we may know the fixed offset of the field, |
| 235 | + // or we may need to fetch it out of the type's metadata at instantiation |
| 236 | + // time. |
| 237 | + if (loweredBaseTy.getStructOrBoundGenericStruct()) { |
| 238 | + if (auto offset = emitPhysicalStructMemberFixedOffset(*this, |
| 239 | + loweredBaseTy, |
| 240 | + property)) { |
| 241 | + // We have a known constant fixed offset. |
| 242 | + addFixedOffset(/*struct*/ true, offset); |
| 243 | + break; |
| 244 | + } |
| 245 | + |
| 246 | + // If the offset isn't fixed, try instead to get the field offset out |
| 247 | + // of the type metadata at instantiation time. |
| 248 | + auto fieldOffset = emitPhysicalStructMemberOffsetOfFieldOffset( |
| 249 | + *this, loweredBaseTy, property); |
| 250 | + fieldOffset = llvm::ConstantExpr::getTruncOrBitCast(fieldOffset, |
| 251 | + Int32Ty); |
| 252 | + auto header = KeyPathComponentHeader::forStructComponentWithUnresolvedFieldOffset(); |
249 | 253 | fields.addInt32(header.getData()); |
250 | | - fields.add(offset); |
| 254 | + fields.add(fieldOffset); |
| 255 | + break; |
| 256 | + } |
| 257 | + |
| 258 | + // For a class, we may know the fixed offset of a field at compile time, |
| 259 | + // or we may need to fetch it at instantiation time. Depending on the |
| 260 | + // ObjC-ness and resilience of the class hierarchy, there might be a few |
| 261 | + // different ways we need to go about this. |
| 262 | + if (loweredBaseTy.getClassOrBoundGenericClass()) { |
| 263 | + switch (getClassFieldAccess(*this, loweredBaseTy, property)) { |
| 264 | + case FieldAccess::ConstantDirect: { |
| 265 | + // Known constant fixed offset. |
| 266 | + auto offset = tryEmitConstantClassFragilePhysicalMemberOffset(*this, |
| 267 | + loweredBaseTy, |
| 268 | + property); |
| 269 | + assert(offset && "no constant offset for ConstantDirect field?!"); |
| 270 | + addFixedOffset(/*struct*/ false, offset); |
| 271 | + break; |
| 272 | + } |
| 273 | + case FieldAccess::NonConstantDirect: { |
| 274 | + // A constant offset that's determined at class realization time. |
| 275 | + // We have to load the offset from a global ivar. |
| 276 | + auto header = |
| 277 | + KeyPathComponentHeader::forClassComponentWithUnresolvedIndirectOffset(); |
| 278 | + fields.addInt32(header.getData()); |
| 279 | + auto offsetVar = getAddrOfFieldOffset(property, /*indirect*/ false, |
| 280 | + NotForDefinition); |
| 281 | + fields.add(cast<llvm::Constant>(offsetVar.getAddress())); |
| 282 | + break; |
| 283 | + } |
| 284 | + case FieldAccess::ConstantIndirect: { |
| 285 | + // An offset that depends on the instance's generic parameterization, |
| 286 | + // but whose field offset is at a known vtable offset. |
| 287 | + auto header = |
| 288 | + KeyPathComponentHeader::forClassComponentWithUnresolvedFieldOffset(); |
| 289 | + fields.addInt32(header.getData()); |
| 290 | + auto fieldOffset = |
| 291 | + getClassFieldOffset(*this, loweredBaseTy.getClassOrBoundGenericClass(), |
| 292 | + property); |
| 293 | + fields.addInt32(fieldOffset.getValue()); |
| 294 | + break; |
| 295 | + } |
| 296 | + case FieldAccess::NonConstantIndirect: |
| 297 | + // An offset that depends on the instance's generic parameterization, |
| 298 | + // whose vtable offset is also unknown. |
| 299 | + // TODO: This doesn't happen until class resilience is enabled. |
| 300 | + llvm_unreachable("not implemented"); |
| 301 | + } |
| 302 | + break; |
251 | 303 | } |
252 | | - break; |
| 304 | + llvm_unreachable("not struct or class"); |
253 | 305 | } |
254 | 306 | case KeyPathPatternComponent::Kind::GettableProperty: |
255 | 307 | case KeyPathPatternComponent::Kind::SettableProperty: { |
|
0 commit comments