mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
LLVM's normal data layout doesn't jive with our own StructLayout's more aggressive use of padding space, and causes problems such as <rdar://problem/14336903>. Because we do our own alignment and stride management, we can safely use LLVM packed struct types with manual padding to get the level of control we need to accurately reflect our desired struct layout. Swift SVN r9056
298 lines
10 KiB
C++
298 lines
10 KiB
C++
//===--- StructLayout.cpp - Layout of structures -------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements algorithms for laying out structures.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
|
|
#include "FixedTypeInfo.h"
|
|
#include "IRGenFunction.h"
|
|
#include "IRGenModule.h"
|
|
#include "StructLayout.h"
|
|
#include "TypeInfo.h"
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
/// Does this layout kind require a heap header?
|
|
static bool requiresHeapHeader(LayoutKind kind) {
|
|
switch (kind) {
|
|
case LayoutKind::NonHeapObject: return false;
|
|
case LayoutKind::HeapObject: return true;
|
|
}
|
|
llvm_unreachable("bad layout kind!");
|
|
}
|
|
|
|
/// Return the size of the standard heap header.
|
|
Size irgen::getHeapHeaderSize(IRGenModule &IGM) {
|
|
return IGM.getPointerSize() + Size(8);
|
|
}
|
|
|
|
/// Add the fields for the standard heap header to the given layout.
|
|
void irgen::addHeapHeaderToLayout(IRGenModule &IGM,
|
|
Size &size, Alignment &align,
|
|
SmallVectorImpl<llvm::Type*> &fields) {
|
|
assert(size.isZero() && align.isOne() && fields.empty());
|
|
size = getHeapHeaderSize(IGM);
|
|
align = IGM.getPointerAlignment();
|
|
fields.push_back(IGM.RefCountedStructTy);
|
|
}
|
|
|
|
/// Perform structure layout on the given types.
|
|
StructLayout::StructLayout(IRGenModule &IGM, LayoutKind layoutKind,
|
|
LayoutStrategy strategy,
|
|
ArrayRef<const TypeInfo *> types,
|
|
llvm::StructType *typeToFill) {
|
|
Elements.reserve(types.size());
|
|
|
|
// Fill in the Elements array.
|
|
for (auto type : types)
|
|
Elements.push_back(ElementLayout::getIncomplete(*type));
|
|
|
|
assert(typeToFill == nullptr || typeToFill->isOpaque());
|
|
|
|
StructLayoutBuilder builder(IGM);
|
|
|
|
// Add the heap header if necessary.
|
|
if (requiresHeapHeader(layoutKind)) {
|
|
builder.addHeapHeader();
|
|
}
|
|
|
|
bool nonEmpty = builder.addFields(Elements, strategy);
|
|
|
|
// Special-case: there's nothing to store.
|
|
// In this case, produce an opaque type; this tends to cause lovely
|
|
// assertions.
|
|
if (!nonEmpty) {
|
|
assert(!builder.empty() == requiresHeapHeader(layoutKind));
|
|
MinimumAlign = Alignment(1);
|
|
MinimumSize = Size(0);
|
|
IsFixedLayout = true;
|
|
IsKnownPOD = IsPOD;
|
|
Ty = (typeToFill ? typeToFill : IGM.OpaquePtrTy->getElementType());
|
|
} else {
|
|
MinimumAlign = builder.getAlignment();
|
|
MinimumSize = builder.getSize();
|
|
IsFixedLayout = builder.isFixedLayout();
|
|
IsKnownPOD = builder.isKnownPOD();
|
|
if (typeToFill) {
|
|
builder.setAsBodyOfStruct(typeToFill);
|
|
Ty = typeToFill;
|
|
} else {
|
|
Ty = builder.getAsAnonStruct();
|
|
}
|
|
}
|
|
assert(typeToFill == nullptr || Ty == typeToFill);
|
|
}
|
|
|
|
llvm::Value *StructLayout::emitSize(IRGenFunction &IGF) const {
|
|
assert(isFixedLayout());
|
|
return IGF.IGM.getSize(getSize());
|
|
}
|
|
|
|
llvm::Value *StructLayout::emitAlignMask(IRGenFunction &IGF) const {
|
|
assert(isFixedLayout());
|
|
return IGF.IGM.getSize(getAlignment().asSize() - Size(1));
|
|
}
|
|
|
|
/// Bitcast an arbitrary pointer to be a pointer to this type.
|
|
Address StructLayout::emitCastTo(IRGenFunction &IGF,
|
|
llvm::Value *ptr,
|
|
const llvm::Twine &name) const {
|
|
llvm::Value *addr =
|
|
IGF.Builder.CreateBitCast(ptr, getType()->getPointerTo(), name);
|
|
return Address(addr, getAlignment());
|
|
}
|
|
|
|
Address ElementLayout::project(IRGenFunction &IGF, Address baseAddr,
|
|
NonFixedOffsets offsets,
|
|
const llvm::Twine &suffix) const {
|
|
switch (getKind()) {
|
|
case Kind::Empty:
|
|
return getType().getUndefAddress();
|
|
|
|
case Kind::Fixed:
|
|
return IGF.Builder.CreateStructGEP(baseAddr,
|
|
getStructIndex(),
|
|
getByteOffset(),
|
|
baseAddr.getAddress()->getName() + suffix);
|
|
|
|
case Kind::NonFixed: {
|
|
assert(offsets.hasValue());
|
|
llvm::Value *offset =
|
|
offsets.getValue()->getOffsetForIndex(IGF, getNonFixedElementIndex());
|
|
return IGF.emitByteOffsetGEP(baseAddr.getAddress(), offset, getType(),
|
|
baseAddr.getAddress()->getName() + suffix);
|
|
}
|
|
|
|
case Kind::InitialNonFixedSize:
|
|
return IGF.Builder.CreateBitCast(baseAddr,
|
|
getType().getStorageType()->getPointerTo(),
|
|
baseAddr.getAddress()->getName() + suffix);
|
|
}
|
|
llvm_unreachable("bad element layout kind");
|
|
}
|
|
|
|
void StructLayoutBuilder::addHeapHeader() {
|
|
assert(StructFields.empty() && "adding heap header at a non-zero offset");
|
|
CurSize = getHeapHeaderSize(IGM);
|
|
CurAlignment = IGM.getPointerAlignment();
|
|
StructFields.push_back(IGM.RefCountedStructTy);
|
|
}
|
|
|
|
bool StructLayoutBuilder::addFields(llvm::MutableArrayRef<ElementLayout> elts,
|
|
LayoutStrategy strategy) {
|
|
// Track whether we've added any storage to our layout.
|
|
bool addedStorage = false;
|
|
|
|
// Loop through the elements. The only valid field in each element
|
|
// is Type; StructIndex and ByteOffset need to be laid out.
|
|
for (auto &elt : elts) {
|
|
auto &eltTI = elt.getType();
|
|
IsKnownPOD &= eltTI.isPOD(ResilienceScope::Local);
|
|
|
|
// If the element type is empty, it adds nothing.
|
|
if (eltTI.isKnownEmpty()) {
|
|
addEmptyElement(elt);
|
|
} else {
|
|
// Anything else we do at least potentially adds storage requirements.
|
|
addedStorage = true;
|
|
|
|
// TODO: consider using different layout rules.
|
|
// If the rules are changed so that fields aren't necessarily laid
|
|
// out sequentially, the computation of InstanceStart in the
|
|
// RO-data will need to be fixed.
|
|
|
|
// If this element is resiliently- or dependently-sized, record
|
|
// that and configure the ElementLayout appropriately.
|
|
if (isa<FixedTypeInfo>(eltTI)) {
|
|
addFixedSizeElement(elt);
|
|
} else {
|
|
addNonFixedSizeElement(elt);
|
|
}
|
|
}
|
|
|
|
NextNonFixedOffsetIndex++;
|
|
}
|
|
|
|
return addedStorage;
|
|
}
|
|
|
|
void StructLayoutBuilder::addFixedSizeElement(ElementLayout &elt) {
|
|
auto &eltTI = cast<FixedTypeInfo>(elt.getType());
|
|
|
|
// Note that, even in the presence of elements with non-fixed
|
|
// size, we continue to compute the minimum size and alignment
|
|
// requirements of the overall aggregate as if all the
|
|
// non-fixed-size elements were empty. This gives us minimum
|
|
// bounds on the size and alignment of the aggregate.
|
|
|
|
// The struct alignment is the max of the alignment of the fields.
|
|
CurAlignment = std::max(CurAlignment, eltTI.getFixedAlignment());
|
|
|
|
// If the current tuple size isn't a multiple of the field's
|
|
// required alignment, we need to pad out.
|
|
Alignment eltAlignment = eltTI.getFixedAlignment();
|
|
if (Size offsetFromAlignment = CurSize % eltAlignment) {
|
|
unsigned paddingRequired
|
|
= eltAlignment.getValue() - offsetFromAlignment.getValue();
|
|
assert(paddingRequired != 0);
|
|
|
|
if (isFixedLayout()) {
|
|
auto paddingTy = llvm::ArrayType::get(IGM.Int8Ty, paddingRequired);
|
|
StructFields.push_back(paddingTy);
|
|
}
|
|
|
|
// Regardless, the storage size goes up.
|
|
CurSize += Size(paddingRequired);
|
|
}
|
|
|
|
// If the overall structure so far has a fixed layout, then add
|
|
// this as a field to the layout.
|
|
if (isFixedLayout()) {
|
|
addElementAtFixedOffset(elt);
|
|
|
|
// Otherwise, just remember the next non-fixed offset index.
|
|
} else {
|
|
addElementAtNonFixedOffset(elt);
|
|
}
|
|
CurSize += eltTI.getFixedSize();
|
|
}
|
|
|
|
void StructLayoutBuilder::addNonFixedSizeElement(ElementLayout &elt) {
|
|
// If the element is the first non-empty element to be added to the
|
|
// structure, we can assign it a fixed offset (namely zero) despite
|
|
// it not having a fixed size/alignment.
|
|
if (isFixedLayout() && CurSize.isZero()) {
|
|
addNonFixedSizeElementAtOffsetZero(elt);
|
|
IsFixedLayout = false;
|
|
return;
|
|
}
|
|
|
|
// Otherwise, we cannot give it a fixed offset, even if all the
|
|
// previous elements are non-fixed. The problem is not that it has
|
|
// an unknown *size*; it's that it has an unknown *alignment*, which
|
|
// might force us to introduce padding. Absent some sort of user
|
|
// "max alignment" annotation (or having reached the platform
|
|
// maximum alignment, if there is one), these are part and parcel.
|
|
IsFixedLayout = false;
|
|
addElementAtNonFixedOffset(elt);
|
|
}
|
|
|
|
/// Add an empty element to the aggregate.
|
|
void StructLayoutBuilder::addEmptyElement(ElementLayout &elt) {
|
|
elt.completeEmpty(elt.getType().isPOD(ResilienceScope::Local));
|
|
}
|
|
|
|
/// Add an element at the fixed offset of the current end of the
|
|
/// aggregate.
|
|
void StructLayoutBuilder::addElementAtFixedOffset(ElementLayout &elt) {
|
|
assert(isFixedLayout());
|
|
assert(isa<FixedTypeInfo>(elt.getType()));
|
|
|
|
elt.completeFixed(elt.getType().isPOD(ResilienceScope::Local),
|
|
CurSize, StructFields.size());
|
|
StructFields.push_back(elt.getType().getStorageType());
|
|
}
|
|
|
|
/// Add an element at a non-fixed offset to the aggregate.
|
|
void StructLayoutBuilder::addElementAtNonFixedOffset(ElementLayout &elt) {
|
|
assert(!isFixedLayout());
|
|
elt.completeNonFixed(elt.getType().isPOD(ResilienceScope::Local),
|
|
NextNonFixedOffsetIndex);
|
|
}
|
|
|
|
/// Add a non-fixed-size element to the aggregate at offset zero.
|
|
void StructLayoutBuilder::addNonFixedSizeElementAtOffsetZero(ElementLayout &elt) {
|
|
assert(isFixedLayout());
|
|
assert(!isa<FixedTypeInfo>(elt.getType()));
|
|
assert(CurSize.isZero());
|
|
elt.completeInitialNonFixedSize(elt.getType().isPOD(ResilienceScope::Local));
|
|
}
|
|
|
|
/// Produce the current fields as an anonymous structure.
|
|
llvm::StructType *StructLayoutBuilder::getAsAnonStruct() const {
|
|
auto ty = llvm::StructType::get(IGM.getLLVMContext(), StructFields,
|
|
/*isPacked*/ true);
|
|
return ty;
|
|
}
|
|
|
|
/// Set the current fields as the body of the given struct type.
|
|
void StructLayoutBuilder::setAsBodyOfStruct(llvm::StructType *type) const {
|
|
assert(type->isOpaque());
|
|
type->setBody(StructFields, /*isPacked*/ true);
|
|
}
|