blob: 084dcea421b778df8d4ccdaff269eb662d978e9f [file] [log] [blame]
//===--- 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 "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)
};
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;
/// Lazily-initialized array of all fragile stored properties directly defined
/// in the class itself.
ArrayRef<VarDecl *> 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<VarDecl *> allStoredProps,
ArrayRef<FieldAccess> allFieldAccesses,
ArrayRef<ElementLayout> allElements);
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(VarDecl *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]);
}
};
} // end namespace irgen
} // end namespace swift
#endif