mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This commit begins to generate correct metadata for @_objcImplementation extensions: • Swift-specific metadata and symbols are not generated. • For main-class @_objcImpls, we visit the class to emit metadata, but visit the extension’s members. • Includes both IR tests and executable tests, including coverage of same-module @objc subclasses, different-module @objc subclasses, and clang subclasses. The test cases do not yet cover stored properties.
230 lines
8.9 KiB
C++
230 lines
8.9 KiB
C++
//===--- ClassLayout.h - Class instance layout ------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines some routines that are useful for calculating class
|
|
// instance layout.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_IRGEN_CLASSLAYOUT_H
|
|
#define SWIFT_IRGEN_CLASSLAYOUT_H
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "Field.h"
|
|
#include "IRGen.h"
|
|
#include "StructLayout.h"
|
|
|
|
namespace swift {
|
|
namespace irgen {
|
|
|
|
/// Different policies for accessing a physical field.
|
|
enum class FieldAccess : uint8_t {
|
|
/// Instance variable offsets are constant.
|
|
ConstantDirect,
|
|
|
|
/// Instance variable offsets must be loaded from "direct offset"
|
|
/// global variables.
|
|
NonConstantDirect,
|
|
|
|
/// Instance variable offsets are kept in fields in metadata, but
|
|
/// the offsets of those fields within the metadata are constant.
|
|
ConstantIndirect
|
|
};
|
|
|
|
/// A set of flags describing properties of a class's metadata layout.
|
|
/// The presence or absence of these flags determines how much static
|
|
/// knowledge the compiler has of the layout of this class and its
|
|
/// metadata, which in turn will determine the strategy used to emit
|
|
/// and initialize class metadata.
|
|
enum class ClassMetadataFlags {
|
|
/// Does the class or any of its superclasses have stored properties that
|
|
/// where dropped due to the Swift language version availability of
|
|
/// their types?
|
|
ClassHasMissingMembers = (1 << 0),
|
|
|
|
/// Does the class or any of its fragile superclasses have stored
|
|
/// properties of unknown size, which do *not* depend on generic
|
|
/// parameters?
|
|
///
|
|
/// This is different from the class itself being resilient or
|
|
/// having resilient ancestry, because we still have a fixed layout
|
|
/// for the class metadata in this case.
|
|
///
|
|
/// In fact, for a class with resilient ancestry, this can still be
|
|
/// false if all of the fields known to us are fixed size.
|
|
ClassHasResilientMembers = (1 << 1),
|
|
|
|
/// Is this class or any of its superclasses generic?
|
|
ClassHasGenericAncestry = (1 << 2),
|
|
|
|
/// Is this class itself generic via the Swift generic system, ie. not a
|
|
/// lightweight Objective-C generic class?
|
|
ClassIsGeneric = (1 << 3),
|
|
|
|
/// Does the class layout depend on the size or alignment of its
|
|
/// generic parameters?
|
|
///
|
|
/// This can be the case if the class has generic resilient ancestry
|
|
/// that depends on the class's generic parameters, of it it has
|
|
/// fields of generic type that are not fixed size.
|
|
ClassHasGenericLayout = (1 << 4),
|
|
|
|
/// Is this class or any of its superclasses resilient from the viewpoint
|
|
/// of the current module? This means that their metadata can change size,
|
|
/// hence field offsets, generic arguments and virtual methods must be
|
|
/// accessed relative to a metadata base global variable.
|
|
///
|
|
/// Note that a @_fixed_layout class in a resilient module still has
|
|
/// resilient metadata, so any subclasses will have this flag set;
|
|
/// to check for resilient stored property layout, check for
|
|
/// ClassHasResilientMembers.
|
|
ClassHasResilientAncestry = (1 << 5),
|
|
|
|
/// Are any of this class's superclasses defined in Objective-C?
|
|
/// This means that field offsets must be loaded from field offset globals
|
|
/// or the field offset vector in the metadata, and the Objective-C runtime
|
|
/// will slide offsets based on the actual superclass size, which is not
|
|
/// known at compile time.
|
|
ClassHasObjCAncestry = (1 << 6),
|
|
|
|
/// Is the class implemented by an \c @_objcImplementation extension? If so,
|
|
/// we should generate pure ObjC-compatible metadata.
|
|
ClassHasObjCImplementation = (1 << 7)
|
|
};
|
|
|
|
using ClassMetadataOptions = OptionSet<ClassMetadataFlags>;
|
|
|
|
class ClassLayout {
|
|
/// The statically-known minimum bound on the alignment.
|
|
Alignment MinimumAlign;
|
|
|
|
/// The statically-known minimum bound on the size.
|
|
Size MinimumSize;
|
|
|
|
/// Whether this layout is fixed in size. If so, the size and
|
|
/// alignment are exact.
|
|
bool IsFixedLayout;
|
|
|
|
ClassMetadataOptions Options;
|
|
|
|
/// The LLVM type for instances of this class.
|
|
llvm::Type *Ty;
|
|
|
|
/// The header size of this class.
|
|
Size HeaderSize;
|
|
|
|
/// Lazily-initialized array of all fragile stored properties directly defined
|
|
/// in the class itself.
|
|
ArrayRef<Field> AllStoredProperties;
|
|
|
|
/// Lazily-initialized array of all field access methods.
|
|
ArrayRef<FieldAccess> AllFieldAccesses;
|
|
|
|
/// Fixed offsets of fields, if known (does not take Objective-C sliding into
|
|
/// account).
|
|
ArrayRef<ElementLayout> AllElements;
|
|
|
|
public:
|
|
ClassLayout(const StructLayoutBuilder &builder,
|
|
ClassMetadataOptions options,
|
|
llvm::Type *classTy,
|
|
ArrayRef<Field> allStoredProps,
|
|
ArrayRef<FieldAccess> allFieldAccesses,
|
|
ArrayRef<ElementLayout> allElements,
|
|
Size headerSize);
|
|
|
|
Size getInstanceStart() const;
|
|
|
|
llvm::Type *getType() const { return Ty; }
|
|
Size getSize() const { return MinimumSize; }
|
|
Alignment getAlignment() const { return MinimumAlign; }
|
|
Size getAlignMask() const { return getAlignment().asSize() - Size(1); }
|
|
|
|
bool isFixedLayout() const { return IsFixedLayout; }
|
|
|
|
/// Returns true if the stored property layout of instances of this class
|
|
/// is known at compile time.
|
|
///
|
|
/// Note that ClassHasResilientAncestry or ClassHasGenericAncestry might
|
|
/// still be true; the former means the class has resilient metadata, so
|
|
/// it might still be @_fixed_layout; the latter means we have a generic
|
|
/// superclass, but it doesn't mean the layout actually depends on any
|
|
/// generic parameters.
|
|
bool isFixedSize() const {
|
|
return !(Options.contains(ClassMetadataFlags::ClassHasMissingMembers) ||
|
|
Options.contains(ClassMetadataFlags::ClassHasResilientMembers) ||
|
|
Options.contains(ClassMetadataFlags::ClassHasGenericLayout) ||
|
|
Options.contains(ClassMetadataFlags::ClassHasObjCAncestry));
|
|
}
|
|
|
|
/// Returns true if the runtime may attempt to assign non-zero offsets to
|
|
/// empty fields for this class. The ObjC runtime will do this if it
|
|
/// decides it needs to slide ivars. This is the one exception to the
|
|
/// general rule that the runtime will not try to assign a different offset
|
|
/// than was computed statically for a field with a fixed offset.
|
|
bool mayRuntimeAssignNonZeroOffsetsToEmptyFields() const {
|
|
return Options.contains(ClassMetadataFlags::ClassHasObjCAncestry);
|
|
}
|
|
|
|
/// Returns true iff everything about the class metadata layout is statically
|
|
/// known except field offsets and the instance size and alignment.
|
|
///
|
|
/// Will assert if the class metadata is "more" dynamic; you must check
|
|
/// doesMetadataRequireRelocation() and doesMetadataRequireInitialization()
|
|
/// first.
|
|
bool doesMetadataRequireUpdate() const {
|
|
assert(!doesMetadataRequireInitialization());
|
|
return (Options.contains(ClassMetadataFlags::ClassHasResilientMembers) ||
|
|
Options.contains(ClassMetadataFlags::ClassHasMissingMembers));
|
|
}
|
|
|
|
/// Returns true iff everything about the class metadata layout is statically
|
|
/// known except the superclass field must be instantiated at runtime because
|
|
/// it is a generic class type.
|
|
///
|
|
/// Will assert if the class metadata is "more" dynamic; you must check
|
|
/// doesMetadataRequireRelocation() first.
|
|
bool doesMetadataRequireInitialization() const {
|
|
assert(!doesMetadataRequireRelocation());
|
|
return Options.contains(ClassMetadataFlags::ClassHasGenericAncestry);
|
|
}
|
|
|
|
/// Returns true if the class metadata must be built at runtime because its
|
|
/// size is not known at compile time. This is the most general case.
|
|
bool doesMetadataRequireRelocation() const {
|
|
return (Options.contains(ClassMetadataFlags::ClassHasResilientAncestry) ||
|
|
Options.contains(ClassMetadataFlags::ClassIsGeneric));
|
|
}
|
|
|
|
std::pair<FieldAccess, ElementLayout>
|
|
getFieldAccessAndElement(Field field) const {
|
|
// FIXME: This is algorithmically terrible.
|
|
auto found = std::find(AllStoredProperties.begin(),
|
|
AllStoredProperties.end(), field);
|
|
assert(found != AllStoredProperties.end() && "didn't find field in type?!");
|
|
unsigned index = found - AllStoredProperties.begin();
|
|
|
|
return std::make_pair(AllFieldAccesses[index], AllElements[index]);
|
|
}
|
|
|
|
/// Returns true if the class is implemented by an \c @_objcImplementation
|
|
/// extension, and therefore should not have any Swift-specific metadata.
|
|
bool hasObjCImplementation() const {
|
|
return Options.contains(ClassMetadataFlags::ClassHasObjCImplementation);
|
|
}
|
|
};
|
|
|
|
} // end namespace irgen
|
|
} // end namespace swift
|
|
|
|
#endif
|